FIX FLEX-33166 Mobile TextInput with native StageTextInput cannot be included in scrollable forms FIX SUB: https://issues.apache.org/jira/browse/FLEX-33931 Cannot initiate view scrolling by touching a TextInput/TextArea Manually tested on: - iOS6 (ipad 2) - iOS7 (iPad 3) - ADL on Windows Mustella test pass: mobile/components/TextInput mobile/components/TextArea mobile/SoftKeyboard mobile/components/ActionBar
Project: http://git-wip-us.apache.org/repos/asf/flex-sdk/repo Commit: http://git-wip-us.apache.org/repos/asf/flex-sdk/commit/249b38b7 Tree: http://git-wip-us.apache.org/repos/asf/flex-sdk/tree/249b38b7 Diff: http://git-wip-us.apache.org/repos/asf/flex-sdk/diff/249b38b7 Branch: refs/heads/develop Commit: 249b38b7c8e4d6b5a464eb5d0ea4af3eb8a5393e Parents: e566d3e Author: mamsellem <[email protected]> Authored: Mon Nov 25 01:01:21 2013 +0100 Committer: mamsellem <[email protected]> Committed: Mon Nov 25 01:14:48 2013 +0100 ---------------------------------------------------------------------- .../supportClasses/ScrollableStageText.as | 70 +++++++++++---- .../skins/mobile/ScrollingStageTextAreaSkin.as | 18 ---- .../skins/mobile/ScrollingStageTextInputSkin.as | 18 ---- .../supportClasses/SkinnableTextBase.as | 94 +++++++++++--------- .../src/spark/core/IProxiedStageTextWrapper.as | 43 +++++++++ 5 files changed, 145 insertions(+), 98 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/249b38b7/frameworks/projects/mobilecomponents/src/spark/components/supportClasses/ScrollableStageText.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/mobilecomponents/src/spark/components/supportClasses/ScrollableStageText.as b/frameworks/projects/mobilecomponents/src/spark/components/supportClasses/ScrollableStageText.as index b09bb47..b777461 100644 --- a/frameworks/projects/mobilecomponents/src/spark/components/supportClasses/ScrollableStageText.as +++ b/frameworks/projects/mobilecomponents/src/spark/components/supportClasses/ScrollableStageText.as @@ -26,7 +26,6 @@ import flash.display.PixelSnapping; import flash.events.Event; import flash.events.FocusEvent; import flash.events.KeyboardEvent; -import flash.events.MouseEvent; import flash.events.SoftKeyboardEvent; import flash.geom.Matrix; import flash.geom.Point; @@ -47,15 +46,16 @@ import mx.core.LayoutDirection; import mx.core.UIComponent; import mx.core.mx_internal; import mx.events.FlexEvent; -import mx.events.TouchInteractionEvent; -import mx.managers.FocusManager; import mx.managers.SystemManager; import mx.utils.MatrixUtil; import spark.components.Application; +import spark.core.IProxiedStageTextWrapper; import spark.core.ISoftKeyboardHintClient; import spark.events.TextOperationEvent; +import flash.text.StageTextInitOptions; + use namespace mx_internal; //-------------------------------------- @@ -309,7 +309,7 @@ use namespace mx_internal; * @playerversion AIR 3.0 * @productversion Flex 4.12 */ -public class ScrollableStageText extends UIComponent implements IStyleableEditableText, ISoftKeyboardHintClient +public class ScrollableStageText extends UIComponent implements IStyleableEditableText, ISoftKeyboardHintClient , IProxiedStageTextWrapper { //-------------------------------------------------------------------------- @@ -336,6 +336,12 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita */ protected static var defaultStyles:Object; + /* Last stage text that received the focus. This is used by tempStageText to use the same keyboard type*/ + protected static var lastFocusedStageText: StageText; + + /* this is a hidden stage text that gets the focus to prevent soft keyboard from hiding*/ + protected static var hiddenFocusStageText: StageText = null; + /** * when set to true, displays the proxy images with a purple background, to help debugging proxy vs stageText usage. */ @@ -665,7 +671,10 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita return _densityScale; } - protected function get showProxy():Boolean { + /** + * @private + */ + protected function get showsProxy():Boolean { return !isEditing; } @@ -1343,7 +1352,7 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita updateViewPort(); } - if (invalidateProxyFlag && showProxy) + if (invalidateProxyFlag && showsProxy) { invalidateProxyFlag = false; updateProxy(); @@ -1364,7 +1373,8 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita { if (stageText != null) { - stageText.assignFocus(); // will trigger stageTextfocusIn + stageText.assignFocus(); + lastFocusedStageText = stageText; } } @@ -1732,7 +1742,6 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita savedSelectionActiveIndex = 0; } - addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_STARTING, touchStartingHandler); if (stageText != null) { stageText.addEventListener(Event.CHANGE, stageText_changeHandler); @@ -1758,7 +1767,6 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita savedSelectionActiveIndex = stageText.selectionActiveIndex; stageText.stage = null; - addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_STARTING, touchStartingHandler); stageText.removeEventListener(Event.CHANGE, stageText_changeHandler); stageText.removeEventListener(Event.COMPLETE, stageText_completeHandler); stageText.removeEventListener(FocusEvent.FOCUS_IN, stageText_focusInHandler); @@ -1807,13 +1815,6 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita - private function touchStartingHandler(event: Event): void - { - // don't allow touch scrolling while editing (of the StageText will stay in place) - if (isEditing) - event.preventDefault(); - } - private function stageText_focusInHandler(event:FocusEvent):void { if (!isEditing){ @@ -1830,7 +1831,7 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita { if (isEditing) { - endTextEdit(); + callLater(endTextEdit); } // Focus events are documented as bubbling. However, all events coming // from StageText are set to not bubble. So we need to create an @@ -1947,15 +1948,46 @@ public class ScrollableStageText extends UIComponent implements IStyleableEdita var parentSkin: UIComponent = this.parent.parent as UIComponent; return parentSkin ? parentSkin.id : "-" ; } + + /** + * @inheritDoc + */ + public function prepareForTouchScroll(): void + { + endTextEdit(); + // hide temp softkeyboard, if any + if (hiddenFocusStageText) { + hiddenFocusStageText.visible = false; + } + + } + + /** + * @inheritDoc + */ + public function keepSoftKeyboardActive(): void + { + // create temp stage text if does not exist + if (!hiddenFocusStageText) + { + hiddenFocusStageText = new StageText(new StageTextInitOptions(false)); + hiddenFocusStageText.viewPort = new Rectangle(0, 0, 0, 0); + hiddenFocusStageText.stage = stage; + } + + if (lastFocusedStageText) + hiddenFocusStageText.softKeyboardType = lastFocusedStageText.softKeyboardType; + hiddenFocusStageText.visible = true; + hiddenFocusStageText.assignFocus(); + } } } import flash.events.TimerEvent; import flash.text.StageText; -import flash.text.StageTextInitOptions; import flash.utils.Dictionary; import flash.utils.Timer; - +import flash.text.StageTextInitOptions; import spark.components.supportClasses.ScrollableStageText; class StageTextPool http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/249b38b7/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextAreaSkin.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextAreaSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextAreaSkin.as index 1990a4a..2b8d02a 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextAreaSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextAreaSkin.as @@ -42,30 +42,12 @@ public class ScrollingStageTextAreaSkin extends StageTextAreaSkin public function ScrollingStageTextAreaSkin() { super(); - addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); } override protected function createTextDisplay():IStyleableEditableText { return new ScrollableStageText(multiline); - - } - - /** @private - * We have to force focus to the text display when user user in the padding area of the TextInput. - we do this hack in the skin instead of the hostComponent TextInput to not impact the behavior of other skins - */ - override public function set owner(value: DisplayObjectContainer): void - { - super.owner = value; - if (owner){ - owner.mouseEnabled = false; - } } - private function mouseDownHandler(event: MouseEvent): void - { - textDisplay.setFocus(); - } } } http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/249b38b7/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextInputSkin.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextInputSkin.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextInputSkin.as index 61e37df..c42c893 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextInputSkin.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextInputSkin.as @@ -42,30 +42,12 @@ public class ScrollingStageTextInputSkin extends StageTextInputSkin public function ScrollingStageTextInputSkin() { super(); - addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); } override protected function createTextDisplay():IStyleableEditableText { return new ScrollableStageText(multiline); - - } - - /** @private - * We have to force focus to the text display when user user in the padding area of the TextInput. - we do this hack in the skin instead of the hostComponent TextInput to not impact the behavior of other skins - */ - override public function set owner(value: DisplayObjectContainer): void - { - super.owner = value; - if (owner){ - owner.mouseEnabled = false; - } } - private function mouseDownHandler(event: MouseEvent): void - { - textDisplay.setFocus(); - } } } http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/249b38b7/frameworks/projects/spark/src/spark/components/supportClasses/SkinnableTextBase.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/spark/src/spark/components/supportClasses/SkinnableTextBase.as b/frameworks/projects/spark/src/spark/components/supportClasses/SkinnableTextBase.as index d43ca69..6b6db26 100644 --- a/frameworks/projects/spark/src/spark/components/supportClasses/SkinnableTextBase.as +++ b/frameworks/projects/spark/src/spark/components/supportClasses/SkinnableTextBase.as @@ -49,6 +49,7 @@ import spark.components.RichEditableText; import spark.components.TextSelectionHighlighting; import spark.core.IDisplayText; import spark.core.IEditableText; +import spark.core.IProxiedStageTextWrapper; import spark.core.ISoftKeyboardHintClient; import spark.events.TextOperationEvent; @@ -443,11 +444,11 @@ public class SkinnableTextBase extends SkinnableComponent * @private * True if we received a mouseDown and we haven't receieved a mouseUp yet */ - private var isMouseDown:Boolean = false; + private var isTouchMouseDown:Boolean = false; /** * @private - * True if setFocus is called while isMouseDown is true + * True if setFocus is called while isTouchMouseDown is true */ private var delaySetFocus:Boolean = false; @@ -455,7 +456,7 @@ public class SkinnableTextBase extends SkinnableComponent * @private * The target from the current mouseDown event */ - private var mouseDownTarget:InteractiveObject; + private var touchMouseDownTarget:InteractiveObject; /** * @private @@ -1721,6 +1722,8 @@ public class SkinnableTextBase extends SkinnableComponent addEventListener(MouseEvent.MOUSE_DOWN, touchMouseDownHandler); addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_STARTING, touchInteractionStartingHandler); + addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_START, + touchInteractionStartHandler); touchHandlersAdded = true; } else if (getStyle("interactionMode") == InteractionMode.MOUSE && touchHandlersAdded) @@ -1728,6 +1731,8 @@ public class SkinnableTextBase extends SkinnableComponent removeEventListener(MouseEvent.MOUSE_DOWN, touchMouseDownHandler); removeEventListener(TouchInteractionEvent.TOUCH_INTERACTION_STARTING, touchInteractionStartingHandler); + removeEventListener(TouchInteractionEvent.TOUCH_INTERACTION_START, + touchInteractionStartHandler); touchHandlersAdded = false; } } @@ -1854,28 +1859,17 @@ public class SkinnableTextBase extends SkinnableComponent */ override public function setFocus():void { - // If the mouse is down, then we don't want the TextField to open the soft keyboard until mouse up. + // If the mouse is down, then we don't want the TextField to open the soft keyboard until mouse up. + // we also want to prevent the keyboard from hiding until the next mouse up focus, or touch scroll. // Otherwise, this was called programmatically and we want the soft keyboard to appear immediately. - // Note that isMouseDown can only be true when we are in InteractionMode == TOUCH. + // Note that isTouchMouseDown can only be true when we are in InteractionMode == TOUCH. if (textDisplay) { - if (isMouseDown) + if (isTouchMouseDown) { delaySetFocus = true; - - // Cancelling an ACTIVATING event will close the softKeyboard if it is - // currently active on iOS only. Add a check to only cancel the event - // if the softKeyboard is not active. Otherwise, the softKeyboard will - // close if you press on the skin of a text component. - var topLevelApp:Application = FlexGlobals.topLevelApplication as Application; - var cancelEvent:Boolean = !(topLevelApp && topLevelApp.isSoftKeyboardActive); - if (cancelEvent) - addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, softKeyboardActivatingHandler); - - textDisplay.setFocus(); - - if (cancelEvent) - removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, softKeyboardActivatingHandler); + if (textDisplay is IProxiedStageTextWrapper) + IProxiedStageTextWrapper(textDisplay).keepSoftKeyboardActive(); } else { @@ -2585,8 +2579,8 @@ public class SkinnableTextBase extends SkinnableComponent */ private function touchMouseDownHandler(event:MouseEvent):void { - isMouseDown = true; - mouseDownTarget = event.target as InteractiveObject; + isTouchMouseDown = true; + touchMouseDownTarget = event.target as InteractiveObject; // If we already have focus, make sure to open soft keyboard // on mouse up @@ -2602,7 +2596,7 @@ public class SkinnableTextBase extends SkinnableComponent /** * @private - * Called if we are in touch interaction mode and a mouseUp occurs on the stage while isMouseDown is true + * Called if we are in touch interaction mode and a mouseUp occurs on the stage while isTouchMouseDown is true */ private function touchMouseUpHandler(event:Event):void { @@ -2617,51 +2611,65 @@ public class SkinnableTextBase extends SkinnableComponent */ if ((event.target is DisplayObject && contains(DisplayObject(event.target))) && (delaySetFocus || - (mouseDownTarget == textDisplay && event.target != textDisplay))) + (touchMouseDownTarget == textDisplay && event.target != textDisplay))) { if (textDisplay) textDisplay.setFocus(); } - clearMouseDownState(); + clearTouchMouseDownState(); } - + + /** + * @private + * Called if we are inside of a Scroller and the user is about to start a scroll gesture. + * ask displayDisplay to show its proxy + */ + private function touchInteractionStartingHandler(event: TouchInteractionEvent): void + { + if (textDisplay && textDisplay is IProxiedStageTextWrapper){ + IProxiedStageTextWrapper(textDisplay).prepareForTouchScroll(); + } + } + /** * @private * Called if we are inside of a Scroller and the user has started a scroll gesture */ - private function touchInteractionStartingHandler(event:TouchInteractionEvent):void + private function touchInteractionStartHandler(event:TouchInteractionEvent):void { - // don't allow initiating scrolling from a TextInput on mobile anymore - event.preventDefault(); - } - + // if in iOS and keyboard is up and scrolling is occurring, drop the keyboard + var topLevelApp:Application = FlexGlobals.topLevelApplication as Application; + if (isIOS && topLevelApp && topLevelApp.isSoftKeyboardActive && editable) + { + // set focus + stage.focus = null; + } + + // Clear out the state because starting a scroll gesture should never + // open the soft keyboard + clearTouchMouseDownState(); + } + /** * @private * Helper function to clear the state if the mouse is up or we started as scroll gesture */ - private function clearMouseDownState():void + private function clearTouchMouseDownState():void { - if (isMouseDown) + if (isTouchMouseDown) { systemManager.getSandboxRoot().removeEventListener( MouseEvent.MOUSE_UP, touchMouseUpHandler, false); systemManager.getSandboxRoot().removeEventListener( SandboxMouseEvent.MOUSE_UP_SOMEWHERE, touchMouseUpHandler, false); - isMouseDown = false; + isTouchMouseDown = false; delaySetFocus = false; - mouseDownTarget = null; + touchMouseDownTarget = null; } } - /** - * @private - */ - private function softKeyboardActivatingHandler(event:SoftKeyboardEvent):void - { - event.preventDefault(); - } - + /** * @private * Called when the RichEditableText dispatches a 'selectionChange' event. http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/249b38b7/frameworks/projects/spark/src/spark/core/IProxiedStageTextWrapper.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/spark/src/spark/core/IProxiedStageTextWrapper.as b/frameworks/projects/spark/src/spark/core/IProxiedStageTextWrapper.as new file mode 100644 index 0000000..c806fef --- /dev/null +++ b/frameworks/projects/spark/src/spark/core/IProxiedStageTextWrapper.as @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +package spark.core +{ + +/** This interface defines properties for mobile wrapper classes of StageText that need to handle touch scrolling and soft keyboard. + * + */ +public interface IProxiedStageTextWrapper +{ + + /** + * this method is called before a touch scroll is about to occur, so that component can hide the StageText and show the proxies + * @see spark.components.supportClasses.ScrollableStageText#prepareForTouchScroll() + */ + function prepareForTouchScroll( ): void; + + /** + * this method is called between the time a text input get out of focus and the next text input gets in focus + * to prevent the soft keyboard from disappearing. + * @see spark.components.supportClasses.ScrollableStageText#keepSoftKeyboardActive() + */ + function keepSoftKeyboardActive( ): void ; + +} +}
