blob: 4c9221135f8bf72437563138e95b807169a76978 [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 spark.components
{
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flashx.textLayout.operations.CompositeOperation;
import flashx.textLayout.operations.CutOperation;
import flashx.textLayout.operations.DeleteTextOperation;
import flashx.textLayout.operations.FlowOperation;
import mx.collections.IList;
import mx.core.IIMESupport;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import spark.components.supportClasses.DropDownListBase;
import spark.components.supportClasses.ListBase;
import spark.events.DropDownEvent;
import spark.events.TextOperationEvent;
import spark.utils.LabelUtil;
use namespace mx_internal;
/**
* Bottom inset, in pixels, for the text in the
* prompt area of the control.
*
* @default 3
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="paddingBottom", type="Number", format="Length", inherit="no")]
/**
* Left inset, in pixels, for the text in the
* prompt area of the control.
*
* @default 3
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="paddingLeft", type="Number", format="Length", inherit="no")]
/**
* Right inset, in pixels, for the text in the
* prompt area of the control.
*
* @default 3
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="paddingRight", type="Number", format="Length", inherit="no")]
/**
* Top inset, in pixels, for the text in the
* prompt area of the control.
*
* @default 5
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="paddingTop", type="Number", format="Length", inherit="no")]
//--------------------------------------
// Other metadata
//--------------------------------------
[AccessibilityClass(implementation="spark.accessibility.ComboBoxAccImpl")]
[IconFile("ComboBox.png")]
/**
* Because this component does not define a skin for the mobile theme, Adobe
* recommends that you not use it in a mobile application. Alternatively, you
* can define your own mobile skin for the component. For more information,
* see <a href="http://help.adobe.com/en_US/flex/mobileapps/WS19f279b149e7481c698e85712b3011fe73-8000.html">Basics of mobile skinning</a>.
*/
[DiscouragedForProfile("mobileDevice")]
/**
* The ComboBox control is a child class of the DropDownListBase control.
* Like the DropDownListBase control, when the user selects an item from
* the drop-down list in the ComboBox control, the data item appears
* in the prompt area of the control.
*
* <p>One difference between the controls is that the prompt area of
* the ComboBox control is implemented by using the TextInput control,
* instead of the Label control for the DropDownList control.
* Therefore, a user can edit the prompt area of the control to enter
* a value that is not one of the predefined options.</p>
*
* <p>For example, the DropDownList control only lets the user select
* from a list of predefined items in the control.
* The ComboBox control lets the user either select a predefined item,
* or enter a new item into the prompt area.
* When the user enters a new item, the <code>selectedIndex</code> property
* is set to -3.
* Your application can recognize that a new item has been entered and,
* optionally, add it to the list of items in the control.</p>
*
* <p>The ComboBox control also searches the item list as the user
* enters characters into the prompt area. As the user enters characters,
* the drop-down area of the control opens.
* It then scrolls to and highlights the closest match in the item list.</p>
*
* <p>To use this component in a list-based component, such as a List or DataGrid,
* create an item renderer.
* For information about creating an item renderer, see
* <a href="http://help.adobe.com/en_US/flex/using/WS4bebcd66a74275c3-fc6548e124e49b51c4-8000.html">
* Custom Spark item renderers</a>. </p>
*
* <p><b>Note: </b>The Spark list-based controls (the Spark ListBase class and its subclasses
* such as ButtonBar, ComboBox, DropDownList, List, and TabBar) do not support the BasicLayout class
* as the value of the <code>layout</code> property.
* Do not use BasicLayout with the Spark list-based controls.</p>
*
* <p>The ComboBox control has the following default characteristics:</p>
* <table class="innertable">
* <tr>
* <th>Characteristic</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>Default size</td>
* <td>146 pixels wide by 23 pixels high</td>
* </tr>
* <tr>
* <td>Minimum size</td>
* <td>20 pixels wide by 23 pixels high</td>
* </tr>
* <tr>
* <td>Maximum size</td>
* <td>10000 pixels wide and 10000 pixels high</td>
* </tr>
* <tr>
* <td>Default skin class</td>
* <td>spark.skins.spark.ComboBoxSkin
* <p>spark.skins.spark.ComboBoxTextInputSkin</p></td>
* </tr>
* </table>
*
* @mxml <p>The <code>&lt;s:ComboBox&gt;</code> tag inherits all of the tag
* attributes of its superclass and adds the following tag attributes:</p>
*
* <pre>
* &lt;s:ComboBox
* <strong>Properties</strong>
* itemMatchingFunction="null"
* labelToItemFunction="null"
* maxChars="0"
* openOnInput="true"
* prompt="null"
* restrict=""
*
* <strong>Styles</strong>
* paddingBottom="3"
* paddingLeft="3"
* paddingRight="3"
* paddingTop="5"
* /&gt;
* </pre>
*
* @see spark.skins.spark.ComboBoxSkin
* @see spark.skins.spark.ComboBoxTextInputSkin
* @includeExample examples/ComboBoxExample.mxml
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public class ComboBox extends DropDownListBase implements IIMESupport
{
//--------------------------------------------------------------------------
//
// Skin Parts
//
//--------------------------------------------------------------------------
/**
* Optional skin part that holds the input text or the selectedItem text.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[SkinPart(required="false")]
public var textInput:TextInput;
//--------------------------------------------------------------------------
//
// Class mixins
//
//--------------------------------------------------------------------------
/**
* @private
* Placeholder for mixin by ComboBoxAccImpl.
*/
mx_internal static var createAccessibilityImplementation:Function;
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function ComboBox()
{
super();
addEventListener(KeyboardEvent.KEY_DOWN, capture_keyDownHandler, true);
allowCustomSelectedItem = true;
}
//--------------------------------------------------------------------------
//
// Static Variables
//
//--------------------------------------------------------------------------
/**
* Static constant representing the value of the <code>selectedIndex</code> property
* when the user enters a value into the prompt area, and the value is committed.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public static const CUSTOM_SELECTED_ITEM:int = ListBase.CUSTOM_SELECTED_ITEM;
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
private var isTextInputInFocus:Boolean;
private var actualProposedSelectedIndex:Number = NO_SELECTION;
private var userTypedIntoText:Boolean;
private var previousTextInputText:String = "";
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// itemMatchingFunction
//--------------------------------------------------------------------------
/**
* Specifies a callback function used to search the item list as the user
* enters characters into the prompt area.
* As the user enters characters, the drop-down area of the control opens.
* It then and scrolls to and highlights the closest match in the item list.
*
* <p>The function referenced by this property takes an input string and returns
* the index of the items in the data provider that match the input.
* The items are returned as a Vector.&lt;int&gt; of indices in the data provider. </p>
*
* <p>The callback function must have the following signature: </p>
*
* <pre>
* function myMatchingFunction(comboBox:ComboBox, inputText:String):Vector.&lt;int&gt;</pre>
*
* <p>If the value of this property is null, the ComboBox finds matches
* using the default algorithm.
* By default, if an input string of length n is equivalent to the first n characters
* of an item (ignoring case), then it is a match to that item. For example, 'aRiz'
* is a match to "Arizona" while 'riz' is not.</p>
*
* <p>To disable search, create a callback function that returns an empty Vector.&lt;int&gt;.</p>
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var itemMatchingFunction:Function = null;
//----------------------------------
// dataProvider
//----------------------------------
[Inspectable(category="Data")]
/**
* @private
* Update the label if the dataProvider has changed
*/
override public function set dataProvider(value:IList):void
{
if (dataProvider === value)
return;
if (dataProvider != null)
selectedItem = null;
super.dataProvider = value;
}
//--------------------------------------------------------------------------
// labelToItemFunction
//--------------------------------------------------------------------------
private var _labelToItemFunction:Function;
private var labelToItemFunctionChanged:Boolean = false;
/**
* Specifies a callback function to convert a new value entered
* into the prompt area to the same data type as the data items in the data provider.
* The function referenced by this properly is called when the text in the prompt area
* is committed, and is not found in the data provider.
*
* <p>The callback function must have the following signature: </p>
*
* <pre>
* function myLabelToItem(value:String):Object</pre>
*
* <p>Where <code>value</code> is the String entered in the prompt area.
* The function returns an Object that is the same type as the items
* in the data provider.</p>
*
* <p>The default callback function returns <code>value</code>. </p>
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function set labelToItemFunction(value:Function):void
{
if (value == _labelToItemFunction)
return;
_labelToItemFunction = value;
labelToItemFunctionChanged = true;
invalidateProperties();
}
/**
* @private
*/
public function get labelToItemFunction():Function
{
return _labelToItemFunction;
}
//--------------------------------------------------------------------------
// maxChars
//--------------------------------------------------------------------------
private var _maxChars:int = 0;
private var maxCharsChanged:Boolean = false;
[Inspectable(category="General", defaultValue="0")]
/**
* The maximum number of characters that the prompt area can contain, as entered by a user.
* A value of 0 corresponds to no limit.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function set maxChars(value:int):void
{
if (value == _maxChars)
return;
_maxChars = value;
maxCharsChanged = true;
invalidateProperties();
}
/**
* @private
*/
public function get maxChars():int
{
return _maxChars;
}
//--------------------------------------------------------------------------
// openOnInput
//--------------------------------------------------------------------------
/**
* If <code>true</code>, the drop-down list opens when the user edits the prompt area.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public var openOnInput:Boolean = true;
//----------------------------------
// prompt
//----------------------------------
private var _prompt:String;
private var promptChanged:Boolean;
[Inspectable(category="General")]
/**
* Text to be displayed if/when the input text is null.
*
* <p>Prompt text appears when the control is first created. Prompt text disappears
* when the control gets focus, when the input text is non-null, or when an item in the list is selected.
* Prompt text reappears when the control loses focus, but only if no text was entered
* (if the value of the text field is null or the empty string).</p>
*
* <p>You can change the style of the prompt text with CSS. If the control has prompt text
* and is not disabled, the style is defined by the <code>normalWithPrompt</code> pseudo selector.
* If the control is disabled, then the styles defined by the <code>disabledWithPrompt</code> pseudo selector are used.</p>
*
* <p>The following example CSS changes the color of the prompt text in TextInput controls. The ComboBox control uses
* a TextInput control as a subcomponent for the prompt text and input, so its prompt text changes when you use this CSS:
* <pre>
* &#64;namespace s "library://ns.adobe.com/flex/spark";
* s|TextInput:normalWithPrompt {
* color: #CCCCFF;
* }
* </pre>
* </p>
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 10.2
* @playerversion AIR 2.0
* @productversion Flex 4.5
*/
public function get prompt():String
{
return _prompt;
}
/**
* @private
*/
public function set prompt(value:String):void
{
_prompt = value;
promptChanged = true;
invalidateProperties();
}
//--------------------------------------------------------------------------
// restrict
//--------------------------------------------------------------------------
private var _restrict:String;
private var restrictChanged:Boolean;
[Inspectable(category="General", defaultValue="")]
/**
* Specifies the set of characters that a user can enter into the prompt area.
* By default, the user can enter any characters, corresponding to a value of
* an empty string.
*
* @default ""
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function set restrict(value:String):void
{
if (value == _restrict)
return;
_restrict = value;
restrictChanged = true;
invalidateProperties();
}
/**
* @private
*/
public function get restrict():String
{
return _restrict;
}
//--------------------------------------------------------------------------
//
// Overridden Properties
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// baselinePosition
//--------------------------------------------------------------------------
/**
* @private
*/
override public function get baselinePosition():Number
{
return getBaselinePositionForPart(textInput);
}
//--------------------------------------------------------------------------
// selectedIndex
//--------------------------------------------------------------------------
[Inspectable(category="General", defaultValue="-1")]
/**
* @private
*/
override public function set selectedIndex(value:int):void
{
super.selectedIndex = value;
actualProposedSelectedIndex = value;
}
//--------------------------------------------------------------------------
// selectedItem
//--------------------------------------------------------------------------
/**
* @private
*/
override public function set selectedItem(value:*):void
{
// If selectedItem set to null or undefined make sure the label display gets cleared.
// The code at the bottom of commitProperties checks for this case.
if (value == null)
selectedIndex = NO_SELECTION;
super.selectedItem = value;
}
//--------------------------------------------------------------------------
// typicalItem
//--------------------------------------------------------------------------
private var typicalItemChanged:Boolean = false;
[Inspectable(category="Data")]
/**
* @private
*/
override public function set typicalItem(value:Object):void
{
if (value == typicalItem)
return;
super.typicalItem = value;
typicalItemChanged = true;
invalidateProperties();
}
//--------------------------------------------------------------------------
// userProposedSelectedIndex
//--------------------------------------------------------------------------
/**
* @private
*/
override mx_internal function set userProposedSelectedIndex(value:Number):void
{
super.userProposedSelectedIndex = value;
actualProposedSelectedIndex = value;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function processInputField():void
{
var matchingItems:Vector.<int>;
// If the textInput has been changed, then use the input string as the selectedItem
actualProposedSelectedIndex = CUSTOM_SELECTED_ITEM;
// Even if there is no data provider, we still want to allow custom items.
if (!dataProvider || dataProvider.length <= 0)
return;
if (textInput.text != "")
{
if (itemMatchingFunction != null)
matchingItems = itemMatchingFunction(this, textInput.text);
else
matchingItems = findMatchingItems(textInput.text);
if (matchingItems.length > 0)
{
super.changeHighlightedSelection(matchingItems[0], true);
var typedLength:int = textInput.text.length;
var item:Object = dataProvider ? dataProvider.getItemAt(matchingItems[0]) : undefined;
if (item)
{
// If we found a match, then replace the textInput text with the match and
// select the non-typed characters
var itemString:String = itemToLabel(item);
textInput.selectAll();
textInput.insertText(itemString);
textInput.selectRange(typedLength, itemString.length);
}
}
else
{
super.changeHighlightedSelection(CUSTOM_SELECTED_ITEM);
}
}
else
{
// If the input string is empty, then don't select anything
super.changeHighlightedSelection(NO_SELECTION);
}
}
/**
* @private
*/
// Returns an array of possible values
private function findMatchingItems(input:String):Vector.<int>
{
// For now, just select the first match
var startIndex:int;
var stopIndex:int;
var retVal:int;
var retVector:Vector.<int> = new Vector.<int>;
retVal = findStringLoop(input, 0, dataProvider.length);
if (retVal != -1)
retVector.push(retVal);
return retVector;
}
/**
* @private
*/
private function getCustomSelectedItem():*
{
// Grab the text from the textInput and process it through labelToItemFunction
var input:String = textInput.text;
if (input == "")
return undefined;
else if (labelToItemFunction != null)
return _labelToItemFunction(input);
else
return input;
}
/**
* @private
* Helper function to apply the textInput text to selectedItem
*/
mx_internal function applySelection():void
{
if (actualProposedSelectedIndex == CUSTOM_SELECTED_ITEM)
{
var itemFromInput:* = getCustomSelectedItem();
if (itemFromInput != undefined)
setSelectedItem(itemFromInput, true);
else
setSelectedIndex(NO_SELECTION, true);
}
else
{
setSelectedIndex(actualProposedSelectedIndex, true);
}
if (textInput)
textInput.selectRange(-1, -1);
userTypedIntoText = false;
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function initializeAccessibility():void
{
if (ComboBox.createAccessibilityImplementation != null)
ComboBox.createAccessibilityImplementation(this);
}
/**
* @private
*/
override protected function commitProperties():void
{
// Keep track of whether selectedIndex was programmatically changed
var selectedIndexChanged:Boolean = _proposedSelectedIndex != NO_PROPOSED_SELECTION;
// If selectedIndex was set to CUSTOM_SELECTED_ITEM, and no selectedItem was specified,
// then don't change the selectedIndex
if (_proposedSelectedIndex == CUSTOM_SELECTED_ITEM &&
_pendingSelectedItem == undefined)
{
_proposedSelectedIndex = NO_PROPOSED_SELECTION;
}
super.commitProperties();
if (textInput)
{
if (maxCharsChanged)
{
textInput.maxChars = _maxChars;
maxCharsChanged = false;
}
if (promptChanged)
{
textInput.prompt = _prompt;
promptChanged = false;
}
if (restrictChanged)
{
textInput.restrict = _restrict;
restrictChanged = false;
}
if (typicalItemChanged)
{
if (typicalItem != null)
{
var itemString:String = LabelUtil.itemToLabel(typicalItem, labelField, labelFunction);
textInput.typicalText = itemString;
}
else
{
// Just set it back to the default value
textInput.widthInChars = 10;
}
typicalItemChanged = false;
}
// Clear the TextInput because we were programmatically set to NO_SELECTION
// We call this after super.commitProperties because commitSelection might have
// changed the value to NO_SELECTION
if (selectedIndexChanged && selectedIndex == NO_SELECTION)
textInput.text = "";
}
}
/**
* @private
*/
override mx_internal function updateLabelDisplay(displayItem:* = undefined):void
{
super.updateLabelDisplay();
if (textInput)
{
if (displayItem == undefined)
displayItem = selectedItem;
if (displayItem != null && displayItem != undefined)
{
textInput.text = LabelUtil.itemToLabel(displayItem, labelField, labelFunction);
}
}
}
/**
* @private
*/
override protected function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
if (instance == textInput)
{
updateLabelDisplay();
textInput.addEventListener(TextOperationEvent.CHANGE, textInput_changeHandler);
textInput.addEventListener(TextOperationEvent.CHANGING, textInput_changingHandler);
textInput.addEventListener(FocusEvent.FOCUS_IN, textInput_focusInHandler, true);
textInput.addEventListener(FocusEvent.FOCUS_OUT, textInput_focusOutHandler, true);
textInput.maxChars = maxChars;
textInput.restrict = restrict;
textInput.focusEnabled = false;
if (textInput.textDisplay is RichEditableText)
RichEditableText(textInput.textDisplay).batchTextInput = false;
}
}
/**
* @private
*/
override protected function partRemoved(partName:String, instance:Object):void
{
super.partRemoved(partName, instance);
if (instance == textInput)
{
textInput.removeEventListener(TextOperationEvent.CHANGE, textInput_changeHandler);
textInput.removeEventListener(TextOperationEvent.CHANGING, textInput_changingHandler);
textInput.removeEventListener(FocusEvent.FOCUS_IN, textInput_focusInHandler, true);
textInput.removeEventListener(FocusEvent.FOCUS_OUT, textInput_focusOutHandler, true);
}
}
/**
* @private
*/
override public function set enabled(value:Boolean):void
{
if (enabled == value)
return;
super.enabled = value;
}
/**
* @private
*/
override mx_internal function changeHighlightedSelection(newIndex:int, scrollToTop:Boolean = false):void
{
super.changeHighlightedSelection(newIndex, scrollToTop);
if (newIndex >= 0 && newIndex < dataProvider.length)
{
var item:Object = dataProvider ? dataProvider.getItemAt(newIndex) : undefined;
if (item && textInput)
{
var itemString:String = itemToLabel(item);
textInput.selectAll();
textInput.insertText(itemString);
textInput.selectAll();
userTypedIntoText = false;
}
}
}
/**
* @private
*/
override mx_internal function findKey(eventCode:int):Boolean
{
return false;
}
/**
* @copy spark.components.supportClasses.ListBase#setSelectedIndex()
*
* @langversion 3.0
* @playerversion Flash 11.1
* @playerversion AIR 3.4
* @productversion Flex 4.10
*/
override public function setSelectedIndex(value:int, dispatchChangeEvent:Boolean = false, changeCaret:Boolean = true):void
{
// It is possible that the label display changed but the selection didn't. If this is
// the case, the label has to be updated since the setSelectedIndex code will short-circuit
// and not commit the selection.
// An example is if the label is deleted and then the first item is chosen from the
// dropdown, the selectedIndex is still 0.
if (userTypedIntoText && value == selectedIndex)
updateLabelDisplay();
super.setSelectedIndex(value, dispatchChangeEvent, changeCaret);
}
// If the TextInput is in focus, listen for keyDown events in the capture phase so that
// we can process the navigation keys (UP/DOWN, PGUP/PGDN, HOME/END). If the ComboBox is in
// focus, just handle keyDown events in the bubble phase
/**
* @private
*/
override protected function keyDownHandler(event:KeyboardEvent) : void
{
if (!isTextInputInFocus)
keyDownHandlerHelper(event);
}
/**
* @private
*/
protected function capture_keyDownHandler(event:KeyboardEvent):void
{
if (isTextInputInFocus)
keyDownHandlerHelper(event);
}
/**
* @private
*/
mx_internal function keyDownHandlerHelper(event:KeyboardEvent):void
{
super.keyDownHandler(event);
if (event.keyCode == Keyboard.ENTER && !isDropDownOpen)
{
// commit the current text
applySelection();
}
else if (event.keyCode == Keyboard.ESCAPE)
{
// Restore the previous selectedItem
if (textInput)
{
if (selectedItem != null)
textInput.text = itemToLabel(selectedItem);
else
textInput.text = "";
}
changeHighlightedSelection(selectedIndex);
}
}
/**
* @private
*/
override public function setFocus():void
{
if (stage && textInput)
{
stage.focus = textInput.textDisplay as InteractiveObject;
}
}
/**
* @private
*/
override protected function isOurFocus(target:DisplayObject):Boolean
{
if (!textInput)
return false;
return target == textInput.textDisplay;
}
/**
* @private
*/
override protected function focusInHandler(event:FocusEvent):void
{
super.focusInHandler(event);
// Since the API ignores the visual editable and selectable
// properties make sure the selection should be set first.
if (textInput &&
(textInput.editable || textInput.selectable))
{
// Workaround RET handling the mouse and performing its own selection logic
callLater(textInput.selectAll);
}
userTypedIntoText = false;
}
/**
* @private
*/
override protected function focusOutHandler(event:FocusEvent):void
{
// always commit the selection if we focus out
if (!isDropDownOpen)
{
if (textInput &&
((selectedItem == null && textInput.text != "") ||
textInput.text != itemToLabel(selectedItem)))
applySelection();
}
dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
super.focusOutHandler(event);
}
/**
* @private
*/
override mx_internal function dropDownController_openHandler(event:DropDownEvent):void
{
super.dropDownController_openHandler(event);
// If the user typed in text, start off by not showing any selection
// If this does match, then processInputField will highlight the match
userProposedSelectedIndex = userTypedIntoText ? NO_SELECTION : selectedIndex;
}
/**
* @private
*/
override protected function dropDownController_closeHandler(event:DropDownEvent):void
{
super.dropDownController_closeHandler(event);
// Commit the textInput text as the selection
if (!event.isDefaultPrevented())
{
applySelection();
}
}
/**
* @private
*/
override protected function itemRemoved(index:int):void
{
if (index == selectedIndex)
updateLabelDisplay("");
super.itemRemoved(index);
}
//--------------------------------------------------------------------------
//
// Event Handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function textInput_focusInHandler(event:FocusEvent):void
{
isTextInputInFocus = true;
}
/**
* @private
*/
private function textInput_focusOutHandler(event:FocusEvent):void
{
isTextInputInFocus = false;
}
/**
* @private
*/
private function textInput_changingHandler(event:TextOperationEvent):void
{
previousTextInputText = textInput.text;
}
/**
* @private
*/
protected function textInput_changeHandler(event:TextOperationEvent):void
{
userTypedIntoText = true;
var operation:FlowOperation = event.operation;
// TLF is batching some operations so it can undo them. If it is a composite operation
// look at the last one to figure out if it was a delete.
var deleteText:Boolean = (operation is DeleteTextOperation || operation is CutOperation);
if (operation is CompositeOperation)
{
const operations:Array = CompositeOperation(operation).operations;
if (operations.length && operations[operations.length-1] is DeleteTextOperation)
deleteText = true;
}
// If deleting text do not want to do item completion or it isn't possible to delete
// individual characters. If the combo is open, leave it open, even if all the text
// is deleted.
if (deleteText)
{
// To commit the selection correctly on close, applySelection needs this set.
actualProposedSelectedIndex = CUSTOM_SELECTED_ITEM;
// Update the selected item in the list.
super.changeHighlightedSelection(CUSTOM_SELECTED_ITEM);
}
else if (previousTextInputText != textInput.text)
{
if (openOnInput)
{
if (!isDropDownOpen)
{
// Open the dropDown if it isn't already open
openDropDown();
addEventListener(DropDownEvent.OPEN, editingOpenHandler);
return;
}
}
processInputField();
}
}
/**
* @private
*/
private function editingOpenHandler(event:DropDownEvent):void
{
removeEventListener(DropDownEvent.OPEN, editingOpenHandler);
processInputField();
}
//----------------------------------
// enableIME
//----------------------------------
/**
* @copy spark.components.TextInput#enableIME
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get enableIME():Boolean
{
if (textInput)
{
return textInput.enableIME;
}
return false;
}
//----------------------------------
// imeMode
//----------------------------------
/**
* @copy spark.components.TextInput#imeMode
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get imeMode():String
{
if (textInput)
{
return textInput.imeMode;
}
return null;
}
/**
* @public
*/
public function set imeMode(value:String):void
{
if (textInput)
{
textInput.imeMode = value;
invalidateProperties();
}
}
}
}