Return-Path:
Note: Since Note: Since StageText allows for better text entry and manipulation experiences on mobile devices
+ * using native text fields.
+ * The native fields provide correct visuals, text spacing and reflow, selection behavior, and
+ * text entry assistance.
+ * This class can also be used on desktop platforms where it behaves as a wrapper around TextField.
+ * Similiar to other native applications, when you tap outside of the native text field, the
+ * text field gives up focus and the soft keyboard goes away.
+ * This differs from when you tap outside of a TextField and the focus stays in the TextField and
+ * the soft keyboard remains visible. Limitation of StageText-based controls:
+ * flash.text.StageText
is not an
+ * InteractiveObject
, the Stage.focus
property may
+ * not be used to determine if a native text field has focus.flash.text.StageText
is not an
+ * InteractiveObject
, the Stage.focus
property may
+ * not be used to determine if a native text field has focus.preventDefault
, the soft keyboard will not open.
+ *
+ * @eventType flash.events.SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Event(name="softKeyboardActivating", type="flash.events.SoftKeyboardEvent")]
+
+/**
+ * Dispatched when a soft keyboard is lowered or hidden.
+ *
+ * @eventType flash.events.SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Event(name="softKeyboardDeactivate", type="flash.events.SoftKeyboardEvent")]
+
+//--------------------------------------
+// Styles
+//--------------------------------------
+
+/**
+ * Color of text in the component, including the component label.
+ *
+ * @default 0x000000
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Style(name="color", type="uint", format="Color", inherit="yes")]
+
+/**
+ * Name of the font to use.
+ * Unlike in a full CSS implementation,
+ * comma-separated lists are not supported.
+ * You can use any font family name.
+ * If you specify a generic font name,
+ * it is converted to an appropriate device font.
+ *
+ * @default "_sans"
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Style(name="fontFamily", type="String", inherit="yes")]
+
+/**
+ * Height of the text, in pixels.
+ *
+ * @default 24
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Style(name="fontSize", type="Number", format="Length", inherit="yes")]
+
+/**
+ * Determines whether the text is italic font.
+ * Recognized values are "normal"
and "italic"
.
+ *
+ * @default "normal"
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Style(name="fontStyle", type="String", enumeration="normal,italic", inherit="yes")]
+
+/**
+ * Determines whether the text is boldface.
+ * Recognized values are "normal"
and "bold"
.
+ *
+ * @default "normal"
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Style(name="fontWeight", type="String", enumeration="normal,bold", inherit="yes")]
+
+/**
+ * @copy spark.components.supportClasses.SkinnableTextBase#style:locale
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Style(name="locale", type="String", inherit="yes")]
+
+/**
+ * Alignment of text within a container.
+ * Possible values are "start"
, "end"
, "left"
,
+ * "right"
, or "center"
.
+ *
+ * @default "start"
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+[Style(name="textAlign", type="String", enumeration="start,end,left,right,center", inherit="yes")]
+
+
+/**
+ * The ScrollableStageText class is a text primitive for use in ActionScript
+ * skins which is used to present the user with a native text input field.
+ * It cannot be used in MXML markup, is not compatible with effects, and
+ * is not compatible with transformations such as rotation, scale, and skew.
+ *
+ *
+ *
+ * text
is always selectable.
multiline
determines what happens when you press the Enter key.
+ * If it is true
, the Enter key starts a new line.
+ * If it is false
, it causes a FlexEvent.ENTER
+ * event to be dispatched.
true
to allow more than one line of text to be input.
+ *
+ * @default false
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+ public function ScrollableStageText(multiline:Boolean = false)
+ {
+ super();
+
+ _multiline = multiline;
+ stageText = StageTextPool.current.acquireStageText(this);
+ stageText.visible = false;
+
+ if (!defaultStyles)
+ {
+ defaultStyles = {};
+
+ defaultStyles["textAlign"] = stageText.textAlign;
+ defaultStyles["fontFamily"] = stageText.fontFamily;
+ defaultStyles["fontWeight"] = stageText.fontWeight;
+ defaultStyles["fontStyle"] = stageText.fontPosture;
+ defaultStyles["fontSize"] = stageText.fontSize;
+ defaultStyles["color"] = stageText.color;
+ defaultStyles["locale"] = stageText.locale;
+ }
+
+ _displayAsPassword = stageText.displayAsPassword;
+ _maxChars = stageText.maxChars;
+ _restrict = stageText.restrict;
+
+ // Flex's default for autoCorrect is now true, so we need to turn on
+ // autoCorrect on the runtime side during construction.
+ stageText.autoCorrect = _autoCorrect;
+
+ addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
+ addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler, false, 0, true);
+
+}
+
+ //--------------------------------------------------------------------------
+ //
+ // Constructor
+ //
+ //--------------------------------------------------------------------------
+ /**
+ * To be displayed when out of focus
+ */
+ protected var proxy:DisplayObject = null;
+
+ //--------------------------------------------------------------------------
+ //
+ // Variables
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * The rectangle that defines the StageText's bounds in this object's
+ * parent's coordinate space.
+ */
+ private var localViewPort:Rectangle;
+ /**
+ * Flag indicating the position or size of the StageText needs to change.
+ */
+ private var invalidateViewPortFlag:Boolean = false;
+ /**
+ * Along with the text, need to save the selection, when the StageText is
+ * removed from the stage, so that when the StageText is restored, the
+ * selection can be restored.
+ */
+ private var savedSelectionAnchorIndex:int = 0;
+ private var savedSelectionActiveIndex:int = 0;
+ private var showProxy:Boolean = true;
+
+ /* if showProxy = true; proxy is visible, and StageText is not visible
+ * */
+ private var isEditing:Boolean = false;
+
+ /* indicates whether editing is in place
+ * */
+ private var isMouseDown:Boolean = false;
+
+ private var invalidateProxyFlag:Boolean = false;
+
+
+
+ //--------------------------------------------------------------------------
+ //
+ // Properties
+ //
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ //
+ // Variables
+ //
+ //--------------------------------------------------------------------------
+
+
+ /**
+ * The runtime StageText object that this field uses for text display and
+ * editing.
+ */
+ protected var stageText:StageText;
+
+ /**
+ * Flag indicating one or more styles have changed. If invalidateStyleFlag
+ * is false, commitStyles is a no-op, so it is safe to call commitStyles
+ * whenever this object is measured or drawn.
+ */
+ protected var invalidateStyleFlag:Boolean = true;
+
+ //--------------------------------------------------------------------------
+ //
+ // Properties
+ //
+ //--------------------------------------------------------------------------
+
+ //----------------------------------
+ // height
+ //----------------------------------
+
+ /**
+ * @private
+ */
+ override public function get height():Number
+ {
+ if (!localViewPort)
+ return 0;
+
+ return localViewPort.height;
+ }
+
+ /**
+ * @private
+ */
+ override public function set height(value:Number):void
+ {
+ super.height = value;
+
+ if (value == height)
+ return;
+
+ if (!localViewPort)
+ localViewPort = new Rectangle();
+
+ localViewPort.height = Math.max(0, value);
+
+ invalidateViewPortFlag = true;
+ invalidateProperties();
+ }
+
+ //----------------------------------
+ // visible
+ //----------------------------------
+
+ /**
+ * Storage for the visible property.
+ */
+ private var _visible:Boolean = true;
+
+ /**
+ * @private
+ */
+ override public function get visible():Boolean
+ {
+ return _visible;
+ }
+
+ /**
+ * @private
+ */
+ override public function set visible(value:Boolean):void
+ {
+ super.visible = value;
+
+ if (value == _visible)
+ return;
+
+ _visible = value;
+ invalidateProperties();
+ }
+
+ //----------------------------------
+ // width
+ //----------------------------------
+
+ /**
+ * @private
+ */
+ override public function get width():Number
+ {
+ if (!localViewPort)
+ return 0;
+
+ return localViewPort.width;
+ }
+
+ /**
+ * @private
+ */
+ override public function set width(value:Number):void
+ {
+ super.width = value;
+
+ if (value == width)
+ return;
+
+ if (!localViewPort)
+ localViewPort = new Rectangle();
+
+ localViewPort.width = Math.max(0, value);
+
+ invalidateViewPortFlag = true;
+ invalidateProperties();
+ }
+
+ //----------------------------------
+ // x
+ //----------------------------------
+
+ /**
+ * @private
+ */
+ override public function get x():Number
+ {
+ if (!localViewPort)
+ return 0;
+
+ return localViewPort.x;
+ }
+
+ /**
+ * @private
+ */
+ override public function set x(value:Number):void
+ {
+ super.x = value;
+
+ if (value == x)
+ return;
+
+ if (!localViewPort)
+ localViewPort = new Rectangle();
+
+ localViewPort.x = value;
+
+ invalidateViewPortFlag = true;
+ invalidateProperties();
+ }
+
+ //----------------------------------
+ // y
+ //----------------------------------
+
+ /**
+ * @private
+ */
+ override public function get y():Number
+ {
+ if (!localViewPort)
+ return 0;
+
+ return localViewPort.y;
+ }
+
+ /**
+ * @private
+ */
+ override public function set y(value:Number):void
+ {
+ super.y = value;
+
+ if (value == y)
+ return;
+
+ if (!localViewPort)
+ localViewPort = new Rectangle();
+
+ localViewPort.y = value;
+
+ invalidateViewPortFlag = true;
+ invalidateProperties();
+ }
+
+ //----------------------------------
+ // baselinePosition
+ //----------------------------------
+
+ /**
+ * @private
+ */
+ override public function get baselinePosition():Number
+ {
+ return measureTextLineHeight();
+ }
+
+ //----------------------------------
+ // densityScale
+ //----------------------------------
+
+ private var _densityScale:Number;
+
+ /**
+ * The scale factor necessary to account for differences in the design
+ * resolution of the application (applicationDPI) and the resolution of the
+ * device the application is running on.
+ */
+ protected function get densityScale():Number
+ {
+ if (isNaN(_densityScale))
+ {
+ var application:Application = FlexGlobals.topLevelApplication as Application;
+ var sm:SystemManager = application ? application.systemManager as SystemManager : null;
+ _densityScale = sm ? sm.densityScale : 1.0;
+ }
+
+ return _densityScale;
+ }
+
+ //----------------------------------
+ // displayAsPassword
+ //----------------------------------
+
+ /**
+ * Storage for the displayAsPassword property.
+ * This is needed because clients may ask for this after the StageText has
+ * been disposed.
+ */
+ protected var _displayAsPassword:Boolean;
+
+ /**
+ * Specifies whether the text field is a password text field.
+ *
+ * @default false
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+ public function get displayAsPassword():Boolean
+ {
+ return _displayAsPassword;
+ }
+
+ public function set displayAsPassword(value:Boolean):void
+ {
+ if (stageText != null)
+ stageText.displayAsPassword = value;
+
+ _displayAsPassword = value;
+ }
+
+ //----------------------------------
+ // editable
+ //----------------------------------
+
+ /**
+ * Storage for the editable property.
+ */
+ protected var _editable:Boolean = true;
+
+ /**
+ * Flag that indicates whether the text in the field is editable.
+ *
+ * @default true
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+ public function get editable():Boolean
+ {
+ return _editable;
+ }
+
+ public function set editable(value:Boolean):void
+ {
+ _editable = value;
+ invalidateProperties();
+ }
+
+ //----------------------------------
+ // horizontalScrollPosition
+ //----------------------------------
+
+ /**
+ * @private
+ * The horizontal scroll position of the text. StageText doesn't support
+ * this currently. Even so, we need to have this to satisfy requirements
+ * of the IEditableText interface.
+ */
+ public function get horizontalScrollPosition():Number
+ {
+ // TODO: StageText doesn't support this yet
+ return 0;
+ }
+
+ public function set horizontalScrollPosition(value:Number):void
+ {
+ // TODO: StageText doesn't support this yet
+ }
+
+ //----------------------------------
+ // isTruncated
+ //----------------------------------
+
+ /**
+ * @private
+ * A flag that indicates whether the text has been truncated. StageText
+ * doesn't support this currently. Even so, we need to have this to satisfy
+ * requirements of the IEditableText interface.
+ */
+ public function get isTruncated():Boolean
+ {
+ // TODO: StageText doesn't support measuring text yet
+ return false;
+ }
+
+ //----------------------------------
+ // lineBreak
+ //----------------------------------
+
+ /**
+ * @private
+ * Controls word wrapping within the text. This property corresponds
+ * to the lineBreak style. StageText doesn't support this currently. Even
+ * so, we need to have this to satisfy requirements of the IEditableText
+ * interface.
+ */
+ public function get lineBreak():String
+ {
+ return LineBreak.TO_FIT;
+ }
+
+ public function set lineBreak(value:String):void
+ {
+ // StageText only supports LineBreak.TO_FIT
+ }
+
+ //----------------------------------
+ // maxChars
+ //----------------------------------
+
+ /**
+ * Storage for the maxChars property.
+ * This is needed because clients may ask for this after the StageText has
+ * been disposed.
+ */
+ protected var _maxChars:int;
+
+ /**
+ * @copy flash.text.StageText#maxChars
+ *
+ * @default 0
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+ public function get maxChars():int
+ {
+ return _maxChars;
+ }
+
+ public function set maxChars(value:int):void
+ {
+ if (stageText != null)
+ stageText.maxChars = value;
+ _maxChars = value;
+ }
+
+ //----------------------------------
+ // multiline
+ //----------------------------------
+
+ /**
+ * Storage for the multiline property.
+ * This is needed because clients may ask for this after the StageText has
+ * been disposed.
+ */
+ protected var _multiline:Boolean;
+
+ /**
+ * @copy flash.text.StageText#multiline
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+ public function get multiline():Boolean
+ {
+ return _multiline;
+ }
+
+ /**
+ * @private
+ * StageText doesn't support setting its multiline property after it has
+ * been created. This setter is only here to satisfy requirements of the
+ * IEditableText interface.
+ */
+ public function set multiline(value:Boolean):void
+ {
+ // Do nothing.
+ // multiline cannot be set on StageText after it is created.
+ }
+
+ //----------------------------------
+ // restrict
+ //----------------------------------
+
+ /**
+ * Storage for the restrict property.
+ * This is needed because clients may ask for this after the StageText has
+ * been disposed.
+ */
+ protected var _restrict:String;
+
+ /**
+ * @copy flash.text.StageText#restrict
+ *
+ * @default null
+ *
+ * @langversion 3.0
+ * @playerversion AIR 3.0
+ * @productversion Flex 4.6
+ */
+ public function get restrict():String
+ {
+ return _restrict;
+ }
+
+ public function set restrict(value:String):void
+ {
+ if (stageText != null)
+ stageText.restrict = value;
+ _restrict = value;
+ }
+
+ //----------------------------------
+ // selectable
+ //----------------------------------
+
+ /**
+ * @private
+ * @inheritDoc
+ */
+ public function get selectable():Boolean
+ {
+ return true;
+ }
+
+ public function set selectable(value:Boolean):void
+ {
+ // Text is always selectable in StageText
+ }
+
+ //----------------------------------
+ // selectionActivePosition
+ //----------------------------------
+
+ /**
+ * The active, or last clicked position, of the selection. If the
+ * implementation does not support selection anchor this is the last
+ * character of the selection.
+ *
+ * This value can not be used as the source for data binding.
+ * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public function get selectionActivePosition():int + { + return stageText ? stageText.selectionActiveIndex : 0; + } + + //---------------------------------- + // selectionAnchorPosition + //---------------------------------- + + /** + * The anchor, or first clicked position, of the selection. If the + * implementation does not support selection anchor this is the first + * character of the selection. + * + *This value can not be used as the source for data binding.
+ * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public function get selectionAnchorPosition():int + { + return stageText ? stageText.selectionAnchorIndex : 0; + } + + //---------------------------------- + // text + //---------------------------------- + + /** + * Storage for the text property. + * This is needed because clients may ask for this after the StageText has + * been disposed. + */ + private var _text:String = ""; + + /** + * A string that is the current text in the text field. + * Lines are separated by the carriage return character ('\r', ASCII 13). + * This property contains unformatted text in the text field, without any formatting tags. + * + *If there was a prior selection, it will be preserved.
+ * If the length of the old text was less than the length of the new text, the selection
+ * will be adjusted so that neither selectionAnchorPosition
or
+ * selectionActivePosition
is greater than the length of the new text.
On iOS, for non multiline StyleableStageText objects, this function + * is not supported and does nothing.
+ * + *For some devices or operating systems, the selection may only be + * visible when the StageText object has focus.
+ * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public function selectAll():void + { + if (stageText != null && stageText.text != null) + { + stageText.selectRange(0, stageText.text.length); + invalidateProxy(); + } + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.6 + */ + public function selectRange(anchorIndex:int, activeIndex:int):void + { + if (stageText != null) + { + stageText.selectRange(anchorIndex, activeIndex); + invalidateProxy(); + } + } + + + /** + * EDITING AND FOCUS MANAGEMENT + */ + + protected function startTextEdit():void + { + if (!isEditing) + { + isEditing = true; + showProxy = false; + proxy.visible = false; + updateViewPort(); + stageText.visible = true; + } + } + + protected function endTextEdit():void + { + if (isEditing) + { + isEditing = false; + invalidateProxy(); + showProxy = true; + proxy.visible = true; + stageText.visible = false; + + if (focusManager is FocusManager) + { + var fm:FocusManager = focusManager as FocusManager; + var lastFocus:Object = fm.lastFocus as Object; + + if (lastFocus && lastFocus.hasOwnProperty("textDisplay") && lastFocus.textDisplay == this) + fm.lastFocus = null; + } + } + } + + //-------------------------------------------------------------------------- + // Proxy Management + //-------------------------------------------------------------------------- + + protected function createProxy():DisplayObject + { + var bm: DisplayObject ; + if (densityScale == 1) + { + bm = new Bitmap(null); + } + else + { + bm = new Bitmap(null, PixelSnapping.NEVER, true); + bm.scaleX = 1.0 / densityScale; + bm.scaleY = 1.0 / densityScale; + } + return bm; + } + + private function invalidateProxy():void + { + invalidateProxyFlag = true; + invalidateProperties(); + } + + /** + * Replace the existing proxy image representing this StageText with a new + * one. Call this whenever the StageText's properties, contents, or + * geometry changes. This does nothing if there is no proxy image, so it is + * safe to call updateProxy even if the state of the proxy image is + * unknown. + */ + protected function updateProxy():void + { + if (stageText == null) + return; + if (proxy != null) + { + var newImageData:BitmapData = captureBitmapData(); + if (newImageData) + { + var oldImageData:BitmapData = Bitmap(proxy).bitmapData; + Bitmap(proxy).bitmapData = newImageData; + if (oldImageData) + oldImageData.dispose(); + } + } + } + + /** Dispose the proxy resources once it has been removed from the stage */ + protected function disposeProxy():void + { + Bitmap(proxy).bitmapData.dispose(); + } + + /** + * If a StageText is visible, this will capture a bitmap copy of what it is + * displaying. This includes any text visible in the StageText and may + * include the text insertion cursor if it is visible at the time of the + * call. + */ + mx_internal function captureBitmapData():BitmapData + { + if (!stageText || !stageText.stage || !localViewPort || + localViewPort.width == 0 || localViewPort.height == 0) + return null; // The StageText is invisible. + + if (stageText.viewPort.width == 0 || stageText.viewPort.height == 0) + updateViewPort(); // The StageText viewport is stale. + + // Make sure any pending style changes get saved before replacing + // the StageText with a bitmap + commitStyles(); + + var bitmap:BitmapData = new BitmapData(stageText.viewPort.width, + stageText.viewPort.height, !debugProxyImage, debugProxyImage ? 0xFF00FF : 0x00FFFFFF); + + stageText.drawViewPortToBitmapData(bitmap); + + return bitmap; + } + + + protected function getGlobalViewPort():Rectangle + { + // We calculate the parent's concatenated matrix to deal with + // issues in the runtime where the concatenated matrix of the + // parent is out of sync. See SDK-31538. + var m:Matrix = MatrixUtil.getConcatenatedMatrix(parent, stage); + var globalTopLeft:Point = m.transformPoint(localViewPort.topLeft); + + // Transform the bottom-right corner of the local rect + // instead of setting width/height to account for any + // transformations applied to ancestor objects. + var globalBottomRight:Point = m.transformPoint(localViewPort.bottomRight); + var globalRect:Rectangle = new Rectangle(); + + // StageText can't deal with upside-down or mirrored rectangles + // or non-integer values. Fix those here. + globalRect.x = Math.floor(Math.min(globalTopLeft.x, globalBottomRight.x)); + globalRect.y = Math.floor(Math.min(globalTopLeft.y, globalBottomRight.y)); + globalRect.width = Math.ceil(Math.abs(globalBottomRight.x - globalTopLeft.x)); + globalRect.height = Math.ceil(Math.abs(globalBottomRight.y - globalTopLeft.y)); + + return globalRect; + } + + /** + * Tell the StageText what rectangle it needs to render in. The StageText + * is not part of the normal display hierarchy, so its coordinates are + * always specified in global space. + */ + protected function updateViewPort():void + { + if (parent && localViewPort && stageText != null) + { + if (stageText.stage) + { + var globalRect:Rectangle = getGlobalViewPort(); + + if (!globalRect.equals(stageText.viewPort)) + { + stageText.viewPort = globalRect; + } + } + } + } + + /** + * StageText does not have any provision for measuring text. To get + * approximate sizing, this uses UIComponent's text measurement method on a + * string with an ascender and a descender. Because platform rendering and + * UIComponent's rendering differ, the measurement should only be used as + * an approximation. + */ + protected function measureTextLineHeight():Number + { + var lineMetrics:TextLineMetrics = measureText("Wj"); + + // Android text heights are slightly different from Flex's. + if (isAndroid) + return lineMetrics.height * androidHeightMultiplier; + + return lineMetrics.height; + } + + protected function restoreStageText():void + { + if (stageText != null) + { + // This has to happen here instead of waiting for commitProperties + // because this will cause stageText.text to get cleared. Subsequent + // change events would then copy that cleared text to the _text + // storage variable, making the change permanent. + stageText.editable = _editable; + + // Restore the text and the selection. + stageText.text = _text; + stageText.selectRange(savedSelectionAnchorIndex, savedSelectionActiveIndex); + savedSelectionAnchorIndex = 0; + savedSelectionActiveIndex = 0; + + stageText.displayAsPassword = _displayAsPassword; + stageText.maxChars = _maxChars; + + stageText.restrict = _restrict; + + // Soft keyboard hints + stageText.autoCapitalize = _autoCapitalize; + stageText.autoCorrect = _autoCorrect; + stageText.returnKeyLabel = _returnKeyLabel; + stageText.softKeyboardType = _softKeyboardType; + + // Make sure styles are restored + invalidateStyleFlag = true; + + // Make sure viewPort and enabled state are recalculated + invalidateViewPortFlag = true; + invalidateProperties(); + } + } + + //-------------------------------------------------------------------------- + // + // event handlers + // + //-------------------------------------------------------------------------- + + protected function addedToStageHandler(event:Event):void + { + + var needsRestore:Boolean = false; + if (stageText == null) + { + needsRestore = !StageTextPool.current.hasCachedStageText(this); + stageText = StageTextPool.current.acquireStageText(this); + stageText.visible = false; + } + + proxy = createProxy(); + addChild(proxy); + invalidateProxy(); + // The "complete" handler must be registered before changes to the stage + // or viewPort. StageText on iOS dispatches complete events during the + // setting of these properties, unlike Android which does so some time + // afterward. + stageText.addEventListener(Event.COMPLETE, stageText_completeHandler); + stageText.stage = stage; + + if (needsRestore) + { + restoreStageText(); + } + else if (savedSelectionAnchorIndex > 0 || savedSelectionActiveIndex > 0) + { + // Even if the StageText has been retrieved from the cache, its + // selection is not preserved. Restore the selection if necessary. + if (savedSelectionAnchorIndex <= _text.length && savedSelectionActiveIndex <= _text.length) + stageText.selectRange(savedSelectionAnchorIndex, savedSelectionActiveIndex); + savedSelectionAnchorIndex = 0; + savedSelectionActiveIndex = 0; + } + +// if (deferredViewPortUpdate) +// updateViewPort(); + + // always create proxy image + + // register listeners + addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); + addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); + if (stageText != null) + { + stageText.addEventListener(Event.CHANGE, stageText_changeHandler); + stageText.addEventListener(FocusEvent.FOCUS_IN, stageText_focusInHandler); + stageText.addEventListener(FocusEvent.FOCUS_OUT, stageText_focusOutHandler); + stageText.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, stageText_softKeyboardActivateHandler); + stageText.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, stageText_softKeyboardDeactivateHandler); + stageText.addEventListener(KeyboardEvent.KEY_DOWN, stageText_keyDownHandler); + stageText.addEventListener(KeyboardEvent.KEY_UP, stageText_keyUpHandler); + } + + invalidateProperties(); + } + + protected function removedFromStageHandler(event:Event):void + { + + if (stageText == null) + return; + + // Text is saved in _text. Also need to save the selection so it can be restored. + savedSelectionAnchorIndex = stageText.selectionAnchorIndex; + savedSelectionActiveIndex = stageText.selectionActiveIndex; + + + stageText.stage = null; + + removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); + + stageText.removeEventListener(Event.CHANGE, stageText_changeHandler); + stageText.removeEventListener(Event.COMPLETE, stageText_completeHandler); + stageText.removeEventListener(FocusEvent.FOCUS_IN, stageText_focusInHandler); + stageText.removeEventListener(FocusEvent.FOCUS_OUT, stageText_focusOutHandler); + stageText.removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, stageText_softKeyboardActivateHandler); + stageText.removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, stageText_softKeyboardDeactivateHandler); + stageText.removeEventListener(KeyboardEvent.KEY_DOWN, stageText_keyDownHandler); + stageText.removeEventListener(KeyboardEvent.KEY_UP, stageText_keyUpHandler); + + StageTextPool.current.releaseStageText(this, stageText); + stageText = null; + + if (proxy != null) + { + // If a textImage exists, we need to get rid of it to keep it in + // sync with our proxy image state. disposeProxyImage does not do + // this. It only sets a flag and invalidates properties. + removeChild(proxy); + disposeProxy(); + proxy = null; + } + } + + private function mouseDownHandler(event:MouseEvent):void + { + isMouseDown = true; + } + + private function mouseUpHandler(event:MouseEvent):void + { + isMouseDown = false; + setFocus(); + } + + protected function stageText_changeHandler(event:Event):void + { + var foundChange:Boolean = false; + + if (stageText != null) + { + var oldText:String = _text; + var newText:String = stageText.text; + + foundChange = newText != oldText; + _text = stageText.text; + } + if (foundChange) + dispatchEvent(new TextOperationEvent(event.type)); + } + + protected function stageText_completeHandler(event:Event):void + { + invalidateProxy(); + invalidateViewPortFlag = true; + invalidateProperties(); + } + + private function stageText_focusInHandler(event:FocusEvent):void + { + startTextEdit(); + } + + private function stageText_focusOutHandler(event:FocusEvent):void + { + endTextEdit(); + // Focus events are documented as bubbling. However, all events coming + // from StageText are set to not bubble. So we need to create an + // appropriate bubbling event here. + dispatchEvent(new FocusEvent(event.type, true, event.cancelable, + event.relatedObject, event.shiftKey, event.keyCode, event.direction)); + } + + private function stageText_keyDownHandler(event:KeyboardEvent):void + { + // Taps on the Enter key on soft keyboards may send us the Next keycode + if ((event.keyCode == Keyboard.ENTER || event.keyCode == Keyboard.NEXT) + && !_multiline) + { + dispatchEvent(new FlexEvent(FlexEvent.ENTER)); + } + + // Keyboard events are documented as bubbling. However, all events + // coming from StageText are set to not bubble. So we need to create an + // appropriate bubbling event here. + dispatchEvent(new KeyboardEvent(event.type, true, event.cancelable, + event.charCode, event.keyCode, event.keyLocation, event.ctrlKey, + event.altKey, event.shiftKey, event.controlKey, event.commandKey)); + + if ((event.keyCode == Keyboard.ENTER || event.keyCode == Keyboard.NEXT) + && !_multiline && !isDesktop) + { + event.preventDefault(); + } + } + + private function stageText_keyUpHandler(event:KeyboardEvent):void + { + // Keyboard events are documented as bubbling. However, all events + // coming from StageText are set to not bubble. So we need to create an + // appropriate bubbling event here. + dispatchEvent(new KeyboardEvent(event.type, true, event.cancelable, + event.charCode, event.keyCode, event.keyLocation, event.ctrlKey, + event.altKey, event.shiftKey, event.controlKey, event.commandKey)); + + if ((event.keyCode == Keyboard.ENTER || event.keyCode == Keyboard.NEXT) + && !_multiline && !isDesktop) + { + event.preventDefault(); + } + } + + private function stageText_softKeyboardActivateHandler(event:SoftKeyboardEvent):void + { + startTextEdit(); + dispatchEvent(new SoftKeyboardEvent(event.type, + true, event.cancelable, this, event.triggerType)); + } + + private function stageText_softKeyboardDeactivateHandler(event:SoftKeyboardEvent):void + { + endTextEdit(); + dispatchEvent(new SoftKeyboardEvent(event.type, + true, event.cancelable, this, event.triggerType)); + } + +} +} + +import flash.events.TimerEvent; +import flash.text.StageText; +import flash.text.StageTextInitOptions; +import flash.utils.Dictionary; +import flash.utils.Timer; + +import spark.components.supportClasses.ScrollableStageText; + +class StageTextPool +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + private static const poolReserve:Number = 5; + private static const poolTimerInterval:Number = 10000; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + private static var _current:StageTextPool; + + internal static function get current():StageTextPool { + if (!_current ){ + _current = new StageTextPool(); + } + return _current; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public function StageTextPool( ) { + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + private var map_StyleableStageText_to_StageText:Dictionary = new Dictionary(true); + private var map_StageText_to_StyleableStageText:Dictionary = new Dictionary(true); + + private var multilinePool:Vector.and can be used in scrollable forms while allowing precise control of keyboard input.
+ * + * @see spark.components.TextArea + * @see spark.components.supportClasses.ScrollableStageText + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.12 + */ +public class ScrollingStageTextAreaSkin extends StageTextAreaSkin +{ + public function ScrollingStageTextAreaSkin() + { + super(); + } + + override protected function createTextDisplay():IStyleableEditableText + { + return new ScrollableStageText(multiline); + } +} +} http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/48169122/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 new file mode 100644 index 0000000..d856d53 --- /dev/null +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/ScrollingStageTextInputSkin.as @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.skins.mobile +{ +import spark.components.supportClasses.IStyleableEditableText; +import spark.components.supportClasses.ScrollableStageText; + +/** + * ActionScript-based skin for TextInput controls in mobile applications that uses a + * ScrollableStageText class for the text display + *and can be used in scrollable forms while allowing precise control of keyboard input.
+ * + * @see spark.components.TextInput + * @see spark.components.supportClasses.ScrollableStageText + * + * @langversion 3.0 + * @playerversion AIR 3.0 + * @productversion Flex 4.12 + */ +public class ScrollingStageTextInputSkin extends StageTextInputSkin +{ + public function ScrollingStageTextInputSkin() + { + super(); + } + + override protected function createTextDisplay():IStyleableEditableText + { + return new ScrollableStageText(multiline); + } +} +} http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/48169122/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/StageTextSkinBase.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/StageTextSkinBase.as b/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/StageTextSkinBase.as index fa0341c..9e4514d 100644 --- a/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/StageTextSkinBase.as +++ b/frameworks/projects/mobiletheme/src/spark/skins/mobile/supportClasses/StageTextSkinBase.as @@ -26,6 +26,8 @@ import flash.geom.Point; import mx.core.DPIClassification; import mx.core.mx_internal; +import spark.components.supportClasses.IStyleableEditableText; + import spark.components.supportClasses.SkinnableTextBase; import spark.components.supportClasses.StyleableStageText; import spark.components.supportClasses.StyleableTextField; @@ -202,7 +204,7 @@ public class StageTextSkinBase extends MobileSkin * @playerversion AIR 3.0 * @productversion Flex 4.6 */ - public var textDisplay:StyleableStageText; + public var textDisplay:IStyleableEditableText; [Bindable] /** @@ -230,11 +232,11 @@ public class StageTextSkinBase extends MobileSkin if (!textDisplay) { - textDisplay = new StyleableStageText(multiline); + textDisplay = createTextDisplay(); textDisplay.editable = true; textDisplay.styleName = this; - this.addChild(textDisplay); + this.addChild(DisplayObject(textDisplay)); } if (!border) @@ -244,6 +246,14 @@ public class StageTextSkinBase extends MobileSkin } } + /** Could be overridden by subclasses + * + * @return instance of IStyleableEditableText + */ + protected function createTextDisplay():IStyleableEditableText { + return new StyleableStageText(multiline); + } + /** * @private */