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

scaffold.libs_as.feathers.controls.Callout.as Maven / Gradle / Ivy

/*
Feathers
Copyright 2012-2016 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.controls
{
	import feathers.core.FeathersControl;
	import feathers.core.IFeathersControl;
	import feathers.core.IMeasureDisplayObject;
	import feathers.core.IValidating;
	import feathers.core.PopUpManager;
	import feathers.events.FeathersEventType;
	import feathers.layout.HorizontalAlign;
	import feathers.layout.RelativePosition;
	import feathers.layout.VerticalAlign;
	import feathers.skins.IStyleProvider;
	import feathers.utils.display.getDisplayObjectDepthFromStage;
	import feathers.utils.skins.resetFluidChildDimensionsForMeasurement;

	import flash.events.KeyboardEvent;
	import flash.geom.Rectangle;
	import flash.ui.Keyboard;

	import starling.core.Starling;
	import starling.display.DisplayObject;
	import starling.display.DisplayObjectContainer;
	import starling.events.EnterFrameEvent;
	import starling.events.Event;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;

	/**
	 * Dispatched when the callout is closed.
	 *
	 * 

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 starling.events.Event.CLOSE */ [Event(name="close",type="starling.events.Event")] /** * A pop-up container that points at (or calls out) a specific region of * the application (typically a specific control that triggered it). * *

In general, a Callout isn't instantiated directly. * Instead, you will typically call the static function * Callout.show(). This is not required, but it result in less * code and no need to manually manage calls to the PopUpManager.

* *

In the following example, a callout displaying a Label is * shown when a Button is triggered:

* * * button.addEventListener( Event.TRIGGERED, button_triggeredHandler ); * * function button_triggeredHandler( event:Event ):void * { * var label:Label = new Label(); * label.text = "Hello World!"; * var button:Button = Button( event.currentTarget ); * Callout.show( label, button ); * } * * @see ../../../help/callout.html How to use the Feathers Callout component */ public class Callout extends FeathersControl { /** * The default IStyleProvider for all Callout * components. * * @default null * @see feathers.core.FeathersControl#styleProvider */ public static var globalStyleProvider:IStyleProvider; /** * The default positions used by a callout. */ public static const DEFAULT_POSITIONS:Vector. = new [ RelativePosition.BOTTOM, RelativePosition.TOP, RelativePosition.RIGHT, RelativePosition.LEFT, ]; /** * @private * DEPRECATED: Replaced by a Vector.<String> containing values from * feathers.layout.RelativePosition. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const DIRECTION_ANY:String = "any"; /** * @private * DEPRECATED: Replaced by a Vector.<String> containing values from * feathers.layout.RelativePosition. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const DIRECTION_VERTICAL:String = "vertical"; /** * @private * DEPRECATED: Replaced by a Vector.<String> containing values from * feathers.layout.RelativePosition. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const DIRECTION_HORIZONTAL:String = "horizontal"; /** * @private * DEPRECATED: Replaced by feathers.layout.RelativePosition.TOP. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const DIRECTION_UP:String = "up"; /** * @private * DEPRECATED: Replaced by feathers.layout.RelativePosition.BOTTOM. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const DIRECTION_DOWN:String = "down"; /** * @private * DEPRECATED: Replaced by feathers.layout.RelativePosition.LEFT. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const DIRECTION_LEFT:String = "left"; /** * @private * DEPRECATED: Replaced by feathers.layout.RelativePosition.RIGHT. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const DIRECTION_RIGHT:String = "right"; /** * @private * DEPRECATED: Replaced by feathers.layout.RelativePosition.TOP. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const ARROW_POSITION_TOP:String = "top"; /** * @private * DEPRECATED: Replaced by feathers.layout.RelativePosition.RIGHT. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const ARROW_POSITION_RIGHT:String = "right"; /** * @private * DEPRECATED: Replaced by feathers.layout.RelativePosition.BOTTOM. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const ARROW_POSITION_BOTTOM:String = "bottom"; /** * @private * DEPRECATED: Replaced by feathers.layout.RelativePosition.LEFT. * *

DEPRECATION WARNING: This constant is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public static const ARROW_POSITION_LEFT:String = "left"; /** * @private */ protected static const INVALIDATION_FLAG_ORIGIN:String = "origin"; /** * @private */ private static const HELPER_RECT:Rectangle = new Rectangle(); /** * @private */ protected static const FUZZY_CONTENT_DIMENSIONS_PADDING:Number = 0.000001; /** * Quickly sets all stage padding properties to the same value. The * stagePadding getter always returns the value of * stagePaddingTop, but the other padding values may be * different. * *

The following example gives the stage 20 pixels of padding on all * sides:

* * * Callout.stagePadding = 20; * * @default 0 * * @see #stagePaddingTop * @see #stagePaddingRight * @see #stagePaddingBottom * @see #stagePaddingLeft */ public static function get stagePadding():Number { return Callout.stagePaddingTop; } /** * @private */ public static function set stagePadding(value:Number):void { Callout.stagePaddingTop = value; Callout.stagePaddingRight = value; Callout.stagePaddingBottom = value; Callout.stagePaddingLeft = value; } /** * The padding between a callout and the top edge of the stage when the * callout is positioned automatically. May be ignored if the callout * is too big for the stage. * *

In the following example, the top stage padding will be set to * 20 pixels:

* * * Callout.stagePaddingTop = 20; */ public static var stagePaddingTop:Number = 0; /** * The padding between a callout and the right edge of the stage when the * callout is positioned automatically. May be ignored if the callout * is too big for the stage. * *

In the following example, the right stage padding will be set to * 20 pixels:

* * * Callout.stagePaddingRight = 20; */ public static var stagePaddingRight:Number = 0; /** * The padding between a callout and the bottom edge of the stage when the * callout is positioned automatically. May be ignored if the callout * is too big for the stage. * *

In the following example, the bottom stage padding will be set to * 20 pixels:

* * * Callout.stagePaddingBottom = 20; */ public static var stagePaddingBottom:Number = 0; /** * The margin between a callout and the top edge of the stage when the * callout is positioned automatically. May be ignored if the callout * is too big for the stage. * *

In the following example, the left stage padding will be set to * 20 pixels:

* * * Callout.stagePaddingLeft = 20; */ public static var stagePaddingLeft:Number = 0; /** * Returns a new Callout instance when * Callout.show() is called. If one wishes to skin the * callout manually or change its behavior, a custom factory may be * provided. * *

This function is expected to have the following signature:

* *
function():Callout
* *

The following example shows how to create a custom callout factory:

* * * Callout.calloutFactory = function():Callout * { * var callout:Callout = new Callout(); * //set properties here! * return callout; * }; * *

Note: the default callout factory sets the following properties:

* * * callout.closeOnTouchBeganOutside = true; * callout.closeOnTouchEndedOutside = true; * callout.closeOnKeys = new <uint>[Keyboard.BACK, Keyboard.ESCAPE]; * * @see #show() */ public static var calloutFactory:Function = defaultCalloutFactory; /** * Returns an overlay to display with a callout that is modal. Uses the * standard overlayFactory of the PopUpManager * by default, but you can use this property to provide your own custom * overlay, if you prefer. * *

This function is expected to have the following signature:

*
function():DisplayObject
* *

The following example uses a semi-transparent Quad as * a custom overlay:

* * * Callout.calloutOverlayFactory = function():Quad * { * var quad:Quad = new Quad(10, 10, 0x000000); * quad.alpha = 0.75; * return quad; * }; * * @see feathers.core.PopUpManager#overlayFactory * * @see #show() */ public static var calloutOverlayFactory:Function = PopUpManager.defaultOverlayFactory; /** * Creates a callout, and then positions and sizes it automatically * based on an origin rectangle and the specified direction relative to * the original. The provided width and height values are optional, and * these values may be ignored if the callout cannot be drawn at the * specified dimensions. * *

The supportedPositions parameter should be a * Vector.<String> of values from the * feathers.layout.RelativePosition class. The positions * should be ordered by preference. This parameter is typed as * Object to allow some deprecated String * values. In a future version of Feathers, the type will change to * Vector.<String> instead.

* *

In the following example, a callout displaying a Label is * shown when a Button is triggered:

* * * button.addEventListener( Event.TRIGGERED, button_triggeredHandler ); * * function button_triggeredHandler( event:Event ):void * { * var label:Label = new Label(); * label.text = "Hello World!"; * var button:Button = Button( event.currentTarget ); * Callout.show( label, button ); * } */ public static function show(content:DisplayObject, origin:DisplayObject, supportedPositions:Object = null, isModal:Boolean = true, customCalloutFactory:Function = null, customOverlayFactory:Function = null):Callout { if(origin.stage === null) { throw new ArgumentError("Callout origin must be added to the stage."); } var factory:Function = customCalloutFactory; if(factory === null) { factory = calloutFactory; if(factory === null) { factory = defaultCalloutFactory; } } var callout:Callout = Callout(factory()); callout.content = content; if(supportedPositions is String) { //fallback for deprecated options callout.supportedDirections = supportedPositions as String; } else { callout.supportedPositions = supportedPositions as Vector.; } callout.origin = origin; factory = customOverlayFactory; if(factory === null) { factory = calloutOverlayFactory; if(factory === null) { factory = PopUpManager.defaultOverlayFactory; } } PopUpManager.addPopUp(callout, isModal, false, factory); return callout; } /** * The default factory that creates callouts when Callout.show() * is called. To use a different factory, you need to set * Callout.calloutFactory to a Function * instance. */ public static function defaultCalloutFactory():Callout { var callout:Callout = new Callout(); callout.closeOnTouchBeganOutside = true; callout.closeOnTouchEndedOutside = true; callout.closeOnKeys = new [Keyboard.BACK, Keyboard.ESCAPE]; return callout; } /** * @private */ protected static function positionBelowOrigin(callout:Callout, globalOrigin:Rectangle):void { callout.measureWithArrowPosition(RelativePosition.TOP); var idealXPosition:Number = globalOrigin.x; if(callout._horizontalAlign === HorizontalAlign.CENTER) { idealXPosition += Math.round((globalOrigin.width - callout.width) / 2); } else if(callout._horizontalAlign === HorizontalAlign.RIGHT) { idealXPosition += (globalOrigin.width - callout.width); } var xPosition:Number = idealXPosition; if(stagePaddingLeft > xPosition) { xPosition = stagePaddingLeft; } else { var maxXPosition:Number = Starling.current.stage.stageWidth - callout.width - stagePaddingRight; if(maxXPosition < xPosition) { xPosition = maxXPosition; } } callout.x = xPosition; callout.y = globalOrigin.y + globalOrigin.height; if(callout._isValidating) { //no need to invalidate and need to validate again next frame callout._arrowOffset = idealXPosition - xPosition; callout._arrowPosition = RelativePosition.TOP; } else { callout.arrowOffset = idealXPosition - xPosition; callout.arrowPosition = RelativePosition.TOP; } } /** * @private */ protected static function positionAboveOrigin(callout:Callout, globalOrigin:Rectangle):void { callout.measureWithArrowPosition(RelativePosition.BOTTOM); var idealXPosition:Number = globalOrigin.x; if(callout._horizontalAlign === HorizontalAlign.CENTER) { idealXPosition += Math.round((globalOrigin.width - callout.width) / 2); } else if(callout._horizontalAlign === HorizontalAlign.RIGHT) { idealXPosition += (globalOrigin.width - callout.width); } var xPosition:Number = idealXPosition; if(stagePaddingLeft > xPosition) { xPosition = stagePaddingLeft; } else { var maxXPosition:Number = Starling.current.stage.stageWidth - callout.width - stagePaddingRight; if(maxXPosition < xPosition) { xPosition = maxXPosition; } } callout.x = xPosition; callout.y = globalOrigin.y - callout.height; if(callout._isValidating) { //no need to invalidate and need to validate again next frame callout._arrowOffset = idealXPosition - xPosition; callout._arrowPosition = RelativePosition.BOTTOM; } else { callout.arrowOffset = idealXPosition - xPosition; callout.arrowPosition = RelativePosition.BOTTOM; } } /** * @private */ protected static function positionToRightOfOrigin(callout:Callout, globalOrigin:Rectangle):void { callout.measureWithArrowPosition(RelativePosition.LEFT); callout.x = globalOrigin.x + globalOrigin.width; var idealYPosition:Number = globalOrigin.y; if(callout._verticalAlign === VerticalAlign.MIDDLE) { idealYPosition += Math.round((globalOrigin.height - callout.height) / 2); } else if(callout._verticalAlign === VerticalAlign.BOTTOM) { idealYPosition += (globalOrigin.height - callout.height); } var yPosition:Number = idealYPosition; if(stagePaddingTop > yPosition) { yPosition = stagePaddingTop; } else { var maxYPosition:Number = Starling.current.stage.stageHeight - callout.height - stagePaddingBottom; if(maxYPosition < yPosition) { yPosition = maxYPosition; } } callout.y = yPosition; if(callout._isValidating) { //no need to invalidate and need to validate again next frame callout._arrowOffset = idealYPosition - yPosition; callout._arrowPosition = RelativePosition.LEFT; } else { callout.arrowOffset = idealYPosition - yPosition; callout.arrowPosition = RelativePosition.LEFT; } } /** * @private */ protected static function positionToLeftOfOrigin(callout:Callout, globalOrigin:Rectangle):void { callout.measureWithArrowPosition(RelativePosition.RIGHT); callout.x = globalOrigin.x - callout.width; var idealYPosition:Number = globalOrigin.y; if(callout._verticalAlign === VerticalAlign.MIDDLE) { idealYPosition += Math.round((globalOrigin.height - callout.height) / 2); } else if(callout._verticalAlign === VerticalAlign.BOTTOM) { idealYPosition += (globalOrigin.height - callout.height); } var yPosition:Number = idealYPosition; if(stagePaddingTop > yPosition) { yPosition = stagePaddingTop; } else { var maxYPosition:Number = Starling.current.stage.stageHeight - callout.height - stagePaddingBottom; if(maxYPosition < yPosition) { yPosition = maxYPosition; } } callout.y = yPosition; if(callout._isValidating) { //no need to invalidate and need to validate again next frame callout._arrowOffset = idealYPosition - yPosition; callout._arrowPosition = RelativePosition.RIGHT; } else { callout.arrowOffset = idealYPosition - yPosition; callout.arrowPosition = RelativePosition.RIGHT; } } /** * Constructor. */ public function Callout() { super(); this.addEventListener(Event.ADDED_TO_STAGE, callout_addedToStageHandler); } /** * Determines if the callout is automatically closed if a touch in the * TouchPhase.BEGAN phase happens outside of the callout's * bounds. * *

In the following example, the callout will not close when a touch * event with TouchPhase.BEGAN is detected outside the * callout's (or its origin's) bounds:

* * * callout.closeOnTouchBeganOutside = false; * * @see #closeOnTouchEndedOutside * @see #closeOnKeys */ public var closeOnTouchBeganOutside:Boolean = false; /** * Determines if the callout is automatically closed if a touch in the * TouchPhase.ENDED phase happens outside of the callout's * bounds. * *

In the following example, the callout will not close when a touch * event with TouchPhase.ENDED is detected outside the * callout's (or its origin's) bounds:

* * * callout.closeOnTouchEndedOutside = false; * * @see #closeOnTouchBeganOutside * @see #closeOnKeys */ public var closeOnTouchEndedOutside:Boolean = false; /** * The callout will be closed if any of these keys are pressed. * *

In the following example, the callout close when the Escape key * is pressed:

* * * callout.closeOnKeys = new <uint>[Keyboard.ESCAPE]; * * @see #closeOnTouchBeganOutside * @see #closeOnTouchEndedOutside */ public var closeOnKeys:Vector.; /** * Determines if the callout will be disposed when close() * is called internally. Close may be called internally in a variety of * cases, depending on values such as closeOnTouchBeganOutside, * closeOnTouchEndedOutside, and closeOnKeys. * If set to false, you may reuse the callout later by * giving it a new origin and adding it to the * PopUpManager again. * *

In the following example, the callout will not be disposed when it * closes itself:

* * * callout.disposeOnSelfClose = false; * * @see #closeOnTouchBeganOutside * @see #closeOnTouchEndedOutside * @see #closeOnKeys * @see #close() */ public var disposeOnSelfClose:Boolean = true; /** * Determines if the callout's content will be disposed when the callout * is disposed. If set to false, the callout's content may * be added to the display list again later. * *

In the following example, the callout's content will not be * disposed when the callout is disposed:

* * * callout.disposeContent = false; */ public var disposeContent:Boolean = true; /** * @private */ protected var _isReadyToClose:Boolean = false; /** * @private */ override protected function get defaultStyleProvider():IStyleProvider { return Callout.globalStyleProvider; } /** * @private */ protected var _explicitContentWidth:Number; /** * @private */ protected var _explicitContentHeight:Number; /** * @private */ protected var _explicitContentMinWidth:Number; /** * @private */ protected var _explicitContentMinHeight:Number; /** * @private */ protected var _explicitBackgroundSkinWidth:Number; /** * @private */ protected var _explicitBackgroundSkinHeight:Number; /** * @private */ protected var _explicitBackgroundSkinMinWidth:Number; /** * @private */ protected var _explicitBackgroundSkinMinHeight:Number; /** * @private */ protected var _content:DisplayObject; /** * The display object that will be presented by the callout. This object * may be resized to fit the callout's bounds. If the content needs to * be scrolled if placed into a smaller region than its ideal size, it * must provide its own scrolling capabilities because the callout does * not offer scrolling. * *

In the following example, the callout's content is an image:

* * * callout.content = new Image( texture ); * * @default null */ public function get content():DisplayObject { return this._content; } /** * @private */ public function set content(value:DisplayObject):void { if(this._content == value) { return; } if(this._content !== null) { if(this._content is IFeathersControl) { IFeathersControl(this._content).removeEventListener(FeathersEventType.RESIZE, content_resizeHandler); } if(this._content.parent === this) { this._content.removeFromParent(false); } } this._content = value; if(this._content !== null) { if(this._content is IFeathersControl) { IFeathersControl(this._content).addEventListener(FeathersEventType.RESIZE, content_resizeHandler); } this.addChild(this._content); if(this._content is IMeasureDisplayObject) { var measureContent:IMeasureDisplayObject = IMeasureDisplayObject(this._content); this._explicitContentWidth = measureContent.explicitWidth; this._explicitContentHeight = measureContent.explicitHeight; this._explicitContentMinWidth = measureContent.explicitMinWidth; this._explicitContentMinHeight = measureContent.explicitMinHeight; } else { this._explicitContentWidth = this._content.width; this._explicitContentHeight = this._content.height; this._explicitContentMinWidth = this._explicitContentWidth; this._explicitContentMinHeight = this._explicitContentHeight; } } this.invalidate(INVALIDATION_FLAG_SIZE); this.invalidate(INVALIDATION_FLAG_DATA); } /** * @private */ protected var _origin:DisplayObject; /** * A callout may be positioned relative to another display object, known * as the callout's origin. Even if the position of the origin changes, * the callout will reposition itself to always point at the origin. * *

When an origin is set, the arrowPosition and * arrowOffset properties will be managed automatically by * the callout. Setting either of these values manually with either have * no effect or unexpected behavior, so it is recommended that you * avoid modifying those properties.

* *

In general, if you use Callout.show(), you will * rarely need to manually manage the origin.

* *

In the following example, the callout's origin is set to a button:

* * * callout.origin = button; * * @default null * * @see #supportedDirections * @see #arrowPosition * @see #arrowOffset */ public function get origin():DisplayObject { return this._origin; } public function set origin(value:DisplayObject):void { if(this._origin == value) { return; } if(value && !value.stage) { throw new ArgumentError("Callout origin must have access to the stage."); } if(this._origin) { this.removeEventListener(EnterFrameEvent.ENTER_FRAME, callout_enterFrameHandler); this._origin.removeEventListener(Event.REMOVED_FROM_STAGE, origin_removedFromStageHandler); } this._origin = value; this._lastGlobalBoundsOfOrigin = null; if(this._origin) { this._origin.addEventListener(Event.REMOVED_FROM_STAGE, origin_removedFromStageHandler); this.addEventListener(EnterFrameEvent.ENTER_FRAME, callout_enterFrameHandler); } this.invalidate(INVALIDATION_FLAG_ORIGIN); } /** * @private */ protected var _supportedDirections:String = null; /** * @private * DEPRECATED: Replaced by the position property. * *

DEPRECATION WARNING: This property is deprecated * starting with Feathers 3.0. It will be removed in a future version of * Feathers according to the standard * Feathers deprecation policy.

*/ public function get supportedDirections():String { return this._supportedDirections; } /** * @private */ public function set supportedDirections(value:String):void { var positions:Vector. = null; if(value === DIRECTION_ANY) { positions = new [RelativePosition.BOTTOM, RelativePosition.TOP, RelativePosition.RIGHT, RelativePosition.LEFT]; } else if(value === DIRECTION_HORIZONTAL) { positions = new [RelativePosition.RIGHT, RelativePosition.LEFT]; } else if(value === DIRECTION_VERTICAL) { positions = new [RelativePosition.BOTTOM, RelativePosition.TOP]; } else if(value === DIRECTION_UP) { positions = new [RelativePosition.TOP]; } else if(value === DIRECTION_DOWN) { positions = new [RelativePosition.BOTTOM]; } else if(value === DIRECTION_RIGHT) { positions = new [RelativePosition.RIGHT]; } else if(value === DIRECTION_LEFT) { positions = new [RelativePosition.LEFT]; } this._supportedDirections = value; this.supportedPositions = positions; } /** * @private */ protected var _supportedPositions:Vector. = null; /** * The position of the callout, relative to its origin. Accepts a * Vector.<String> containing one or more of the * constants from feathers.layout.RelativePosition or * null. If null, the callout will attempt to * position itself using values in the following order: * *
    *
  • RelativePosition.BOTTOM
  • *
  • RelativePosition.TOP
  • *
  • RelativePosition.RIGHT
  • *
  • RelativePosition.LEFT
  • *
* *

Note: If the callout's origin is not set, the * supportedPositions property will be ignored.

* *

In the following example, the callout's supported positions are * restricted to the top and bottom of the origin:

* * * callout.supportedPositions = new <String>[RelativePosition.TOP, RelativePosition.BOTTOM]; * *

In the following example, the callout's position is restricted to * the right of the origin:

* * * callout.supportedPositions = new <String>[RelativePosition.RIGHT]; * *

Note: The arrowPosition property is related to this * one, but they have different meanings and are usually opposites. For * example, a callout on the right side of its origin will generally * display its left arrow.

* * @default null * * @see feathers.layout.RelativePosition#TOP * @see feathers.layout.RelativePosition#RIGHT * @see feathers.layout.RelativePosition#BOTTOM * @see feathers.layout.RelativePosition#LEFT */ public function get supportedPositions():Vector. { return this._supportedPositions; } /** * @private */ public function set supportedPositions(value:Vector.):void { this._supportedPositions = value; } /** * @private */ protected var _horizontalAlign:String = HorizontalAlign.CENTER; /** * The horizontal alignment of the callout, relative to the origin. * * @default feathers.layout.HorizontalAlign.CENTER * * @see feathers.layout.HorizontalAlign#LEFT * @see feathers.layout.HorizontalAlign#CENTER * @see feathers.layout.HorizontalAlign#RIGHT */ public function get horizontalAlign():String { return this._horizontalAlign; } /** * @private */ public function set horizontalAlign(value:String):void { if(this._horizontalAlign === value) { return; } this._horizontalAlign = value; this._lastGlobalBoundsOfOrigin = null; this.invalidate(INVALIDATION_FLAG_ORIGIN); } /** * @private */ protected var _verticalAlign:String = HorizontalAlign.CENTER; /** * The vertical alignment of the callout, relative to the origin. * * @default feathers.layout.VerticalAlign.MIDDLE * * @see feathers.layout.VerticalAlign#TOP * @see feathers.layout.VerticalAlign#MIDDLE * @see feathers.layout.VerticalAlign#BOTTOM */ public function get verticalAlign():String { return this._verticalAlign; } /** * @private */ public function set verticalAlign(value:String):void { if(this._verticalAlign === value) { return; } this._verticalAlign = value; this._lastGlobalBoundsOfOrigin = null; this.invalidate(INVALIDATION_FLAG_ORIGIN); } /** * Quickly sets all padding properties to the same value. The * padding getter always returns the value of * paddingTop, but the other padding values may be * different. * *

In the following example, the padding of all sides of the callout * is set to 20 pixels:

* * * callout.padding = 20; * * @default 0 * * @see #paddingTop * @see #paddingRight * @see #paddingBottom * @see #paddingLeft */ public function get padding():Number { return this._paddingTop; } /** * @private */ public function set padding(value:Number):void { this.paddingTop = value; this.paddingRight = value; this.paddingBottom = value; this.paddingLeft = value; } /** * @private */ protected var _paddingTop:Number = 0; /** * The minimum space, in pixels, between the callout's top edge and the * callout's content. * *

In the following example, the padding on the top edge of the * callout is set to 20 pixels:

* * * callout.paddingTop = 20; * * @default 0 */ public function get paddingTop():Number { return this._paddingTop; } /** * @private */ public function set paddingTop(value:Number):void { if(this._paddingTop == value) { return; } this._paddingTop = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _paddingRight:Number = 0; /** * The minimum space, in pixels, between the callout's right edge and * the callout's content. * *

In the following example, the padding on the right edge of the * callout is set to 20 pixels:

* * * callout.paddingRight = 20; * * @default 0 */ public function get paddingRight():Number { return this._paddingRight; } /** * @private */ public function set paddingRight(value:Number):void { if(this._paddingRight == value) { return; } this._paddingRight = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _paddingBottom:Number = 0; /** * The minimum space, in pixels, between the callout's bottom edge and * the callout's content. * *

In the following example, the padding on the bottom edge of the * callout is set to 20 pixels:

* * * callout.paddingBottom = 20; * * @default 0 */ public function get paddingBottom():Number { return this._paddingBottom; } /** * @private */ public function set paddingBottom(value:Number):void { if(this._paddingBottom == value) { return; } this._paddingBottom = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _paddingLeft:Number = 0; /** * The minimum space, in pixels, between the callout's left edge and the * callout's content. * *

In the following example, the padding on the left edge of the * callout is set to 20 pixels:

* * * callout.paddingLeft = 20; * * @default 0 */ public function get paddingLeft():Number { return this._paddingLeft; } /** * @private */ public function set paddingLeft(value:Number):void { if(this._paddingLeft == value) { return; } this._paddingLeft = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _arrowPosition:String = RelativePosition.TOP; [Inspectable(type="String",enumeration="top,right,bottom,left")] /** * The position of the callout's arrow relative to the callout's * background. If the callout's origin is set, this value * will be managed by the callout and may change automatically if the * origin moves to a new position or if the stage resizes. * *

The supportedDirections property is related to this * one, but they have different meanings and are usually opposites. For * example, a callout on the right side of its origin will generally * display its left arrow.

* *

If you use Callout.show() or set the origin * property manually, you should avoid manually modifying the * arrowPosition and arrowOffset properties.

* *

In the following example, the callout's arrow is positioned on the * left side:

* * * callout.arrowPosition = RelativePosition.LEFT; * * @default feathers.layout.RelativePosition.TOP * * @see feathers.layout.RelativePosition#TOP * @see feathers.layout.RelativePosition#RIGHT * @see feathers.layout.RelativePosition#BOTTOM * @see feathers.layout.RelativePosition#LEFT * * @see #origin * @see #supportedDirections * @see #arrowOffset */ public function get arrowPosition():String { return this._arrowPosition; } /** * @private */ public function set arrowPosition(value:String):void { if(this._arrowPosition == value) { return; } this._arrowPosition = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _backgroundSkin:DisplayObject; /** * The primary background to display. * *

In the following example, the callout's background is set to an image:

* * * callout.backgroundSkin = new Image( texture ); * * @default null */ public function get backgroundSkin():DisplayObject { return this._backgroundSkin; } /** * @private */ public function set backgroundSkin(value:DisplayObject):void { if(this._backgroundSkin == value) { return; } if(this._backgroundSkin !== null && this._backgroundSkin.parent === this) { this._backgroundSkin.removeFromParent(false); } this._backgroundSkin = value; if(this._backgroundSkin !== null) { this.addChildAt(this._backgroundSkin, 0); if(this._backgroundSkin is IMeasureDisplayObject) { var measureSkin:IMeasureDisplayObject = IMeasureDisplayObject(this._backgroundSkin); this._explicitBackgroundSkinWidth = measureSkin.explicitWidth; this._explicitBackgroundSkinHeight = measureSkin.explicitHeight; this._explicitBackgroundSkinMinWidth = measureSkin.explicitMinWidth; this._explicitBackgroundSkinMinHeight = measureSkin.explicitMinHeight; } else { this._explicitBackgroundSkinWidth = this._backgroundSkin.width; this._explicitBackgroundSkinHeight = this._backgroundSkin.height; this._explicitBackgroundSkinMinWidth = this._explicitBackgroundSkinWidth; this._explicitBackgroundSkinMinHeight = this._explicitBackgroundSkinHeight; } } this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var currentArrowSkin:DisplayObject; /** * @private */ protected var _bottomArrowSkin:DisplayObject; /** * The arrow skin to display on the bottom edge of the callout. This * arrow is displayed when the callout is displayed above the region it * points at. * *

In the following example, the callout's bottom arrow skin is set * to an image:

* * * callout.bottomArrowSkin = new Image( texture ); * * @default null */ public function get bottomArrowSkin():DisplayObject { return this._bottomArrowSkin; } /** * @private */ public function set bottomArrowSkin(value:DisplayObject):void { if(this._bottomArrowSkin == value) { return; } if(this._bottomArrowSkin !== null && this._bottomArrowSkin.parent === this) { this._bottomArrowSkin.removeFromParent(false); } this._bottomArrowSkin = value; if(this._bottomArrowSkin !== null) { this._bottomArrowSkin.visible = false; var index:int = this.getChildIndex(this._content); if(index < 0) { this.addChild(this._bottomArrowSkin); } else { this.addChildAt(this._bottomArrowSkin, index); } } this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _topArrowSkin:DisplayObject; /** * The arrow skin to display on the top edge of the callout. This arrow * is displayed when the callout is displayed below the region it points * at. * *

In the following example, the callout's top arrow skin is set * to an image:

* * * callout.topArrowSkin = new Image( texture ); * * @default null */ public function get topArrowSkin():DisplayObject { return this._topArrowSkin; } /** * @private */ public function set topArrowSkin(value:DisplayObject):void { if(this._topArrowSkin == value) { return; } if(this._topArrowSkin !== null && this._topArrowSkin.parent === this) { this._topArrowSkin.removeFromParent(false); } this._topArrowSkin = value; if(this._topArrowSkin !== null) { this._topArrowSkin.visible = false; var index:int = this.getChildIndex(this._content); if(index < 0) { this.addChild(this._topArrowSkin); } else { this.addChildAt(this._topArrowSkin, index); } } this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _leftArrowSkin:DisplayObject; /** * The arrow skin to display on the left edge of the callout. This arrow * is displayed when the callout is displayed to the right of the region * it points at. * *

In the following example, the callout's left arrow skin is set * to an image:

* * * callout.leftArrowSkin = new Image( texture ); * * @default null */ public function get leftArrowSkin():DisplayObject { return this._leftArrowSkin; } /** * @private */ public function set leftArrowSkin(value:DisplayObject):void { if(this._leftArrowSkin == value) { return; } if(this._leftArrowSkin !== null && this._leftArrowSkin.parent === this) { this._leftArrowSkin.removeFromParent(false); } this._leftArrowSkin = value; if(this._leftArrowSkin !== null) { this._leftArrowSkin.visible = false; var index:int = this.getChildIndex(this._content); if(index < 0) { this.addChild(this._leftArrowSkin); } else { this.addChildAt(this._leftArrowSkin, index); } } this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _rightArrowSkin:DisplayObject; /** * The arrow skin to display on the right edge of the callout. This * arrow is displayed when the callout is displayed to the left of the * region it points at. * *

In the following example, the callout's right arrow skin is set * to an image:

* * * callout.rightArrowSkin = new Image( texture ); * * @default null */ public function get rightArrowSkin():DisplayObject { return this._rightArrowSkin; } /** * @private */ public function set rightArrowSkin(value:DisplayObject):void { if(this._rightArrowSkin == value) { return; } if(this._rightArrowSkin !== null && this._rightArrowSkin.parent === this) { this._rightArrowSkin.removeFromParent(false); } this._rightArrowSkin = value; if(this._rightArrowSkin !== null) { this._rightArrowSkin.visible = false; var index:int = this.getChildIndex(this._content); if(index < 0) { this.addChild(this._rightArrowSkin); } else { this.addChildAt(this._rightArrowSkin, index); } } this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _topArrowGap:Number = 0; /** * The space, in pixels, between the top arrow skin and the background * skin. To have the arrow overlap the background, you may use a * negative gap value. * *

In the following example, the gap between the callout and its * top arrow is set to -4 pixels (perhaps to hide a border on the * callout's background):

* * * callout.topArrowGap = -4; * * @default 0 */ public function get topArrowGap():Number { return this._topArrowGap; } /** * @private */ public function set topArrowGap(value:Number):void { if(this._topArrowGap == value) { return; } this._topArrowGap = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _bottomArrowGap:Number = 0; /** * The space, in pixels, between the bottom arrow skin and the * background skin. To have the arrow overlap the background, you may * use a negative gap value. * *

In the following example, the gap between the callout and its * bottom arrow is set to -4 pixels (perhaps to hide a border on the * callout's background):

* * * callout.bottomArrowGap = -4; * * @default 0 */ public function get bottomArrowGap():Number { return this._bottomArrowGap; } /** * @private */ public function set bottomArrowGap(value:Number):void { if(this._bottomArrowGap == value) { return; } this._bottomArrowGap = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _rightArrowGap:Number = 0; /** * The space, in pixels, between the right arrow skin and the background * skin. To have the arrow overlap the background, you may use a * negative gap value. * *

In the following example, the gap between the callout and its * right arrow is set to -4 pixels (perhaps to hide a border on the * callout's background):

* * * callout.rightArrowGap = -4; * * @default 0 */ public function get rightArrowGap():Number { return this._rightArrowGap; } /** * @private */ public function set rightArrowGap(value:Number):void { if(this._rightArrowGap == value) { return; } this._rightArrowGap = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _leftArrowGap:Number = 0; /** * The space, in pixels, between the right arrow skin and the background * skin. To have the arrow overlap the background, you may use a * negative gap value. * *

In the following example, the gap between the callout and its * left arrow is set to -4 pixels (perhaps to hide a border on the * callout's background):

* * * callout.leftArrowGap = -4; * * @default 0 */ public function get leftArrowGap():Number { return this._leftArrowGap; } /** * @private */ public function set leftArrowGap(value:Number):void { if(this._leftArrowGap == value) { return; } this._leftArrowGap = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _arrowOffset:Number = 0; /** * The offset, in pixels, of the arrow skin from the horizontal center * or vertical middle of the background skin, depending on the position * of the arrow (which side it is on). This value is used to point at * the callout's origin when the callout is not perfectly centered * relative to the origin. * *

On the top and bottom edges, the arrow will move left for negative * values of arrowOffset and right for positive values. On * the left and right edges, the arrow will move up for negative values * and down for positive values.

* *

If you use Callout.show() or set the origin * property manually, you should avoid manually modifying the * arrowPosition and arrowOffset properties.

* *

In the following example, the arrow offset is set to 20 pixels:

* * * callout.arrowOffset = 20; * * @default 0 * * @see #arrowPosition * @see #origin */ public function get arrowOffset():Number { return this._arrowOffset; } /** * @private */ public function set arrowOffset(value:Number):void { if(this._arrowOffset == value) { return; } this._arrowOffset = value; this.invalidate(INVALIDATION_FLAG_STYLES); } /** * @private */ protected var _lastGlobalBoundsOfOrigin:Rectangle; /** * @private */ protected var _ignoreContentResize:Boolean = false; /** * @private */ override public function dispose():void { this.origin = null; var savedContent:DisplayObject = this._content; this.content = null; //remove the content safely if it should not be disposed if(savedContent !== null && this.disposeContent) { savedContent.dispose(); } super.dispose(); } /** * Closes the callout. */ public function close(dispose:Boolean = false):void { if(this.parent) { //don't dispose here because we need to keep the event listeners //when dispatching Event.CLOSE. we'll dispose after that. this.removeFromParent(false); this.dispatchEventWith(Event.CLOSE); } if(dispose) { this.dispose(); } } /** * @private */ override protected function initialize():void { this.addEventListener(Event.REMOVED_FROM_STAGE, callout_removedFromStageHandler); } /** * @private */ override protected function draw():void { var dataInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_DATA); var sizeInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_SIZE); var stateInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_STATE); var stylesInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_STYLES); var originInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_ORIGIN); if(sizeInvalid) { this._lastGlobalBoundsOfOrigin = null; originInvalid = true; } if(originInvalid) { this.positionRelativeToOrigin(); } if(stylesInvalid || stateInvalid) { this.refreshArrowSkin(); } if(stateInvalid || dataInvalid) { this.refreshEnabled(); } sizeInvalid = this.autoSizeIfNeeded() || sizeInvalid; this.layoutChildren(); } /** * If the component's dimensions have not been set explicitly, it will * measure its content and determine an ideal size for itself. If the * explicitWidth or explicitHeight member * variables are set, those value will be used without additional * measurement. If one is set, but not the other, the dimension with the * explicit value will not be measured, but the other non-explicit * dimension will still need measurement. * *

Calls saveMeasurements() to set up the * actualWidth and actualHeight member * variables used for layout.

* *

Meant for internal use, and subclasses may override this function * with a custom implementation.

*/ protected function autoSizeIfNeeded():Boolean { return this.measureWithArrowPosition(this._arrowPosition); } /** * @private */ protected function measureWithArrowPosition(arrowPosition:String):Boolean { var needsWidth:Boolean = this._explicitWidth !== this._explicitWidth; //isNaN var needsHeight:Boolean = this._explicitHeight !== this._explicitHeight; //isNaN var needsMinWidth:Boolean = this._explicitMinWidth !== this._explicitMinWidth; //isNaN var needsMinHeight:Boolean = this._explicitMinHeight !== this._explicitMinHeight; //isNaN if(!needsWidth && !needsHeight && !needsMinWidth && !needsMinHeight) { return false; } if(this._backgroundSkin !== null) { var oldBackgroundWidth:Number = this._backgroundSkin.width; var oldBackgroundHeight:Number = this._backgroundSkin.height; } var measureBackground:IMeasureDisplayObject = this._backgroundSkin as IMeasureDisplayObject; resetFluidChildDimensionsForMeasurement(this._backgroundSkin, this._explicitWidth, this._explicitHeight, this._explicitMinWidth, this._explicitMinHeight, this._explicitBackgroundSkinWidth, this._explicitBackgroundSkinHeight, this._explicitBackgroundSkinMinWidth, this._explicitBackgroundSkinMinHeight); if(this._backgroundSkin is IValidating) { IValidating(this._backgroundSkin).validate(); } var leftOrRightArrowWidth:Number = 0; var leftOrRightArrowHeight:Number = 0; if(arrowPosition === RelativePosition.LEFT && this._leftArrowSkin !== null) { leftOrRightArrowWidth = this._leftArrowSkin.width + this._leftArrowGap; leftOrRightArrowHeight = this._leftArrowSkin.height; } else if(arrowPosition === RelativePosition.RIGHT && this._rightArrowSkin !== null) { leftOrRightArrowWidth = this._rightArrowSkin.width + this._rightArrowGap; leftOrRightArrowHeight = this._rightArrowSkin.height; } var topOrBottomArrowWidth:Number = 0; var topOrBottomArrowHeight:Number = 0; if(arrowPosition === RelativePosition.TOP && this._topArrowSkin !== null) { topOrBottomArrowWidth = this._topArrowSkin.width; topOrBottomArrowHeight = this._topArrowSkin.height + this._topArrowGap; } else if(arrowPosition === RelativePosition.BOTTOM && this._bottomArrowSkin !== null) { topOrBottomArrowWidth = this._bottomArrowSkin.width; topOrBottomArrowHeight = this._bottomArrowSkin.height + this._bottomArrowGap; } //the content resizes when the callout resizes, so we can treat it //similarly to a background skin var measureContent:IMeasureDisplayObject = this._content as IMeasureDisplayObject; resetFluidChildDimensionsForMeasurement(this._content, this._explicitWidth - leftOrRightArrowWidth - this._paddingLeft - this._paddingRight, this._explicitHeight - topOrBottomArrowHeight - this._paddingTop - this._paddingBottom, this._explicitMinWidth - leftOrRightArrowWidth - this._paddingLeft - this._paddingRight, this._explicitMinHeight - topOrBottomArrowHeight - this._paddingTop - this._paddingBottom, this._explicitContentWidth, this._explicitContentHeight, this._explicitContentMinWidth, this._explicitContentMinHeight); if(this._content is IValidating) { IValidating(this._content).validate(); } //the dimensions of the stage (plus stage padding) affect the //maximum width and height var maxWidth:Number = this._maxWidth; var maxHeight:Number = this._maxHeight; if(this.stage !== null) { var stageMaxWidth:Number = this.stage.stageWidth - stagePaddingLeft - stagePaddingRight; if(maxWidth > stageMaxWidth) { maxWidth = stageMaxWidth; } var stageMaxHeight:Number = this.stage.stageHeight - stagePaddingTop - stagePaddingBottom; if(maxHeight > stageMaxHeight) { maxHeight = stageMaxHeight; } } var newWidth:Number = this._explicitWidth; if(needsWidth) { var contentWidth:Number = 0; if(this._content !== null) { contentWidth = this._content.width; } if(topOrBottomArrowWidth > contentWidth) { contentWidth = topOrBottomArrowWidth; } newWidth = contentWidth + this._paddingLeft + this._paddingRight; var backgroundWidth:Number = 0; if(this._backgroundSkin !== null) { backgroundWidth = this._backgroundSkin.width; } if(backgroundWidth > newWidth) { newWidth = backgroundWidth; } newWidth += leftOrRightArrowWidth; if(newWidth > maxWidth) { newWidth = maxWidth; } } var newHeight:Number = this._explicitHeight; if(needsHeight) { var contentHeight:Number = 0; if(this._content !== null) { contentHeight = this._content.height; } if(leftOrRightArrowHeight > contentWidth) { contentHeight = leftOrRightArrowHeight; } newHeight = contentHeight + this._paddingTop + this._paddingBottom; var backgroundHeight:Number = 0; if(this._backgroundSkin !== null) { backgroundHeight = this._backgroundSkin.height; } if(backgroundHeight > newHeight) { newHeight = backgroundHeight; } newHeight += topOrBottomArrowHeight; if(newHeight > maxHeight) { newHeight = maxHeight; } } var newMinWidth:Number = this._explicitMinWidth; if(needsMinWidth) { var contentMinWidth:Number = 0; if(measureContent !== null) { contentMinWidth = measureContent.minWidth; } else if(this._content !== null) { contentMinWidth = this._content.width; } if(topOrBottomArrowWidth > contentMinWidth) { contentMinWidth = topOrBottomArrowWidth; } newMinWidth = contentMinWidth + this._paddingLeft + this._paddingRight; var backgroundMinWidth:Number = 0; if(measureBackground !== null) { backgroundMinWidth = measureBackground.minWidth; } else if(this._backgroundSkin !== null) { backgroundMinWidth = this._backgroundSkin.width; } if(backgroundMinWidth > newMinWidth) { newMinWidth = backgroundMinWidth; } newMinWidth += leftOrRightArrowWidth; if(newMinWidth > maxWidth) { newMinWidth = maxWidth; } } var newMinHeight:Number = this._explicitHeight; if(needsMinHeight) { var contentMinHeight:Number = 0; if(measureContent !== null) { contentMinHeight = measureContent.minHeight; } else if(this._content !== null) { contentMinHeight = this._content.height; } if(leftOrRightArrowHeight > contentMinHeight) { contentMinHeight = leftOrRightArrowHeight; } newMinHeight = contentMinHeight + this._paddingTop + this._paddingBottom; var backgroundMinHeight:Number = 0; if(measureBackground !== null) { backgroundMinHeight = measureBackground.minHeight; } else if(this._backgroundSkin !== null) { backgroundMinHeight = this._backgroundSkin.height; } if(backgroundMinHeight > newMinHeight) { newMinHeight = backgroundMinHeight; } newMinHeight += topOrBottomArrowHeight; if(newMinHeight > maxHeight) { newMinHeight = maxHeight; } } if(this._backgroundSkin !== null) { this._backgroundSkin.width = oldBackgroundWidth; this._backgroundSkin.height = oldBackgroundHeight; } return this.saveMeasurements(newWidth, newHeight, newMinWidth, newMinHeight); } /** * @private */ protected function refreshArrowSkin():void { this.currentArrowSkin = null; if(this._arrowPosition == RelativePosition.BOTTOM) { this.currentArrowSkin = this._bottomArrowSkin; } else if(this._bottomArrowSkin) { this._bottomArrowSkin.visible = false; } if(this._arrowPosition == RelativePosition.TOP) { this.currentArrowSkin = this._topArrowSkin; } else if(this._topArrowSkin) { this._topArrowSkin.visible = false; } if(this._arrowPosition == RelativePosition.LEFT) { this.currentArrowSkin = this._leftArrowSkin; } else if(this._leftArrowSkin) { this._leftArrowSkin.visible = false; } if(this._arrowPosition == RelativePosition.RIGHT) { this.currentArrowSkin = this._rightArrowSkin; } else if(this._rightArrowSkin) { this._rightArrowSkin.visible = false; } if(this.currentArrowSkin) { this.currentArrowSkin.visible = true; } } /** * @private */ protected function refreshEnabled():void { if(this._content is IFeathersControl) { IFeathersControl(this._content).isEnabled = this._isEnabled; } } /** * @private */ protected function layoutChildren():void { var xPosition:Number = 0; if(this._leftArrowSkin !== null && this._arrowPosition === RelativePosition.LEFT) { xPosition = this._leftArrowSkin.width + this._leftArrowGap; } var yPosition:Number = 0; if(this._topArrowSkin !== null && this._arrowPosition === RelativePosition.TOP) { yPosition = this._topArrowSkin.height + this._topArrowGap; } var widthOffset:Number = 0; if(this._rightArrowSkin !== null && this._arrowPosition === RelativePosition.RIGHT) { widthOffset = this._rightArrowSkin.width + this._rightArrowGap; } var heightOffset:Number = 0; if(this._bottomArrowSkin !== null && this._arrowPosition === RelativePosition.BOTTOM) { heightOffset = this._bottomArrowSkin.height + this._bottomArrowGap; } var backgroundWidth:Number = this.actualWidth - xPosition - widthOffset; var backgroundHeight:Number = this.actualHeight - yPosition - heightOffset; if(this._backgroundSkin !== null) { this._backgroundSkin.x = xPosition; this._backgroundSkin.y = yPosition; this._backgroundSkin.width = backgroundWidth; this._backgroundSkin.height = backgroundHeight; } if(this.currentArrowSkin !== null) { var contentWidth:Number = backgroundWidth - this._paddingLeft - this._paddingRight; var contentHeight:Number = backgroundHeight - this._paddingTop - this._paddingBottom; if(this._arrowPosition === RelativePosition.LEFT) { this._leftArrowSkin.x = xPosition - this._leftArrowSkin.width - this._leftArrowGap; var leftArrowSkinY:Number = this._arrowOffset + yPosition + this._paddingTop; if(this._verticalAlign === VerticalAlign.MIDDLE) { leftArrowSkinY += Math.round((contentHeight - this._leftArrowSkin.height) / 2); } else if(this._verticalAlign === VerticalAlign.BOTTOM) { leftArrowSkinY += (contentHeight - this._leftArrowSkin.height); } var minLeftArrowSkinY:Number = yPosition + this._paddingTop; if(minLeftArrowSkinY > leftArrowSkinY) { leftArrowSkinY = minLeftArrowSkinY; } else { var maxLeftArrowSkinY:Number = yPosition + this._paddingTop + contentHeight - this._leftArrowSkin.height; if(maxLeftArrowSkinY < leftArrowSkinY) { leftArrowSkinY = maxLeftArrowSkinY; } } this._leftArrowSkin.y = leftArrowSkinY; } else if(this._arrowPosition === RelativePosition.RIGHT) { this._rightArrowSkin.x = xPosition + backgroundWidth + this._rightArrowGap; var rightArrowSkinY:Number = this._arrowOffset + yPosition + this._paddingTop; if(this._verticalAlign === VerticalAlign.MIDDLE) { rightArrowSkinY += Math.round((contentHeight - this._rightArrowSkin.height) / 2); } else if(this._verticalAlign === VerticalAlign.BOTTOM) { rightArrowSkinY += (contentHeight - this._rightArrowSkin.height); } var minRightArrowSkinY:Number = yPosition + this._paddingTop; if(minRightArrowSkinY > rightArrowSkinY) { rightArrowSkinY = minRightArrowSkinY; } else { var maxRightArrowSkinY:Number = yPosition + this._paddingTop + contentHeight - this._rightArrowSkin.height; if(maxRightArrowSkinY < rightArrowSkinY) { rightArrowSkinY = maxRightArrowSkinY; } } this._rightArrowSkin.y = rightArrowSkinY; } else if(this._arrowPosition === RelativePosition.BOTTOM) { var bottomArrowSkinX:Number = this._arrowOffset + xPosition + this._paddingLeft; if(this._horizontalAlign === HorizontalAlign.CENTER) { bottomArrowSkinX += Math.round((contentWidth - this._bottomArrowSkin.width) / 2); } else if(this._horizontalAlign === HorizontalAlign.RIGHT) { bottomArrowSkinX += (contentWidth - this._bottomArrowSkin.width); } var minBottomArrowSkinX:Number = xPosition + this._paddingLeft; if(minBottomArrowSkinX > bottomArrowSkinX) { bottomArrowSkinX = minBottomArrowSkinX; } else { var maxBottomArrowSkinX:Number = xPosition + this._paddingLeft + contentWidth - this._bottomArrowSkin.width; if(maxBottomArrowSkinX < bottomArrowSkinX) { bottomArrowSkinX = maxBottomArrowSkinX; } } this._bottomArrowSkin.x = bottomArrowSkinX; this._bottomArrowSkin.y = yPosition + backgroundHeight + this._bottomArrowGap; } else //top { var topArrowSkinX:Number = this._arrowOffset + xPosition + this._paddingLeft; if(this._horizontalAlign === HorizontalAlign.CENTER) { topArrowSkinX += Math.round((contentWidth - this._topArrowSkin.width) / 2); } else if(this._horizontalAlign === HorizontalAlign.RIGHT) { topArrowSkinX += (contentWidth - this._topArrowSkin.width); } var minTopArrowSkinX:Number = xPosition + this._paddingLeft; if(minTopArrowSkinX > topArrowSkinX) { topArrowSkinX = minTopArrowSkinX; } else { var maxTopArrowSkinX:Number = xPosition + this._paddingLeft + contentWidth - this._topArrowSkin.width; if(maxTopArrowSkinX < topArrowSkinX) { topArrowSkinX = maxTopArrowSkinX; } } this._topArrowSkin.x = topArrowSkinX; this._topArrowSkin.y = yPosition - this._topArrowSkin.height - this._topArrowGap; } } if(this._content !== null) { this._content.x = xPosition + this._paddingLeft; this._content.y = yPosition + this._paddingTop; var oldIgnoreContentResize:Boolean = this._ignoreContentResize; this._ignoreContentResize = true; this._content.width = backgroundWidth - this._paddingLeft - this._paddingRight; this._content.height = backgroundHeight - this._paddingTop - this._paddingBottom; this._ignoreContentResize = oldIgnoreContentResize; } } /** * @private */ protected function positionRelativeToOrigin():void { if(this._origin === null) { return; } this._origin.getBounds(Starling.current.stage, HELPER_RECT); var hasGlobalBounds:Boolean = this._lastGlobalBoundsOfOrigin != null; if(hasGlobalBounds && this._lastGlobalBoundsOfOrigin.equals(HELPER_RECT)) { return; } if(!hasGlobalBounds) { this._lastGlobalBoundsOfOrigin = new Rectangle(); } this._lastGlobalBoundsOfOrigin.x = HELPER_RECT.x; this._lastGlobalBoundsOfOrigin.y = HELPER_RECT.y; this._lastGlobalBoundsOfOrigin.width = HELPER_RECT.width; this._lastGlobalBoundsOfOrigin.height = HELPER_RECT.height; var supportedPositions:Vector. = this._supportedPositions; if(supportedPositions === null) { supportedPositions = DEFAULT_POSITIONS; } var upSpace:Number = -1; var rightSpace:Number = -1; var downSpace:Number = -1; var leftSpace:Number = -1; var positionsCount:int = supportedPositions.length; for(var i:int = 0; i < positionsCount; i++) { var position:String = supportedPositions[i]; switch(position) { case RelativePosition.TOP: { //arrow is opposite, on bottom side this.measureWithArrowPosition(RelativePosition.BOTTOM); upSpace = this._lastGlobalBoundsOfOrigin.y - this.actualHeight; if(upSpace >= stagePaddingTop) { positionAboveOrigin(this, this._lastGlobalBoundsOfOrigin); return; } if(upSpace < 0) { upSpace = 0; } break; } case RelativePosition.RIGHT: { //arrow is opposite, on left side this.measureWithArrowPosition(RelativePosition.LEFT); rightSpace = (Starling.current.stage.stageWidth - actualWidth) - (this._lastGlobalBoundsOfOrigin.x + this._lastGlobalBoundsOfOrigin.width); if(rightSpace >= stagePaddingRight) { positionToRightOfOrigin(this, this._lastGlobalBoundsOfOrigin); return; } if(rightSpace < 0) { rightSpace = 0; } break; } case RelativePosition.LEFT: { this.measureWithArrowPosition(RelativePosition.RIGHT); leftSpace = this._lastGlobalBoundsOfOrigin.x - this.actualWidth; if(leftSpace >= stagePaddingLeft) { positionToLeftOfOrigin(this, this._lastGlobalBoundsOfOrigin); return; } if(leftSpace < 0) { leftSpace = 0; } break; } default: //bottom { //arrow is opposite, on top side this.measureWithArrowPosition(RelativePosition.TOP); downSpace = (Starling.current.stage.stageHeight - this.actualHeight) - (this._lastGlobalBoundsOfOrigin.y + this._lastGlobalBoundsOfOrigin.height); if(downSpace >= stagePaddingBottom) { positionBelowOrigin(this, this._lastGlobalBoundsOfOrigin); return; } if(downSpace < 0) { downSpace = 0; } } } } //worst case: pick the side that has the most available space if(downSpace !== -1 && downSpace >= upSpace && downSpace >= rightSpace && downSpace >= leftSpace) { positionBelowOrigin(this, this._lastGlobalBoundsOfOrigin); } else if(upSpace !== -1 && upSpace >= rightSpace && upSpace >= leftSpace) { positionAboveOrigin(this, this._lastGlobalBoundsOfOrigin); } else if(rightSpace !== -1 && rightSpace >= leftSpace) { positionToRightOfOrigin(this, this._lastGlobalBoundsOfOrigin); } else { positionToLeftOfOrigin(this, this._lastGlobalBoundsOfOrigin); } } /** * @private */ protected function callout_addedToStageHandler(event:Event):void { //using priority here is a hack so that objects higher up in the //display list have a chance to cancel the event first. var priority:int = -getDisplayObjectDepthFromStage(this); Starling.current.nativeStage.addEventListener(KeyboardEvent.KEY_DOWN, callout_nativeStage_keyDownHandler, false, priority, true); this.stage.addEventListener(TouchEvent.TOUCH, stage_touchHandler); //to avoid touch events bubbling up to the callout and causing it to //close immediately, we wait one frame before allowing it to close //based on touches. this._isReadyToClose = false; this.addEventListener(EnterFrameEvent.ENTER_FRAME, callout_oneEnterFrameHandler); } /** * @private */ protected function callout_removedFromStageHandler(event:Event):void { this.stage.removeEventListener(TouchEvent.TOUCH, stage_touchHandler); Starling.current.nativeStage.removeEventListener(KeyboardEvent.KEY_DOWN, callout_nativeStage_keyDownHandler); } /** * @private */ protected function callout_oneEnterFrameHandler(event:Event):void { this.removeEventListener(EnterFrameEvent.ENTER_FRAME, callout_oneEnterFrameHandler); this._isReadyToClose = true; } /** * @private */ protected function callout_enterFrameHandler(event:EnterFrameEvent):void { this.positionRelativeToOrigin(); } /** * @private */ protected function stage_touchHandler(event:TouchEvent):void { var target:DisplayObject = DisplayObject(event.target); if(!this._isReadyToClose || (!this.closeOnTouchEndedOutside && !this.closeOnTouchBeganOutside) || this.contains(target) || (PopUpManager.isPopUp(this) && !PopUpManager.isTopLevelPopUp(this))) { return; } if(this._origin == target || (this._origin is DisplayObjectContainer && DisplayObjectContainer(this._origin).contains(target))) { return; } if(this.closeOnTouchBeganOutside) { var touch:Touch = event.getTouch(this.stage, TouchPhase.BEGAN); if(touch) { this.close(this.disposeOnSelfClose); return; } } if(this.closeOnTouchEndedOutside) { touch = event.getTouch(this.stage, TouchPhase.ENDED); if(touch) { this.close(this.disposeOnSelfClose); return; } } } /** * @private */ protected function callout_nativeStage_keyDownHandler(event:KeyboardEvent):void { if(event.isDefaultPrevented()) { //someone else already handled this one return; } if(!this.closeOnKeys || this.closeOnKeys.indexOf(event.keyCode) < 0) { return; } //don't let the OS handle the event event.preventDefault(); this.close(this.disposeOnSelfClose); } /** * @private */ protected function origin_removedFromStageHandler(event:Event):void { this.close(this.disposeOnSelfClose); } /** * @private */ protected function content_resizeHandler(event:Event):void { if(this._ignoreContentResize) { return; } this.invalidate(INVALIDATION_FLAG_SIZE); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy