blob: c4158b2eb8d7bbb31c7eb4ca87f8e742c2f36699 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.controls
{
import flash.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>&lt;mx:MenuBar&gt</code> tag inherits all of the tag attributes of its superclass, and
* adds the following tag attributes:
* </p>
*
* <pre>
* &lt;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>
* /&gt;
* </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>&lt;mx:MenuBar id="menubar" dataDescriptor="{new MyCustomDataDescriptor()}"/&gt;</pre>
*
* <p>Alternatively, you can specify the property in MXML as a nested
* subtag, as the following example shows:</p>
*
* <pre>&lt;mx:MenuBar&gt;
* &lt;mx:dataDescriptor&gt;
* &lt;myCustomDataDescriptor&gt;
* &lt;/mx:dataDescriptor&gt;
* ...</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();
}
}
}
}