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-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.controls
{
	import feathers.core.FeathersControl;
	import feathers.core.IFeathersControl;
	import feathers.core.IValidating;
	import feathers.core.PopUpManager;
	import feathers.events.FeathersEventType;
	import feathers.layout.RelativePosition;
	import feathers.skins.IStyleProvider;
	import feathers.utils.display.getDisplayObjectDepthFromStage;

	import flash.events.KeyboardEvent;
	import flash.geom.Point;
	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 callout may be positioned on any side of the origin region. * * @see #supportedDirections */ public static const DIRECTION_ANY:String = "any"; /** * The callout may be positioned on top or bottom of the origin region. * * @see #supportedDirections */ public static const DIRECTION_VERTICAL:String = "vertical"; /** * The callout may be positioned on top or bottom of the origin region. * * @see #supportedDirections */ public static const DIRECTION_HORIZONTAL:String = "horizontal"; /** * The callout must be positioned above the origin region. * * @see #supportedDirections */ public static const DIRECTION_UP:String = "up"; /** * The callout must be positioned below the origin region. * * @see #supportedDirections */ public static const DIRECTION_DOWN:String = "down"; /** * The callout must be positioned to the left side of the origin region. * * @see #supportedDirections */ public static const DIRECTION_LEFT:String = "left"; /** * The callout must be positioned to the right side of the origin region. * * @see #supportedDirections */ 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 */ private static const HELPER_POINT:Point = new Point(); /** * @private */ protected static const DIRECTION_TO_FUNCTION:Object = {}; DIRECTION_TO_FUNCTION[DIRECTION_ANY] = positionBestSideOfOrigin; DIRECTION_TO_FUNCTION[DIRECTION_UP] = positionAboveOrigin; DIRECTION_TO_FUNCTION[DIRECTION_DOWN] = positionBelowOrigin; DIRECTION_TO_FUNCTION[DIRECTION_LEFT] = positionToLeftOfOrigin; DIRECTION_TO_FUNCTION[DIRECTION_RIGHT] = positionToRightOfOrigin; DIRECTION_TO_FUNCTION[DIRECTION_VERTICAL] = positionAboveOrBelowOrigin; DIRECTION_TO_FUNCTION[DIRECTION_HORIZONTAL] = positionToLeftOrRightOfOrigin; /** * @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. * *

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, supportedDirections:String = DIRECTION_ANY, isModal:Boolean = true, customCalloutFactory:Function = null, customOverlayFactory:Function = null):Callout { if(!origin.stage) { throw new ArgumentError("Callout origin must be added to the stage."); } var factory:Function = customCalloutFactory; if(factory == null) { factory = calloutFactory != null ? calloutFactory : defaultCalloutFactory; } var callout:Callout = Callout(factory()); callout.content = content; callout.supportedDirections = supportedDirections; callout.origin = origin; factory = customOverlayFactory; if(factory == null) { factory = calloutOverlayFactory != null ? calloutOverlayFactory : 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 positionWithSupportedDirections(callout:Callout, globalOrigin:Rectangle, direction:String):void { if(DIRECTION_TO_FUNCTION.hasOwnProperty(direction)) { var calloutPositionFunction:Function = DIRECTION_TO_FUNCTION[direction]; calloutPositionFunction(callout, globalOrigin); } else { positionBestSideOfOrigin(callout, globalOrigin); } } /** * @private */ protected static function positionBestSideOfOrigin(callout:Callout, globalOrigin:Rectangle):void { callout.measureWithArrowPosition(RelativePosition.TOP, HELPER_POINT); var downSpace:Number = (Starling.current.stage.stageHeight - HELPER_POINT.y) - (globalOrigin.y + globalOrigin.height); if(downSpace >= stagePaddingBottom) { positionBelowOrigin(callout, globalOrigin); return; } callout.measureWithArrowPosition(RelativePosition.BOTTOM, HELPER_POINT); var upSpace:Number = globalOrigin.y - HELPER_POINT.y; if(upSpace >= stagePaddingTop) { positionAboveOrigin(callout, globalOrigin); return; } callout.measureWithArrowPosition(RelativePosition.LEFT, HELPER_POINT); var rightSpace:Number = (Starling.current.stage.stageWidth - HELPER_POINT.x) - (globalOrigin.x + globalOrigin.width); if(rightSpace >= stagePaddingRight) { positionToRightOfOrigin(callout, globalOrigin); return; } callout.measureWithArrowPosition(RelativePosition.RIGHT, HELPER_POINT); var leftSpace:Number = globalOrigin.x - HELPER_POINT.x; if(leftSpace >= stagePaddingLeft) { positionToLeftOfOrigin(callout, globalOrigin); return; } //worst case: pick the side that has the most available space if(downSpace >= upSpace && downSpace >= rightSpace && downSpace >= leftSpace) { positionBelowOrigin(callout, globalOrigin); } else if(upSpace >= rightSpace && upSpace >= leftSpace) { positionAboveOrigin(callout, globalOrigin); } else if(rightSpace >= leftSpace) { positionToRightOfOrigin(callout, globalOrigin); } else { positionToLeftOfOrigin(callout, globalOrigin); } } /** * @private */ protected static function positionAboveOrBelowOrigin(callout:Callout, globalOrigin:Rectangle):void { callout.measureWithArrowPosition(RelativePosition.TOP, HELPER_POINT); var downSpace:Number = (Starling.current.stage.stageHeight - HELPER_POINT.y) - (globalOrigin.y + globalOrigin.height); if(downSpace >= stagePaddingBottom) { positionBelowOrigin(callout, globalOrigin); return; } callout.measureWithArrowPosition(RelativePosition.BOTTOM, HELPER_POINT); var upSpace:Number = globalOrigin.y - HELPER_POINT.y; if(upSpace >= stagePaddingTop) { positionAboveOrigin(callout, globalOrigin); return; } //worst case: pick the side that has the most available space if(downSpace >= upSpace) { positionBelowOrigin(callout, globalOrigin); } else { positionAboveOrigin(callout, globalOrigin); } } /** * @private */ protected static function positionToLeftOrRightOfOrigin(callout:Callout, globalOrigin:Rectangle):void { callout.measureWithArrowPosition(RelativePosition.LEFT, HELPER_POINT); var rightSpace:Number = (Starling.current.stage.stageWidth - HELPER_POINT.x) - (globalOrigin.x + globalOrigin.width); if(rightSpace >= stagePaddingRight) { positionToRightOfOrigin(callout, globalOrigin); return; } callout.measureWithArrowPosition(RelativePosition.RIGHT, HELPER_POINT); var leftSpace:Number = globalOrigin.x - HELPER_POINT.x; if(leftSpace >= stagePaddingLeft) { positionToLeftOfOrigin(callout, globalOrigin); return; } //worst case: pick the side that has the most available space if(rightSpace >= leftSpace) { positionToRightOfOrigin(callout, globalOrigin); } else { positionToLeftOfOrigin(callout, globalOrigin); } } /** * @private */ protected static function positionBelowOrigin(callout:Callout, globalOrigin:Rectangle):void { callout.measureWithArrowPosition(RelativePosition.TOP, HELPER_POINT); var idealXPosition:Number = globalOrigin.x + Math.round((globalOrigin.width - HELPER_POINT.x) / 2); var xPosition:Number = Math.max(stagePaddingLeft, Math.min(Starling.current.stage.stageWidth - HELPER_POINT.x - stagePaddingRight, idealXPosition)); 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, HELPER_POINT); var idealXPosition:Number = globalOrigin.x + Math.round((globalOrigin.width - HELPER_POINT.x) / 2); var xPosition:Number = Math.max(stagePaddingLeft, Math.min(Starling.current.stage.stageWidth - HELPER_POINT.x - stagePaddingRight, idealXPosition)); callout.x = xPosition; callout.y = globalOrigin.y - HELPER_POINT.y; 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, HELPER_POINT); callout.x = globalOrigin.x + globalOrigin.width; var idealYPosition:Number = globalOrigin.y + Math.round((globalOrigin.height - HELPER_POINT.y) / 2); var yPosition:Number = Math.max(stagePaddingTop, Math.min(Starling.current.stage.stageHeight - HELPER_POINT.y - stagePaddingBottom, idealYPosition)); 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, HELPER_POINT); callout.x = globalOrigin.x - HELPER_POINT.x; var idealYPosition:Number = globalOrigin.y + Math.round((globalOrigin.height - HELPER_POINT.y) / 2); var yPosition:Number = Math.max(stagePaddingLeft, Math.min(Starling.current.stage.stageHeight - HELPER_POINT.y - stagePaddingBottom, idealYPosition)); 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 _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) { 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) { if(this._content is IFeathersControl) { IFeathersControl(this._content).addEventListener(FeathersEventType.RESIZE, content_resizeHandler); } this.addChild(this._content); } 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 = DIRECTION_ANY; [Inspectable(type="String",enumeration="any,vertical,horizontal,up,down,left,right")] /** * The directions that the callout may be positioned, relative to its * origin. If the callout's origin is not set, this value will be * ignored. * *

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.

* *

In the following example, the callout's supported directions are * restricted to up and down:

* * * callout.supportedDirections = Callout.DIRECTION_VERTICAL; * * @default Callout.DIRECTION_ANY * * @see #origin * @see #DIRECTION_ANY * @see #DIRECTION_VERTICAL * @see #DIRECTION_HORIZONTAL * @see #DIRECTION_UP * @see #DIRECTION_DOWN * @see #DIRECTION_LEFT * @see #DIRECTION_RIGHT * @see #arrowPosition */ public function get supportedDirections():String { return this._supportedDirections; } public function set supportedDirections(value:String):void { this._supportedDirections = value; } /** * 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 _originalBackgroundWidth:Number = NaN; /** * @private */ protected var _originalBackgroundHeight:Number = NaN; /** * @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) { this.removeChild(this._backgroundSkin); } this._backgroundSkin = value; if(this._backgroundSkin) { this._originalBackgroundWidth = this._backgroundSkin.width; this._originalBackgroundHeight = this._backgroundSkin.height; this.addChildAt(this._backgroundSkin, 0); } 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) { this.removeChild(this._bottomArrowSkin); } this._bottomArrowSkin = value; if(this._bottomArrowSkin) { 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) { this.removeChild(this._topArrowSkin); } this._topArrowSkin = value; if(this._topArrowSkin) { 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) { this.removeChild(this._leftArrowSkin); } this._leftArrowSkin = value; if(this._leftArrowSkin) { 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) { this.removeChild(this._rightArrowSkin); } this._rightArrowSkin = value; if(this._rightArrowSkin) { 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 && 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.positionToOrigin(); } if(stylesInvalid || stateInvalid) { this.refreshArrowSkin(); } if(stateInvalid) { if(this._content is IFeathersControl) { IFeathersControl(this._content).isEnabled = this._isEnabled; } } sizeInvalid = this.autoSizeIfNeeded() || sizeInvalid; if(sizeInvalid || stylesInvalid || dataInvalid || stateInvalid) { 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 setSizeInternal() 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 { this.measureWithArrowPosition(this._arrowPosition, HELPER_POINT); return this.setSizeInternal(HELPER_POINT.x, HELPER_POINT.y, false); } /** * @private */ protected function measureWithArrowPosition(arrowPosition:String, result:Point = null):Point { if(!result) { result = new Point(); } var needsWidth:Boolean = this._explicitWidth !== this._explicitWidth; //isNaN var needsHeight:Boolean = this._explicitHeight !== this._explicitHeight; //isNaN if(!needsWidth && !needsHeight) { result.x = this._explicitWidth; result.y = this._explicitHeight; return result; } var minWidth:Number = this._explicitMinWidth; var maxWidth:Number = this._maxWidth; var minHeight:Number = this._explicitMinHeight; var maxHeight:Number = this._maxHeight; //the background skin dimensions affect the minimum width if(this._originalBackgroundWidth === this._originalBackgroundWidth && //!isNaN minWidth < this._originalBackgroundWidth) { minWidth = this._originalBackgroundWidth; } if(this._originalBackgroundHeight === this._originalBackgroundHeight && //!isNaN minHeight < this._originalBackgroundHeight) { minHeight = this._originalBackgroundHeight; } //the width of top or bottom arrow (plus padding) affect the minimum //width too if(arrowPosition === RelativePosition.TOP && this._topArrowSkin) { var minWidthWithTopArrow:Number = this._topArrowSkin.width + this._paddingLeft + this._paddingRight; if(minWidth < minWidthWithTopArrow) { minWidth = minWidthWithTopArrow; } } if(arrowPosition === RelativePosition.BOTTOM && this._bottomArrowSkin) { var minWidthWithBottomArrow:Number = this._bottomArrowSkin.width + this._paddingLeft + this._paddingRight; if(minWidth < minWidthWithBottomArrow) { minWidth = minWidthWithBottomArrow; } } //the height of left or right arrow (plus padding) affect the //minimum height too if(arrowPosition === RelativePosition.LEFT && this._leftArrowSkin) { var minHeightWithLeftArrow:Number = this._leftArrowSkin.height + this._paddingTop + this._paddingBottom; if(minHeight < minHeightWithLeftArrow) { minHeight = minHeightWithLeftArrow; } } if(arrowPosition === RelativePosition.RIGHT && this._rightArrowSkin) { var minHeightWithRightArrow:Number = this._rightArrowSkin.height + this._paddingTop + this._paddingBottom; if(minHeight < minHeightWithRightArrow) { minHeight = minHeightWithRightArrow; } } //the dimensions of the stage (plus stage padding) affect the //maximum width and height if(this.stage) { var stageMaxWidth:Number = this.stage.stageWidth - stagePaddingLeft - stagePaddingRight; var stageMaxHeight:Number = this.stage.stageHeight - stagePaddingTop - stagePaddingBottom; if(maxWidth > stageMaxWidth) { maxWidth = stageMaxWidth; } if(maxHeight > stageMaxHeight) { maxHeight = stageMaxHeight; } } //the width of the left or right arrow skin affects the minimum and //maximum width of the content var leftOrRightArrowWidth:Number = 0; if(arrowPosition === RelativePosition.LEFT && this._leftArrowSkin) { leftOrRightArrowWidth += this._leftArrowSkin.width + this._leftArrowGap; } else if(arrowPosition === RelativePosition.RIGHT && this._rightArrowSkin) { leftOrRightArrowWidth += this._rightArrowSkin.width + this._rightArrowGap; } //the height of the top or bottom arrow skin affects the minimum and //maximum height of the content var topOrBottomArrowHeight:Number = 0; if(arrowPosition === RelativePosition.TOP && this._topArrowSkin) { topOrBottomArrowHeight += this._topArrowSkin.height + this._topArrowGap; } else if(arrowPosition === RelativePosition.BOTTOM && this._bottomArrowSkin) { topOrBottomArrowHeight += this._bottomArrowSkin.height + this._bottomArrowGap; } if(this._content is IFeathersControl) { var minContentWidth:Number = minWidth - leftOrRightArrowWidth - this._paddingLeft - this._paddingRight; var maxContentWidth:Number = maxWidth - leftOrRightArrowWidth - this._paddingLeft - this._paddingRight; var minContentHeight:Number = minHeight - topOrBottomArrowHeight - this._paddingTop - this._paddingBottom; var maxContentHeight:Number = maxHeight - topOrBottomArrowHeight - this._paddingTop - this._paddingBottom; //we only adjust the minimum and maximum dimensions of the //content when it won't fit into the callout's minimum or //maximum dimensions var feathersContent:IFeathersControl = IFeathersControl(this._content); if(feathersContent.minWidth < minContentWidth) { feathersContent.minWidth = minContentWidth; } if(feathersContent.maxWidth > maxContentWidth) { feathersContent.maxWidth = maxContentWidth; } if(feathersContent.minHeight < minContentHeight) { feathersContent.minHeight = minContentHeight; } if(feathersContent.maxHeight > maxContentHeight) { feathersContent.maxHeight = maxContentHeight; } } if(this._content is IValidating) { IValidating(this._content).validate(); } var newWidth:Number = this._explicitWidth; var newHeight:Number = this._explicitHeight; if(needsWidth) { newWidth = this._content.width + leftOrRightArrowWidth + this._paddingLeft + this._paddingRight; if(newWidth < minWidth) { newWidth = minWidth; } else if(newWidth > maxWidth) { newWidth = maxWidth; } } if(needsHeight) { newHeight = this._content.height + topOrBottomArrowHeight + this._paddingTop + this._paddingBottom; if(newHeight < minHeight) { newHeight = minHeight; } else if(newHeight > maxHeight) { newHeight = maxHeight; } } result.x = newWidth; result.y = newHeight; return result; } /** * @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 layoutChildren():void { var xPosition:Number = (this._leftArrowSkin && this._arrowPosition == RelativePosition.LEFT) ? this._leftArrowSkin.width + this._leftArrowGap : 0; var yPosition:Number = (this._topArrowSkin && this._arrowPosition == RelativePosition.TOP) ? this._topArrowSkin.height + this._topArrowGap : 0; var widthOffset:Number = (this._rightArrowSkin && this._arrowPosition == RelativePosition.RIGHT) ? this._rightArrowSkin.width + this._rightArrowGap : 0; var heightOffset:Number = (this._bottomArrowSkin && this._arrowPosition == RelativePosition.BOTTOM) ? this._bottomArrowSkin.height + this._bottomArrowGap : 0; var backgroundWidth:Number = this.actualWidth - xPosition - widthOffset; var backgroundHeight:Number = this.actualHeight - yPosition - heightOffset; if(this._backgroundSkin) { this._backgroundSkin.x = xPosition; this._backgroundSkin.y = yPosition; this._backgroundSkin.width = backgroundWidth; this._backgroundSkin.height = backgroundHeight; } if(this.currentArrowSkin) { if(this._arrowPosition == RelativePosition.LEFT) { this._leftArrowSkin.x = xPosition - this._leftArrowSkin.width - this._leftArrowGap; this._leftArrowSkin.y = this._arrowOffset + yPosition + Math.round((backgroundHeight - this._leftArrowSkin.height) / 2); this._leftArrowSkin.y = Math.min(yPosition + backgroundHeight - this._paddingBottom - this._leftArrowSkin.height, Math.max(yPosition + this._paddingTop, this._leftArrowSkin.y)); } else if(this._arrowPosition == RelativePosition.RIGHT) { this._rightArrowSkin.x = xPosition + backgroundWidth + this._rightArrowGap; this._rightArrowSkin.y = this._arrowOffset + yPosition + Math.round((backgroundHeight - this._rightArrowSkin.height) / 2); this._rightArrowSkin.y = Math.min(yPosition + backgroundHeight - this._paddingBottom - this._rightArrowSkin.height, Math.max(yPosition + this._paddingTop, this._rightArrowSkin.y)); } else if(this._arrowPosition == RelativePosition.BOTTOM) { this._bottomArrowSkin.x = this._arrowOffset + xPosition + Math.round((backgroundWidth - this._bottomArrowSkin.width) / 2); this._bottomArrowSkin.x = Math.min(xPosition + backgroundWidth - this._paddingRight - this._bottomArrowSkin.width, Math.max(xPosition + this._paddingLeft, this._bottomArrowSkin.x)); this._bottomArrowSkin.y = yPosition + backgroundHeight + this._bottomArrowGap; } else //top { this._topArrowSkin.x = this._arrowOffset + xPosition + Math.round((backgroundWidth - this._topArrowSkin.width) / 2); this._topArrowSkin.x = Math.min(xPosition + backgroundWidth - this._paddingRight - this._topArrowSkin.width, Math.max(xPosition + this._paddingLeft, this._topArrowSkin.x)); this._topArrowSkin.y = yPosition - this._topArrowSkin.height - this._topArrowGap; } } if(this._content) { this._content.x = xPosition + this._paddingLeft; this._content.y = yPosition + this._paddingTop; var oldIgnoreContentResize:Boolean = this._ignoreContentResize; this._ignoreContentResize = true; var contentWidth:Number = backgroundWidth - this._paddingLeft - this._paddingRight; var difference:Number = Math.abs(this._content.width - contentWidth); //instead of !=, we do some fuzzy math to account for possible //floating point errors. if(difference > FUZZY_CONTENT_DIMENSIONS_PADDING) { //we prefer not to set the width property of the content //because that stops it from being able to resize, but //sometimes, it's required. this._content.width = contentWidth; } var contentHeight:Number = backgroundHeight - this._paddingTop - this._paddingBottom; difference = Math.abs(this._content.height - contentHeight); //instead of !=, we do some fuzzy math to account for possible //floating point errors. if(difference > FUZZY_CONTENT_DIMENSIONS_PADDING) { this._content.height = contentHeight; } this._ignoreContentResize = oldIgnoreContentResize; } } /** * @private */ protected function positionToOrigin():void { if(!this._origin) { return; } this._origin.getBounds(Starling.current.stage, HELPER_RECT); var hasGlobalBounds:Boolean = this._lastGlobalBoundsOfOrigin != null; if(!hasGlobalBounds || !this._lastGlobalBoundsOfOrigin.equals(HELPER_RECT)) { 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; positionWithSupportedDirections(this, this._lastGlobalBoundsOfOrigin, this._supportedDirections); } } /** * @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.positionToOrigin(); } /** * @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 - 2024 Weber Informatics LLC | Privacy Policy