//////////////////////////////////////////////////////////////////////////////// // // 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.

* * @eventType mx.events.FlexEvent.DATA_CHANGE * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="dataChange", type="mx.events.FlexEvent")] //-------------------------------------- // Styles //-------------------------------------- include "../styles/metadata/FocusStyles.as" include "../styles/metadata/LeadingStyle.as" include "../styles/metadata/PaddingStyles.as" include "../styles/metadata/SkinStyles.as" include "../styles/metadata/TextStyles.as" /** * Color applied to the button when the emphasized flag is true. * * @default #0099FF * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ [Style(name="accentColor", type="uint", format="Color", inherit="yes", theme="spark")] /** * Color of focus ring when the component is in focus * * @default 0x70B2EE * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ [Style(name="focusColor", type="uint", format="Color", inherit="yes", theme="spark")] /** * Gap between the label and icon, when the 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.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="upSkin", type="Class", inherit="no")] /** * Name of the class to use as the skin for the background and border * when the button is not selected and the mouse is 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.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="overSkin", type="Class", inherit="no")] /** * Name of the class to use as the skin for the background and border * when the button is not selected and the mouse button is down. * *

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.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="downSkin", type="Class", inherit="no")] /** * Name of the class to use as the skin for the background and border * when the button is not selected and is disabled. * *

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.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="disabledSkin", type="Class", inherit="no")] /** * Name of the class to use as the skin for the button when the button * is 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.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectedUpSkin", type="Class", inherit="no")] /** * Name of the class to use as the skin for the background and border * when a toggle button is selected and the mouse is 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.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectedOverSkin", type="Class", inherit="no")] /** * Name of the class to use as the skin for the background and border * when a toggle button is selected and the mouse button is down. * *

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.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectedDownSkin", type="Class", inherit="no")] /** * Name of the class to use as the skin for the background and border * when a toggle button is selected and disabled. * *

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.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectedDisabledSkin", type="Class", inherit="no")] /** * Name of the class to use as the default icon. * Setting any other icon style overrides this setting. * * @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, selectedUp, selectedOver, selectedDown, selectedDisabled")] /** * Name of the class to use as the icon when a toggle button is not * selected and the mouse is not over the button. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="upIcon", type="Class", inherit="no")] /** * Name of the class to use as the icon when the button is not * selected and the mouse is over the control. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="overIcon", type="Class", inherit="no")] /** * Name of the class to use as the icon when the button is not * selected and the mouse button is down. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="downIcon", type="Class", inherit="no")] /** * Name of the class to use as the icon * when the button is disabled and not selected. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="disabledIcon", type="Class", inherit="no")] /** * Name of the class to use as the icon * when the button is selected and the mouse button is up. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectedUpIcon", type="Class", inherit="no")] /** * Name of the class to use as the icon * when the button is selected and the mouse is over the control. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectedOverIcon", type="Class", inherit="no")] /** * Name of the class to use as the icon * when the button is selected and the mouse button is down. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectedDownIcon", type="Class", inherit="no")] /** * Name of the class to use as the icon * when the button is selected and disabled. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectedDisabledIcon", type="Class", inherit="no")] //-------------------------------------- // Other metadata //-------------------------------------- [AccessibilityClass(implementation="mx.accessibility.ButtonAccImpl")] [DefaultBindingProperty(source="selected", destination="label")] [DefaultTriggerEvent("click")] [IconFile("Button.png")] [Alternative(replacement="spark.components.Button", since="4.0")] /** * The Button control is a commonly used rectangular button. * Button controls look like they can be pressed. * They can have a text label, an icon, or both on their face. * *

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:

* * * * * *
CharacteristicDescription
Default sizeA size large enough to hold the label text, and any icon
Minimum size0 pixels
Maximum sizeNo limit
* * @mxml * *

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 if emphasized * 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.

* * @default "" * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get label():String { return _label; } /** * @private */ public function set label(value:String):void { labelSet = true; if (_label != value) { _label = value; labelChanged = true; invalidateSize(); invalidateDisplayList(); dispatchEvent(new Event("labelChanged")); } } //---------------------------------- // labelPlacement //---------------------------------- /** * @private * Storage for labelPlacement property. */ mx_internal var _labelPlacement:String = ButtonLabelPlacement.RIGHT; [Bindable("labelPlacementChanged")] [Inspectable(category="General", enumeration="left,right,top,bottom", defaultValue="right")] /** * Orientation of the label in relation to a specified icon. * Valid MXML values are 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.

* * @default ButtonLabelPlacement.RIGHT * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get labelPlacement():String { return _labelPlacement; } /** * @private */ public function set labelPlacement(value:String):void { _labelPlacement = value; invalidateSize(); invalidateDisplayList(); dispatchEvent(new Event("labelPlacementChanged")); } //----------------------------------- // listData //----------------------------------- /** * @private * Storage for the listData property. */ private var _listData:BaseListData; [Bindable("dataChange")] [Inspectable(environment="none")] /** * When a component is used as a drop-in item renderer or drop-in * item editor, Flex initializes the 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.

* * @default false * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get selected():Boolean { return _selected; } /** * @private */ public function set selected(value:Boolean):void { selectedSet = true; setSelected(value, true); } mx_internal function setSelected(value:Boolean, isProgrammatic:Boolean = false):void { if (_selected != value) { _selected = value; invalidateDisplayList(); if (toggle && !isProgrammatic) dispatchEvent(new Event(Event.CHANGE)); dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT)); } } //---------------------------------- // selectedField //---------------------------------- /** * The name of the field in the 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.

* * @default false * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var stickyHighlighting:Boolean = false; //---------------------------------- // toggle //---------------------------------- /** * @private * Storage for toggle property. */ mx_internal var _toggle:Boolean = false; /** * @private */ mx_internal var toggleChanged:Boolean = false; [Bindable("toggleChanged")] [Inspectable(category="General", defaultValue="false")] /** * Controls whether a Button is in a toggle state or not. * * If 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)); } } }