scaffold.libs_as.feathers.controls.Header.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.ITextRenderer;
import feathers.core.IValidating;
import feathers.core.PropertyProxy;
import feathers.events.FeathersEventType;
import feathers.layout.HorizontalAlign;
import feathers.layout.HorizontalLayout;
import feathers.layout.LayoutBoundsResult;
import feathers.layout.VerticalAlign;
import feathers.layout.ViewPortBounds;
import feathers.skins.IStyleProvider;
import feathers.system.DeviceCapabilities;
import flash.display.Stage;
import flash.display.StageDisplayState;
import flash.events.FullScreenEvent;
import flash.geom.Point;
import flash.system.Capabilities;
import starling.core.Starling;
import starling.display.DisplayObject;
import starling.events.Event;
/**
* A header that displays an optional title along with a horizontal regions
* on the sides for additional UI controls. The left side is typically for
* navigation (to display a back button, for example) and the right for
* additional actions. The title is displayed in the center by default,
* but it may be aligned to the left or right if there are no items on the
* desired side.
*
* In the following example, a header is created, given a title, and a
* back button:
*
*
* var header:Header = new Header();
* header.title = "I'm a header";
*
* var backButton:Button = new Button();
* backButton.label = "Back";
* backButton.styleNameList.add( Button.ALTERNATE_STYLE_NAME_BACK_BUTTON );
* backButton.addEventListener( Event.TRIGGERED, backButton_triggeredHandler );
* header.leftItems = new <DisplayObject>[ backButton ];
*
* this.addChild( header );
*
* @see ../../../help/header.html How to use the Feathers Header component
*/
public class Header extends FeathersControl
{
/**
* @private
*/
protected static const INVALIDATION_FLAG_LEFT_CONTENT:String = "leftContent";
/**
* @private
*/
protected static const INVALIDATION_FLAG_RIGHT_CONTENT:String = "rightContent";
/**
* @private
*/
protected static const INVALIDATION_FLAG_CENTER_CONTENT:String = "centerContent";
/**
* @private
*/
protected static const IOS_STATUS_BAR_HEIGHT:Number = 20;
/**
* @private
*/
protected static const IOS_DPI:Number = 132;
/**
* @private
*/
protected static const IOS_NAME_PREFIX:String = "iPhone OS ";
/**
* @private
*/
protected static const STATUS_BAR_MIN_IOS_VERSION:int = 7;
/**
* The default IStyleProvider
for all Header
* components.
*
* @default null
* @see feathers.core.FeathersControl#styleProvider
*/
public static var globalStyleProvider:IStyleProvider;
/**
* The title will appear in the center of the header.
*
* @see #titleAlign
*/
public static const TITLE_ALIGN_CENTER:String = "center";
/**
* The title will appear on the left of the header, if there is no other
* content on that side. If there is content, the title will appear in
* the center.
*
* @see #titleAlign
*/
public static const TITLE_ALIGN_PREFER_LEFT:String = "preferLeft";
/**
* The title will appear on the right of the header, if there is no
* other content on that side. If there is content, the title will
* appear in the center.
*
* @see #titleAlign
*/
public static const TITLE_ALIGN_PREFER_RIGHT:String = "preferRight";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.VerticalAlign.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 VERTICAL_ALIGN_TOP:String = "top";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.VerticalAlign.MIDDLE
.
*
* 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 VERTICAL_ALIGN_MIDDLE:String = "middle";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.VerticalAlign.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 VERTICAL_ALIGN_BOTTOM:String = "bottom";
/**
* The default value added to the styleNameList
of the header's
* items.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_ITEM:String = "feathers-header-item";
/**
* The default value added to the styleNameList
of the header's
* title.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_TITLE:String = "feathers-header-title";
/**
* @private
*/
private static const HELPER_BOUNDS:ViewPortBounds = new ViewPortBounds();
/**
* @private
*/
private static const HELPER_LAYOUT_RESULT:LayoutBoundsResult = new LayoutBoundsResult();
/**
* @private
*/
private static const HELPER_POINT:Point = new Point();
/**
* Constructor.
*/
public function Header()
{
super();
this.addEventListener(Event.ADDED_TO_STAGE, header_addedToStageHandler);
this.addEventListener(Event.REMOVED_FROM_STAGE, header_removedFromStageHandler);
}
/**
* The text renderer for the header's title.
*
* For internal use in subclasses.
*
* @see #title
* @see #titleFactory
* @see #createTitle()
*/
protected var titleTextRenderer:ITextRenderer;
/**
* The value added to the styleNameList
of the header's
* title text renderer. This variable is protected
so that
* sub-classes can customize the title text renderer style name in their
* constructors instead of using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_TITLE
.
*
* @see feathers.core.FeathersControl#styleNameList
*/
protected var titleStyleName:String = DEFAULT_CHILD_STYLE_NAME_TITLE;
/**
* The value added to the styleNameList
of each of the
* header's items. This variable is protected
so that
* sub-classes can customize the item style name in their constructors
* instead of using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_ITEM
.
*
* @see feathers.core.FeathersControl#styleNameList
*/
protected var itemStyleName:String = DEFAULT_CHILD_STYLE_NAME_ITEM;
/**
* @private
*/
protected var leftItemsWidth:Number = 0;
/**
* @private
*/
protected var rightItemsWidth:Number = 0;
/**
* @private
* The layout algorithm. Shared by both sides.
*/
protected var _layout:HorizontalLayout;
/**
* @private
*/
override protected function get defaultStyleProvider():IStyleProvider
{
return Header.globalStyleProvider;
}
/**
* @private
*/
protected var _title:String = "";
/**
* The text displayed for the header's title.
*
* In the following example, the header's title is set:
*
*
* header.title = "I'm a Header";
*
* @default ""
*
* @see #titleFactory
*/
public function get title():String
{
return this._title;
}
/**
* @private
*/
public function set title(value:String):void
{
if(value === null)
{
value = "";
}
if(this._title == value)
{
return;
}
this._title = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _titleFactory:Function;
/**
* A function used to instantiate the header's title text renderer
* sub-component. By default, the header will use the global text
* renderer factory, FeathersControl.defaultTextRendererFactory()
,
* to create the title text renderer. The title text renderer must be an
* instance of ITextRenderer
. This factory can be used to
* change properties on the title text renderer when it is first
* created. For instance, if you are skinning Feathers components
* without a theme, you might use this factory to style the title text
* renderer.
*
* If you are not using a theme, the title factory can be used to
* provide skin the title with appropriate text styles.
*
* The factory should have the following function signature:
* function():ITextRenderer
*
* In the following example, a custom title factory is passed to the
* header:
*
*
* header.titleFactory = function():ITextRenderer
* {
* var titleRenderer:TextFieldTextRenderer = new TextFieldTextRenderer();
* titleRenderer.textFormat = new TextFormat( "_sans", 12, 0xff0000 );
* return titleRenderer;
* }
*
* @default null
*
* @see #title
* @see feathers.core.ITextRenderer
* @see feathers.core.FeathersControl#defaultTextRendererFactory
*/
public function get titleFactory():Function
{
return this._titleFactory;
}
/**
* @private
*/
public function set titleFactory(value:Function):void
{
if(this._titleFactory == value)
{
return;
}
this._titleFactory = value;
this.invalidate(INVALIDATION_FLAG_TEXT_RENDERER);
}
/**
* @private
*/
protected var _disposeItems:Boolean = true;
/**
* Determines if the leftItems
, centerItems
,
* and rightItems
are disposed or not when the header is
* disposed.
*
* If you change this value to false
, you must dispose
* the items manually. Failing to dispose the items may result in a
* memory leak.
*
* @default true
*/
public function get disposeItems():Boolean
{
return this._disposeItems;
}
/**
* @private
*/
public function set disposeItems(value:Boolean):void
{
this._disposeItems = value;
}
/**
* @private
*/
protected var _leftItems:Vector.;
/**
* The UI controls that appear in the left region of the header.
*
* In the following example, a back button is displayed on the left
* side of the header:
*
*
* var backButton:Button = new Button();
* backButton.label = "Back";
* backButton.styleNameList.add( Button.ALTERNATE_STYLE_NAME_BACK_BUTTON );
* backButton.addEventListener( Event.TRIGGERED, backButton_triggeredHandler );
* header.leftItems = new <DisplayObject>[ backButton ];
*
* @default null
*/
public function get leftItems():Vector.
{
return this._leftItems;
}
/**
* @private
*/
public function set leftItems(value:Vector.):void
{
if(this._leftItems == value)
{
return;
}
if(this._leftItems)
{
for each(var item:DisplayObject in this._leftItems)
{
if(item is IFeathersControl)
{
IFeathersControl(item).styleNameList.remove(this.itemStyleName);
item.removeEventListener(FeathersEventType.RESIZE, item_resizeHandler);
}
item.removeFromParent();
}
}
this._leftItems = value;
if(this._leftItems)
{
for each(item in this._leftItems)
{
if(item is IFeathersControl)
{
item.addEventListener(FeathersEventType.RESIZE, item_resizeHandler);
}
}
}
this.invalidate(INVALIDATION_FLAG_LEFT_CONTENT);
}
/**
* @private
*/
protected var _centerItems:Vector.;
/**
* The UI controls that appear in the center region of the header. If
* centerItems
is not null
, and the
* titleAlign
property is Header.TITLE_ALIGN_CENTER
,
* the title text renderer will be hidden.
*
* In the following example, a settings button is displayed in the
* center of the header:
*
*
* var settingsButton:Button = new Button();
* settingsButton.label = "Settings";
* settingsButton.addEventListener( Event.TRIGGERED, settingsButton_triggeredHandler );
* header.centerItems = new <DisplayObject>[ settingsButton ];
*
* @default null
*/
public function get centerItems():Vector.
{
return this._centerItems;
}
/**
* @private
*/
public function set centerItems(value:Vector.):void
{
if(this._centerItems == value)
{
return;
}
if(this._centerItems)
{
for each(var item:DisplayObject in this._centerItems)
{
if(item is IFeathersControl)
{
IFeathersControl(item).styleNameList.remove(this.itemStyleName);
item.removeEventListener(FeathersEventType.RESIZE, item_resizeHandler);
}
item.removeFromParent();
}
}
this._centerItems = value;
if(this._centerItems)
{
for each(item in this._centerItems)
{
if(item is IFeathersControl)
{
item.addEventListener(FeathersEventType.RESIZE, item_resizeHandler);
}
}
}
this.invalidate(INVALIDATION_FLAG_CENTER_CONTENT);
}
/**
* @private
*/
protected var _rightItems:Vector.;
/**
* The UI controls that appear in the right region of the header.
*
* In the following example, a settings button is displayed on the
* right side of the header:
*
*
* var settingsButton:Button = new Button();
* settingsButton.label = "Settings";
* settingsButton.addEventListener( Event.TRIGGERED, settingsButton_triggeredHandler );
* header.rightItems = new <DisplayObject>[ settingsButton ];
*
* @default null
*/
public function get rightItems():Vector.
{
return this._rightItems;
}
/**
* @private
*/
public function set rightItems(value:Vector.):void
{
if(this._rightItems == value)
{
return;
}
if(this._rightItems)
{
for each(var item:DisplayObject in this._rightItems)
{
if(item is IFeathersControl)
{
IFeathersControl(item).styleNameList.remove(this.itemStyleName);
item.removeEventListener(FeathersEventType.RESIZE, item_resizeHandler);
}
item.removeFromParent();
}
}
this._rightItems = value;
if(this._rightItems)
{
for each(item in this._rightItems)
{
if(item is IFeathersControl)
{
item.addEventListener(FeathersEventType.RESIZE, item_resizeHandler);
}
}
}
this.invalidate(INVALIDATION_FLAG_RIGHT_CONTENT);
}
/**
* 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 header's padding is set to 20 pixels:
*
*
* header.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 header's top edge and the
* header's content.
*
* In the following example, the header's top padding is set to 20
* pixels:
*
*
* header.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 header's right edge and the
* header's content.
*
* In the following example, the header's right padding is set to 20
* pixels:
*
*
* header.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 header's bottom edge and
* the header's content.
*
* In the following example, the header's bottom padding is set to 20
* pixels:
*
*
* header.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 header's left edge and the
* header's content.
*
* In the following example, the header's left padding is set to 20
* pixels:
*
*
* header.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 _gap:Number = 0;
/**
* Space, in pixels, between items. The same value is used with the
* leftItems
and rightItems
.
*
* Set the titleGap
to make the gap on the left and
* right of the title use a different value.
*
* In the following example, the header's gap between items is set to
* 20 pixels:
*
*
* header.gap = 20;
*
* @default 0
*
* @see #titleGap
* @see #leftItems
* @see #rightItems
*/
public function get gap():Number
{
return _gap;
}
/**
* @private
*/
public function set gap(value:Number):void
{
if(this._gap == value)
{
return;
}
this._gap = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _titleGap:Number = NaN;
/**
* Space, in pixels, between the title and the left or right groups of
* items. If NaN
(the default), the default gap
* property is used instead.
*
* In the following example, the header's title gap is set to 20
* pixels:
*
*
* header.titleGap = 20;
*
* @default NaN
*
* @see #gap
*/
public function get titleGap():Number
{
return _titleGap;
}
/**
* @private
*/
public function set titleGap(value:Number):void
{
if(this._titleGap == value)
{
return;
}
this._titleGap = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _useExtraPaddingForOSStatusBar:Boolean = false;
/**
* If enabled, the header's top padding will be increased to account for
* the height of the OS status bar when the app is rendered under the OS
* status bar. The header will not add extra padding to apps that aren't
* rendered under the OS status bar.
*
* iOS started rendering apps that aren't full screen under the OS
* status bar in version 7.
*
* In the following example, the header's padding will account for
* the iOS status bar height:
*
*
* header.useExtraPaddingForOSStatusBar = true;
*
* @default false;
*
* @see #paddingTop
*/
public function get useExtraPaddingForOSStatusBar():Boolean
{
return this._useExtraPaddingForOSStatusBar;
}
/**
* @private
*/
public function set useExtraPaddingForOSStatusBar(value:Boolean):void
{
if(this._useExtraPaddingForOSStatusBar == value)
{
return;
}
this._useExtraPaddingForOSStatusBar = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _verticalAlign:String = VerticalAlign.MIDDLE;
[Inspectable(type="String",enumeration="top,middle,bottom")]
/**
* The alignment of the items vertically, on the y-axis.
*
* In the following example, the header's vertical alignment is set
* to the middle:
*
*
* header.verticalAlign = VerticalAlign.MIDDLE;
*
* @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.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var originalBackgroundWidth:Number = NaN;
/**
* @private
*/
protected var originalBackgroundHeight:Number = NaN;
/**
* @private
*/
protected var currentBackgroundSkin:DisplayObject;
/**
* @private
*/
protected var _backgroundSkin:DisplayObject;
/**
* A display object displayed behind the header's content.
*
* In the following example, the header's background skin is set to
* a Quad
:
*
*
* header.backgroundSkin = new Quad( 10, 10, 0xff0000 );
*
* @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._backgroundSkin != this._backgroundDisabledSkin)
{
this.removeChild(this._backgroundSkin);
}
this._backgroundSkin = value;
if(this._backgroundSkin && this._backgroundSkin.parent != this)
{
this._backgroundSkin.visible = false;
this.addChildAt(this._backgroundSkin, 0);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _backgroundDisabledSkin:DisplayObject;
/**
* A background to display when the header is disabled. If the property
* is null
, the value of the backgroundSkin
* property will be used instead.
*
* In the following example, the header's disabled background skin is
* set to a Quad
:
*
*
* header.backgroundDisabledSkin = new Quad( 10, 10, 0x999999 );
*
* @default null
*/
public function get backgroundDisabledSkin():DisplayObject
{
return this._backgroundDisabledSkin;
}
/**
* @private
*/
public function set backgroundDisabledSkin(value:DisplayObject):void
{
if(this._backgroundDisabledSkin == value)
{
return;
}
if(this._backgroundDisabledSkin && this._backgroundDisabledSkin != this._backgroundSkin)
{
this.removeChild(this._backgroundDisabledSkin);
}
this._backgroundDisabledSkin = value;
if(this._backgroundDisabledSkin && this._backgroundDisabledSkin.parent != this)
{
this._backgroundDisabledSkin.visible = false;
this.addChildAt(this._backgroundDisabledSkin, 0);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _customTitleStyleName:String;
/**
* A style name to add to the header's title text renderer
* sub-component. Typically used by a theme to provide different styles
* to different headers.
*
* In the following example, a custom title style name is passed to
* the header:
*
*
* header.customTitleStyleName = "my-custom-header-title";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( BitmapFontTextRenderer ).setFunctionForStyleName( "my-custom-header-title", setCustomHeaderTitleStyles );
*
* @default null
*
* @see #DEFAULT_CHILD_STYLE_NAME_TITLE
* @see feathers.core.FeathersControl#styleNameList
* @see #titleFactory
*/
public function get customTitleStyleName():String
{
return this._customTitleStyleName;
}
/**
* @private
*/
public function set customTitleStyleName(value:String):void
{
if(this._customTitleStyleName == value)
{
return;
}
this._customTitleStyleName = value;
this.invalidate(INVALIDATION_FLAG_TEXT_RENDERER);
}
/**
* @private
*/
protected var _titleProperties:PropertyProxy;
/**
* An object that stores properties for the header's title text renderer
* sub-component, and the properties will be passed down to the text
* renderer when the header validates. The available properties
* depend on which ITextRenderer
implementation is returned
* by textRendererFactory
. Refer to
* feathers.core.ITextRenderer
* for a list of available text renderer implementations.
*
* In the following example, some properties are set for the header's
* title text renderer (this example assumes that the title text renderer
* is a BitmapFontTextRenderer
):
*
*
* header.titleProperties.textFormat = new BitmapFontTextFormat( bitmapFont );
* header.titleProperties.wordWrap = true;
*
* If the subcomponent has its own subcomponents, their properties
* can be set too, using attribute @
notation. For example,
* to set the skin on the thumb which is in a SimpleScrollBar
,
* which is in a List
, you can use the following syntax:
* list.verticalScrollBarProperties.@thumbProperties.defaultSkin = new Image(texture);
*
* Setting properties in a titleFactory
function instead
* of using titleProperties
will result in better
* performance.
*
* @default null
*
* @see #titleFactory
* @see feathers.core.ITextRenderer
*/
public function get titleProperties():Object
{
if(!this._titleProperties)
{
this._titleProperties = new PropertyProxy(titleProperties_onChange);
}
return this._titleProperties;
}
/**
* @private
*/
public function set titleProperties(value:Object):void
{
if(this._titleProperties == value)
{
return;
}
if(value && !(value is PropertyProxy))
{
value = PropertyProxy.fromObject(value);
}
if(this._titleProperties)
{
this._titleProperties.removeOnChangeCallback(titleProperties_onChange);
}
this._titleProperties = PropertyProxy(value);
if(this._titleProperties)
{
this._titleProperties.addOnChangeCallback(titleProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _titleAlign:String = TITLE_ALIGN_CENTER;
[Inspectable(type="String",enumeration="center,preferLeft,preferRight")]
/**
* The preferred position of the title. If leftItems
and/or
* rightItems
are not null
, the title may be
* forced to the center even if the preferred position is on the left or
* right. If centerItems
is not null
, and the
* title is centered, the title will be hidden.
*
* In the following example, the header's title aligment is set to
* prefer the left side:
*
*
* header.titleAlign = Header.TITLE_ALIGN_PREFER_LEFT;
*
* @default Header.TITLE_ALIGN_CENTER
*
* @see #TITLE_ALIGN_CENTER
* @see #TITLE_ALIGN_PREFER_LEFT
* @see #TITLE_ALIGN_PREFER_RIGHT
*/
public function get titleAlign():String
{
return this._titleAlign;
}
/**
* @private
*/
public function set titleAlign(value:String):void
{
if(this._titleAlign == value)
{
return;
}
this._titleAlign = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
override public function dispose():void
{
if(this._disposeItems)
{
for each(var item:DisplayObject in this._leftItems)
{
item.dispose();
}
for each(item in this._centerItems)
{
item.dispose();
}
for each(item in this._rightItems)
{
item.dispose();
}
}
this.leftItems = null;
this.rightItems = null;
this.centerItems = null;
super.dispose();
}
/**
* @private
*/
override protected function initialize():void
{
if(!this._layout)
{
this._layout = new HorizontalLayout();
this._layout.useVirtualLayout = false;
this._layout.verticalAlign = VerticalAlign.MIDDLE;
}
}
/**
* @private
*/
override protected function draw():void
{
var sizeInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_SIZE);
var dataInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_DATA);
var stylesInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_STYLES);
var stateInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_STATE);
var leftContentInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_LEFT_CONTENT);
var rightContentInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_RIGHT_CONTENT);
var centerContentInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_CENTER_CONTENT);
var textRendererInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_TEXT_RENDERER);
if(textRendererInvalid)
{
this.createTitle();
}
if(textRendererInvalid || dataInvalid)
{
this.titleTextRenderer.text = this._title;
}
if(stateInvalid || stylesInvalid)
{
this.refreshBackground();
}
if(textRendererInvalid || stylesInvalid || sizeInvalid)
{
this.refreshLayout();
}
if(textRendererInvalid || stylesInvalid)
{
this.refreshTitleStyles();
}
if(leftContentInvalid)
{
if(this._leftItems)
{
for each(var item:DisplayObject in this._leftItems)
{
if(item is IFeathersControl)
{
IFeathersControl(item).styleNameList.add(this.itemStyleName);
}
this.addChild(item);
}
}
}
if(rightContentInvalid)
{
if(this._rightItems)
{
for each(item in this._rightItems)
{
if(item is IFeathersControl)
{
IFeathersControl(item).styleNameList.add(this.itemStyleName);
}
this.addChild(item);
}
}
}
if(centerContentInvalid)
{
if(this._centerItems)
{
for each(item in this._centerItems)
{
if(item is IFeathersControl)
{
IFeathersControl(item).styleNameList.add(this.itemStyleName);
}
this.addChild(item);
}
}
}
if(stateInvalid || textRendererInvalid)
{
this.refreshEnabled();
}
sizeInvalid = this.autoSizeIfNeeded() || sizeInvalid;
if(sizeInvalid || stylesInvalid)
{
this.layoutBackground();
}
if(sizeInvalid || leftContentInvalid || rightContentInvalid || centerContentInvalid || stylesInvalid)
{
this.leftItemsWidth = 0;
this.rightItemsWidth = 0;
if(this._leftItems)
{
this.layoutLeftItems();
}
if(this._rightItems)
{
this.layoutRightItems();
}
if(this._centerItems)
{
this.layoutCenterItems();
}
}
if(textRendererInvalid || sizeInvalid || stylesInvalid || dataInvalid || leftContentInvalid || rightContentInvalid || centerContentInvalid)
{
this.layoutTitle();
}
}
/**
* 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
{
var needsWidth:Boolean = this._explicitWidth !== this._explicitWidth; //isNaN
var needsHeight:Boolean = this._explicitHeight !== this._explicitHeight; //isNaN
if(!needsWidth && !needsHeight)
{
return false;
}
var newWidth:Number = needsWidth ? (this._paddingLeft + this._paddingRight) : this._explicitWidth;
var newHeight:Number = needsHeight ? 0 : this._explicitHeight;
var totalItemWidth:Number = 0;
var leftItemCount:int = this._leftItems ? this._leftItems.length : 0;
for(var i:int = 0; i < leftItemCount; i++)
{
var item:DisplayObject = this._leftItems[i];
if(item is IValidating)
{
IValidating(item).validate();
}
var itemWidth:Number = item.width;
if(needsWidth &&
itemWidth === itemWidth) //!isNaN
{
totalItemWidth += itemWidth;
if(i > 0)
{
totalItemWidth += this._gap;
}
}
var itemHeight:Number = item.height;
if(needsHeight &&
itemHeight === itemHeight && //!isNaN
itemHeight > newHeight)
{
newHeight = itemHeight;
}
}
var centerItemCount:int = this._centerItems ? this._centerItems.length : 0;
for(i = 0; i < centerItemCount; i++)
{
item = this._centerItems[i];
if(item is IValidating)
{
IValidating(item).validate();
}
itemWidth = item.width;
if(needsWidth &&
itemWidth === itemWidth) //!isNaN
{
totalItemWidth += itemWidth;
if(i > 0)
{
totalItemWidth += this._gap;
}
}
itemHeight = item.height;
if(needsHeight &&
itemHeight === itemHeight && //!isNaN
itemHeight > newHeight)
{
newHeight = itemHeight;
}
}
var rightItemCount:int = this._rightItems ? this._rightItems.length : 0;
for(i = 0; i < rightItemCount; i++)
{
item = this._rightItems[i];
if(item is IValidating)
{
IValidating(item).validate();
}
itemWidth = item.width
if(needsWidth &&
itemWidth === itemWidth) //!isNaN
{
totalItemWidth += itemWidth;
if(i > 0)
{
totalItemWidth += this._gap;
}
}
itemHeight = item.height;
if(needsHeight &&
itemHeight === itemHeight && //!isNaN
itemHeight > newHeight)
{
newHeight = itemHeight;
}
}
newWidth += totalItemWidth;
if(this._title && !(this._titleAlign == TITLE_ALIGN_CENTER && this._centerItems))
{
var calculatedTitleGap:Number = this._titleGap;
if(calculatedTitleGap !== calculatedTitleGap) //isNaN
{
calculatedTitleGap = this._gap;
}
newWidth += 2 * calculatedTitleGap;
var maxTitleWidth:Number = (needsWidth ? this._maxWidth : this._explicitWidth) - totalItemWidth;
if(leftItemCount > 0)
{
maxTitleWidth -= calculatedTitleGap;
}
if(centerItemCount > 0)
{
maxTitleWidth -= calculatedTitleGap;
}
if(rightItemCount > 0)
{
maxTitleWidth -= calculatedTitleGap;
}
this.titleTextRenderer.maxWidth = maxTitleWidth;
this.titleTextRenderer.measureText(HELPER_POINT);
var measuredTitleWidth:Number = HELPER_POINT.x;
var measuredTitleHeight:Number = HELPER_POINT.y;
if(needsWidth &&
measuredTitleWidth === measuredTitleWidth) //!isNaN
{
newWidth += measuredTitleWidth;
if(leftItemCount > 0)
{
newWidth += calculatedTitleGap;
}
if(rightItemCount > 0)
{
newWidth += calculatedTitleGap;
}
}
if(needsHeight &&
measuredTitleHeight === measuredTitleHeight && //!isNaN
measuredTitleHeight > newHeight)
{
newHeight = measuredTitleHeight;
}
}
if(needsHeight)
{
newHeight += this._paddingTop + this._paddingBottom;
var extraPaddingTop:Number = this.calculateExtraOSStatusBarPadding();
if(extraPaddingTop > 0)
{
//account for the minimum height before adding the padding
if(newHeight < this._explicitMinHeight)
{
newHeight = this._explicitMinHeight;
}
newHeight += extraPaddingTop;
}
}
if(needsWidth &&
this.originalBackgroundWidth === this.originalBackgroundWidth && //!isNaN
this.originalBackgroundWidth > newWidth)
{
newWidth = this.originalBackgroundWidth;
}
if(needsHeight &&
this.originalBackgroundHeight === this.originalBackgroundHeight && //!isNaN
this.originalBackgroundHeight > newHeight)
{
newHeight = this.originalBackgroundHeight;
}
return this.setSizeInternal(newWidth, newHeight, false);
}
/**
* Creates and adds the titleTextRenderer
sub-component and
* removes the old instance, if one exists.
*
* Meant for internal use, and subclasses may override this function
* with a custom implementation.
*
* @see #title
* @see #titleTextRenderer
* @see #titleFactory
*/
protected function createTitle():void
{
if(this.titleTextRenderer)
{
this.removeChild(DisplayObject(this.titleTextRenderer), true);
this.titleTextRenderer = null;
}
var factory:Function = this._titleFactory != null ? this._titleFactory : FeathersControl.defaultTextRendererFactory;
this.titleTextRenderer = ITextRenderer(factory());
var uiTitleRenderer:IFeathersControl = IFeathersControl(this.titleTextRenderer);
var titleStyleName:String = this._customTitleStyleName != null ? this._customTitleStyleName : this.titleStyleName;
uiTitleRenderer.styleNameList.add(titleStyleName);
this.addChild(DisplayObject(uiTitleRenderer));
}
/**
* @private
*/
protected function refreshBackground():void
{
this.currentBackgroundSkin = this._backgroundSkin;
if(!this._isEnabled && this._backgroundDisabledSkin)
{
if(this._backgroundSkin)
{
this._backgroundSkin.visible = false;
}
this.currentBackgroundSkin = this._backgroundDisabledSkin;
}
else if(this._backgroundDisabledSkin)
{
this._backgroundDisabledSkin.visible = false;
}
if(this.currentBackgroundSkin)
{
this.currentBackgroundSkin.visible = true;
if(this.originalBackgroundWidth !== this.originalBackgroundWidth) //isNaN
{
this.originalBackgroundWidth = this.currentBackgroundSkin.width;
}
if(this.originalBackgroundHeight !== this.originalBackgroundHeight) //isNaN
{
this.originalBackgroundHeight = this.currentBackgroundSkin.height;
}
}
}
/**
* @private
*/
protected function refreshLayout():void
{
this._layout.gap = this._gap;
this._layout.paddingTop = this._paddingTop + this.calculateExtraOSStatusBarPadding();
this._layout.paddingBottom = this._paddingBottom;
this._layout.verticalAlign = this._verticalAlign;
}
/**
* @private
*/
protected function refreshEnabled():void
{
this.titleTextRenderer.isEnabled = this._isEnabled;
}
/**
* @private
*/
protected function refreshTitleStyles():void
{
for(var propertyName:String in this._titleProperties)
{
var propertyValue:Object = this._titleProperties[propertyName];
this.titleTextRenderer[propertyName] = propertyValue;
}
}
/**
* @private
*/
protected function calculateExtraOSStatusBarPadding():Number
{
if(!this._useExtraPaddingForOSStatusBar)
{
return 0;
}
//first, we check if it's iOS or not. at this time, we only need to
//use extra padding on iOS. android and others are fine.
var os:String = Capabilities.os;
if(os.indexOf(IOS_NAME_PREFIX) != 0 || parseInt(os.substr(IOS_NAME_PREFIX.length, 1), 10) < STATUS_BAR_MIN_IOS_VERSION)
{
return 0;
}
//next, we check if the app is full screen or not. if it is full
//screen, then the status bar isn't visible, and we don't need the
//extra padding.
var nativeStage:Stage = Starling.current.nativeStage;
if(nativeStage.displayState != StageDisplayState.NORMAL)
{
return 0;
}
return IOS_STATUS_BAR_HEIGHT * Math.floor(DeviceCapabilities.dpi / IOS_DPI) / Starling.current.contentScaleFactor;
}
/**
* @private
*/
protected function layoutBackground():void
{
if(!this.currentBackgroundSkin)
{
return;
}
this.currentBackgroundSkin.width = this.actualWidth;
this.currentBackgroundSkin.height = this.actualHeight;
}
/**
* @private
*/
protected function layoutLeftItems():void
{
for each(var item:DisplayObject in this._leftItems)
{
if(item is IValidating)
{
IValidating(item).validate();
}
}
HELPER_BOUNDS.x = HELPER_BOUNDS.y = 0;
HELPER_BOUNDS.scrollX = HELPER_BOUNDS.scrollY = 0;
HELPER_BOUNDS.explicitWidth = this.actualWidth;
HELPER_BOUNDS.explicitHeight = this.actualHeight;
this._layout.horizontalAlign = HorizontalAlign.LEFT;
this._layout.paddingRight = 0;
this._layout.paddingLeft = this._paddingLeft;
this._layout.layout(this._leftItems, HELPER_BOUNDS, HELPER_LAYOUT_RESULT);
this.leftItemsWidth = HELPER_LAYOUT_RESULT.contentWidth;
if(this.leftItemsWidth !== this.leftItemsWidth) //isNaN
{
this.leftItemsWidth = 0;
}
}
/**
* @private
*/
protected function layoutRightItems():void
{
for each(var item:DisplayObject in this._rightItems)
{
if(item is IValidating)
{
IValidating(item).validate();
}
}
HELPER_BOUNDS.x = HELPER_BOUNDS.y = 0;
HELPER_BOUNDS.scrollX = HELPER_BOUNDS.scrollY = 0;
HELPER_BOUNDS.explicitWidth = this.actualWidth;
HELPER_BOUNDS.explicitHeight = this.actualHeight;
this._layout.horizontalAlign = HorizontalAlign.RIGHT;
this._layout.paddingRight = this._paddingRight;
this._layout.paddingLeft = 0;
this._layout.layout(this._rightItems, HELPER_BOUNDS, HELPER_LAYOUT_RESULT);
this.rightItemsWidth = HELPER_LAYOUT_RESULT.contentWidth;
if(this.rightItemsWidth !== this.rightItemsWidth) //isNaN
{
this.rightItemsWidth = 0;
}
}
/**
* @private
*/
protected function layoutCenterItems():void
{
for each(var item:DisplayObject in this._centerItems)
{
if(item is IValidating)
{
IValidating(item).validate();
}
}
HELPER_BOUNDS.x = HELPER_BOUNDS.y = 0;
HELPER_BOUNDS.scrollX = HELPER_BOUNDS.scrollY = 0;
HELPER_BOUNDS.explicitWidth = this.actualWidth;
HELPER_BOUNDS.explicitHeight = this.actualHeight;
this._layout.horizontalAlign = HorizontalAlign.CENTER;
this._layout.paddingRight = this._paddingRight;
this._layout.paddingLeft = this._paddingLeft;
this._layout.layout(this._centerItems, HELPER_BOUNDS, HELPER_LAYOUT_RESULT);
}
/**
* @private
*/
protected function layoutTitle():void
{
if((this._titleAlign == TITLE_ALIGN_CENTER && this._centerItems) || this._title.length == 0)
{
this.titleTextRenderer.visible = false;
return;
}
this.titleTextRenderer.visible = true;
var calculatedTitleGap:Number = this._titleGap;
if(calculatedTitleGap !== calculatedTitleGap) //isNaN
{
calculatedTitleGap = this._gap;
}
//left and right offsets already include padding
var leftOffset:Number = (this._leftItems && this._leftItems.length > 0) ? (this.leftItemsWidth + calculatedTitleGap) : 0;
var rightOffset:Number = (this._rightItems && this._rightItems.length > 0) ? (this.rightItemsWidth + calculatedTitleGap) : 0;
if(this._titleAlign == TITLE_ALIGN_PREFER_LEFT && (!this._leftItems || this._leftItems.length == 0))
{
this.titleTextRenderer.maxWidth = this.actualWidth - this._paddingLeft - rightOffset;
this.titleTextRenderer.validate();
this.titleTextRenderer.x = this._paddingLeft;
}
else if(this._titleAlign == TITLE_ALIGN_PREFER_RIGHT && (!this._rightItems || this._rightItems.length == 0))
{
this.titleTextRenderer.maxWidth = this.actualWidth - this._paddingRight - leftOffset;
this.titleTextRenderer.validate();
this.titleTextRenderer.x = this.actualWidth - this._paddingRight - this.titleTextRenderer.width;
}
else
{
var actualWidthMinusPadding:Number = this.actualWidth - this._paddingLeft - this._paddingRight;
var actualWidthMinusOffsets:Number = this.actualWidth - leftOffset - rightOffset;
this.titleTextRenderer.maxWidth = actualWidthMinusOffsets;
this.titleTextRenderer.validate();
var idealTitlePosition:Number = this._paddingLeft + (actualWidthMinusPadding - this.titleTextRenderer.width) / 2;
if(leftOffset > idealTitlePosition ||
(idealTitlePosition + this.titleTextRenderer.width) > (this.actualWidth - rightOffset))
{
this.titleTextRenderer.x = leftOffset + (actualWidthMinusOffsets - this.titleTextRenderer.width) / 2;
}
else
{
this.titleTextRenderer.x = idealTitlePosition;
}
}
var paddingTop:Number = this._paddingTop + this.calculateExtraOSStatusBarPadding();
if(this._verticalAlign == VerticalAlign.TOP)
{
this.titleTextRenderer.y = paddingTop;
}
else if(this._verticalAlign == VerticalAlign.BOTTOM)
{
this.titleTextRenderer.y = this.actualHeight - this._paddingBottom - this.titleTextRenderer.height;
}
else
{
this.titleTextRenderer.y = paddingTop + (this.actualHeight - paddingTop - this._paddingBottom - this.titleTextRenderer.height) / 2;
}
}
/**
* @private
*/
protected function header_addedToStageHandler(event:Event):void
{
Starling.current.nativeStage.addEventListener("fullScreen", nativeStage_fullScreenHandler);
}
/**
* @private
*/
protected function header_removedFromStageHandler(event:Event):void
{
Starling.current.nativeStage.removeEventListener("fullScreen", nativeStage_fullScreenHandler);
}
/**
* @private
*/
protected function nativeStage_fullScreenHandler(event:FullScreenEvent):void
{
this.invalidate(INVALIDATION_FLAG_SIZE);
}
/**
* @private
*/
protected function titleProperties_onChange(proxy:PropertyProxy, propertyName:String):void
{
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected function item_resizeHandler(event:Event):void
{
this.invalidate(INVALIDATION_FLAG_SIZE);
}
}
}