scaffold.libs_as.feathers.controls.GroupedList.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.controls.renderers.DefaultGroupedListHeaderOrFooterRenderer;
import feathers.controls.renderers.DefaultGroupedListItemRenderer;
import feathers.controls.renderers.IGroupedListFooterRenderer;
import feathers.controls.renderers.IGroupedListHeaderRenderer;
import feathers.controls.renderers.IGroupedListItemRenderer;
import feathers.controls.supportClasses.GroupedListDataViewPort;
import feathers.core.IFocusContainer;
import feathers.core.PropertyProxy;
import feathers.data.HierarchicalCollection;
import feathers.events.CollectionEventType;
import feathers.layout.HorizontalAlign;
import feathers.layout.ILayout;
import feathers.layout.IVariableVirtualLayout;
import feathers.layout.VerticalAlign;
import feathers.layout.VerticalLayout;
import feathers.skins.IStyleProvider;
import flash.geom.Point;
import flash.ui.Keyboard;
import starling.events.Event;
import starling.events.KeyboardEvent;
/**
* Dispatched when the selected item 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")]
/**
* Dispatched when the the user taps or clicks an item renderer in the list.
* The touch must remain within the bounds of the item renderer on release,
* and the list must not have scrolled, to register as a tap or a click.
*
* 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
The item associated with the item
* renderer that was triggered.
* 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.TRIGGERED
*/
[Event(name="triggered",type="starling.events.Event")]
/**
* Dispatched when an item renderer is added to the list. When the layout is
* virtualized, item renderers may not exist for every item in the data
* provider. This event can be used to track which items currently have
* renderers.
*
* 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
The item renderer that was added
* 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 feathers.events.FeathersEventType.RENDERER_ADD
*/
[Event(name="rendererAdd",type="starling.events.Event")]
/**
* Dispatched when an item renderer is removed from the list. When the layout is
* virtualized, item renderers may not exist for every item in the data
* provider. This event can be used to track which items currently have
* renderers.
*
* 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
The item renderer that was removed
* 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 feathers.events.FeathersEventType.RENDERER_REMOVE
*/
[Event(name="rendererRemove",type="starling.events.Event")]
/**
* Displays a list of items divided into groups or sections. Takes a
* hierarchical provider limited to two levels of hierarchy. This component
* supports scrolling, custom item (and header and footer) renderers, and
* custom layouts.
*
* Layouts may be, and are highly encouraged to be, virtual,
* meaning that the List is capable of creating a limited number of item
* renderers to display a subset of the data provider instead of creating a
* renderer for every single item. This allows for optimal performance with
* very large data providers.
*
* The following example creates a grouped list, gives it a data
* provider, tells the item renderer how to interpret the data, and listens
* for when the selection changes:
*
*
* var list:GroupedList = new GroupedList();
*
* list.dataProvider = new HierarchicalCollection(
* [
* {
* header: "Dairy",
* children:
* [
* { text: "Milk", thumbnail: textureAtlas.getTexture( "milk" ) },
* { text: "Cheese", thumbnail: textureAtlas.getTexture( "cheese" ) },
* ]
* },
* {
* header: "Bakery",
* children:
* [
* { text: "Bread", thumbnail: textureAtlas.getTexture( "bread" ) },
* ]
* },
* {
* header: "Produce",
* children:
* [
* { text: "Bananas", thumbnail: textureAtlas.getTexture( "bananas" ) },
* { text: "Lettuce", thumbnail: textureAtlas.getTexture( "lettuce" ) },
* { text: "Onion", thumbnail: textureAtlas.getTexture( "onion" ) },
* ]
* },
* ]);
*
* list.itemRendererFactory = function():IGroupedListItemRenderer
* {
* var renderer:DefaultGroupedListItemRenderer = new DefaultGroupedListItemRenderer();
* renderer.labelField = "text";
* renderer.iconSourceField = "thumbnail";
* return renderer;
* };
*
* list.addEventListener( Event.CHANGE, list_changeHandler );
*
* this.addChild( list );
*
* @see ../../../help/grouped-list.html How to use the Feathers GroupedList component
* @see ../../../help/default-item-renderers.html How to use the Feathers default item renderer
* @see ../../../help/item-renderers.html Creating custom item renderers for the Feathers List and GroupedList components
* @see feathers.controls.List
*/
public class GroupedList extends Scroller implements IFocusContainer
{
/**
* @private
*/
private static const HELPER_POINT:Point = new Point();
/**
* The default IStyleProvider
for all GroupedList
* components.
*
* @default null
* @see feathers.core.FeathersControl#styleProvider
*/
public static var globalStyleProvider:IStyleProvider;
/**
* An alternate style name to use with GroupedList
to allow
* a theme to give it an inset style. If a theme does not provide a
* style for an inset grouped list, the theme will automatically fall
* back to using the default grouped list style.
*
* An alternate style name should always be added to a component's
* styleNameList
before the component is initialized. If
* the style name is added later, it will be ignored.
*
* In the following example, the inset style is applied to a grouped
* list:
*
*
* var list:GroupedList = new GroupedList();
* list.styleNameList.add( GroupedList.ALTERNATE_STYLE_NAME_INSET_GROUPED_LIST );
* this.addChild( list );
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const ALTERNATE_STYLE_NAME_INSET_GROUPED_LIST:String = "feathers-inset-grouped-list";
/**
* The default name to use with header renderers.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_HEADER_RENDERER:String = "feathers-grouped-list-header-renderer";
/**
* An alternate name to use with header renderers to give them an inset
* style. This name is usually only referenced inside themes.
*
* In the following example, the inset style is applied to a grouped
* list's header:
*
*
* list.customHeaderRendererStyleName = GroupedList.ALTERNATE_CHILD_STYLE_NAME_INSET_HEADER_RENDERER;
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const ALTERNATE_CHILD_STYLE_NAME_INSET_HEADER_RENDERER:String = "feathers-grouped-list-inset-header-renderer";
/**
* The default name to use with footer renderers.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_FOOTER_RENDERER:String = "feathers-grouped-list-footer-renderer";
/**
* An alternate name to use with footer renderers to give them an inset
* style. This name is usually only referenced inside themes.
*
* In the following example, the inset style is applied to a grouped
* list's footer:
*
*
* list.customFooterRendererStyleName = GroupedList.ALTERNATE_CHILD_STYLE_NAME_INSET_FOOTER_RENDERER;
*/
public static const ALTERNATE_CHILD_STYLE_NAME_INSET_FOOTER_RENDERER:String = "feathers-grouped-list-inset-footer-renderer";
/**
* An alternate name to use with item renderers to give them an inset
* style. This name is usually only referenced inside themes.
*
* In the following example, the inset style is applied to a grouped
* list's item renderer:
*
*
* list.customItemRendererStyleName = GroupedList.ALTERNATE_CHILD_STYLE_NAME_INSET_ITEM_RENDERER;
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const ALTERNATE_CHILD_STYLE_NAME_INSET_ITEM_RENDERER:String = "feathers-grouped-list-inset-item-renderer";
/**
* An alternate name to use for item renderers to give them an inset
* style. Typically meant to be used for the renderer of the first item
* in a group. This name is usually only referenced inside themes.
*
* In the following example, the inset style is applied to a grouped
* list's first item renderer:
*
*
* list.customFirstItemRendererStyleName = GroupedList.ALTERNATE_CHILD_STYLE_NAME_INSET_FIRST_ITEM_RENDERER;
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const ALTERNATE_CHILD_STYLE_NAME_INSET_FIRST_ITEM_RENDERER:String = "feathers-grouped-list-inset-first-item-renderer";
/**
* An alternate name to use for item renderers to give them an inset
* style. Typically meant to be used for the renderer of the last item
* in a group. This name is usually only referenced inside themes.
*
* In the following example, the inset style is applied to a grouped
* list's last item renderer:
*
*
* list.customLastItemRendererStyleName = GroupedList.ALTERNATE_CHILD_STYLE_NAME_INSET_LAST_ITEM_RENDERER;
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const ALTERNATE_CHILD_STYLE_NAME_INSET_LAST_ITEM_RENDERER:String = "feathers-grouped-list-inset-last-item-renderer";
/**
* An alternate name to use for item renderers to give them an inset
* style. Typically meant to be used for the renderer of an item in a
* group that has no other items. This name is usually only referenced
* inside themes.
*
* In the following example, the inset style is applied to a grouped
* list's single item renderer:
*
*
* list.customSingleItemRendererStyleName = GroupedList.ALTERNATE_CHILD_STYLE_NAME_INSET_SINGLE_ITEM_RENDERER;
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const ALTERNATE_CHILD_STYLE_NAME_INSET_SINGLE_ITEM_RENDERER:String = "feathers-grouped-list-inset-single-item-renderer";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollPolicy.AUTO
.
*
* 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 SCROLL_POLICY_AUTO:String = "auto";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollPolicy.ON
.
*
* 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 SCROLL_POLICY_ON:String = "on";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollPolicy.OFF
.
*
* 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 SCROLL_POLICY_OFF:String = "off";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollBarDisplayMode.FLOAT
.
*
* 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 SCROLL_BAR_DISPLAY_MODE_FLOAT:String = "float";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollBarDisplayMode.FIXED
.
*
* 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 SCROLL_BAR_DISPLAY_MODE_FIXED:String = "fixed";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollBarDisplayMode.FIXED_FLOAT
.
*
* 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 SCROLL_BAR_DISPLAY_MODE_FIXED_FLOAT:String = "fixedFloat";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollBarDisplayMode.NONE
.
*
* 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 SCROLL_BAR_DISPLAY_MODE_NONE:String = "none";
/**
* @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 VERTICAL_SCROLL_BAR_POSITION_RIGHT:String = "right";
/**
* @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 VERTICAL_SCROLL_BAR_POSITION_LEFT:String = "left";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollInteractionMode.TOUCH
.
*
* 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 INTERACTION_MODE_TOUCH:String = "touch";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollInteractionMode.MOUSE
.
*
* 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 INTERACTION_MODE_MOUSE:String = "mouse";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.ScrollInteractionMode.TOUCH_AND_SCROLL_BARS
.
*
* 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 INTERACTION_MODE_TOUCH_AND_SCROLL_BARS:String = "touchAndScrollBars";
/**
* @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 MOUSE_WHEEL_SCROLL_DIRECTION_VERTICAL:String = "vertical";
/**
* @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 MOUSE_WHEEL_SCROLL_DIRECTION_HORIZONTAL:String = "horizontal";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.DecelerationRate.NORMAL
.
*
* 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 DECELERATION_RATE_NORMAL:Number = 0.998;
/**
* @private
* DEPRECATED: Replaced by feathers.controls.DecelerationRate.FAST
.
*
* 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 DECELERATION_RATE_FAST:Number = 0.99;
/**
* Constructor.
*/
public function GroupedList()
{
super();
}
/**
* @private
* The guts of the List's functionality. Handles layout and selection.
*/
protected var dataViewPort:GroupedListDataViewPort;
/**
* @private
*/
override protected function get defaultStyleProvider():IStyleProvider
{
return GroupedList.globalStyleProvider;
}
/**
* @private
*/
override public function get isFocusEnabled():Boolean
{
return (this._isSelectable || this._minHorizontalScrollPosition != this._maxHorizontalScrollPosition ||
this._minVerticalScrollPosition != this._maxVerticalScrollPosition) &&
this._isEnabled && this._isFocusEnabled;
}
/**
* @private
*/
protected var _isChildFocusEnabled:Boolean = true;
/**
* @copy feathers.core.IFocusContainer#isChildFocusEnabled
*
* @default true
*
* @see #isFocusEnabled
*/
public function get isChildFocusEnabled():Boolean
{
return this._isEnabled && this._isChildFocusEnabled;
}
/**
* @private
*/
public function set isChildFocusEnabled(value:Boolean):void
{
this._isChildFocusEnabled = value;
}
/**
* @private
*/
protected var _layout:ILayout;
/**
* The layout algorithm used to position and, optionally, size the
* list's items.
*
* By default, if no layout is provided by the time that the list
* initializes, a vertical layout with options targeted at touch screens
* is created.
*
* The following example tells the list to use a horizontal layout:
*
*
* var layout:HorizontalLayout = new HorizontalLayout();
* layout.gap = 20;
* layout.padding = 20;
* list.layout = layout;
*/
public function get layout():ILayout
{
return this._layout;
}
/**
* @private
*/
public function set layout(value:ILayout):void
{
if(this._layout == value)
{
return;
}
if(this._layout)
{
this._layout.removeEventListener(Event.SCROLL, layout_scrollHandler);
}
this._layout = value;
if(this._layout is IVariableVirtualLayout)
{
this._layout.addEventListener(Event.SCROLL, layout_scrollHandler);
}
this.invalidate(INVALIDATION_FLAG_LAYOUT);
}
/**
* @private
*/
protected var _dataProvider:HierarchicalCollection;
/**
* The collection of data displayed by the list. Changing this property
* to a new value is considered a drastic change to the list's data, so
* the horizontal and vertical scroll positions will be reset, and the
* list's selection will be cleared.
*
* The following example passes in a data provider and tells the item
* renderer how to interpret the data:
*
*
* list.dataProvider = new HierarchicalCollection(
* [
* {
* header: "Dairy",
* children:
* [
* { text: "Milk", thumbnail: textureAtlas.getTexture( "milk" ) },
* { text: "Cheese", thumbnail: textureAtlas.getTexture( "cheese" ) },
* ]
* },
* {
* header: "Bakery",
* children:
* [
* { text: "Bread", thumbnail: textureAtlas.getTexture( "bread" ) },
* ]
* },
* {
* header: "Produce",
* children:
* [
* { text: "Bananas", thumbnail: textureAtlas.getTexture( "bananas" ) },
* { text: "Lettuce", thumbnail: textureAtlas.getTexture( "lettuce" ) },
* { text: "Onion", thumbnail: textureAtlas.getTexture( "onion" ) },
* ]
* },
* ]);
*
* list.itemRendererFactory = function():IGroupedListItemRenderer
* {
* var renderer:DefaultGroupedListItemRenderer = new DefaultGroupedListItemRenderer();
* renderer.labelField = "text";
* renderer.iconSourceField = "thumbnail";
* return renderer;
* };
*
* By default, a HierarchicalCollection
accepts an
* Array
containing objects for each group. By default, the
* header
and footer
fields in each group will
* contain data to pass to the header and footer renderers of the
* grouped list. The children
field of each group should be
* be an Array
of data where each item is passed to an item
* renderer.
*
* A custom data descriptor may be passed to the
* HierarchicalCollection
to tell it to parse the data
* source differently than the default behavior described above. For
* instance, you might want to use Vector
instead of
* Array
or structure the data differently. Custom data
* descriptors may be implemented with the
* IHierarchicalCollectionDataDescriptor
interface.
*
* Warning: A grouped list's data provider cannot contain
* duplicate items. To display the same item in multiple item renderers,
* you must create separate objects with the same properties. This
* limitation exists because it significantly improves performance.
*
* Warning: If the data provider contains display objects,
* concrete textures, or anything that needs to be disposed, those
* objects will not be automatically disposed when the grouped list is
* disposed. Similar to how starling.display.Image
cannot
* automatically dispose its texture because the texture may be used
* by other display objects, a list cannot dispose its data provider
* because the data provider may be used by other lists. See the
* dispose()
function on HierarchicalCollection
* to see how the data provider can be disposed properly.
*
* @default null
*
* @see feathers.data.HierarchicalCollection
* @see feathers.data.IHierarchicalCollectionDataDescriptor
*/
public function get dataProvider():HierarchicalCollection
{
return this._dataProvider;
}
/**
* @private
*/
public function set dataProvider(value:HierarchicalCollection):void
{
if(this._dataProvider == value)
{
return;
}
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.RESET, dataProvider_resetHandler);
this._dataProvider.removeEventListener(Event.CHANGE, dataProvider_changeHandler);
}
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.RESET, dataProvider_resetHandler);
this._dataProvider.addEventListener(Event.CHANGE, dataProvider_changeHandler);
}
//reset the scroll position because this is a drastic change and
//the data is probably completely different
this.horizontalScrollPosition = 0;
this.verticalScrollPosition = 0;
//clear the selection for the same reason
this.setSelectedLocation(-1, -1);
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _isSelectable:Boolean = true;
/**
* Determines if an item in the list may be selected.
*
* The following example disables selection:
*
*
* list.isSelectable = false;
*
* @default true
*/
public function get isSelectable():Boolean
{
return this._isSelectable;
}
/**
* @private
*/
public function set isSelectable(value:Boolean):void
{
if(this._isSelectable == value)
{
return;
}
this._isSelectable = value;
if(!this._isSelectable)
{
this.setSelectedLocation(-1, -1);
}
this.invalidate(INVALIDATION_FLAG_SELECTED);
}
/**
* @private
*/
protected var _selectedGroupIndex:int = -1;
/**
* The group index of the currently selected item. Returns -1
* if no item is selected.
*
* Because the selection consists of both a group index and an item
* index, this property does not have a setter. To change the selection,
* call setSelectedLocation()
instead.
*
* The following example listens for when selection changes and
* requests the selected group index and selected item index:
*
*
* function list_changeHandler( event:Event ):void
* {
* var list:List = GroupedList(event.currentTarget);
* var groupIndex:int = list.selectedGroupIndex;
* var itemIndex:int = list.selectedItemIndex;
*
* }
* list.addEventListener( Event.CHANGE, list_changeHandler );
*
* @default -1
*
* @see #selectedItemIndex
* @see #setSelectedLocation()
*/
public function get selectedGroupIndex():int
{
return this._selectedGroupIndex;
}
/**
* @private
*/
protected var _selectedItemIndex:int = -1;
/**
* The item index of the currently selected item. Returns -1
* if no item is selected.
*
* Because the selection consists of both a group index and an item
* index, this property does not have a setter. To change the selection,
* call setSelectedLocation()
instead.
*
* The following example listens for when selection changes and
* requests the selected group index and selected item index:
*
*
* function list_changeHandler( event:Event ):void
* {
* var list:GroupedList = GroupedList( event.currentTarget );
* var groupIndex:int = list.selectedGroupIndex;
* var itemIndex:int = list.selectedItemIndex;
*
* }
* list.addEventListener( Event.CHANGE, list_changeHandler );
*
* @default -1
*
* @see #selectedGroupIndex
* @see #setSelectedLocation()
*/
public function get selectedItemIndex():int
{
return this._selectedItemIndex;
}
/**
* The currently selected item. Returns null
if no item is
* selected.
*
* The following example listens for when selection changes and
* requests the selected item:
*
*
* function list_changeHandler( event:Event ):void
* {
* var list:GroupedList = GroupedList( event.currentTarget );
* var item:Object = list.selectedItem;
*
* }
* list.addEventListener( Event.CHANGE, list_changeHandler );
*
* @default null
*/
public function get selectedItem():Object
{
if(!this._dataProvider || this._selectedGroupIndex < 0 || this._selectedItemIndex < 0)
{
return null;
}
return this._dataProvider.getItemAt(this._selectedGroupIndex, this._selectedItemIndex);
}
/**
* @private
*/
public function set selectedItem(value:Object):void
{
if(!this._dataProvider)
{
this.setSelectedLocation(-1, -1);
return;
}
var result:Vector. = this._dataProvider.getItemLocation(value);
if(result.length == 2)
{
this.setSelectedLocation(result[0], result[1]);
}
else
{
this.setSelectedLocation(-1, -1);
}
}
/**
* @private
*/
protected var _itemRendererType:Class = DefaultGroupedListItemRenderer;
/**
* The class used to instantiate item renderers. Must implement the
* IGroupedListItemRenderer
interface.
*
* To customize properties on the item renderer, use
* itemRendererFactory
instead.
*
* The following example changes the item renderer type:
*
*
* list.itemRendererType = CustomItemRendererClass;
*
* The first item and last item in a group may optionally use
* different item renderer types, if desired. Use the
* firstItemRendererType
and lastItemRendererType
,
* respectively. Additionally, if a group contains only one item, it may
* also have a different type. Use the singleItemRendererType
.
* Finally, factories for each of these types may also be customized.
*
* @default feathers.controls.renderers.DefaultGroupedListItemRenderer
*
* @see feathers.controls.renderers.IGroupedListItemRenderer
* @see #itemRendererFactory
* @see #firstItemRendererType
* @see #lastItemRendererType
* @see #singleItemRendererType
*/
public function get itemRendererType():Class
{
return this._itemRendererType;
}
/**
* @private
*/
public function set itemRendererType(value:Class):void
{
if(this._itemRendererType == value)
{
return;
}
this._itemRendererType = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _itemRendererFactories:Object;
/**
* @private
*/
protected var _itemRendererFactory:Function;
/**
* A function called that is expected to return a new item renderer. Has
* a higher priority than itemRendererType
. Typically, you
* would use an itemRendererFactory
instead of an
* itemRendererType
if you wanted to initialize some
* properties on each separate item renderer, such as skins.
*
* The function is expected to have the following signature:
*
* function():IGroupedListItemRenderer
*
* The following example provides a factory for the item renderer:
*
*
* list.itemRendererFactory = function():IGroupedListItemRenderer
* {
* var renderer:CustomItemRendererClass = new CustomItemRendererClass();
* renderer.backgroundSkin = new Quad( 10, 10, 0xff0000 );
* return renderer;
* };
*
* The first item and last item in a group may optionally use
* different item renderer factories, if desired. Use the
* firstItemRendererFactory
and lastItemRendererFactory
,
* respectively. Additionally, if a group contains only one item, it may
* also have a different factory. Use the singleItemRendererFactory
.
*
* @default null
*
* @see feathers.controls.renderers.IGroupedListItemRenderer
* @see #itemRendererType
* @see #setItemRendererFactoryWithID()
*/
public function get itemRendererFactory():Function
{
return this._itemRendererFactory;
}
/**
* @private
*/
public function set itemRendererFactory(value:Function):void
{
if(this._itemRendererFactory === value)
{
return;
}
this._itemRendererFactory = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _factoryIDFunction:Function;
/**
* When a list requires multiple item renderer types, this function is
* used to determine which type of item renderer is required for a
* specific item (or index). Returns the ID of the item renderer type
* to use for the item, or null
if the default
* itemRendererFactory
should be used.
*
* The function is expected to have one of the following
* signatures:
*
* function(item:Object):String
*
* function(item:Object, groupIndex:int, itemIndex:int):String
*
* The following example provides a factoryIDFunction
:
*
*
* function regularItemFactory():IGroupedListItemRenderer
* {
* return new DefaultGroupedListItemRenderer();
* }
* function firstItemFactory():IGroupedListItemRenderer
* {
* return new CustomItemRenderer();
* }
* list.setItemRendererFactoryWithID( "regular-item", regularItemFactory );
* list.setItemRendererFactoryWithID( "first-item", firstItemFactory );
*
* list.factoryIDFunction = function( item:Object, groupIndex:int, itemIndex:int ):String
* {
* if(index === 0)
* {
* return "first-item";
* }
* return "regular-item";
* };
*
* @default null
*
* @see #setItemRendererFactoryWithID()
* @see #itemRendererFactory
*/
public function get factoryIDFunction():Function
{
return this._factoryIDFunction;
}
/**
* @private
*/
public function set factoryIDFunction(value:Function):void
{
if(this._factoryIDFunction === value)
{
return;
}
this._factoryIDFunction = value;
if(value !== null && this._itemRendererFactories === null)
{
this._itemRendererFactories = {};
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _typicalItem:Object = null;
/**
* Used to auto-size the list when a virtualized layout is used. If the
* list's width or height is unknown, the list will try to automatically
* pick an ideal size. This item is used to create a sample item
* renderer to measure item renderers that are virtual and not visible
* in the viewport.
*
* The following example provides a typical item:
*
*
* list.typicalItem = { text: "A typical item", thumbnail: texture };
* list.itemRendererProperties.labelField = "text";
* list.itemRendererProperties.iconSourceField = "thumbnail";
*
* @default null
*/
public function get typicalItem():Object
{
return this._typicalItem;
}
/**
* @private
*/
public function set typicalItem(value:Object):void
{
if(this._typicalItem == value)
{
return;
}
this._typicalItem = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _customItemRendererStyleName:String;
/**
* A style name to add to all item renderers in this list. Typically
* used by a theme to provide different styles to different grouped
* lists.
*
* The following example sets the item renderer style name:
*
*
* list.customItemRendererStyleName = "my-custom-item-renderer";
*
* In your theme, you can target this sub-component style name to
* provide different skins than the default:
*
*
* getStyleProviderForClass( DefaultGroupedListItemRenderer ).setFunctionForStyleName( "my-custom-item-renderer", setCustomItemRendererStyles );
*
* @default null
*
* @see feathers.core.FeathersControl#styleNameList
* @see #customFirstItemRendererStyleName
* @see #customLastItemRendererStyleName
* @see #customSingleItemRendererStyleName
*/
public function get customItemRendererStyleName():String
{
return this._customItemRendererStyleName;
}
/**
* @private
*/
public function set customItemRendererStyleName(value:String):void
{
if(this._customItemRendererStyleName == value)
{
return;
}
this._customItemRendererStyleName = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _itemRendererProperties:PropertyProxy;
/**
* An object that stores properties for all of the list's item
* renderers, and the properties will be passed down to every item
* renderer when the list validates. The available properties
* depend on which IGroupedListItemRenderer
implementation
* is returned by itemRendererFactory
.
*
* By default, the itemRendererFactory
will return a
* DefaultGroupedListItemRenderer
instance. If you aren't
* using a custom item renderer, you can refer to
* feathers.controls.renderers.DefaultGroupedListItemRenderer
* for a list of available properties.
*
* These properties are shared by every item renderer, so anything
* that cannot be shared (such as display objects, which cannot be added
* to multiple parents) should be passed to item renderers using the
* itemRendererFactory
or in the theme.
*
* The following example customizes some item renderer properties
* (this example assumes that the item renderer's label text renderer
* is a BitmapFontTextRenderer
):
*
*
* list.itemRendererProperties.labelField = "text";
* list.itemRendererProperties.accessoryField = "control";
*
* 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 itemRendererFactory
function instead
* of using itemRendererProperties
will result in better
* performance.
*
* @default null
*
* @see #itemRendererFactory
* @see feathers.controls.renderers.IGroupedListItemRenderer
* @see feathers.controls.renderers.DefaultGroupedListItemRenderer
*/
public function get itemRendererProperties():Object
{
if(!this._itemRendererProperties)
{
this._itemRendererProperties = new PropertyProxy(childProperties_onChange);
}
return this._itemRendererProperties;
}
/**
* @private
*/
public function set itemRendererProperties(value:Object):void
{
if(this._itemRendererProperties == 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._itemRendererProperties)
{
this._itemRendererProperties.removeOnChangeCallback(childProperties_onChange);
}
this._itemRendererProperties = PropertyProxy(value);
if(this._itemRendererProperties)
{
this._itemRendererProperties.addOnChangeCallback(childProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _firstItemRendererType:Class;
/**
* The class used to instantiate the item renderer for the first item in
* a group. Must implement the IGroupedListItemRenderer
* interface.
*
* The following example changes the first item renderer type:
*
*
* list.firstItemRendererType = CustomItemRendererClass;
*
* @default null
*
* @see feathers.controls.renderer.IGroupedListItemRenderer
* @see #itemRendererType
* @see #lastItemRendererType
* @see #singleItemRendererType
*/
public function get firstItemRendererType():Class
{
return this._firstItemRendererType;
}
/**
* @private
*/
public function set firstItemRendererType(value:Class):void
{
if(this._firstItemRendererType == value)
{
return;
}
this._firstItemRendererType = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _firstItemRendererFactory:Function;
/**
* A function called that is expected to return a new item renderer for
* the first item in a group. Has a higher priority than
* firstItemRendererType
. Typically, you would use an
* firstItemRendererFactory
instead of an
* firstItemRendererType
if you wanted to initialize some
* properties on each separate item renderer, such as skins.
*
* The function is expected to have the following signature:
*
* function():IGroupedListItemRenderer
*
* The following example provides a factory for the item renderer
* used for the first item in a group:
*
*
* list.firstItemRendererFactory = function():IGroupedListItemRenderer
* {
* var renderer:CustomItemRendererClass = new CustomItemRendererClass();
* renderer.backgroundSkin = new Quad( 10, 10, 0xff0000 );
* return renderer;
* };
*
* @default null
*
* @see feathers.controls.renderers.IGroupedListItemRenderer
* @see #firstItemRendererType
* @see #itemRendererFactory
* @see #lastItemRendererFactory
* @see #singleItemRendererFactory
*/
public function get firstItemRendererFactory():Function
{
return this._firstItemRendererFactory;
}
/**
* @private
*/
public function set firstItemRendererFactory(value:Function):void
{
if(this._firstItemRendererFactory === value)
{
return;
}
this._firstItemRendererFactory = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _customFirstItemRendererStyleName:String;
/**
* A style name to add to all item renderers in this grouped list that
* are the first item in a group. Typically used by a theme to provide
* different styles to different grouped lists, and to differentiate
* first items from regular items if they are created with the same
* class. If this value is null
, the regular
* customItemRendererStyleName
will be used instead.
*
* The following example provides a style name for the first item
* renderer in each group:
*
*
* list.customFirstItemRendererStyleName = "my-custom-first-item-renderer";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( DefaultGroupedListItemRenderer ).setFunctionForStyleName( "my-custom-first-item-renderer", setCustomFirstItemRendererStyles );
*
* @default null
*
* @see feathers.core.FeathersControl#styleNameList
* @see #customItemRendererStyleName
* @see #customLastItemRendererStyleName
* @see #customSingleItemRendererStyleName
*/
public function get customFirstItemRendererStyleName():String
{
return this._customFirstItemRendererStyleName;
}
/**
* @private
*/
public function set customFirstItemRendererStyleName(value:String):void
{
if(this._customFirstItemRendererStyleName == value)
{
return;
}
this._customFirstItemRendererStyleName = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _lastItemRendererType:Class;
/**
* The class used to instantiate the item renderer for the last item in
* a group. Must implement the IGroupedListItemRenderer
* interface.
*
* The following example changes the last item renderer type:
*
*
* list.lastItemRendererType = CustomItemRendererClass;
*
* @default null
*
* @see feathers.controls.renderer.IGroupedListItemRenderer
* @see #lastItemRendererFactory
* @see #itemRendererType
* @see #firstItemRendererType
* @see #singleItemRendererType
*/
public function get lastItemRendererType():Class
{
return this._lastItemRendererType;
}
/**
* @private
*/
public function set lastItemRendererType(value:Class):void
{
if(this._lastItemRendererType == value)
{
return;
}
this._lastItemRendererType = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _lastItemRendererFactory:Function;
/**
* A function called that is expected to return a new item renderer for
* the last item in a group. Has a higher priority than
* lastItemRendererType
. Typically, you would use an
* lastItemRendererFactory
instead of an
* lastItemRendererType
if you wanted to initialize some
* properties on each separate item renderer, such as skins.
*
* The function is expected to have the following signature:
*
* function():IGroupedListItemRenderer
*
* The following example provides a factory for the item renderer
* used for the last item in a group:
*
*
* list.firstItemRendererFactory = function():IGroupedListItemRenderer
* {
* var renderer:CustomItemRendererClass = new CustomItemRendererClass();
* renderer.backgroundSkin = new Quad( 10, 10, 0xff0000 );
* return renderer;
* };
*
* @default null
*
* @see feathers.controls.renderers.IGroupedListItemRenderer
* @see #lastItemRendererType
* @see #itemRendererFactory
* @see #firstItemRendererFactory
* @see #singleItemRendererFactory
*/
public function get lastItemRendererFactory():Function
{
return this._lastItemRendererFactory;
}
/**
* @private
*/
public function set lastItemRendererFactory(value:Function):void
{
if(this._lastItemRendererFactory === value)
{
return;
}
this._lastItemRendererFactory = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _customLastItemRendererStyleName:String;
/**
* A style name to add to all item renderers in this grouped list that
* are the last item in a group. Typically used by a theme to provide
* different styles to different grouped lists, and to differentiate
* last items from regular items if they are created with the same
* class. If this value is null
the regular
* customItemRendererStyleName
will be used instead.
*
* The following example provides a style name for the last item
* renderer in each group:
*
*
* list.customLastItemRendererStyleName = "my-custom-last-item-renderer";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( DefaultGroupedListItemRenderer ).setFunctionForStyleName( "my-custom-last-item-renderer", setCustomLastItemRendererStyles );
*
* @default null
*
* @see feathers.core.FeathersControl#styleNameList
* @see #customItemRendererStyleName
* @see #customFirstItemRendererStyleName
* @see #customSingleItemRendererStyleName
*/
public function get customLastItemRendererStyleName():String
{
return this._customLastItemRendererStyleName;
}
/**
* @private
*/
public function set customLastItemRendererStyleName(value:String):void
{
if(this._customLastItemRendererStyleName == value)
{
return;
}
this._customLastItemRendererStyleName = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _singleItemRendererType:Class;
/**
* The class used to instantiate the item renderer for an item in a
* group with no other items. Must implement the
* IGroupedListItemRenderer
interface.
*
* The following example changes the single item renderer type:
*
*
* list.singleItemRendererType = CustomItemRendererClass;
*
* @default null
*
* @see feathers.controls.renderer.IGroupedListItemRenderer
* @see #singleItemRendererFactory
* @see #itemRendererType
* @see #firstItemRendererType
* @see #lastItemRendererType
*/
public function get singleItemRendererType():Class
{
return this._singleItemRendererType;
}
/**
* @private
*/
public function set singleItemRendererType(value:Class):void
{
if(this._singleItemRendererType == value)
{
return;
}
this._singleItemRendererType = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _singleItemRendererFactory:Function;
/**
* A function called that is expected to return a new item renderer for
* an item in a group with no other items. Has a higher priority than
* singleItemRendererType
. Typically, you would use an
* singleItemRendererFactory
instead of an
* singleItemRendererType
if you wanted to initialize some
* properties on each separate item renderer, such as skins.
*
* The function is expected to have the following signature:
*
* function():IGroupedListItemRenderer
*
* The following example provides a factory for the item renderer
* used for when only one item appears in a group:
*
*
* list.firstItemRendererFactory = function():IGroupedListItemRenderer
* {
* var renderer:CustomItemRendererClass = new CustomItemRendererClass();
* renderer.backgroundSkin = new Quad( 10, 10, 0xff0000 );
* return renderer;
* };
*
* @default null
*
* @see feathers.controls.renderers.IGroupedListItemRenderer
* @see #singleItemRendererType
* @see #itemRendererFactory
* @see #firstItemRendererFactory
* @see #lastItemRendererFactory
*/
public function get singleItemRendererFactory():Function
{
return this._singleItemRendererFactory;
}
/**
* @private
*/
public function set singleItemRendererFactory(value:Function):void
{
if(this._singleItemRendererFactory === value)
{
return;
}
this._singleItemRendererFactory = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _customSingleItemRendererStyleName:String;
/**
* A style name to add to all item renderers in this grouped list that
* are a single item in a group with no other items. Typically used by a
* theme to provide different styles to different grouped lists, and to
* differentiate single items from regular items if they are created
* with the same class. If this value is null
the regular
* customItemRendererStyleName
will be used instead.
*
* The following example provides a style name for a single item
* renderer in each group:
*
*
* list.customSingleItemRendererStyleName = "my-custom-single-item-renderer";
*
* In your theme, you can target this sub-component style name to
* provide different skins than the default style:
*
*
* getStyleProviderForClass( DefaultGroupedListItemRenderer ).setFunctionForStyleName( "my-custom-single-item-renderer", setCustomSingleItemRendererStyles );
*
* @default null
*
* @see feathers.core.FeathersControl#styleNameList
* @see #customItemRendererStyleName
* @see #customFirstItemRendererStyleName
* @see #customLastItemRendererStyleName
*/
public function get customSingleItemRendererStyleName():String
{
return this._customSingleItemRendererStyleName;
}
/**
* @private
*/
public function set customSingleItemRendererStyleName(value:String):void
{
if(this._customSingleItemRendererStyleName == value)
{
return;
}
this._customSingleItemRendererStyleName = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _headerRendererType:Class = DefaultGroupedListHeaderOrFooterRenderer;
/**
* The class used to instantiate header renderers. Must implement the
* IGroupedListHeaderOrFooterRenderer
interface.
*
* The following example changes the header renderer type:
*
*
* list.headerRendererType = CustomHeaderRendererClass;
*
* @default feathers.controls.renderers.DefaultGroupedListHeaderOrFooterRenderer
*
* @see feathers.controls.renderers.IGroupedListHeaderOrFooterRenderer
* @see #headerRendererFactory
*/
public function get headerRendererType():Class
{
return this._headerRendererType;
}
/**
* @private
*/
public function set headerRendererType(value:Class):void
{
if(this._headerRendererType == value)
{
return;
}
this._headerRendererType = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _headerRendererFactories:Object;
/**
* @private
*/
protected var _headerRendererFactory:Function;
/**
* A function called that is expected to return a new header renderer.
* Has a higher priority than headerRendererType
.
* Typically, you would use an headerRendererFactory
* instead of a headerRendererType
if you wanted to
* initialize some properties on each separate header renderer, such as
* skins.
*
* The function is expected to have the following signature:
*
* function():IGroupedListHeaderOrFooterRenderer
*
* The following example provides a factory for the header renderer:
*
*
* list.itemRendererFactory = function():IGroupedListHeaderOrFooterRenderer
* {
* var renderer:CustomHeaderRendererClass = new CustomHeaderRendererClass();
* renderer.backgroundSkin = new Quad( 10, 10, 0xff0000 );
* return renderer;
* };
*
* @default null
*
* @see feathers.controls.renderers.IGroupedListHeaderOrFooterRenderer
* @see #headerRendererType
* @see #setHeaderRendererFactoryWithID()
*/
public function get headerRendererFactory():Function
{
return this._headerRendererFactory;
}
/**
* @private
*/
public function set headerRendererFactory(value:Function):void
{
if(this._headerRendererFactory === value)
{
return;
}
this._headerRendererFactory = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _headerFactoryIDFunction:Function;
/**
* When a list requires multiple header renderer types, this function is
* used to determine which type of header renderer is required for a
* specific header (or group index). Returns the ID of the factory
* to use for the header, or null
if the default
* headerRendererFactory
should be used.
*
* The function is expected to have one of the following
* signatures:
*
* function(header:Object):String
*
* function(header:Object, groupIndex:int):String
*
* The following example provides a headerFactoryIDFunction
:
*
*
* function regularHeaderFactory():IGroupedListHeaderRenderer
* {
* return new DefaultGroupedListHeaderOrFooterRenderer();
* }
* function customHeaderFactory():IGroupedListHeaderRenderer
* {
* return new CustomHeaderRenderer();
* }
* list.setHeaderRendererFactoryWithID( "regular-header", regularHeaderFactory );
* list.setHeaderRendererFactoryWithID( "custom-header", customHeaderFactory );
*
* list.headerFactoryIDFunction = function( header:Object, groupIndex:int ):String
* {
* //check if the subTitle property exists in the header data
* if( "subTitle" in header )
* {
* return "custom-header";
* }
* return "regular-header";
* };
*
* @default null
*
* @see #setHeaderRendererFactoryWithID()
* @see #headerRendererFactory
*/
public function get headerFactoryIDFunction():Function
{
return this._headerFactoryIDFunction;
}
/**
* @private
*/
public function set headerFactoryIDFunction(value:Function):void
{
if(this._headerFactoryIDFunction === value)
{
return;
}
this._headerFactoryIDFunction = value;
if(value !== null && this._headerRendererFactories === null)
{
this._headerRendererFactories = {};
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _customHeaderRendererStyleName:String = DEFAULT_CHILD_STYLE_NAME_HEADER_RENDERER;
/**
* A style name to add to all header renderers in this grouped list.
* Typically used by a theme to provide different styles to different
* grouped lists.
*
* The following example sets the header renderer style name:
*
*
* list.customHeaderRendererStyleName = "my-custom-header-renderer";
*
* In your theme, you can target this sub-component style name to
* provide different skins than the default:
*
*
* getStyleProviderForClass( DefaultGroupedListHeaderOrFooterRenderer ).setFunctionForStyleName( "my-custom-header-renderer", setCustomHeaderRendererStyles );
*
* @default null
*
* @see feathers.core.FeathersControl#styleNameList
*/
public function get customHeaderRendererStyleName():String
{
return this._customHeaderRendererStyleName;
}
/**
* @private
*/
public function set customHeaderRendererStyleName(value:String):void
{
if(this._customHeaderRendererStyleName == value)
{
return;
}
this._customHeaderRendererStyleName = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _headerRendererProperties:PropertyProxy;
/**
* An object that stores properties for all of the list's header
* renderers, and the properties will be passed down to every header
* renderer when the list validates. The available properties
* depend on which IGroupedListHeaderOrFooterRenderer
* implementation is returned by headerRendererFactory
.
*
* By default, the headerRendererFactory
will return a
* DefaultGroupedListHeaderOrFooterRenderer
instance. If
* you aren't using a custom header renderer, you can refer to
* feathers.controls.renderers.DefaultGroupedListHeaderOrFooterRenderer
* for a list of available properties.
*
* These properties are shared by every header renderer, so anything
* that cannot be shared (such as display objects, which cannot be added
* to multiple parents) should be passed to header renderers using the
* headerRendererFactory
or in the theme.
*
* The following example customizes some header renderer properties:
*
*
* list.headerRendererProperties.contentLabelField = "headerText";
* list.headerRendererProperties.contentLabelStyleName = "custom-header-renderer-content-label";
*
* 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 headerRendererFactory
function instead
* of using headerRendererProperties
will result in better
* performance.
*
* @default null
*
* @see #headerRendererFactory
* @see feathers.controls.renderers.IGroupedListHeaderOrFooterRenderer
* @see feathers.controls.renderers.DefaultGroupedListHeaderOrFooterRenderer
*/
public function get headerRendererProperties():Object
{
if(!this._headerRendererProperties)
{
this._headerRendererProperties = new PropertyProxy(childProperties_onChange);
}
return this._headerRendererProperties;
}
/**
* @private
*/
public function set headerRendererProperties(value:Object):void
{
if(this._headerRendererProperties == 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._headerRendererProperties)
{
this._headerRendererProperties.removeOnChangeCallback(childProperties_onChange);
}
this._headerRendererProperties = PropertyProxy(value);
if(this._headerRendererProperties)
{
this._headerRendererProperties.addOnChangeCallback(childProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _footerRendererType:Class = DefaultGroupedListHeaderOrFooterRenderer;
/**
* The class used to instantiate footer renderers. Must implement the
* IGroupedListHeaderOrFooterRenderer
interface.
*
* The following example changes the footer renderer type:
*
*
* list.footerRendererType = CustomFooterRendererClass;
*
* @default feathers.controls.renderers.DefaultGroupedListHeaderOrFooterRenderer
*
* @see feathers.controls.renderers.IGroupedListHeaderOrFooterRenderer
* @see #footerRendererFactory
*/
public function get footerRendererType():Class
{
return this._footerRendererType;
}
/**
* @private
*/
public function set footerRendererType(value:Class):void
{
if(this._footerRendererType == value)
{
return;
}
this._footerRendererType = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _footerRendererFactories:Object;
/**
* @private
*/
protected var _footerRendererFactory:Function;
/**
* A function called that is expected to return a new footer renderer.
* Has a higher priority than footerRendererType
.
* Typically, you would use an footerRendererFactory
* instead of a footerRendererType
if you wanted to
* initialize some properties on each separate footer renderer, such as
* skins.
*
* The function is expected to have the following signature:
*
* function():IGroupedListHeaderOrFooterRenderer
*
* The following example provides a factory for the footer renderer:
*
*
* list.itemRendererFactory = function():IGroupedListHeaderOrFooterRenderer
* {
* var renderer:CustomFooterRendererClass = new CustomFooterRendererClass();
* renderer.backgroundSkin = new Quad( 10, 10, 0xff0000 );
* return renderer;
* };
*
* @default null
*
* @see feathers.controls.renderers.IGroupedListHeaderOrFooterRenderer
* @see #footerRendererType
* @see #setFooterRendererFactoryWithID()
*/
public function get footerRendererFactory():Function
{
return this._footerRendererFactory;
}
/**
* @private
*/
public function set footerRendererFactory(value:Function):void
{
if(this._footerRendererFactory === value)
{
return;
}
this._footerRendererFactory = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _footerFactoryIDFunction:Function;
/**
* When a list requires multiple footer renderer types, this function is
* used to determine which type of footer renderer is required for a
* specific footer (or group index). Returns the ID of the factory
* to use for the footer, or null
if the default
* footerRendererFactory
should be used.
*
* The function is expected to have one of the following
* signatures:
*
* function(footer:Object):String
*
* function(footer:Object, groupIndex:int):String
*
* The following example provides a footerFactoryIDFunction
:
*
*
* function regularFooterFactory():IGroupedListFooterRenderer
* {
* return new DefaultGroupedListHeaderOrFooterRenderer();
* }
* function customFooterFactory():IGroupedListFooterRenderer
* {
* return new CustomFooterRenderer();
* }
* list.setFooterRendererFactoryWithID( "regular-footer", regularFooterFactory );
* list.setFooterRendererFactoryWithID( "custom-footer", customFooterFactory );
*
* list.footerFactoryIDFunction = function( footer:Object, groupIndex:int ):String
* {
* //check if the footerAccessory property exists in the footer data
* if( "footerAccessory" in footer )
* {
* return "custom-footer";
* }
* return "regular-footer";
* };
*
* @default null
*
* @see #setFooterRendererFactoryWithID()
* @see #footerRendererFactory
*/
public function get footerFactoryIDFunction():Function
{
return this._footerFactoryIDFunction;
}
/**
* @private
*/
public function set footerFactoryIDFunction(value:Function):void
{
if(this._footerFactoryIDFunction === value)
{
return;
}
this._footerFactoryIDFunction = value;
if(value !== null && this._footerRendererFactories === null)
{
this._footerRendererFactories = {};
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _customFooterRendererStyleName:String = DEFAULT_CHILD_STYLE_NAME_FOOTER_RENDERER;
/**
* A style name to add to all footer renderers in this grouped list.
* Typically used by a theme to provide different styles to different
* grouped lists.
*
* The following example sets the footer renderer style name:
*
*
* list.customFooterRendererStyleName = "my-custom-footer-renderer";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( DefaultGroupedListHeaderOrFooterRenderer ).setFunctionForStyleName( "my-custom-footer-renderer", setCustomFooterRendererStyles );
*
* @default null
*
* @see feathers.core.FeathersControl#styleNameList
*/
public function get customFooterRendererStyleName():String
{
return this._customFooterRendererStyleName;
}
/**
* @private
*/
public function set customFooterRendererStyleName(value:String):void
{
if(this._customFooterRendererStyleName == value)
{
return;
}
this._customFooterRendererStyleName = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _footerRendererProperties:PropertyProxy;
/**
* An object that stores properties for all of the list's footer
* renderers, and the properties will be passed down to every footer
* renderer when the list validates. The available properties
* depend on which IGroupedListHeaderOrFooterRenderer
* implementation is returned by footerRendererFactory
.
*
* By default, the footerRendererFactory
will return a
* DefaultGroupedListHeaderOrFooterRenderer
instance. If
* you aren't using a custom footer renderer, you can refer to
* feathers.controls.renderers.DefaultGroupedListHeaderOrFooterRenderer
* for a list of available properties.
*
* These properties are shared by every footer renderer, so anything
* that cannot be shared (such as display objects, which cannot be added
* to multiple parents) should be passed to footer renderers using the
* footerRendererFactory
or in the theme.
*
* The following example customizes some footer renderer properties:
*
*
* list.footerRendererProperties.contentLabelField = "footerText";
* list.footerRendererProperties.contentLabelStyleName = "custom-footer-renderer-content-label";
*
* 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 footerRendererFactory
function instead
* of using footerRendererProperties
will result in better
* performance.
*
* @default null
*
* @see #footerRendererFactory
* @see feathers.controls.renderers.IGroupedListHeaderOrFooterRenderer
* @see feathers.controls.renderers.DefaultGroupedListHeaderOrFooterRenderer
*/
public function get footerRendererProperties():Object
{
if(!this._footerRendererProperties)
{
this._footerRendererProperties = new PropertyProxy(childProperties_onChange);
}
return this._footerRendererProperties;
}
/**
* @private
*/
public function set footerRendererProperties(value:Object):void
{
if(this._footerRendererProperties == 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._footerRendererProperties)
{
this._footerRendererProperties.removeOnChangeCallback(childProperties_onChange);
}
this._footerRendererProperties = PropertyProxy(value);
if(this._footerRendererProperties)
{
this._footerRendererProperties.addOnChangeCallback(childProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _headerField:String = "header";
/**
* The field in a group that contains the data for a header. If the
* group does not have this field, and a headerFunction
is
* not defined, then no header will be displayed for the group. In other
* words, a header is optional, and a group may not have one.
*
* All of the header fields and functions, ordered by priority:
*
* headerFunction
* headerField
*
*
* The following example sets the header field:
*
*
* list.headerField = "alphabet";
*
* @default "header"
*
* @see #headerFunction
*/
public function get headerField():String
{
return this._headerField;
}
/**
* @private
*/
public function set headerField(value:String):void
{
if(this._headerField == value)
{
return;
}
this._headerField = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _headerFunction:Function;
/**
* A function used to generate header data for a specific group. If this
* function is not null, then the headerField
will be
* ignored.
*
* The function is expected to have the following signature:
* function( item:Object ):Object
*
* All of the header fields and functions, ordered by priority:
*
* headerFunction
* headerField
*
*
* The following example sets the header function:
*
*
* list.headerFunction = function( group:Object ):Object
* {
* return group.header;
* };
*
* @default null
*
* @see #headerField
*/
public function get headerFunction():Function
{
return this._headerFunction;
}
/**
* @private
*/
public function set headerFunction(value:Function):void
{
if(this._headerFunction == value)
{
return;
}
this._headerFunction = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _footerField:String = "footer";
/**
* The field in a group that contains the data for a footer. If the
* group does not have this field, and a footerFunction
is
* not defined, then no footer will be displayed for the group. In other
* words, a footer is optional, and a group may not have one.
*
* All of the footer fields and functions, ordered by priority:
*
* footerFunction
* footerField
*
*
* The following example sets the footer field:
*
*
* list.footerField = "controls";
*
* @default "footer"
*
* @see #footerFunction
*/
public function get footerField():String
{
return this._footerField;
}
/**
* @private
*/
public function set footerField(value:String):void
{
if(this._footerField == value)
{
return;
}
this._footerField = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _footerFunction:Function;
/**
* A function used to generate footer data for a specific group. If this
* function is not null, then the footerField
will be
* ignored.
*
* The function is expected to have the following signature:
* function( item:Object ):Object
*
* All of the footer fields and functions, ordered by priority:
*
* footerFunction
* footerField
*
*
* The following example sets the footer function:
*
*
* list.footerFunction = function( group:Object ):Object
* {
* return group.footer;
* };
*
* @default null
*
* @see #footerField
*/
public function get footerFunction():Function
{
return this._footerFunction;
}
/**
* @private
*/
public function set footerFunction(value:Function):void
{
if(this._footerFunction == value)
{
return;
}
this._footerFunction = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _keyScrollDuration:Number = 0.25;
/**
* The duration, in seconds, of the animation when the selected item is
* changed by keyboard navigation and the item scrolls into view.
*
* In the following example, the duration of the animation that
* scrolls the list to a new selected item is set to 500 milliseconds:
*
*
* list.keyScrollDuration = 0.5;
*
* @default 0.25
*/
public function get keyScrollDuration():Number
{
return this._keyScrollDuration;
}
/**
* @private
*/
public function set keyScrollDuration(value:Number):void
{
this._keyScrollDuration = value;
}
/**
* The pending group index to scroll to after validating. A value of
* -1
means that the scroller won't scroll to a group after
* validating.
*/
protected var pendingGroupIndex:int = -1;
/**
* The pending item index to scroll to after validating. A value of
* -1
means that the scroller won't scroll to an item after
* validating.
*/
protected var pendingItemIndex:int = -1;
/**
* @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._selectedGroupIndex = -1;
this._selectedItemIndex = -1;
this.dataProvider = null;
this.layout = null;
super.dispose();
}
/**
* @private
*/
override public function scrollToPosition(horizontalScrollPosition:Number, verticalScrollPosition:Number, animationDuration:Number = NaN):void
{
this.pendingItemIndex = -1;
super.scrollToPosition(horizontalScrollPosition, verticalScrollPosition, animationDuration);
}
/**
* @private
*/
override public function scrollToPageIndex(horizontalPageIndex:int, verticalPageIndex:int, animationDuration:Number = NaN):void
{
this.pendingGroupIndex = -1;
this.pendingItemIndex = -1;
super.scrollToPageIndex(horizontalPageIndex, verticalPageIndex, animationDuration);
}
/**
* After the next validation, scrolls the list so that the specified
* item is visible. If animationDuration
is greater than
* zero, the scroll will animate. The duration is in seconds.
*
* The itemIndex
parameter is optional. If set to
* -1
, the list will scroll to the start of the specified
* group.
*
* In the following example, the list is scrolled to display the
* third item in the second group:
*
*
* list.scrollToDisplayIndex( 1, 2 );
*
* In the following example, the list is scrolled to display the
* third group:
*
*
* list.scrollToDisplayIndex( 2 );
*/
public function scrollToDisplayIndex(groupIndex:int, itemIndex:int = -1, animationDuration:Number = 0):void
{
//cancel any pending scroll to a different page or scroll position.
//we can have only one type of pending scroll at a time.
this.hasPendingHorizontalPageIndex = false;
this.hasPendingVerticalPageIndex = false;
this.pendingHorizontalScrollPosition = NaN;
this.pendingVerticalScrollPosition = NaN;
if(this.pendingGroupIndex == groupIndex &&
this.pendingItemIndex == itemIndex &&
this.pendingScrollDuration == animationDuration)
{
return;
}
this.pendingGroupIndex = groupIndex;
this.pendingItemIndex = itemIndex;
this.pendingScrollDuration = animationDuration;
this.invalidate(INVALIDATION_FLAG_PENDING_SCROLL);
}
/**
* Sets the selected group and item index.
*
* In the following example, the third item in the second group
* is selected:
*
*
* list.setSelectedLocation( 1, 2 );
*
* In the following example, the selection is cleared:
*
*
* list.setSelectedLocation( -1, -1 );
*
* @see #selectedGroupIndex
* @see #selectedItemIndex
* @see #selectedItem
*/
public function setSelectedLocation(groupIndex:int, itemIndex:int):void
{
if(this._selectedGroupIndex == groupIndex && this._selectedItemIndex == itemIndex)
{
return;
}
if((groupIndex < 0 && itemIndex >= 0) || (groupIndex >= 0 && itemIndex < 0))
{
throw new ArgumentError("To deselect items, group index and item index must both be < 0.");
}
this._selectedGroupIndex = groupIndex;
this._selectedItemIndex = itemIndex;
this.invalidate(INVALIDATION_FLAG_SELECTED);
this.dispatchEventWith(Event.CHANGE);
}
/**
* Returns the item renderer factory associated with a specific ID.
* Returns null
if no factory is associated with the ID.
*
* @see #setItemRendererFactoryWithID()
*/
public function getItemRendererFactoryWithID(id:String):Function
{
if(this._itemRendererFactories && (id in this._itemRendererFactories))
{
return this._itemRendererFactories[id] as Function;
}
return null;
}
/**
* Associates an item renderer factory with an ID to allow multiple
* types of item renderers may be displayed in the list. A custom
* factoryIDFunction
may be specified to return the ID of
* the factory to use for a specific item in the data provider.
*
* @see #factoryIDFunction
* @see #getItemRendererFactoryWithID()
*/
public function setItemRendererFactoryWithID(id:String, factory:Function):void
{
if(id === null)
{
this.itemRendererFactory = factory;
return;
}
if(this._itemRendererFactories === null)
{
this._itemRendererFactories = {};
}
if(factory !== null)
{
this._itemRendererFactories[id] = factory;
}
else
{
delete this._itemRendererFactories[id];
}
}
/**
* Returns the header renderer factory associated with a specific ID.
* Returns null
if no factory is associated with the ID.
*
* @see #setHeaderRendererFactoryWithID()
*/
public function getHeaderRendererFactoryWithID(id:String):Function
{
if(this._headerRendererFactories && (id in this._headerRendererFactories))
{
return this._headerRendererFactories[id] as Function;
}
return null;
}
/**
* Associates a header renderer factory with an ID to allow multiple
* types of header renderers may be displayed in the list. A custom
* headerFactoryIDFunction
may be specified to return the
* ID of the factory to use for a specific header in the data provider.
*
* @see #headerFactoryIDFunction
* @see #getHeaderRendererFactoryWithID()
*/
public function setHeaderRendererFactoryWithID(id:String, factory:Function):void
{
if(id === null)
{
this.headerRendererFactory = factory;
return;
}
if(this._headerRendererFactories === null)
{
this._headerRendererFactories = {};
}
if(factory !== null)
{
this._headerRendererFactories[id] = factory;
}
else
{
delete this._headerRendererFactories[id];
}
}
/**
* Returns the footer renderer factory associated with a specific ID.
* Returns null
if no factory is associated with the ID.
*
* @see #setFooterRendererFactoryWithID()
*/
public function getFooterRendererFactoryWithID(id:String):Function
{
if(this._footerRendererFactories && (id in this._footerRendererFactories))
{
return this._footerRendererFactories[id] as Function;
}
return null;
}
/**
* Associates a footer renderer factory with an ID to allow multiple
* types of footer renderers may be displayed in the list. A custom
* footerFactoryIDFunction
may be specified to return the
* ID of the factory to use for a specific footer in the data provider.
*
* @see #footerFactoryIDFunction
* @see #getFooterRendererFactoryWithID()
*/
public function setFooterRendererFactoryWithID(id:String, factory:Function):void
{
if(id === null)
{
this.footerRendererFactory = factory;
return;
}
if(this._footerRendererFactories === null)
{
this._footerRendererFactories = {};
}
if(factory !== null)
{
this._footerRendererFactories[id] = factory;
}
else
{
delete this._footerRendererFactories[id];
}
}
/**
* Extracts header data from a group object.
*/
public function groupToHeaderData(group:Object):Object
{
if(this._headerFunction != null)
{
return this._headerFunction(group);
}
else if(this._headerField != null && group && group.hasOwnProperty(this._headerField))
{
return group[this._headerField];
}
return null;
}
/**
* Extracts footer data from a group object.
*/
public function groupToFooterData(group:Object):Object
{
if(this._footerFunction != null)
{
return this._footerFunction(group);
}
else if(this._footerField != null && group && group.hasOwnProperty(this._footerField))
{
return group[this._footerField];
}
return null;
}
/**
* Returns the current item renderer used to render a specific item. May
* return null
if an item doesn't currently have an item
* renderer. Most lists use virtual layouts where only the visible items
* will have an item renderer, so the result will usually be
* null
for most items in the data provider.
*
* @see ../../../help/faq/layout-virtualization.html What is layout virtualization?
*/
public function itemToItemRenderer(item:Object):IGroupedListItemRenderer
{
return this.dataViewPort.itemToItemRenderer(item);
}
/**
* Returns the current header renderer used to render specific header
* data. May return null
if the header data doesn't
* currently have a header renderer. Most lists use virtual layouts
* where only the visible headers will have a header renderer, so the
* result will usually be null
for most header data in the
* data provider.
*
* @see #groupToHeaderData()
* @see ../../../help/faq/layout-virtualization.html What is layout virtualization?
*/
public function headerDataToHeaderRenderer(headerData:Object):IGroupedListHeaderRenderer
{
return this.dataViewPort.headerDataToHeaderRenderer(headerData);
}
/**
* Returns the current footer renderer used to render specific footer
* data. May return null
if the footer data doesn't
* currently have a footer renderer. Most lists use virtual layouts
* where only the visible footers will have a footer renderer, so the
* result will usually be null
for most footer data in the
* data provider.
*
* @see #groupToFooterData()
* @see ../../../help/faq/layout-virtualization.html What is layout virtualization?
*/
public function footerDataToFooterRenderer(footerData:Object):IGroupedListFooterRenderer
{
return this.dataViewPort.footerDataToFooterRenderer(footerData);
}
/**
* @private
*/
override protected function initialize():void
{
var hasLayout:Boolean = this._layout != null;
super.initialize();
if(!this.dataViewPort)
{
this.viewPort = this.dataViewPort = new GroupedListDataViewPort();
this.dataViewPort.owner = this;
this.dataViewPort.addEventListener(Event.CHANGE, dataViewPort_changeHandler);
this.viewPort = this.dataViewPort;
}
if(!hasLayout)
{
if(this._hasElasticEdges &&
this._verticalScrollPolicy === ScrollPolicy.AUTO &&
this._scrollBarDisplayMode !== ScrollBarDisplayMode.FIXED)
{
//so that the elastic edges work even when the max scroll
//position is 0, similar to iOS.
this.verticalScrollPolicy = ScrollPolicy.ON;
}
var layout:VerticalLayout = new VerticalLayout();
layout.useVirtualLayout = true;
layout.padding = 0;
layout.gap = 0;
layout.horizontalAlign = HorizontalAlign.JUSTIFY;
layout.verticalAlign = VerticalAlign.TOP;
layout.stickyHeader = !this._styleNameList.contains(ALTERNATE_STYLE_NAME_INSET_GROUPED_LIST);
this.layout = layout;
}
}
/**
* @private
*/
override protected function draw():void
{
this.refreshDataViewPortProperties();
super.draw();
}
/**
* @private
*/
protected function refreshDataViewPortProperties():void
{
this.dataViewPort.isSelectable = this._isSelectable;
this.dataViewPort.setSelectedLocation(this._selectedGroupIndex, this._selectedItemIndex);
this.dataViewPort.dataProvider = this._dataProvider;
this.dataViewPort.typicalItem = this._typicalItem;
this.dataViewPort.itemRendererType = this._itemRendererType;
this.dataViewPort.itemRendererFactory = this._itemRendererFactory;
this.dataViewPort.itemRendererFactories = this._itemRendererFactories;
this.dataViewPort.factoryIDFunction = this._factoryIDFunction;
this.dataViewPort.itemRendererProperties = this._itemRendererProperties;
this.dataViewPort.customItemRendererStyleName = this._customItemRendererStyleName;
this.dataViewPort.firstItemRendererType = this._firstItemRendererType;
this.dataViewPort.firstItemRendererFactory = this._firstItemRendererFactory;
this.dataViewPort.customFirstItemRendererStyleName = this._customFirstItemRendererStyleName;
this.dataViewPort.lastItemRendererType = this._lastItemRendererType;
this.dataViewPort.lastItemRendererFactory = this._lastItemRendererFactory;
this.dataViewPort.customLastItemRendererStyleName = this._customLastItemRendererStyleName;
this.dataViewPort.singleItemRendererType = this._singleItemRendererType;
this.dataViewPort.singleItemRendererFactory = this._singleItemRendererFactory;
this.dataViewPort.customSingleItemRendererStyleName = this._customSingleItemRendererStyleName;
this.dataViewPort.headerRendererType = this._headerRendererType;
this.dataViewPort.headerRendererFactory = this._headerRendererFactory;
this.dataViewPort.headerRendererFactories = this._headerRendererFactories;
this.dataViewPort.headerFactoryIDFunction = this._headerFactoryIDFunction;
this.dataViewPort.headerRendererProperties = this._headerRendererProperties;
this.dataViewPort.customHeaderRendererStyleName = this._customHeaderRendererStyleName;
this.dataViewPort.footerRendererType = this._footerRendererType;
this.dataViewPort.footerRendererFactory = this._footerRendererFactory;
this.dataViewPort.footerRendererFactories = this._footerRendererFactories;
this.dataViewPort.footerFactoryIDFunction = this._footerFactoryIDFunction;
this.dataViewPort.footerRendererProperties = this._footerRendererProperties;
this.dataViewPort.customFooterRendererStyleName = this._customFooterRendererStyleName;
this.dataViewPort.layout = this._layout;
}
/**
* @private
*/
override protected function handlePendingScroll():void
{
if(this.pendingGroupIndex >= 0)
{
if(this.pendingItemIndex >= 0)
{
var pendingData:Object = this._dataProvider.getItemAt(this.pendingGroupIndex, this.pendingItemIndex);
}
else
{
pendingData = this._dataProvider.getItemAt(this.pendingGroupIndex);
}
if(pendingData is Object)
{
this.dataViewPort.getScrollPositionForIndex(this.pendingGroupIndex, this.pendingItemIndex, HELPER_POINT);
this.pendingGroupIndex = -1;
this.pendingItemIndex = -1;
var targetHorizontalScrollPosition:Number = HELPER_POINT.x;
if(targetHorizontalScrollPosition < this._minHorizontalScrollPosition)
{
targetHorizontalScrollPosition = this._minHorizontalScrollPosition;
}
else if(targetHorizontalScrollPosition > this._maxHorizontalScrollPosition)
{
targetHorizontalScrollPosition = this._maxHorizontalScrollPosition;
}
var targetVerticalScrollPosition:Number = HELPER_POINT.y;
if(targetVerticalScrollPosition < this._minVerticalScrollPosition)
{
targetVerticalScrollPosition = this._minVerticalScrollPosition;
}
else if(targetVerticalScrollPosition > this._maxVerticalScrollPosition)
{
targetVerticalScrollPosition = this._maxVerticalScrollPosition;
}
this.throwTo(targetHorizontalScrollPosition, targetVerticalScrollPosition, this.pendingScrollDuration);
}
}
super.handlePendingScroll();
}
/**
* @private
*/
override protected function stage_keyDownHandler(event:KeyboardEvent):void
{
if(!this._dataProvider)
{
return;
}
var changedSelection:Boolean = false;
if(event.keyCode == Keyboard.HOME)
{
if(this._dataProvider.getLength() > 0 && this._dataProvider.getLength(0) > 0)
{
this.setSelectedLocation(0, 0);
changedSelection = true;
}
}
if(event.keyCode == Keyboard.END)
{
var groupIndex:int = this._dataProvider.getLength();
var itemIndex:int = -1;
do
{
groupIndex--;
if(groupIndex >= 0)
{
itemIndex = this._dataProvider.getLength(groupIndex) - 1;
}
}
while(groupIndex > 0 && itemIndex < 0)
if(groupIndex >= 0 && itemIndex >= 0)
{
this.setSelectedLocation(groupIndex, itemIndex);
changedSelection = true;
}
}
else if(event.keyCode == Keyboard.UP)
{
groupIndex = this._selectedGroupIndex;
itemIndex = this._selectedItemIndex - 1;
if(itemIndex < 0)
{
do
{
groupIndex--;
if(groupIndex >= 0)
{
itemIndex = this._dataProvider.getLength(groupIndex) - 1;
}
}
while(groupIndex > 0 && itemIndex < 0)
}
if(groupIndex >= 0 && itemIndex >= 0)
{
this.setSelectedLocation(groupIndex, itemIndex);
changedSelection = true;
}
}
else if(event.keyCode == Keyboard.DOWN)
{
groupIndex = this._selectedGroupIndex;
if(groupIndex < 0)
{
itemIndex = -1;
}
else
{
itemIndex = this._selectedItemIndex + 1;
}
if(groupIndex < 0 || itemIndex >= this._dataProvider.getLength(groupIndex))
{
itemIndex = -1;
groupIndex++;
var groupCount:int = this._dataProvider.getLength();
while(groupIndex < groupCount && itemIndex < 0)
{
if(this._dataProvider.getLength(groupIndex) > 0)
{
itemIndex = 0;
}
else
{
groupIndex++;
}
}
}
if(groupIndex >= 0 && itemIndex >= 0)
{
this.setSelectedLocation(groupIndex, itemIndex);
changedSelection = true;
}
}
if(changedSelection)
{
this.dataViewPort.getNearestScrollPositionForIndex(this._selectedGroupIndex, this.selectedItemIndex, HELPER_POINT);
this.scrollToPosition(HELPER_POINT.x, HELPER_POINT.y, this._keyScrollDuration);
}
}
/**
* @private
*/
protected function dataProvider_changeHandler(event:Event):void
{
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected function dataProvider_resetHandler(event:Event):void
{
this.horizontalScrollPosition = 0;
this.verticalScrollPosition = 0;
//the entire data provider was replaced. select no item.
this.setSelectedLocation(-1, -1);
}
/**
* @private
*/
protected function dataProvider_addItemHandler(event:Event, indices:Array):void
{
if(this._selectedGroupIndex == -1)
{
return;
}
var groupIndex:int = indices[0] as int;
if(indices.length > 1) //adding an item to a group
{
var itemIndex:int = indices[1] as int;
if(this._selectedGroupIndex == groupIndex && this._selectedItemIndex >= itemIndex)
{
//adding an item at an index that is less than or equal to
//the item that is selected. need to update the selected
//item index.
this.setSelectedLocation(this._selectedGroupIndex, this._selectedItemIndex + 1);
}
}
else //adding an entire group
{
//adding a group before the group that the selected item is in.
//need to update the selected group index.
this.setSelectedLocation(this._selectedGroupIndex + 1, this._selectedItemIndex);
}
}
/**
* @private
*/
protected function dataProvider_removeItemHandler(event:Event, indices:Array):void
{
if(this._selectedGroupIndex == -1)
{
return;
}
var groupIndex:int = indices[0] as int;
if(indices.length > 1) //removing an item from a group
{
var itemIndex:int = indices[1] as int;
if(this._selectedGroupIndex == groupIndex)
{
if(this._selectedItemIndex == itemIndex)
{
//removing the item that was selected.
//now, nothing will be selected.
this.setSelectedLocation(-1, -1);
}
else if(this._selectedItemIndex > itemIndex)
{
//removing an item from the same group that appears
//before the item that is selected. need to update the
//selected item index.
this.setSelectedLocation(this._selectedGroupIndex, this._selectedItemIndex - 1);
}
}
}
else //removing an entire group
{
if(this._selectedGroupIndex == groupIndex)
{
//removing the group that the selected item was in.
//now, nothing will be selected.
this.setSelectedLocation(-1, -1);
}
else if(this._selectedGroupIndex > groupIndex)
{
//removing a group before the group that the selected item
//is in. need to update the selected group index.
this.setSelectedLocation(this._selectedGroupIndex - 1, this._selectedItemIndex);
}
}
}
/**
* @private
*/
protected function dataProvider_replaceItemHandler(event:Event, indices:Array):void
{
if(this._selectedGroupIndex == -1)
{
return;
}
var groupIndex:int = indices[0] as int;
if(indices.length > 1) //replacing an item from a group
{
var itemIndex:int = indices[1] as int;
if(this._selectedGroupIndex == groupIndex && this._selectedItemIndex == itemIndex)
{
//replacing the selected item.
//now, nothing will be selected.
this.setSelectedLocation(-1, -1);
}
}
else if(this._selectedGroupIndex == groupIndex) //replacing an entire group
{
//replacing the group with the selected item.
//now, nothing will be selected.
this.setSelectedLocation(-1, -1);
}
}
/**
* @private
*/
protected function dataViewPort_changeHandler(event:Event):void
{
this.setSelectedLocation(this.dataViewPort.selectedGroupIndex, this.dataViewPort.selectedItemIndex);
}
/**
* @private
*/
private function layout_scrollHandler(event:Event, scrollOffset:Point):void
{
var layout:IVariableVirtualLayout = IVariableVirtualLayout(this._layout);
if(!this.isScrolling || !layout.useVirtualLayout || !layout.hasVariableItemDimensions)
{
return;
}
var scrollOffsetX:Number = scrollOffset.x;
this._startHorizontalScrollPosition += scrollOffsetX;
this._horizontalScrollPosition += scrollOffsetX;
if(this._horizontalAutoScrollTween)
{
this._targetHorizontalScrollPosition += scrollOffsetX;
this.throwTo(this._targetHorizontalScrollPosition, NaN, this._horizontalAutoScrollTween.totalTime - this._horizontalAutoScrollTween.currentTime);
}
var scrollOffsetY:Number = scrollOffset.y;
this._startVerticalScrollPosition += scrollOffsetY;
this._verticalScrollPosition += scrollOffsetY;
if(this._verticalAutoScrollTween)
{
this._targetVerticalScrollPosition += scrollOffsetY;
this.throwTo(NaN, this._targetVerticalScrollPosition, this._verticalAutoScrollTween.totalTime - this._verticalAutoScrollTween.currentTime);
}
}
}
}