All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scaffold.libs_as.feathers.core.FeathersControl.as Maven / Gradle / Ivy

/*
Feathers
Copyright 2012-2015 Bowler Hat LLC. All Rights Reserved.

This program is free software. You can redistribute and/or modify it in
accordance with the terms of the accompanying license agreement.
*/
package feathers.core
{
	import feathers.controls.text.BitmapFontTextRenderer;
	import feathers.controls.text.StageTextTextEditor;
	import feathers.events.FeathersEventType;
	import feathers.layout.ILayoutData;
	import feathers.layout.ILayoutDisplayObject;
	import feathers.skins.IStyleProvider;
	import feathers.utils.display.getDisplayObjectDepthFromStage;

	import flash.errors.IllegalOperationError;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;

	import starling.core.Starling;
	import starling.display.DisplayObject;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.utils.MatrixUtil;

	/**
	 * Dispatched after initialize() has been called, but before
	 * the first time that draw() has been called.
	 *
	 * 

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @eventType feathers.events.FeathersEventType.INITIALIZE */ [Event(name="initialize",type="starling.events.Event")] /** * Dispatched after the component has validated for the first time. Both * initialize() and draw() will have been called, * and all children will have been created. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @eventType feathers.events.FeathersEventType.CREATION_COMPLETE */ [Event(name="creationComplete",type="starling.events.Event")] /** * Dispatched when the width or height of the control changes. * *

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @eventType feathers.events.FeathersEventType.RESIZE */ [Event(name="resize",type="starling.events.Event")] /** * Base class for all UI controls. Implements invalidation and sets up some * basic template functions like initialize() and * draw(). * *

This is a base class for Feathers components that isn't meant to be * instantiated directly. It should only be subclassed. For a simple * component that will automatically size itself based on its children, * and with optional support for layouts, see LayoutGroup.

* * @see feathers.controls.LayoutGroup */ public class FeathersControl extends Sprite implements IFeathersControl, ILayoutDisplayObject { /** * @private */ private static const HELPER_MATRIX:Matrix = new Matrix(); /** * @private */ private static const HELPER_POINT:Point = new Point(); /** * Flag to indicate that everything is invalid and should be redrawn. */ public static const INVALIDATION_FLAG_ALL:String = "all"; /** * Invalidation flag to indicate that the state has changed. Used by * isEnabled, but may be used for other control states too. * * @see #isEnabled */ public static const INVALIDATION_FLAG_STATE:String = "state"; /** * Invalidation flag to indicate that the dimensions of the UI control * have changed. */ public static const INVALIDATION_FLAG_SIZE:String = "size"; /** * Invalidation flag to indicate that the styles or visual appearance of * the UI control has changed. */ public static const INVALIDATION_FLAG_STYLES:String = "styles"; /** * Invalidation flag to indicate that the skin of the UI control has changed. */ public static const INVALIDATION_FLAG_SKIN:String = "skin"; /** * Invalidation flag to indicate that the layout of the UI control has * changed. */ public static const INVALIDATION_FLAG_LAYOUT:String = "layout"; /** * Invalidation flag to indicate that the primary data displayed by the * UI control has changed. */ public static const INVALIDATION_FLAG_DATA:String = "data"; /** * Invalidation flag to indicate that the scroll position of the UI * control has changed. */ public static const INVALIDATION_FLAG_SCROLL:String = "scroll"; /** * Invalidation flag to indicate that the selection of the UI control * has changed. */ public static const INVALIDATION_FLAG_SELECTED:String = "selected"; /** * Invalidation flag to indicate that the focus of the UI control has * changed. */ public static const INVALIDATION_FLAG_FOCUS:String = "focus"; /** * @private */ protected static const INVALIDATION_FLAG_TEXT_RENDERER:String = "textRenderer"; /** * @private */ protected static const INVALIDATION_FLAG_TEXT_EDITOR:String = "textEditor"; /** * @private */ protected static const ILLEGAL_WIDTH_ERROR:String = "A component's width cannot be NaN."; /** * @private */ protected static const ILLEGAL_HEIGHT_ERROR:String = "A component's height cannot be NaN."; /** * @private */ protected static const ABSTRACT_CLASS_ERROR:String = "FeathersControl is an abstract class. For a lightweight Feathers wrapper, use feathers.controls.LayoutGroup."; /** * A function used by all UI controls that support text renderers to * create an ITextRenderer instance. You may replace the default * function with your own, if you prefer not to use the * BitmapFontTextRenderer. * *

The function is expected to have the following signature:

*
function():ITextRenderer
* * @see ../../../help/text-renderers.html Introduction to Feathers text renderers * @see feathers.core.ITextRenderer */ public static var defaultTextRendererFactory:Function = function():ITextRenderer { return new BitmapFontTextRenderer(); } /** * A function used by all UI controls that support text editor to * create an ITextEditor instance. You may replace the * default function with your own, if you prefer not to use the * StageTextTextEditor. * *

The function is expected to have the following signature:

*
function():ITextEditor
* * @see ../../../help/text-editors.html Introduction to Feathers text editors * @see feathers.core.ITextEditor */ public static var defaultTextEditorFactory:Function = function():ITextEditor { return new StageTextTextEditor(); } /** * Constructor. */ public function FeathersControl() { super(); if(Object(this).constructor == FeathersControl) { throw new Error(ABSTRACT_CLASS_ERROR); } this._styleProvider = this.defaultStyleProvider; this.addEventListener(Event.ADDED_TO_STAGE, feathersControl_addedToStageHandler); this.addEventListener(Event.REMOVED_FROM_STAGE, feathersControl_removedFromStageHandler); } /** * @private */ protected var _validationQueue:ValidationQueue; /** * The concatenated styleNameList, with values separated * by spaces. Style names are somewhat similar to classes in CSS * selectors. In Feathers, they are a non-unique identifier that can * differentiate multiple styles of the same type of UI control. A * single control may have many style names, and many controls can share * a single style name. A theme * or another skinning mechanism may use style names to provide a * variety of visual appearances for a single component class. * *

In general, the styleName property should not be set * directly on a Feathers component. You should add and remove style * names from the styleNameList property instead.

* * @default "" * * @see #styleNameList * @see ../../../help/themes.html Introduction the Feathers themes * @see ../../../help/custom-themes.html Creating custom Feathers themes */ public function get styleName():String { return this._styleNameList.value; } /** * @private */ public function set styleName(value:String):void { this._styleNameList.value = value; } /** * @private */ protected var _styleNameList:TokenList = new TokenList(); /** * Contains a list of all "styles" assigned to this control. Names are * like classes in CSS selectors. They are a non-unique identifier that * can differentiate multiple styles of the same type of UI control. A * single control may have many names, and many controls can share a * single name. A theme * or another skinning mechanism may use style names to provide a * variety of visual appearances for a single component class. * *

Names may be added, removed, or toggled on the * styleNameList. Names cannot contain spaces.

* *

In the following example, a name is added to the name list:

* * * control.styleNameList.add( "custom-component-name" ); * * @see #styleName * @see ../../../help/themes.html Introduction to Feathers themes * @see ../../../help/custom-themes.html Creating custom Feathers themes */ public function get styleNameList():TokenList { return this._styleNameList; } /** * @private */ protected var _styleProvider:IStyleProvider; /** * When a component initializes, a style provider may be used to set * properties that affect the component's visual appearance. * *

You can set or replace an existing style provider at any time * before a component initializes without immediately affecting the * component's visual appearance. After the component initializes, the * style provider may still be changed, but any properties that * were set by the previous style provider will not be reset to their * default values.

* * @see #styleName * @see #styleNameList * @see ../../../help/themes.html Introduction to Feathers themes */ public function get styleProvider():IStyleProvider { return this._styleProvider; } /** * @private */ public function set styleProvider(value:IStyleProvider):void { this._styleProvider = value; if(this._styleProvider && this.isInitialized) { this._styleProvider.applyStyles(this); } } /** * When the FeathersControl constructor is called, the * globalStyleProvider property is set to this value. May be * null. * *

Typically, a subclass of FeathersControl will * override this function to return its static globalStyleProvider * value. For instance, feathers.controls.Button overrides * this function, and its implementation looks like this:

* * * override protected function get defaultStyleProvider():IStyleProvider * { * return Button.globalStyleProvider; * } * * @see #styleProvider */ protected function get defaultStyleProvider():IStyleProvider { return null; } /** * @private */ protected var _isQuickHitAreaEnabled:Boolean = false; /** * Similar to mouseChildren on the classic display list. If * true, children cannot dispatch touch events, but hit * tests will be much faster. Easier than overriding * hitTest(). * *

In the following example, the quick hit area is enabled:

* * * control.isQuickHitAreaEnabled = true; * * @default false */ public function get isQuickHitAreaEnabled():Boolean { return this._isQuickHitAreaEnabled; } /** * @private */ public function set isQuickHitAreaEnabled(value:Boolean):void { this._isQuickHitAreaEnabled = value; } /** * @private */ protected var _hitArea:Rectangle = new Rectangle(); /** * @private */ protected var _isInitializing:Boolean = false; /** * @private */ protected var _isInitialized:Boolean = false; /** * Determines if the component has been initialized yet. The * initialize() function is called one time only, when the * Feathers UI control is added to the display list for the first time. * *

In the following example, we check if the component is initialized * or not, and we listen for an event if it isn't:

* * * if( !control.isInitialized ) * { * control.addEventListener( FeathersEventType.INITIALIZE, initializeHandler ); * } * * @see #event:initialize * @see #isCreated */ public function get isInitialized():Boolean { return this._isInitialized; } /** * @private * A flag that indicates that everything is invalid. If true, no other * flags will need to be tracked. */ protected var _isAllInvalid:Boolean = false; /** * @private */ protected var _invalidationFlags:Object = {}; /** * @private */ protected var _delayedInvalidationFlags:Object = {}; /** * @private */ protected var _isEnabled:Boolean = true; /** * Indicates whether the control is interactive or not. * *

In the following example, the control is disabled:

* * * control.isEnabled = false; * * @default true */ public function get isEnabled():Boolean { return _isEnabled; } /** * @private */ public function set isEnabled(value:Boolean):void { if(this._isEnabled == value) { return; } this._isEnabled = value; this.invalidate(INVALIDATION_FLAG_STATE); } /** * @private */ protected var _explicitWidth:Number = NaN; /** * The width value explicitly set by passing a value to the * width setter or to the setSize() method. */ public function get explicitWidth():Number { return this._explicitWidth; } /** * The final width value that should be used for layout. If the width * has been explicitly set, then that value is used. If not, the actual * width will be calculated automatically. Each component has different * automatic sizing behavior, but it's usually based on the component's * skin or content, including text or subcomponents. */ protected var actualWidth:Number = 0; /** * @private * The actualWidth value that accounts for * scaleX. Not intended to be used for layout since layout * uses unscaled values. This is the value exposed externally through * the width getter. */ protected var scaledActualWidth:Number = 0; /** * The width of the component, in pixels. This could be a value that was * set explicitly, or the component will automatically resize if no * explicit width value is provided. Each component has a different * automatic sizing behavior, but it's usually based on the component's * skin or content, including text or subcomponents. * *

Note: Values of the width and * height properties may not be accurate until after * validation. If you are seeing width or height * values of 0, but you can see something on the screen and * know that the value should be larger, it may be because you asked for * the dimensions before the component had validated. Call * validate() to tell the component to immediately redraw * and calculate an accurate values for the dimensions.

* *

In the following example, the width is set to 120 pixels:

* * * control.width = 120; * *

In the following example, the width is cleared so that the * component can automatically measure its own width:

* * * control.width = NaN; * * @see feathers.core.FeathersControl#setSize() * @see feathers.core.FeathersControl#validate() */ override public function get width():Number { return this.scaledActualWidth; } /** * @private */ override public function set width(value:Number):void { if(this._explicitWidth == value) { return; } var valueIsNaN:Boolean = value !== value; //isNaN if(valueIsNaN && this._explicitWidth !== this._explicitWidth) { return; } this._explicitWidth = value; if(valueIsNaN) { this.actualWidth = this.scaledActualWidth = 0; this.invalidate(INVALIDATION_FLAG_SIZE); } else { this.setSizeInternal(value, this.actualHeight, true); } } /** * @private */ protected var _explicitHeight:Number = NaN; /** * The height value explicitly set by passing a value to the * height setter or by calling the setSize() * function. */ public function get explicitHeight():Number { return this._explicitHeight; } /** * The final height value that should be used for layout. If the height * has been explicitly set, then that value is used. If not, the actual * height will be calculated automatically. Each component has different * automatic sizing behavior, but it's usually based on the component's * skin or content, including text or subcomponents. */ protected var actualHeight:Number = 0; /** * @private * The actualHeight value that accounts for * scaleY. Not intended to be used for layout since layout * uses unscaled values. This is the value exposed externally through * the height getter. */ protected var scaledActualHeight:Number = 0; /** * The height of the component, in pixels. This could be a value that * was set explicitly, or the component will automatically resize if no * explicit height value is provided. Each component has a different * automatic sizing behavior, but it's usually based on the component's * skin or content, including text or subcomponents. * *

Note: Values of the width and * height properties may not be accurate until after * validation. If you are seeing width or height * values of 0, but you can see something on the screen and * know that the value should be larger, it may be because you asked for * the dimensions before the component had validated. Call * validate() to tell the component to immediately redraw * and calculate an accurate values for the dimensions.

* *

In the following example, the height is set to 120 pixels:

* * * control.height = 120; * *

In the following example, the height is cleared so that the * component can automatically measure its own height:

* * * control.height = NaN; * * @see feathers.core.FeathersControl#setSize() * @see feathers.core.FeathersControl#validate() */ override public function get height():Number { return this.scaledActualHeight; } /** * @private */ override public function set height(value:Number):void { if(this._explicitHeight == value) { return; } var valueIsNaN:Boolean = value !== value; //isNaN if(valueIsNaN && this._explicitHeight !== this._explicitHeight) { return; } this._explicitHeight = value; if(valueIsNaN) { this.actualHeight = this.scaledActualHeight = 0; this.invalidate(INVALIDATION_FLAG_SIZE); } else { this.setSizeInternal(this.actualWidth, value, true); } } /** * @private */ protected var _minTouchWidth:Number = 0; /** * If using isQuickHitAreaEnabled, and the hit area's * width is smaller than this value, it will be expanded. * *

In the following example, the minimum width of the hit area is * set to 120 pixels:

* * * control.minTouchWidth = 120; * * @default 0 */ public function get minTouchWidth():Number { return this._minTouchWidth; } /** * @private */ public function set minTouchWidth(value:Number):void { if(this._minTouchWidth == value) { return; } this._minTouchWidth = value; this.refreshHitAreaX(); } /** * @private */ protected var _minTouchHeight:Number = 0; /** * If using isQuickHitAreaEnabled, and the hit area's * height is smaller than this value, it will be expanded. * *

In the following example, the minimum height of the hit area is * set to 120 pixels:

* * * control.minTouchHeight = 120; * * @default 0 */ public function get minTouchHeight():Number { return this._minTouchHeight; } /** * @private */ public function set minTouchHeight(value:Number):void { if(this._minTouchHeight == value) { return; } this._minTouchHeight = value; this.refreshHitAreaY(); } /** * @private */ protected var _explicitMinWidth:Number = NaN; /** * The minimum width value explicitly set by passing a value to the * minWidth setter. */ public function get explicitMinWidth():Number { return this._explicitMinWidth; } /** * The final minimum width value that should be used for layout. If the * minimum width has been explicitly set, then that value is used. If * not, the actual minimum width will be calculated automatically. Each * component has different automatic sizing behavior, but it's usually * based on the component's skin or content, including text or * subcomponents. */ protected var actualMinWidth:Number = 0; /** * @private * The actualMinWidth value that accounts for * scaleX. Not intended to be used for layout since layout * uses unscaled values. This is the value exposed externally through * the minWidth getter. */ protected var scaledActualMinWidth:Number = 0; /** * The minimum recommended width to be used for self-measurement and, * optionally, by any code that is resizing this component. This value * is not strictly enforced in all cases. An explicit width value that * is smaller than minWidth may be set and will not be * affected by the minimum. * *

In the following example, the minimum width of the control is * set to 120 pixels:

* * * control.minWidth = 120; * * @default 0 */ public function get minWidth():Number { return this.scaledActualMinWidth; } /** * @private */ public function set minWidth(value:Number):void { if(this._explicitMinWidth == value) { return; } var valueIsNaN:Boolean = value !== value; //isNaN if(valueIsNaN && this._explicitMinWidth !== this._explicitMinWidth) { return; } this._explicitMinWidth = value; if(valueIsNaN) { this.actualMinWidth = this.scaledActualMinWidth = 0; } else { this.saveMeasurements(this.actualWidth, this.actualHeight, value, this.actualMinHeight); } this.invalidate(INVALIDATION_FLAG_SIZE); } /** * @private */ protected var _explicitMinHeight:Number = NaN; /** * The minimum height value explicitly set by passing a value to the * minHeight setter. */ public function get explicitMinHeight():Number { return this._explicitMinHeight; } /** * The final minimum height value that should be used for layout. If the * minimum height has been explicitly set, then that value is used. If * not, the actual minimum height will be calculated automatically. Each * component has different automatic sizing behavior, but it's usually * based on the component's skin or content, including text or * subcomponents. */ protected var actualMinHeight:Number = 0; /** * @private * The actuaMinHeight value that accounts for * scaleY. Not intended to be used for layout since layout * uses unscaled values. This is the value exposed externally through * the minHeight getter. */ protected var scaledActualMinHeight:Number = 0; /** * The minimum recommended height to be used for self-measurement and, * optionally, by any code that is resizing this component. This value * is not strictly enforced in all cases. An explicit height value that * is smaller than minHeight may be set and will not be * affected by the minimum. * *

In the following example, the minimum height of the control is * set to 120 pixels:

* * * control.minHeight = 120; * * @default 0 */ public function get minHeight():Number { return this.scaledActualMinHeight; } /** * @private */ public function set minHeight(value:Number):void { if(this._explicitMinHeight == value) { return; } var valueIsNaN:Boolean = value !== value; //isNaN if(valueIsNaN && this._explicitMinHeight !== this._explicitMinHeight) { return; } this._explicitMinHeight = value; if(valueIsNaN) { this.actualMinHeight = this.scaledActualMinHeight = 0; } else { this.saveMeasurements(this.actualWidth, this.actualHeight, this.actualMinWidth, value); } this.invalidate(INVALIDATION_FLAG_SIZE); } /** * @private */ protected var _maxWidth:Number = Number.POSITIVE_INFINITY; /** * The maximum recommended width to be used for self-measurement and, * optionally, by any code that is resizing this component. This value * is not strictly enforced in all cases. An explicit width value that * is larger than maxWidth may be set and will not be * affected by the maximum. * *

In the following example, the maximum width of the control is * set to 120 pixels:

* * * control.maxWidth = 120; * * @default Number.POSITIVE_INFINITY */ public function get maxWidth():Number { return this._maxWidth; } /** * @private */ public function set maxWidth(value:Number):void { if(this._maxWidth == value) { return; } if(value !== value) //isNaN { throw new ArgumentError("maxWidth cannot be NaN"); } this._maxWidth = value; this.invalidate(INVALIDATION_FLAG_SIZE); } /** * @private */ protected var _maxHeight:Number = Number.POSITIVE_INFINITY; /** * The maximum recommended height to be used for self-measurement and, * optionally, by any code that is resizing this component. This value * is not strictly enforced in all cases. An explicit height value that * is larger than maxHeight may be set and will not be * affected by the maximum. * *

In the following example, the maximum width of the control is * set to 120 pixels:

* * * control.maxWidth = 120; * * @default Number.POSITIVE_INFINITY */ public function get maxHeight():Number { return this._maxHeight; } /** * @private */ public function set maxHeight(value:Number):void { if(this._maxHeight == value) { return; } if(value !== value) //isNaN { throw new ArgumentError("maxHeight cannot be NaN"); } this._maxHeight = value; this.invalidate(INVALIDATION_FLAG_SIZE); } /** * @private */ override public function set scaleX(value:Number):void { super.scaleX = value; this.setSizeInternal(this.actualWidth, this.actualHeight, false); } /** * @private */ override public function set scaleY(value:Number):void { super.scaleY = value; this.setSizeInternal(this.actualWidth, this.actualHeight, false); } /** * @private */ protected var _includeInLayout:Boolean = true; /** * @inheritDoc * * @default true */ public function get includeInLayout():Boolean { return this._includeInLayout; } /** * @private */ public function set includeInLayout(value:Boolean):void { if(this._includeInLayout == value) { return; } this._includeInLayout = value; this.dispatchEventWith(FeathersEventType.LAYOUT_DATA_CHANGE); } /** * @private */ protected var _layoutData:ILayoutData; /** * @inheritDoc * * @default null */ public function get layoutData():ILayoutData { return this._layoutData; } /** * @private */ public function set layoutData(value:ILayoutData):void { if(this._layoutData == value) { return; } if(this._layoutData) { this._layoutData.removeEventListener(Event.CHANGE, layoutData_changeHandler); } this._layoutData = value; if(this._layoutData) { this._layoutData.addEventListener(Event.CHANGE, layoutData_changeHandler); } this.dispatchEventWith(FeathersEventType.LAYOUT_DATA_CHANGE); } /** * @private */ protected var _toolTip:String; /** * Text to display in a tool tip to when hovering over this component, * if the ToolTipManager is enabled. * * @default null * * @see ../../../help/tool-tips.html Tool tips in Feathers * @see feathers.core.ToolTipManager */ public function get toolTip():String { return this._toolTip; } /** * @private */ public function set toolTip(value:String):void { this._toolTip = value; } /** * @private */ protected var _focusManager:IFocusManager; /** *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* * @copy feathers.core.IFocusDisplayObject#focusManager * * @default null * * @see feathers.core.IFocusDisplayObject */ public function get focusManager():IFocusManager { return this._focusManager; } /** * @private */ public function set focusManager(value:IFocusManager):void { if(!(this is IFocusDisplayObject)) { throw new IllegalOperationError("Cannot pass a focus manager to a component that does not implement feathers.core.IFocusDisplayObject"); } if(this._focusManager == value) { return; } this._focusManager = value; if(this._focusManager) { this.addEventListener(FeathersEventType.FOCUS_IN, focusInHandler); this.addEventListener(FeathersEventType.FOCUS_OUT, focusOutHandler); } else { this.removeEventListener(FeathersEventType.FOCUS_IN, focusInHandler); this.removeEventListener(FeathersEventType.FOCUS_OUT, focusOutHandler); } } /** * @private */ protected var _focusOwner:IFocusDisplayObject; /** *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* * @copy feathers.core.IFocusDisplayObject#focusOwner * * @default null * * @see feathers.core.IFocusDisplayObject */ public function get focusOwner():IFocusDisplayObject { return this._focusOwner; } /** * @private */ public function set focusOwner(value:IFocusDisplayObject):void { this._focusOwner = value; } /** * @private */ protected var _isFocusEnabled:Boolean = true; /** *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* * @copy feathers.core.IFocusDisplayObject#isFocusEnabled * * @default true * * @see feathers.core.IFocusDisplayObject */ public function get isFocusEnabled():Boolean { return this._isEnabled && this._isFocusEnabled; } /** * @private */ public function set isFocusEnabled(value:Boolean):void { if(!(this is IFocusDisplayObject)) { throw new IllegalOperationError("Cannot enable focus on a component that does not implement feathers.core.IFocusDisplayObject"); } if(this._isFocusEnabled == value) { return; } this._isFocusEnabled = value; } /** * @private */ protected var _nextTabFocus:IFocusDisplayObject; /** *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* * @copy feathers.core.IFocusDisplayObject#nextTabFocus * * @default null * * @see feathers.core.IFocusDisplayObject */ public function get nextTabFocus():IFocusDisplayObject { return this._nextTabFocus; } /** * @private */ public function set nextTabFocus(value:IFocusDisplayObject):void { if(!(this is IFocusDisplayObject)) { throw new IllegalOperationError("Cannot set next tab focus on a component that does not implement feathers.core.IFocusDisplayObject"); } this._nextTabFocus = value; } /** * @private */ protected var _previousTabFocus:IFocusDisplayObject; /** *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* * @copy feathers.core.IFocusDisplayObject#previousTabFocus * * @default null * * @see feathers.core.IFocusDisplayObject */ public function get previousTabFocus():IFocusDisplayObject { return this._previousTabFocus; } /** * @private */ public function set previousTabFocus(value:IFocusDisplayObject):void { if(!(this is IFocusDisplayObject)) { throw new IllegalOperationError("Cannot set previous tab focus on a component that does not implement feathers.core.IFocusDisplayObject"); } this._previousTabFocus = value; } /** * @private */ protected var _focusIndicatorSkin:DisplayObject; /** * If this component supports focus, this optional skin will be * displayed above the component when showFocus() is * called. The focus indicator skin is not always displayed when the * component has focus. Typically, if the component receives focus from * a touch, the focus indicator is not displayed. * *

The touchable of this skin will always be set to * false so that it does not "steal" touches from the * component or its sub-components. This skin will not affect the * dimensions of the component or its hit area. It is simply a visual * indicator of focus.

* *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* *

In the following example, the focus indicator skin is set:

* * * control.focusIndicatorSkin = new Image( texture ); * * @default null */ public function get focusIndicatorSkin():DisplayObject { return this._focusIndicatorSkin; } /** * @private */ public function set focusIndicatorSkin(value:DisplayObject):void { if(!(this is IFocusDisplayObject)) { throw new IllegalOperationError("Cannot set focus indicator skin on a component that does not implement feathers.core.IFocusDisplayObject"); } if(this._focusIndicatorSkin == value) { return; } if(this._focusIndicatorSkin) { if(this._focusIndicatorSkin.parent == this) { this._focusIndicatorSkin.removeFromParent(false); } if(this._focusIndicatorSkin is IStateObserver && this is IStateContext) { IStateObserver(this._focusIndicatorSkin).stateContext = null; } } this._focusIndicatorSkin = value; if(this._focusIndicatorSkin) { this._focusIndicatorSkin.touchable = false; } if(this._focusIndicatorSkin is IStateObserver && this is IStateContext) { IStateObserver(this._focusIndicatorSkin).stateContext = IStateContext(this); } if(this._focusManager && this._focusManager.focus == this) { this.invalidate(INVALIDATION_FLAG_STYLES); } } /** * Quickly sets all focus padding properties to the same value. The * focusPadding getter always returns the value of * focusPaddingTop, but the other focus padding values may * be different. * *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* *

The following example gives the button 2 pixels of focus padding * on all sides:

* * * control.focusPadding = 2; * * @default 0 * * @see #focusPaddingTop * @see #focusPaddingRight * @see #focusPaddingBottom * @see #focusPaddingLeft */ public function get focusPadding():Number { return this._focusPaddingTop; } /** * @private */ public function set focusPadding(value:Number):void { this.focusPaddingTop = value; this.focusPaddingRight = value; this.focusPaddingBottom = value; this.focusPaddingLeft = value; } /** * @private */ protected var _focusPaddingTop:Number = 0; /** * The minimum space, in pixels, between the object's top edge and the * top edge of the focus indicator skin. A negative value may be used * to expand the focus indicator skin outside the bounds of the object. * *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* *

The following example gives the focus indicator skin -2 pixels of * padding on the top edge only:

* * * control.focusPaddingTop = -2; * * @default 0 */ public function get focusPaddingTop():Number { return this._focusPaddingTop; } /** * @private */ public function set focusPaddingTop(value:Number):void { if(this._focusPaddingTop == value) { return; } this._focusPaddingTop = value; this.invalidate(INVALIDATION_FLAG_FOCUS); } /** * @private */ protected var _focusPaddingRight:Number = 0; /** * The minimum space, in pixels, between the object's right edge and the * right edge of the focus indicator skin. A negative value may be used * to expand the focus indicator skin outside the bounds of the object. * *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* *

The following example gives the focus indicator skin -2 pixels of * padding on the right edge only:

* * * control.focusPaddingRight = -2; * * @default 0 */ public function get focusPaddingRight():Number { return this._focusPaddingRight; } /** * @private */ public function set focusPaddingRight(value:Number):void { if(this._focusPaddingRight == value) { return; } this._focusPaddingRight = value; this.invalidate(INVALIDATION_FLAG_FOCUS); } /** * @private */ protected var _focusPaddingBottom:Number = 0; /** * The minimum space, in pixels, between the object's bottom edge and the * bottom edge of the focus indicator skin. A negative value may be used * to expand the focus indicator skin outside the bounds of the object. * *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* *

The following example gives the focus indicator skin -2 pixels of * padding on the bottom edge only:

* * * control.focusPaddingBottom = -2; * * @default 0 */ public function get focusPaddingBottom():Number { return this._focusPaddingBottom; } /** * @private */ public function set focusPaddingBottom(value:Number):void { if(this._focusPaddingBottom == value) { return; } this._focusPaddingBottom = value; this.invalidate(INVALIDATION_FLAG_FOCUS); } /** * @private */ protected var _focusPaddingLeft:Number = 0; /** * The minimum space, in pixels, between the object's left edge and the * left edge of the focus indicator skin. A negative value may be used * to expand the focus indicator skin outside the bounds of the object. * *

The implementation of this property is provided for convenience, * but it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* *

The following example gives the focus indicator skin -2 pixels of * padding on the right edge only:

* * * control.focusPaddingLeft = -2; * * @default 0 */ public function get focusPaddingLeft():Number { return this._focusPaddingLeft; } /** * @private */ public function set focusPaddingLeft(value:Number):void { if(this._focusPaddingLeft == value) { return; } this._focusPaddingLeft = value; this.invalidate(INVALIDATION_FLAG_FOCUS); } /** * @private */ protected var _hasFocus:Boolean = false; /** * @private */ protected var _showFocus:Boolean = false; /** * @private * Flag to indicate that the control is currently validating. */ protected var _isValidating:Boolean = false; /** * @private * Flag to indicate that the control has validated at least once. */ protected var _hasValidated:Boolean = false; /** * Determines if the component has been initialized and validated for * the first time. * *

In the following example, we check if the component is created or * not, and we listen for an event if it isn't:

* * * if( !control.isCreated ) * { * control.addEventListener( FeathersEventType.CREATION_COMPLETE, creationCompleteHandler ); * } * * @see #event:creationComplete * @see #isInitialized */ public function get isCreated():Boolean { return this._hasValidated; } /** * @private */ protected var _depth:int = -1; /** * @copy feathers.core.IValidating#depth */ public function get depth():int { return this._depth; } /** * @private */ protected var _invalidateCount:int = 0; /** * @private */ override public function getBounds(targetSpace:DisplayObject, resultRect:Rectangle=null):Rectangle { if(!resultRect) { resultRect = new Rectangle(); } var minX:Number = Number.MAX_VALUE, maxX:Number = -Number.MAX_VALUE; var minY:Number = Number.MAX_VALUE, maxY:Number = -Number.MAX_VALUE; if (targetSpace == this) // optimization { minX = 0; minY = 0; maxX = this.actualWidth; maxY = this.actualHeight; } else { this.getTransformationMatrix(targetSpace, HELPER_MATRIX); MatrixUtil.transformCoords(HELPER_MATRIX, 0, 0, HELPER_POINT); minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x; maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x; minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y; maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y; MatrixUtil.transformCoords(HELPER_MATRIX, 0, this.actualHeight, HELPER_POINT); minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x; maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x; minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y; maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y; MatrixUtil.transformCoords(HELPER_MATRIX, this.actualWidth, 0, HELPER_POINT); minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x; maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x; minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y; maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y; MatrixUtil.transformCoords(HELPER_MATRIX, this.actualWidth, this.actualHeight, HELPER_POINT); minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x; maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x; minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y; maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y; } resultRect.x = minX; resultRect.y = minY; resultRect.width = maxX - minX; resultRect.height = maxY - minY; return resultRect; } /** * @private */ override public function hitTest(localPoint:Point):DisplayObject { if(this._isQuickHitAreaEnabled) { if(!this.visible || !this.touchable) { return null; } if(this.mask && !this.hitTestMask(localPoint)) { return null; } return this._hitArea.containsPoint(localPoint) ? this : null; } return super.hitTest(localPoint); } /** * @private */ protected var _isDisposed:Boolean = false; /** * @private */ override public function dispose():void { this._isDisposed = true; this._validationQueue = null; super.dispose(); } /** * Call this function to tell the UI control that a redraw is pending. * The redraw will happen immediately before Starling renders the UI * control to the screen. The validation system exists to ensure that * multiple properties can be set together without redrawing multiple * times in between each property change. * *

If you cannot wait until later for the validation to happen, you * can call validate() to redraw immediately. As an example, * you might want to validate immediately if you need to access the * correct width or height values of the UI * control, since these values are calculated during validation.

* * @see feathers.core.FeathersControl#validate() */ public function invalidate(flag:String = INVALIDATION_FLAG_ALL):void { var isAlreadyInvalid:Boolean = this.isInvalid(); var isAlreadyDelayedInvalid:Boolean = false; if(this._isValidating) { for(var otherFlag:String in this._delayedInvalidationFlags) { isAlreadyDelayedInvalid = true; break; } } if(!flag || flag == INVALIDATION_FLAG_ALL) { if(this._isValidating) { this._delayedInvalidationFlags[INVALIDATION_FLAG_ALL] = true; } else { this._isAllInvalid = true; } } else { if(this._isValidating) { this._delayedInvalidationFlags[flag] = true; } else if(flag != INVALIDATION_FLAG_ALL && !this._invalidationFlags.hasOwnProperty(flag)) { this._invalidationFlags[flag] = true; } } if(!this._validationQueue || !this._isInitialized) { //we'll add this component to the queue later, after it has been //added to the stage. return; } if(this._isValidating) { if(isAlreadyDelayedInvalid) { return; } this._invalidateCount++; this._validationQueue.addControl(this, this._invalidateCount >= 10); return; } if(isAlreadyInvalid) { return; } this._invalidateCount = 0; this._validationQueue.addControl(this, false); } /** * @copy feathers.core.IValidating#validate() * * @see #invalidate() */ public function validate():void { if(this._isDisposed) { //disposed components have no reason to validate, but they may //have been left in the queue. return; } if(!this._isInitialized) { if(this._isInitializing) { //initializing components cannot validate until they've //finished initializing. we'll have to wait. return; } this.initializeInternal(); } if(!this.isInvalid()) { return; } if(this._isValidating) { //we were already validating, and something else told us to //validate. that's bad... if(this._validationQueue) { //...so we'll just try to do it later this._validationQueue.addControl(this, true); } return; } this._isValidating = true; this.draw(); for(var flag:String in this._invalidationFlags) { delete this._invalidationFlags[flag]; } this._isAllInvalid = false; for(flag in this._delayedInvalidationFlags) { if(flag == INVALIDATION_FLAG_ALL) { this._isAllInvalid = true; } else { this._invalidationFlags[flag] = true; } delete this._delayedInvalidationFlags[flag]; } this._isValidating = false; if(!this._hasValidated) { this._hasValidated = true; this.dispatchEventWith(FeathersEventType.CREATION_COMPLETE); } } /** * Indicates whether the control is pending validation or not. By * default, returns true if any invalidation flag has been * set. If you pass in a specific flag, returns true only * if that flag has been set (others may be set too, but it checks the * specific flag only. If all flags have been marked as invalid, always * returns true. */ public function isInvalid(flag:String = null):Boolean { if(this._isAllInvalid) { return true; } if(!flag) //return true if any flag is set { for(flag in this._invalidationFlags) { return true; } return false; } return this._invalidationFlags[flag]; } /** * Sets both the width and the height of the control in a single * function call. * * @see #width * @see #height */ public function setSize(width:Number, height:Number):void { this._explicitWidth = width; var widthIsNaN:Boolean = width !== width; if(widthIsNaN) { this.actualWidth = this.scaledActualWidth = 0; } this._explicitHeight = height; var heightIsNaN:Boolean = height !== height; if(heightIsNaN) { this.actualHeight = this.scaledActualHeight = 0; } if(widthIsNaN || heightIsNaN) { this.invalidate(INVALIDATION_FLAG_SIZE); } else { this.setSizeInternal(width, height, true); } } /** * Sets both the x and the y positions of the control in a single * function call. * * @see #x * @see #y */ public function move(x:Number, y:Number):void { this.x = x; this.y = y; } /** * Resets the styleProvider property to its default value, * which is usually the global style provider for the component. * * @see #styleProvider * @see #defaultStyleProvider */ public function resetStyleProvider():void { this.styleProvider = this.defaultStyleProvider; } /** *

The implementation of this method is provided for convenience, but * it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* * @copy feathers.core.IFocusDisplayObject#showFocus() * * @see feathers.core.IFocusDisplayObject */ public function showFocus():void { if(!this._hasFocus || !this._focusIndicatorSkin) { return; } this._showFocus = true; this.invalidate(INVALIDATION_FLAG_FOCUS); } /** *

The implementation of this method is provided for convenience, but * it cannot be used unless a subclass implements the * IFocusDisplayObject interface.

* * @copy feathers.core.IFocusDisplayObject#hideFocus() * * @see feathers.core.IFocusDisplayObject */ public function hideFocus():void { if(!this._hasFocus || !this._focusIndicatorSkin) { return; } this._showFocus = false; this.invalidate(INVALIDATION_FLAG_FOCUS); } /** * Sets the width and height of the control, with the option of * invalidating or not. Intended to be used when the width * and height values have not been set explicitly, and the * UI control needs to measure itself and choose an "ideal" size. */ protected function setSizeInternal(width:Number, height:Number, canInvalidate:Boolean):Boolean { var changed:Boolean = this.saveMeasurements(width, height, this.actualMinWidth, this.actualMinHeight); if(canInvalidate && changed) { this.invalidate(INVALIDATION_FLAG_SIZE); } return changed; } /** * */ protected function saveMeasurements(width:Number, height:Number, minWidth:Number = 0, minHeight:Number = 0):Boolean { if(this._explicitMinWidth === this._explicitMinWidth) //!isNaN { //the min width has been set explicitly. it has precedence over //the measured min width minWidth = this._explicitMinWidth; } if(this._explicitMinHeight === this._explicitMinHeight) //!isNaN { //the min height has been set explicitly. it has precedence over //the measured min height minHeight = this._explicitMinHeight; } if(this._explicitWidth === this._explicitWidth) //!isNaN { width = this._explicitWidth; } else { if(width < minWidth) { width = minWidth; } else if(width > this._maxWidth) { width = this._maxWidth; } } if(this._explicitHeight === this._explicitHeight) //!isNaN { height = this._explicitHeight; } else { if(height < minHeight) { height = minHeight; } else if(height > this._maxHeight) { height = this._maxHeight; } } if(width !== width) //isNaN { throw new ArgumentError(ILLEGAL_WIDTH_ERROR); } if(height !== height) //isNaN { throw new ArgumentError(ILLEGAL_HEIGHT_ERROR); } var scaleX:Number = this.scaleX; if(scaleX < 0) { scaleX = -scaleX; } var scaleY:Number = this.scaleY; if(scaleY < 0) { scaleY = -scaleY; } var resized:Boolean = false; if(this.actualWidth !== width) { this.actualWidth = width; this.refreshHitAreaX(); resized = true; } if(this.actualHeight !== height) { this.actualHeight = height; this.refreshHitAreaY(); resized = true; } if(this.actualMinWidth !== minWidth) { this.actualMinWidth = minWidth; resized = true; } if(this.actualMinHeight !== minHeight) { this.actualMinHeight = minHeight; resized = true; } width = this.scaledActualWidth; height = this.scaledActualHeight; minWidth = this.scaledActualMinWidth; minHeight = this.scaledActualMinHeight; this.scaledActualWidth = this.actualWidth * scaleX; this.scaledActualHeight = this.actualHeight * scaleY; this.scaledActualMinWidth = this.actualMinWidth * scaleX; this.scaledActualMinHeight = this.actualMinHeight * scaleY; if(width !== this.scaledActualWidth || height !== this.scaledActualHeight || minWidth !== this.scaledActualMinWidth || minHeight !== this.scaledActualMinHeight) { resized = true; } if(resized) { this.dispatchEventWith(FeathersEventType.RESIZE); } return resized; } /** * Called the first time that the UI control is added to the stage, and * you should override this function to customize the initialization * process. Do things like create children and set up event listeners. * After this function is called, FeathersEventType.INITIALIZE * is dispatched. * * @see #event:initialize feathers.events.FeathersEventType.INITIALIZE */ protected function initialize():void { } /** * Override to customize layout and to adjust properties of children. * Called when the component validates, if any flags have been marked * to indicate that validation is pending. */ protected function draw():void { } /** * Sets an invalidation flag. This will not add the component to the * validation queue. It only sets the flag. A subclass might use * this function during draw() to manipulate the flags that * its superclass sees. */ protected function setInvalidationFlag(flag:String):void { if(this._invalidationFlags.hasOwnProperty(flag)) { return; } this._invalidationFlags[flag] = true; } /** * Clears an invalidation flag. This will not remove the component from * the validation queue. It only clears the flag. A subclass might use * this function during draw() to manipulate the flags that * its superclass sees. */ protected function clearInvalidationFlag(flag:String):void { delete this._invalidationFlags[flag]; } /** * Updates the focus indicator skin by showing or hiding it and * adjusting its position and dimensions. This function is not called * automatically. Components that support focus should call this * function at an appropriate point within the draw() * function. This function may be overridden if the default behavior is * not desired. */ protected function refreshFocusIndicator():void { if(this._focusIndicatorSkin) { if(this._hasFocus && this._showFocus) { if(this._focusIndicatorSkin.parent != this) { this.addChild(this._focusIndicatorSkin); } else { this.setChildIndex(this._focusIndicatorSkin, this.numChildren - 1); } } else if(this._focusIndicatorSkin.parent) { this._focusIndicatorSkin.removeFromParent(false); } this._focusIndicatorSkin.x = this._focusPaddingLeft; this._focusIndicatorSkin.y = this._focusPaddingTop; this._focusIndicatorSkin.width = this.actualWidth - this._focusPaddingLeft - this._focusPaddingRight; this._focusIndicatorSkin.height = this.actualHeight - this._focusPaddingTop - this._focusPaddingBottom; } } /** * @private */ protected function refreshHitAreaX():void { if(this.actualWidth < this._minTouchWidth) { this._hitArea.width = this._minTouchWidth; } else { this._hitArea.width = this.actualWidth; } var hitAreaX:Number = (this.actualWidth - this._hitArea.width) / 2; if(hitAreaX !== hitAreaX) //isNaN { this._hitArea.x = 0; } else { this._hitArea.x = hitAreaX; } } /** * @private */ protected function refreshHitAreaY():void { if(this.actualHeight < this._minTouchHeight) { this._hitArea.height = this._minTouchHeight; } else { this._hitArea.height = this.actualHeight; } var hitAreaY:Number = (this.actualHeight - this._hitArea.height) / 2; if(hitAreaY !== hitAreaY) //isNaN { this._hitArea.y = 0; } else { this._hitArea.y = hitAreaY; } } /** * @private */ protected function initializeInternal():void { if(this._isInitialized || this._isInitializing) { return; } this._isInitializing = true; this.initialize(); this.invalidate(); //invalidate everything this._isInitializing = false; this._isInitialized = true; this.dispatchEventWith(FeathersEventType.INITIALIZE); if(this._styleProvider) { this._styleProvider.applyStyles(this); } this._styleNameList.addEventListener(Event.CHANGE, styleNameList_changeHandler); } /** * Default event handler for FeathersEventType.FOCUS_IN * that may be overridden in subclasses to perform additional actions * when the component receives focus. */ protected function focusInHandler(event:Event):void { this._hasFocus = true; this.invalidate(INVALIDATION_FLAG_FOCUS); } /** * Default event handler for FeathersEventType.FOCUS_OUT * that may be overridden in subclasses to perform additional actions * when the component loses focus. */ protected function focusOutHandler(event:Event):void { this._hasFocus = false; this._showFocus = false; this.invalidate(INVALIDATION_FLAG_FOCUS); } /** * @private * Initialize the control, if it hasn't been initialized yet. Then, * invalidate. If already initialized, check if invalid and put back * into queue. */ protected function feathersControl_addedToStageHandler(event:Event):void { this._depth = getDisplayObjectDepthFromStage(this); this._validationQueue = ValidationQueue.forStarling(Starling.current); if(!this._isInitialized) { this.initializeInternal(); } if(this.isInvalid()) { this._invalidateCount = 0; //add to validation queue, if required this._validationQueue.addControl(this, false); } } /** * @private */ protected function feathersControl_removedFromStageHandler(event:Event):void { this._depth = -1; this._validationQueue = null; } /** * @private */ protected function layoutData_changeHandler(event:Event):void { this.dispatchEventWith(FeathersEventType.LAYOUT_DATA_CHANGE); } /** * @private */ protected function styleNameList_changeHandler(event:Event):void { if(!this._styleProvider) { return; } this._styleProvider.applyStyles(this); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy