blob: d3b76629fc4e975b2636ae256559189edbb88b50 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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 mx.controls
{
import flash.accessibility.AccessibilityProperties;
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.system.IME;
import flash.system.IMEConversionMode;
import flash.text.StyleSheet;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextLineMetrics;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.EdgeMetrics;
import mx.core.IDataRenderer;
import mx.core.IFlexModuleFactory;
import mx.core.IFontContextComponent;
import mx.core.IIMESupport;
import mx.core.IInvalidating;
import mx.core.IUITextField;
import mx.core.ScrollControlBase;
import mx.core.ScrollPolicy;
import mx.core.UITextField;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import mx.events.ScrollEvent;
import mx.events.ScrollEventDetail;
import mx.events.ScrollEventDirection;
import mx.managers.IFocusManager;
import mx.managers.IFocusManagerComponent;
import mx.managers.ISystemManager;
import mx.managers.SystemManager;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched when text in the TextArea control changes
* through user input.
* This event does not occur if you use data binding or
* ActionScript code to change the text.
*
* <p>Even though the default value of the <code>Event.bubbles</code> property
* is <code>true</code>, this control dispatches the event with
* the <code>Event.bubbles</code> property set to <code>false</code>.</p>
*
* @eventType flash.events.Event.CHANGE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="change", type="flash.events.Event")]
/**
* Dispatched when the <code>data</code> property changes.
*
* <p>When you use a component as an item renderer,
* the <code>data</code> property contains the data to display.
* You can listen for this event and update the component
* when the <code>data</code> property changes.</p>
*
* @eventType mx.events.FlexEvent.DATA_CHANGE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="dataChange", type="mx.events.FlexEvent")]
/**
* Dispatched when a user clicks a hyperlink in text defined by the
* <code>htmlText</code> property, where the URL begins with <code>"event:"</code>.
* The remainder of the URL after
* <code>"event:"</code> is placed in the text property of the <code>link</code> event object.
*
* <p>When you handle the <code>link</code> event, the hyperlink is not automatically executed;
* you need to execute the hyperlink from within your event handler.
* You typically use the <code>navigateToURL()</code> method to execute the hyperlink.
* This allows you to modify the hyperlink, or even prohibit it from occurring,
* in your application. </p>
*
* @eventType flash.events.TextEvent.LINK
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="link", type="flash.events.TextEvent")]
/**
* Dispatched when the user types, deletes, or pastes text into the control.
* No event is dispatched when the user presses the Delete key, or Backspace key.
*
* <p>Even though the default value of the <code>TextEvent.bubbles</code> property
* is <code>true</code>, this control dispatches the event with
* the <code>TextEvent.bubbles</code> property set to <code>false</code>.</p>
*
* @eventType flash.events.TextEvent.TEXT_INPUT
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="textInput", type="flash.events.TextEvent")]
//--------------------------------------
// Styles
//--------------------------------------
include "../styles/metadata/PaddingStyles.as"
/**
* Number of pixels between the component's bottom border
* and the bottom edge of its content area.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingBottom", type="Number", format="Length", inherit="no")]
/**
* Number of pixels between the component's top border
* and the top edge of its content area.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingTop", type="Number", format="Length", inherit="no")]
/**
* Color of the component if it is disabled.
*
* @default 0xAAB3B3
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="disabledColor", type="uint", format="Color", inherit="yes")]
//--------------------------------------
// Excluded APIs
//--------------------------------------
[Exclude(name="maxHorizontalScrollPosition", kind="property")]
[Exclude(name="maxVerticalScrollPosition", kind="property")]
//--------------------------------------
// Other metadata
//--------------------------------------
[DataBindingInfo("editEvents", "&quot;focusIn;focusOut&quot;")]
[DefaultBindingProperty(source="text", destination="text")]
[DefaultTriggerEvent("change")]
[IconFile("TextArea.png")]
[ResourceBundle("controls")]
[Alternative(replacement="spark.components.TextArea", since="4.0")]
/**
* The TextArea control is a multiline text field
* with a border and optional scroll bars.
* The TextArea control supports the HTML rendering capabilities
* of Flash Player and AIR.
*
* <p>If you disable a TextArea control, it displays its contents in the
* color specified by the <code>disabledColor</code> style.
* You can also set a TextArea control to read-only
* to disallow editing of the text.
* To conceal input text by displaying characters as asterisks,
* set the TextArea's <code>displayAsPassword</code> property.</p>
*
* <p>The TextArea control has the following default sizing characteristics:</p>
* <table class="innertable">
* <tr>
* <th>Characteristic</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>Default size</td>
* <td>width: 160 pixels; height: 44 pixels</td>
* </tr>
* <tr>
* <td>Minimum size</td>
* <td>0 pixels</td>
* </tr>
* <tr>
* <td>Maximum size</td>
* <td>10000 by 10000 pixels</td>
* </tr>
* </table>
*
* @mxml
*
* <p>The <code>&lt;mx:TextArea&gt;</code> tag inherits the attributes
* of its superclass and adds the following attributes:</p>
*
* <pre>
* &lt;mx:TextArea
* <b>Properties</b>
* condenseWhite="false|true"
* data="undefined"
* displayAsPassword="false|true"
* editable="true|false"
* horizontalScrollPolicy="auto|on|off"
* horizontalScrollPosition="0"
* htmlText="null"
* imeMode="null"
* length="0"
* listData="null"
* maxChars="0"
* restrict="null"
* selectionBeginIndex="0"
* selectionEndIndex="0"
* styleSheet="null"
* text=""
* textHeight="<i>height of text</i>" [read-only]
* textWidth="<i>width of text</i>" [read-only]
* verticalScrollPolicy="auto|on|off"
* verticalScrollPosition="0"
* wordWrap="true|false"
* &nbsp;
* <b>Styles</b>
* disabledColor="0xAAB3B3"
* focusAlpha="0.5"
* focusRoundedCorners"tl tr bl br"
* paddingLeft="0""
* paddingRight="0""
* &nbsp;
* <b>Events</b>
* change="<i>No default</i>"
* /&gt;
* </pre>
*
* @see mx.controls.Label
* @see mx.controls.Text
* @see mx.controls.TextInput
* @see mx.controls.RichTextEditor
* @see mx.controls.textClasses.TextRange
*
* @includeExample examples/TextAreaExample.mxml
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class TextArea extends ScrollControlBase
implements IDataRenderer, IDropInListItemRenderer,
IFocusManagerComponent, IIMESupport, IListItemRenderer,
IFontContextComponent
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function TextArea()
{
super();
// ScrollControlBase variables.
_horizontalScrollPolicy = ScrollPolicy.AUTO;
_verticalScrollPolicy = ScrollPolicy.AUTO;
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* Flag that indicates whether scroll Events should be dispatched
*/
private var allowScrollEvent:Boolean = true;
/**
* @private
* Flag that will block default data/listData behavior.
*/
private var textSet:Boolean;
/**
* @private
*/
private var selectionChanged:Boolean = false;
/**
* @private
*/
private var errorCaught:Boolean = false;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// accessibilityProperties
//----------------------------------
/**
* @private
* Storage for the accessibilityProperties property.
*/
private var _accessibilityProperties:AccessibilityProperties;
/**
* @private
*/
private var accessibilityPropertiesChanged:Boolean = false;
/**
* @private
* Accessibility data.
*
* @tiptext
* @helpid 3185
*/
override public function get accessibilityProperties():
AccessibilityProperties
{
return _accessibilityProperties;
}
/**
* @private
*/
override public function set accessibilityProperties(
value:AccessibilityProperties):void
{
if (value == _accessibilityProperties)
return;
_accessibilityProperties = value;
accessibilityPropertiesChanged = true;
invalidateProperties();
}
//----------------------------------
// baselinePosition
//----------------------------------
/**
* @private
* The baselinePosition of a TextArea is calculated for its textField.
*/
override public function get baselinePosition():Number
{
if (!validateBaselinePosition())
return NaN;
return textField.y + textField.baselinePosition;
}
//----------------------------------
// enabled
//----------------------------------
/**
* @private
*/
private var enabledChanged:Boolean = false;
[Inspectable(category="General", enumeration="true,false", defaultValue="true")]
/**
* @private
* Disable scrollbars and text field if we're disabled.
*/
override public function set enabled(value:Boolean):void
{
if (value == enabled)
return;
super.enabled = value;
enabledChanged = true;
if (verticalScrollBar)
verticalScrollBar.enabled = value;
if (horizontalScrollBar)
horizontalScrollBar.enabled = value;
invalidateProperties();
if (border && border is IInvalidating)
IInvalidating(border).invalidateDisplayList();
}
//----------------------------------
// fontContext
//----------------------------------
/**
* @private
*/
public function get fontContext():IFlexModuleFactory
{
return moduleFactory;
}
/**
* @private
*/
public function set fontContext(moduleFactory:IFlexModuleFactory):void
{
this.moduleFactory = moduleFactory;
}
//----------------------------------
// horizontalScrollPosition
//----------------------------------
/**
* @private
* Position of the horizontal scrollbar.
*/
private var _hScrollPosition:Number;
[Bindable("scroll")]
[Bindable("viewChanged")]
[Inspectable(defaultValue="0")]
/**
* Pixel position in the content area of the leftmost pixel
* that is currently displayed.
* (The content area includes all contents of a control, not just
* the portion that is currently displayed.)
* This property is always set to 0, and ignores changes,
* if <code>wordWrap</code> is set to <code>true</code>.
*
* @default 0
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function set horizontalScrollPosition(value:Number):void
{
// Dispatch viewChanged event in ScrollControlBase super/
super.horizontalScrollPosition = value;
// Set _hScrollPosition since it is compared against everytime
// in updateDisplayList().
_hScrollPosition = value;
if (textField)
{
textField.scrollH = value;
// This works around a player bug.
textField.background = false;
}
else
{
invalidateProperties();
}
}
//----------------------------------
// horizontalScrollPolicy
//----------------------------------
[Inspectable(enumeration="off,on,auto", defaultValue="auto")]
/**
* Specifies whether the horizontal scroll bar is
* always on (<code>ScrollPolicy.ON</code>),
* always off (<code>ScrollPolicy.OFF</code>),
* or turns on when needed (<code>ScrollPolicy.AUTO</code>).
*
* @default ScrollPolicy.AUTO
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get horizontalScrollPolicy():String
{
return height <= 40 ? ScrollPolicy.OFF : _horizontalScrollPolicy;
}
//----------------------------------
// maxHorizontalScrollPosition
//----------------------------------
/**
* Maximum value of <code>horizontalScrollPosition</code>.
* <p>The default value is 0, which means that horizontal scrolling is not
* required.</p>
* This property is always set to 0
* if <code>wordWrap</code> is set to <code>true</code>.
*
* <p>The value of the <code>maxHorizontalScrollPosition</code> property is
* computed from the data and size of component, and must not be set by
* the application code.</p>
*
* @default 0
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get maxHorizontalScrollPosition():Number
{
return textField ? textField.maxScrollH : 0;
}
//----------------------------------
// maxVerticalScrollPosition
//----------------------------------
/**
* Maximum value of <code>verticalScrollPosition</code>.
* The default value is 0, which means that vertical scrolling is not
* required.
*
* <p>The value of the <code>maxVerticalScrollPosition</code> property is
* computed from the data and size of component, and must not be set by
* the application code.</p>
*
* @tiptext The maximum pixel offset into the content from the top edge
* @helpid 3182
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get maxVerticalScrollPosition():Number
{
return textField ? textField.maxScrollV - 1 : 0;
}
//----------------------------------
// tabIndex
//----------------------------------
/**
* @private
* Storage for the tabIndex property.
*/
private var _tabIndex:int = -1;
/**
* @private
*/
private var tabIndexChanged:Boolean = false;
/**
* @private
* Tab order in which the control receives the focus when navigating
* with the Tab key.
*
* @default -1
* @tiptext tabIndex of the component
* @helpid 3184
*/
override public function get tabIndex():int
{
return _tabIndex;
}
/**
* @private
*/
override public function set tabIndex(value:int):void
{
if (value == _tabIndex)
return;
_tabIndex = value;
tabIndexChanged = true;
invalidateProperties();
}
//----------------------------------
// textField
//----------------------------------
/**
* The internal UITextField that renders the text of this TextArea.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var textField:IUITextField;
//----------------------------------
// verticalScrollPosition
//----------------------------------
/**
* @private
* Position of the vertical scrollbar.
*/
private var _vScrollPosition:Number;
[Bindable("scroll")]
[Bindable("viewChanged")]
[Inspectable(defaultValue="0")]
/**
* Line number of the top row of characters that is currently displayed.
* The default value is 0.
*
* @tiptext The pixel offset into the content from the top edge
* @helpid 3181
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function set verticalScrollPosition(value:Number):void
{
// Dispatch viewChanged event in ScrollControlBase super.
super.verticalScrollPosition = value;
// Set scrollV since it is compared against everytime
// in updateDisplayList().
_vScrollPosition = value;
if (textField)
{
textField.scrollV = value + 1;
// This works around a player bug.
textField.background = false;
}
else
{
invalidateProperties();
}
}
//----------------------------------
// verticalScrollPolicy
//----------------------------------
[Inspectable(enumeration="off,on,auto", defaultValue="auto")]
/**
* Whether the vertical scroll bar is
* always on (<code>ScrollPolicy.ON</code>),
* always off (<code>ScrollPolicy.OFF</code>),
* or turns on when needed (<code>ScrollPolicy.AUTO</code>).
*
* @default ScrollPolicy.AUTO
* @tiptext Specifies if vertical scrollbar is on, off,
* or automatically adjusts
* @helpid 3428
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get verticalScrollPolicy():String
{
return height <= 40 ? ScrollPolicy.OFF : _verticalScrollPolicy;
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// condenseWhite
//----------------------------------
/**
* @private
* Storage for the condenseWhite property.
*/
private var _condenseWhite:Boolean = false;
/**
* @private
*/
private var condenseWhiteChanged:Boolean = false;
[Bindable("condenseWhiteChanged")]
[Inspectable(category="General", defaultValue="")]
/**
* Specifies whether extra white space (spaces, line breaks,
* and so on) should be removed in a TextArea control with HTML text.
*
* <p>The <code>condenseWhite</code> property only affects text set with
* the <code>htmlText</code> property, not the <code>text</code> property.
* If you set text with the <code>text</code> property,
* <code>condenseWhite</code> is ignored.</p>
*
* <p>If you set the <code>condenseWhite</code> property to <code>true</code>,
* you must use standard HTML commands such as <code>&lt;br&gt;</code>
* and <code>&lt;p&gt;</code> to place line breaks in the text field.</p>
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get condenseWhite():Boolean
{
return _condenseWhite;
}
/**
* @private
*/
public function set condenseWhite(value:Boolean):void
{
if (value == _condenseWhite)
return;
_condenseWhite = value;
condenseWhiteChanged = true;
// Changing the condenseWhite property needs to trigger
// the same response as changing the htmlText property
// if this TextArea is displaying HTML.
if (isHTML)
htmlTextChanged = true;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
dispatchEvent(new Event("condenseWhiteChanged"));
}
//----------------------------------
// data
//----------------------------------
/**
* @private
* Storage for the data property.
*/
private var _data:Object;
[Bindable("dataChange")]
[Inspectable(environment="none")]
/**
* Lets you pass a value to the component
* when you use it in an item renderer or item editor.
* You typically use data binding to bind a field of the <code>data</code>
* property to a property of this component.
*
* <p>When you use the control as a drop-in item renderer or drop-in
* item editor, Flex automatically writes the current value of the item
* to the <code>text</code> property of this control.</p>
*
* <p>You do not set this property in MXML.</p>
*
* @default null
* @see mx.core.IDataRenderer
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get data():Object
{
return _data;
}
/**
* @private
*/
public function set data(value:Object):void
{
var newText:*;
_data = value;
if (_listData)
{
newText = _listData.label;
}
else if (_data != null)
{
if (_data is String)
newText = String(_data);
else
newText = _data.toString();
}
if (newText !== undefined && !textSet)
{
text = newText;
textSet = false;
}
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
//----------------------------------
// displayAsPassword
//----------------------------------
/**
* @private
* Storage for the displayAsPassword proeprty.
*/
private var _displayAsPassword:Boolean = false;
/**
* @private
*/
private var displayAsPasswordChanged:Boolean = false;
[Bindable("displayAsPasswordChanged")]
[Inspectable(category="General", defaultValue="false")]
/**
* Indicates whether this control is used for entering passwords.
* If <code>true</code>, the field does not display entered text,
* instead, each text character entered into the control
* appears as the character "&#42;".
*
* @default false
*
* @tiptext Specifies whether to display '*'
* instead of the actual characters
* @helpid 3177
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get displayAsPassword():Boolean
{
return _displayAsPassword;
}
/**
* @private
*/
public function set displayAsPassword(value:Boolean):void
{
if (value == _displayAsPassword)
return;
_displayAsPassword = value;
displayAsPasswordChanged = true;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
dispatchEvent(new Event("displayAsPasswordChanged"));
}
//----------------------------------
// editable
//----------------------------------
/**
* @private
* Storage for the editable property.
*/
private var _editable:Boolean = true;
/**
* @private
*/
private var editableChanged:Boolean = false;
[Bindable("editableChanged")]
[Inspectable(category="General", defaultValue="true")]
/**
* Indicates whether the user is allowed to edit the text in this control.
* If <code>true</code>, the user can edit the text.
*
* @default true;
*
* @tiptext Specifies whether the component is editable or not
* @helpid 3176
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get editable():Boolean
{
return _editable;
}
/**
* @private
*/
public function set editable(value:Boolean):void
{
if (value == _editable)
return;
_editable = value;
editableChanged = true;
invalidateProperties();
dispatchEvent(new Event("editableChanged"));
}
//----------------------------------
// enableIME
//----------------------------------
/**
* A flag that indicates whether the IME should
* be enabled when the component receives focus.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get enableIME():Boolean
{
return editable;
}
//----------------------------------
// htmlText
//----------------------------------
/**
* @private
* Storage for the htmlText property.
* In addition to being set in the htmlText setter,
* it is automatically updated at two other times.
* 1. When the 'text' or 'htmlText' is pushed down into
* the textField in commitProperties(), this causes
* the textField to update its own 'htmlText'.
* Therefore in commitProperties() we reset this storage var
* to be in sync with the textField.
* 2. When the TextFormat of the textField changes
* because a CSS style has changed (see validateNow()
* in UITextField), the textField also updates its own 'htmlText'.
* Therefore in textField_textFieldStyleChangeHandler()
*/
private var _htmlText:String = "";
/**
* @private
*/
private var htmlTextChanged:Boolean = false;
/**
* @private
* The last value of htmlText that was set.
* We have to keep track of this because when you set the htmlText
* of a TextField and read it back, you don't get what you set.
* In general it will have additional HTML markup corresponding
* to the defaultTextFormat set from the CSS styles.
* If this var is null, it means that 'text' rather than 'htmlText'
* was last set.
*/
private var explicitHTMLText:String = null;
[Bindable("htmlTextChanged")]
[CollapseWhiteSpace]
[Inspectable(category="General", defaultValue="")]
[NonCommittingChangeEvent("change")]
/**
* Specifies the text displayed by the TextArea control, including HTML markup that
* expresses the styles of that text.
* When you specify HTML text in this property, you can use the subset of HTML
* tags that is supported by the Flash TextField control.
*
* <p>When you set this property, the HTML markup is applied
* after the CSS styles for the TextArea instance are applied.
* When you get this property, the HTML markup includes
* the CSS styles.</p>
*
* <p>For example, if you set this to be a string such as,
* <code>"This is an example of &lt;b&gt;bold&lt;/b&gt; markup"</code>,
* the text "This is an example of <b>bold</b> markup" appears
* in the TextArea with whatever CSS styles normally apply.
* Also, the word "bold" appears in boldface font because of the
* <code>&lt;b&gt;</code> markup.</p>
*
* <p>HTML markup uses characters such as &lt; and &gt;,
* which have special meaning in XML (and therefore in MXML). So,
* code such as the following does not compile:</p>
*
* <pre>
* &lt;mx:TextArea htmlText="This is an example of &lt;b&gt;bold&lt;/b&gt; markup"/&gt;
* </pre>
*
* <p>There are three ways around this problem.</p>
*
* <ul>
*
* <li>
*
* <p>Set the <code>htmlText</code> property in an ActionScript method called as
* an <code>initialize</code> handler:</p>
*
* <pre>
* &lt;mx:TextArea id="myTextArea" initialize="myTextArea_initialize()"/&gt;
* </pre>
*
* <p>where the <code>myTextArea_initialize</code> method is in a script CDATA section:</p>
*
* <pre>
* &lt;fx:Script&gt;
* &lt;![CDATA[
* private function myTextArea_initialize():void {
* myTextArea.htmlText = "This is an example of &lt;b&gt;bold&lt;/b&gt; markup";
* }
* ]]&gt;
* &lt;/fx:Script&gt;
*
* </pre>
*
* <p>This is the simplest approach because the HTML markup
* remains easily readable.
* Notice that you must assign an <code>id</code> to the TextArea
* so you can refer to it in the <code>initialize</code>
* handler.</p>
*
* </li>
*
* <li>
*
* <p>Specify the <code>htmlText</code> property by using a child tag
* with a CDATA section. A CDATA section in XML contains character data
* where characters like &lt; and &gt; aren't given a special meaning.</p>
*
* <pre>
* &lt;mx:TextArea&gt;
* &lt;mx:htmlText&gt;&lt;![CDATA[This is an example of &lt;b&gt;bold&lt;/b&gt; markup]]&gt;&lt;/mx:htmlText&gt;
* &lt;mx:TextArea/&gt;
* </pre>
*
* <p>You must write the <code>htmlText</code> property as a child tag
* rather than as an attribute on the <code>&lt;mx:TextArea&gt;</code> tag
* because XML doesn't allow CDATA for the value of an attribute.
* Notice that the markup is readable, but the CDATA section makes
* this approach more complicated.</p>
*
* </li>
*
* <li>
*
* <p>Use an <code>hmtlText</code> attribute where any occurences
* of the HTML markup characters &lt; and &gt; in the attribute value
* are written instead of the XML "entities" <code>&amp;lt;</code>
* and <code>&amp;gt;</code>:</p>
*
* <pre>
* &lt;mx:TextArea htmlText="This is an example of &amp;lt;b&amp;gt;bold&amp;lt;/b&amp;gt; markup"/&amp;gt;
* </pre>
*
* Adobe does not recommend this approach because the HTML markup becomes
* nearly impossible to read.
*
* </li>
*
* </ul>
*
* <p>If the <code>condenseWhite</code> property is <code>true</code>
* when you set the <code>htmlText</code> property, multiple
* white-space characters are condensed, as in HTML-based browsers;
* for example, three consecutive spaces are displayed
* as a single space.
* The default value for <code>condenseWhite</code> is
* <code>false</code>, so you must set <code>condenseWhite</code>
* to <code>true</code> to collapse the white space.</p>
*
* <p>If you read back the <code>htmlText</code> property quickly
* after setting it, you get the same string that you set.
* However, after the LayoutManager runs, the value changes
* to include additional markup that includes the CSS styles.</p>
*
* <p>Setting the <code>htmlText</code> property affects the <code>text</code>
* property in several ways.
* If you read the <code>text</code> property quickly after setting
* the <code>htmlText</code> property, you get <code>null</code>,
* which indicates that the <code>text</code> corresponding to the new
* <code>htmlText</code> has not yet been determined.
* However, after the LayoutManager runs, the <code>text</code> property
* value changes to the <code>htmlText</code> string with all the
* HTML markup removed; that is,
* the value is the characters that the TextArea actually displays.</p>
*
* <p>Conversely, if you set the <code>text</code> property,
* any previously set <code>htmlText</code> is irrelevant.
* If you read the <code>htmlText</code> property quickly after setting
* the <code>text</code> property, you get <code>null</code>,
* which indicates that the <code>htmlText</code> that corresponds to the new
* <code>text</code> has not yet been determined.
* However, after the LayoutManager runs, the <code>htmlText</code> property
* value changes to the new text plus the HTML markup for the CSS styles.</p>
*
* <p>To make the LayoutManager run immediately, you can call the
* <code>validateNow()</code> method on the TextArea.
* For example, you could set some <code>htmlText</code>,
* call the <code>validateNow()</code> method, and immediately
* obtain the corresponding <code>text</code> that doesn't have
* the HTML markup.</p>
*
* <p>If you set both <code>text</code> and <code>htmlText</code> properties
* in ActionScript, whichever is set last takes effect.
* Do not set both in MXML, because MXML does not guarantee that
* the properties of an instance get set in any particular order.</p>
*
* <p>Setting either <code>text</code> or <code>htmlText</code> property
* inside a loop is a fast operation, because the underlying TextField
* that actually renders the text is not updated until
* the LayoutManager runs.</p>
*
* <p>If you try to set this property to <code>null</code>,
* it is set, instead, to the empty string.
* If the property temporarily has the value <code>null</code>,
* it indicates that the <code>text</code> has been recently set
* and the corresponding <code>htmlText</code>
* has not yet been determined.</p>
*
* @default ""
*
* @see flash.text.TextField#htmlText
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get htmlText():String
{
return _htmlText;
}
/**
* @private
*/
public function set htmlText(value:String):void
{
textSet = true;
// The htmlText property can't be set to null,
// only to the empty string, because if you set the htmlText
// of a TextField to null it throws an RTE.
// If the getter returns null, it means that 'text' was just set
// and the value of 'htmlText' isn't yet known, because the 'text'
// hasn't been committed into the textField and the 'htmlText'
// hasn't yet been read back out of the textField.
if (!value)
value = "";
_htmlText = value;
htmlTextChanged = true;
// The text property is unknown until commitProperties(),
// when we push the htmlText into the TextField and it
// calculates the text.
// But you can call validateNow() to make this happen right away.
_text = null;
explicitHTMLText = value;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
// Trigger bindings to htmlText.
dispatchEvent(new Event("htmlTextChanged"));
// commitProperties() will dispatch a "valueCommit" event
// after the TextField determines the 'text' based on the
// 'htmlText'; this event will trigger any bindings to 'text'.
}
//----------------------------------
// imeMode
//----------------------------------
/**
* @private
*/
private var _imeMode:String = null;
/**
* Specifies the IME (input method editor) mode.
* The IME enables users to enter text in Chinese, Japanese, and Korean.
* Flex sets the specified IME mode when the control gets the focus,
* and sets it back to the previous value when the control loses the focus.
*
* <p>The flash.system.IMEConversionMode class defines constants for the
* valid values for this property.
* You can also specify <code>null</code> to specify no IME.</p>
*
* @default null
*
* @see flash.system.IMEConversionMode
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get imeMode():String
{
return _imeMode;
}
/**
* @private
*/
public function set imeMode(value:String):void
{
_imeMode = value;
}
//----------------------------------
// isHTML
//----------------------------------
/**
* @private
*/
private function get isHTML():Boolean
{
return explicitHTMLText != null;
}
//----------------------------------
// length
//----------------------------------
/**
* The number of characters of text displayed in the TextArea.
*
* @default 0
*
* @tiptext The number of characters in the TextArea
* @helpid 3173
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get length():int
{
return text != null ? text.length : -1;
}
//----------------------------------
// listData
//----------------------------------
/**
* @private
* Storage for the listData property.
*/
private var _listData:BaseListData;
[Bindable("dataChange")]
[Inspectable(environment="none")]
/**
* When a component is used as a drop-in item renderer or drop-in
* item editor, Flex initializes the <code>listData</code> property
* of the component with the appropriate data from the list control.
* The component can then use the <code>listData</code> property
* to initialize the <code>data</code> property of the drop-in
* item renderer or drop-in item editor.
*
* <p>You do not set this property in MXML or ActionScript;
* Flex sets it when the component is used as a drop-in item renderer
* or drop-in item editor.</p>
*
* @default null
* @see mx.controls.listClasses.IDropInListItemRenderer
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get listData():BaseListData
{
return _listData;
}
/**
* @private
*/
public function set listData(value:BaseListData):void
{
_listData = value;
}
//----------------------------------
// maxChars
//----------------------------------
/**
* @private
* Storage for the maxChars property.
*/
private var _maxChars:int = 0;
/**
* @private
*/
private var maxCharsChanged:Boolean = false;
[Bindable("maxCharsChanged")]
[Inspectable(category="General", defaultValue="0")]
/**
* Maximum number of characters that users can enter in the text field.
* This property does not limit the length of text specified by the
* setting the control's <code>text</code> or <code>htmlText</code>property.
*
* <p>The default value of 0, which indicates no limit.</p>
*
* @tiptext The maximum number of characters that the TextArea can contain
* @helpid 3172
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maxChars():int
{
return _maxChars;
}
/**
* @private
*/
public function set maxChars(value:int):void
{
if (value == _maxChars)
return;
_maxChars = value;
maxCharsChanged = true;
invalidateProperties();
dispatchEvent(new Event("maxCharsChanged"));
}
//----------------------------------
// restrict
//----------------------------------
/**
* @private
* Storage for the restrict property.
*/
private var _restrict:String = null;
/**
* @private
*/
private var restrictChanged:Boolean = false;
[Bindable("restrictChanged")]
[Inspectable(category="General")]
/**
* Indicates the set of characters that a user can enter into the control.
* If the value of the <code>restrict</code> property is <code>null</code>,
* you can enter any character. If the value of the <code>restrict</code>
* property is an empty string, you cannot enter any character.
* This property only restricts user interaction; a script
* can put any text into the text field. If the value of
* the <code>restrict</code> property is a string of characters,
* you may enter only characters in that string into the
* text field.
*
* <p>Flex scans the string from left to right. You can specify a range by
* using the hyphen (-) character.
* If the string begins with a caret (^) character, all characters are
* initially accepted and succeeding characters in the string are excluded
* from the set of accepted characters. If the string does not begin with a
* caret (^) character, no characters are initially accepted and succeeding
* characters in the string are included in the set of accepted characters.</p>
*
* <p>Because some characters have a special meaning when used
* in the <code>restrict</code> property, you must use
* backslash characters to specify the literal characters -, &#094;, and \.
* When you use the <code>restrict</code> property as an attribute
* in an MXML tag, use single backslashes, as in the following
* example: \&#094;\-\\.
* When you set the <code>restrict</code> In and ActionScript expression,
* use double backslashes, as in the following example: \\&#094;\\-\\\.</p>
*
* @default null
*
* @see flash.text.TextField#restrict
*
* @tiptext The set of characters that may be entered into the TextArea
* @helpid 3174
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get restrict():String
{
return _restrict;
}
/**
* @private
*/
public function set restrict(value:String):void
{
if (value == _restrict)
return;
_restrict = value;
restrictChanged = true;
invalidateProperties();
dispatchEvent(new Event("restrictChanged"));
}
//----------------------------------
// selectable
//----------------------------------
/**
* @private
* Storage for selectable property.
*/
private var _selectable:Boolean = true;
/**
* @private
* Change flag for selectable property.
*/
private var selectableChanged:Boolean = false;
[Inspectable(category="General", defaultValue="true")]
/**
* Specifies whether the text can be selected.
* Making the text selectable lets you copy text from the control.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get selectable():Boolean
{
return _selectable;
}
/**
* @private
*/
public function set selectable(value:Boolean):void
{
if (value == selectable)
return;
_selectable = value;
selectableChanged = true;
invalidateProperties();
}
//----------------------------------
// selectionBeginIndex
//----------------------------------
/**
* @private
* Storage for the selectionBeginIndex property.
*/
private var _selectionBeginIndex:int = 0;
[Inspectable(defaultValue="0")]
/**
* The zero-based character index value of the first character
* in the current selection.
* For example, the first character is 0, the second character is 1,
* and so on.
* When the control gets the focus, the selection is visible if the
* <code>selectionBeginIndex</code> and <code>selectionEndIndex</code>
* properties are both set.
*
* @default 0
*
* @tiptext The zero-based index value of the first character
* in the selection.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get selectionBeginIndex():int
{
return textField ?
textField.selectionBeginIndex :
_selectionBeginIndex;
}
/**
* @private
*/
public function set selectionBeginIndex(value:int):void
{
_selectionBeginIndex = value;
selectionChanged = true;
invalidateProperties();
}
//----------------------------------
// selectionEndIndex
//----------------------------------
/**
* @private
* Storage for the selectionEndIndex property.
*/
private var _selectionEndIndex:int = 0;
[Inspectable(defaultValue="0")]
/**
* The zero-based index of the position <i>after</i>the last character
* in the current selection (equivalent to the one-based index of the last
* character).
* If the last character in the selection, for example, is the fifth
* character, this property has the value 5.
* When the control gets the focus, the selection is visible if the
* <code>selectionBeginIndex</code> and <code>selectionEndIndex</code>
* properties are both set.
*
* @default 0
*
* @tiptext The zero-based index value of the last character
* in the selection.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get selectionEndIndex():int
{
return textField ?
textField.selectionEndIndex :
_selectionEndIndex;
}
/**
* @private
*/
public function set selectionEndIndex(value:int):void
{
_selectionEndIndex = value;
selectionChanged = true;
invalidateProperties();
}
//----------------------------------
// styleSheet
//----------------------------------
/**
* @private
* Change flag for the styleSheet property
*/
private var styleSheetChanged:Boolean = false;
/**
* @private
* Storage for the styleSheet property.
*/
private var _styleSheet:StyleSheet;
/**
* A flash.text.StyleSheet object that can perform rendering
* on the TextArea control's text.
* Used for detailed control of HTML styles for the text.
* For more information, see the flash.text.StyleSheet
* class documentation.
*
* @see flash.text.StyleSheet
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get styleSheet():StyleSheet
{
return _styleSheet;
}
/**
* @private
*/
public function set styleSheet(value:StyleSheet):void
{
_styleSheet = value;
styleSheetChanged = true;
htmlTextChanged = true;
invalidateProperties();
}
//----------------------------------
// text
//----------------------------------
/**
* @private
* Storage for the text property.
* In addition to being set in the 'text' setter,
* it is automatically updated at another time:
* When the 'text' or 'htmlText' is pushed down into
* the textField in commitProperties(), this causes
* the textField to update its own 'text'.
* Therefore in commitProperties() we reset this storage var
* to be in sync with the textField.
*/
private var _text:String = "";
/**
* @private
*/
private var textChanged:Boolean = false;
[Bindable("textChanged")]
[CollapseWhiteSpace]
[Inspectable(category="General", defaultValue="")]
[NonCommittingChangeEvent("change")]
/**
* Plain text that appears in the control.
* Its appearance is determined by the CSS styles of this Label control.
*
* <p>Any HTML tags in the text string are ignored,
* and appear as entered in the string.
* To display text formatted using HTML tags,
* use the <code>htmlText</code> property instead.
* If you set the <code>htmlText</code> property,
* the HTML replaces any text you had set using this propety, and the
* <code>text</code> property returns a plain-text version of the
* HTML text, with all HTML tags stripped out.
* For more information see the <code>htmlText</code> property.</p>
*
* <p>To include the special characters left angle bracket (&lt;),
* right angle bracket (&gt;), or ampersand (&amp;) in the text,
* wrap the text string in the CDATA tag.
* Alternatively, you can use HTML character entities for the
* special characters, for example, <code>&amp;lt;</code>.</p>
*
* <p>If you try to set this property to <code>null</code>,
* it is set, instead, to the empty string.
* The <code>text</code> property can temporarily have the value <code>null</code>,
* which indicates that the <code>htmlText</code> has been recently set
* and the corresponding <code>text</code> value
* has not yet been determined.</p>
*
* @default ""
* @tiptext Gets or sets the TextArea content
* @helpid 3179
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get text():String
{
return _text;
}
/**
* @private
*/
public function set text(value:String):void
{
textSet = true;
// The text property can't be set to null, only to the empty string.
// If the getter returns null, it means that 'htmlText' was just set
// and the value of 'text' isn't yet known, because the 'htmlText'
// hasn't been committed into the textField and the 'text'
// hasn't yet been read back out of the textField.
if (!value)
value = "";
if (!isHTML && value == _text)
return;
_text = value;
textChanged = true;
// The htmlText property is unknown until commitProperties(),
// when we push the text into the TextField and it
// calculates the htmlText.
// But you can call validateNow() to make this happen right away.
_htmlText = null;
explicitHTMLText = null;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
// Trigger bindings to 'text'.
dispatchEvent(new Event("textChanged"));
// commitProperties() will dispatch an "htmlTextChanged" event
// after the TextField determines the 'htmlText' based on the
// 'text'; this event will trigger any bindings to 'htmlText'.
dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
}
//----------------------------------
// textHeight
//----------------------------------
/**
* @private
*/
private var _textHeight:Number;
/**
* The height of the text.
*
* <p>The value of the <code>textHeight</code> property is correct only
* after the component has been validated.
* If you set <code>text</code> and then immediately ask for the
* <code>textHeight</code>, you might receive an incorrect value.
* You should wait for the component to validate
* or call the <code>validateNow()</code> method before you get the value.
* This behavior differs from that of the flash.text.TextField control,
* which updates the value immediately.</p>
*
* @see flash.text.TextField
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get textHeight():Number
{
return _textHeight;
}
//----------------------------------
// textWidth
//----------------------------------
/**
* @private
*/
private var _textWidth:Number;
/**
* The width of the text.
*
* <p>The value of the <code>textWidth</code> property is correct only
* after the component has been validated.
* If you set <code>text</code> and then immediately ask for the
* <code>textWidth</code>, you might receive an incorrect value.
* You should wait for the component to validate
* or call the <code>validateNow()</code> method before you get the value.
* This behavior differs from that of the flash.text.TextField control,
* which updates the value immediately.</p>
*
* @see flash.text.TextField
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get textWidth():Number
{
return _textWidth;
}
//----------------------------------
// wordWrap
//----------------------------------
/**
* @private
* Storage for the wordWrap property.
*/
private var _wordWrap:Boolean = true;
/**
* @private
*/
private var wordWrapChanged:Boolean = false;
[Bindable("wordWrapChanged")]
[Inspectable(category="General", defaultValue="true")]
/**
* Indicates whether the text automatically wraps at the end of a line.
* If <code>true</code>, the text wraps to occupy
* multiple lines, if necessary.
*
* @default true
*
* @tiptext If true, lines will wrap. If false, long lines get clipped.
* @helpid 3175
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get wordWrap():Boolean
{
return _wordWrap;
}
/**
* @private
*/
public function set wordWrap(value:Boolean):void
{
if (value == _wordWrap)
return;
_wordWrap = value;
wordWrapChanged = true;
invalidateProperties();
invalidateDisplayList();
dispatchEvent(new Event("wordWrapChanged"));
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function createChildren():void
{
super.createChildren();
createTextField(-1);
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
if (hasFontContextChanged() && textField != null)
{
removeTextField();
createTextField(-1);
accessibilityPropertiesChanged = true;
condenseWhiteChanged = true;
displayAsPasswordChanged = true;
editableChanged = true;
enabledChanged = true;
maxCharsChanged = true;
restrictChanged = true;
selectableChanged = true;
tabIndexChanged = true;
wordWrapChanged = true;
textChanged = true;
selectionChanged = true;
}
if (accessibilityPropertiesChanged)
{
textField.accessibilityProperties = _accessibilityProperties;
accessibilityPropertiesChanged = false;
}
if (condenseWhiteChanged)
{
textField.condenseWhite = _condenseWhite;
condenseWhiteChanged = false;
}
if (displayAsPasswordChanged)
{
textField.displayAsPassword = _displayAsPassword;
displayAsPasswordChanged = false;
}
if (editableChanged)
{
textField.type = _editable && enabled ?
TextFieldType.INPUT :
TextFieldType.DYNAMIC;
editableChanged = false;
}
if (enabledChanged)
{
textField.enabled = enabled;
enabledChanged = false;
}
if (maxCharsChanged)
{
textField.maxChars = _maxChars;
maxCharsChanged = false;
}
if (restrictChanged)
{
textField.restrict = _restrict;
restrictChanged = false;
}
if (selectableChanged)
{
textField.selectable = _selectable;
selectableChanged = false;
}
if (styleSheetChanged)
{
textField.styleSheet = _styleSheet;
styleSheetChanged = false;
}
if (tabIndexChanged)
{
textField.tabIndex = _tabIndex;
tabIndexChanged = false;
}
if (wordWrapChanged)
{
textField.wordWrap = _wordWrap;
wordWrapChanged = false;
}
if (textChanged || htmlTextChanged)
{
// If the 'text' and 'htmlText' properties have both changed,
// the last one set wins.
if (isHTML)
textField.htmlText = explicitHTMLText;
else
textField.text = _text;
textFieldChanged(false, true)
textChanged = false;
htmlTextChanged = false;
}
if (selectionChanged)
{
textField.setSelection(_selectionBeginIndex, _selectionEndIndex);
selectionChanged = false;
}
if (!isNaN(_hScrollPosition))
horizontalScrollPosition = _hScrollPosition;
if (!isNaN(_vScrollPosition))
verticalScrollPosition = _vScrollPosition;
}
/**
* @private
*/
override protected function measure():void
{
super.measure();
measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH;
measuredWidth = DEFAULT_MEASURED_WIDTH;
// TextArea is minimum of two lines of text
measuredMinHeight = measuredHeight = 2 * DEFAULT_MEASURED_MIN_HEIGHT;
}
/**
* @private
* Position the internal textfield taking scrollbars into consideration.
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
var vm:EdgeMetrics = viewMetrics;
vm.left += getStyle("paddingLeft");
vm.top += getStyle("paddingTop");
vm.right += getStyle("paddingRight");
vm.bottom += getStyle("paddingBottom");
textField.move(vm.left, vm.top);
var w:Number = unscaledWidth - vm.left - vm.right;
var h:Number = unscaledHeight - vm.top - vm.bottom;
// If there's a border, add another line of pixels to the textField
// that go under the border but allow for descenders on some fonts.
if (vm.top + vm.bottom > 0)
h++;
// maxScrollV doesnt get proper value if width < 4
textField.setActualSize(Math.max(4, w), Math.max(4, h));
// For some reason the textfield resizes itself when initialized
// and needs to be forced back to its new size.
// textField.background = false;
// textField gets proper values of scroll properties after 1 update
// so scroll bars adjustment would need to wait.
if (!initialized)
{
callLater(invalidateDisplayList);
}
else
{
// maxScrollV isn't updated yet in some cases
callLater(adjustScrollBars);
}
// Reset the scrolling position here.
// This is most important at init time if someone
// explicitly sets a position as well as text,
// because setting the text causes a scroll event
// with the last known scrollpositions a frame later,
// overridding what we just set.
if (isNaN(_hScrollPosition))
_hScrollPosition = 0;
if (isNaN(_vScrollPosition))
_vScrollPosition = 0;
var p:Number = Math.min(textField.maxScrollH, _hScrollPosition);
if (p != textField.scrollH)
horizontalScrollPosition = p;
p = Math.min(textField.maxScrollV - 1, _vScrollPosition);
if (p != textField.scrollV - 1)
verticalScrollPosition = p;
}
/**
* @private
* Focus should always be on the internal TextField.
*/
override public function setFocus():void
{
// We want to preserve the scroll position rather than
// scroll to the line containing the caret.
var vScrollPos:int = verticalScrollPosition;
// we dont want to dispatch a scroll event on focus.
allowScrollEvent = false;
textField.setFocus();
verticalScrollPosition = vScrollPos;
allowScrollEvent = true;
}
/**
* @private
*/
override protected function isOurFocus(target:DisplayObject):Boolean
{
return target == textField || super.isOurFocus(target);
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Creates the text field child and adds it as a child of this component.
*
* @param childIndex The index of where to add the child.
* If -1, the text field is appended to the end of the list.
*/
mx_internal function createTextField(childIndex:int):void
{
if (!textField)
{
textField = IUITextField(createInFontContext(UITextField));
textField.autoSize = TextFieldAutoSize.NONE;
textField.enabled = enabled;
textField.ignorePadding = true;
textField.multiline = true;
textField.selectable = true;
textField.styleName = this;
textField.tabEnabled = true;
textField.type = TextFieldType.INPUT;
textField.useRichTextClipboard = true;
textField.wordWrap = true;
textField.addEventListener(Event.CHANGE, textField_changeHandler);
textField.addEventListener(Event.SCROLL, textField_scrollHandler);
textField.addEventListener(IOErrorEvent.IO_ERROR,
textField_ioErrorHandler);
textField.addEventListener(TextEvent.TEXT_INPUT,
textField_textInputHandler);
textField.addEventListener("textFieldStyleChange",
textField_textFieldStyleChangeHandler)
textField.addEventListener("textFormatChange",
textField_textFormatChangeHandler)
textField.addEventListener("textInsert",
textField_textModifiedHandler);
textField.addEventListener("textReplace",
textField_textModifiedHandler);
textField.addEventListener("textFieldWidthChange",
textField_textFieldWidthChangeHandler);
// can't use NativeDragEvent.NATIVE_DRAG_DROP b/c we need AIR for that
// ideally we don't need to listen for this event as doing a dragDrop should
// dispatch a TEXT_INPUT and a CHANGE event for us (see SDK-19816)
textField.addEventListener("nativeDragDrop", textField_nativeDragDropHandler);
if (childIndex == -1)
addChild(DisplayObject(textField));
else
addChildAt(DisplayObject(textField), childIndex);
}
}
/**
* @private
* Removes the text field from this component.
*/
mx_internal function removeTextField():void
{
if (textField)
{
textField.removeEventListener(Event.CHANGE, textField_changeHandler);
textField.removeEventListener(Event.SCROLL, textField_scrollHandler);
textField.removeEventListener(IOErrorEvent.IO_ERROR,
textField_ioErrorHandler);
textField.removeEventListener(TextEvent.TEXT_INPUT,
textField_textInputHandler);
textField.removeEventListener("textFieldStyleChange",
textField_textFieldStyleChangeHandler)
textField.removeEventListener("textFormatChange",
textField_textFormatChangeHandler)
textField.removeEventListener("textInsert",
textField_textModifiedHandler);
textField.removeEventListener("textReplace",
textField_textModifiedHandler);
textField.removeEventListener("textFieldWidthChange",
textField_textFieldWidthChangeHandler);
textField.removeEventListener("nativeDragDrop", textField_nativeDragDropHandler);
removeChild(DisplayObject(textField));
textField = null;
}
}
/**
* Returns a TextLineMetrics object with information about the text
* position and measurements for a line of text in the control.
* The component must be validated to get a correct number.
* If you set the <code>text</code> or <code>htmlText</code> property
* and then immediately call
* <code>getLineMetrics()</code> you may receive an incorrect value.
* You should either wait for the component to validate
* or call <code>validateNow()</code>.
* This is behavior differs from that of the flash.text.TextField class,
* which updates the value immediately.
*
* @param lineIndex The zero-based index of the line for which to get the metrics.
*
* @return The object that contains information about the text position
* and measurements for the specified line of text in the control.
*
* @see flash.text.TextField
* @see flash.text.TextLineMetrics
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function getLineMetrics(lineIndex:int):TextLineMetrics
{
return textField ? textField.getLineMetrics(lineIndex) : null;
}
/**
* Selects the text in the range specified by the parameters.
* If the control is not in focus, the selection highlight will not show
* until the control gains focus. Also, if the focus is gained by clicking
* on the control, any previous selection would be lost.
* If the two parameter values are the same,
* the new selection is an insertion point.
*
* @param beginIndex The zero-based index of the first character in the
* selection; that is, the first character is 0, the second character
* is 1, and so on.
*
* @param endIndex The zero-based index of the position <i>after</i>
* the last character in the selection (equivalent to the one-based
* index of the last character).
* If the parameter is 5, the last character in the selection, for
* example, is the fifth character.
* When the TextArea control gets the focus, the selection is visible
* if the <code>selectionBeginIndex</code> and <code>selectionEndIndex</code>
* properties are both set.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function setSelection(beginIndex:int, endIndex:int):void
{
_selectionBeginIndex = beginIndex;
_selectionEndIndex = endIndex;
selectionChanged = true;
invalidateProperties();
}
/**
* @private
* Setting the 'htmlText' of textField changes its 'text',
* and vice versa, so afterwards doing so we call this method
* to update the storage vars for various properties.
* Afterwards, the Label's 'text', 'htmlText', 'textWidth',
* and 'textHeight' are all in sync with each other
* and are identical to the TextField's.
*/
private function textFieldChanged(styleChangeOnly:Boolean,
dispatchValueCommitEvent:Boolean):void
{
var changed1:Boolean;
var changed2:Boolean;
if (!styleChangeOnly)
{
changed1 = _text != textField.text;
_text = textField.text;
}
changed2 = _htmlText != textField.htmlText;
_htmlText = textField.htmlText;
// If the 'text' property changes, trigger bindings to it
// and conditionally dispatch a 'valueCommit' event.
if (changed1)
{
dispatchEvent(new Event("textChanged"));
if (dispatchValueCommitEvent)
dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
}
// If the 'htmlText' property changes, trigger bindings to it.
if (changed2)
dispatchEvent(new Event("htmlTextChanged"));
_textWidth = textField.textWidth;
_textHeight = textField.textHeight;
}
/**
* @private
*/
private function adjustScrollBars():void
{
var visibleRows:Number =
textField.bottomScrollV - textField.scrollV + 1;
setScrollBarProperties(textField.width + textField.maxScrollH,
textField.width, textField.numLines,
visibleRows);
}
/**
* @private
* Some other components which use a TextArea as an internal
* subcomponent need access to its UITextField, but can't access the
* textField var because it is protected and therefore available
* only to subclasses.
*/
mx_internal function getTextField():IUITextField
{
return textField;
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function focusInHandler(event:FocusEvent):void
{
if (event.target == this)
systemManager.stage.focus = TextField(textField);
var fm:IFocusManager = focusManager;
if (editable && fm)
fm.showFocusIndicator = true;
if (fm)
fm.defaultButtonEnabled = false;
super.focusInHandler(event);
if (_imeMode != null && _editable)
{
// When IME.conversionMode is unknown it cannot be
// set to anything other than unknown(English)
try
{
if (!errorCaught &&
IME.conversionMode != IMEConversionMode.UNKNOWN)
{
IME.conversionMode = _imeMode;
}
errorCaught = false;
}
catch(e:Error)
{
// Once an error is thrown, focusIn is called
// again after the Alert is closed, throw error
// only the first time.
errorCaught = true;
var message:String = resourceManager.getString(
"controls", "unsupportedMode", [ _imeMode ]);
throw new Error(message);
}
}
}
/**
* @private
* Gets called by internal field so we remove focus rect.
*/
override protected function focusOutHandler(event:FocusEvent):void
{
var fm:IFocusManager = focusManager;
if (fm)
fm.defaultButtonEnabled = true;
super.focusOutHandler(event);
dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: ScrollControlBase
//
//--------------------------------------------------------------------------
/**
* @private
* Mouse wheel scroll handler.
* TextField scrolls automatically so we don't need to handle this.
*/
override protected function mouseWheelHandler(event:MouseEvent):void
{
event.stopPropagation();
}
/**
* @private
* We got a scroll event so update everything
*/
override protected function scrollHandler(event:Event):void
{
// TextField.scroll bubbles so you might see it here
if (event is ScrollEvent)
{
if (!liveScrolling &&
ScrollEvent(event).detail == ScrollEventDetail.THUMB_TRACK)
{
return;
}
super.scrollHandler(event);
textField.scrollH = horizontalScrollPosition;
textField.scrollV = verticalScrollPosition + 1;
_vScrollPosition = textField.scrollV - 1;
_hScrollPosition = textField.scrollH;
}
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
* Only gets called on keyboard not programmatic setting of text.
*/
private function textField_changeHandler(event:Event):void
{
textFieldChanged(false, false);
adjustScrollBars();
// Kill any programmatic change we might be looking at.
textChanged = false;
htmlTextChanged = false;
// Stop this bubbling "change" event
// and dispatch another one that doesn't bubble.
event.stopImmediatePropagation();
dispatchEvent(new Event(Event.CHANGE));
}
/**
* @private
*/
private function textField_scrollHandler(event:Event):void
{
if (initialized && allowScrollEvent)
{
// delta is positive when scrolling down/right and
// negative in the opposite direction
var deltaX:int = textField.scrollH - horizontalScrollPosition;
var deltaY:int = textField.scrollV - 1 - verticalScrollPosition;
horizontalScrollPosition = textField.scrollH;
verticalScrollPosition = textField.scrollV - 1;
var scrollEvent:ScrollEvent;
if (deltaX)
{
scrollEvent = new ScrollEvent(ScrollEvent.SCROLL, false, false,
null, horizontalScrollPosition, ScrollEventDirection.HORIZONTAL,
deltaX);
dispatchEvent(scrollEvent);
}
if (deltaY)
{
scrollEvent = new ScrollEvent(ScrollEvent.SCROLL, false, false,
null, verticalScrollPosition, ScrollEventDirection.VERTICAL,
deltaY);
dispatchEvent(scrollEvent);
}
}
}
/**
* @private
*/
private function textField_ioErrorHandler(event:IOErrorEvent):void
{
}
/**
* @private
*/
private function textField_nativeDragDropHandler(event:Event):void
{
// just call the "change" handler
textField_changeHandler(event);
}
/**
* @private
*/
private function textField_textInputHandler(event:TextEvent):void
{
event.stopImmediatePropagation();
// Dispatch a cancelable version of this event.
var newEvent:TextEvent =
new TextEvent(TextEvent.TEXT_INPUT, false, true);
newEvent.text = event.text;
dispatchEvent(newEvent);
// If any handler has called preventDefault(),
// then stop the TextField from accepting the text.
if (newEvent.isDefaultPrevented())
event.preventDefault();
}
/**
* @private
*/
private function textField_textFieldStyleChangeHandler(event:Event):void
{
textFieldChanged(true, false);
}
/**
* @private
*/
private function textField_textFormatChangeHandler(event:Event):void
{
textFieldChanged(true, false);
}
/**
* @private
*/
private function textField_textModifiedHandler(event:Event):void
{
textFieldChanged(false, true);
}
/**
* @private
* Changing the width of the text field can cause the text to reflow.
* Make sure our local _textWidth and _textHeight are in sync with text field.
*/
private function textField_textFieldWidthChangeHandler(event:Event):void
{
textFieldChanged(true, false);
}
}
}