| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.display.DisplayObjectContainer; |
| import flash.events.Event; |
| import flash.events.FocusEvent; |
| import flash.events.KeyboardEvent; |
| import flash.events.MouseEvent; |
| import flash.geom.Point; |
| import flash.geom.Rectangle; |
| import flash.ui.Keyboard; |
| import flash.xml.XMLNode; |
| |
| import mx.collections.ArrayCollection; |
| import mx.collections.ICollectionView; |
| import mx.collections.IViewCursor; |
| import mx.collections.XMLListCollection; |
| import mx.collections.errors.ItemPendingError; |
| import mx.containers.ApplicationControlBar; |
| import mx.controls.menuClasses.IMenuBarItemRenderer; |
| import mx.controls.menuClasses.IMenuDataDescriptor; |
| import mx.controls.menuClasses.MenuBarItem; |
| import mx.controls.treeClasses.DefaultDataDescriptor; |
| import mx.core.ClassFactory; |
| import mx.core.EventPriority; |
| import mx.core.IFactory; |
| import mx.core.IFlexDisplayObject; |
| import mx.core.IUIComponent; |
| import mx.core.LayoutDirection; |
| import mx.core.UIComponent; |
| import mx.core.UIComponentGlobals; |
| import mx.core.mx_internal; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.FlexEvent; |
| import mx.events.InterManagerRequest; |
| import mx.events.MenuEvent; |
| import mx.managers.IFocusManagerComponent; |
| import mx.managers.ISystemManager; |
| import mx.managers.PopUpManager; |
| import mx.styles.CSSStyleDeclaration; |
| import mx.styles.ISimpleStyleClient; |
| import mx.styles.StyleProxy; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Events |
| //-------------------------------------- |
| |
| /** |
| * Dispatched when selection changes as a result of user |
| * interaction. |
| * This event is also dispatched when the user changes |
| * the current menu selection in a pop-up submenu. |
| * When the event occurs on the menu bar, |
| * the <code>menu</code> property of the MenuEvent object is <code>null</code>. |
| * When it occurs in a pop-up submenu, the <code>menu</code> property |
| * contains a reference to the Menu object that represents the |
| * the pop-up submenu. |
| * |
| * @eventType mx.events.MenuEvent.CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="change", type="mx.events.MenuEvent")] |
| |
| /** |
| * Dispatched when the user selects an item in a pop-up submenu. |
| * |
| * @eventType mx.events.MenuEvent.ITEM_CLICK |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemClick", type="mx.events.MenuEvent")] |
| |
| /** |
| * Dispatched when a pop-up submenu closes. |
| * |
| * @eventType mx.events.MenuEvent.MENU_HIDE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="menuHide", type="mx.events.MenuEvent")] |
| |
| /** |
| * Dispatched when a pop-up submenu opens, or the |
| * user selects a menu bar item with no drop-down menu. |
| * |
| * @eventType mx.events.MenuEvent.MENU_SHOW |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="menuShow", type="mx.events.MenuEvent")] |
| |
| /** |
| * Dispatched when the mouse pointer rolls out of a menu item. |
| * |
| * @eventType mx.events.MenuEvent.ITEM_ROLL_OUT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemRollOut", type="mx.events.MenuEvent")] |
| |
| /** |
| * Dispatched when the mouse pointer rolls over a menu item. |
| * |
| * @eventType mx.events.MenuEvent.ITEM_ROLL_OVER |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemRollOver", type="mx.events.MenuEvent")] |
| |
| //-------------------------------------- |
| // Styles |
| //-------------------------------------- |
| |
| include "../styles/metadata/FocusStyles.as" |
| include "../styles/metadata/LeadingStyle.as" |
| include "../styles/metadata/SkinStyles.as" |
| include "../styles/metadata/TextStyles.as" |
| |
| /** |
| * The background skin of the MenuBar control. |
| * |
| * <p>The default skin class is based on the theme. For example, with the Halo theme, |
| * the default skin class is <code>mx.skins.halo.MenuBarBackgroundSkin</code>. For the Spark theme, the default skin |
| * class is <code>mx.skins.spark.ButtonSkin</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="backgroundSkin", type="Class", inherit="no")] |
| |
| /** |
| * The default skin for a MenuBar item. |
| * |
| * <p>The default skin class is based on the theme. For example, with the Halo theme, |
| * the default skin class is <code>mx.skins.halo.ActivatorSkin</code>. For the Spark theme, the default skin |
| * class is <code>mx.skins.spark.MenuItemSkin</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="itemSkin", type="Class", inherit="no", states="up, over, down")] |
| |
| /** |
| * The skin when a MenuBar item is not selected. |
| * |
| * <p>The default skin class is based on the theme. For example, with the Halo theme, |
| * the default skin class is <code>mx.skins.halo.ActivatorSkin</code>. For the Spark theme, the default skin |
| * class is <code>mx.skins.spark.MenuItemSkin</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="itemUpSkin", type="Class", inherit="no")] |
| |
| /** |
| * The skin when focus is over a MenuBar item. |
| * |
| * <p>The default skin class is based on the theme. For example, with the Halo theme, |
| * the default skin class is <code>mx.skins.halo.ActivatorSkin</code>. For the Spark theme, the default skin |
| * class is <code>mx.skins.spark.MenuItemSkin</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="itemOverSkin", type="Class", inherit="no")] |
| |
| /** |
| * The skin when a MenuBar item is selected. |
| * |
| * <p>The default skin class is based on the theme. For example, with the Halo theme, |
| * the default skin class is <code>mx.skins.halo.ActivatorSkin</code>. For the Spark theme, the default skin |
| * class is <code>mx.skins.spark.MenuItemSkin</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="itemDownSkin", type="Class", inherit="no")] |
| |
| /** |
| * Name of the CSSStyleDeclaration that specifies the styles for |
| * the Menu controls displayed by this MenuBar control. |
| * By default, the Menu controls use the MenuBar control's |
| * inheritable styles. |
| * |
| * <p>You can use this class selector to set the values of all the style properties |
| * of the Menu class, including <code>backgroundAlpha</code> and <code>backgroundColor</code>.</p> |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="menuStyleName", type="String", inherit="no")] |
| |
| |
| /** |
| * @copy mx.controls.Menu#style:rollOverColor |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="rollOverColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * @copy mx.controls.Menu#style:selectionColor |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="selectionColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * Color of any symbol of a component. Examples include the check mark of a CheckBox or |
| * the arrow of a ScrollBar button. |
| * |
| * @default 0x000000 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="symbolColor", type="uint", format="Color", inherit="yes", theme="spark")] |
| |
| //-------------------------------------- |
| // Other metadata |
| //-------------------------------------- |
| |
| [AccessibilityClass(implementation="mx.accessibility.MenuBarAccImpl")] |
| |
| [DefaultBindingProperty(destination="dataProvider")] |
| |
| [DefaultProperty("dataProvider")] |
| |
| [DefaultTriggerEvent("change")] |
| |
| [IconFile("MenuBar.png")] |
| |
| [RequiresDataBinding(true)] |
| |
| /** |
| * A MenuBar control defines a horizontal, top-level menu bar that contains |
| * one or more menus. Clicking on a top-level menu item opens a pop-up submenu |
| * that is an instance of the Menu control. |
| * |
| * <p>The top-level menu bar of the MenuBar control is generally always visible. |
| * It is not intended for use as a pop-up menu. The individual submenus |
| * pop up as the user selects them with the mouse or keyboard. Open submenus |
| * disappear when a menu item is selected, or if the menu is dismissed by the |
| * user clicking outside the menu.</p> |
| * |
| * <p>For information and an example on the attributes that you can use |
| * in the data provider for the MenuBar control, see the Menu control.</p> |
| * |
| * <p>The MenuBar control has the following sizing characteristics: |
| * </p> |
| * <table class="innertable"> |
| * <tr> |
| * <th>Characteristic</th> |
| * <th>Description</th> |
| * </tr> |
| * <tr> |
| * <td>Default size</td> |
| * <td>The width is determined from the menu text, with a |
| * minimum value of 27 pixels for the width. The default |
| * value for the height is 22 pixels.</td> |
| * </tr> |
| * </table> |
| * |
| * @mxml |
| * <p> |
| * The <code><mx:MenuBar></code> tag inherits all of the tag attributes of its superclass, and |
| * adds the following tag attributes: |
| * </p> |
| * |
| * <pre> |
| * <mx:MenuBar |
| * <b>Properties</b> |
| * dataDescriptor="<i>mx.controls.treeClasses.DefaultDataDescriptor</i>" |
| * dataProvider="<i>undefined</i>" |
| * iconField="icon" |
| * labelField="label" |
| * labelFunction="<i>undefined</i>" |
| * menuBarItemRenderer="<i>mx.controls.menuClasses.MenuBarItem</i>" |
| * menuBarItems="[]" |
| * menus="[]" |
| * selectedIndex="-1" |
| * showRoot="true" |
| * |
| * <b>Styles</b> |
| * backgroundSkin="mx.skins.halo.MenuBarBackgroundSkin" |
| * borderColor="0xAAB3B3" |
| * color="0x0B333C" |
| * cornerRadius="0" |
| * disabledColor="0xAAB3B3" |
| * fillAlphas="[0.6,0.4]" |
| * fillColors="[0xFFFFFF, 0xCCCCCC]" |
| * focusAlpha="0.5" |
| * 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]" |
| * itemDownSkin="mx.skins.halo.ActivatorSkin" |
| * itemOverSkin="mx.skins.halo.ActivatorSkin" |
| * itemUpSkin="mx.skins.halo.ActivatorSkin" |
| * leading="2" |
| * menuStyleName="<i>No default</i>" |
| * rollOverColor="0xB2E1FF" |
| * selectionColor="0x7FCEFF" |
| * textAlign="left" |
| * textDecoration="none" |
| * textIndent="0" |
| * |
| * <b>Events</b> |
| * itemClick="<i>No default"</i> |
| * itemRollOut="<i>No default"</i> |
| * itemRollOver="<i>No default"</i> |
| * menuHide="<i>No default"</i> |
| * menuShow="<i>No default"</i> |
| * /> |
| * </pre> |
| * </p> |
| * |
| * @see mx.controls.Menu |
| * @see mx.controls.PopUpMenuButton |
| * @see mx.controls.menuClasses.IMenuBarItemRenderer |
| * @see mx.controls.menuClasses.MenuBarItem |
| * @see mx.controls.menuClasses.IMenuDataDescriptor |
| * @see mx.controls.treeClasses.DefaultDataDescriptor |
| * |
| * @includeExample examples/MenuBarExample.mxml |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class MenuBar extends UIComponent implements IFocusManagerComponent |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private static const MARGIN_WIDTH:int = 10; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class mixins |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Placeholder for mixin by MenuBarAccImpl. |
| */ |
| mx_internal static var createAccessibilityImplementation:Function; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function MenuBar() |
| { |
| super(); |
| menuBarItemRenderer = new ClassFactory(MenuBarItem); |
| tabChildren = false; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Storage variable for the original dataProvider |
| */ |
| mx_internal var _rootModel:ICollectionView; |
| |
| /** |
| * @private |
| */ |
| private var isDown:Boolean; |
| |
| /** |
| * @private |
| */ |
| private var inKeyDown:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var background:IFlexDisplayObject; |
| |
| /** |
| * @private |
| * This menu bar could be inside an ApplicationControlBar (ACB). |
| */ |
| private var isInsideACB:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var supposedToLoseFocus:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var dataProviderChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var iconFieldChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var menuBarItemRendererChanged:Boolean = false; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // baselinePosition |
| //---------------------------------- |
| |
| /** |
| * @private |
| * The baselinePosition of a MenuBar is calculated |
| * for its first MenuBarItem. |
| */ |
| override public function get baselinePosition():Number |
| { |
| if (!validateBaselinePosition()) |
| return NaN; |
| |
| if (menuBarItems.length == 0) |
| return super.baselinePosition; |
| |
| var menuBarItem0:IUIComponent = menuBarItems[0] as IUIComponent; |
| if (!menuBarItem0) |
| return super.baselinePosition; |
| |
| validateNow(); |
| |
| return menuBarItem0.y + menuBarItem0.baselinePosition; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // enabled |
| //-------------------------------------------------------------------------- |
| |
| [Inspectable(category="General", enumeration="true,false", defaultValue="true")] |
| |
| /** |
| * @private |
| */ |
| override public function set enabled(value:Boolean):void |
| { |
| super.enabled = value; |
| |
| if (menuBarItems) |
| { |
| var n:int = menuBarItems.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| menuBarItems[i].enabled = value; |
| } |
| } |
| } |
| |
| //---------------------------------- |
| // showInAutomationHierarchy |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function set showInAutomationHierarchy(value:Boolean):void |
| { |
| //do not allow value changes |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // dataDescriptor |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| mx_internal var _dataDescriptor:IMenuDataDescriptor = |
| new DefaultDataDescriptor(); |
| |
| [Inspectable(category="Data")] |
| |
| /** |
| * The object that accesses and manipulates data in the data provider. |
| * The MenuBar control delegates to the data descriptor for information |
| * about its data. This data is then used to parse and move about the |
| * data source. The data descriptor defined for the MenuBar is used for |
| * all child menus and submenus. |
| * |
| * <p>When you specify this property as an attribute in MXML, you must |
| * use a reference to the data descriptor, not the string name of the |
| * descriptor. Use the following format for setting the property:</p> |
| * |
| * <pre><mx:MenuBar id="menubar" dataDescriptor="{new MyCustomDataDescriptor()}"/></pre> |
| * |
| * <p>Alternatively, you can specify the property in MXML as a nested |
| * subtag, as the following example shows:</p> |
| * |
| * <pre><mx:MenuBar> |
| * <mx:dataDescriptor> |
| * <myCustomDataDescriptor> |
| * </mx:dataDescriptor> |
| * ...</pre> |
| * |
| * <p>The default value is an internal instance of the |
| * DefaultDataDescriptor class.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get dataDescriptor():IMenuDataDescriptor |
| { |
| return IMenuDataDescriptor(_dataDescriptor); |
| } |
| |
| /** |
| * @private |
| */ |
| public function set dataDescriptor(value:IMenuDataDescriptor):void |
| { |
| _dataDescriptor = value; |
| |
| //force all the menus to be re-created with the new dataDescriptor |
| menus = []; |
| } |
| |
| //---------------------------------- |
| // dataProvider |
| //---------------------------------- |
| |
| [Bindable("collectionChange")] |
| [Inspectable(category="Data")] |
| |
| /** |
| * The hierarchy of objects that are displayed as MenuBar items and menus. |
| * The top-level children all become MenuBar items, and their children |
| * become the items in the menus and submenus. |
| * |
| * The MenuBar control handles the source data object as follows: |
| * <p> |
| * <ul> |
| * <li>A String containing valid XML text is converted to an XML object.</li> |
| * <li>An XMLNode is converted to an XML object.</li> |
| * <li>An XMLList is converted to an XMLListCollection.</li> |
| * <li>Any object that implements the ICollectionView interface is cast to |
| * an ICollectionView.</li> |
| * <li>An Array is converted to an ArrayCollection.</li> |
| * <li>Any other type object is wrapped in an Array with the object as its sole |
| * entry.</li> |
| * </ul> |
| * </p> |
| * |
| * @default "undefined" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get dataProvider():Object |
| { |
| if (_rootModel) |
| { |
| return _rootModel; |
| } |
| else return null; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set dataProvider(value:Object):void |
| { |
| if (_rootModel) |
| { |
| _rootModel.removeEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler); |
| } |
| |
| // handle strings and xml |
| if (typeof(value)=="string") |
| value = new XML(value); |
| else if (value is XMLNode) |
| value = new XML(XMLNode(value).toString()); |
| else if (value is XMLList) |
| value = new XMLListCollection(value as XMLList); |
| |
| if (value is XML) |
| { |
| _hasRoot = true; |
| var xl:XMLList = new XMLList(); |
| xl += value; |
| _rootModel = new XMLListCollection(xl); |
| } |
| //if already a collection dont make new one |
| else if (value is ICollectionView) |
| { |
| _rootModel = ICollectionView(value); |
| if (_rootModel.length == 1) |
| _hasRoot = true; |
| } |
| else if (value is Array) |
| { |
| _rootModel = new ArrayCollection(value as Array); |
| } |
| //all other types get wrapped in an ArrayCollection |
| else if (value is Object) |
| { |
| _hasRoot = true; |
| // convert to an array containing this one item |
| var tmp:Array = []; |
| tmp.push(value); |
| _rootModel = new ArrayCollection(tmp); |
| } |
| else |
| { |
| _rootModel = new ArrayCollection(); |
| } |
| //add listeners as weak references |
| _rootModel.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, false, 0, true); |
| //flag for processing in commitProps |
| dataProviderChanged = true; |
| invalidateProperties(); |
| |
| var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.RESET; |
| collectionChangeHandler(event); |
| dispatchEvent(event); |
| } |
| |
| //---------------------------------- |
| // hasRoot |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Flag to indicate if the model has a root |
| */ |
| mx_internal var _hasRoot:Boolean = false; |
| |
| /** |
| * @copy mx.controls.Menu#hasRoot |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get hasRoot():Boolean |
| { |
| return _hasRoot; |
| } |
| |
| //---------------------------------- |
| // iconField |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for iconField property. |
| */ |
| private var _iconField:String = "icon"; |
| |
| [Bindable("iconFieldChanged")] |
| [Inspectable(category="Other", defaultValue="icon")] |
| |
| /** |
| * The name of the field in the data provider that determines the |
| * icon to display for each menu item. By default, the MenuBar does not |
| * try to display icons along with the text in a menu item. By specifying |
| * an icon field, you can define a graphic that is created |
| * and displayed as an icon for a menu item. |
| * |
| * <p>The MenuItemRenderer examines |
| * the data provider for a property of the name defined |
| * by the <code>iconField</code> property. If the value of the property is a Class, it |
| * instantiates that class and expects it to be an instance of |
| * IFlexDisplayObject. If the value of the property is a String, it |
| * looks to see if a Class exists with that name in the application, and if |
| * it cannot find one, it looks for a property on the document |
| * with that name and expects that property to map to a Class.</p> |
| * |
| * @default "icon" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get iconField():String |
| { |
| return _iconField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconField(value:String):void |
| { |
| if (_iconField != value) |
| { |
| iconFieldChanged = true; |
| _iconField = value; |
| invalidateProperties(); |
| dispatchEvent(new Event("iconFieldChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // labelField |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _labelField:String = "label"; |
| |
| [Bindable("labelFieldChanged")] |
| [Inspectable(category="Data", defaultValue="label")] |
| |
| /** |
| * The name of the field in the data provider that determines the |
| * text to display for each menu item. If the data provider is an Array of |
| * Strings, Flex uses each string value as the label. If the data |
| * provider is an E4X XML object, you must set this property explicitly. |
| * For example, use @label to specify the label attribute in an E4X XML Object |
| * as the text to display for each menu item. |
| * |
| * Setting the <code>labelFunction</code> property overrides this property. |
| * |
| * @default "label" |
| * |
| * @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 |
| { |
| if (_labelField != value) |
| { |
| _labelField = value; |
| dispatchEvent(new Event("labelFieldChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // labelFunction |
| //---------------------------------- |
| |
| [Inspectable(category="Data")] |
| |
| /** |
| * The function that determines the text to display for each menu item. |
| * The label function must find the appropriate field or fields in the |
| * data provider and return a displayable string. |
| * |
| * If you omit this property, Flex uses the contents of the field or |
| * attribute specified by the <code>labelField</code> property. |
| * If you specify this property, Flex ignores any <code>labelField</code> |
| * property value. |
| * |
| * The <code>labelFunction</code> property is good for handling formatting |
| * and localization. |
| * |
| * <p>The label function must take a single argument which is the item |
| * in the data provider and return a String.</p> |
| * <pre> |
| * <code>myLabelFunction(item:Object):String</code> </pre> |
| * |
| * @default "undefined" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var labelFunction:Function; |
| |
| //---------------------------------- |
| // menuBarItemRenderer |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the menuBarItemRenderer property. |
| */ |
| private var _menuBarItemRenderer:IFactory; |
| |
| [Bindable("menuBarItemRendererChanged")] |
| [Inspectable(category="Data")] |
| |
| /** |
| * The item renderer used by the MenuBar control for |
| * the top-level menu bar of the MenuBar control. |
| * |
| * <p>You can define an item renderer for the pop-up submenus |
| * of the MenuBar control. |
| * Because each pop-up submenu is an instance of the Menu control, |
| * you use the class MenuItemRenderer to define an item renderer |
| * for the pop-up submenus. |
| * To set the item renderer for a pop-up submenu, access the Menu object using |
| * the <code>menus</code> property. </p> |
| * |
| * @default "mx.controls.menuClasses.MenuBarItem" |
| * |
| * @see mx.controls.menuClasses.MenuBarItem |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get menuBarItemRenderer():IFactory |
| { |
| return _menuBarItemRenderer; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set menuBarItemRenderer(value:IFactory):void |
| { |
| if (_menuBarItemRenderer != value) |
| { |
| _menuBarItemRenderer = value; |
| |
| menuBarItemRendererChanged = true; |
| invalidateProperties(); |
| dispatchEvent(new Event("menuBarItemRendererChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // menuBarItems |
| //---------------------------------- |
| |
| /** |
| * An Array that contains the MenuBarItem objects that render |
| * each item in the top-level menu bar of a MenuBar control. By default, |
| * this property contains instances of the MenuBarItem class. |
| * |
| * Items should not be added directly to the <code>menuBarItems</code> array. To |
| * add new menubar items, add them directly to the MenuBar control's |
| * data provider. |
| * |
| * @default [ ] |
| * |
| * @see mx.controls.menuClasses.MenuBarItem |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var menuBarItems:Array = []; |
| |
| //---------------------------------- |
| // menuBarItemStyleFilters |
| //---------------------------------- |
| |
| /** |
| * The set of styles to pass from the MenuBar to the menuBar items. |
| * @see mx.styles.StyleProxy |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function get menuBarItemStyleFilters():Object |
| { |
| return _menuBarItemStyleFilters; |
| } |
| |
| private static var _menuBarItemStyleFilters:Object = null; |
| |
| //---------------------------------- |
| // menus |
| //---------------------------------- |
| |
| /** |
| * An Array containing the Menu objects corresponding to the |
| * pop-up submenus of this MenuBar control. |
| * Each MenuBar item can have a corresponding Menu object in the Array, |
| * even if the item does not have a pop-up submenu. |
| * Flex does not initially populate the <code>menus</code> array; |
| * instead, it creates the menus dynamically, as needed. |
| * |
| * Items should not be added directly to the <code>menus</code> Array. To |
| * add new drop-down menus, add directly to the MenuBar |
| * control's data provider. |
| * |
| * @default [ ] |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var menus:Array = []; |
| |
| //---------------------------------- |
| // selectedIndex |
| //---------------------------------- |
| |
| /** |
| * @private |
| * The index of the currently open menu item, or -1 if none is open. |
| */ |
| private var openMenuIndex:int = -1; |
| |
| [Bindable("valueCommit")] |
| [Inspectable(category="General", defaultValue="-1")] |
| |
| /** |
| * The index in the MenuBar control of the currently open Menu |
| * or the last opened Menu if none are currently open. |
| * |
| * @default -1 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get selectedIndex():int |
| { |
| return openMenuIndex; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set selectedIndex(value:int):void |
| { |
| openMenuIndex = value; |
| dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT)); |
| } |
| |
| //---------------------------------- |
| // showRoot |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage variable for showRoot flag. |
| */ |
| mx_internal var _showRoot:Boolean = true; |
| |
| /** |
| * @private |
| */ |
| mx_internal var showRootChanged:Boolean = false; |
| |
| [Inspectable(category="Data", enumeration="true,false", defaultValue="false")] |
| |
| /** |
| * A Boolean flag that specifies whether to display the data provider's |
| * root node. |
| * |
| * If the data provider has a root node, and the <code>showRoot</code> property |
| * is set to <code>false</code>, the items on the MenuBar control correspond to |
| * the immediate descendants of the root node. |
| * |
| * This flag has no effect on data providers without root nodes, |
| * like Lists and Arrays. |
| * |
| * @default true |
| * @see #hasRoot |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get showRoot():Boolean |
| { |
| return _showRoot; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set showRoot(value:Boolean):void |
| { |
| if (_showRoot != value) |
| { |
| showRootChanged = true; |
| _showRoot = value; |
| invalidateProperties(); |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // |
| // Overridden methods |
| // |
| //------------------------------------------------------------------------ |
| |
| /** |
| * @private |
| */ |
| override protected function initializeAccessibility():void |
| { |
| if (MenuBar.createAccessibilityImplementation != null) |
| MenuBar.createAccessibilityImplementation(this); |
| } |
| |
| |
| /** |
| * @private |
| */ |
| override protected function createChildren():void |
| { |
| super.createChildren(); |
| |
| // Check if this MenuBar is inside an ACB. |
| for (var p:Object = parent; p; p = p.parent) |
| { |
| if (p is ApplicationControlBar) |
| { |
| isInsideACB = true; |
| break; |
| } |
| } |
| |
| updateBackground(); |
| } |
| |
| /** |
| * Updates the MenuBar control's background skin. |
| * |
| * This method is called when MenuBar children are created or when |
| * any styles on the MenuBar changes. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function updateBackground():void |
| { |
| if (isInsideACB) |
| { |
| // draw translucent menubar |
| setStyle("translucent", true); |
| } |
| else |
| { |
| // Remove existing background |
| if (background) |
| { |
| removeChild(DisplayObject(background)); |
| background = null; |
| } |
| |
| var backgroundSkinClass:Class = getStyle("backgroundSkin"); |
| if (backgroundSkinClass) |
| { |
| background = new backgroundSkinClass(); |
| if (background is ISimpleStyleClient) |
| ISimpleStyleClient(background).styleName = this; |
| addChildAt(DisplayObject(background), 0); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function commitProperties():void |
| { |
| var i:int; |
| var cursor:IViewCursor; |
| |
| if (showRootChanged) |
| { |
| if (!_hasRoot) |
| showRootChanged = false; |
| } |
| |
| if (dataProviderChanged || showRootChanged) |
| { |
| var tmpCollection:ICollectionView; |
| |
| //reset flags |
| dataProviderChanged = false; |
| showRootChanged = false; |
| |
| // forget last menu |
| openMenuIndex = -1; |
| |
| // are we swallowing the root? |
| if (_rootModel && !_showRoot && _hasRoot) |
| { |
| var rootItem:* = _rootModel.createCursor().current; |
| if (rootItem != null && |
| _dataDescriptor.isBranch(rootItem, _rootModel) && |
| _dataDescriptor.hasChildren(rootItem, _rootModel)) |
| { |
| // then get rootItem children |
| tmpCollection = |
| _dataDescriptor.getChildren(rootItem, _rootModel); |
| } |
| } |
| //make top level items |
| removeAll(); |
| if (_rootModel) |
| { |
| if (!tmpCollection) |
| tmpCollection = _rootModel; |
| // not really a default handler, but we need to |
| // be later than the wrapper |
| tmpCollection.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, |
| false, |
| EventPriority.DEFAULT_HANDLER, true); |
| |
| if (tmpCollection.length > 0) |
| { |
| cursor = tmpCollection.createCursor(); |
| i= 0; |
| while (!cursor.afterLast) |
| { |
| try |
| { |
| insertMenuBarItem(i, cursor.current); |
| } |
| catch(e:ItemPendingError) |
| { |
| //we probably dont need to actively recover from here |
| } |
| |
| cursor.moveNext(); |
| i++; |
| } |
| } |
| } |
| } |
| |
| if (iconFieldChanged || menuBarItemRendererChanged) |
| { |
| //reset flag |
| iconFieldChanged = false; |
| menuBarItemRendererChanged = false; |
| |
| removeAll(); |
| if (_rootModel) |
| { |
| if (!tmpCollection) |
| tmpCollection = _rootModel; |
| |
| if (tmpCollection.length > 0) |
| { |
| cursor = tmpCollection.createCursor(); |
| i= 0; |
| while (!cursor.afterLast) |
| { |
| try |
| { |
| insertMenuBarItem(i, cursor.current); |
| } |
| catch(e:ItemPendingError) |
| { |
| //we probably dont need to actively recover from here |
| } |
| |
| cursor.moveNext(); |
| i++; |
| } |
| } |
| } |
| } |
| |
| super.commitProperties(); |
| } |
| |
| /** |
| * Calculates the preferred width and height of the MenuBar based on the |
| * default widths of the MenuBar items. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override protected function measure():void |
| { |
| super.measure(); |
| |
| var len:int = menuBarItems.length; |
| |
| measuredWidth = 0; |
| |
| // measured height is at least 22 |
| measuredHeight = DEFAULT_MEASURED_MIN_HEIGHT; |
| for (var i:int = 0; i < len; i++) |
| { |
| measuredWidth += menuBarItems[i].getExplicitOrMeasuredWidth(); |
| measuredHeight = Math.max( |
| measuredHeight, menuBarItems[i].getExplicitOrMeasuredHeight()); |
| } |
| |
| if (len > 0) |
| measuredWidth += 2 * MARGIN_WIDTH; |
| else // else give it a default width, MARGIN_WIDTH = 10. |
| measuredWidth = DEFAULT_MEASURED_MIN_WIDTH; // setting it slightly more than the width |
| |
| measuredMinWidth = measuredWidth; |
| measuredMinHeight = measuredHeight; |
| } |
| |
| /** |
| * @private |
| * Sizes and positions the items on the MenuBar. |
| */ |
| override protected function updateDisplayList(unscaledWidth:Number, |
| unscaledHeight:Number):void |
| { |
| super.updateDisplayList(unscaledWidth, unscaledHeight); |
| |
| var lastX:Number = MARGIN_WIDTH; |
| var lastW:Number = 0; |
| var len:int = menuBarItems.length; |
| |
| var clipContent:Boolean = false; |
| var hideItems:Boolean = (unscaledWidth == 0 || unscaledHeight == 0); |
| |
| for (var i:int = 0; i < len; i++) |
| { |
| var item:IMenuBarItemRenderer = menuBarItems[i]; |
| |
| item.setActualSize(item.getExplicitOrMeasuredWidth(), unscaledHeight); |
| item.visible = !hideItems; |
| |
| lastX = item.x = lastX+lastW; |
| lastW = item.width; |
| |
| if (!hideItems && |
| (item.getExplicitOrMeasuredHeight() > unscaledHeight || |
| (lastX + lastW) > unscaledWidth)) |
| { |
| clipContent = true; |
| } |
| } |
| |
| if (background) |
| { |
| background.setActualSize(unscaledWidth, unscaledHeight); |
| background.visible = !hideItems; |
| } |
| |
| // Set a scroll rect to handle clipping. |
| scrollRect = clipContent ? new Rectangle(0, 0, |
| unscaledWidth, unscaledHeight) : null; |
| } |
| |
| //------------------------------------------------------------------------ |
| // Focus handling |
| //------------------------------------------------------------------------ |
| |
| /** |
| * @private |
| */ |
| override protected function focusInHandler(event:FocusEvent):void |
| { |
| super.focusInHandler(event); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function focusOutHandler(event:FocusEvent):void |
| { |
| super.focusOutHandler(event); |
| |
| if (supposedToLoseFocus) |
| getMenuAt(openMenuIndex).hide(); |
| |
| supposedToLoseFocus = false; |
| } |
| |
| //------------------------------------------------------------------------ |
| // Support for setStyle |
| //------------------------------------------------------------------------ |
| |
| /** |
| * @private |
| */ |
| override public function styleChanged(styleProp:String):void |
| { |
| var i:int; |
| super.styleChanged(styleProp); |
| |
| var n:int = menuBarItems.length; |
| for (i = 0; i < n; i++) |
| { |
| getMenuAt(i).styleChanged(styleProp); |
| } |
| |
| if (!styleProp || styleProp == "" || styleProp == "backgroundSkin") |
| { |
| updateBackground(); |
| } |
| |
| if (styleProp == null || |
| styleProp == "styleName" || |
| styleProp == "menuStyleName") |
| { |
| var menuStyleName:String = getStyle("menuStyleName"); |
| var m:Menu; |
| if (menuStyleName) |
| { |
| for (i = 0; i < menus.length; i++) |
| { |
| m = menus[i]; |
| m.styleName = menuStyleName; |
| } |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override public function notifyStyleChangeInChildren( |
| styleProp:String, |
| recursive:Boolean):void |
| { |
| super.notifyStyleChangeInChildren(styleProp, recursive); |
| |
| var n:int = menuBarItems.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| getMenuAt(i).notifyStyleChangeInChildren(styleProp, recursive); |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // |
| // Methods |
| // |
| //------------------------------------------------------------------------ |
| |
| /** |
| * @private |
| */ |
| private function collectionChangeHandler(event:Event):void |
| { |
| //trace("[MenuBar] caught Model changed"); |
| if (event is CollectionEvent) |
| { |
| var ce:CollectionEvent = CollectionEvent(event); |
| if (ce.kind == CollectionEventKind.ADD) |
| { |
| dataProviderChanged = true; |
| invalidateProperties(); |
| //trace("[MenuBar] add event"); |
| } |
| else if (ce.kind == CollectionEventKind.REMOVE) |
| { |
| dataProviderChanged = true; |
| invalidateProperties(); |
| //trace("[MenuBar] remove event at:", ce.location); |
| } |
| else if (ce.kind == CollectionEventKind.REFRESH) |
| { |
| dataProviderChanged = true; |
| dataProvider = dataProvider; //start over |
| invalidateProperties(); |
| invalidateSize(); |
| //trace("[MenuBar] refresh event"); |
| } |
| else if (ce.kind == CollectionEventKind.RESET) |
| { |
| dataProviderChanged = true; |
| invalidateProperties(); |
| invalidateSize(); |
| //trace("[MenuBar] reset event"); |
| } |
| else if (ce.kind == CollectionEventKind.UPDATE) |
| { |
| //As long as there are no open menus, we can |
| //handle the update. Otherwise we create new |
| //menubar items and submenus in commitProperties |
| //and run the risk of orphaning the already open |
| //menu. |
| if (openMenuIndex == -1) |
| { |
| dataProviderChanged = true; |
| invalidateProperties(); |
| } |
| } |
| } |
| |
| //bItemsSizeChanged = true; |
| invalidateDisplayList(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function eventHandler(event:Event):void |
| { |
| //these events come from the menu's themselves. |
| //we'll redispatch all of them. |
| if (event is MenuEvent) |
| { |
| var t:String = event.type; |
| |
| if (event.type == MenuEvent.MENU_HIDE && |
| MenuEvent(event).menu == menus[openMenuIndex]) |
| { |
| menuBarItems[openMenuIndex].menuBarItemState = "itemUpSkin"; |
| openMenuIndex = -1; |
| dispatchEvent(event as MenuEvent); |
| } |
| else |
| dispatchEvent(event); |
| } |
| } |
| |
| /** |
| * @private |
| * |
| * Adds a menu to the MenuBar control at the specified location. |
| * An index of 0 inserts the menu at the leftmost spot in the MenuBar. |
| * |
| * @param index Index where the menu should be inserted. |
| * @param arg1 May be either:a String, which is the item's label; or an xmlNode. |
| * @param arg2 May be: undefined; a menu; or an xml/xmlNode. |
| */ |
| private function addMenuAt(index:int, arg1:Object, arg2:Object = null):void |
| { |
| if (!dataProvider) |
| dataProvider = {}; |
| |
| var newMenu:Menu; |
| var mdp:Object; |
| var newItem:Object = arg1; |
| |
| insertMenuBarItem(index, mdp); |
| } |
| |
| /** |
| * @copy mx.controls.listClasses.ListBase#itemToLabel() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function itemToLabel(data:Object):String |
| { |
| if (data == null) |
| return " "; |
| |
| if (labelFunction != null) |
| return labelFunction(data); |
| |
| if (data is XML) |
| { |
| try |
| { |
| if (data[labelField].length() != 0) |
| data = data[labelField]; |
| |
| //if (XMLList(data.@label).length() != 0) |
| //{ |
| // data = data.@label; |
| //} |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| else if (data is Object) |
| { |
| try |
| { |
| if (data[labelField] != null) |
| data = data[labelField]; |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| else if (data is String) |
| return String(data); |
| |
| try |
| { |
| return data.toString(); |
| } |
| catch(e:Error) |
| { |
| } |
| |
| return " "; |
| } |
| |
| /** |
| * Returns the class for an icon, if any, for a data item, |
| * based on the <code>iconField</code> property. |
| * The field in the item can return a string as long as that |
| * string represents the name of a class in the application. |
| * The field in the item can also be a string that is the name |
| * of a variable in the document that holds the class for |
| * the icon. |
| * |
| * @param data The item from which to extract the icon class |
| * @return The icon for the item, as a class reference or |
| * <code>null</code> if none. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function itemToIcon(data:Object):Class |
| { |
| if (data == null) |
| { |
| return null; |
| } |
| var iconClass:Class; |
| var icon:*; |
| |
| if (data is XML) |
| { |
| try |
| { |
| if (data[iconField].length() != 0) |
| { |
| icon = String(data[iconField]); |
| if (icon != null) |
| { |
| iconClass = |
| Class(systemManager.getDefinitionByName(icon)); |
| if (iconClass) |
| return iconClass; |
| |
| return document[icon]; |
| } |
| } |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| |
| else if (data is Object) |
| { |
| try |
| { |
| if (data[iconField] != null) |
| { |
| if (data[iconField] is Class) |
| { |
| return data[iconField]; |
| } |
| if (data[iconField] is String) |
| { |
| iconClass = Class(systemManager.getDefinitionByName( |
| data[iconField])); |
| if (iconClass) |
| { |
| return iconClass; |
| } |
| return document[data[iconField]]; |
| } |
| } |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| |
| return null; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // Activator list management |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private function insertMenuBarItem(index:int, mdp:Object):void |
| { |
| if (dataProviderChanged) |
| { |
| commitProperties(); |
| return; |
| } |
| |
| var item:IMenuBarItemRenderer = menuBarItemRenderer.newInstance(); |
| item.styleName = new StyleProxy(this, menuBarItemStyleFilters); |
| item.visible = false; |
| item.enabled = enabled && _dataDescriptor.isEnabled(mdp) != false; |
| item.data = mdp; |
| item.menuBar = this; |
| item.menuBarItemIndex = index; |
| addChild(DisplayObject(item)); |
| menuBarItems.splice(index, 0, item); |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| |
| item.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler); |
| item.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); |
| item.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); |
| item.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler); |
| } |
| |
| /** |
| * Returns a reference to the Menu object at the specified MenuBar item index, |
| * where 0 is the Menu contained at the leftmost MenuBar item index. |
| * |
| * @param index Index of the Menu instance to return. |
| * |
| * @return Reference to the Menu contained at the specified index. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getMenuAt(index:int):Menu |
| { |
| if (dataProviderChanged) |
| commitProperties(); |
| |
| var item:IMenuBarItemRenderer; |
| |
| if (index < 0 || index >= menuBarItems.length) |
| return null; |
| |
| item = menuBarItems[index]; |
| |
| var mdp:Object = item.data; |
| var menu:Menu = menus[index]; |
| |
| if (menu == null) |
| { |
| menu = new Menu(); |
| menu.showRoot = false; |
| |
| var menuStyleName:Object = getStyle("menuStyleName"); |
| if (menuStyleName) |
| menu.styleName = menuStyleName; |
| |
| menu.sourceMenuBar = this; |
| menu.owner = this; |
| menu.addEventListener("menuHide", eventHandler); |
| menu.addEventListener("itemRollOver", eventHandler); |
| menu.addEventListener("itemRollOut", eventHandler); |
| menu.addEventListener("menuShow", eventHandler); |
| menu.addEventListener("itemClick", eventHandler); |
| menu.addEventListener("change", eventHandler); |
| menu.iconField = _iconField; |
| menu.labelField = _labelField; |
| menu.labelFunction = labelFunction; |
| menu.dataDescriptor = _dataDescriptor; |
| menu.invalidateSize(); |
| |
| menus[index] = menu; |
| menu.sourceMenuBarItem = item; // menu needs this for a hitTest when clicking outside menu area |
| Menu.popUpMenu(menu, this, mdp); |
| } |
| |
| return menu; |
| } |
| |
| /** |
| * @private |
| * Show a menuBarItem's menu |
| */ |
| private function showMenu(index:Number):void |
| { |
| selectedIndex = index; |
| var item:IMenuBarItemRenderer = menuBarItems[index]; |
| |
| // The getMenuAt function will create the Menu if it doesn't |
| // already exist. |
| var menu:Menu = getMenuAt(index); |
| var sm:ISystemManager = systemManager.topLevelSystemManager; |
| var screen:Rectangle = sm.getVisibleApplicationRect(null, true); |
| |
| // pop it up if we haven't already. this allows us to validate the menu and get correct sizes |
| if (menu.parentDisplayObject && (!menu.parent || !menu.parent.contains(menu.parentDisplayObject))) |
| { |
| PopUpManager.addPopUp(menu, this, false); |
| menu.addEventListener(MenuEvent.MENU_HIDE, menuHideHandler, false, EventPriority.DEFAULT_HANDLER); |
| } |
| |
| // pop it up if we haven't already. this allows us to validate the menu and get correct sizes |
| if (menu.parentDisplayObject && (!menu.parent || !menu.parent.contains(menu.parentDisplayObject))) |
| { |
| PopUpManager.addPopUp(menu, this, false); |
| menu.addEventListener(MenuEvent.MENU_HIDE, menuHideHandler, false, EventPriority.DEFAULT_HANDLER); |
| } |
| |
| UIComponentGlobals.layoutManager.validateClient(menu, true); |
| |
| // popups go on the root of the swf which if loaded, is not |
| // necessarily at 0,0 in global coordinates |
| var pt:Point = new Point(0, 0); |
| pt = DisplayObject(item).localToGlobal(pt); |
| |
| // If the layout has been mirrored, then the 0,0 is the uppper |
| // right corner; compensate here. |
| if (layoutDirection == LayoutDirection.RTL) |
| pt.x -= menu.getExplicitOrMeasuredWidth(); |
| |
| // check to see if we'll go offscreen |
| if (pt.y + item.height + 1 + menu.getExplicitOrMeasuredHeight() > screen.height + screen.y) |
| pt.y -= menu.getExplicitOrMeasuredHeight(); |
| else |
| pt.y += item.height + 1; |
| if (pt.x + menu.getExplicitOrMeasuredWidth() > screen.width + screen.x) |
| pt.x = screen.x + screen.width - menu.getExplicitOrMeasuredWidth(); |
| pt.x = Math.max(screen.x, pt.x); |
| pt = sm.getSandboxRoot().globalToLocal(pt); |
| |
| // If inside an ACB, slight offset looks much better. |
| if (isInsideACB) |
| pt.y += 2; |
| |
| menu.show(pt.x, pt.y); |
| } |
| |
| /** |
| * @private |
| * Removes the root menu from the display list. This is called only for |
| * menus created using "createMenu". |
| * |
| * MJM private static? |
| */ |
| private static function menuHideHandler(event:MenuEvent):void |
| { |
| var menu:Menu = Menu(event.target); |
| if (!event.isDefaultPrevented() && event.menu == menu) |
| { |
| menu.supposedToLoseFocus = true; |
| PopUpManager.removePopUp(menu); |
| menu.removeEventListener(MenuEvent.MENU_HIDE, menuHideHandler); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function removeMenuBarItemAt(index:int):void |
| { |
| if (dataProviderChanged) |
| commitProperties(); |
| |
| var item:IMenuBarItemRenderer = menuBarItems[index]; |
| |
| if (item) |
| { |
| item.removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler); |
| item.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); |
| item.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); |
| item.removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler); |
| |
| removeChild(DisplayObject(item)); |
| menuBarItems.splice(index, 1); |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| } |
| |
| |
| /** |
| * @private |
| */ |
| private function removeAll():void |
| { |
| if (dataProviderChanged) |
| commitProperties(); |
| |
| while (menuBarItems.length > 0) |
| { |
| var item:IMenuBarItemRenderer = menuBarItems[0]; |
| |
| item.removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler); |
| item.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); |
| item.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); |
| item.removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler); |
| |
| removeChild(DisplayObject(item)); |
| menuBarItems.splice(0, 1); |
| } |
| |
| menus = []; |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // Mouse Event Handlers |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private function mouseOverHandler(event:MouseEvent):void |
| { |
| var item:IMenuBarItemRenderer = IMenuBarItemRenderer(event.target); |
| var index:int = item.menuBarItemIndex; |
| var requiresEvent:Boolean = false; |
| var m:Menu = getMenuAt(index); |
| var evt:MenuEvent; |
| if (item.enabled) |
| { |
| if (openMenuIndex != -1 || inKeyDown) |
| { |
| var oldIndex:Number = openMenuIndex; |
| if (oldIndex != index) |
| { |
| // Deactivate the old |
| isDown = false; |
| if (oldIndex != -1) |
| { |
| var oldItem:IMenuBarItemRenderer = menuBarItems[oldIndex]; |
| oldItem.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP)); |
| oldItem.menuBarItemState = "itemUpSkin"; |
| |
| //we've effectively rolled out of this item, dispatch the |
| //correct itemRollOut event |
| evt = new MenuEvent(MenuEvent.ITEM_ROLL_OUT); |
| evt.menuBar = this; |
| evt.index = oldIndex; |
| evt.label = itemToLabel(oldItem.data); |
| evt.item = oldItem.data; |
| evt.itemRenderer = oldItem; |
| dispatchEvent(evt); |
| } |
| |
| // Activate the new |
| item.menuBarItemState = "itemDownSkin"; |
| var dp:ICollectionView = ICollectionView(m.dataProvider); |
| |
| //Show only those menus that have children to show |
| if (m.dataDescriptor.isBranch(item.data, item.data) && |
| m.dataDescriptor.hasChildren(item.data, item.data)) |
| { |
| showMenu(index); |
| } |
| // we still want to dispatch a menuShow event for top-level menuitems with |
| // no children and update the selectedIndex property |
| else if (m) |
| { |
| selectedIndex = index; |
| |
| evt = new MenuEvent(MenuEvent.MENU_SHOW); |
| evt.menuBar = this; |
| evt.menu = m; |
| dispatchEvent(evt); |
| |
| item.menuBarItemState = "itemOverSkin"; |
| } |
| isDown = true; |
| |
| if (m && m.dataDescriptor.getType(item.data) != "separator") |
| { |
| requiresEvent = true; |
| //fire the change event |
| evt = new MenuEvent(MenuEvent.CHANGE); |
| evt.index = index; |
| evt.menuBar = this; |
| evt.label = itemToLabel(item.data); |
| evt.item = item.data; |
| evt.itemRenderer = item; |
| dispatchEvent(evt); |
| } |
| |
| } |
| else |
| { |
| var mm:Menu = getMenuAt(index); |
| if (mm) { |
| mm.deleteDependentSubMenus(); |
| mm.setFocus(); |
| } |
| } |
| } |
| else |
| { |
| item.menuBarItemState = "itemOverSkin"; |
| isDown = false; |
| if (m.dataDescriptor.getType(item.data) != "separator") |
| requiresEvent = true; |
| } |
| inKeyDown = false; |
| |
| if (requiresEvent) |
| { |
| // Fire the appropriate rollover event |
| evt = new MenuEvent(MenuEvent.ITEM_ROLL_OVER); |
| evt.index = index; |
| evt.menuBar = this; |
| evt.label = itemToLabel(item.data); |
| evt.item = item.data; |
| evt.itemRenderer = item; |
| dispatchEvent(evt); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function mouseDownHandler(event:MouseEvent):void |
| { |
| var item:IMenuBarItemRenderer = IMenuBarItemRenderer(event.target); |
| var index:int = item.menuBarItemIndex; |
| var m:Menu = getMenuAt(index); |
| |
| if (item.enabled) |
| { |
| item.menuBarItemState = "itemDownSkin"; |
| |
| if (m && !isDown) |
| { |
| m.supposedToLoseFocus = true; |
| |
| var dp:ICollectionView = ICollectionView(m.dataProvider) |
| //Show only those menus that have children to show |
| if (m.dataDescriptor.isBranch(item.data, item.data) && |
| m.dataDescriptor.hasChildren(item.data, item.data)) |
| { |
| showMenu(index); |
| } |
| |
| // we still want to dispatch a menuShow event for top-level menuitems with no children |
| // and update the selectedIndex property |
| else if (m) |
| { |
| selectedIndex = index; |
| |
| var evt:MenuEvent = new MenuEvent(MenuEvent.MENU_SHOW); |
| evt.menuBar = this; |
| evt.menu = m; |
| dispatchEvent(evt); |
| } |
| |
| isDown = true; |
| } |
| else |
| { |
| isDown = false; |
| } |
| |
| if (m && m.dataDescriptor.getType(item.data) != "separator") |
| { |
| //fire the change event |
| var menuEvent:MenuEvent = new MenuEvent(MenuEvent.CHANGE); |
| menuEvent.index = index; |
| menuEvent.menuBar = this; |
| menuEvent.label = itemToLabel(item.data); |
| menuEvent.item = item.data; |
| menuEvent.itemRenderer = item; |
| dispatchEvent(menuEvent); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function mouseUpHandler(event:MouseEvent):void |
| { |
| var item:IMenuBarItemRenderer = IMenuBarItemRenderer(event.target); |
| var index:int = item.menuBarItemIndex; |
| |
| if (item.enabled && !isDown) |
| { |
| var m:Menu = getMenuAt(index); |
| |
| if (m) |
| m.hideAllMenus(); |
| item.menuBarItemState = "itemOverSkin"; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function mouseOutHandler(event:MouseEvent):void |
| { |
| var item:IMenuBarItemRenderer = IMenuBarItemRenderer(event.target); |
| var index:int = item.menuBarItemIndex; |
| var m:Menu = getMenuAt(index); |
| |
| if (item.enabled && m && openMenuIndex != index) |
| { |
| menuBarItems[index].menuBarItemState = "itemUpSkin"; |
| } |
| // Fire the appropriate rollout event |
| if (item.data && m && |
| (m.dataDescriptor.getType(item.data) != "separator")) |
| { |
| var menuEvent:MenuEvent = new MenuEvent(MenuEvent.ITEM_ROLL_OUT); |
| menuEvent.index = index; |
| menuEvent.menuBar = this; |
| menuEvent.label = itemToLabel(item.data); |
| menuEvent.item = item.data; |
| menuEvent.itemRenderer = item; |
| dispatchEvent(menuEvent); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // Keyboard Navigation Handlers |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| override protected function keyDownHandler(event:KeyboardEvent):void |
| { |
| var barLen:int = menuBarItems.length; |
| |
| if (event.keyCode == Keyboard.RIGHT || event.keyCode == Keyboard.LEFT) |
| { |
| inKeyDown = true; |
| var nextIndex:int = openMenuIndex; |
| var found:Boolean = false; |
| var count:int = 0; |
| |
| // If rtl layout, need to swap LEFT and RIGHT so correct action |
| // is done. |
| var keyCode:uint = mapKeycodeForLayoutDirection(event); |
| |
| while (!found && count < barLen) |
| { |
| count++; |
| nextIndex = (keyCode == Keyboard.RIGHT) ? nextIndex + 1 : nextIndex - 1; |
| |
| if (nextIndex>=barLen) |
| nextIndex = 0; |
| else if (nextIndex < 0) |
| nextIndex = barLen - 1; |
| |
| if (menuBarItems[nextIndex].enabled) |
| found = true; |
| } |
| |
| // trigger next item in the bar |
| if (count <= barLen && found) |
| menuBarItems[nextIndex].dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OVER)); |
| |
| event.stopPropagation(); |
| } |
| |
| // Handle Keyboard.DOWN Navigation |
| if (keyCode == Keyboard.DOWN) |
| { |
| if (openMenuIndex != -1) |
| { |
| var menu:Menu = getMenuAt(openMenuIndex); |
| menu.selectedIndex = 0; |
| supposedToLoseFocus = true; |
| var dp:ICollectionView = ICollectionView(menu.dataProvider); |
| var item:IMenuBarItemRenderer = menu.sourceMenuBarItem; |
| |
| //focus only those menus that have children to show |
| if (menu.dataDescriptor.isBranch(item.data, item.data) && |
| menu.dataDescriptor.hasChildren(item.data, item.data)) |
| { |
| menu.setFocus(); |
| } |
| } |
| event.stopPropagation(); |
| } |
| |
| // Handle Keyboard.ENTER/ESCAPE Commands |
| if ((keyCode == Keyboard.ENTER) || (keyCode == Keyboard.ESCAPE)) |
| { |
| if (openMenuIndex != -1) |
| getMenuAt(openMenuIndex).hide(); |
| event.stopPropagation(); |
| } |
| } |
| |
| } |
| |
| } |