| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.supportClasses |
| { |
| import flash.events.Event; |
| import flash.events.EventPhase; |
| import flash.events.FocusEvent; |
| import flash.events.KeyboardEvent; |
| import flash.events.MouseEvent; |
| import flash.ui.Keyboard; |
| |
| import mx.collections.IList; |
| import mx.core.EventPriority; |
| import mx.core.IFactory; |
| import mx.core.ISelectableList; |
| import mx.core.IVisualElement; |
| import mx.core.mx_internal; |
| import mx.events.FlexEvent; |
| import mx.events.IndexChangedEvent; |
| import mx.managers.IFocusManagerComponent; |
| |
| import spark.components.ButtonBarButton; |
| import spark.components.IItemRenderer; |
| import spark.components.supportClasses.ButtonBase; |
| import spark.events.IndexChangeEvent; |
| import spark.events.RendererExistenceEvent; |
| |
| use namespace mx_internal; // use of ListBase/setCurrentCaretIndex(index); |
| |
| //-------------------------------------- |
| // Styles |
| //-------------------------------------- |
| |
| /** |
| * Orientation of the icon in relation to the label. |
| * Valid MXML values are <code>right</code>, <code>left</code>, |
| * <code>bottom</code>, and <code>top</code>. |
| * |
| * <p>In ActionScript, you can use the following constants |
| * to set this property: |
| * <code>IconPlacement.RIGHT</code>, |
| * <code>IconPlacement.LEFT</code>, |
| * <code>IconPlacement.BOTTOM</code>, and |
| * <code>IconPlacement.TOP</code>.</p> |
| * |
| * @default IconPlacement.LEFT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="iconPlacement", type="String", enumeration="top,bottom,right,left", inherit="no")] |
| |
| //-------------------------------------- |
| // Other metadata |
| //-------------------------------------- |
| |
| [AccessibilityClass(implementation="spark.accessibility.ButtonBarBaseAccImpl")] |
| |
| /** |
| * The ButtonBarBase class defines the common behavior for the ButtonBar, TabBar and similar subclasses. |
| * This class does not add any new API however it refines selection, keyboard focus and keyboard navigation |
| * behavior for the control's ItemRenderer elements. |
| * This base class is not intended to be instantiated directly. |
| * |
| * <p>Clicking on an ItemRenderer selects it by setting the <code>selectedIndex</code> and the |
| * <code>caretIndex</code> properties. If <code>requireSelection</code> is <code>false</code>, then clicking |
| * again deselects it. If the data provider is an <code>ISelectableList</code> object, then its |
| * <code>selectedIndex</code> is set as well.</p> |
| * |
| * <p>Arrow key events are handled by adjusting the <code>caretIndex</code>. |
| * If <code>arrowKeysWrapFocus</code> is <code>true</code>, then the <code>caretIndex</code> wraps. |
| * Pressing the Space key selects the ItemRenderer at the <code>caretIndex</code>.</p> |
| * |
| * <p>The <code>showsCaret</code> property of the ItemRenderer at <code>caretIndex</code> |
| * is set to <code>true</code> when the ButtonBarBase object has focus and |
| * the <code>caretIndex</code> was reached as a consequence |
| * of a keyboard gesture. |
| * If the <code>caretIndex</code> was set as a side effect of responding to a |
| * mouse click, then <code>showsCaret</code> is not set.</p> |
| * |
| * <p>The <code>allowDeselection</code> property of <code>ButtonBarButton</code> |
| * ItemRenderers is set to <code>!requireSelection</code>.</p> |
| * |
| * @mxml |
| * |
| * <p>The <code><s:ButtonBarBase></code> tag inherits all of the tag |
| * attributes of its superclass and adds no new tag attributes:</p> |
| * |
| * <pre> |
| * <s:ButtonBarBase/> |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public class ButtonBarBase extends ListBase |
| { |
| include "../../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class mixins |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Placeholder for mixin by ButtonBarBaseAccImpl. |
| */ |
| mx_internal static var createAccessibilityImplementation:Function; |
| |
| /** |
| * Constructor. |
| * |
| * <p>Initializes tab processing: tabbing to this component will give it the focus, but not |
| * clicking on it with the mouse. Tabbing to the children is disabled.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function ButtonBarBase() |
| { |
| super(); |
| |
| tabChildren = false; |
| tabEnabled = true; |
| tabFocusEnabled = true; |
| setCurrentCaretIndex(0); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * If false, don't show the focusRing for the tab at caretIndex, see |
| * itemShowingCaret() below. |
| * |
| * If the caret index changes because of something other than a arrow |
| * or space keypress then we don't show the focus ring, i.e. we do not |
| * set showsCaret=true for the item renderer at caretIndex. |
| * |
| * This flag is valid at commitProperties() time. It's set to false |
| * if at least one selectedIndex change (see item_clickHandler()) occurred |
| * because of a mouse click. |
| */ |
| private var enableFocusHighlight:Boolean = true; |
| |
| /** |
| * @private |
| */ |
| private var inCollectionChangeHandler:Boolean = false; |
| |
| /** |
| * @private |
| * Used to distinguish item_clickHandler() calls initiated by the mouse, from calls |
| * initiated by pressing the space bar. |
| */ |
| private var inKeyUpHandler:Boolean = false; |
| |
| /** |
| * @private |
| * Index of item that is currently pressed by the |
| * spacebar. |
| */ |
| private var pressedIndex:Number; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // dataProvider |
| //---------------------------------- |
| |
| [Inspectable(category="Data")] |
| |
| /** |
| * @private |
| */ |
| override public function set dataProvider(value:IList):void |
| { |
| if (dataProvider is ISelectableList) |
| { |
| dataProvider.removeEventListener(FlexEvent.VALUE_COMMIT, dataProvider_changeHandler); |
| dataProvider.removeEventListener(IndexChangedEvent.CHANGE, dataProvider_changeHandler); |
| } |
| |
| if (value is ISelectableList) |
| { |
| value.addEventListener(FlexEvent.VALUE_COMMIT, dataProvider_changeHandler, false, 0, true); |
| value.addEventListener(IndexChangedEvent.CHANGE, dataProvider_changeHandler, false, 0, true); |
| } |
| |
| super.dataProvider = value; |
| |
| if (value is ISelectableList) |
| selectedIndex = ISelectableList(dataProvider).selectedIndex; |
| } |
| |
| //---------------------------------- |
| // iconField |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconField:String = "icon"; |
| |
| /** |
| * @private |
| */ |
| private var iconFieldOrFunctionChanged:Boolean; |
| |
| /** |
| * The name of the field in the data provider items which serves |
| * as the icon to display. |
| * |
| * The <code>iconFunction</code> property overrides this property. |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconField():String |
| { |
| return _iconField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconField(value:String):void |
| { |
| if (value == _iconField) |
| return; |
| |
| _iconField = value; |
| iconFieldOrFunctionChanged = true; |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // iconFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconFunction:Function; |
| |
| /** |
| * A user-supplied function to run on each item to determine its icon. |
| * The <code>iconFunction</code> property overrides |
| * the <code>iconField</code> property. |
| * |
| * <p>You can supply an <code>iconFunction</code> that finds the |
| * appropriate fields and returns a displayable icon. </p> |
| * |
| * <p>The icon function takes a single argument which is the item in |
| * the data provider and returns a valid BitmapImage source.</p> |
| * <pre> |
| * myIconFunction(item:Object):Object</pre> |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconFunction():Function |
| { |
| return _iconFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconFunction(value:Function):void |
| { |
| if (value == _iconFunction) |
| return; |
| |
| _iconFunction = value; |
| iconFieldOrFunctionChanged = true; |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // requireSelection |
| //---------------------------------- |
| |
| private var requireSelectionChanged:Boolean; |
| |
| [Inspectable(category="General", defaultValue="false")] |
| |
| /** |
| * @private |
| * See commitProperties(). |
| */ |
| override public function set requireSelection(value:Boolean):void |
| { |
| if (value == requireSelection) |
| return; |
| |
| super.requireSelection = value; |
| requireSelectionChanged = true; |
| invalidateProperties(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Called by the initialize() method of UIComponent |
| * to hook in the accessibility code. |
| */ |
| override protected function initializeAccessibility():void |
| { |
| if (ButtonBarBase.createAccessibilityImplementation != null) |
| ButtonBarBase.createAccessibilityImplementation(this); |
| } |
| |
| /** |
| * @private |
| * if the collection is changing and the collection is a viewstack |
| * the viewstack will also adjust the selection and notify us |
| * via dataProvider_changeHandler so we want to |
| * ignore calls to adjustSelection that the base class |
| * calls so we don't increment or decrement |
| * selectedIndex twice |
| */ |
| override protected function dataProvider_collectionChangeHandler(event:Event):void |
| { |
| inCollectionChangeHandler = true; |
| |
| super.dataProvider_collectionChangeHandler(event); |
| |
| inCollectionChangeHandler = false; |
| |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function adjustSelection(newIndex:int, add:Boolean=false):void |
| { |
| // see comment in dataProvider_collectionChangeHandler |
| if (inCollectionChangeHandler && dataProvider is ISelectableList) |
| return; |
| |
| super.adjustSelection(newIndex, add); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function commitProperties():void |
| { |
| super.commitProperties(); |
| |
| if (requireSelectionChanged && dataGroup) |
| { |
| requireSelectionChanged = false; |
| const n:int = dataGroup.numElements; |
| for (var i:int = 0; i < n; i++) |
| { |
| var renderer:ButtonBarButton = dataGroup.getElementAt(i) as ButtonBarButton; |
| if (renderer) |
| renderer.allowDeselection = !requireSelection; |
| } |
| } |
| |
| if (iconFieldOrFunctionChanged) |
| { |
| updateRendererIcons(); |
| iconFieldOrFunctionChanged = false; |
| } |
| |
| enableFocusHighlight = true; |
| } |
| |
| /** |
| * @private |
| * Return the item renderer at the specified index, or null. |
| */ |
| private function getItemRenderer(index:int):IVisualElement |
| { |
| if (!dataGroup || (index < 0) || (index >= dataGroup.numElements)) |
| return null; |
| |
| return dataGroup.getElementAt(index); |
| } |
| |
| /** |
| * @private |
| * Called when setCurrentCaretIndex() moves the caret, or when it's moved as a consequence |
| * of items being moved/removed. See ListBase/caretIndexAdjusted. |
| */ |
| override protected function itemShowingCaret(index:int, showsCaret:Boolean):void |
| { |
| super.itemShowingCaret(index, showsCaret); |
| |
| const renderer:IVisualElement = getItemRenderer(index); |
| if (renderer) |
| { |
| const hasFocus:Boolean = focusManager && (focusManager.getFocus() == this); |
| renderer.depth = (showsCaret) ? 1 : 0; |
| if (renderer is IItemRenderer) |
| IItemRenderer(renderer).showsCaret = showsCaret && enableFocusHighlight && hasFocus; |
| } |
| } |
| |
| /** |
| * @private |
| * Called when the focus is gained/lost by tabbing in or out. |
| */ |
| override public function drawFocus(isFocused:Boolean):void |
| { |
| const renderer:IVisualElement = getItemRenderer(caretIndex); |
| if (renderer) |
| { |
| renderer.depth = (isFocused) ? 1 : 0; |
| if (renderer is IItemRenderer) |
| IItemRenderer(renderer).showsCaret = isFocused; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function itemSelected(index:int, selected:Boolean):void |
| { |
| super.itemSelected(index, selected); |
| |
| const renderer:IItemRenderer = getItemRenderer(index) as IItemRenderer; |
| if (renderer) |
| { |
| if (selected) |
| setCurrentCaretIndex(index); // causes itemShowingCaret() call |
| renderer.selected = selected; |
| } |
| |
| if ((dataProvider is ISelectableList) && selected) |
| ISelectableList(dataProvider).selectedIndex = index; |
| } |
| |
| /** |
| * @private |
| * Detected changes to iconPlacement and update as necessary. |
| */ |
| override public function styleChanged(styleProp:String):void |
| { |
| if (!styleProp || |
| styleProp == "styleName" || styleProp == "iconPlacement") |
| { |
| // Cause icon association to reoccur, taking into consideration |
| // the new placement. |
| iconFieldOrFunctionChanged = true; |
| invalidateProperties(); |
| } |
| |
| super.styleChanged(styleProp); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function updateRenderer(renderer:IVisualElement, itemIndex:int, data:Object):void |
| { |
| itemToIcon(renderer, data); |
| super.updateRenderer(renderer, itemIndex, data); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Gets the index for several of a ButtonBar's Buttons, referencing them by the ButtonBarbutton's <code>label</code>. |
| * |
| * <p>The method takes an array of ButtonBarButtons label and an optional field name.</p> |
| * <pre>myButtonBar.getButtonIndices(["My Button Label1", "My Label2"])</pre> |
| * |
| * @param labelValues Are the ButtonBarButton labels to find. |
| * @param fieldName Field used for comparing the label (optional) |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11.1 |
| * @playerversion AIR 3.4 |
| * @productversion Flex 4.10 |
| */ |
| public function getButtonIndices(labelValues:Array, fieldName:String = ""):Array |
| { |
| var buttonIndices:Array; |
| |
| |
| if (!dataGroup || labelValues.length < 1 || labelValues == null) |
| { |
| return []; |
| } |
| |
| if (fieldName == "" || fieldName == null) |
| { |
| return findRowIndices(labelField, labelValues); |
| } |
| else |
| { |
| return findRowIndices(fieldName, labelValues); |
| } |
| } |
| |
| |
| /** |
| * Allows changing the <code>enabled</code> property of a the child ButtonBarbutton's. |
| * It identifies the button given its label field (default) or an different optional field name may be passed. |
| * |
| * <p>The method takes a single ButtonBarButton label, a new <code>enabled</code> property value, and an optional field name to use as the comparison field.</p> |
| * <pre> |
| * myButtonBar.setButtonEnabled("My Button Label", false)</pre> |
| * |
| * @param labelValue Is the ButtonBarButton label. |
| * @param enabledValue The buttons new enabled value. |
| * @param fieldName Field used to compare the label value against. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11.1 |
| * @playerversion AIR 3.4 |
| * @productversion Flex 4.10 |
| */ |
| public function setButtonEnabled(labelValue:String, enabledValue:Boolean, fieldName:String = ""):void |
| { |
| setButtonsEnabled([labelValue], enabledValue, fieldName); |
| } |
| |
| |
| /** |
| * Allows changing the <code>enabled</code> property of several child ButtonBarbutton's. |
| * It identifies the buttons given their label fields (default) or an different optional field name may be passed. |
| * |
| * <p>The method takes an array of ButtonBarButton labels, a new <code>enabled</code> property value, and an optional field name to use as the comparison field.</p> |
| * <pre> |
| * myButtonBar.setButtonsEnabled(["My Button Label1", "My Label2"], false)</pre> |
| * |
| * @param labelValues Is an array of ButtonBarButton labels. |
| * @param enabledValue The buttons new enabled value. |
| * @param fieldName Field used to compare the label value against. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11.1 |
| * @playerversion AIR 3.4 |
| * @productversion Flex 4.10 |
| */ |
| public function setButtonsEnabled(labelValues:Array, enabledValue:Boolean, fieldName:String = ""):void |
| { |
| var btnCurrent:ButtonBarButton = null; |
| var buttonIndices:Array; |
| var indicesTotal:uint = 0; |
| var loopingIndex:uint = 0; |
| |
| |
| buttonIndices = getButtonIndices(labelValues, fieldName); |
| indicesTotal = buttonIndices.length; |
| |
| if (indicesTotal == 0) |
| { |
| return; |
| } |
| |
| |
| for (loopingIndex; loopingIndex < indicesTotal; loopingIndex++) |
| { |
| btnCurrent = dataGroup.getElementAt(buttonIndices[loopingIndex]) as ButtonBarButton; |
| btnCurrent.enabled = enabledValue; |
| } |
| } |
| |
| |
| /** |
| * @private |
| */ |
| private function itemToIcon(renderer:IVisualElement, item:Object):void |
| { |
| if (!(renderer is ButtonBase)) |
| return; |
| |
| // Assign iconPlacement if appropriate. |
| var iconPlacement:String = getStyle("iconPlacement"); |
| if (iconPlacement) |
| ButtonBase(renderer).setStyle("iconPlacement", iconPlacement); |
| |
| // iconFunction takes precedence. |
| if (_iconFunction != null) |
| { |
| ButtonBase(renderer).setStyle("icon", _iconFunction(item)); |
| return; |
| } |
| |
| // iconField |
| if (_iconField && |
| _iconField.length > 0 && |
| item is Object) |
| { |
| try |
| { |
| if (item[_iconField] != null) |
| { |
| ButtonBase(renderer).setStyle("icon", item[_iconField]); |
| return; |
| } |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| |
| ButtonBase(renderer).clearStyle("icon"); |
| } |
| |
| /** |
| * @private |
| */ |
| private function updateRendererIcons():void |
| { |
| if (!dataGroup) |
| return; |
| |
| const count:int = dataGroup.numElements; |
| for (var i:int = 0; i < count; i++) |
| { |
| var renderer:IItemRenderer = dataGroup.getElementAt(i) as IItemRenderer; |
| if (renderer && renderer.data) |
| itemToIcon(renderer, renderer.data); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event Handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private function dataProvider_changeHandler(event:Event):void |
| { |
| var newSelectedIndex:int = ISelectableList(dataProvider).selectedIndex; |
| if (selectedIndex != newSelectedIndex) |
| { |
| selectedIndex = newSelectedIndex; |
| commitSelection(false); |
| dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT)); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function dataGroup_rendererAddHandler(event:RendererExistenceEvent):void |
| { |
| super.dataGroup_rendererAddHandler(event); |
| |
| const renderer:IVisualElement = event.renderer; |
| if (renderer) |
| { |
| renderer.addEventListener(MouseEvent.CLICK, item_clickHandler); |
| if (renderer is IFocusManagerComponent) |
| IFocusManagerComponent(renderer).focusEnabled = false; |
| if (renderer is ButtonBarButton) |
| ButtonBarButton(renderer).allowDeselection = !requireSelection; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function dataGroup_rendererRemoveHandler(event:RendererExistenceEvent):void |
| { |
| super.dataGroup_rendererRemoveHandler(event); |
| |
| const renderer:IVisualElement = event.renderer; |
| if (renderer) |
| renderer.removeEventListener(MouseEvent.CLICK, item_clickHandler); |
| } |
| |
| /** |
| * @private |
| * Called synchronously when the space bar is pressed or the mouse is clicked. |
| */ |
| private function item_clickHandler(event:MouseEvent):void |
| { |
| var newIndex:int; |
| if (event.currentTarget is IItemRenderer) |
| newIndex = IItemRenderer(event.currentTarget).itemIndex; |
| else |
| newIndex = dataGroup.getElementIndex(event.currentTarget as IVisualElement); |
| |
| var oldSelectedIndex:int = selectedIndex; |
| if (newIndex == selectedIndex) |
| { |
| if (!requireSelection) |
| setSelectedIndex(NO_SELECTION, true); |
| } |
| else |
| setSelectedIndex(newIndex, true); |
| |
| // Changing the selectedIndex typically causes a call to itemSelected() at |
| // commitProperties() time. We'll update the caretIndex then. If this |
| // method was -not- called as a consequence of a keypress, we will not show |
| // the focus highlight at caretIndex. See itemShowingCaret(). |
| |
| if (enableFocusHighlight && (selectedIndex != oldSelectedIndex)) |
| enableFocusHighlight = inKeyUpHandler; |
| } |
| |
| /** |
| * @private |
| * Increment or decrement the caretIndex. Wrap if arrowKeysWrapFocus=true. |
| */ |
| private function adjustCaretIndex(delta:int):void |
| { |
| if (!dataGroup || (caretIndex < 0)) |
| return; |
| |
| const oldCaretIndex:int = caretIndex; |
| const length:int = dataGroup.numElements; |
| |
| if (arrowKeysWrapFocus) |
| setCurrentCaretIndex((caretIndex + delta + length) % length); |
| else |
| setCurrentCaretIndex(Math.min(length - 1, Math.max(0, caretIndex + delta))); |
| |
| if (oldCaretIndex != caretIndex) |
| dispatchEvent(new IndexChangeEvent(IndexChangeEvent.CARET_CHANGE, false, false, oldCaretIndex, caretIndex)); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function keyDownHandler(event:KeyboardEvent):void |
| { |
| if (event.eventPhase == EventPhase.BUBBLING_PHASE) |
| return; |
| |
| if (!enabled || !dataGroup || event.isDefaultPrevented()) |
| return; |
| |
| // Block input if space bar is being held down. |
| if (!isNaN(pressedIndex)) |
| { |
| event.preventDefault(); |
| return; |
| } |
| |
| super.keyDownHandler(event); |
| |
| // If rtl layout, need to swap LEFT/UP and RIGHT/DOWN so correct action |
| // is done. |
| var keyCode:uint = mapKeycodeForLayoutDirection(event, true); |
| |
| switch (keyCode) |
| { |
| case Keyboard.UP: |
| case Keyboard.LEFT: |
| { |
| adjustCaretIndex(-1); |
| event.preventDefault(); |
| break; |
| } |
| case Keyboard.DOWN: |
| case Keyboard.RIGHT: |
| { |
| adjustCaretIndex(+1); |
| event.preventDefault(); |
| break; |
| } |
| case Keyboard.SPACE: |
| { |
| const renderer:IItemRenderer = getItemRenderer(caretIndex) as IItemRenderer; |
| if (renderer && ((!renderer.selected && requireSelection) || !requireSelection)) |
| { |
| renderer.dispatchEvent(event); |
| pressedIndex = caretIndex; |
| } |
| break; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function keyUpHandler(event:KeyboardEvent):void |
| { |
| if (event.eventPhase == EventPhase.BUBBLING_PHASE) |
| return; |
| |
| if (!enabled) |
| return; |
| |
| super.keyUpHandler(event); |
| |
| switch (event.keyCode) |
| { |
| case Keyboard.SPACE: |
| { |
| inKeyUpHandler = true; |
| |
| // Need to check pressedIndex for NaN for the case when key up |
| // happens on an already selected renderer and under the condition |
| // that requireSelection=true. |
| if (!isNaN(pressedIndex)) |
| { |
| // Dispatch key up to the previously pressed item in case focus was lost |
| // through other interaction (e.g. mouse clicks, etc...) |
| const renderer:IItemRenderer = getItemRenderer(pressedIndex) as IItemRenderer; |
| if (renderer && ((!renderer.selected && requireSelection) || !requireSelection)) |
| { |
| renderer.dispatchEvent(event); |
| pressedIndex = NaN; |
| } |
| } |
| inKeyUpHandler = false; |
| break; |
| } |
| } |
| } |
| } |
| } |