| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.display.DisplayObject; |
| import flash.events.Event; |
| import flash.events.FocusEvent; |
| import flash.events.KeyboardEvent; |
| import flash.events.MouseEvent; |
| import flash.geom.Matrix; |
| import flash.geom.Point; |
| import flash.geom.Rectangle; |
| import flash.text.TextLineMetrics; |
| import flash.ui.Keyboard; |
| |
| import mx.collections.ArrayCollection; |
| import mx.collections.CursorBookmark; |
| import mx.controls.dataGridClasses.DataGridListData; |
| import mx.controls.listClasses.BaseListData; |
| import mx.controls.listClasses.IDropInListItemRenderer; |
| import mx.controls.listClasses.IListItemRenderer; |
| import mx.controls.listClasses.ListBase; |
| import mx.controls.listClasses.ListData; |
| import mx.core.ClassFactory; |
| import mx.core.EdgeMetrics; |
| import mx.core.IDataRenderer; |
| import mx.core.IFactory; |
| import mx.core.LayoutDirection; |
| import mx.core.ScrollPolicy; |
| import mx.core.UIComponent; |
| import mx.core.UIComponentGlobals; |
| import mx.core.mx_internal; |
| import mx.effects.Tween; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.DropdownEvent; |
| import mx.events.FlexEvent; |
| import mx.events.FlexMouseEvent; |
| import mx.events.InterManagerRequest; |
| import mx.events.ListEvent; |
| import mx.events.SandboxMouseEvent; |
| import mx.events.ScrollEvent; |
| import mx.events.ScrollEventDetail; |
| import mx.managers.ISystemManager; |
| import mx.managers.PopUpManager; |
| import mx.styles.CSSStyleDeclaration; |
| import mx.utils.MatrixUtil; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Events |
| //-------------------------------------- |
| |
| /** |
| * Dispatched when the ComboBox contents changes as a result of user |
| * interaction, when the <code>selectedIndex</code> or |
| * <code>selectedItem</code> property changes, and, if the ComboBox control |
| * is editable, each time a keystroke is entered in the box. |
| * |
| * @eventType mx.events.ListEvent.CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="change", type="mx.events.ListEvent")] |
| |
| /** |
| * Dispatched when the drop-down list is dismissed for any reason such when |
| * the user: |
| * <ul> |
| * <li>selects an item in the drop-down list</li> |
| * <li>clicks outside of the drop-down list</li> |
| * <li>clicks the drop-down button while the drop-down list is |
| * displayed</li> |
| * <li>presses the ESC key while the drop-down list is displayed</li> |
| * </ul> |
| * |
| * @eventType mx.events.DropdownEvent.CLOSE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="close", type="mx.events.DropdownEvent")] |
| |
| /** |
| * Dispatched when the <code>data</code> property changes. |
| * |
| * <p>When you use a component as an item renderer, |
| * the <code>data</code> property contains an item from the |
| * dataProvider. |
| * 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 if the <code>editable</code> property |
| * is set to <code>true</code> and the user presses the Enter key |
| * while typing in the editable text field. |
| * |
| * @eventType mx.events.FlexEvent.ENTER |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="enter", type="mx.events.FlexEvent")] |
| |
| /** |
| * Dispatched when user rolls the mouse out of a drop-down list item. |
| * The event object's <code>target</code> property contains a reference |
| * to the ComboBox and not the drop-down list. |
| * |
| * @eventType mx.events.ListEvent.ITEM_ROLL_OUT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemRollOut", type="mx.events.ListEvent")] |
| |
| /** |
| * Dispatched when the user rolls the mouse over a drop-down list item. |
| * The event object's <code>target</code> property contains a reference |
| * to the ComboBox and not the drop-down list. |
| * |
| * @eventType mx.events.ListEvent.ITEM_ROLL_OVER |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemRollOver", type="mx.events.ListEvent")] |
| |
| /** |
| * Dispatched when the user clicks the drop-down button |
| * to display the drop-down list. It is also dispatched if the user |
| * uses the keyboard and types Ctrl-Down to open the drop-down. |
| * |
| * @eventType mx.events.DropdownEvent.OPEN |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="open", type="mx.events.DropdownEvent")] |
| |
| /** |
| * Dispatched when the user scrolls the ComboBox control's drop-down list. |
| * |
| * @eventType mx.events.ScrollEvent.SCROLL |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="scroll", type="mx.events.ScrollEvent")] |
| |
| //-------------------------------------- |
| // Styles |
| //-------------------------------------- |
| |
| include "../styles/metadata/FocusStyles.as" |
| include "../styles/metadata/IconColorStyles.as" |
| include "../styles/metadata/LeadingStyle.as" |
| include "../styles/metadata/PaddingStyles.as" |
| include "../styles/metadata/SkinStyles.as" |
| include "../styles/metadata/TextStyles.as" |
| |
| /** |
| * The set of BackgroundColors for drop-down list rows in an alternating |
| * pattern. |
| * Value can be an Array of two of more colors. |
| * If <code>undefined</code> then the rows will use the drop-down list's |
| * backgroundColor style. |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="alternatingItemColors", type="Array", arrayType="uint", format="Color", inherit="yes")] |
| |
| /** |
| * Width of the arrow button in pixels. |
| * |
| * The default value for the Halo theme is 22. |
| * The default value for the Spark theme is 18. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="arrowButtonWidth", type="Number", format="Length", inherit="no")] |
| |
| /** |
| * The thickness of the border of the drop-down list, in pixels. |
| * This value is overridden if you define |
| * <code>borderThickness</code> when setting the |
| * <code>dropdownStyleName</code> CSSStyleDeclaration. |
| * |
| * @default 1 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="borderThickness", type="Number", format="Length", inherit="no")] |
| |
| /** |
| * The length of the transition when the drop-down list closes, in milliseconds. |
| * The default transition has the drop-down slide up into the ComboBox. |
| * |
| * The default value for the Halo theme is 250. |
| * The default value for the Spark theme is 50. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="closeDuration", type="Number", format="Time", inherit="no")] |
| |
| /** |
| * An easing function to control the close transition. Easing functions can |
| * be used to control the acceleration and deceleration of the transition. |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="closeEasingFunction", type="Function", inherit="no")] |
| |
| /** |
| * The color of the border of the ComboBox. If <code>undefined</code> |
| * the drop-down list will use its normal borderColor style. This style |
| * is used by the validators to show the ComboBox in an error state. |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="dropdownBorderColor", type="uint", format="Color", inherit="yes", theme="halo")] |
| |
| /** |
| * The name of a CSSStyleDeclaration to be used by the drop-down list. This |
| * allows you to control the appearance of the drop-down list or its item |
| * renderers. |
| * |
| * [deprecated] |
| * |
| * @default "comboDropDown" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="dropDownStyleName", type="String", inherit="no", deprecatedReplacement="dropdownStyleName")] |
| |
| /** |
| * The name of a CSSStyleDeclaration to be used by the drop-down list. This |
| * allows you to control the appearance of the drop-down list or its item |
| * renderers. |
| * |
| * @default "comboDropdown" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="dropdownStyleName", type="String", inherit="no")] |
| |
| /** |
| * Length of the transition when the drop-down list opens, in milliseconds. |
| * The default transition has the drop-down slide down from the ComboBox. |
| * |
| * The default value for the Halo theme is 250. |
| * The default value for the Spark theme is 0. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="openDuration", type="Number", format="Time", inherit="no")] |
| |
| /** |
| * An easing function to control the open transition. Easing functions can |
| * be used to control the acceleration and deceleration of the transition. |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="openEasingFunction", type="Function", inherit="no")] |
| |
| /** |
| * Number of pixels between the control's bottom border |
| * and the bottom of its content area. |
| * When the <code>editable</code> property is <code>true</code>, |
| * <code>paddingTop</code> and <code>paddingBottom</code> affect the size |
| * of the ComboBox control, but do not affect the position of the editable text field. |
| * |
| * The default value for the Halo theme is 0. |
| * The default value for the Spark theme is -2. |
| * |
| * @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 control's top border |
| * and the top of its content area. |
| * When the <code>editable</code> property is <code>true</code>, |
| * <code>paddingTop</code> and <code>paddingBottom</code> affect the size |
| * of the ComboBox control, but do not affect the position of the editable text field. |
| * |
| * The default value for the Halo theme is 0. |
| * The default value for the Spark theme is -1. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="paddingTop", type="Number", format="Length", inherit="no")] |
| |
| /** |
| * The rollOverColor of the drop-down list. |
| * |
| * The default value for the Halo theme is <code>0xB2E1FF</code>. |
| * The default value for the Spark theme is <code>0xCEDBEF</code>. |
| * |
| * @see mx.controls.List |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="rollOverColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The selectionColor of the drop-down list. |
| * |
| * The default value for the Halo theme is <code>0x7FCEFF</code>. |
| * The default value for the Spark theme is <code>0xA8C6EE</code>. |
| * |
| * @see mx.controls.List |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="selectionColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The selectionDuration of the drop-down list. |
| * |
| * @default 250 |
| * |
| * @see mx.controls.List |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="selectionDuration", type="uint", format="Time", inherit="no")] |
| |
| /** |
| * The selectionEasingFunction of the drop-down list. |
| * |
| * @default undefined |
| * |
| * @see mx.controls.List |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="selectionEasingFunction", type="Function", inherit="no")] |
| |
| /** |
| * The textRollOverColor of the drop-down list. |
| * |
| * The default value for the Halo theme is <code>0x2B333C</code>. |
| * The default value for the Spark theme is <code>0x000000</code>. |
| * |
| * @see mx.controls.List |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="textRollOverColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The textSelectedColor of the drop-down list. |
| * |
| * The default value for the Halo theme is <code>0x2B333C</code>. |
| * The default value for the Spark theme is <code>0x000000</code>. |
| * |
| * @see mx.controls.List |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="textSelectedColor", type="uint", format="Color", inherit="yes")] |
| |
| //-------------------------------------- |
| // Other metadata |
| //-------------------------------------- |
| |
| [AccessibilityClass(implementation="mx.accessibility.ComboBoxAccImpl")] |
| |
| [DataBindingInfo("acceptedTypes", "{ dataProvider: { label: "String" } }")] |
| |
| [DefaultBindingProperty(source="selectedItem", destination="dataProvider")] |
| |
| [DefaultProperty("dataProvider")] |
| |
| [DefaultTriggerEvent("change")] |
| |
| [IconFile("ComboBox.png")] |
| |
| [Alternative(replacement="spark.components.DropDownList", since="4.0")] |
| [Alternative(replacement="spark.components.ComboBox", since="4.0")] |
| |
| /** |
| * The ComboBox control contains a drop-down list |
| * from which the user can select a single value. |
| * Its functionality is very similar to that of the |
| * SELECT form element in HTML. |
| * The ComboBox can be editable, in which case |
| * the user can type entries into the TextInput portion |
| * of the ComboBox that are not in the list. |
| * |
| * <p>The ComboBox 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>Wide enough to accommodate the longest entry in the |
| * drop-down list in the display area of the main |
| * control, plus the drop-down button. When the |
| * drop-down list is not visible, the default height |
| * is based on the label text size. |
| * |
| * <p>The default drop-down list height is five rows, or |
| * the number of entries in the drop-down list, whichever |
| * is smaller. The default height of each entry in the |
| * drop-down list is 22 pixels.</p></td> |
| * </tr> |
| * <tr> |
| * <td>Minimum size</td> |
| * <td>0 pixels.</td> |
| * </tr> |
| * <tr> |
| * <td>Maximum size</td> |
| * <td>5000 by 5000.</td> |
| * </tr> |
| * <tr> |
| * <td>dropdownWidth</td> |
| * <td>The width of the ComboBox control.</td> |
| * </tr> |
| * <tr> |
| * <td>rowCount</td> |
| * <td>5 rows.</td> |
| * </tr> |
| * </table> |
| * |
| * @mxml |
| * |
| * <p>The <code><mx:ComboBox></code> tag inherits all the tag attributes |
| * of its superclass, and adds the following tag attributes:</p> |
| * |
| * <pre> |
| * <mx:ComboBox |
| * <b>Properties</b> |
| * dataProvider="null" |
| * dropdownFactory="<i>ClassFactory that creates an mx.controls.List</i>" |
| * dropdownWidth="<i>100 or width of the longest text in the dataProvider</i>" |
| * itemRenderer="null" |
| * labelField="label" |
| * labelFunction="null" |
| * prompt="null" |
| * rowCount="5" |
| * selectedIndex="-1" |
| * selectedItem="null" |
| * |
| * <b>Styles</b> |
| * alternatingItemColors="undefined" |
| * arrowButtonWidth="22" |
| * borderColor="0xB7BABC" |
| * borderThickness="1" |
| * closeDuration="250" |
| * closeEasingFunction="undefined" |
| * color="0x0B333C" |
| * cornerRadius="0" |
| * disabledColor="0xAAB3B3" |
| * disabledIconColor="0x919999" |
| * dropdownBorderColor="undefined" |
| * dropdownStyleName="comboDropdown" |
| * fillAlphas="[0.6,0.4]" |
| * fillColors="[0xFFFFFF, 0xCCCCCC]" |
| * focusAlpha="0.4" |
| * focusRoundedCorners="tl tr bl br" |
| * fontAntiAliasType="advanced|normal" |
| * fontFamily="Verdana" |
| * fontGridFitType="pixel|none|subpixel" |
| * fontSharpness="0" |
| * fontSize="10" |
| * fontStyle="normal|italic" |
| * fontThickness="0" |
| * fontWeight="normal|bold" |
| * highlightAlphas="[0.3,0.0]" |
| * iconColor="0x111111" |
| * leading="0" |
| * openDuration="250" |
| * openEasingFunction="undefined" |
| * paddingTop="0" |
| * paddingBottom="0" |
| * paddingLeft="5" |
| * paddingRight="5" |
| * rollOverColor="<i>Depends on theme color"</i> |
| * selectionColor="<i>Depends on theme color"</i> |
| * selectionDuration="250" |
| * selectionEasingFunction="undefined" |
| * textAlign="left|center|right" |
| * textDecoration="none|underline" |
| * textIndent="0" |
| * textRollOverColor="0x2B333C" |
| * textSelectedColor="0x2B333C" |
| * |
| * <b>Events</b> |
| * change="<i>No default</i>" |
| * close="<i>No default</i>" |
| * dataChange="<i>No default</i>" |
| * enter="<i>No default</i>" |
| * itemRollOut="<i>No default</i>" |
| * itemRollOver="<i>No default</i>" |
| * open="<i>No default</i>" |
| * scroll="<i>No default</i>" |
| * /> |
| * </pre> |
| * |
| * @includeExample examples/SimpleComboBox.mxml |
| * |
| * @see mx.controls.List |
| * @see mx.effects.Tween |
| * @see mx.managers.PopUpManager |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class ComboBox extends ComboBase |
| implements IDataRenderer, IDropInListItemRenderer, |
| IListItemRenderer |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class mixins |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Placeholder for mixin by ComboBoxAccImpl. |
| */ |
| mx_internal static var createAccessibilityImplementation:Function; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function ComboBox() |
| { |
| super(); |
| |
| // It it better to start out with an empty data provider rather than |
| // an undefined one. Otherwise, code in getDropdown() sets it to [] |
| // later, but via setDataProvider(). This API has side effects like |
| // setting selectionChanged, which causes the text in an editable |
| // ComboBox to be lost. |
| dataProvider = new ArrayCollection(); |
| |
| useFullDropdownSkin = true; |
| wrapDownArrowButton = false; |
| addEventListener("unload", unloadHandler); |
| addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * A reference to the internal List that pops up to display a row |
| * for each dataProvider item. |
| */ |
| private var _dropdown:ListBase; |
| |
| /** |
| * @private |
| * A int to track the oldIndex, used when the dropdown is dismissed using the ESC key. |
| */ |
| private var _oldIndex:int; |
| |
| /** |
| * @private |
| * The tween used for showing and hiding the drop-down list. |
| */ |
| private var tween:Tween = null; |
| |
| /** |
| * @private |
| * A flag to track whether the dropDown tweened up or down. |
| */ |
| private var tweenUp:Boolean = false; |
| |
| |
| /** |
| * @private |
| */ |
| private var preferredDropdownWidth:Number; |
| |
| /** |
| * @private |
| */ |
| private var dropdownBorderStyle:String = "solid"; |
| |
| /** |
| * @private |
| * Is the dropdown list currently shown? |
| */ |
| private var _showingDropdown:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var _selectedIndexOnDropdown:int = -1; |
| |
| /** |
| * @private |
| */ |
| private var bRemoveDropdown:Boolean = true; |
| |
| /** |
| * @private |
| */ |
| private var inTween:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var bInKeyDown:Boolean = false; |
| |
| /** |
| * @private |
| * Flag that will block default data/listData behavior |
| */ |
| private var selectedItemSet:Boolean; |
| |
| /** |
| * @private |
| * Event that is causing the dropDown to open or close. |
| */ |
| private var triggerEvent:Event; |
| |
| /** |
| * @private |
| * Whether the text property was explicitly set or not |
| */ |
| private var explicitText:Boolean; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // data |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the data property. |
| */ |
| private var _data:Object; |
| |
| [Bindable("dataChange")] |
| [Inspectable(environment="none")] |
| |
| /** |
| * The <code>data</code> property 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>The ComboBox control uses the <code>listData</code> property and the |
| * <code>data</code> property as follows. If the ComboBox is in a |
| * DataGrid control, it expects the <code>dataField</code> property of the |
| * column to map to a property in the data and sets |
| * <code>selectedItem</code> to that property. If the ComboBox control is |
| * in a List control, it expects the <code>labelField</code> of the list |
| * to map to a property in the data and sets <code>selectedItem</code> to |
| * that property. |
| * Otherwise, it sets <code>selectedItem</code> to the data itself.</p> |
| * |
| * <p>You do not set this property in MXML.</p> |
| * |
| * @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 newSelectedItem:*; |
| |
| _data = value; |
| |
| if (_listData && _listData is DataGridListData) |
| newSelectedItem = _data[DataGridListData(_listData).dataField]; |
| else if (_listData is ListData && ListData(_listData).labelField in _data) |
| newSelectedItem = _data[ListData(_listData).labelField]; |
| else |
| newSelectedItem = _data; |
| |
| if (newSelectedItem !== undefined && !selectedItemSet) |
| { |
| selectedItem = newSelectedItem; |
| selectedItemSet = false; |
| } |
| |
| dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); |
| } |
| |
| //---------------------------------- |
| // 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 and the |
| * <code>data</code> property to display the appropriate information |
| * as a 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> |
| * |
| * @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; |
| } |
| |
| //---------------------------------- |
| // dataProvider |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var collectionChanged:Boolean = false; |
| |
| [Bindable("collectionChange")] |
| [Inspectable(category="Data", arrayType="Object")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function set dataProvider(value:Object):void |
| { |
| selectionChanged = true; |
| |
| super.dataProvider = value; |
| |
| destroyDropdown(); |
| |
| invalidateProperties(); |
| invalidateSize(); |
| } |
| |
| //---------------------------------- |
| // itemRenderer |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for itemRenderer property. |
| */ |
| private var _itemRenderer:IFactory; |
| |
| [Inspectable(category="Data")] |
| |
| /** |
| * IFactory that generates the instances that displays the data for the |
| * drop-down list of the control. You can use this property to specify |
| * a custom item renderer for the drop-down list. |
| * |
| * <p>The control uses a List control internally to create the drop-down |
| * list. |
| * The default item renderer for the List control is the ListItemRenderer |
| * class, which draws the text associated with each item in the list, |
| * and an optional icon. </p> |
| * |
| * @see mx.controls.List |
| * @see mx.controls.listClasses.ListItemRenderer |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get itemRenderer():IFactory |
| { |
| return _itemRenderer; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set itemRenderer(value:IFactory):void |
| { |
| _itemRenderer = value; |
| |
| if (_dropdown) |
| _dropdown.itemRenderer = value; |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| |
| dispatchEvent(new Event("itemRendererChanged")); |
| } |
| |
| //---------------------------------- |
| // selectedIndex |
| //---------------------------------- |
| |
| [Bindable("change")] |
| [Bindable("collectionChange")] |
| [Bindable("valueCommit")] |
| [Inspectable(category="General", defaultValue="0")] |
| |
| /** |
| * Index of the selected item in the drop-down list. |
| * Setting this property sets the current index and displays |
| * the associated label in the TextInput portion. |
| * <p>The default value is -1, but it set to 0 |
| * when a <code>dataProvider</code> is assigned, unless there is a prompt. |
| * If the control is editable, and the user types in the TextInput portion, |
| * the value of the <code>selectedIndex</code> property becomes |
| * -1. If the value of the <code>selectedIndex</code> |
| * property is out of range, the <code>selectedIndex</code> property is set to the last |
| * item in the <code>dataProvider</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function set selectedIndex(value:int):void |
| { |
| super.selectedIndex = value; |
| |
| if (value >= 0) |
| selectionChanged = true; |
| |
| implicitSelectedIndex = false; |
| invalidateDisplayList(); |
| |
| // value committed event needs the text to be set |
| if (textInput && !textChanged && value >= 0) |
| textInput.text = selectedLabel; |
| else if (textInput && prompt) |
| textInput.text = prompt; |
| |
| // [Matt] setting the text of the textInput should take care of this now |
| // Send a valueCommit event, which is used by the data model |
| //dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT)); |
| } |
| |
| //---------------------------------- |
| // selectedItem |
| //---------------------------------- |
| |
| [Bindable("change")] |
| [Bindable("collectionChange")] |
| [Bindable("valueCommit")] |
| |
| /** |
| * Contains a reference to the selected item in the |
| * <code>dataProvider</code>. |
| * If the data is an object or class instance, modifying |
| * properties in the object or instance modifies the <code>dataProvider</code> |
| * and thus its views. Setting the selectedItem itself causes the |
| * ComboBox to select that item (display it in the TextInput portion and set |
| * the selectedIndex) if it exists in the dataProvider. |
| * <p>If the ComboBox control is editable, the <code>selectedItem</code> |
| * property is <code>null</code> if the user types any text |
| * into the TextInput. |
| * It has a value only if the user selects an item from the drop-down |
| * list, or if it is set programmatically.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function set selectedItem(value:Object):void |
| { |
| selectedItemSet = true; |
| |
| // We do not want to apply an implicit default index in this case. |
| implicitSelectedIndex = false; |
| |
| super.selectedItem = value; |
| } |
| |
| //---------------------------------- |
| // showInAutomationHierarchy |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function set showInAutomationHierarchy(value:Boolean):void |
| { |
| //do not allow value changes |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // dropdown |
| //---------------------------------- |
| |
| /** |
| * A reference to the List control that acts as the drop-down in the ComboBox. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get dropdown():ListBase |
| { |
| return getDropdown(); |
| } |
| |
| //---------------------------------- |
| // dropdownFactory |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the dropdownFactory property. |
| */ |
| private var _dropdownFactory:IFactory = new ClassFactory(List); |
| |
| [Bindable("dropdownFactoryChanged")] |
| |
| /** |
| * The IFactory that creates a ListBase-derived instance to use |
| * as the drop-down. |
| * The default value is an IFactory for List |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get dropdownFactory():IFactory |
| { |
| return _dropdownFactory; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set dropdownFactory(value:IFactory):void |
| { |
| _dropdownFactory = value; |
| |
| dispatchEvent(new Event("dropdownFactoryChanged")); |
| } |
| |
| //---------------------------------- |
| // dropDownStyleFilters |
| //---------------------------------- |
| |
| /** |
| * The set of styles to pass from the ComboBox to the dropDown. |
| * Styles in the dropDownStyleName style will override these styles. |
| * @see mx.styles.StyleProxy |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function get dropDownStyleFilters():Object |
| { |
| return null; |
| } |
| |
| //---------------------------------- |
| // dropdownWidth |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the dropdownWidth property. |
| */ |
| private var _dropdownWidth:Number = 100; |
| |
| [Bindable("dropdownWidthChanged")] |
| [Inspectable(category="Size", defaultValue="100")] |
| |
| /** |
| * Width of the drop-down list, in pixels. |
| * <p>The default value is 100 or the width of the longest text |
| * in the <code>dataProvider</code>, whichever is greater.</p> |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get dropdownWidth():Number |
| { |
| return _dropdownWidth; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set dropdownWidth(value:Number):void |
| { |
| _dropdownWidth = value; |
| |
| preferredDropdownWidth = value; |
| |
| if (_dropdown) |
| _dropdown.setActualSize(value, _dropdown.height); |
| |
| dispatchEvent(new Event("dropdownWidthChanged")); |
| } |
| |
| //---------------------------------- |
| // labelField |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the labelField property. |
| */ |
| private var _labelField:String = "label"; |
| |
| /** |
| * @private |
| */ |
| private var labelFieldChanged:Boolean; |
| |
| [Bindable("labelFieldChanged")] |
| [Inspectable(category="Data", defaultValue="label")] |
| |
| /** |
| * Name of the field in the items in the <code>dataProvider</code> |
| * Array to display as the label in the TextInput portion and drop-down list. |
| * By default, the control uses a property named <code>label</code> |
| * on each Array object and displays it. |
| * <p>However, if the <code>dataProvider</code> items do not contain |
| * a <code>label</code> property, you can set the <code>labelField</code> |
| * property to use a different property.</p> |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get labelField():String |
| { |
| return _labelField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set labelField(value:String):void |
| { |
| _labelField = value; |
| labelFieldChanged = true; |
| |
| invalidateDisplayList(); |
| |
| dispatchEvent(new Event("labelFieldChanged")); |
| } |
| |
| //---------------------------------- |
| // labelFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the labelFunction property. |
| */ |
| private var _labelFunction:Function; |
| |
| /** |
| * @private |
| */ |
| private var labelFunctionChanged:Boolean; |
| |
| [Bindable("labelFunctionChanged")] |
| [Inspectable(category="Data")] |
| |
| /** |
| * User-supplied function to run on each item to determine its label. |
| * By default the control uses a property named <code>label</code> |
| * on each <code>dataProvider</code> item to determine its label. |
| * However, some data sets do not have a <code>label</code> property, |
| * or do not have another property that can be used for displaying |
| * as a label. |
| * <p>An example is a data set that has <code>lastName</code> and |
| * <code>firstName</code> fields but you want to display full names. |
| * You use <code>labelFunction</code> to specify a callback function |
| * that uses the appropriate fields and return a displayable String.</p> |
| * |
| * <p>The labelFunction takes a single argument which is the item |
| * in the dataProvider and returns a String:</p> |
| * <pre> |
| * myLabelFunction(item:Object):String |
| * </pre> |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get labelFunction():Function |
| { |
| return _labelFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set labelFunction(value:Function):void |
| { |
| _labelFunction = value; |
| labelFunctionChanged = true; |
| |
| invalidateDisplayList(); |
| |
| dispatchEvent(new Event("labelFunctionChanged")); |
| } |
| |
| //---------------------------------- |
| // prompt |
| //---------------------------------- |
| |
| private var promptChanged:Boolean = false; |
| |
| /** |
| * @private |
| * Storage for the prompt property. |
| */ |
| private var _prompt:String; |
| |
| [Inspectable(category="General")] |
| |
| /** |
| * The prompt for the ComboBox control. A prompt is |
| * a String that is displayed in the TextInput portion of the |
| * ComboBox when <code>selectedIndex</code> = -1. It is usually |
| * a String like "Select one...". If there is no |
| * prompt, the ComboBox control sets <code>selectedIndex</code> to 0 |
| * and displays the first item in the <code>dataProvider</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get prompt():String |
| { |
| return _prompt; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set prompt(value:String):void |
| { |
| _prompt = value; |
| promptChanged = true; |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // rowCount |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the rowCount property. |
| */ |
| private var _rowCount:int = 5; |
| |
| [Bindable("resize")] |
| [Inspectable(category="General", defaultValue="5")] |
| |
| /** |
| * Maximum number of rows visible in the ComboBox control list. |
| * If there are fewer items in the |
| * dataProvider, the ComboBox shows only as many items as |
| * there are in the dataProvider. |
| * |
| * @default 5 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get rowCount():int |
| { |
| return Math.max(1, Math.min(collection.length, _rowCount)); |
| } |
| |
| /** |
| * @private |
| */ |
| public function set rowCount(value:int):void |
| { |
| _rowCount = value; |
| |
| if (_dropdown) |
| _dropdown.rowCount = value; |
| } |
| |
| //---------------------------------- |
| // selectedLabel |
| //---------------------------------- |
| |
| /** |
| * The String displayed in the TextInput portion of the ComboBox. It |
| * is calculated from the data by using the <code>labelField</code> |
| * or <code>labelFunction</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get selectedLabel():String |
| { |
| return itemToLabel(selectedItem); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden methods: UIComponent |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| override protected function initializeAccessibility():void |
| { |
| if (ComboBox.createAccessibilityImplementation != null) |
| ComboBox.createAccessibilityImplementation(this); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function styleChanged(styleProp:String):void |
| { |
| destroyDropdown(); |
| |
| super.styleChanged(styleProp); |
| } |
| |
| /** |
| * Makes sure the control is at least 40 pixels wide, |
| * and tall enough to fit one line of text |
| * in the TextInput portion of the control but at least |
| * 22 pixels high. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override protected function measure():void |
| { |
| super.measure(); |
| |
| // Make sure we're not too small |
| measuredMinWidth = Math.max(measuredWidth, DEFAULT_MEASURED_MIN_WIDTH); |
| |
| // Make sure we're tall enough to hold our text. |
| // Text field height is text height + 4 pixels top/bottom |
| var textHeight:Number = measureText("M").height + 6; |
| var bm:EdgeMetrics = borderMetrics; |
| measuredMinHeight = measuredHeight = |
| Math.max(textHeight + bm.top + bm.bottom, DEFAULT_MEASURED_MIN_HEIGHT); |
| measuredMinHeight = measuredHeight += getStyle("paddingTop") + getStyle("paddingBottom"); |
| } |
| |
| /** |
| * @private |
| * Make sure the drop-down width is the same as the rest of the ComboBox |
| */ |
| override protected function updateDisplayList(unscaledWidth:Number, |
| unscaledHeight:Number):void |
| { |
| super.updateDisplayList(unscaledWidth, unscaledHeight); |
| |
| // we toss the dropdown when resized |
| // except if we're opening the dropdown |
| // then we assume the updateDisplayList() call is spurious |
| // and will not affect the dropdown size |
| if (_dropdown && !inTween) |
| { |
| // We will not destroy our dropdown if user is actively |
| // interacting with it, this has been legacy behavior since |
| // Flex 3.0. |
| if (!_showingDropdown) |
| destroyDropdown(); |
| } |
| else if (_showingDropdown) |
| { |
| bRemoveDropdown = false; |
| } |
| |
| var ddw:Number = preferredDropdownWidth; |
| if (isNaN(ddw)) |
| ddw = _dropdownWidth = unscaledWidth; |
| |
| if (labelFieldChanged) |
| { |
| if (_dropdown) |
| _dropdown.labelField = _labelField; |
| |
| selectionChanged = true; |
| if (!explicitText) |
| textInput.text = selectedLabel; |
| labelFieldChanged = false; |
| } |
| |
| if (labelFunctionChanged) |
| { |
| selectionChanged = true; |
| if (!explicitText) |
| textInput.text = selectedLabel; |
| labelFunctionChanged = false; |
| } |
| |
| if (selectionChanged) |
| { |
| if (!textChanged) |
| { |
| if (selectedIndex == -1 && prompt) |
| textInput.text = prompt; |
| else if (!explicitText) |
| textInput.text = selectedLabel; |
| } |
| |
| textInput.invalidateDisplayList(); |
| textInput.validateNow(); |
| |
| if (editable) |
| { |
| textInput.selectRange(0, textInput.text.length); |
| textInput.horizontalScrollPosition = 0; |
| } |
| |
| if (_dropdown) |
| _dropdown.selectedIndex = selectedIndex; |
| |
| selectionChanged = false; |
| } |
| |
| // We might need to decrease the number of rows. |
| if (_dropdown && _dropdown.rowCount != rowCount) |
| _dropdown.rowCount = rowCount; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function commitProperties():void |
| { |
| explicitText = textChanged; |
| |
| super.commitProperties(); |
| |
| if (collectionChanged) |
| { |
| if (selectedIndex == -1 && implicitSelectedIndex && _prompt == null) |
| selectedIndex = 0; |
| selectedIndexChanged = true; |
| collectionChanged = false; |
| } |
| if (promptChanged && prompt != null && selectedIndex == -1) |
| { |
| promptChanged = false; |
| textInput.text = prompt; |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Returns a string representing the <code>item</code> parameter. |
| * |
| * <p>This method checks in the following order to find a value to return:</p> |
| * |
| * <ol> |
| * <li>If you have specified a <code>labelFunction</code> property, |
| * returns the result of passing the item to the function.</li> |
| * <li>If the item is a String, Number, Boolean, or Function, returns |
| * the item.</li> |
| * <li>If the item has a property with the name specified by the control's |
| * <code>labelField</code> property, returns the contents of the property.</li> |
| * <li>If the item has a label property, returns its value.</li> |
| * </ol> |
| * |
| * @param item The object that contains the value to convert to a label. |
| * If the item is null, this method returns the empty string. |
| * |
| * @return A string representing the <code>item</code> parameter. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function itemToLabel(item:Object, ...rest):String |
| { |
| // rest args are needed in case dropdown is some other thing like DataGrid |
| // that has multiple arguments to labelFunction |
| |
| // we need to check for null explicitly otherwise |
| // a numeric zero will not get properly converted to a string. |
| // (do not use !item) |
| if (item == null) |
| return ""; |
| |
| if (labelFunction != null) |
| return labelFunction(item); |
| |
| if (typeof(item) == "object") |
| { |
| try |
| { |
| if (item[labelField] != null) |
| item = item[labelField]; |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| else if (typeof(item) == "xml") |
| { |
| try |
| { |
| if (item[labelField].length() != 0) |
| item = item[labelField]; |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| |
| if (typeof(item) == "string") |
| return String(item); |
| |
| try |
| { |
| return item.toString(); |
| } |
| catch(e:Error) |
| { |
| } |
| |
| return " "; |
| } |
| |
| /** |
| * Displays the drop-down list. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function open():void |
| { |
| displayDropdown(true); |
| } |
| |
| /** |
| * Hides the drop-down list. |
| * |
| * @param trigger The event to dispatch when the |
| * drop-down list closes. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function close(trigger:Event = null):void |
| { |
| if (_showingDropdown) |
| { |
| if (_dropdown && selectedIndex != _dropdown.selectedIndex) |
| selectedIndex = _dropdown.selectedIndex; |
| |
| displayDropdown(false, trigger); |
| |
| dispatchChangeEvent(new Event("dummy"), |
| _selectedIndexOnDropdown, |
| selectedIndex); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function hasDropdown():Boolean |
| { |
| return _dropdown != null; |
| } |
| |
| /** |
| * @private |
| */ |
| private function getDropdown():ListBase |
| { |
| if (!initialized) |
| return null; |
| |
| if (!hasDropdown()) |
| { |
| var dropDownStyleName:String = getStyle("dropDownStyleName"); |
| if (dropDownStyleName == null ) |
| dropDownStyleName = getStyle("dropdownStyleName"); |
| |
| |
| _dropdown = dropdownFactory.newInstance(); |
| _dropdown.visible = false; |
| _dropdown.focusEnabled = false; |
| _dropdown.owner = this; |
| |
| if (itemRenderer) |
| _dropdown.itemRenderer = itemRenderer; |
| |
| if (dropDownStyleName) |
| _dropdown.styleName = dropDownStyleName; |
| |
| PopUpManager.addPopUp(_dropdown, this); |
| |
| // Don't display a tween when the selection changes. |
| // The dropdown menu is about to appear anyway, |
| // and other processing can make the tween look choppy. |
| _dropdown.setStyle("selectionDuration", 0); |
| |
| // Set up a data provider in case one doesn't yet exist, |
| // so we can share it with the dropdown listbox. |
| if (!dataProvider) |
| dataProvider = new ArrayCollection(); |
| |
| _dropdown.dataProvider = dataProvider; |
| _dropdown.rowCount = rowCount; |
| _dropdown.width = _dropdownWidth; |
| _dropdown.selectedIndex = selectedIndex; |
| _oldIndex = selectedIndex; |
| _dropdown.verticalScrollPolicy = ScrollPolicy.AUTO; |
| _dropdown.labelField = _labelField; |
| _dropdown.labelFunction = itemToLabel; |
| _dropdown.allowDragSelection = true; |
| |
| _dropdown.addEventListener("change", dropdown_changeHandler); |
| _dropdown.addEventListener(ScrollEvent.SCROLL, dropdown_scrollHandler); |
| _dropdown.addEventListener(ListEvent.ITEM_ROLL_OVER, dropdown_itemRollOverHandler); |
| _dropdown.addEventListener(ListEvent.ITEM_ROLL_OUT, dropdown_itemRollOutHandler); |
| |
| // the drop down should close if the user clicks on any item. |
| // add a handler to detect a click in the list |
| _dropdown.addEventListener(ListEvent.ITEM_CLICK, dropdown_itemClickHandler); |
| |
| UIComponentGlobals.layoutManager.validateClient(_dropdown, true); |
| _dropdown.setActualSize(_dropdownWidth, _dropdown.getExplicitOrMeasuredHeight()); |
| _dropdown.validateDisplayList(); |
| |
| _dropdown.cacheAsBitmap = true; |
| |
| // weak reference to stage |
| systemManager.addEventListener(Event.RESIZE, stage_resizeHandler, false, 0, true); |
| } |
| |
| |
| var m:Matrix = MatrixUtil.getConcatenatedMatrix(this, systemManager.getSandboxRoot()); |
| _dropdown.scaleX = m.a; //scale x |
| _dropdown.scaleY = m.d; //scale y |
| |
| return _dropdown; |
| } |
| |
| /** |
| * @private |
| */ |
| private function displayDropdown(show:Boolean, trigger:Event = null, playEffect:Boolean = true):void |
| { |
| if (!initialized || show == _showingDropdown) |
| return; |
| |
| if (inTween && tween) |
| tween.endTween(); |
| |
| // Subclasses may extend to do pre-processing |
| // before the dropdown is displayed |
| // or override to implement special display behavior |
| |
| // Show or hide the dropdown |
| var initY:Number; |
| var endY:Number; |
| var duration:Number; |
| var easingFunction:Function; |
| |
| var point:Point = new Point(0, unscaledHeight); |
| point = localToGlobal(point); |
| |
| var sm:ISystemManager = systemManager.topLevelSystemManager; |
| var screen:Rectangle = sm.getVisibleApplicationRect(null, true); |
| |
| //opening the dropdown |
| if (show) |
| { |
| // Store the selectedIndex temporarily so we can tell |
| // if the value changed when the dropdown is closed |
| _selectedIndexOnDropdown = selectedIndex; |
| |
| getDropdown(); |
| |
| _dropdown.addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE, dropdown_mouseOutsideHandler); |
| _dropdown.addEventListener(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE, dropdown_mouseOutsideHandler); |
| _dropdown.addEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, dropdown_mouseOutsideHandler); |
| _dropdown.addEventListener(SandboxMouseEvent.MOUSE_WHEEL_SOMEWHERE, dropdown_mouseOutsideHandler); |
| |
| if (_dropdown.parent == null) // was popped up then closed |
| PopUpManager.addPopUp(_dropdown, this); |
| else |
| PopUpManager.bringToFront(_dropdown); |
| |
| // if we donot have enough space in the bottom display the dropdown |
| // at the top. But if the space there is also less than required |
| // display it below. |
| if (point.y + _dropdown.height > screen.bottom && |
| point.y > screen.top + _dropdown.height) |
| { |
| // Dropdown will go below the bottom of the stage |
| // and be clipped. Instead, have it grow up. |
| point.y -= (unscaledHeight + _dropdown.height); |
| initY = -_dropdown.height; |
| tweenUp = true; |
| } |
| else |
| { |
| initY = _dropdown.height; |
| tweenUp = false; |
| } |
| |
| point = _dropdown.parent.globalToLocal(point); |
| |
| // If the combobox's layout is mirrored then the the dropdown's |
| // will be too. That also means that (stage coordinate) point.x is |
| // currently the right edge of the dropdown. Fix that: |
| if (layoutDirection == LayoutDirection.RTL) |
| point.x -= _dropdown.width; |
| |
| var sel:int = _dropdown.selectedIndex; |
| if (sel == -1) |
| sel = 0; |
| var pos:Number = _dropdown.verticalScrollPosition; |
| |
| // try to set the verticalScrollPosition one above the selected index so |
| // it looks better when the dropdown is displayed |
| pos = sel - 1; |
| pos = Math.min(Math.max(pos, 0), _dropdown.maxVerticalScrollPosition); |
| _dropdown.verticalScrollPosition = pos; |
| |
| if (_dropdown.x != point.x || _dropdown.y != point.y) |
| _dropdown.move(point.x, point.y); |
| |
| _dropdown.scrollRect = new Rectangle(0, initY, |
| _dropdown.width, _dropdown.height); |
| |
| if (!_dropdown.visible) |
| _dropdown.visible = true; |
| |
| // Make sure we don't remove the dropdown at the end of the tween |
| bRemoveDropdown = false; |
| |
| // Set up the tween and relevant variables. |
| _showingDropdown = show; |
| duration = getStyle("openDuration"); |
| endY = 0; |
| easingFunction = getStyle("openEasingFunction") as Function; |
| } |
| |
| // closing the dropdown |
| else if (_dropdown) |
| { |
| // Set up the tween and relevant variables. |
| endY = (point.y + _dropdown.height > screen.bottom || tweenUp |
| ? -_dropdown.height |
| : _dropdown.height); |
| _showingDropdown = show; |
| initY = 0; |
| duration = getStyle("closeDuration"); |
| easingFunction = getStyle("closeEasingFunction") as Function; |
| |
| _dropdown.resetDragScrolling(); |
| } |
| |
| inTween = true; |
| // only force validation if we're about to show |
| // or closing with effect. Otherwise, if the iterator |
| // is out of sync in the dropdown, we'll get an RTE |
| // when closing |
| if (playEffect || show) |
| UIComponentGlobals.layoutManager.validateNow(); |
| |
| // Block all layout, responses from web service, and other background |
| // processing until the tween finishes executing. |
| UIComponent.suspendBackgroundProcessing(); |
| |
| // Disable the dropdown during the tween. |
| if (_dropdown) |
| _dropdown.enabled = false; |
| |
| duration = Math.max(1, duration); |
| if (!playEffect) |
| duration = 1; |
| tween = new Tween(this, initY, endY, duration); |
| |
| if (easingFunction != null && tween) |
| tween.easingFunction = easingFunction; |
| |
| triggerEvent = trigger; |
| } |
| |
| /** |
| * @private |
| */ |
| private function dispatchChangeEvent(oldEvent:Event, prevValue:int, |
| newValue:int):void |
| { |
| if (prevValue != newValue) |
| { |
| var newEvent:Event = oldEvent is ListEvent ? |
| oldEvent : |
| new ListEvent("change"); |
| |
| dispatchEvent(newEvent); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function destroyDropdown():void |
| { |
| if (inTween && tween) |
| tween.endTween(); |
| |
| displayDropdown(false, null, false); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden event handlers |
| // |
| //-------------------------------------------------------------------------- |
| private var implicitSelectedIndex:Boolean = false; |
| /** |
| * @private |
| */ |
| override protected function collectionChangeHandler(event:Event):void |
| { |
| // Save a copy of the selectedIndex |
| var curSelectedIndex:int = selectedIndex; |
| |
| super.collectionChangeHandler(event); |
| |
| if (event is CollectionEvent) |
| { |
| // trace("ListBase collectionEvent"); |
| var ce:CollectionEvent = CollectionEvent(event); |
| |
| if (collection.length == 0) |
| { |
| // Special case: Empty dataProvider. |
| if (!selectedIndexChanged && !selectedItemChanged) |
| { |
| if (super.selectedIndex != -1) |
| super.selectedIndex = -1; |
| implicitSelectedIndex = true; |
| invalidateDisplayList(); |
| } |
| // if the combobox is non-editable remove the text |
| // we don't want to remove the text if it is editable as user might |
| // have typed something. |
| if (textInput && !editable) |
| textInput.text = ""; |
| } |
| |
| else if (ce.kind == CollectionEventKind.ADD) |
| { |
| if (collection.length == ce.items.length) |
| { |
| // Special case: Adding the first item(s). Select item 0 |
| // if there is no prompt |
| if (selectedIndex == -1 && _prompt == null) |
| selectedIndex = 0; |
| } |
| else |
| { |
| // we dont want to destroy the dropdown just |
| // because data got added. Especially true |
| // for paged data. |
| return; |
| } |
| } |
| |
| else if (ce.kind == CollectionEventKind.UPDATE) |
| { |
| if (ce.location == selectedIndex || |
| ce.items[0].source == selectedItem) |
| // unsorted lists don't have a valid location |
| // Force an update of the text input |
| selectionChanged = true; |
| } |
| |
| else if (ce.kind == CollectionEventKind.REPLACE) |
| { |
| // bail on a replace, no need to change anything, |
| // especially for paged data |
| return; |
| } |
| |
| else if (ce.kind == CollectionEventKind.RESET) |
| { |
| collectionChanged = true; |
| if (!selectedIndexChanged && !selectedItemChanged) |
| selectedIndex = prompt ? -1 : 0; |
| invalidateProperties(); |
| } |
| |
| invalidateDisplayList(); |
| |
| destroyDropdown(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function textInput_changeHandler(event:Event):void |
| { |
| super.textInput_changeHandler(event); |
| |
| // Force a change event to be dispatched |
| dispatchChangeEvent(event, -1, -2); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function downArrowButton_buttonDownHandler( |
| event:FlexEvent):void |
| { |
| // The down arrow should always toggle the visibility of the dropdown. |
| if (_showingDropdown) |
| { |
| close(event); |
| } |
| else |
| { |
| displayDropdown(true, event); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private function dropdown_mouseOutsideHandler(event:Event):void |
| { |
| // trace("dropdown_mouseOutsideHandler: " + event); |
| |
| if (event is MouseEvent) |
| { |
| var mouseEvent:MouseEvent = MouseEvent(event); |
| if (mouseEvent.target != _dropdown) |
| // the dropdown's items can dispatch a mouseDownOutside |
| // event which then bubbles up to us |
| return; |
| |
| if (!hitTestPoint(mouseEvent.stageX, mouseEvent.stageY, true)) |
| { |
| close(event); |
| } |
| } |
| else if (event is SandboxMouseEvent) |
| { |
| close(event); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function dropdown_itemClickHandler(event:ListEvent):void |
| { |
| if (_showingDropdown) |
| { |
| close(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function focusOutHandler(event:FocusEvent):void |
| { |
| // Note: event.relatedObject is the object getting focus. |
| // It can be null in some cases, such as when you open |
| // the dropdown and then click outside the application. |
| |
| // If the dropdown is open... |
| if (_showingDropdown && _dropdown && |
| this.contains(DisplayObject(event.target))) |
| { |
| // If focus is moving outside the dropdown... |
| if (!event.relatedObject || |
| !_dropdown.contains(event.relatedObject)) |
| { |
| // Close the dropdown. |
| close(); |
| } |
| } |
| |
| super.focusOutHandler(event); |
| } |
| |
| private function stage_resizeHandler(event:Event):void |
| { |
| destroyDropdown(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function dropdown_scrollHandler(event:Event):void |
| { |
| // TextField.scroll bubbles so you might see it here |
| if (event is ScrollEvent) |
| { |
| var se:ScrollEvent = ScrollEvent(event); |
| if (se.detail == ScrollEventDetail.THUMB_TRACK || |
| se.detail == ScrollEventDetail.THUMB_POSITION || |
| se.detail == ScrollEventDetail.LINE_UP || |
| se.detail == ScrollEventDetail.LINE_DOWN) |
| dispatchEvent(se); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function dropdown_itemRollOverHandler(event:Event):void |
| { |
| dispatchEvent(event); |
| } |
| |
| /** |
| * @private |
| */ |
| private function dropdown_itemRollOutHandler(event:Event):void |
| { |
| dispatchEvent(event); |
| } |
| |
| /** |
| * @private |
| */ |
| private function dropdown_changeHandler(event:Event):void |
| { |
| var prevValue:int = selectedIndex; |
| |
| // This assignment will also assign the label to the text field. |
| // See setSelectedIndex(). |
| if (_dropdown) |
| selectedIndex = _dropdown.selectedIndex; |
| |
| // If this was generated by the dropdown as a result of a keystroke, it is |
| // likely a Page-Up or Page-Down, or Arrow-Up or Arrow-Down. |
| // If the selection changes due to a keystroke, |
| // we leave the dropdown displayed. |
| // If it changes as a result of a mouse selection, |
| // we close the dropdown. |
| if (!_showingDropdown) |
| dispatchChangeEvent(event, prevValue, selectedIndex); |
| else if (!bInKeyDown) |
| { |
| // this will also send a change event if needed |
| close(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function keyDownHandler(event:KeyboardEvent):void |
| { |
| // If the combo box is disabled, don't do anything |
| if (!enabled) |
| return; |
| |
| // If a the editable field currently has focus, it is handling |
| // all arrow keys. We shouldn't also scroll this selection. |
| if (event.target == textInput) |
| return; |
| |
| if (event.ctrlKey && event.keyCode == Keyboard.DOWN) |
| { |
| displayDropdown(true, event); |
| event.stopPropagation(); |
| } |
| else if (event.ctrlKey && event.keyCode == Keyboard.UP) |
| { |
| close(event); |
| event.stopPropagation(); |
| } |
| else if (event.keyCode == Keyboard.ESCAPE) |
| { |
| if (_showingDropdown) |
| { |
| if (_oldIndex != _dropdown.selectedIndex) |
| selectedIndex = _oldIndex; |
| |
| displayDropdown(false); |
| event.stopPropagation(); |
| } |
| } |
| |
| else if (event.keyCode == Keyboard.ENTER) |
| { |
| if (_showingDropdown) |
| { |
| close(); |
| event.stopPropagation(); |
| } |
| } |
| else |
| { |
| if (!editable || |
| event.keyCode == Keyboard.UP || |
| event.keyCode == Keyboard.DOWN || |
| event.keyCode == Keyboard.PAGE_UP || |
| event.keyCode == Keyboard.PAGE_DOWN) |
| { |
| var oldIndex:int = selectedIndex; |
| |
| // Make sure we know we are handling a keyDown, |
| // so if the dropdown sends out a "change" event |
| // (like when an up-arrow or down-arrow changes |
| // the selection) we know not to close the dropdown. |
| bInKeyDown = _showingDropdown; |
| // Redispatch the event to the dropdown |
| // and let its keyDownHandler() handle it. |
| |
| dropdown.dispatchEvent(event.clone()); |
| event.stopPropagation(); |
| bInKeyDown = false; |
| |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * This acts as the destructor. |
| */ |
| private function unloadHandler(event:Event):void |
| { |
| if (inTween) |
| { |
| UIComponent.resumeBackgroundProcessing(); |
| inTween = false; |
| } |
| |
| if (_dropdown) |
| _dropdown.parent.removeChild(_dropdown); |
| } |
| |
| /** |
| * @private |
| */ |
| private function removedFromStageHandler(event:Event):void |
| { |
| // Ensure we've unregistered ourselves from PopupManager, else |
| // we'll be leaked. |
| destroyDropdown(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Tween handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| mx_internal function onTweenUpdate(value:Number):void |
| { |
| if (_dropdown) |
| { |
| _dropdown.scrollRect = new Rectangle(0, value, |
| _dropdown.width, _dropdown.height); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function onTweenEnd(value:Number):void |
| { |
| if (_dropdown) |
| { |
| // Clear the scrollRect here. This way if drop shadows are |
| // assigned to the dropdown they show up correctly |
| _dropdown.scrollRect = null; |
| |
| inTween = false; |
| _dropdown.enabled = true; |
| _dropdown.visible = _showingDropdown; |
| |
| if (bRemoveDropdown) |
| { |
| _dropdown.removeEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE, dropdown_mouseOutsideHandler); |
| _dropdown.removeEventListener(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE, dropdown_mouseOutsideHandler); |
| _dropdown.removeEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, dropdown_mouseOutsideHandler); |
| _dropdown.removeEventListener(SandboxMouseEvent.MOUSE_WHEEL_SOMEWHERE, dropdown_mouseOutsideHandler); |
| |
| PopUpManager.removePopUp(_dropdown); |
| _dropdown = null; |
| } |
| } |
| |
| bRemoveDropdown = true; |
| |
| UIComponent.resumeBackgroundProcessing(); |
| var cbdEvent:DropdownEvent = |
| new DropdownEvent(_showingDropdown ? DropdownEvent.OPEN : DropdownEvent.CLOSE); |
| cbdEvent.triggerEvent = triggerEvent; |
| dispatchEvent(cbdEvent); |
| } |
| |
| /** |
| * Determines default values of the height and width to use for each |
| * entry in the drop-down list, based on the maximum size of the label |
| * text in the first <code>numItems</code> items in the data provider. |
| * |
| * @param count The number of items to check to determine the value. |
| * |
| * @return An Object containing two properties: width and height. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override protected function calculatePreferredSizeFromData(count:int):Object |
| { |
| var maxW:Number = 0; |
| var maxH:Number = 0; |
| |
| var bookmark:CursorBookmark = iterator ? iterator.bookmark : null; |
| |
| iterator.seek(CursorBookmark.FIRST, 0); |
| |
| var more:Boolean = iterator != null; |
| |
| var lineMetrics:TextLineMetrics; |
| |
| for (var i:int = 0; i < count; i++) |
| { |
| var data:Object; |
| if (more) |
| data = iterator ? iterator.current : null; |
| else |
| data = null; |
| |
| var txt:String = itemToLabel(data); |
| |
| lineMetrics = measureText(txt); |
| |
| maxW = Math.max(maxW, lineMetrics.width); |
| maxH = Math.max(maxH, lineMetrics.height); |
| |
| if (iterator) |
| iterator.moveNext(); |
| } |
| |
| if (prompt) |
| { |
| lineMetrics = measureText(prompt); |
| |
| maxW = Math.max(maxW, lineMetrics.width); |
| maxH = Math.max(maxH, lineMetrics.height); |
| } |
| |
| maxW += getStyle("paddingLeft") + getStyle("paddingRight"); |
| |
| if (iterator) |
| iterator.seek(bookmark, 0); |
| |
| return { width: maxW, height: maxH }; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function get isShowingDropdown():Boolean |
| { |
| return _showingDropdown; |
| } |
| |
| |
| } |
| |
| } |