////////////////////////////////////////////////////////////////////////////////
//
// ADOBE SYSTEMS INCORPORATED
// Copyright 2003-2007 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package mx.controls
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.text.TextFormatAlign;
import flash.text.TextLineMetrics;
import flash.ui.Keyboard;
import flash.utils.Timer;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.EdgeMetrics;
import mx.core.FlexVersion;
import mx.core.IBorder;
import mx.core.IButton;
import mx.core.IDataRenderer;
import mx.core.IFlexAsset;
import mx.core.IFlexDisplayObject;
import mx.core.IFlexModuleFactory;
import mx.core.IFontContextComponent;
import mx.core.IInvalidating;
import mx.core.ILayoutDirectionElement;
import mx.core.IProgrammaticSkin;
import mx.core.IStateClient;
import mx.core.IUIComponent;
import mx.core.IUITextField;
import mx.core.UIComponent;
import mx.core.UITextField;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import mx.events.MoveEvent;
import mx.events.SandboxMouseEvent;
import mx.managers.IFocusManagerComponent;
import mx.styles.ISimpleStyleClient;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched when the user presses the Button control.
* If the autoRepeat
property is true
,
* this event is dispatched repeatedly as long as the button stays down.
*
* @eventType mx.events.FlexEvent.BUTTON_DOWN
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="buttonDown", type="mx.events.FlexEvent")]
/**
* Dispatched when the selected
property
* changes for a toggle Button control. A toggle Button control means that the
* toggle
property is set to true
.
*
* For the RadioButton controls, this event is dispatched when the selected
* property changes.
*
* For the CheckBox controls, this event is dispatched only when the
* user interacts with the control by using the mouse.
*
* @eventType flash.events.Event.CHANGE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="change", type="flash.events.Event")]
/**
* Dispatched when the data
property changes.
*
*
When you use a component as an item renderer,
* the data
property contains the data to display.
* You can listen for this event and update the component
* when the data
property changes.
labelPlacement
property
* is set to left
or right
.
*
* @default 2
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="horizontalGap", type="Number", format="Length", inherit="no")]
/**
* Number of pixels of vertical offset to apply to the label position.
* Positive numbers move the label down.
*
* The default value for the Halo theme is 0
.
* The default value for the Spark theme is 1
.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="labelVerticalOffset", type="Number", format="Length", inherit="no")]
/**
* Number of pixels between the component's bottom border
* and the bottom of its content area.
*
* The default value for the Halo theme is 2
.
* The default value for the Spark theme is 0
.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingBottom", type="Number", format="Length", inherit="no")]
/**
* Number of pixels between the component's top border
* and the top of its content area.
*
* The default value for the Halo theme is 2
.
* The default value for the Spark theme is 0
.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingTop", type="Number", format="Length", inherit="no")]
/**
* Number of milliseconds to wait after the first buttonDown
* event before repeating buttonDown
events at each
* repeatInterval
.
*
* @default 500
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="repeatDelay", type="Number", format="Time", inherit="no")]
/**
* Number of milliseconds between buttonDown
events
* if the user presses and holds the mouse on a button.
*
* @default 35
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="repeatInterval", type="Number", format="Time", inherit="no")]
/**
* Text color of the label as the user moves the mouse pointer over the button.
*
* The default value for the Halo theme is 0x2B333C
.
* The default value for the Spark theme is 0x000000
.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="textRollOverColor", type="uint", format="Color", inherit="yes")]
/**
* Text color of the label as the user presses it.
*
* The default value for the Halo theme is 0x2B333C
.
* The default value for the Spark theme is 0x000000
.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="textSelectedColor", type="uint", format="Color", inherit="yes")]
/**
* Gap between the button's label and icon when the labelPlacement
* property is set to "top"
or "bottom"
.
*
* @default 2
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="verticalGap", type="Number", format="Length", inherit="no")]
//--------------------------------------
// Skins
//--------------------------------------
/**
* Name of the class to use as the default skin for the background and border.
*
* The default value for the Halo theme is mx.skins.halo.ButtonSkin
.
* The default value for the Spark theme is mx.skins.spark.ButtonSkin
.
*
* @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, selectedUp, selectedOver, selectedDown, selectedDisabled")]
/**
* Name of the class to use as the skin for the background and border
* when the button is not selected and the mouse is not over the control.
*
* The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is mx.skins.halo.ButtonSkin
. For the Spark theme, the default skin
* class is mx.skins.spark.ButtonSkin
.
The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is mx.skins.halo.ButtonSkin
. For the Spark theme, the default skin
* class is mx.skins.spark.ButtonSkin
.
The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is mx.skins.halo.ButtonSkin
. For the Spark theme, the default skin
* class is mx.skins.spark.ButtonSkin
.
The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is mx.skins.halo.ButtonSkin
. For the Spark theme, the default skin
* class is mx.skins.spark.ButtonSkin
.
emphasized
(such as when serving as the default
* button for a container).
*
* The default value for the Halo theme is mx.skins.halo.ButtonSkin
.
* The default value for the Spark theme is mx.skins.spark.DefaultButtonSkin
.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="emphasizedSkin", type="Class", inherit="no", states="up, over, down, disabled, selectedUp, selectedOver, selectedDown, selectedDisabled")]
/**
* Name of the class to use as the skin for the background and border
* when a toggle button is selected and the mouse is not over the control.
*
* The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is mx.skins.halo.ButtonSkin
. For the Spark theme, the default skin
* class is mx.skins.spark.ButtonSkin
.
The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is mx.skins.halo.ButtonSkin
. For the Spark theme, the default skin
* class is mx.skins.spark.ButtonSkin
.
The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is mx.skins.halo.ButtonSkin
. For the Spark theme, the default skin
* class is mx.skins.spark.ButtonSkin
.
The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is mx.skins.halo.ButtonSkin
. For the Spark theme, the default skin
* class is mx.skins.spark.ButtonSkin
.
Buttons typically use event listeners to perform an action
* when the user selects the control. When a user clicks the mouse
* on a Button control, and the Button control is enabled,
* it dispatches a click
event and a buttonDown
event.
* A button always dispatches events such as the mouseMove
,
* mouseOver
, mouseOut
, rollOver
,
* rollOut
, mouseDown
, and
* mouseUp
events whether enabled or disabled.
You can customize the look of a Button control * and change its functionality from a push button to a toggle button. * You can change the button appearance by using a skin * for each of the button's states.
* *The label of a Button control uses a bold typeface. If you embed * a font that you want to use for the label of the Button control, you must * embed the bold typeface; for example:
* ** <fx:style> * @font-face { * src:url("../MyFont-Bold.ttf"); * fontFamily: myFont; * fontWeight: bold; * } * .myBoldStyle { * fontFamily: myFont; * fontWeight: bold; * } * </fx:style> * ... * <mx:Button ... styleName="myBoldStyle"/> ** *
The Button control has the following default characteristics:
*Characteristic | Description |
---|---|
Default size | A size large enough to hold the label text, and any icon |
Minimum size | 0 pixels |
Maximum size | No limit |
The <mx:Button>
tag inherits all the tag attributes
* of its superclass, and adds the following tag attributes:
* <mx:Button * Properties * autoRepeat="false|true" * emphasized="false|true" * fontContext="IFontModuleFactory" * label="" * labelPlacement="right|left|bottom|top" * selected="false|true" * selectedField="null" * stickyHighlighting="false|true" * toggle="false|true" * * Styles * borderColor="0xAAB3B3" * color="0x0B333C" * cornerRadius="4" * disabledColor="0xAAB3B3" * disabledIcon="null" * disabledSkin="mx.skins.halo.ButtonSkin" * downIcon="null" * downSkin="mx.skins.halo.ButtonSkin" * fillAlphas="[0.6, 0.4]" * fillColors="[0xE6EEEE, 0xFFFFFF]" * focusAlpha="0.5" * focusRoundedCorners"tl tr bl br" * fontAntiAliasType="advanced" * fontFamily="Verdana" * fontGridFitType="pixel" * fontSharpness="0" * fontSize="10" * fontStyle="normal|italic" * fontThickness="0" * fontWeight="bold|normal" * highlightAlphas="[0.3, 0.0]" * horizontalGap="2" * icon="null" * kerning="false|true" * leading="2" * letterSpacing="0" * overIcon="null" * overSkin="mx.skins.halo.ButtonSkin" * paddingBottom="2" * paddingLeft="0" * paddingRight="0" * paddingTop="2" * repeatDelay="500" * repeatInterval="35" * selectedDisabledIcon="null" * selectedDisabledSkin="mx.skins.halo.ButtonSkin" * selectedDownIcon="null" * selectedDownSkin="mx.skins.halo.ButtonSkin" * selectedOverIcon="null" * selectedOverSkin="mx.skins.halo.ButtonSkin" * selectedUpIcon="null" * selectedUpSkin="mx.skins.halo.ButtonSkin" * skin="mx.skins.halo.ButtonSkin" * textAlign="center|left|right" * textDecoration="none|underline" * textIndent="0" * textRollOverColor="0x2B333C" * textSelectedColor="0x000000" * upIcon="null" * upSkin="mx.skins.halo.ButtonSkin" * verticalGap="2" * * Events * buttonDown="No default" * change="No default" * dataChange="No default" * /> ** * @includeExample examples/ButtonExample.mxml * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class Button extends UIComponent implements IDataRenderer, IDropInListItemRenderer, IFocusManagerComponent, IListItemRenderer, IFontContextComponent, IButton { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class mixins // //-------------------------------------------------------------------------- /** * @private * Placeholder for mixin by ButtonAccImpl. */ mx_internal static var createAccessibilityImplementation:Function; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function Button() { super(); // DisplayObjectContainer properties. // Setting mouseChildren to false ensure that mouse events // are dispatched from the Button itself, // not from its skins, icons, or TextField. // One reason for doing this is that if you press the mouse button // while over the TextField and release the mouse button while over // a skin or icon, we want the player to dispatch a "click" event. // Another is that if mouseChildren were true and someone uses // Sprites rather than Shapes for the skins or icons, // then we we wouldn't get a click because the current skin or icon // changes between the mouseDown and the mouseUp. // (This doesn't happen even when mouseChildren is true if the skins // and icons are Shapes, because Shapes never dispatch mouse events; // they are dispatched from the Button in this case.) mouseChildren = false; // Register for player events. addEventListener(MouseEvent.ROLL_OVER, rollOverHandler); addEventListener(MouseEvent.ROLL_OUT, rollOutHandler); addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); addEventListener(MouseEvent.CLICK, clickHandler); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * Skins for the various states (falseUp, trueOver, etc.) * are created just-in-time as they are needed. * Each skin is a child Sprite of this Button. * Each skin has a name property indicating which skin it is; * for example, the instance of the class specified by the falseUpSkin * style has the name "falseUpSkin" and can be found using * getChildByName(). Note that there is no falseUpSkin property * of Button containing a reference to this skin instance. * This array contains references to all skins that have been created, * for looping over them; without this array we wouldn't know * which of the children are the skins. * New skins are created and added to this array in viewSkin(). */ private var skins:Array /* of Sprite */ = []; /** * @private * A reference to the current skin. * Set by viewSkin(). */ mx_internal var currentSkin:IFlexDisplayObject; /** * The icons array contains references to all icons * that have been created. Since each icon is a child * Sprite of this button, we need this array to keep * track of which children are icons. Each icon has a * name property indicating which icon it is; for example, * the instance of the class specified by the falseUpIcon * style has the name "falseUpIcon" and can be found using * getChildByName(). Note that there is no falseUpIcon property * of Button containing a reference to this icon instance. * New icons are created and added to this array in viewIcon(). * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var icons:Array /* of Sprite */ = []; /** * @private * A reference to the current icon. * Set by viewIcon(). */ mx_internal var currentIcon:IFlexDisplayObject; /** * @private * Timer for doing auto-repeat. */ private var autoRepeatTimer:Timer; /** * @private * Number used to offset the label and/or icon * when button is pressed. */ mx_internal var buttonOffset:Number = 0; /** * @private * used by old measure/layout logic */ mx_internal var centerContent:Boolean = true; /** * @private * used by old measure/layout logic */ mx_internal var extraSpacing:Number = 10 + 10; /** * @private */ mx_internal static var TEXT_WIDTH_PADDING:Number = UITextField.TEXT_WIDTH_PADDING + 1; /** * @private */ private var styleChangedFlag:Boolean = true; /** * @private * The measured width of the first skin loaded. */ private var skinMeasuredWidth:Number; /** * @private * The measured height of the first skin loaded. */ private var skinMeasuredHeight:Number; /** * @private * The value of the unscaledWidth parameter during the most recent * call to updateDisplayList */ private var oldUnscaledWidth:Number; /** * @private * Flags that will block default data/listData behavior */ private var selectedSet:Boolean; private var labelSet:Boolean; /** * @private * Flags used to save information about the skin and icon styles */ mx_internal var checkedDefaultSkin:Boolean = false; mx_internal var defaultSkinUsesStates:Boolean = false; mx_internal var checkedDefaultIcon:Boolean = false; mx_internal var defaultIconUsesStates:Boolean = false; /** * @private * Skin names. * Allows subclasses to re-define the skin property names. */ mx_internal var skinName:String = "skin"; mx_internal var emphasizedSkinName:String = "emphasizedSkin"; mx_internal var upSkinName:String = "upSkin"; mx_internal var overSkinName:String = "overSkin"; mx_internal var downSkinName:String = "downSkin"; mx_internal var disabledSkinName:String = "disabledSkin"; mx_internal var selectedUpSkinName:String = "selectedUpSkin"; mx_internal var selectedOverSkinName:String = "selectedOverSkin"; mx_internal var selectedDownSkinName:String = "selectedDownSkin"; mx_internal var selectedDisabledSkinName:String = "selectedDisabledSkin"; /** * @private * Icon names. * Allows subclasses to re-define the icon property names. */ mx_internal var iconName:String = "icon"; mx_internal var upIconName:String = "upIcon"; mx_internal var overIconName:String = "overIcon"; mx_internal var downIconName:String = "downIcon"; mx_internal var disabledIconName:String = "disabledIcon"; mx_internal var selectedUpIconName:String = "selectedUpIcon"; mx_internal var selectedOverIconName:String = "selectedOverIcon"; mx_internal var selectedDownIconName:String = "selectedDownIcon"; mx_internal var selectedDisabledIconName:String = "selectedDisabledIcon"; //-------------------------------------------------------------------------- // // Overridden properties // //-------------------------------------------------------------------------- //---------------------------------- // baselinePosition //---------------------------------- /** * @private * The baselinePosition of a Button is calculated for its label. */ override public function get baselinePosition():Number { if (!validateBaselinePosition()) return NaN; return textField.y + textField.baselinePosition; } //---------------------------------- // enabled //---------------------------------- /** * @private */ private var enabledChanged:Boolean = false; [Inspectable(category="General", enumeration="true,false", defaultValue="true")] /** * @private * This is called whenever the enabled state changes. */ override public function set enabled(value:Boolean):void { if (super.enabled == value) return; super.enabled = value; enabledChanged = true; invalidateProperties(); invalidateDisplayList(); } //---------------------------------- // textField //---------------------------------- /** * The internal UITextField object that renders the label of this Button. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var textField:IUITextField; //---------------------------------- // toolTip //---------------------------------- /** * @private */ private var toolTipSet:Boolean = false; [Inspectable(category="General", defaultValue="null")] /** * @private */ override public function set toolTip(value:String):void { super.toolTip = value; if (value) { toolTipSet = true; } else { toolTipSet = false; invalidateDisplayList(); } } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // autoRepeat //---------------------------------- /** * @private * Storage for the autoRepeat property. */ private var _autoRepeat:Boolean = false; [Inspectable(defaultValue="false")] /** * Specifies whether to dispatch repeated
buttonDown
* events if the user holds down the mouse button.
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get autoRepeat():Boolean
{
return _autoRepeat;
}
/**
* @private
*/
public function set autoRepeat(value:Boolean):void
{
_autoRepeat = value;
if (value)
{
// Create a Timer object for driving the autorepeat.
// The duration gets set in mouseDownHandler and reset
// in autoRepeatTimer_timerDelayHandler, because
// there is a delay before the first autorepeat
// and then a possibly different interval
// between subsequent ones.
autoRepeatTimer = new Timer(1);
}
else
{
autoRepeatTimer = null;
}
}
//----------------------------------
// data
//----------------------------------
/**
* @private
* Storage for the data property;
*/
private var _data:Object;
[Bindable("dataChange")]
[Inspectable(environment="none")]
/**
* The data
property lets you pass a value
* to the component when you use it as an item renderer or item editor.
* You typically use data binding to bind a field of the data
* property to a property of this component.
*
* When you use the control as a drop-in item renderer or drop-in
* item editor, Flex automatically writes the current value of the item
* to the selected
property of this control.
You do not set this property in MXML.
* * @default null * @see mx.core.IDataRenderer * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get data():Object { return _data; } /** * @private */ public function set data(value:Object):void { var newSelected:*; var newLabel:*; _data = value; if (_listData && _listData is DataGridListData && DataGridListData(_listData).dataField !=null) { newSelected = _data[DataGridListData(_listData).dataField]; newLabel = ""; } else if (_listData) { if (selectedField) newSelected = _data[selectedField]; newLabel = _listData.label; } else { newSelected = _data; } if (newSelected !== undefined && !selectedSet) { selected = newSelected as Boolean; selectedSet = false; } if (newLabel !== undefined && !labelSet) { label = newLabel; labelSet = false; } dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); } //---------------------------------- // emphasized //---------------------------------- /** * @private * Storage for the emphasized property. */ mx_internal var _emphasized:Boolean = false; /** * @private */ private var emphasizedChanged:Boolean = false; [Inspectable(category="General", defaultValue="false")] /** * Draws a thick border around the Button control * when the control is in its up state ifemphasized
* is set to true
.
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get emphasized():Boolean
{
return _emphasized;
}
/**
* @private
*/
public function set emphasized(value:Boolean):void
{
_emphasized = value;
emphasizedChanged = true;
invalidateDisplayList();
}
//----------------------------------
// fontContext
//----------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get fontContext():IFlexModuleFactory
{
return moduleFactory;
}
/**
* @private
*/
public function set fontContext(moduleFactory:IFlexModuleFactory):void
{
this.moduleFactory = moduleFactory;
}
//----------------------------------
// label
//----------------------------------
/**
* @private
* Storage for label property.
*/
private var _label:String = "";
/**
* @private
*/
private var labelChanged:Boolean = false;
[Bindable("labelChanged")]
[Inspectable(category="General", defaultValue="")]
/**
* Text to appear on the Button control.
*
* If the label is wider than the Button control,
* the label is truncated and terminated by an ellipsis (...).
* The full label displays as a tooltip
* when the user moves the mouse over the Button control.
* If you have also set a tooltip by using the tooltip
* property, the tooltip is displayed rather than the label text.
right
, left
,
* bottom
, and top
.
*
* In ActionScript, you can use the following constants
* to set this property:
* ButtonLabelPlacement.RIGHT
,
* ButtonLabelPlacement.LEFT
,
* ButtonLabelPlacement.BOTTOM
, and
* ButtonLabelPlacement.TOP
.
listData
property
* of the component with the appropriate data from the list control.
* The component can then use the listData
property
* to initialize the data
property
* of the drop-in item renderer or drop-in item editor.
*
* You do not set this property in MXML or ActionScript; * Flex sets it when the component is used as a drop-in item renderer * or drop-in item editor.
* * @default null * @see mx.controls.listClasses.IDropInListItemRenderer * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get listData():BaseListData { return _listData; } /** * @private */ public function set listData(value:BaseListData):void { _listData = value; } //---------------------------------- // phase //---------------------------------- /** * @private * Mouse and focus events set this to * ButtonPhase.UP, ButtonPhase.OVER, or ButtonPhase.DOWN. */ private var _phase:String = ButtonPhase.UP; /** * @private */ mx_internal var phaseChanged:Boolean = false; /** * @private */ mx_internal function get phase():String { return _phase; } /** * @private */ mx_internal function set phase(value:String):void { _phase = value; phaseChanged = true; invalidateSize(); invalidateProperties(); invalidateDisplayList(); } //---------------------------------- // selected //---------------------------------- /** * @private * Storage for selected property. */ mx_internal var _selected:Boolean = false; [Bindable("click")] [Bindable("valueCommit")] [Inspectable(category="General", defaultValue="false")] /** * Indicates whether a toggle button is toggled * on (true
) or off (false
).
* This property can be set only if the toggle
property
* is set to true
.
*
* For a CheckBox control, indicates whether the box * is displaying a check mark. For a RadioButton control, * indicates whether the control is selected.
* *The user can change this property by clicking the control, * but you can also set the property programmatically.
* *In previous versions, If the toggle
property
* was set to true
, changing this property also dispatched
* a change
event. Starting in version 3.0, setting this
* property programmatically only dispatches a
* valueCommit
event.
data
property which specifies
* the value of the Button control's selected
property.
* You can set this property when you use the Button control in an item renderer.
* The default value is null, which means that the Button control does
* not set its selected state based on a property in the data
property.
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var selectedField:String = null;
//----------------------------------
// skinLayoutDirection
//----------------------------------
private var skinLayoutDirectionSet:Boolean = false;
private var _skinLayoutDirection:String;
/**
* @private
*/
mx_internal function set skinLayoutDirection(value:String):void
{
skinLayoutDirectionSet = true;
_skinLayoutDirection = value;
}
//----------------------------------
// stickyHighlighting
//----------------------------------
/**
* If false
, the Button displays its down skin
* when the user presses it but changes to its over skin when
* the user drags the mouse off of it.
* If true
, the Button displays its down skin
* when the user presses it, and continues to display this skin
* when the user drags the mouse off of it.
*
* Button subclasses, such as the SliderThumb and ScrollThumb classes
* or the up and down arrows of a ScrollBar, set
* this property to true
.
true
, clicking the button toggles it
* between a selected and an unselected state.
* You can get or set this state programmatically
* by using the selected
property.
*
* If false
, the button does not stay pressed
* after the user releases it.
* In this case, its selected
property
* is always false
.
* Buttons like this are used for performing actions.
*
* When toggle
is set to false
,
* selected
is forced to false
* because only toggle buttons can be selected.
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get toggle():Boolean
{
return _toggle;
}
/**
* @private
*/
public function set toggle(value:Boolean):void
{
_toggle = value;
toggleChanged = true;
invalidateProperties();
invalidateDisplayList();
dispatchEvent(new Event("toggleChanged"));
}
//--------------------------------------------------------------------------
//
// Overridden methods: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function initializeAccessibility():void
{
if (Button.createAccessibilityImplementation != null)
Button.createAccessibilityImplementation(this);
}
/**
* @private
*/
override protected function createChildren():void
{
super.createChildren();
// Create a UITextField to display the label.
if (!textField)
{
textField = IUITextField(createInFontContext(UITextField));
textField.styleName = this;
addChild(DisplayObject(textField));
}
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
// if the font changed and we already created the textfield, we will need to
// destory it so it can be re-created, possibly in a different swf context.
if (hasFontContextChanged() && textField != null)
{
removeChild(DisplayObject(textField));
textField = null;
}
// Create a UITextField to display the label.
if (!textField)
{
textField = IUITextField(createInFontContext(UITextField));
textField.styleName = this;
addChild(DisplayObject(textField));
enabledChanged = true;
toggleChanged = true;
}
if (!initialized)
{
viewSkin();
viewIcon();
}
if (enabledChanged)
{
textField.enabled = enabled;
if (currentIcon && currentIcon is IUIComponent)
IUIComponent(currentIcon).enabled = enabled;
enabledChanged = false;
}
if (toggleChanged)
{
// If the button is no longer toggleable,
// deselect it.
if (!toggle)
selected = false;
toggleChanged = false;
}
if (phaseChanged)
{
// Ensure all potential pseudo-selectors are reevaluated if
// necessary.
var prevState:String = _currentButtonState;
if (prevState != getCurrentButtonState())
stateChanged(prevState, _currentButtonState, false);
phaseChanged = false;
}
}
/**
* @private
*/
override protected function measure():void
{
super.measure();
var textWidth:Number = 0;
var textHeight:Number = 0;
if (label)
{
var lineMetrics:TextLineMetrics = measureText(label);
textWidth = lineMetrics.width + TEXT_WIDTH_PADDING;
textHeight = lineMetrics.height + UITextField.TEXT_HEIGHT_PADDING;
}
var tempCurrentIcon:IFlexDisplayObject = getCurrentIcon();
var iconWidth:Number = tempCurrentIcon ? tempCurrentIcon.width : 0;
var iconHeight:Number = tempCurrentIcon ? tempCurrentIcon.height : 0;
var w:Number = 0;
var h:Number = 0;
if (labelPlacement == ButtonLabelPlacement.LEFT ||
labelPlacement == ButtonLabelPlacement.RIGHT)
{
w = textWidth + iconWidth;
if (textWidth && iconWidth)
w += getStyle("horizontalGap");
h = Math.max(textHeight, iconHeight);
}
else
{
w = Math.max(textWidth, iconWidth);
h = textHeight + iconHeight;
if (textHeight && iconHeight)
h += getStyle("verticalGap");
}
// Add padding. !!!Need a hack here to only add padding if we don't
// have text or icon. This is required to make small buttons (like scroll
// arrows and numeric stepper buttons) look correct.
if (textWidth || iconWidth)
{
w += getStyle("paddingLeft") + getStyle("paddingRight");
h += getStyle("paddingTop") + getStyle("paddingBottom");
}
var bm:EdgeMetrics = currentSkin &&
currentSkin is IBorder && !(currentSkin is IFlexAsset) ?
IBorder(currentSkin).borderMetrics :
null;
if (bm)
{
w += bm.left + bm.right;
h += bm.top + bm.bottom
}
// Use the larger of the measured sizes and the skin's preferred sizes.
// Each skin should override measure() with their measuredWidth
// and measuredHeight.
if (currentSkin && (isNaN(skinMeasuredWidth) || isNaN(skinMeasuredHeight)))
{
skinMeasuredWidth = currentSkin.measuredWidth;
skinMeasuredHeight = currentSkin.measuredHeight;
}
if (!isNaN(skinMeasuredWidth))
w = Math.max(skinMeasuredWidth, w);
if (!isNaN(skinMeasuredHeight))
h = Math.max(skinMeasuredHeight, h);
measuredMinWidth = measuredWidth = w;
measuredMinHeight = measuredHeight = h;
// trace("measure: Button width = " + w + " height = " + h);
}
/**
* @private
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (emphasizedChanged)
{
changeSkins();
emphasizedChanged = false;
}
// Set each skin's size to the layout size of this Button.
var n:int = skins.length;
for (var i:int = 0; i < n; i++)
{
var skin:IFlexDisplayObject = IFlexDisplayObject(skins[i]);
skin.setActualSize(unscaledWidth, unscaledHeight);
}
// Show the appropriate skin and icon, 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 phase).
viewSkin();
viewIcon();
/* if (currentIcon && currentIcon is IUIComponent)
IUIComponent(currentIcon).enabled = enabled; */
layoutContents(unscaledWidth, unscaledHeight,
phase == ButtonPhase.DOWN);
// If our width changed, reset the label text to get it to fit.
if (oldUnscaledWidth > unscaledWidth ||
textField.text != label ||
labelChanged ||
styleChangedFlag)
{
textField.text = label;
var truncated:Boolean = textField.truncateToFit();
if (!toolTipSet)
{
if (truncated)
super.toolTip = label;
else
super.toolTip = null;
}
styleChangedFlag = false;
labelChanged = false;
}
oldUnscaledWidth = unscaledWidth;
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
styleChangedFlag = true;
super.styleChanged(styleProp);
// Check for skin/icon changes here.
// We could only throw out any skins that change,
// but since dynamic re-skinning is uncommon, we'll take
// the simpler approach of throwing out all skins.
if (!styleProp || styleProp == "styleName")
{
// All style props have changed, so dump skins and icons.
changeSkins();
changeIcons();
if (initialized)
{
viewSkin();
viewIcon();
}
}
else if (styleProp.toLowerCase().indexOf("skin") != -1)
{
changeSkins();
}
else if (styleProp.toLowerCase().indexOf("icon") != -1)
{
changeIcons();
invalidateSize();
}
}
/**
* @private
*/
override protected function adjustFocusRect(
object:DisplayObject = null):void
{
// If we don't have a skin, show focus around the icon.
super.adjustFocusRect(!currentSkin ? DisplayObject(currentIcon) : this);
}
/**
* @private
* The state to be used when matching CSS pseudo-selectors. This override
* returns the current button state.
*/
override protected function get currentCSSState():String
{
return getCurrentButtonState();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Displays one of the eight possible skins,
* creating it if it doesn't already exist.
*/
mx_internal function viewSkin():void
{
// Determine which skin to display, 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).
var tempSkinName:String;
if (!enabled)
tempSkinName = selected ? selectedDisabledSkinName : disabledSkinName;
else if (phase == ButtonPhase.UP)
tempSkinName = selected ? selectedUpSkinName : upSkinName;
else if (phase == ButtonPhase.OVER)
tempSkinName = selected ? selectedOverSkinName : overSkinName;
else if (phase == ButtonPhase.DOWN)
tempSkinName = selected ? selectedDownSkinName : downSkinName;
viewSkinForPhase(tempSkinName, getCurrentButtonState());
}
/**
* @private
* Displays one of the several possible skins,
* depending on the skinName and creating
* it if it doesn't already exist.
*/
mx_internal function viewSkinForPhase(tempSkinName:String, stateName:String):void
{
var newSkinClass:Class = Class(getStyle(tempSkinName));
var newSkin:IFlexDisplayObject;
if (!newSkinClass)
{
// Try the default skin
newSkinClass = _emphasized ? Class(getStyle(emphasizedSkinName)) : Class(getStyle(skinName));
newSkinClass = !newSkinClass && _emphasized ? Class(getStyle(skinName)) : newSkinClass;
// If we are using the default skin, then
if (defaultSkinUsesStates)
tempSkinName = skinName;
if (!checkedDefaultSkin && newSkinClass)
{
newSkin = IFlexDisplayObject(new newSkinClass());
// Check if the skin class is a state client or a programmatic skin
if (!(newSkin is IProgrammaticSkin) && newSkin is IStateClient)
{
defaultSkinUsesStates = true;
tempSkinName = skinName;
}
if (newSkin)
{
checkedDefaultSkin = true;
if (newSkin is ILayoutDirectionElement && skinLayoutDirectionSet)
ILayoutDirectionElement(newSkin).layoutDirection = _skinLayoutDirection;
}
}
}
// Has this skin already been created?
newSkin = IFlexDisplayObject(getChildByName(tempSkinName));
// If not, create it.
if (!newSkin)
{
if (newSkinClass)
{
newSkin = IFlexDisplayObject(new newSkinClass());
// Set its name so that we can find it in the future
// using getChildByName().
newSkin.name = tempSkinName;
// Make the getStyle() calls in ButtonSkin find the styles
// for this Button.
var styleableSkin:ISimpleStyleClient = newSkin as ISimpleStyleClient;
if (styleableSkin)
styleableSkin.styleName = this;
if (newSkin is ILayoutDirectionElement && skinLayoutDirectionSet)
ILayoutDirectionElement(newSkin).layoutDirection = _skinLayoutDirection;
addChild(DisplayObject(newSkin));
// Make the skin the proper size for this Button.
// This will cause to skin to be drawn by drawHaloRect()
// in ButtonSkin.
newSkin.setActualSize(unscaledWidth, unscaledHeight);
// If the skin is programmatic, and we've already been
// initialized, update it now to avoid flicker.
if (newSkin is IInvalidating && initialized)
{
IInvalidating(newSkin).validateNow();
}
else if (newSkin is IProgrammaticSkin && initialized)
{
IProgrammaticSkin(newSkin).validateDisplayList()
}
// Keep track of all skin children that have been created.
skins.push(newSkin);
}
}
// Hide the old skin.
if (currentSkin)
currentSkin.visible = false;
// Keep track of which skin is current.
currentSkin = newSkin;
// Update the state of the skin if it accepts states and it implements the IStateClient interface.
if (defaultSkinUsesStates && currentSkin is IStateClient)
{
IStateClient(currentSkin).currentState = stateName;
if (currentSkin is IInvalidating)
IInvalidating(currentSkin).validateNow();
}
// Show the new skin.
if (currentSkin)
currentSkin.visible = true;
var labelColor:Number;
if (enabled)
{
if (phase == ButtonPhase.OVER)
labelColor = textField.getStyle("textRollOverColor");
else if (phase == ButtonPhase.DOWN)
labelColor = textField.getStyle("textSelectedColor");
else
labelColor = textField.getStyle("color");
textField.setColor(labelColor);
}
}
/**
* @private
* Gets the currentIconName (string) based on the Button's phase.
*/
mx_internal function getCurrentIconName():String
{
var tempIconName:String;
if (!enabled)
{
tempIconName = selected ?
selectedDisabledIconName :
disabledIconName;
}
else if (phase == ButtonPhase.UP)
{
tempIconName = selected ? selectedUpIconName : upIconName;
}
else if (phase == ButtonPhase.OVER)
{
tempIconName = selected ? selectedOverIconName : overIconName;
}
else if (phase == ButtonPhase.DOWN)
{
tempIconName = selected ? selectedDownIconName : downIconName;
}
return tempIconName;
}
/**
* @private
* gets the currentIcon based on the button.phase
*/
mx_internal function getCurrentIcon():IFlexDisplayObject
{
// Determine which icon will get displayed, 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).
var tempIconName:String = getCurrentIconName();
if (!tempIconName)
return null;
return viewIconForPhase(tempIconName);
}
/**
* @private
* Displays one of the eight possible icons,
* creating it if it doesn't already exist.
*/
mx_internal function viewIcon():void
{
// Determine which icon to display, 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).
var tempIconName:String = getCurrentIconName();
viewIconForPhase(tempIconName);
}
/**
* @private
* Displays one of the several possible icons,
* depending on the iconName and creating it if it
* doesn't already exist.
*/
mx_internal function viewIconForPhase(tempIconName:String):IFlexDisplayObject
{
var newIconClass:Class = Class(getStyle(tempIconName));
var newIcon:IFlexDisplayObject;
if (!newIconClass)
{
newIconClass = Class(getStyle(iconName));
// If we are using the default icon, then set use the default icon name
if (defaultIconUsesStates)
tempIconName = iconName;
if (!checkedDefaultIcon && newIconClass)
{
newIcon = IFlexDisplayObject(new newIconClass());
// Check if the icon class is a state client or a programmatic skin
if (!(newIcon is IProgrammaticSkin) && newIcon is IStateClient)
{
defaultIconUsesStates = true;
tempIconName = iconName;
}
if (newIcon)
checkedDefaultIcon = true;
}
}
// Has this icon already been created?
newIcon = IFlexDisplayObject(getChildByName(tempIconName));
// If not, create it.
if (newIcon == null)
{
if (newIconClass != null)
{
newIcon = IFlexDisplayObject(new newIconClass());
// Set its name so that we can find it in the future
// using getChildByName().
newIcon.name = tempIconName;
if (newIcon is ISimpleStyleClient)
ISimpleStyleClient(newIcon).styleName = this;
addChild(DisplayObject(newIcon));
// If the skin is programmatic, and we've already been
// initialized, update it now to avoid flicker.
var sizeIcon:Boolean = false;
if (newIcon is IInvalidating)
{
IInvalidating(newIcon).validateNow();
sizeIcon = true;
}
else if (newIcon is IProgrammaticSkin)
{
IProgrammaticSkin(newIcon).validateDisplayList();
sizeIcon = true;
}
if (newIcon && newIcon is IUIComponent)
IUIComponent(newIcon).enabled = enabled;
if (sizeIcon)
newIcon.setActualSize(newIcon.measuredWidth, newIcon.measuredHeight);
// Keep track of all icon children that have been created.
icons.push(newIcon);
}
}
// Hide the old icon.
if (currentIcon != null)
currentIcon.visible = false;
// Keep track of which icon is current.
currentIcon = newIcon;
if (defaultIconUsesStates && currentIcon is IStateClient)
{
IStateClient(currentIcon).currentState = getCurrentButtonState();
if (currentIcon is IInvalidating)
IInvalidating(currentIcon).validateNow();
}
// Show the new icon.
if (currentIcon != null)
currentIcon.visible = true;
return newIcon;
}
/**
* @private
* Storage for the most recent button state.
*/
protected var _currentButtonState:String;
/**
* @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).
*/
mx_internal function getCurrentButtonState():String
{
_currentButtonState = "";
if (!enabled)
_currentButtonState = selected ? "selectedDisabled" : "disabled";
else if (phase == ButtonPhase.UP)
_currentButtonState = selected ? "selectedUp" : "up";
else if (phase == ButtonPhase.OVER)
_currentButtonState = selected ? "selectedOver" : "over";
else if (phase == ButtonPhase.DOWN)
_currentButtonState = selected ? "selectedDown" : "down";
return _currentButtonState;
}
/**
* @private
* Controls the layout of the icon and the label within the button.
* The text/icon are aligned based on the textAlign style setting.
*/
mx_internal function layoutContents(unscaledWidth:Number,
unscaledHeight:Number,
offset:Boolean):void
{
var labelWidth:Number = 0;
var labelHeight:Number = 0;
var labelX:Number = 0;
var labelY:Number = 0;
var iconWidth:Number = 0;
var iconHeight:Number = 0;
var iconX:Number = 0;
var iconY:Number = 0;
var horizontalGap:Number = 0;
var verticalGap:Number = 0;
var paddingLeft:Number = getStyle("paddingLeft");
var paddingRight:Number = getStyle("paddingRight");
var paddingTop:Number = getStyle("paddingTop");
var paddingBottom:Number = getStyle("paddingBottom");
var textWidth:Number = 0;
var textHeight:Number = 0;
var lineMetrics:TextLineMetrics;
if (label)
{
lineMetrics = measureText(label);
textWidth = lineMetrics.width + TEXT_WIDTH_PADDING;
textHeight = lineMetrics.height + UITextField.TEXT_HEIGHT_PADDING;
}
else
{
lineMetrics = measureText("Wj");
textHeight = lineMetrics.height + UITextField.TEXT_HEIGHT_PADDING;
}
var n:Number = offset ? buttonOffset : 0;
var textAlign:String = getStyle("textAlign");
// Map new Spark values that might be set in a selector
// affecting both Halo and Spark components.
if (textAlign == "start")
textAlign = TextFormatAlign.LEFT;
else if (textAlign == "end")
textAlign = TextFormatAlign.RIGHT;
var viewWidth:Number = unscaledWidth;
var viewHeight:Number = unscaledHeight;
var bm:EdgeMetrics = currentSkin &&
currentSkin is IBorder && !(currentSkin is IFlexAsset) ?
IBorder(currentSkin).borderMetrics :
null;
if (bm)
{
viewWidth -= bm.left + bm.right;
viewHeight -= bm.top + bm.bottom;
}
if (currentIcon)
{
iconWidth = currentIcon.width;
iconHeight = currentIcon.height;
}
if (labelPlacement == ButtonLabelPlacement.LEFT ||
labelPlacement == ButtonLabelPlacement.RIGHT)
{
horizontalGap = getStyle("horizontalGap");
if (iconWidth == 0 || textWidth == 0)
horizontalGap = 0;
if (textWidth > 0)
{
textField.width = labelWidth =
Math.max(Math.min(viewWidth - iconWidth - horizontalGap -
paddingLeft - paddingRight, textWidth), 0);
}
else
{
textField.width = labelWidth = 0;
}
textField.height = labelHeight = Math.min(viewHeight, textHeight);
if (textAlign == "left")
{
labelX += paddingLeft;
}
else if (textAlign == "right")
{
labelX += (viewWidth - labelWidth - iconWidth -
horizontalGap - paddingRight);
}
else // "center" -- default value
{
labelX += ((viewWidth - labelWidth - iconWidth -
horizontalGap - paddingLeft - paddingRight) / 2) + paddingLeft;
}
if (labelPlacement == ButtonLabelPlacement.RIGHT)
{
labelX += iconWidth + horizontalGap;
iconX = labelX - (iconWidth + horizontalGap);
}
else
{
iconX = labelX + labelWidth + horizontalGap;
}
iconY = ((viewHeight - iconHeight - paddingTop - paddingBottom) / 2) + paddingTop;
labelY = ((viewHeight - labelHeight - paddingTop - paddingBottom) / 2) + paddingTop;
}
else
{
verticalGap = getStyle("verticalGap");
if (iconHeight == 0 || label == "")
verticalGap = 0;
if (textWidth > 0)
{
textField.width = labelWidth = Math.max(viewWidth - paddingLeft - paddingRight, 0);
textField.height = labelHeight =
Math.min(viewHeight - iconHeight - paddingTop - paddingBottom - verticalGap, textHeight);
}
else
{
textField.width = labelWidth = 0;
textField.height = labelHeight = 0;
}
labelX = paddingLeft;
if (textAlign == "left")
{
iconX += paddingLeft;
}
else if (textAlign == "right")
{
iconX += Math.max(viewWidth - iconWidth - paddingRight, paddingLeft);
}
else
{
iconX += ((viewWidth - iconWidth - paddingLeft - paddingRight) / 2) + paddingLeft;
}
if (labelPlacement == ButtonLabelPlacement.TOP)
{
labelY += ((viewHeight - labelHeight - iconHeight -
paddingTop - paddingBottom - verticalGap) / 2) + paddingTop;
iconY += labelY + labelHeight + verticalGap;
}
else
{
iconY += ((viewHeight - labelHeight - iconHeight -
paddingTop - paddingBottom - verticalGap) / 2) + paddingTop;
labelY += iconY + iconHeight + verticalGap;
}
}
var buffX:Number = n;
var buffY:Number = n;
if (bm)
{
buffX += bm.left;
buffY += bm.top;
}
if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0)
labelY += getStyle("labelVerticalOffset");
textField.x = Math.round(labelX + buffX);
textField.y = Math.round(labelY + buffY);
if (currentIcon)
{
iconX += buffX;
iconY += buffY;
// dispatch a move on behalf of the icon
// the focus system uses that to adjust
// focus rectangles
var moveEvent:MoveEvent = new MoveEvent(MoveEvent.MOVE);
moveEvent.oldX = currentIcon.x;
moveEvent.oldY = currentIcon.y;
currentIcon.x = Math.round(iconX);
currentIcon.y = Math.round(iconY);
currentIcon.dispatchEvent(moveEvent);
}
// The skins and icons get created on demand as the user interacts
// with the Button, and as they are created they become the
// frontmost child.
// Here we ensure that the textField is the frontmost child,
// with the current icon behind it and the current skin behind that.
// Any other skins and icons are left behind these three,
// with arbitrary layering.
if (currentSkin)
setChildIndex(DisplayObject(currentSkin), numChildren - 1);
if (currentIcon)
setChildIndex(DisplayObject(currentIcon), numChildren - 1);
if (textField)
setChildIndex(DisplayObject(textField), numChildren - 1);
}
/**
* @private
*/
mx_internal function changeSkins():void
{
var n:int = skins.length;
for (var i:int = 0; i < n; i++)
{
removeChild(skins[i]);
}
skins = [];
skinMeasuredWidth = NaN;
skinMeasuredHeight = NaN;
checkedDefaultSkin = false;
defaultSkinUsesStates = false;
if (initialized)
{
viewSkin();
invalidateSize();
}
}
/**
* @private
*/
mx_internal function changeIcons():void
{
var n:int = icons.length;
for (var i:int = 0; i < n; i++)
{
removeChild(icons[i]);
}
icons = [];
checkedDefaultIcon = false;
defaultIconUsesStates = false;
}
/**
* @private
*/
mx_internal function buttonPressed():void
{
phase = ButtonPhase.DOWN;
dispatchEvent(new FlexEvent(FlexEvent.BUTTON_DOWN));
if (autoRepeat)
{
autoRepeatTimer.delay = getStyle("repeatDelay");
autoRepeatTimer.addEventListener(
TimerEvent.TIMER, autoRepeatTimer_timerDelayHandler);
autoRepeatTimer.start();
}
}
/**
* @private
*/
mx_internal function buttonReleased():void
{
// Remove the handlers that were added in mouseDownHandler().
systemManager.getSandboxRoot().removeEventListener(
MouseEvent.MOUSE_UP, systemManager_mouseUpHandler, true);
systemManager.getSandboxRoot().removeEventListener(
SandboxMouseEvent.MOUSE_UP_SOMEWHERE, stage_mouseLeaveHandler);
if (autoRepeatTimer)
{
autoRepeatTimer.removeEventListener(
TimerEvent.TIMER, autoRepeatTimer_timerDelayHandler);
autoRepeatTimer.removeEventListener(
TimerEvent.TIMER, autoRepeatTimer_timerHandler);
autoRepeatTimer.reset();
}
}
/**
* @private
* Some other components which use a Button as an internal
* subcomponent need access to its UITextField, but can't access the
* textField var because it is protected and therefore available
* only to subclasses.
*/
mx_internal function getTextField():IUITextField
{
return textField;
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function focusOutHandler(event:FocusEvent):void
{
super.focusOutHandler(event);
// Most of the time the system sends a rollout, but there are
// situations where the mouse is over something else
// that you don't get one so we force one here.
if (phase != ButtonPhase.UP)
phase = ButtonPhase.UP;
}
/**
* @private
*/
override protected function keyDownHandler(event:KeyboardEvent):void
{
if (!enabled)
return;
if (event.keyCode == Keyboard.SPACE)
buttonPressed();
}
/**
* @private
*/
override protected function keyUpHandler(event:KeyboardEvent):void
{
if (!enabled)
return;
if (event.keyCode == Keyboard.SPACE)
{
buttonReleased();
if (phase == ButtonPhase.DOWN)
dispatchEvent(new MouseEvent(MouseEvent.CLICK));
phase = ButtonPhase.UP;
}
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/*
Mouse interaction sequences that Button must handle:
All start with Button in "up" phase, mouse outside Button,
and mouse button up.
Normal click:
roll over Button -> "over" phase
mouse down on Button -> "down" phase, dispatch "buttonDown"
mouse up while over Button -> "over" phase, dispatch "click"
roll out of Button -> "up" phase
Click canceled:
roll over Button -> "over" phase
mouse down on Button -> "down" phase, dispatch "buttonDown"
roll out of Button -> "over" phase
maybe roll over and out of other objects -> dispatch events from them
maybe roll off the stage, or off and back on
mouse up while out of Button -> "up" phase
if mouseup was over another Button, it goes into "over" phase
Click resumed:
roll over Button -> "over" phase
mouse down on Button -> "down" phase, dispatch "buttonDown"
roll out of Button -> "over" phase
maybe roll over and out of other objects -> dispatch events from them
roll over Button -> "down" phase
maybe roll off the stage, or off and back on
maybe repeat last four steps
mouse up while over Button -> "over" phase, dispatch "click"
roll out of Button -> "up" phase
Drag over and out
mouse down while out of Button
roll over Button -> stay in "up" phase
roll out of Button -> stay in "up" phase
Drag over and up
mouse down while out of Button
roll over Button -> stay in "up" phase
mouse up while over Button -> "over" phase
continue with step 2 of first three sequences above
*/
/**
* The default handler for the MouseEvent.ROLL_OVER
event.
*
* @param The event object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function rollOverHandler(event:MouseEvent):void
{
// Note that we don't prevent the propagation of rollOver
// from a disabled Button.
// Developers may want to detect this low-level event.
if (phase == ButtonPhase.UP)
{
if (event.buttonDown)
return;
phase = ButtonPhase.OVER;
// Force a "render" event, which will cause updateDisplayList()
// to show the appropriate skin for the new phase.
event.updateAfterEvent();
}
else if (phase == ButtonPhase.OVER)
{
phase = ButtonPhase.DOWN;
// Force a "render" event, which will cause updateDisplayList()
// to show the appropriate skin for the new phase.
event.updateAfterEvent();
// The mouse is back over the Button and the Button is down again,
// so resume auto-repeating.
if (autoRepeatTimer)
autoRepeatTimer.start();
}
}
/**
* The default handler for the MouseEvent.ROLL_OUT
event.
*
* @param The event object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function rollOutHandler(event:MouseEvent):void
{
// Note that we don't prevent the propagation of rollOut
// from a disabled Button.
// Developers may want to detect this low-level event.
if (phase == ButtonPhase.OVER)
{
phase = ButtonPhase.UP;
// Force a "render" event, which will cause updateDisplayList()
// to show the appropriate skin for the new phase.
event.updateAfterEvent();
}
else if (phase == ButtonPhase.DOWN && !stickyHighlighting)
{
phase = ButtonPhase.OVER;
// Force a "render" event, which will cause updateDisplayList()
// to show the appropriate skin for the new phase.
event.updateAfterEvent();
// If the Button no longer looks "down", it shouldn't auto-repeat.
if (autoRepeatTimer)
autoRepeatTimer.stop();
}
}
/**
* The default handler for the MouseEvent.MOUSE_DOWN
event.
*
* @param The event object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function mouseDownHandler(event:MouseEvent):void
{
if (!enabled)
return;
// Note that we don't prevent the propagation of mouseDown
// from a disabled Button.
// Developers may want to detect this low-level event.
// In case the user drags out of the Button and then releases
// the mouse button, we need to get the mouseUp.
// To accomplish this, we temporarily place a capture-phase
// mouseUp handler on the SystemManager.
// We also place a mouseLeave handler on the stage
// in case the user drags off the stage and releases the mouse.
// These handlers are removed in buttonReleased().
systemManager.getSandboxRoot().addEventListener(
MouseEvent.MOUSE_UP, systemManager_mouseUpHandler, true);
systemManager.getSandboxRoot().addEventListener(
SandboxMouseEvent.MOUSE_UP_SOMEWHERE, stage_mouseLeaveHandler);
buttonPressed();
// Force a "render" event, which will cause updateDisplayList()
// to show the appropriate skin for the new phase.
event.updateAfterEvent();
}
/**
* The default handler for the MouseEvent.MOUSE_UP
event.
*
* @param The event object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function mouseUpHandler(event:MouseEvent):void
{
if (!enabled)
return;
// Note that we don't prevent the propagation of mouseUp
// from a disabled Button.
// Developers may want to detect this low-level event.
phase = ButtonPhase.OVER;
buttonReleased();
// Force a "render" event, which will cause updateDisplayList()
// to show the appropriate skin for the new phase.
if (!toggle)
event.updateAfterEvent();
}
/**
* The default handler for the MouseEvent.CLICK
event.
*
* @param The event object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function clickHandler(event:MouseEvent):void
{
if (!enabled)
{
// Prevent the propagation of click from a disabled Button.
// This is conceptually a higher-level event and
// developers will expect their click handlers not to fire
// if the Button is disabled.
event.stopImmediatePropagation();
return;
}
if (toggle)
{
setSelected(!selected);
event.updateAfterEvent();
}
}
/**
* @private
* This method is called when the user has pressed the Button
* and then released the mouse button anywhere.
* It's purpose is to get the mouseUp event when the user has
* dragged out of the Button before releasing.
* However, it gets an inside mouseUp as well;
* we have to check for this case becuase mouseHandler()
* already deals with it..
*/
private function systemManager_mouseUpHandler(event:MouseEvent):void
{
// If the mouse button was released over the Button,
// mouseUpHandler() will handle it, so do nothing.
if (contains(DisplayObject(event.target)))
return;
phase = ButtonPhase.UP;
buttonReleased();
// Force a "render" event, which will cause updateDisplayList()
// to show the appropriate skin for the new phase.
event.updateAfterEvent();
}
/**
* @private
* This method is called when the user has pressed the Button,
* dragged of the stage, and released the mouse button.
*/
private function stage_mouseLeaveHandler(event:Event):void
{
phase = ButtonPhase.UP;
buttonReleased();
}
/**
* @private
*/
private function autoRepeatTimer_timerDelayHandler(event:Event):void
{
if (!enabled)
return;
dispatchEvent(new FlexEvent(FlexEvent.BUTTON_DOWN));
if (autoRepeat)
{
autoRepeatTimer.reset();
autoRepeatTimer.removeEventListener(
TimerEvent.TIMER, autoRepeatTimer_timerDelayHandler);
autoRepeatTimer.delay = getStyle("repeatInterval");
autoRepeatTimer.addEventListener(
TimerEvent.TIMER, autoRepeatTimer_timerHandler);
autoRepeatTimer.start();
}
}
/**
* @private
*/
private function autoRepeatTimer_timerHandler(event:Event):void
{
if (!enabled)
return;
dispatchEvent(new FlexEvent(FlexEvent.BUTTON_DOWN));
}
}
}