scaffold.libs_as.feathers.controls.TabBar.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.IFocusDisplayObject;
import feathers.core.PropertyProxy;
import feathers.core.ToggleGroup;
import feathers.data.ListCollection;
import feathers.events.CollectionEventType;
import feathers.events.FeathersEventType;
import feathers.layout.Direction;
import feathers.layout.HorizontalAlign;
import feathers.layout.HorizontalLayout;
import feathers.layout.LayoutBoundsResult;
import feathers.layout.VerticalAlign;
import feathers.layout.VerticalLayout;
import feathers.layout.ViewPortBounds;
import feathers.skins.IStyleProvider;
import flash.ui.Keyboard;
import starling.display.DisplayObject;
import starling.events.Event;
import starling.events.KeyboardEvent;
/**
* Dispatched when the selected tab changes.
*
* The properties of the event object have the following values:
*
* Property Value
* bubbles
false
* currentTarget
The 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
.
* data
null
* target
The 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.CHANGE
*/
[Event(name="change",type="starling.events.Event")]
/**
* A line of tabs (vertical or horizontal), where one may be selected at a
* time.
*
* The following example sets the data provider, selects the second tab,
* and listens for when the selection changes:
*
*
* var tabs:TabBar = new TabBar();
* tabs.dataProvider = new ListCollection(
* [
* { label: "One" },
* { label: "Two" },
* { label: "Three" },
* ]);
* tabs.selectedIndex = 1;
* tabs.addEventListener( Event.CHANGE, tabs_changeHandler );
* this.addChild( tabs );
*
* @see ../../../help/tab-bar.html How to use the Feathers TabBar component
*/
public class TabBar extends FeathersControl implements IFocusDisplayObject
{
/**
* @private
*/
protected static const INVALIDATION_FLAG_TAB_FACTORY:String = "tabFactory";
/**
* @private
*/
protected static const LABEL_FIELD:String = "label";
/**
* @private
*/
protected static const ENABLED_FIELD:String = "isEnabled";
/**
* @private
*/
private static const DEFAULT_TAB_FIELDS:Vector. = new
[
"defaultIcon",
"upIcon",
"downIcon",
"hoverIcon",
"disabledIcon",
"defaultSelectedIcon",
"selectedUpIcon",
"selectedDownIcon",
"selectedHoverIcon",
"selectedDisabledIcon",
"name"
];
/**
* @private
* DEPRECATED: Replaced by feathers.layout.Direction.HORIZONTAL
.
*
* DEPRECATION WARNING: This constant is deprecated
* starting with Feathers 3.0. It will be removed in a future version of
* Feathers according to the standard
* Feathers deprecation policy.
*/
public static const DIRECTION_HORIZONTAL:String = "horizontal";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.Direction.VERTICAL
.
*
* DEPRECATION WARNING: This constant is deprecated
* starting with Feathers 3.0. It will be removed in a future version of
* Feathers according to the standard
* Feathers deprecation policy.
*/
public static const DIRECTION_VERTICAL:String = "vertical";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.HorizontalAlign.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 HORIZONTAL_ALIGN_LEFT:String = "left";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.HorizontalAlign.CENTER
.
*
* 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 HORIZONTAL_ALIGN_CENTER:String = "center";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.HorizontalAlign.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 HORIZONTAL_ALIGN_RIGHT:String = "right";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.HorizontalAlign.JUSTIFY
.
*
* 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 HORIZONTAL_ALIGN_JUSTIFY:String = "justify";
/**
* @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";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.VerticalAlign.JUSTIFY
.
*
* 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_JUSTIFY:String = "justify";
/**
* The default value added to the styleNameList
of the tabs.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_TAB:String = "feathers-tab-bar-tab";
/**
* The default IStyleProvider
for all TabBar
* components.
*
* @default null
* @see feathers.core.FeathersControl#styleProvider
*/
public static var globalStyleProvider:IStyleProvider;
/**
* @private
*/
protected static function defaultTabFactory():ToggleButton
{
return new ToggleButton();
}
/**
* Constructor.
*/
public function TabBar()
{
super();
}
/**
* The value added to the styleNameList
of the tabs. This
* variable is protected
so that sub-classes can customize
* the tab style name in their constructors instead of using the default
* style name defined by DEFAULT_CHILD_STYLE_NAME_TAB
.
*
* To customize the tab style name without subclassing, see
* customTabStyleName
.
*
* @see #customTabStyleName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var tabStyleName:String = DEFAULT_CHILD_STYLE_NAME_TAB;
/**
* The value added to the styleNameList
of the first tab.
* This variable is protected
so that sub-classes can
* customize the first tab style name in their constructors instead of
* using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_TAB
.
*
* To customize the first tab name without subclassing, see
* customFirstTabName
.
*
* @see #customFirstTabName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var firstTabStyleName:String = DEFAULT_CHILD_STYLE_NAME_TAB;
/**
* The value added to the styleNameList
of the last tab.
* This variable is protected
so that sub-classes can
* customize the last tab style name in their constructors instead of
* using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_TAB
.
*
* To customize the last tab name without subclassing, see
* customLastTabName
.
*
* @see #customLastTabName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var lastTabStyleName:String = DEFAULT_CHILD_STYLE_NAME_TAB;
/**
* The toggle group.
*/
protected var toggleGroup:ToggleGroup;
/**
* @private
*/
protected var activeFirstTab:ToggleButton;
/**
* @private
*/
protected var inactiveFirstTab:ToggleButton;
/**
* @private
*/
protected var activeLastTab:ToggleButton;
/**
* @private
*/
protected var inactiveLastTab:ToggleButton;
/**
* @private
*/
protected var _layoutItems:Vector. = new [];
/**
* @private
*/
protected var activeTabs:Vector. = new [];
/**
* @private
*/
protected var inactiveTabs:Vector. = new [];
/**
* @private
*/
override protected function get defaultStyleProvider():IStyleProvider
{
return TabBar.globalStyleProvider;
}
/**
* @private
*/
protected var _dataProvider:ListCollection;
/**
* The collection of data to be displayed with tabs. The default
* tab initializer interprets this data to customize the tabs
* with various fields available to buttons, including the following:
*
*
* - label
* - defaultIcon
* - upIcon
* - downIcon
* - hoverIcon
* - disabledIcon
* - defaultSelectedIcon
* - selectedUpIcon
* - selectedDownIcon
* - selectedHoverIcon
* - selectedDisabledIcon
* - isEnabled
* - name
*
*
* The following example passes in a data provider:
*
*
* list.dataProvider = new ListCollection(
* [
* { label: "General", defaultIcon: new Image( generalTexture ) },
* { label: "Security", defaultIcon: new Image( securityTexture ) },
* { label: "Advanced", defaultIcon: new Image( advancedTexture ) },
* ]);
*
* @default null
*
* @see #tabInitializer
*/
public function get dataProvider():ListCollection
{
return this._dataProvider;
}
/**
* @private
*/
public function set dataProvider(value:ListCollection):void
{
if(this._dataProvider == value)
{
return;
}
var oldSelectedIndex:int = this.selectedIndex;
var oldSelectedItem:Object = this.selectedItem;
if(this._dataProvider)
{
this._dataProvider.removeEventListener(CollectionEventType.ADD_ITEM, dataProvider_addItemHandler);
this._dataProvider.removeEventListener(CollectionEventType.REMOVE_ITEM, dataProvider_removeItemHandler);
this._dataProvider.removeEventListener(CollectionEventType.REPLACE_ITEM, dataProvider_replaceItemHandler);
this._dataProvider.removeEventListener(CollectionEventType.UPDATE_ITEM, dataProvider_updateItemHandler);
this._dataProvider.removeEventListener(CollectionEventType.UPDATE_ALL, dataProvider_updateAllHandler);
this._dataProvider.removeEventListener(CollectionEventType.RESET, dataProvider_resetHandler);
}
this._dataProvider = value;
if(this._dataProvider)
{
this._dataProvider.addEventListener(CollectionEventType.ADD_ITEM, dataProvider_addItemHandler);
this._dataProvider.addEventListener(CollectionEventType.REMOVE_ITEM, dataProvider_removeItemHandler);
this._dataProvider.addEventListener(CollectionEventType.REPLACE_ITEM, dataProvider_replaceItemHandler);
this._dataProvider.addEventListener(CollectionEventType.UPDATE_ITEM, dataProvider_updateItemHandler);
this._dataProvider.addEventListener(CollectionEventType.UPDATE_ALL, dataProvider_updateAllHandler);
this._dataProvider.addEventListener(CollectionEventType.RESET, dataProvider_resetHandler);
}
if(!this._dataProvider || this._dataProvider.length == 0)
{
this.selectedIndex = -1;
}
else
{
this.selectedIndex = 0;
}
//this ensures that Event.CHANGE will dispatch for selectedItem
//changing, even if selectedIndex has not changed.
if(this.selectedIndex == oldSelectedIndex && this.selectedItem != oldSelectedItem)
{
this.dispatchEventWith(Event.CHANGE);
}
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var verticalLayout:VerticalLayout;
/**
* @private
*/
protected var horizontalLayout:HorizontalLayout;
/**
* @private
*/
protected var _viewPortBounds:ViewPortBounds = new ViewPortBounds();
/**
* @private
*/
protected var _layoutResult:LayoutBoundsResult = new LayoutBoundsResult();
/**
* @private
*/
protected var _direction:String = Direction.HORIZONTAL;
[Inspectable(type="String",enumeration="horizontal,vertical")]
/**
* The tab bar layout is either vertical or horizontal.
*
* In the following example, the tab bar's direction is set to
* vertical:
*
*
* tabs.direction = Direction.VERTICAL;
*
* @default feathers.layout.Direction.HORIZONTAL
*
* @see feathers.layout.Direction#HORIZONTAL
* @see feathers.layout.Direction#VERTICAL
*/
public function get direction():String
{
return this._direction;
}
/**
* @private
*/
public function set direction(value:String):void
{
if(this._direction == value)
{
return;
}
this._direction = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _horizontalAlign:String = HorizontalAlign.JUSTIFY;
[Inspectable(type="String",enumeration="left,center,right,justify")]
/**
* Determines how the tabs are horizontally aligned within the bounds
* of the tab bar (on the x-axis).
*
* The following example aligns the tabs to the left:
*
*
* tabs.horizontalAlign = HorizontalAlign.LEFT;
*
* @default feathers.layout.HorizontalAlign.JUSTIFY
*
* @see feathers.layout.HorizontalAlign#LEFT
* @see feathers.layout.HorizontalAlign#CENTER
* @see feathers.layout.HorizontalAlign#RIGHT
* @see feathers.layout.HorizontalAlign#JUSTIFY
*/
public function get horizontalAlign():String
{
return this._horizontalAlign;
}
/**
* @private
*/
public function set horizontalAlign(value:String):void
{
if(this._horizontalAlign == value)
{
return;
}
this._horizontalAlign = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _verticalAlign:String = VerticalAlign.JUSTIFY;
[Inspectable(type="String",enumeration="top,middle,bottom,justify")]
/**
* Determines how the tabs are vertically aligned within the bounds
* of the tab bar (on the y-axis).
*
* The following example aligns the tabs to the top:
*
*
* tabs.verticalAlign = VerticalAlign.TOP;
*
* @default feathers.layout.VerticalAlign.JUSTIFY
*
* @see feathers.layout.VerticalAlign#TOP
* @see feathers.layout.VerticalAlign#MIDDLE
* @see feathers.layout.VerticalAlign#BOTTOM
* @see feathers.layout.VerticalAlign#JUSTIFY
*/
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 _distributeTabSizes:Boolean = true;
/**
* If true
, the tabs will be equally sized in the direction
* of the layout. In other words, if the tab bar is horizontal, each tab
* will have the same width, and if the tab bar is vertical, each tab
* will have the same height. If false
, the tabs will be
* sized to their ideal dimensions.
*
* The following example aligns the tabs to the middle without distributing them:
*
*
* tabs.direction = Direction.VERTICAL;
* tabs.verticalAlign = VerticalAlign.MIDDLE;
* tabs.distributeTabSizes = false;
*
* @default true
*/
public function get distributeTabSizes():Boolean
{
return this._distributeTabSizes;
}
/**
* @private
*/
public function set distributeTabSizes(value:Boolean):void
{
if(this._distributeTabSizes == value)
{
return;
}
this._distributeTabSizes = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _gap:Number = 0;
/**
* Space, in pixels, between tabs.
*
* In the following example, the tab bar's gap is set to 20 pixels:
*
*
* tabs.gap = 20;
*
* @default 0
*/
public function get gap():Number
{
return this._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 _firstGap:Number = NaN;
/**
* Space, in pixels, between the first two tabs. If NaN
,
* the default gap
property will be used.
*
* The following example sets the gap between the first and second
* tab to a different value than the standard gap:
*
*
* tabs.firstGap = 30;
* tabs.gap = 20;
*
* @default NaN
*
* @see #gap
* @see #lastGap
*/
public function get firstGap():Number
{
return this._firstGap;
}
/**
* @private
*/
public function set firstGap(value:Number):void
{
if(this._firstGap == value)
{
return;
}
this._firstGap = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _lastGap:Number = NaN;
/**
* Space, in pixels, between the last two tabs. If NaN
,
* the default gap
property will be used.
*
* The following example sets the gap between the last and next to last
* tab to a different value than the standard gap:
*
*
* tabs.lastGap = 30;
* tabs.gap = 20;
*
* @default NaN
*
* @see #gap
* @see #firstGap
*/
public function get lastGap():Number
{
return this._lastGap;
}
/**
* @private
*/
public function set lastGap(value:Number):void
{
if(this._lastGap == value)
{
return;
}
this._lastGap = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* 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 tab bar
* is set to 20 pixels:
*
*
* tabs.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 tab bar's top edge and the
* tabs.
*
* In the following example, the padding on the top edge of the
* tab bar is set to 20 pixels:
*
*
* tabs.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 tab bar's right edge and
* the tabs.
*
* In the following example, the padding on the right edge of the
* tab bar is set to 20 pixels:
*
*
* tabs.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 tab bar's bottom edge and
* the tabs.
*
* In the following example, the padding on the bottom edge of the
* tab bar is set to 20 pixels:
*
*
* tabs.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 tab bar's left edge and the
* tabs.
*
* In the following example, the padding on the left edge of the
* tab bar is set to 20 pixels:
*
*
* tabs.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 _tabFactory:Function = defaultTabFactory;
/**
* Creates a new tab. A tab must be an instance of ToggleButton
.
* This factory can be used to change properties on the tabs when they
* are first created. For instance, if you are skinning Feathers
* components without a theme, you might use this factory to set skins
* and other styles on a tab.
*
* This function is expected to have the following signature:
*
* function():ToggleButton
*
* In the following example, a custom tab factory is passed to the
* tab bar:
*
*
* tabs.tabFactory = function():ToggleButton
* {
* var tab:ToggleButton = new ToggleButton();
* tab.defaultSkin = new Image( upTexture );
* tab.defaultSelectedSkin = new Image( selectedTexture );
* tab.downSkin = new Image( downTexture );
* return tab;
* };
*
* @default null
*
* @see feathers.controls.ToggleButton
* @see #firstTabFactory
* @see #lastTabFactory
*/
public function get tabFactory():Function
{
return this._tabFactory;
}
/**
* @private
*/
public function set tabFactory(value:Function):void
{
if(this._tabFactory == value)
{
return;
}
this._tabFactory = value;
this.invalidate(INVALIDATION_FLAG_TAB_FACTORY);
}
/**
* @private
*/
protected var _firstTabFactory:Function;
/**
* Creates a new first tab. If the firstTabFactory
is
* null
, then the tab bar will use the tabFactory
.
* The first tab must be an instance of ToggleButton
. This
* factory can be used to change properties on the first tab when it
* is first created. For instance, if you are skinning Feathers
* components without a theme, you might use this factory to set skins
* and other styles on the first tab.
*
* This function is expected to have the following signature:
*
* function():ToggleButton
*
* In the following example, a custom first tab factory is passed to the
* tab bar:
*
*
* tabs.firstTabFactory = function():ToggleButton
* {
* var tab:ToggleButton = new ToggleButton();
* tab.defaultSkin = new Image( upTexture );
* tab.defaultSelectedSkin = new Image( selectedTexture );
* tab.downSkin = new Image( downTexture );
* return tab;
* };
*
* @default null
*
* @see feathers.controls.ToggleButton
* @see #tabFactory
* @see #lastTabFactory
*/
public function get firstTabFactory():Function
{
return this._firstTabFactory;
}
/**
* @private
*/
public function set firstTabFactory(value:Function):void
{
if(this._firstTabFactory == value)
{
return;
}
this._firstTabFactory = value;
this.invalidate(INVALIDATION_FLAG_TAB_FACTORY);
}
/**
* @private
*/
protected var _lastTabFactory:Function;
/**
* Creates a new last tab. If the lastTabFactory
is
* null
, then the tab bar will use the tabFactory
.
* The last tab must be an instance of Button
. This
* factory can be used to change properties on the last tab when it
* is first created. For instance, if you are skinning Feathers
* components without a theme, you might use this factory to set skins
* and other styles on the last tab.
*
* This function is expected to have the following signature:
*
* function():ToggleButton
*
* In the following example, a custom last tab factory is passed to the
* tab bar:
*
*
* tabs.lastTabFactory = function():ToggleButton
* {
* var tab:ToggleButton = new Button();
* tab.defaultSkin = new Image( upTexture );
* tab.defaultSelectedSkin = new Image( selectedTexture );
* tab.downSkin = new Image( downTexture );
* return tab;
* };
*
* @default null
*
* @see feathers.controls.ToggleButton
* @see #tabFactory
* @see #firstTabFactory
*/
public function get lastTabFactory():Function
{
return this._lastTabFactory;
}
/**
* @private
*/
public function set lastTabFactory(value:Function):void
{
if(this._lastTabFactory == value)
{
return;
}
this._lastTabFactory = value;
this.invalidate(INVALIDATION_FLAG_TAB_FACTORY);
}
/**
* @private
*/
protected var _tabInitializer:Function = defaultTabInitializer;
/**
* Modifies the properties of an individual tab, using an item from the
* data provider. The default initializer will set the tab's label and
* icons. A custom tab initializer can be provided to update additional
* properties or to use different field names in the data provider.
*
* This function is expected to have the following signature:
* function( tab:ToggleButton, item:Object ):void
*
* In the following example, a custom tab initializer is passed to the
* tab bar:
*
*
* tabs.tabInitializer = function( tab:ToggleButton, item:Object ):void
* {
* tab.label = item.text;
* tab.defaultIcon = item.icon;
* };
*
* @see #dataProvider
*/
public function get tabInitializer():Function
{
return this._tabInitializer;
}
/**
* @private
*/
public function set tabInitializer(value:Function):void
{
if(this._tabInitializer == value)
{
return;
}
this._tabInitializer = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _ignoreSelectionChanges:Boolean = false;
/**
* @private
*/
protected var _selectedIndex:int = -1;
/**
* The index of the currently selected tab. Returns -1 if no tab is
* selected.
*
* In the following example, the tab bar's selected index is changed:
*
*
* tabs.selectedIndex = 2;
*
* The following example listens for when selection changes and
* requests the selected index:
*
*
* function tabs_changeHandler( event:Event ):void
* {
* var tabs:TabBar = TabBar( event.currentTarget );
* var index:int = tabs.selectedIndex;
*
* }
* tabs.addEventListener( Event.CHANGE, tabs_changeHandler );
*
* @default -1
*
* @see #selectedItem
*/
public function get selectedIndex():int
{
return this._selectedIndex;
}
/**
* @private
*/
public function set selectedIndex(value:int):void
{
if(this._selectedIndex == value)
{
return;
}
this._selectedIndex = value;
this.invalidate(INVALIDATION_FLAG_SELECTED);
this.dispatchEventWith(Event.CHANGE);
}
/**
* The currently selected item from the data provider. Returns
* null
if no item is selected.
*
* In the following example, the tab bar's selected item is changed:
*
*
* tabs.selectedItem = tabs.dataProvider.getItemAt(2);
*
* The following example listens for when selection changes and
* requests the selected item:
*
*
* function tabs_changeHandler( event:Event ):void
* {
* var tabs:TabBar = TabBar( event.currentTarget );
* var item:Object = tabs.selectedItem;
*
* }
* tabs.addEventListener( Event.CHANGE, tabs_changeHandler );
*
* @default null
*
* @see #selectedIndex
*/
public function get selectedItem():Object
{
var index:int = this.selectedIndex;
if(!this._dataProvider || index < 0 || index >= this._dataProvider.length)
{
return null;
}
return this._dataProvider.getItemAt(index);
}
/**
* @private
*/
public function set selectedItem(value:Object):void
{
if(!this._dataProvider)
{
this.selectedIndex = -1;
return;
}
this.selectedIndex = this._dataProvider.getItemIndex(value);
}
/**
* @private
*/
protected var _customTabStyleName:String;
/**
* A style name to add to all tabs in this tab bar. Typically used by a
* theme to provide different styles to different tab bars.
*
* In the following example, a custom tab style name is provided to
* the tab bar:
*
*
* tabs.customTabStyleName = "my-custom-tab";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( ToggleButton ).setFunctionForStyleName( "my-custom-tab", setCustomTabStyles );
*
* @default null
*
* @see #DEFAULT_CHILD_STYLE_NAME_TAB
* @see feathers.core.FeathersControl#styleNameList
*/
public function get customTabStyleName():String
{
return this._customTabStyleName;
}
/**
* @private
*/
public function set customTabStyleName(value:String):void
{
if(this._customTabStyleName == value)
{
return;
}
this._customTabStyleName = value;
this.invalidate(INVALIDATION_FLAG_TAB_FACTORY);
}
/**
* @private
*/
protected var _customFirstTabStyleName:String;
/**
* A style name to add to the first tab in this tab bar. Typically used
* by a theme to provide different styles to the first tab.
*
* In the following example, a custom first tab style name is
* provided to the tab bar:
*
*
* tabs.customFirstTabStyleName = "my-custom-first-tab";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( ToggleButton ).setFunctionForStyleName( "my-custom-first-tab", setCustomFirstTabStyles );
*
* @default null
*
* @see feathers.core.FeathersControl#styleNameList
*/
public function get customFirstTabStyleName():String
{
return this._customFirstTabStyleName;
}
/**
* @private
*/
public function set customFirstTabStyleName(value:String):void
{
if(this._customFirstTabStyleName == value)
{
return;
}
this._customFirstTabStyleName = value;
this.invalidate(INVALIDATION_FLAG_TAB_FACTORY);
}
/**
* @private
*/
protected var _customLastTabStyleName:String;
/**
* A style name to add to the last tab in this tab bar. Typically used
* by a theme to provide different styles to the last tab.
*
* In the following example, a custom last tab style name is provided
* to the tab bar:
*
*
* tabs.customLastTabStyleName = "my-custom-last-tab";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( ToggleButton ).setFunctionForStyleName( "my-custom-last-tab", setCustomLastTabStyles );
*
* @default null
*
* @see feathers.core.FeathersControl#styleNameList
*/
public function get customLastTabStyleName():String
{
return this._customLastTabStyleName;
}
/**
* @private
*/
public function set customLastTabStyleName(value:String):void
{
if(this._customLastTabStyleName == value)
{
return;
}
this._customLastTabStyleName = value;
this.invalidate(INVALIDATION_FLAG_TAB_FACTORY);
}
/**
* @private
*/
protected var _tabProperties:PropertyProxy;
/**
* An object that stores properties for all of the tab bar's tabs, and
* the properties will be passed down to every tab when the tab bar
* validates. For a list of available properties, refer to
* feathers.controls.ToggleButton
.
*
* These properties are shared by every tab, so anything that cannot
* be shared (such as display objects, which cannot be added to multiple
* parents) should be passed to tabs using the tabFactory
* or in the theme.
*
* 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 tabFactory
function instead
* of using tabProperties
will result in better
* performance.
*
* In the following example, the tab bar's tab properties are updated:
*
*
* tabs.tabProperties.iconPosition = RelativePosition.RIGHT;
*
* @default null
*
* @see #tabFactory
* @see feathers.controls.ToggleButton
*/
public function get tabProperties():Object
{
if(!this._tabProperties)
{
this._tabProperties = new PropertyProxy(childProperties_onChange);
}
return this._tabProperties;
}
/**
* @private
*/
public function set tabProperties(value:Object):void
{
if(this._tabProperties == value)
{
return;
}
if(!value)
{
value = new PropertyProxy();
}
if(!(value is PropertyProxy))
{
var newValue:PropertyProxy = new PropertyProxy();
for(var propertyName:String in value)
{
newValue[propertyName] = value[propertyName];
}
value = newValue;
}
if(this._tabProperties)
{
this._tabProperties.removeOnChangeCallback(childProperties_onChange);
}
this._tabProperties = PropertyProxy(value);
if(this._tabProperties)
{
this._tabProperties.addOnChangeCallback(childProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @inheritDoc
*/
public function get baseline():Number
{
if(!this.activeTabs || this.activeTabs.length === 0)
{
return this.scaledActualHeight;
}
var firstTab:ToggleButton = this.activeTabs[0];
return this.scaleY * (firstTab.y + firstTab.baseline);
}
/**
* @private
*/
override public function dispose():void
{
//clearing selection now so that the data provider setter won't
//cause a selection change that triggers events.
this._selectedIndex = -1;
this.dataProvider = null;
super.dispose();
}
/**
* @private
*/
override protected function initialize():void
{
this.toggleGroup = new ToggleGroup();
this.toggleGroup.isSelectionRequired = true;
this.toggleGroup.addEventListener(Event.CHANGE, toggleGroup_changeHandler);
}
/**
* @private
*/
override protected function draw():void
{
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 selectionInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_SELECTED);
var tabFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_TAB_FACTORY);
var sizeInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_SIZE);
if(dataInvalid || tabFactoryInvalid || stateInvalid)
{
this.refreshTabs(tabFactoryInvalid);
}
if(dataInvalid || tabFactoryInvalid || stylesInvalid)
{
this.refreshTabStyles();
}
if(dataInvalid || tabFactoryInvalid || selectionInvalid)
{
this.commitSelection();
}
if(dataInvalid || tabFactoryInvalid || stateInvalid)
{
this.commitEnabled();
}
if(stylesInvalid)
{
this.refreshLayoutStyles();
}
this.layoutTabs();
}
/**
* @private
*/
protected function commitSelection():void
{
this.toggleGroup.selectedIndex = this._selectedIndex;
}
/**
* @private
*/
protected function commitEnabled():void
{
for each(var tab:ToggleButton in this.activeTabs)
{
tab.isEnabled &&= this._isEnabled;
}
}
/**
* @private
*/
protected function refreshTabStyles():void
{
for(var propertyName:String in this._tabProperties)
{
var propertyValue:Object = this._tabProperties[propertyName];
for each(var tab:ToggleButton in this.activeTabs)
{
tab[propertyName] = propertyValue;
}
}
}
/**
* @private
*/
protected function refreshLayoutStyles():void
{
if(this._direction == Direction.VERTICAL)
{
if(this.horizontalLayout)
{
this.horizontalLayout = null;
}
if(!this.verticalLayout)
{
this.verticalLayout = new VerticalLayout();
this.verticalLayout.useVirtualLayout = false;
}
this.verticalLayout.distributeHeights = this._distributeTabSizes;
this.verticalLayout.horizontalAlign = this._horizontalAlign;
this.verticalLayout.verticalAlign = (this._verticalAlign == VerticalAlign.JUSTIFY) ? VerticalAlign.TOP : this._verticalAlign;
this.verticalLayout.gap = this._gap;
this.verticalLayout.firstGap = this._firstGap;
this.verticalLayout.lastGap = this._lastGap;
this.verticalLayout.paddingTop = this._paddingTop;
this.verticalLayout.paddingRight = this._paddingRight;
this.verticalLayout.paddingBottom = this._paddingBottom;
this.verticalLayout.paddingLeft = this._paddingLeft;
}
else //horizontal
{
if(this.verticalLayout)
{
this.verticalLayout = null;
}
if(!this.horizontalLayout)
{
this.horizontalLayout = new HorizontalLayout();
this.horizontalLayout.useVirtualLayout = false;
}
this.horizontalLayout.distributeWidths = this._distributeTabSizes;
this.horizontalLayout.horizontalAlign = (this._horizontalAlign == HorizontalAlign.JUSTIFY) ? HorizontalAlign.LEFT : this._horizontalAlign;
this.horizontalLayout.verticalAlign = this._verticalAlign;
this.horizontalLayout.gap = this._gap;
this.horizontalLayout.firstGap = this._firstGap;
this.horizontalLayout.lastGap = this._lastGap;
this.horizontalLayout.paddingTop = this._paddingTop;
this.horizontalLayout.paddingRight = this._paddingRight;
this.horizontalLayout.paddingBottom = this._paddingBottom;
this.horizontalLayout.paddingLeft = this._paddingLeft;
}
}
/**
* @private
*/
protected function defaultTabInitializer(tab:ToggleButton, item:Object):void
{
if(item is Object)
{
if(item.hasOwnProperty(LABEL_FIELD))
{
tab.label = item.label;
}
else
{
tab.label = item.toString();
}
if(item.hasOwnProperty(ENABLED_FIELD))
{
tab.isEnabled = item.isEnabled as Boolean;
}
else
{
tab.isEnabled = this._isEnabled;
}
for each(var field:String in DEFAULT_TAB_FIELDS)
{
if(item.hasOwnProperty(field))
{
tab[field] = item[field];
}
}
}
else
{
tab.label = "";
tab.isEnabled = this._isEnabled;
}
}
/**
* @private
*/
protected function refreshTabs(isFactoryInvalid:Boolean):void
{
var oldIgnoreSelectionChanges:Boolean = this._ignoreSelectionChanges;
this._ignoreSelectionChanges = true;
var oldSelectedIndex:int = this.toggleGroup.selectedIndex;
this.toggleGroup.removeAllItems();
var temp:Vector. = this.inactiveTabs;
this.inactiveTabs = this.activeTabs;
this.activeTabs = temp;
this.activeTabs.length = 0;
this._layoutItems.length = 0;
temp = null;
if(isFactoryInvalid)
{
this.clearInactiveTabs();
}
else
{
if(this.activeFirstTab)
{
this.inactiveTabs.shift();
}
this.inactiveFirstTab = this.activeFirstTab;
if(this.activeLastTab)
{
this.inactiveTabs.pop();
}
this.inactiveLastTab = this.activeLastTab;
}
this.activeFirstTab = null;
this.activeLastTab = null;
var pushIndex:int = 0;
var itemCount:int = this._dataProvider ? this._dataProvider.length : 0;
var lastItemIndex:int = itemCount - 1;
for(var i:int = 0; i < itemCount; i++)
{
var item:Object = this._dataProvider.getItemAt(i);
if(i == 0)
{
var tab:ToggleButton = this.activeFirstTab = this.createFirstTab(item);
}
else if(i == lastItemIndex)
{
tab = this.activeLastTab = this.createLastTab(item);
}
else
{
tab = this.createTab(item);
}
this.toggleGroup.addItem(tab);
this.activeTabs[pushIndex] = tab;
this._layoutItems[pushIndex] = tab;
pushIndex++;
}
this.clearInactiveTabs();
this._ignoreSelectionChanges = oldIgnoreSelectionChanges;
if(oldSelectedIndex >= 0)
{
var newSelectedIndex:int = this.activeTabs.length - 1;
if(oldSelectedIndex < newSelectedIndex)
{
newSelectedIndex = oldSelectedIndex;
}
//removing all items from the ToggleGroup clears the selection,
//so we need to set it back to the old value (or a new clamped
//value). we want the change event to dispatch only if the old
//value and the new value don't match.
this._ignoreSelectionChanges = oldSelectedIndex == newSelectedIndex;
this.toggleGroup.selectedIndex = newSelectedIndex;
this._ignoreSelectionChanges = oldIgnoreSelectionChanges;
}
}
/**
* @private
*/
protected function clearInactiveTabs():void
{
var itemCount:int = this.inactiveTabs.length;
for(var i:int = 0; i < itemCount; i++)
{
var tab:ToggleButton = this.inactiveTabs.shift();
this.destroyTab(tab);
}
if(this.inactiveFirstTab)
{
this.destroyTab(this.inactiveFirstTab);
this.inactiveFirstTab = null;
}
if(this.inactiveLastTab)
{
this.destroyTab(this.inactiveLastTab);
this.inactiveLastTab = null;
}
}
/**
* @private
*/
protected function createFirstTab(item:Object):ToggleButton
{
if(this.inactiveFirstTab)
{
var tab:ToggleButton = this.inactiveFirstTab;
this.inactiveFirstTab = null;
}
else
{
var factory:Function = this._firstTabFactory != null ? this._firstTabFactory : this._tabFactory;
tab = ToggleButton(factory());
if(this._customFirstTabStyleName)
{
tab.styleNameList.add(this._customFirstTabStyleName);
}
else if(this._customTabStyleName)
{
tab.styleNameList.add(this._customTabStyleName);
}
else
{
tab.styleNameList.add(this.firstTabStyleName);
}
tab.isToggle = true;
this.addChild(tab);
}
this._tabInitializer(tab, item);
return tab;
}
/**
* @private
*/
protected function createLastTab(item:Object):ToggleButton
{
if(this.inactiveLastTab)
{
var tab:ToggleButton = this.inactiveLastTab;
this.inactiveLastTab = null;
}
else
{
var factory:Function = this._lastTabFactory != null ? this._lastTabFactory : this._tabFactory;
tab = ToggleButton(factory());
if(this._customLastTabStyleName)
{
tab.styleNameList.add(this._customLastTabStyleName);
}
else if(this._customTabStyleName)
{
tab.styleNameList.add(this._customTabStyleName);
}
else
{
tab.styleNameList.add(this.lastTabStyleName);
}
tab.isToggle = true;
this.addChild(tab);
}
this._tabInitializer(tab, item);
return tab;
}
/**
* @private
*/
protected function createTab(item:Object):ToggleButton
{
if(this.inactiveTabs.length == 0)
{
var tab:ToggleButton = this._tabFactory();
if(this._customTabStyleName)
{
tab.styleNameList.add(this._customTabStyleName);
}
else
{
tab.styleNameList.add(this.tabStyleName);
}
tab.isToggle = true;
this.addChild(tab);
}
else
{
tab = this.inactiveTabs.shift();
}
this._tabInitializer(tab, item);
return tab;
}
/**
* @private
*/
protected function destroyTab(tab:ToggleButton):void
{
this.toggleGroup.removeItem(tab);
this.removeChild(tab, true);
}
/**
* @private
*/
protected function layoutTabs():void
{
this._viewPortBounds.x = 0;
this._viewPortBounds.y = 0;
this._viewPortBounds.scrollX = 0;
this._viewPortBounds.scrollY = 0;
this._viewPortBounds.explicitWidth = this._explicitWidth;
this._viewPortBounds.explicitHeight = this._explicitHeight;
this._viewPortBounds.minWidth = this._explicitMinWidth;
this._viewPortBounds.minHeight = this._explicitMinHeight;
this._viewPortBounds.maxWidth = this._maxWidth;
this._viewPortBounds.maxHeight = this._maxHeight;
if(this.verticalLayout)
{
this.verticalLayout.layout(this._layoutItems, this._viewPortBounds, this._layoutResult);
}
else if(this.horizontalLayout)
{
this.horizontalLayout.layout(this._layoutItems, this._viewPortBounds, this._layoutResult);
}
this.setSizeInternal(this._layoutResult.contentWidth, this._layoutResult.contentHeight, false);
//final validation to avoid juggler next frame issues
for each(var tab:ToggleButton in this.activeTabs)
{
tab.validate();
}
}
/**
* @private
*/
override public function showFocus():void
{
if(!this._hasFocus)
{
return;
}
this._showFocus = true;
this.showFocusedTab();
this.invalidate(INVALIDATION_FLAG_FOCUS);
}
/**
* @private
*/
override public function hideFocus():void
{
if(!this._hasFocus)
{
return;
}
this._showFocus = false;
this.hideFocusedTab();
this.invalidate(INVALIDATION_FLAG_FOCUS);
}
/**
* @private
*/
protected function hideFocusedTab():void
{
if(this._focusedTabIndex < 0)
{
return;
}
var focusedTab:ToggleButton = this.activeTabs[this._focusedTabIndex];
focusedTab.hideFocus();
}
/**
* @private
*/
protected function showFocusedTab():void
{
if(!this._showFocus || this._focusedTabIndex < 0)
{
return;
}
var tab:ToggleButton = this.activeTabs[this._focusedTabIndex];
tab.showFocus();
}
/**
* @private
*/
protected function focusedTabFocusIn():void
{
if(this._focusedTabIndex < 0)
{
return;
}
var tab:ToggleButton = this.activeTabs[this._focusedTabIndex];
tab.dispatchEventWith(FeathersEventType.FOCUS_IN);
}
/**
* @private
*/
protected function focusedTabFocusOut():void
{
if(this._focusedTabIndex < 0)
{
return;
}
var tab:ToggleButton = this.activeTabs[this._focusedTabIndex];
tab.dispatchEventWith(FeathersEventType.FOCUS_OUT);
}
/**
* @private
*/
protected function childProperties_onChange(proxy:PropertyProxy, name:String):void
{
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
override protected function focusInHandler(event:Event):void
{
super.focusInHandler(event);
this._focusedTabIndex = this._selectedIndex;
this.focusedTabFocusIn();
this.stage.addEventListener(KeyboardEvent.KEY_DOWN, stage_keyDownHandler);
}
/**
* @private
*/
override protected function focusOutHandler(event:Event):void
{
super.focusOutHandler(event);
this.hideFocusedTab();
this.focusedTabFocusOut();
this.stage.removeEventListener(KeyboardEvent.KEY_DOWN, stage_keyDownHandler);
}
protected var _focusedTabIndex:int = -1;
/**
* @private
*/
protected function stage_keyDownHandler(event:KeyboardEvent):void
{
if(!this._isEnabled)
{
return;
}
if(!this._dataProvider || this._dataProvider.length === 0)
{
return;
}
var newFocusedTabIndex:int = this._focusedTabIndex;
var maxFocusedTabIndex:int = this._dataProvider.length - 1;
if(event.keyCode == Keyboard.HOME)
{
this.selectedIndex = newFocusedTabIndex = 0;
}
else if(event.keyCode == Keyboard.END)
{
this.selectedIndex = newFocusedTabIndex = maxFocusedTabIndex;
}
else if(event.keyCode == Keyboard.PAGE_UP)
{
newFocusedTabIndex--;
if(newFocusedTabIndex < 0)
{
newFocusedTabIndex = maxFocusedTabIndex;
}
this.selectedIndex = newFocusedTabIndex;
}
else if(event.keyCode == Keyboard.PAGE_DOWN)
{
newFocusedTabIndex++;
if(newFocusedTabIndex > maxFocusedTabIndex)
{
newFocusedTabIndex = 0;
}
this.selectedIndex = newFocusedTabIndex;
}
else if(event.keyCode === Keyboard.UP || event.keyCode === Keyboard.LEFT)
{
newFocusedTabIndex--;
if(newFocusedTabIndex < 0)
{
newFocusedTabIndex = maxFocusedTabIndex;
}
}
else if(event.keyCode === Keyboard.DOWN || event.keyCode === Keyboard.RIGHT)
{
newFocusedTabIndex++;
if(newFocusedTabIndex > maxFocusedTabIndex)
{
newFocusedTabIndex = 0;
}
}
if(newFocusedTabIndex >= 0 && newFocusedTabIndex !== this._focusedTabIndex)
{
this.hideFocusedTab();
this.focusedTabFocusOut();
this._focusedTabIndex = newFocusedTabIndex;
this.focusedTabFocusIn();
this.showFocusedTab();
}
}
/**
* @private
*/
protected function toggleGroup_changeHandler(event:Event):void
{
if(this._ignoreSelectionChanges)
{
return;
}
this.selectedIndex = this.toggleGroup.selectedIndex;
}
/**
* @private
*/
protected function dataProvider_addItemHandler(event:Event, index:int):void
{
if(this._selectedIndex >= index)
{
//we're keeping the same selected item, but the selected index
//will change, so we need to manually dispatch the change event
this.selectedIndex += 1;
this.invalidate(INVALIDATION_FLAG_SELECTED);
}
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected function dataProvider_removeItemHandler(event:Event, index:int):void
{
if(this._selectedIndex > index)
{
//the same item is selected, but its index has changed.
this.selectedIndex -= 1;
}
else if(this._selectedIndex == index)
{
var oldIndex:int = this._selectedIndex;
var newIndex:int = oldIndex;
var maxIndex:int = this._dataProvider.length - 1;
if(newIndex > maxIndex)
{
newIndex = maxIndex;
}
if(oldIndex == newIndex)
{
//we're keeping the same selected index, but the selected
//item will change, so we need to manually dispatch the
//change event
this.invalidate(INVALIDATION_FLAG_SELECTED);
this.dispatchEventWith(Event.CHANGE);
}
else
{
//we're selecting both a different index and a different
//item, so we'll just call the selectedIndex setter
this.selectedIndex = newIndex;
}
}
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected function dataProvider_resetHandler(event:Event):void
{
if(this._dataProvider.length > 0)
{
//the data provider has changed drastically. we should reset the
//selection to the first item.
if(this._selectedIndex != 0)
{
this.selectedIndex = 0;
}
else
{
//we're keeping the same selected index, but the selected
//item will change, so we need to manually dispatch the
//change event
this.invalidate(INVALIDATION_FLAG_SELECTED);
this.dispatchEventWith(Event.CHANGE);
}
}
else if(this._selectedIndex >= 0)
{
this.selectedIndex = -1;
}
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected function dataProvider_replaceItemHandler(event:Event, index:int):void
{
if(this._selectedIndex == index)
{
//we're keeping the same selected index, but the selected
//item will change, so we need to manually dispatch the
//change event
this.invalidate(INVALIDATION_FLAG_SELECTED);
this.dispatchEventWith(Event.CHANGE);
}
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected function dataProvider_updateItemHandler(event:Event, index:int):void
{
//no need to dispatch a change event. the index and the item are the
//same. the item's properties have changed, but that doesn't require
//a change event.
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected function dataProvider_updateAllHandler(event:Event):void
{
this.invalidate(INVALIDATION_FLAG_DATA);
}
}
}