· 6 years ago · Mar 19, 2020, 02:02 PM
1/*
2Feathers
3Copyright 2012-2015 Bowler Hat LLC. All Rights Reserved.
4
5This program is free software. You can redistribute and/or modify it in
6accordance with the terms of the accompanying license agreement.
7*/
8package feathers.controls.text {
9 import feathers.core.FeathersControl;
10 import feathers.core.FocusManager;
11 import feathers.core.IMultilineTextEditor;
12 import feathers.events.FeathersEventType;
13 import feathers.skins.IStyleProvider;
14 import feathers.text.StageTextField;
15 import feathers.utils.display.stageToStarling;
16 import feathers.utils.geom.matrixToScaleX;
17 import feathers.utils.geom.matrixToScaleY;
18
19 import flash.display.BitmapData;
20 import flash.events.Event;
21 import flash.events.FocusEvent;
22 import flash.events.KeyboardEvent;
23 import flash.events.SoftKeyboardEvent;
24 import flash.geom.Matrix;
25 import flash.geom.Matrix3D;
26 import flash.geom.Point;
27 import flash.geom.Rectangle;
28 import flash.geom.Vector3D;
29 import flash.system.Capabilities;
30 import flash.text.TextField;
31 import flash.text.TextFieldAutoSize;
32 import flash.text.TextFormat;
33 import flash.text.TextFormatAlign;
34 import flash.text.engine.FontPosture;
35 import flash.text.engine.FontWeight;
36 import flash.ui.Keyboard;
37 import flash.utils.getDefinitionByName;
38
39 import starling.core.RenderSupport;
40 import starling.core.Starling;
41 import starling.display.DisplayObject;
42 import starling.display.Image;
43 import starling.events.Event;
44 import starling.textures.ConcreteTexture;
45 import starling.textures.Texture;
46 import starling.utils.MatrixUtil;
47 import starling.utils.SystemUtil;
48
49 /**
50 * Dispatched when the text property changes.
51 *
52 * <p>The properties of the event object have the following values:</p>
53 * <table class="innertable">
54 * <tr><th>Property</th><th>Value</th></tr>
55 * <tr><td><code>bubbles</code></td><td>false</td></tr>
56 * <tr><td><code>currentTarget</code></td><td>The Object that defines the
57 * event listener that handles the event. For example, if you use
58 * <code>myButton.addEventListener()</code> to register an event listener,
59 * myButton is the value of the <code>currentTarget</code>.</td></tr>
60 * <tr><td><code>data</code></td><td>null</td></tr>
61 * <tr><td><code>target</code></td><td>The Object that dispatched the event;
62 * it is not always the Object listening for the event. Use the
63 * <code>currentTarget</code> property to always access the Object
64 * listening for the event.</td></tr>
65 * </table>
66 */
67 [Event(name = "change", type = "starling.events.Event")]
68
69 /**
70 * Dispatched when the user presses the Enter key while the editor has
71 * focus. This event may not be dispatched on some platforms, depending on
72 * the value of <code>returnKeyLabel</code>. This issue may even occur when
73 * using the <em>default value</em> of <code>returnKeyLabel</code>!
74 *
75 * <p>The properties of the event object have the following values:</p>
76 * <table class="innertable">
77 * <tr><th>Property</th><th>Value</th></tr>
78 * <tr><td><code>bubbles</code></td><td>false</td></tr>
79 * <tr><td><code>currentTarget</code></td><td>The Object that defines the
80 * event listener that handles the event. For example, if you use
81 * <code>myButton.addEventListener()</code> to register an event listener,
82 * myButton is the value of the <code>currentTarget</code>.</td></tr>
83 * <tr><td><code>data</code></td><td>null</td></tr>
84 * <tr><td><code>target</code></td><td>The Object that dispatched the event;
85 * it is not always the Object listening for the event. Use the
86 * <code>currentTarget</code> property to always access the Object
87 * listening for the event.</td></tr>
88 * </table>
89 *
90 * @eventType feathers.events.FeathersEventType.ENTER
91 * @see #returnKeyLabel
92 */
93 [Event(name = "enter", type = "starling.events.Event")]
94
95 /**
96 * Dispatched when the text editor receives focus.
97 *
98 * <p>The properties of the event object have the following values:</p>
99 * <table class="innertable">
100 * <tr><th>Property</th><th>Value</th></tr>
101 * <tr><td><code>bubbles</code></td><td>false</td></tr>
102 * <tr><td><code>currentTarget</code></td><td>The Object that defines the
103 * event listener that handles the event. For example, if you use
104 * <code>myButton.addEventListener()</code> to register an event listener,
105 * myButton is the value of the <code>currentTarget</code>.</td></tr>
106 * <tr><td><code>data</code></td><td>null</td></tr>
107 * <tr><td><code>target</code></td><td>The Object that dispatched the event;
108 * it is not always the Object listening for the event. Use the
109 * <code>currentTarget</code> property to always access the Object
110 * listening for the event.</td></tr>
111 * </table>
112 *
113 * @eventType feathers.events.FeathersEventType.FOCUS_IN
114 */
115 [Event(name = "focusIn", type = "starling.events.Event")]
116
117 /**
118 * Dispatched when the text editor loses focus.
119 *
120 * <p>The properties of the event object have the following values:</p>
121 * <table class="innertable">
122 * <tr><th>Property</th><th>Value</th></tr>
123 * <tr><td><code>bubbles</code></td><td>false</td></tr>
124 * <tr><td><code>currentTarget</code></td><td>The Object that defines the
125 * event listener that handles the event. For example, if you use
126 * <code>myButton.addEventListener()</code> to register an event listener,
127 * myButton is the value of the <code>currentTarget</code>.</td></tr>
128 * <tr><td><code>data</code></td><td>null</td></tr>
129 * <tr><td><code>target</code></td><td>The Object that dispatched the event;
130 * it is not always the Object listening for the event. Use the
131 * <code>currentTarget</code> property to always access the Object
132 * listening for the event.</td></tr>
133 * </table>
134 *
135 * @eventType feathers.events.FeathersEventType.FOCUS_OUT
136 */
137 [Event(name = "focusOut", type = "starling.events.Event")]
138
139 /**
140 * Dispatched when the soft keyboard is activated. Not all text editors will
141 * activate a soft keyboard.
142 *
143 * <p>The properties of the event object have the following values:</p>
144 * <table class="innertable">
145 * <tr><th>Property</th><th>Value</th></tr>
146 * <tr><td><code>bubbles</code></td><td>false</td></tr>
147 * <tr><td><code>currentTarget</code></td><td>The Object that defines the
148 * event listener that handles the event. For example, if you use
149 * <code>myButton.addEventListener()</code> to register an event listener,
150 * myButton is the value of the <code>currentTarget</code>.</td></tr>
151 * <tr><td><code>data</code></td><td>null</td></tr>
152 * <tr><td><code>target</code></td><td>The Object that dispatched the event;
153 * it is not always the Object listening for the event. Use the
154 * <code>currentTarget</code> property to always access the Object
155 * listening for the event.</td></tr>
156 * </table>
157 *
158 * @eventType feathers.events.FeathersEventType.SOFT_KEYBOARD_ACTIVATE
159 */
160 [Event(name = "softKeyboardActivate", type = "starling.events.Event")]
161
162 /**
163 * Dispatched when the soft keyboard is deactivated. Not all text editors
164 * will activate a soft keyboard.
165 *
166 * <p>The properties of the event object have the following values:</p>
167 * <table class="innertable">
168 * <tr><th>Property</th><th>Value</th></tr>
169 * <tr><td><code>bubbles</code></td><td>false</td></tr>
170 * <tr><td><code>currentTarget</code></td><td>The Object that defines the
171 * event listener that handles the event. For example, if you use
172 * <code>myButton.addEventListener()</code> to register an event listener,
173 * myButton is the value of the <code>currentTarget</code>.</td></tr>
174 * <tr><td><code>data</code></td><td>null</td></tr>
175 * <tr><td><code>target</code></td><td>The Object that dispatched the event;
176 * it is not always the Object listening for the event. Use the
177 * <code>currentTarget</code> property to always access the Object
178 * listening for the event.</td></tr>
179 * </table>
180 *
181 * @eventType feathers.events.FeathersEventType.SOFT_KEYBOARD_DEACTIVATE
182 */
183 [Event(name = "softKeyboardDeactivate", type = "starling.events.Event")]
184
185 /**
186 * Text that may be edited at runtime by the user with the
187 * <code>TextInput</code> component, rendered with the native
188 * <code>flash.text.StageText</code> class in Adobe AIR and the custom
189 * <code>feathers.text.StageTextField</code> class in Adobe Flash Player
190 * (<code>StageTextField</code> simulates <code>StageText</code> using
191 * <code>flash.text.TextField</code>). When not in focus, the
192 * <code>StageText</code> (or <code>StageTextField</code>) is drawn to
193 * <code>BitmapData</code> and uploaded to a texture on the GPU. Textures
194 * are managed internally by this component, and they will be automatically
195 * disposed when the component is disposed.
196 *
197 * <p>The following example shows how to use
198 * <code>StageTextTextEditor</code> with a <code>TextInput</code>:</p>
199 *
200 * <listing version="3.0">
201 * var input:TextInput = new TextInput();
202 * input.textEditorFactory = function():ITextEditor
203 * {
204 * return new StageTextTextEditor();
205 * };
206 * this.addChild( input );</listing>
207 *
208 * @see feathers.controls.TextInput
209 * @see ../../../../help/text-editors.html Introduction to Feathers text editors
210 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html flash.text.StageText
211 * @see feathers.text.StageTextField
212 */
213 public class StageTextTextEditor extends FeathersControl implements IMultilineTextEditor {
214 /**
215 * @private
216 */
217 private static var HELPER_MATRIX3D: Matrix3D;
218
219 /**
220 * @private
221 */
222 private static var HELPER_POINT3D: Vector3D;
223
224 /**
225 * @private
226 */
227 private static const HELPER_MATRIX: Matrix = new Matrix();
228
229 /**
230 * @private
231 */
232 private static const HELPER_POINT: Point = new Point();
233
234 /**
235 * The default <code>IStyleProvider</code> for all <code>StageTextTextEditor</code>
236 * components.
237 *
238 * @default null
239 * @see feathers.core.FeathersControl#styleProvider
240 */
241 public static var globalStyleProvider: IStyleProvider;
242
243 /**
244 * Constructor.
245 */
246 public function StageTextTextEditor() {
247 this._stageTextIsTextField = /^(Windows|Mac OS|Linux) .*/.exec(Capabilities.os);
248 this.isQuickHitAreaEnabled = true;
249 this.addEventListener(starling.events.Event.REMOVED_FROM_STAGE, textEditor_removedFromStageHandler);
250 }
251
252 /**
253 * @private
254 */
255 override protected function get defaultStyleProvider(): IStyleProvider {
256 return globalStyleProvider;
257 }
258
259 /**
260 * The StageText instance. It's typed Object so that a replacement class
261 * can be used in browser-based Flash Player.
262 */
263 protected var stageText: Object;
264
265 /**
266 * An image that displays a snapshot of the native <code>StageText</code>
267 * in the Starling display list when the editor doesn't have focus.
268 */
269 protected var textSnapshot: Image;
270
271 /**
272 * @private
273 */
274 protected var _needsNewTexture: Boolean = false;
275
276 /**
277 * @private
278 */
279 protected var _ignoreStageTextChanges: Boolean = false;
280
281 /**
282 * @private
283 */
284 protected var _text: String = "";
285
286 /**
287 * The text displayed by the input.
288 *
289 * <p>In the following example, the text is changed:</p>
290 *
291 * <listing version="3.0">
292 * textEditor.text = "Lorem ipsum";</listing>
293 *
294 * @default ""
295 */
296 public function get text(): String {
297 return this._text;
298 }
299
300 /**
301 * @private
302 */
303 public function set text(value: String): void {
304 if (!value) {
305 //don't allow null or undefined
306 value = "";
307 }
308 if (this._text == value) {
309 return;
310 }
311 this._text = value;
312 this.invalidate(INVALIDATION_FLAG_DATA);
313 this.dispatchEventWith(starling.events.Event.CHANGE);
314 }
315
316 /**
317 * @private
318 */
319 protected var _measureTextField: TextField;
320
321 /**
322 * @private
323 * This flag tells us if StageText is implemented by a TextField under
324 * the hood. We want to eliminate that damn TextField gutter to improve
325 * consistency across platforms.
326 */
327 protected var _stageTextIsTextField: Boolean = false;
328
329 /**
330 * @private
331 */
332 protected var _stageTextHasFocus: Boolean = false;
333
334 /**
335 * @private
336 */
337 protected var _isWaitingToSetFocus: Boolean = false;
338
339 /**
340 * @private
341 */
342 protected var _pendingSelectionBeginIndex: int = -1;
343
344 /**
345 * @inheritDoc
346 */
347 public function get selectionBeginIndex(): int {
348 if (this._pendingSelectionBeginIndex >= 0) {
349 return this._pendingSelectionBeginIndex;
350 }
351 if (this.stageText) {
352 return this.stageText.selectionAnchorIndex;
353 }
354 return 0;
355 }
356
357 /**
358 * @private
359 */
360 protected var _pendingSelectionEndIndex: int = -1;
361
362 /**
363 * @inheritDoc
364 */
365 public function get selectionEndIndex(): int {
366 if (this._pendingSelectionEndIndex >= 0) {
367 return this._pendingSelectionEndIndex;
368 }
369 if (this.stageText) {
370 return this.stageText.selectionActiveIndex;
371 }
372 return 0;
373 }
374
375 /**
376 * @private
377 */
378 protected var _stageTextIsComplete: Boolean = false;
379
380 /**
381 * @inheritDoc
382 */
383 public function get baseline(): Number {
384 if (!this._measureTextField) {
385 return 0;
386 }
387 return this._measureTextField.getLineMetrics(0).ascent;
388 }
389
390 /**
391 * @private
392 */
393 protected var _autoCapitalize: String = "none";
394
395 /**
396 * Controls how a device applies auto capitalization to user input. This
397 * property is only a hint to the underlying platform, because not all
398 * devices and operating systems support this functionality.
399 *
400 * <p>In the following example, the auto capitalize behavior is changed:</p>
401 *
402 * <listing version="3.0">
403 * textEditor.autoCapitalize = AutoCapitalize.WORD;</listing>
404 *
405 * @default flash.text.AutoCapitalize.NONE
406 *
407 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#autoCapitalize Full description of flash.text.StageText.autoCapitalize in Adobe's Flash Platform API Reference
408 */
409 public function get autoCapitalize(): String {
410 return this._autoCapitalize;
411 }
412
413 /**
414 * @private
415 */
416 public function set autoCapitalize(value: String): void {
417 if (this._autoCapitalize == value) {
418 return;
419 }
420 this._autoCapitalize = value;
421 this.invalidate(INVALIDATION_FLAG_STYLES);
422 }
423
424 /**
425 * @private
426 */
427 protected var _autoCorrect: Boolean = false;
428
429 /**
430 * Indicates whether a device auto-corrects user input for spelling or
431 * punctuation mistakes. This property is only a hint to the underlying
432 * platform, because not all devices and operating systems support this
433 * functionality.
434 *
435 * <p>In the following example, auto correct is enabled:</p>
436 *
437 * <listing version="3.0">
438 * textEditor.autoCorrect = true;</listing>
439 *
440 * @default false
441 *
442 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#autoCorrect Full description of flash.text.StageText.autoCorrect in Adobe's Flash Platform API Reference
443 */
444 public function get autoCorrect(): Boolean {
445 return this._autoCorrect;
446 }
447
448 /**
449 * @private
450 */
451 public function set autoCorrect(value: Boolean): void {
452 if (this._autoCorrect == value) {
453 return;
454 }
455 this._autoCorrect = value;
456 this.invalidate(INVALIDATION_FLAG_STYLES);
457 }
458
459 /**
460 * @private
461 */
462 protected var _color: uint = 0x000000;
463
464 /**
465 * Specifies text color as a number containing three 8-bit RGB
466 * components.
467 *
468 * <p>In the following example, the text color is changed:</p>
469 *
470 * <listing version="3.0">
471 * textEditor.color = 0xff9900;</listing>
472 *
473 * @default 0x000000
474 *
475 * @see #disabledColor
476 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#color Full description of flash.text.StageText.color in Adobe's Flash Platform API Reference
477 */
478 public function get color(): uint {
479 return this._color;
480 }
481
482 /**
483 * @private
484 */
485 public function set color(value: uint): void {
486 if (this._color == value) {
487 return;
488 }
489 this._color = value;
490 this.invalidate(INVALIDATION_FLAG_STYLES);
491 }
492
493 /**
494 * @private
495 */
496 protected var _disabledColor: uint = 0x999999;
497
498 /**
499 * Specifies text color when the component is disabled as a number
500 * containing three 8-bit RGB components.
501 *
502 * <p>In the following example, the text color is changed:</p>
503 *
504 * <listing version="3.0">
505 * textEditor.isEnabled = false;
506 * textEditor.disabledColor = 0xff9900;</listing>
507 *
508 * @default 0x999999
509 *
510 * @see #disabledColor
511 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#color Full description of flash.text.StageText.color in Adobe's Flash Platform API Reference
512 */
513 public function get disabledColor(): uint {
514 return this._disabledColor;
515 }
516
517 /**
518 * @private
519 */
520 public function set disabledColor(value: uint): void {
521 if (this._disabledColor == value) {
522 return;
523 }
524 this._disabledColor = value;
525 this.invalidate(INVALIDATION_FLAG_STYLES);
526 }
527
528 /**
529 * @private
530 */
531 protected var _displayAsPassword: Boolean = false;
532
533 /**
534 * <p>This property is managed by the <code>TextInput</code>.</p>
535 *
536 * @copy feathers.controls.TextInput#displayAsPassword
537 *
538 * @see feathers.controls.TextInput#displayAsPassword
539 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#displayAsPassword Full description of flash.text.StageText.displayAsPassword in Adobe's Flash Platform API Reference
540 */
541 public function get displayAsPassword(): Boolean {
542 return this._displayAsPassword;
543 }
544
545 /**
546 * @private
547 */
548 public function set displayAsPassword(value: Boolean): void {
549 if (this._displayAsPassword == value) {
550 return;
551 }
552 this._displayAsPassword = value;
553 this.invalidate(INVALIDATION_FLAG_STYLES);
554 }
555
556 /**
557 * @private
558 */
559 protected var _isEditable: Boolean = true;
560
561 /**
562 * <p>This property is managed by the <code>TextInput</code>.</p>
563 *
564 * @copy feathers.controls.TextInput#isEditable
565 *
566 * @see feathers.controls.TextInput#isEditable
567 */
568 public function get isEditable(): Boolean {
569 return this._isEditable;
570 }
571
572 /**
573 * @private
574 */
575 public function set isEditable(value: Boolean): void {
576 if (this._isEditable == value) {
577 return;
578 }
579 this._isEditable = value;
580 this.invalidate(INVALIDATION_FLAG_STYLES);
581 }
582
583 /**
584 * @private
585 */
586 protected var _isSelectable: Boolean = true;
587
588 /**
589 * <p>This property is managed by the <code>TextInput</code>.</p>
590 *
591 * @copy feathers.controls.TextInput#isSelectable
592 *
593 * @see feathers.controls.TextInput#isSelectable
594 */
595 public function get isSelectable(): Boolean {
596 return this._isEditable;
597 }
598
599 /**
600 * @private
601 */
602 public function set isSelectable(value: Boolean): void {
603 if (this._isSelectable == value) {
604 return;
605 }
606 this._isSelectable = value;
607 this.invalidate(INVALIDATION_FLAG_STYLES);
608 }
609
610 /**
611 * @inheritDoc
612 *
613 * @default true
614 */
615 public function get setTouchFocusOnEndedPhase(): Boolean {
616 return true;
617 }
618
619 /**
620 * @private
621 */
622 protected var _fontFamily: String = null;
623
624 /**
625 * Indicates the name of the current font family. A value of null
626 * indicates the system default.
627 *
628 * <p>In the following example, the font family is changed:</p>
629 *
630 * <listing version="3.0">
631 * textEditor.fontFamily = "Source Sans Pro";</listing>
632 *
633 * @default null
634 *
635 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#fontFamily Full description of flash.text.StageText.fontFamily in Adobe's Flash Platform API Reference
636 */
637 public function get fontFamily(): String {
638 return this._fontFamily;
639 }
640
641 /**
642 * @private
643 */
644 public function set fontFamily(value: String): void {
645 if (this._fontFamily == value) {
646 return;
647 }
648 this._fontFamily = value;
649 this.invalidate(INVALIDATION_FLAG_STYLES);
650 }
651
652 /**
653 * @private
654 */
655 protected var _fontPosture: String = FontPosture.NORMAL;
656
657 /**
658 * Specifies the font posture, using constants defined in the
659 * <code>flash.text.engine.FontPosture</code> class.
660 *
661 * <p>In the following example, the font posture is changed:</p>
662 *
663 * <listing version="3.0">
664 * textEditor.fontPosture = FontPosture.ITALIC;</listing>
665 *
666 * @default flash.text.engine.FontPosture.NORMAL
667 *
668 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#fontPosture Full description of flash.text.StageText.fontPosture in Adobe's Flash Platform API Reference
669 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/engine/FontPosture.html flash.text.engine.FontPosture
670 */
671 public function get fontPosture(): String {
672 return this._fontPosture;
673 }
674
675 /**
676 * @private
677 */
678 public function set fontPosture(value: String): void {
679 if (this._fontPosture == value) {
680 return;
681 }
682 this._fontPosture = value;
683 this.invalidate(INVALIDATION_FLAG_STYLES);
684 }
685
686 /**
687 * @private
688 */
689 protected var _fontSize: int = 12;
690
691 /**
692 * The size in pixels for the current font family.
693 *
694 * <p>In the following example, the font size is increased to 16 pixels:</p>
695 *
696 * <listing version="3.0">
697 * textEditor.fontSize = 16;</listing>
698 *
699 * @default 12
700 *
701 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#fontSize Full description of flash.text.StageText.fontSize in Adobe's Flash Platform API Reference
702 */
703 public function get fontSize(): int {
704 return this._fontSize;
705 }
706
707 /**
708 * @private
709 */
710 public function set fontSize(value: int): void {
711 if (this._fontSize == value) {
712 return;
713 }
714 this._fontSize = value;
715 this.invalidate(INVALIDATION_FLAG_STYLES);
716 }
717
718 /**
719 * @private
720 */
721 protected var _fontWeight: String = FontWeight.NORMAL;
722
723 /**
724 * Specifies the font weight, using constants defined in the
725 * <code>flash.text.engine.FontWeight</code> class.
726 *
727 * <p>In the following example, the font weight is changed to bold:</p>
728 *
729 * <listing version="3.0">
730 * textEditor.fontWeight = FontWeight.BOLD;</listing>
731 *
732 * @default flash.text.engine.FontWeight.NORMAL
733 *
734 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#fontWeight Full description of flash.text.StageText.fontWeight in Adobe's Flash Platform API Reference
735 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/engine/FontWeight.html flash.text.engine.FontWeight
736 */
737 public function get fontWeight(): String {
738 return this._fontWeight;
739 }
740
741 /**
742 * @private
743 */
744 public function set fontWeight(value: String): void {
745 if (this._fontWeight == value) {
746 return;
747 }
748 this._fontWeight = value;
749 this.invalidate(INVALIDATION_FLAG_STYLES);
750 }
751
752 /**
753 * @private
754 */
755 protected var _locale: String = "en";
756
757 /**
758 * Indicates the locale of the text. <code>StageText</code> uses the
759 * standard locale identifiers. For example <code>"en"</code>,
760 * <code>"en_US"</code> and <code>"en-US"</code> are all English.
761 *
762 * <p>In the following example, the locale is changed to Russian:</p>
763 *
764 * <listing version="3.0">
765 * textEditor.locale = "ru";</listing>
766 *
767 * @default "en"
768 *
769 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#locale Full description of flash.text.StageText.locale in Adobe's Flash Platform API Reference
770 */
771 public function get locale(): String {
772 return this._locale;
773 }
774
775 /**
776 * @private
777 */
778 public function set locale(value: String): void {
779 if (this._locale == value) {
780 return;
781 }
782 this._locale = value;
783 this.invalidate(INVALIDATION_FLAG_STYLES);
784 }
785
786 /**
787 * @private
788 */
789 protected var _maxChars: int = 0;
790
791 /**
792 * <p>This property is managed by the <code>TextInput</code>.</p>
793 *
794 * @copy feathers.controls.TextInput#maxChars
795 *
796 * @see feathers.controls.TextInput#maxChars
797 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#maxChars Full description of flash.text.StageText.maxChars in Adobe's Flash Platform API Reference
798 */
799 public function get maxChars(): int {
800 return this._maxChars;
801 }
802
803 /**
804 * @private
805 */
806 public function set maxChars(value: int): void {
807 if (this._maxChars == value) {
808 return;
809 }
810 this._maxChars = value;
811 this.invalidate(INVALIDATION_FLAG_STYLES);
812 }
813
814 /**
815 * @private
816 */
817 protected var _multiline: Boolean = false;
818
819 /**
820 * Indicates whether the StageText object can display more than one line
821 * of text. This property is configurable after the text editor is
822 * created, unlike a regular <code>StageText</code> instance. The text
823 * editor will dispose and recreate its internal <code>StageText</code>
824 * instance if the value of the <code>multiline</code> property is
825 * changed after the <code>StageText</code> is initially created.
826 *
827 * <p>In the following example, multiline is enabled:</p>
828 *
829 * <listing version="3.0">
830 * textEditor.multiline = true;</listing>
831 *
832 * When setting this property to <code>true</code>, it is recommended
833 * that the text input's <code>verticalAlign</code> property is set to
834 * <code>TextInput.VERTICAL_ALIGN_JUSTIFY</code>.
835 *
836 * @default false
837 *
838 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#multiline Full description of flash.text.StageText.multiline in Adobe's Flash Platform API Reference
839 */
840 public function get multiline(): Boolean {
841 return this._multiline;
842 }
843
844 /**
845 * @private
846 */
847 public function set multiline(value: Boolean): void {
848 if (this._multiline == value) {
849 return;
850 }
851 this._multiline = value;
852 this.invalidate(INVALIDATION_FLAG_STYLES);
853 }
854
855 /**
856 * @private
857 */
858 protected var _restrict: String;
859
860 /**
861 * <p>This property is managed by the <code>TextInput</code>.</p>
862 *
863 * @copy feathers.controls.TextInput#restrict
864 *
865 * @see feathers.controls.TextInput#restrict
866 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#restrict Full description of flash.text.StageText.restrict in Adobe's Flash Platform API Reference
867 */
868 public function get restrict(): String {
869 return this._restrict;
870 }
871
872 /**
873 * @private
874 */
875 public function set restrict(value: String): void {
876 if (this._restrict == value) {
877 return;
878 }
879 this._restrict = value;
880 this.invalidate(INVALIDATION_FLAG_STYLES);
881 }
882
883 /**
884 * @private
885 */
886 protected var _returnKeyLabel: String = "default";
887
888 /**
889 * Indicates the label on the Return key for devices that feature a soft
890 * keyboard. The available values are constants defined in the
891 * <code>flash.text.ReturnKeyLabel</code> class. This property is only a
892 * hint to the underlying platform, because not all devices and
893 * operating systems support this functionality.
894 *
895 * <p>In the following example, the return key label is changed:</p>
896 *
897 * <listing version="3.0">
898 * textEditor.returnKeyLabel = ReturnKeyLabel.GO;</listing>
899 *
900 * @default flash.text.ReturnKeyLabel.DEFAULT
901 *
902 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#returnKeyLabel Full description of flash.text.StageText.returnKeyLabel in Adobe's Flash Platform API Reference
903 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/ReturnKeyLabel.html flash.text.ReturnKeyLabel
904 */
905 public function get returnKeyLabel(): String {
906 return this._returnKeyLabel;
907 }
908
909 /**
910 * @private
911 */
912 public function set returnKeyLabel(value: String): void {
913 if (this._returnKeyLabel == value) {
914 return;
915 }
916 this._returnKeyLabel = value;
917 this.invalidate(INVALIDATION_FLAG_STYLES);
918 }
919
920 /**
921 * @private
922 */
923 protected var _softKeyboardType: String = "default";
924
925 /**
926 * Controls the appearance of the soft keyboard. Valid values are
927 * defined as constants in the <code>flash.text.SoftKeyboardType</code>
928 * class. This property is only a hint to the underlying platform,
929 * because not all devices and operating systems support this
930 * functionality.
931 *
932 * <p>In the following example, the soft keyboard type is changed:</p>
933 *
934 * <listing version="3.0">
935 * textEditor.softKeyboardType = SoftKeyboardType.NUMBER;</listing>
936 *
937 * @default flash.text.SoftKeyboardType.DEFAULT
938 *
939 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#softKeyboardType Full description of flash.text.StageText.softKeyboardType in Adobe's Flash Platform API Reference
940 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/SoftKeyboardType.html flash.text.SoftKeyboardType
941 */
942 public function get softKeyboardType(): String {
943 return this._softKeyboardType;
944 }
945
946 /**
947 * @private
948 */
949 public function set softKeyboardType(value: String): void {
950 if (this._softKeyboardType == value) {
951 return;
952 }
953 this._softKeyboardType = value;
954 this.invalidate(INVALIDATION_FLAG_STYLES);
955 }
956
957 /**
958 * @private
959 */
960 protected var _textAlign: String = TextFormatAlign.START;
961
962 /**
963 * Indicates the paragraph alignment. Valid values are defined as
964 * constants in the <code>flash.text.TextFormatAlign</code> class.
965 *
966 * <p>In the following example, the text is centered:</p>
967 *
968 * <listing version="3.0">
969 * textEditor.textAlign = TextFormatAlign.CENTER;</listing>
970 *
971 * @default flash.text.TextFormatAlign.START
972 *
973 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/StageText.html#textAlign Full description of flash.text.StageText.textAlign in Adobe's Flash Platform API Reference
974 * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/TextFormatAlign.html flash.text.TextFormatAlign
975 */
976 public function get textAlign(): String {
977 return this._textAlign;
978 }
979
980 /**
981 * @private
982 */
983 public function set textAlign(value: String): void {
984 if (this._textAlign == value) {
985 return;
986 }
987 this._textAlign = value;
988 this.invalidate(INVALIDATION_FLAG_STYLES);
989 }
990
991 /**
992 * @private
993 */
994 protected var _lastGlobalScaleX: Number = 0;
995
996 /**
997 * @private
998 */
999 protected var _lastGlobalScaleY: Number = 0;
1000
1001 /**
1002 * @private
1003 */
1004 protected var _updateSnapshotOnScaleChange: Boolean = false;
1005
1006 /**
1007 * Refreshes the texture snapshot every time that the text editor is
1008 * scaled. Based on the scale in global coordinates, so scaling the
1009 * parent will require a new snapshot.
1010 *
1011 * <p>Warning: setting this property to true may result in reduced
1012 * performance because every change of the scale requires uploading a
1013 * new texture to the GPU. Use with caution. Consider setting this
1014 * property to false temporarily during animations that modify the
1015 * scale.</p>
1016 *
1017 * <p>In the following example, the snapshot will be updated when the
1018 * text editor is scaled:</p>
1019 *
1020 * <listing version="3.0">
1021 * textEditor.updateSnapshotOnScaleChange = true;</listing>
1022 *
1023 * @default false
1024 */
1025 public function get updateSnapshotOnScaleChange(): Boolean {
1026 return this._updateSnapshotOnScaleChange;
1027 }
1028
1029 /**
1030 * @private
1031 */
1032 public function set updateSnapshotOnScaleChange(value: Boolean): void {
1033 if (this._updateSnapshotOnScaleChange == value) {
1034 return;
1035 }
1036 this._updateSnapshotOnScaleChange = value;
1037 this.invalidate(INVALIDATION_FLAG_DATA);
1038 }
1039
1040 /**
1041 * @private
1042 */
1043 override public function dispose(): void {
1044 if (this._measureTextField) {
1045 Starling.current.nativeStage.removeChild(this._measureTextField);
1046 this._measureTextField = null;
1047 }
1048
1049 if (this.stageText) {
1050 this.disposeStageText();
1051 }
1052
1053 if (this.textSnapshot) {
1054 //avoid the need to call dispose(). we'll create a new snapshot
1055 //when the renderer is added to stage again.
1056 this.textSnapshot.texture.dispose();
1057 this.removeChild(this.textSnapshot, true);
1058 this.textSnapshot = null;
1059 }
1060
1061 super.dispose();
1062 }
1063
1064 /**
1065 * @private
1066 */
1067 override public function render(support: RenderSupport, parentAlpha: Number): void {
1068 if (this.textSnapshot && this._updateSnapshotOnScaleChange) {
1069 this.getTransformationMatrix(this.stage, HELPER_MATRIX);
1070 if (matrixToScaleX(HELPER_MATRIX) != this._lastGlobalScaleX || matrixToScaleY(HELPER_MATRIX) != this._lastGlobalScaleY) {
1071 //the snapshot needs to be updated because the scale has
1072 //changed since the last snapshot was taken.
1073 this.invalidate(INVALIDATION_FLAG_SIZE);
1074 this.validate();
1075 }
1076 }
1077
1078 //we'll skip this if the text field isn't visible to avoid running
1079 //that code every frame.
1080 if (this.stageText && this.stageText.visible) {
1081 this.refreshViewPortAndFontSize();
1082 }
1083
1084 if (this.textSnapshot) {
1085 this.positionSnapshot();
1086 }
1087
1088 super.render(support, parentAlpha);
1089 }
1090
1091 /**
1092 * @inheritDoc
1093 */
1094 public function setFocus(position: Point = null): void {
1095 //setting the editable property of a StageText to false seems to be
1096 //ignored on Android, so this is the workaround
1097 if (!this._isEditable && SystemUtil.platform === "AND") {
1098 return;
1099 }
1100 if (!this._isEditable && !this._isSelectable) {
1101 return;
1102 }
1103 if (this.stage && !this.stageText.stage) {
1104 this.stageText.stage = Starling.current.nativeStage;
1105 }
1106 if (this.stageText && this._stageTextIsComplete) {
1107 if (position) {
1108 var positionX: Number = position.x + 2;
1109 var positionY: Number = position.y + 2;
1110 if (positionX < 0) {
1111 this._pendingSelectionBeginIndex = this._pendingSelectionEndIndex = 0;
1112 } else {
1113 this._pendingSelectionBeginIndex = this._measureTextField.getCharIndexAtPoint(positionX, positionY);
1114 if (this._pendingSelectionBeginIndex < 0) {
1115 if (this._multiline) {
1116 var lineIndex: int = int(positionY / this._measureTextField.getLineMetrics(0).height);
1117 try {
1118 this._pendingSelectionBeginIndex = this._measureTextField.getLineOffset(lineIndex) + this._measureTextField.getLineLength(lineIndex);
1119 if (this._pendingSelectionBeginIndex != this._text.length) {
1120 this._pendingSelectionBeginIndex--;
1121 }
1122 } catch (error: Error) {
1123 //we may be checking for a line beyond the
1124 //end that doesn't exist
1125 this._pendingSelectionBeginIndex = this._text.length;
1126 }
1127 } else {
1128 this._pendingSelectionBeginIndex = this._measureTextField.getCharIndexAtPoint(positionX, this._measureTextField.getLineMetrics(0).ascent / 2);
1129 if (this._pendingSelectionBeginIndex < 0) {
1130 this._pendingSelectionBeginIndex = this._text.length;
1131 }
1132 }
1133 } else {
1134 var bounds: Rectangle = this._measureTextField.getCharBoundaries(this._pendingSelectionBeginIndex);
1135 var boundsX: Number = bounds.x;
1136 if (bounds && (boundsX + bounds.width - positionX) < (positionX - boundsX)) {
1137 this._pendingSelectionBeginIndex++;
1138 }
1139 }
1140 this._pendingSelectionEndIndex = this._pendingSelectionBeginIndex;
1141 }
1142 } else {
1143 this._pendingSelectionBeginIndex = this._pendingSelectionEndIndex = -1;
1144 }
1145 this.stageText.visible = true;
1146 this.stageText.assignFocus();
1147 } else {
1148 this._isWaitingToSetFocus = true;
1149 }
1150 }
1151
1152 /**
1153 * @inheritDoc
1154 */
1155 public function clearFocus(): void {
1156 if (!this._stageTextHasFocus) {
1157 return;
1158 }
1159 //setting the focus to Starling.current.nativeStage doesn't work
1160 //here, so we need to use null. on Android, if we give focus to the
1161 //nativeStage, focus will be removed from the StageText, but the
1162 //soft keyboard will incorrectly remain open.
1163 Starling.current.nativeStage.focus = null;
1164 }
1165
1166 /**
1167 * @inheritDoc
1168 */
1169 public function selectRange(beginIndex: int, endIndex: int): void {
1170 if (this._stageTextIsComplete && this.stageText) {
1171 this._pendingSelectionBeginIndex = -1;
1172 this._pendingSelectionEndIndex = -1;
1173 this.stageText.selectRange(beginIndex, endIndex);
1174 } else {
1175 this._pendingSelectionBeginIndex = beginIndex;
1176 this._pendingSelectionEndIndex = endIndex;
1177 }
1178 }
1179
1180 /**
1181 * @inheritDoc
1182 */
1183 public function measureText(result: Point = null): Point {
1184 if (!result) {
1185 result = new Point();
1186 }
1187
1188 var needsWidth: Boolean = this.explicitWidth !== this.explicitWidth; //isNaN
1189 var needsHeight: Boolean = this.explicitHeight !== this.explicitHeight; //isNaN
1190 if (!needsWidth && !needsHeight) {
1191 result.x = this.explicitWidth;
1192 result.y = this.explicitHeight;
1193 return result;
1194 }
1195
1196 //if a parent component validates before we're added to the stage,
1197 //measureText() may be called before initialization, so we need to
1198 //force it.
1199 if (!this._isInitialized) {
1200 this.initializeInternal();
1201 }
1202
1203 var stylesInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_STYLES);
1204 var dataInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_DATA);
1205
1206 if (stylesInvalid || dataInvalid) {
1207 this.refreshMeasureProperties();
1208 }
1209
1210 result = this.measure(result);
1211
1212 return result;
1213 }
1214
1215 /**
1216 * @private
1217 */
1218 override protected function initialize(): void {
1219 if (this._measureTextField && !this._measureTextField.parent) {
1220 Starling.current.nativeStage.addChild(this._measureTextField);
1221 } else if (!this._measureTextField) {
1222 this._measureTextField = new TextField();
1223 this._measureTextField.visible = false;
1224 this._measureTextField.mouseEnabled = this._measureTextField.mouseWheelEnabled = false;
1225 this._measureTextField.autoSize = TextFieldAutoSize.LEFT;
1226 this._measureTextField.multiline = false;
1227 this._measureTextField.wordWrap = false;
1228 this._measureTextField.embedFonts = false;
1229 this._measureTextField.defaultTextFormat = new TextFormat(null, 11, 0x000000, false, false, false);
1230 Starling.current.nativeStage.addChild(this._measureTextField);
1231 }
1232
1233 this.createStageText();
1234 }
1235
1236 /**
1237 * @private
1238 */
1239 override protected function draw(): void {
1240 var sizeInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_SIZE);
1241
1242 this.commit();
1243
1244 sizeInvalid = this.autoSizeIfNeeded() || sizeInvalid;
1245
1246 this.layout(sizeInvalid);
1247 }
1248
1249 /**
1250 * @private
1251 */
1252 protected function commit(): void {
1253 var stateInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_STATE);
1254 var stylesInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_STYLES);
1255 var dataInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_DATA);
1256
1257 if (stylesInvalid || dataInvalid) {
1258 this.refreshMeasureProperties();
1259 }
1260
1261 var oldIgnoreStageTextChanges: Boolean = this._ignoreStageTextChanges;
1262 this._ignoreStageTextChanges = true;
1263 if (stateInvalid || stylesInvalid) {
1264 this.refreshStageTextProperties();
1265 }
1266
1267 if (dataInvalid) {
1268 if (this.stageText.text != this._text) {
1269 if (this._pendingSelectionBeginIndex < 0) {
1270 this._pendingSelectionBeginIndex = this.stageText.selectionActiveIndex;
1271 this._pendingSelectionEndIndex = this.stageText.selectionAnchorIndex;
1272 }
1273 this.stageText.text = this._text;
1274 }
1275 }
1276 this._ignoreStageTextChanges = oldIgnoreStageTextChanges;
1277
1278 if (stylesInvalid || stateInvalid) {
1279 this.stageText.editable = this._isEditable && this._isEnabled;
1280 }
1281 }
1282
1283 /**
1284 * @private
1285 */
1286 protected function measure(result: Point = null): Point {
1287 if (!result) {
1288 result = new Point();
1289 }
1290
1291 var needsWidth: Boolean = this.explicitWidth !== this.explicitWidth; //isNaN
1292 var needsHeight: Boolean = this.explicitHeight !== this.explicitHeight; //isNaN
1293
1294 this._measureTextField.autoSize = TextFieldAutoSize.LEFT;
1295
1296 var newWidth: Number = this.explicitWidth;
1297 if (needsWidth) {
1298 newWidth = this._measureTextField.textWidth;
1299 if (newWidth < this._minWidth) {
1300 newWidth = this._minWidth;
1301 } else if (newWidth > this._maxWidth) {
1302 newWidth = this._maxWidth;
1303 }
1304 }
1305
1306 //the +4 is accounting for the TextField gutter
1307 this._measureTextField.width = newWidth + 4;
1308 var newHeight: Number = this.explicitHeight;
1309 if (needsHeight) {
1310 //since we're measuring with TextField, but rendering with
1311 //StageText, we're using height instead of textHeight here to be
1312 //sure that the measured size is on the larger side, in case the
1313 //rendered size is actually bigger than textHeight
1314 //if only StageText had an API for text measurement, we wouldn't
1315 //be in this mess...
1316 newHeight = this._measureTextField.height;
1317 if (newHeight < this._minHeight) {
1318 newHeight = this._minHeight;
1319 } else if (newHeight > this._maxHeight) {
1320 newHeight = this._maxHeight;
1321 }
1322 }
1323
1324 this._measureTextField.autoSize = TextFieldAutoSize.NONE;
1325
1326 //put the width and height back just in case we measured without
1327 //a full validation
1328 //the +4 is accounting for the TextField gutter
1329 this._measureTextField.width = this.actualWidth + 4;
1330 this._measureTextField.height = this.actualHeight;
1331
1332 result.x = newWidth;
1333 result.y = newHeight;
1334
1335 return result;
1336 }
1337
1338 /**
1339 * @private
1340 */
1341 protected function layout(sizeInvalid: Boolean): void {
1342 var stateInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_STATE);
1343 var stylesInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_STYLES);
1344 var dataInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_DATA);
1345 var skinInvalid: Boolean = this.isInvalid(INVALIDATION_FLAG_SKIN);
1346
1347 if (sizeInvalid || stylesInvalid || skinInvalid || stateInvalid) {
1348 this.refreshViewPortAndFontSize();
1349 this.refreshMeasureTextFieldDimensions()
1350 var viewPort: Rectangle = this.stageText.viewPort;
1351 var textureRoot: ConcreteTexture = this.textSnapshot ? this.textSnapshot.texture.root : null;
1352 this._needsNewTexture = this._needsNewTexture || !this.textSnapshot ||
1353 textureRoot.scale != Starling.contentScaleFactor ||
1354 viewPort.width != textureRoot.width || viewPort.height != textureRoot.height;
1355 }
1356
1357 if (!this._stageTextHasFocus && (stateInvalid || stylesInvalid || dataInvalid || sizeInvalid || this._needsNewTexture)) {
1358 var hasText: Boolean = this._text.length > 0;
1359 if (hasText) {
1360 this.refreshSnapshot();
1361 }
1362 if (this.textSnapshot) {
1363 this.textSnapshot.visible = !this._stageTextHasFocus;
1364 this.textSnapshot.alpha = hasText ? 1 : 0;
1365 }
1366 this.stageText.visible = false;
1367 }
1368
1369 this.doPendingActions();
1370 }
1371
1372 /**
1373 * If the component's dimensions have not been set explicitly, it will
1374 * measure its content and determine an ideal size for itself. If the
1375 * <code>explicitWidth</code> or <code>explicitHeight</code> member
1376 * variables are set, those value will be used without additional
1377 * measurement. If one is set, but not the other, the dimension with the
1378 * explicit value will not be measured, but the other non-explicit
1379 * dimension will still need measurement.
1380 *
1381 * <p>Calls <code>setSizeInternal()</code> to set up the
1382 * <code>actualWidth</code> and <code>actualHeight</code> member
1383 * variables used for layout.</p>
1384 *
1385 * <p>Meant for internal use, and subclasses may override this function
1386 * with a custom implementation.</p>
1387 */
1388 protected function autoSizeIfNeeded(): Boolean {
1389 var needsWidth: Boolean = this.explicitWidth !== this.explicitWidth; //isNaN
1390 var needsHeight: Boolean = this.explicitHeight !== this.explicitHeight; //isNaN
1391 if (!needsWidth && !needsHeight) {
1392 return false;
1393 }
1394
1395 this.measure(HELPER_POINT);
1396 return this.setSizeInternal(HELPER_POINT.x, HELPER_POINT.y, false);
1397 }
1398
1399 /**
1400 * @private
1401 */
1402 protected function refreshMeasureProperties(): void {
1403 var nativeScaleFactor: Number = 1;
1404 if (Starling.current.supportHighResolutions) {
1405 nativeScaleFactor = Starling.current.nativeStage.contentsScaleFactor;
1406 }
1407
1408 this._measureTextField.displayAsPassword = this._displayAsPassword;
1409 this._measureTextField.maxChars = this._maxChars;
1410 this._measureTextField.restrict = this._restrict;
1411 this._measureTextField.multiline = this._measureTextField.wordWrap = this._multiline;
1412
1413 var format: TextFormat = this._measureTextField.defaultTextFormat;
1414 format.color = this._color;
1415 format.font = this._fontFamily;
1416 format.italic = this._fontPosture == FontPosture.ITALIC;
1417 format.size = this._fontSize * nativeScaleFactor;
1418 format.bold = this._fontWeight == FontWeight.BOLD;
1419 var alignValue: String = this._textAlign;
1420 if (alignValue == TextFormatAlign.START) {
1421 alignValue = TextFormatAlign.LEFT;
1422 } else if (alignValue == TextFormatAlign.END) {
1423 alignValue = TextFormatAlign.RIGHT;
1424 }
1425 format.align = alignValue;
1426 this._measureTextField.defaultTextFormat = format;
1427 this._measureTextField.setTextFormat(format);
1428 if (this._text.length == 0) {
1429 this._measureTextField.text = " ";
1430 } else {
1431 this._measureTextField.text = this._text;
1432 }
1433 }
1434
1435 /**
1436 * @private
1437 */
1438 protected function refreshStageTextProperties(): void {
1439 if (this.stageText.multiline != this._multiline) {
1440 if (this.stageText) {
1441 this.disposeStageText();
1442 }
1443 this.createStageText();
1444 }
1445
1446 this.stageText.autoCapitalize = this._autoCapitalize;
1447 this.stageText.autoCorrect = this._autoCorrect;
1448 if (this._isEnabled) {
1449 this.stageText.color = this._color;
1450 } else {
1451 this.stageText.color = this._disabledColor;
1452 }
1453 this.stageText.displayAsPassword = this._displayAsPassword;
1454 this.stageText.fontFamily = this._fontFamily;
1455 this.stageText.fontPosture = this._fontPosture;
1456 this.stageText.fontWeight = this._fontWeight;
1457 this.stageText.locale = this._locale;
1458 this.stageText.maxChars = this._maxChars;
1459 this.stageText.restrict = this._restrict;
1460 this.stageText.returnKeyLabel = this._returnKeyLabel;
1461 this.stageText.softKeyboardType = this._softKeyboardType;
1462 this.stageText.textAlign = this._textAlign;
1463 }
1464
1465 /**
1466 * @private
1467 */
1468 protected function doPendingActions(): void {
1469 if (this._isWaitingToSetFocus) {
1470 this._isWaitingToSetFocus = false;
1471 this.setFocus();
1472 }
1473 if (this._pendingSelectionBeginIndex >= 0) {
1474 var startIndex: int = this._pendingSelectionBeginIndex;
1475 var endIndex: int = (this._pendingSelectionEndIndex < 0) ? this._pendingSelectionBeginIndex : this._pendingSelectionEndIndex;
1476 this._pendingSelectionBeginIndex = -1;
1477 this._pendingSelectionEndIndex = -1;
1478 if (this.stageText.selectionAnchorIndex != startIndex || this.stageText.selectionActiveIndex != endIndex) {
1479 //if the same range is already selected, don't try to do it
1480 //again because on iOS, if the StageText is multiline, this
1481 //will cause the clipboard menu to appear when it shouldn't.
1482 this.selectRange(startIndex, endIndex);
1483 }
1484 }
1485 }
1486
1487 /**
1488 * @private
1489 */
1490 protected function texture_onRestore(): void {
1491 if (this.textSnapshot.texture.scale != Starling.contentScaleFactor) {
1492 //if we've changed between scale factors, we need to recreate
1493 //the texture to match the new scale factor.
1494 this.invalidate(INVALIDATION_FLAG_SIZE);
1495 } else {
1496 this.refreshSnapshot();
1497 if (this.textSnapshot) {
1498 this.textSnapshot.visible = !this._stageTextHasFocus;
1499 this.textSnapshot.alpha = this._text.length > 0 ? 1 : 0;
1500 }
1501 if (!this._stageTextHasFocus) {
1502 this.stageText.visible = false;
1503 }
1504 }
1505 }
1506
1507 /**
1508 * @private
1509 */
1510 protected function refreshSnapshot(): void {
1511 //StageText's stage property cannot be null when we call
1512 //drawViewPortToBitmapData()
1513 if (this.stage && !this.stageText.stage) {
1514 this.stageText.stage = Starling.current.nativeStage;
1515 }
1516 if (!this.stageText.stage) {
1517 //we need to keep a flag active so that the snapshot will be
1518 //refreshed after the text editor is added to the stage
1519 this.invalidate(INVALIDATION_FLAG_DATA);
1520 return;
1521 }
1522 var viewPort: Rectangle = this.stageText.viewPort;
1523 if (viewPort.width == 0 || viewPort.height == 0) {
1524 return;
1525 }
1526 var nativeScaleFactor: Number = 1;
1527 if (Starling.current.supportHighResolutions) {
1528 nativeScaleFactor = Starling.current.nativeStage.contentsScaleFactor;
1529 }
1530 //StageText sucks because it requires that the BitmapData's width
1531 //and height exactly match its view port width and height.
1532 //(may be doubled on Retina Mac)
1533 try {
1534 var bitmapData: BitmapData = new BitmapData(viewPort.width * nativeScaleFactor, viewPort.height * nativeScaleFactor, true, 0x00ff00ff);
1535 this.stageText.drawViewPortToBitmapData(bitmapData);
1536 } catch (error: Error) {
1537 //drawing stage text to the bitmap data at double size may fail
1538 //on runtime versions less than 15, so fall back to using a
1539 //snapshot that is half size. it's not ideal, but better than
1540 //nothing.
1541 bitmapData.dispose();
1542 bitmapData = new BitmapData(viewPort.width, viewPort.height, true, 0x00ff00ff);
1543 this.stageText.drawViewPortToBitmapData(bitmapData);
1544 }
1545
1546 var newTexture: Texture;
1547 if (!this.textSnapshot || this._needsNewTexture) {
1548 var scaleFactor: Number = Starling.contentScaleFactor;
1549 //skip Texture.fromBitmapData() because we don't want
1550 //it to create an onRestore function that will be
1551 //immediately discarded for garbage collection.
1552 newTexture = Texture.empty(bitmapData.width / scaleFactor, bitmapData.height / scaleFactor,
1553 true, false, false, scaleFactor);
1554 newTexture.root.uploadBitmapData(bitmapData);
1555 newTexture.root.onRestore = texture_onRestore;
1556 }
1557 if (!this.textSnapshot) {
1558 this.textSnapshot = new Image(newTexture);
1559 this.addChild(this.textSnapshot);
1560 } else {
1561 if (this._needsNewTexture) {
1562 this.textSnapshot.texture.dispose();
1563 this.textSnapshot.texture = newTexture;
1564 this.textSnapshot.readjustSize();
1565 } else {
1566 //this is faster, if we haven't resized the bitmapdata
1567 var existingTexture: Texture = this.textSnapshot.texture;
1568 existingTexture.root.uploadBitmapData(bitmapData);
1569 }
1570 }
1571 this.getTransformationMatrix(this.stage, HELPER_MATRIX);
1572 var globalScaleX: Number = matrixToScaleX(HELPER_MATRIX);
1573 var globalScaleY: Number = matrixToScaleY(HELPER_MATRIX);
1574 if (this._updateSnapshotOnScaleChange) {
1575 this.textSnapshot.scaleX = 1 / globalScaleX;
1576 this.textSnapshot.scaleY = 1 / globalScaleY;
1577 this._lastGlobalScaleX = globalScaleX;
1578 this._lastGlobalScaleY = globalScaleY;
1579 } else {
1580 this.textSnapshot.scaleX = 1;
1581 this.textSnapshot.scaleY = 1;
1582 }
1583 if (nativeScaleFactor > 1 && bitmapData.width == viewPort.width) {
1584 //when we fall back to using a snapshot that is half size on
1585 //older runtimes, we need to scale it up.
1586 this.textSnapshot.scaleX *= nativeScaleFactor;
1587 this.textSnapshot.scaleY *= nativeScaleFactor;
1588 }
1589 bitmapData.dispose();
1590 this._needsNewTexture = false;
1591 }
1592
1593 /**
1594 * @private
1595 */
1596 protected function refreshViewPortAndFontSize(): void {
1597 HELPER_POINT.x = HELPER_POINT.y = 0;
1598 var desktopGutterPositionOffset: Number = 0;
1599 var desktopGutterDimensionsOffset: Number = 0;
1600 if (this._stageTextIsTextField) {
1601 desktopGutterPositionOffset = 2;
1602 desktopGutterDimensionsOffset = 4;
1603 }
1604 this.getTransformationMatrix(this.stage, HELPER_MATRIX);
1605 if (this._stageTextHasFocus || this._updateSnapshotOnScaleChange) {
1606 var globalScaleX: Number = matrixToScaleX(HELPER_MATRIX);
1607 var globalScaleY: Number = matrixToScaleY(HELPER_MATRIX);
1608 var smallerGlobalScale: Number = globalScaleX;
1609 if (globalScaleY < smallerGlobalScale) {
1610 smallerGlobalScale = globalScaleY;
1611 }
1612 } else {
1613 globalScaleX = 1;
1614 globalScaleY = 1;
1615 smallerGlobalScale = 1;
1616 }
1617 if (this.is3D) {
1618 HELPER_MATRIX3D = this.getTransformationMatrix3D(this.stage, HELPER_MATRIX3D);
1619 HELPER_POINT3D = MatrixUtil.transformCoords3D(HELPER_MATRIX3D, -desktopGutterPositionOffset, -desktopGutterPositionOffset, 0, HELPER_POINT3D);
1620 HELPER_POINT.setTo(HELPER_POINT3D.x, HELPER_POINT3D.y);
1621 } else {
1622 MatrixUtil.transformCoords(HELPER_MATRIX, -desktopGutterPositionOffset, -desktopGutterPositionOffset, HELPER_POINT);
1623 }
1624 var nativeScaleFactor: Number = 1;
1625 if (Starling.current.supportHighResolutions) {
1626 nativeScaleFactor = Starling.current.nativeStage.contentsScaleFactor;
1627 }
1628 var scaleFactor: Number = Starling.contentScaleFactor / nativeScaleFactor;
1629 var starlingViewPort: Rectangle = Starling.current.viewPort;
1630 var stageTextViewPort: Rectangle = this.stageText.viewPort;
1631 if (!stageTextViewPort) {
1632 stageTextViewPort = new Rectangle();
1633 }
1634 var heightScaleFactor: Number = starlingViewPort.height/stage.stageHeight;
1635 stageTextViewPort.x = Math.round(starlingViewPort.x + HELPER_POINT.x * scaleFactor);
1636 stageTextViewPort.y = Math.round(starlingViewPort.y + HELPER_POINT.y * heightScaleFactor);
1637 var viewPortWidth: Number = Math.round((this.actualWidth + desktopGutterDimensionsOffset) * scaleFactor * globalScaleX);
1638 if (viewPortWidth < 1 ||
1639 viewPortWidth !== viewPortWidth) //isNaN
1640 {
1641 viewPortWidth = 1;
1642 }
1643 var viewPortHeight: Number = Math.round((this.actualHeight + desktopGutterDimensionsOffset) * heightScaleFactor * globalScaleY);
1644 if (viewPortHeight < 1 ||
1645 viewPortHeight !== viewPortHeight) //isNaN
1646 {
1647 viewPortHeight = 1;
1648 }
1649 stageTextViewPort.width = viewPortWidth;
1650 stageTextViewPort.height = viewPortHeight;
1651 this.stageText.viewPort = stageTextViewPort;
1652
1653 //for some reason, we don't need to account for the native scale factor here
1654 scaleFactor = Starling.contentScaleFactor;
1655 //StageText's fontSize property is an int, so we need to
1656 //specifically avoid using Number here.
1657 var newFontSize: int = this._fontSize * scaleFactor * smallerGlobalScale;
1658 if (this.stageText.fontSize != newFontSize) {
1659 //we need to check if this value has changed because on iOS
1660 //if displayAsPassword is set to true, the new character
1661 //will not be shown if the font size changes. instead, it
1662 //immediately changes to a bullet. (Github issue #881)
1663 this.stageText.fontSize = newFontSize;
1664 }
1665
1666 }
1667
1668 /**
1669 * @private
1670 */
1671 protected function refreshMeasureTextFieldDimensions(): void {
1672 //the +4 is accounting for the TextField gutter
1673 this._measureTextField.width = this.actualWidth + 4;
1674 this._measureTextField.height = this.actualHeight;
1675 }
1676
1677 /**
1678 * @private
1679 */
1680 protected function positionSnapshot(): void {
1681 this.getTransformationMatrix(this.stage, HELPER_MATRIX);
1682 var desktopGutterPositionOffset: Number = 0;
1683 if (this._stageTextIsTextField) {
1684 desktopGutterPositionOffset = 2;
1685 }
1686 this.textSnapshot.x = Math.round(HELPER_MATRIX.tx) - HELPER_MATRIX.tx - desktopGutterPositionOffset;
1687 this.textSnapshot.y = Math.round(HELPER_MATRIX.ty) - HELPER_MATRIX.ty - desktopGutterPositionOffset;
1688 }
1689
1690 /**
1691 * @private
1692 */
1693 protected function disposeStageText(): void {
1694 if (!this.stageText) {
1695 return;
1696 }
1697 this.stageText.removeEventListener(flash.events.Event.CHANGE, stageText_changeHandler);
1698 this.stageText.removeEventListener(KeyboardEvent.KEY_DOWN, stageText_keyDownHandler);
1699 this.stageText.removeEventListener(KeyboardEvent.KEY_UP, stageText_keyUpHandler);
1700 this.stageText.removeEventListener(FocusEvent.FOCUS_IN, stageText_focusInHandler);
1701 this.stageText.removeEventListener(FocusEvent.FOCUS_OUT, stageText_focusOutHandler);
1702 this.stageText.removeEventListener(flash.events.Event.COMPLETE, stageText_completeHandler);
1703 this.stageText.removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, stageText_softKeyboardActivateHandler);
1704 this.stageText.removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, stageText_softKeyboardDeactivateHandler);
1705 this.stageText.stage = null;
1706 this.stageText.dispose();
1707 this.stageText = null;
1708 }
1709
1710 /**
1711 * Creates and adds the <code>stageText</code> instance.
1712 *
1713 * <p>Meant for internal use, and subclasses may override this function
1714 * with a custom implementation.</p>
1715 */
1716 protected function createStageText(): void {
1717 this._stageTextIsComplete = false;
1718 var StageTextType: Class;
1719 var initOptions: Object;
1720 try {
1721 StageTextType = Class(getDefinitionByName("flash.text.StageText"));
1722 var StageTextInitOptionsType: Class = Class(getDefinitionByName("flash.text.StageTextInitOptions"));
1723 initOptions = new StageTextInitOptionsType(this._multiline);
1724 } catch (error: Error) {
1725 StageTextType = StageTextField;
1726 initOptions = {
1727 multiline: this._multiline
1728 };
1729 }
1730 this.stageText = new StageTextType(initOptions);
1731 this.stageText.visible = false;
1732 this.stageText.addEventListener(flash.events.Event.CHANGE, stageText_changeHandler);
1733 this.stageText.addEventListener(KeyboardEvent.KEY_DOWN, stageText_keyDownHandler);
1734 this.stageText.addEventListener(KeyboardEvent.KEY_UP, stageText_keyUpHandler);
1735 this.stageText.addEventListener(FocusEvent.FOCUS_IN, stageText_focusInHandler);
1736 this.stageText.addEventListener(FocusEvent.FOCUS_OUT, stageText_focusOutHandler);
1737 this.stageText.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, stageText_softKeyboardActivateHandler);
1738 this.stageText.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, stageText_softKeyboardDeactivateHandler);
1739 this.stageText.addEventListener(flash.events.Event.COMPLETE, stageText_completeHandler);
1740 this.invalidate();
1741 }
1742
1743 /**
1744 * @private
1745 */
1746 protected function dispatchKeyFocusChangeEvent(event: KeyboardEvent): void {
1747 var starling: Starling = stageToStarling(this.stage);
1748 var focusEvent: FocusEvent = new FocusEvent(FocusEvent.KEY_FOCUS_CHANGE, true, false, null, event.shiftKey, event.keyCode);
1749 starling.nativeStage.dispatchEvent(focusEvent);
1750 }
1751
1752 /**
1753 * @private
1754 */
1755 protected function textEditor_removedFromStageHandler(event: starling.events.Event): void {
1756 //remove this from the stage, if needed
1757 //it will be added back next time we receive focus
1758 this.stageText.stage = null;
1759 }
1760
1761 /**
1762 * @private
1763 */
1764 protected function stageText_changeHandler(event: flash.events.Event): void {
1765 if (this._ignoreStageTextChanges) {
1766 return;
1767 }
1768 this.text = this.stageText.text;
1769 }
1770
1771 /**
1772 * @private
1773 */
1774 protected function stageText_completeHandler(event: flash.events.Event): void {
1775 this.stageText.removeEventListener(flash.events.Event.COMPLETE, stageText_completeHandler);
1776 this.invalidate();
1777
1778 this._stageTextIsComplete = true;
1779 }
1780
1781 /**
1782 * @private
1783 */
1784 protected function stageText_focusInHandler(event: FocusEvent): void {
1785 this._stageTextHasFocus = true;
1786 this.addEventListener(starling.events.Event.ENTER_FRAME, hasFocus_enterFrameHandler);
1787 if (this.textSnapshot) {
1788 this.textSnapshot.visible = false;
1789 }
1790 this.invalidate(INVALIDATION_FLAG_SKIN);
1791 this.dispatchEventWith(FeathersEventType.FOCUS_IN);
1792 }
1793
1794 /**
1795 * @private
1796 */
1797 protected function stageText_focusOutHandler(event: FocusEvent): void {
1798 this._stageTextHasFocus = false;
1799 //since StageText doesn't expose its scroll position, we need to
1800 //set the selection back to the beginning to scroll there. it's a
1801 //hack, but so is everything about StageText.
1802 //in other news, why won't 0,0 work here?
1803 this.stageText.selectRange(1, 1);
1804
1805 this.invalidate(INVALIDATION_FLAG_DATA);
1806 this.invalidate(INVALIDATION_FLAG_SKIN);
1807 this.dispatchEventWith(FeathersEventType.FOCUS_OUT);
1808 }
1809
1810 /**
1811 * @private
1812 */
1813 protected function hasFocus_enterFrameHandler(event: starling.events.Event): void {
1814 if (this._stageTextHasFocus) {
1815 var target: DisplayObject = this;
1816 do {
1817 if (!target.hasVisibleArea) {
1818 this.stageText.stage.focus = null;
1819 break;
1820 }
1821 target = target.parent;
1822 }
1823 while (target)
1824 } else {
1825 this.removeEventListener(starling.events.Event.ENTER_FRAME, hasFocus_enterFrameHandler);
1826 }
1827 }
1828
1829 /**
1830 * @private
1831 */
1832 protected function stageText_keyDownHandler(event: KeyboardEvent): void {
1833 if (!this._multiline && (event.keyCode == Keyboard.ENTER || event.keyCode == Keyboard.NEXT)) {
1834 event.preventDefault();
1835 this.dispatchEventWith(FeathersEventType.ENTER);
1836 } else if (event.keyCode == Keyboard.BACK) {
1837 //even a listener on the stage won't detect the back key press that
1838 //will close the application if the StageText has focus, so we
1839 //always need to prevent it here
1840 event.preventDefault();
1841 Starling.current.nativeStage.focus = Starling.current.nativeStage;
1842 }
1843 if (event.keyCode === Keyboard.TAB && FocusManager.isEnabledForStage(this.stage)) {
1844 event.preventDefault();
1845 this.dispatchKeyFocusChangeEvent(event);
1846 }
1847 }
1848
1849 /**
1850 * @private
1851 */
1852 protected function stageText_keyUpHandler(event: KeyboardEvent): void {
1853 if (!this._multiline && (event.keyCode == Keyboard.ENTER || event.keyCode == Keyboard.NEXT)) {
1854 event.preventDefault();
1855 }
1856 if (event.keyCode === Keyboard.TAB && FocusManager.isEnabledForStage(this.stage)) {
1857 event.preventDefault();
1858 }
1859 }
1860
1861 /**
1862 * @private
1863 */
1864 protected function stageText_softKeyboardActivateHandler(event: SoftKeyboardEvent): void {
1865 this.dispatchEventWith(FeathersEventType.SOFT_KEYBOARD_ACTIVATE, true);
1866 }
1867
1868 /**
1869 * @private
1870 */
1871 protected function stageText_softKeyboardDeactivateHandler(event: SoftKeyboardEvent): void {
1872 this.dispatchEventWith(FeathersEventType.SOFT_KEYBOARD_DEACTIVATE, true);
1873 }
1874 }
1875}