blob: 9a6062704a304b804db6b76874aa3b9d68f96389 [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.utils.getTimer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.EdgeMetrics;
import mx.core.IFlexDisplayObject;
import mx.core.ILayoutDirectionElement;
import mx.core.IRectangularBorder;
import mx.core.IUIComponent;
import mx.core.UIComponent;
import mx.core.UIComponentGlobals;
import mx.core.mx_internal;
import mx.effects.Tween;
import mx.events.DropdownEvent;
import mx.events.FlexMouseEvent;
import mx.events.InterManagerRequest;
import mx.events.ListEvent;
import mx.events.MenuEvent;
import mx.events.SandboxMouseEvent;
import mx.managers.IFocusManagerComponent;
import mx.managers.ISystemManager;
import mx.managers.PopUpManager;
import mx.styles.ISimpleStyleClient;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched when the specified UIComponent closes.
*
* @eventType mx.events.DropdownEvent.CLOSE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="close", type="mx.events.DropdownEvent")]
/**
* Dispatched when the specified UIComponent opens.
*
* @eventType mx.events.DropdownEvent.OPEN
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="open", type="mx.events.DropdownEvent")]
//--------------------------------------
// Styles
//--------------------------------------
include "../styles/metadata/IconColorStyles.as"
/**
* Number of pixels between the divider line and the right
* edge of the component.
*
* @default 18
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="arrowButtonWidth", type="Number", format="Length", inherit="no")]
/**
* Length of a close transition, in milliseconds.
*
* The default value for the Halo theme is 250.
* The default value for the Spark theme is 50.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="closeDuration", type="Number", format="Time", inherit="no")]
/**
* Easing function to control component closing tween.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="closeEasingFunction", type="Function", inherit="no")]
/**
* The default icon class for the main button.
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="icon", type="Class", inherit="no", states="up, over, down, disabled")]
/**
* Length of an open transition, in milliseconds.
*
* The default value for the Halo theme is 250.
* The default value for the Spark theme is 0.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="openDuration", type="Number", format="Time", inherit="no")]
/**
* Easing function to control component opening tween.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="openEasingFunction", type="Function", inherit="no")]
/**
* The name of a CSS style declaration used by the control.
* This style allows you to control the appearance of the
* UIComponent object popped up by this control.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="popUpStyleName", type="String", inherit="no")]
/**
* Skin class for the popUpDown state (when arrowButton is in down
* state) of the background and border.
*
* <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.PopUpButtonSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.PopUpButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="popUpDownSkin", type="Class", inherit="no")]
/**
* Number of vertical pixels between the PopUpButton and the
* specified popup UIComponent.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="popUpGap", type="Number", format="Length", inherit="no")]
/**
* The icon used for the right button of PopUpButton.
* Supported classes are mx.skins.halo.PopUpIcon
* and mx.skins.halo.PopUpMenuIcon.
*
* @default mx.skins.halo.PopUpIcon (for Halo and Spark themes)
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="popUpIcon", type="Class", inherit="no")]
/**
* Skin class for the popUpOver state (over arrowButton) of
* the background and border.
*
* <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.PopUpButtonSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.PopUpButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="popUpOverSkin", type="Class", inherit="no")]
/**
* Default stateful skin class for the control.
*
* The default value for the Halo theme is <code>mx.skins.halo.PopUpButtonSkin</code>.
* The default value for the Spark theme is <code>mx.skins.spark.PopUpButtonSkin</code>.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="skin", type="Class", inherit="no", states="up, over, down, disabled, popUpOver, popUpDown")]
/**
* 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")]
//--------------------------------------
// Excluded APIs
//--------------------------------------
[Exclude(name="toggle", kind="property")]
[Exclude(name="selectedDisabledIcon", kind="style")]
[Exclude(name="selectedDisabledSkin", kind="style")]
[Exclude(name="selectedDownIcon", kind="style")]
[Exclude(name="selectedDownSkin", kind="style")]
[Exclude(name="selectedOverIcon", kind="style")]
[Exclude(name="selectedOverSkin", kind="style")]
[Exclude(name="selectedUpIcon", kind="style")]
[Exclude(name="selectedUpSkin", kind="style")]
//--------------------------------------
// Other metadata
//--------------------------------------
//[IconFile("PopUpButton.png")]
[RequiresDataBinding(true)]
/**
* The PopUpButton control adds a flexible pop-up control
* interface to a Button control.
* It contains a main button and a secondary button,
* called the pop-up button, which pops up any UIComponent
* object when a user clicks the pop-up button.
*
* <p>A PopUpButton control can have a text label, an icon,
* or both on its face.
* When a user clicks the main part of the PopUpButton
* control, it dispatches a <code>click</code> event.</p>
*
* <p>One common use for the PopUpButton control is to have
* the pop-up button open a List control or a Menu control
* that changes the function and label of the main button.</p>
*
* <p>The PopUpButton control has the following default characteristics:</p>
* <table class="innertable">
* <tr>
* <th>Characteristic</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>Default size</td>
* <td>Sufficient width to accommodate the label and icon on the main button and the icon on the pop-up button</td>
* </tr>
* <tr>
* <td>Minimum size</td>
* <td>0 pixels</td>
* </tr>
* <tr>
* <td>Maximum size</td>
* <td>Undefined</td>
* </tr>
* </table>
*
* @mxml
*
* <p>The <code>&lt;mx:PopUpButton&gt;</code> tag inherits all of the tag
* attributes of its superclass and adds the following tag attributes:</p>
*
* <pre>
* &lt;mx:PopUpButton
* <strong>Properties</strong>
* openAlways="false|true
* popUp="No default"
*
* <strong>Styles</strong>
* arrowButtonWidth="16"
* closeDuration="250"
* closeEasingFunction="No default"
* disabledIconColor="0x999999"
* iconColor="0x111111"
* openDuration="250"
* openEasingFunction="No default"
* popUpDownSkin="popUpDownSkin"
* popUpGap="0"
* popUpIcon="PopUpIcon"
* popUpOverSkin="popUpOverSkin"
*
* <strong>Events</strong>
* close="No default"
* open="No default"
* /&gt;
* </pre>
*
* @includeExample examples/PopUpButtonExample.mxml
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class PopUpButton extends Button
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function PopUpButton()
{
super();
addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var inTween:Boolean = false;
/**
* @private
* Is the popUp list currently shown?
*/
private var showingPopUp:Boolean = false;
/**
* @private
* The tween used for showing/hiding the popUp.
*/
private var tween:Tween = null;
/**
* @private
* Greater of the arrowButtonWidth style and the icon's width.
*/
private var arrowButtonsWidth:Number = 0;
/**
* @private
* Greater of the arrowButtonsHeight style and the icon's height.
*/
private var arrowButtonsHeight:Number = 0;
/**
* @private
*/
private var popUpIconChanged:Boolean = false;
/**
* @private
*/
private var popUpChanged:Boolean = false;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// showInAutomationHierarchy
//----------------------------------
/**
* @private
*/
override public function set showInAutomationHierarchy(value:Boolean):void
{
//do not allow value changes
}
//----------------------------------
// toggle
//----------------------------------
[Inspectable(environment="none")]
/**
* @private
* A PopUpButton is not toggleable by definition, so _toggle is set
* to false in the constructor and can't be changed via this setter.
*/
override public function set toggle(value:Boolean):void
{
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// closeOnActivity
//----------------------------------
/**
* @private
* Storage for the closeOnActivity property.
*/
private var _closeOnActivity:Boolean = true;
/**
* If <code>true</code>, specifies that the pop-up control closes
* on a click or other user activity.
* The pop-up control is the control opened by a click on the
* pop-up button.
*
* <p>For pop-up controls like Menu, List, or TileList,
* you can leave this property with its default value of <code>true</code>
* because those controls should close on activity.
* However, for multiple selection and other types of pop-up controls,
* you can set this property to <code>false</code> to prevent the
* pop-up control from closing on activity.</p>
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get closeOnActivity():Boolean
{
return _closeOnActivity;
}
/**
* @private
*/
public function set closeOnActivity(value:Boolean):void
{
_closeOnActivity = value;
}
//----------------------------------
// openAlways
//----------------------------------
/**
* @private
* Storage for the openAlways property.
*/
private var _openAlways:Boolean = false;
[Inspectable(category="General", defaultValue="false")]
/**
* If <code>true</code>, specifies to pop up the
* <code>popUp</code> when you click the main button.
* The <code>popUp</code> always appears when you press the Spacebar,
* or when you click the pop-up button, regardless of the setting of
* the <code>openAlways</code> property.
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get openAlways():Boolean
{
// We are not exposing this property for now, until the need arises.
return _openAlways;
}
/**
* @private
*/
public function set openAlways(value:Boolean):void
{
_openAlways = value;
}
//----------------------------------
// popUp
//----------------------------------
/**
* @private
* Storage for popUp property.
*/
private var _popUp:IUIComponent = null;
[Bindable(event='popUpChanged')]
[Inspectable(category="General", defaultValue="null")]
/**
* Specifies the UIComponent object, or object defined by a subclass
* of UIComponent, to pop up.
* For example, you can specify a Menu, TileList, or Tree control.
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get popUp():IUIComponent
{
return _popUp;
}
/**
* @private
*/
public function set popUp(value:IUIComponent):void
{
_popUp = value;
popUpChanged = true;
invalidateProperties();
}
//--------------------------------------------------------------------------
//
// Overridden methods: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
if (popUpChanged)
{
if (_popUp is IFocusManagerComponent)
IFocusManagerComponent(_popUp).focusEnabled = false;
_popUp.cacheAsBitmap = true;
_popUp.scrollRect = new Rectangle(0, 0, 0, 0);
if (_popUp is Menu)
_popUp.addEventListener(MenuEvent.MENU_HIDE, menuHideHandler);
if (_popUp is IListItemRenderer)
{
_popUp.addEventListener(
ListEvent.ITEM_CLICK, popUpItemClickHandler);
}
_popUp.addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE,
popMouseDownOutsideHandler);
_popUp.addEventListener(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE,
popMouseDownOutsideHandler);
_popUp.addEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE,
popMouseDownOutsideHandler);
_popUp.addEventListener(SandboxMouseEvent.MOUSE_WHEEL_SOMEWHERE,
popMouseDownOutsideHandler);
//weak reference to stage
var sm:ISystemManager = systemManager.topLevelSystemManager;
sm.getSandboxRoot().addEventListener(Event.RESIZE, stage_resizeHandler, false, 0, true);
_popUp.owner = this;
if (_popUp is ISimpleStyleClient)
ISimpleStyleClient(_popUp).styleName = getStyle("popUpStyleName");
popUpChanged = false;
}
// Close if we're disabled and we happen to still be showing our popup.
if (showingPopUp && !enabled)
close();
}
/**
* @private
*/
override protected function measure():void
{
super.measure();
calcArrowButtonSize();
// Add in pop-up button width
measuredWidth += arrowButtonsWidth;
measuredHeight = arrowButtonsHeight;
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
// PopUpIcon style prop has changed, dump skins
if (styleProp == null ||
styleProp == "styleName" ||
styleProp.toLowerCase().indexOf("popupicon") != -1)
{
popUpIconChanged = true;
changeSkins();
}
if (styleProp == "arrowButtonWidth")
invalidateSize();
else if (styleProp == "popUpStyleName" && _popUp
&& _popUp is ISimpleStyleClient)
ISimpleStyleClient(_popUp).styleName = getStyle("popUpStyleName");
super.styleChanged(styleProp);
}
//--------------------------------------------------------------------------
//
// Overridden methods: Button
//
//--------------------------------------------------------------------------
/**
* @private
*/
override mx_internal function layoutContents(unscaledWidth:Number,
unscaledHeight:Number,
offset:Boolean):void
{
if ((!isNaN(explicitWidth) && !isNaN(explicitHeight)) ||
popUpIconChanged)
{
calcArrowButtonSize();
popUpIconChanged = false;
}
unscaledWidth -= arrowButtonsWidth;
super.layoutContents(unscaledWidth, unscaledHeight, offset);
unscaledWidth += arrowButtonsWidth;
}
/**
* @private
* Displays one of the six possible skins,
* creating it if it doesn't already exist.
*/
override mx_internal function viewSkin():void
{
// Determine which skin to display, based on whether this
// PopUpButton is enabled or disabled, whether it is
// selected or unselected, and how it is currently interacting
// with the mouse (i.e., the up/over/down state).
var tempSkinName:String;
var stateName:String;
if (!enabled)
tempSkinName = "disabledSkin";
else if (phase == "arrowOver")
tempSkinName = "popUpOverSkin";
else if (phase == "arrowDown")
tempSkinName = "popUpDownSkin";
else
tempSkinName = phase + "Skin";
viewSkinForPhase(tempSkinName, getCurrentButtonState());
}
/**
* @private
*/
override mx_internal function getCurrentIconName():String
{
var iconName:String = super.getCurrentIconName();
if (iconName)
return iconName;
if (phase == "arrowOver")
iconName = selected ? selectedOverIconName : overIconName;
else if (phase == "arrowDown")
iconName = selected ? selectedDownIconName : downIconName;
else
iconName = phase + "Icon";
return iconName;
}
/**
* @private
* Displays one of the four possible icons,
* creating it if it doesn't already exist.
*/
override mx_internal function viewIcon():void
{
// Determine which skin to display, based on whether this
// PopUpButton is enabled or disabled, whether it is
// selected or unselected, and how it is currently interacting
// with the mouse (i.e., the up/over/down state).
var iconName:String;
if (!enabled)
iconName = "disabledIcon";
else if (phase == "arrowOver")
iconName = selected ? selectedOverIconName : overIconName;
else if (phase == "arrowDown")
iconName = selected ? selectedDownIconName : downIconName;
else
iconName = phase + "Icon";
viewIconForPhase(iconName);
}
/**
* @private
* Computes the current button state based on whether this button is
* enabled or disabled, whether it is selected or unselected, and how it
* is currently interacting with the mouse (i.e. the up/over/down state).
*/
override mx_internal function getCurrentButtonState():String
{
_currentButtonState = "";
if (!enabled)
_currentButtonState = "disabled";
else if (phase == "arrowOver")
_currentButtonState = "popUpOver";
else if (phase == "arrowDown")
_currentButtonState = "popUpDown";
else
_currentButtonState = phase;
return _currentButtonState;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Used by PopUpMenuButton
*/
mx_internal function getPopUp():IUIComponent
{
return _popUp ? _popUp : null;
}
/**
* Opens the UIComponent object specified by the <code>popUp</code> property.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function open():void
{
openWithEvent(null);
}
/**
* @private
*/
private function openWithEvent(trigger:Event = null):void
{
if (!showingPopUp && enabled)
{
displayPopUp(true);
var cbde:DropdownEvent = new DropdownEvent(DropdownEvent.OPEN);
cbde.triggerEvent = trigger;
dispatchEvent(cbde);
}
}
/**
* Closes the UIComponent object opened by the PopUpButton control.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function close():void
{
closeWithEvent(null);
}
/**
* @private
*/
private function closeWithEvent(trigger:Event = null):void
{
if (showingPopUp)
{
displayPopUp(false);
var cbde:DropdownEvent = new DropdownEvent(DropdownEvent.CLOSE);
cbde.triggerEvent = trigger;
dispatchEvent(cbde);
}
}
/**
* @private
*/
private function displayPopUp(show:Boolean):void
{
if (!initialized || (show == showingPopUp))
return;
// Subclasses may extend to do pre-processing
// before the popUp is displayed
// or override to implement special display behavior
if (getPopUp() == null)
return;
if (_popUp is ILayoutDirectionElement)
{
ILayoutDirectionElement(_popUp).layoutDirection = layoutDirection;
}
//Show or hide the popup
var initY:Number;
var endY:Number;
var easingFunction:Function;
var duration:Number;
var sm:ISystemManager = systemManager.topLevelSystemManager;
var screen:Rectangle = sm.getVisibleApplicationRect(null, true);
if (show)
{
if (_popUp.parent == null)
{
PopUpManager.addPopUp(_popUp, this, false);
_popUp.owner = this;
}
else
PopUpManager.bringToFront(_popUp);
}
var popUpGap:Number = getStyle("popUpGap");
var point:Point = new Point(layoutDirection == "rtl" ? _popUp.getExplicitOrMeasuredWidth() : 0, unscaledHeight + popUpGap);
point = localToGlobal(point);
if (show)
{
if (point.y + _popUp.height > screen.bottom &&
point.y > (screen.top + height + _popUp.height))
{
// PopUp will go below the bottom of the stage
// and be clipped. Instead, have it grow up.
point.y -= (unscaledHeight + _popUp.height + 2*popUpGap);
initY = -_popUp.height;
}
else
{
initY = _popUp.height;
}
point.x = Math.min( point.x, screen.right - _popUp.getExplicitOrMeasuredWidth());
point.x = Math.max( point.x, 0);
point = _popUp.parent.globalToLocal(point);
if (_popUp.x != point.x || _popUp.y != point.y)
_popUp.move(point.x, point.y);
_popUp.scrollRect = new Rectangle(0, initY,
_popUp.width, _popUp.height);
if (!_popUp.visible)
_popUp.visible = true;
endY = 0;
showingPopUp = show;
duration = getStyle("openDuration");
easingFunction = getStyle("openEasingFunction") as Function;
}
else
{
showingPopUp = show;
if (_popUp.parent == null)
return;
point = _popUp.parent.globalToLocal(point);
endY = (point.y + _popUp.height > screen.bottom &&
point.y > (screen.top + height + _popUp.height)
? -_popUp.height - 2
: _popUp.height + 2);
initY = 0;
duration = getStyle("closeDuration")
easingFunction = getStyle("closeEasingFunction") as Function;
}
inTween = true;
UIComponentGlobals.layoutManager.validateNow();
// Block all layout, responses from web service, and other background
// processing until the tween finishes executing.
UIComponent.suspendBackgroundProcessing();
tween = new Tween(this, initY, endY, duration);
if (easingFunction != null)
tween.easingFunction = easingFunction;
}
/**
* @private
* Calculates the ArrowButton's sizes.
*/
private function calcArrowButtonSize():void
{
// If the current skin defines a borderMetrics property,
// then use it. Otherwise, use a default value.
var bm:EdgeMetrics;
if (currentSkin is IRectangularBorder)
bm = IRectangularBorder(currentSkin).borderMetrics;
else
bm = new EdgeMetrics(3, 3, 3, 3);
var popUpIcon:IFlexDisplayObject =
IFlexDisplayObject(getChildByName("popUpIcon"));
// If not, create it.
if (!popUpIcon)
{
var popUpIconClass:Class = Class(getStyle("popUpIcon"));
popUpIcon = new popUpIconClass();
}
arrowButtonsWidth = Math.max(getStyle("arrowButtonWidth"),
popUpIcon.width + bm.right + 1);
arrowButtonsHeight = Math.max(measuredHeight,
popUpIcon.height + bm.top + bm.bottom);
}
/**
* @private
* Returns true if the mouse is over the pop-up button
*/
mx_internal function overArrowButton(event:MouseEvent):Boolean
{
return event.localX >= unscaledWidth - arrowButtonsWidth;
}
/**
* @private
*/
mx_internal function onTweenUpdate(value:Number):void
{
_popUp.scrollRect =
new Rectangle(0, value, _popUp.width, _popUp.height);
}
/**
* @private
*/
mx_internal function onTweenEnd(value:Number):void
{
_popUp.scrollRect =
new Rectangle(0, value, _popUp.width, _popUp.height);
inTween = false;
UIComponent.resumeBackgroundProcessing();
if (!showingPopUp)
{
_popUp.visible = false;
_popUp.scrollRect = null;
}
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function keyDownHandler(event:KeyboardEvent):void
{
super.keyDownHandler(event);
if (event.ctrlKey && event.keyCode == Keyboard.DOWN)
{
openWithEvent(event);
event.stopPropagation();
}
else if ((event.ctrlKey && event.keyCode == Keyboard.UP) ||
(event.keyCode == Keyboard.ESCAPE))
{
closeWithEvent(event);
event.stopPropagation();
}
else if (event.keyCode == Keyboard.ENTER && showingPopUp)
{
// Redispatch the event to the popup
// and let its keyDownHandler() handle it.
_popUp.dispatchEvent(event);
closeWithEvent(event);
event.stopPropagation();
}
else if (showingPopUp &&
(event.keyCode == Keyboard.UP ||
event.keyCode == Keyboard.DOWN ||
event.keyCode == Keyboard.LEFT ||
event.keyCode == Keyboard.RIGHT ||
event.keyCode == Keyboard.PAGE_UP ||
event.keyCode == Keyboard.PAGE_DOWN))
{
// Redispatch the event to the popup
// and let its keyDownHandler() handle it.
_popUp.dispatchEvent(event);
event.stopPropagation();
}
}
/**
* @private
*/
override protected function focusOutHandler(event:FocusEvent):void
{
// Note: event.relatedObject is the object getting focus.
// It can be null in some cases, such as when you open
// the popUp and then click outside the application.
// If the dropdown is open...
if (showingPopUp && _popUp && event.target == this)
{
// If focus is moving outside the popUp...
if (!event.relatedObject)
{
close();
}
else if (event.relatedObject is Menu)
{
// For nested Menu's find parent.
var target:Menu = Menu(event.relatedObject);
while (target.parentMenu)
{
target = target.parentMenu;
}
if (_popUp is DisplayObjectContainer && !DisplayObjectContainer(_popUp).contains(target))
close();
}
else if (_popUp is DisplayObjectContainer && !DisplayObjectContainer(_popUp).contains(event.relatedObject))
{
close();
}
}
super.focusOutHandler(event);
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: Button
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function rollOverHandler(event:MouseEvent):void
{
// Check if the pop-up button was pressed.
if (overArrowButton(event))
{
phase = "arrowOver";
viewSkin();
return;
}
super.rollOverHandler(event);
}
/**
* @private
*/
override protected function rollOutHandler(event:MouseEvent):void
{
// Check if the pop-up button was pressed.
if (overArrowButton(event))
{
phase = "up";
viewSkin();
return;
}
super.rollOutHandler(event);
}
/**
* @private
*/
override protected function mouseDownHandler(event:MouseEvent):void
{
// Check if the pop-up button was pressed.
if (overArrowButton(event))
{
phase = "arrowDown";
viewSkin();
return;
}
super.mouseDownHandler(event);
}
/**
* @private
*/
override protected function mouseUpHandler(event:MouseEvent):void
{
// Check if the pop-up button was pressed.
if (overArrowButton(event))
{
phase = "arrowOver";
viewSkin();
return;
}
super.mouseUpHandler(event);
}
/**
* @private
*/
override protected function clickHandler(event:MouseEvent):void
{
if (overArrowButton(event))
{
if (showingPopUp)
closeWithEvent(event);
else
openWithEvent(event);
event.stopImmediatePropagation();
}
else
{
super.clickHandler(event);
if (openAlways)
{
if (showingPopUp)
closeWithEvent(event);
else
openWithEvent(event);
}
}
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function mouseMoveHandler(event:MouseEvent):void
{
// Check if the pop-up button was pressed.
if (phase == "over" || phase == "arrowOver")
{
phase = overArrowButton(event) ? "arrowOver" : "over";
viewSkin();
}
}
/**
* @private
* Close popUp for IListItemRenderer's like List/Menu.
*/
private function popUpItemClickHandler(event:Event):void
{
if (_closeOnActivity)
close();
}
/**
* @private
* Hide is called intermittently before close gets called.
* Call close() in such cases to reset variables.
*/
private function menuHideHandler(event:MenuEvent):void
{
//we don't want to close the popUp if we're just hiding
//a submenu
if (event.menu != Menu(_popUp).getRootMenu())
{
return;
}
// Menu is hidden through a call to hide()
// Make it visible again so that the menu can
// be tweened and closed.
showingPopUp = true;
_popUp.visible = true;
displayPopUp(false);
}
/**
* @private
*/
private function popMouseDownOutsideHandler(event:Event):void
{
if (event is MouseEvent)
{
// for automated testing, since we're generating this event and
// can only set localX and localY, transpose those coordinates
// and use them for the test point.
var mouseEvent:MouseEvent = MouseEvent(event);
var p:Point = event.target.localToGlobal(new Point(mouseEvent.localX,
mouseEvent.localY));
if (hitTestPoint(p.x, p.y, true))
{
// do nothing
}
else
{
close();
}
}
else if (event is SandboxMouseEvent)
close();
}
/**
* @private
*/
private function removedFromStageHandler(event:Event):void
{
// Ensure we've unregistered ourselves from PopupManager, else
// we'll be leaked.
if (_popUp) {
PopUpManager.removePopUp(_popUp);
showingPopUp = false;
}
}
/**
* @private
*/
private function stage_resizeHandler(event:Event):void
{
// Hide the popUp and don't show tweening when popUp is closed
// due to resizing.
_popUp.visible = false;
close();
}
/**
* @private
*/
mx_internal function get isShowingPopUp():Boolean
{
return showingPopUp;
}
/**
* @private
*/
mx_internal function getArrowButtonsWidth():Number
{
return arrowButtonsWidth;
}
}
}