scaffold.libs_as.feathers.data.HierarchicalCollection.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.data
{
import feathers.events.CollectionEventType;
import starling.events.Event;
import starling.events.EventDispatcher;
/**
* Dispatched when the underlying data source changes and the ui will
* need to redraw the data.
*
* 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 collection has changed drastically, such as when
* the underlying data source is replaced completely.
*
* 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 feathers.events.CollectionEventType.RESET
*/
[Event(name="reset",type="starling.events.Event")]
/**
* Dispatched when an item is added to the collection.
*
* 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 index path of the item that has
* been added. It is of type Array
and contains objects of
* type int
.
* 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.CollectionEventType.ADD_ITEM
*/
[Event(name="addItem",type="starling.events.Event")]
/**
* Dispatched when an item is removed from the collection.
*
* 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 index path of the item that has
* been removed. It is of type Array
and contains objects of
* type int
.
* 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.CollectionEventType.REMOVE_ITEM
*/
[Event(name="removeItem",type="starling.events.Event")]
/**
* Dispatched when an item is replaced in the collection.
*
* 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 index path of the item that has
* been re[;aced. It is of type Array
and contains objects of
* type int
.
* 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.CollectionEventType.REPLACE_ITEM
*/
[Event(name="replaceItem",type="starling.events.Event")]
/**
* Dispatched when the updateItemAt()
function is called on the
* HierarchicalCollection
.
*
* 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 index path of the item that has
* been updated. It is of type Array
and contains objects of
* type int
.
* 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.
*
*
* @see #updateItemAt()
*
* @eventType feathers.events.CollectionEventType.UPDATE_ITEM
*/
[Event(name="updateItem",type="starling.events.Event")]
/**
* Dispatched when the updateAll()
function is called on the
* HierarchicalCollection
.
*
* 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.
*
*
* @see #updateAll()
*
* @eventType feathers.events.CollectionEventType.UPDATE_ALL
*/
[Event(name="updateAll",type="starling.events.Event")]
/**
* Wraps a two-dimensional data source with a common API for use with UI
* controls that support this type of data.
*/
public class HierarchicalCollection extends EventDispatcher
{
public function HierarchicalCollection(data:Object = null)
{
if(!data)
{
//default to an array if no data is provided
data = [];
}
this.data = data;
}
/**
* @private
*/
protected var _data:Object;
/**
* The data source for this collection. May be any type of data, but a
* dataDescriptor
needs to be provided to translate from
* the data source's APIs to something that can be understood by
* HierarchicalCollection
.
*/
public function get data():Object
{
return _data;
}
/**
* @private
*/
public function set data(value:Object):void
{
if(this._data == value)
{
return;
}
this._data = value;
this.dispatchEventWith(CollectionEventType.RESET);
this.dispatchEventWith(Event.CHANGE);
}
/**
* @private
*/
protected var _dataDescriptor:IHierarchicalCollectionDataDescriptor = new ArrayChildrenHierarchicalCollectionDataDescriptor();
/**
* Describes the underlying data source by translating APIs.
*/
public function get dataDescriptor():IHierarchicalCollectionDataDescriptor
{
return this._dataDescriptor;
}
/**
* @private
*/
public function set dataDescriptor(value:IHierarchicalCollectionDataDescriptor):void
{
if(this._dataDescriptor == value)
{
return;
}
this._dataDescriptor = value;
this.dispatchEventWith(CollectionEventType.RESET);
this.dispatchEventWith(Event.CHANGE);
}
/**
* Determines if a node from the data source is a branch.
*/
public function isBranch(node:Object):Boolean
{
return this._dataDescriptor.isBranch(node);
}
/**
* The number of items at the specified location in the collection.
*/
public function getLength(...rest:Array):int
{
rest.insertAt(0, this._data);
return this._dataDescriptor.getLength.apply(null, rest);
}
/**
* Call updateItemAt()
to manually inform any component
* rendering the HierarchicalCollection
that the properties
* of a single item in the collection have changed, and that any views
* associated with the item should be updated. The collection will
* dispatch the CollectionEventType.UPDATE_ITEM
event.
*
* Alternatively, the item can dispatch an event when one of its
* properties has changed, and item renderers can listen for that event
* and update themselves automatically.
*
* @see #updateAll()
*/
public function updateItemAt(index:int, ...rest:Array):void
{
rest.insertAt(0, index);
this.dispatchEventWith(CollectionEventType.UPDATE_ITEM, false, rest);
}
/**
* Call updateAll()
to manually inform any component
* rendering the HierarchicalCollection
that the properties
* of all, or many, of the collection's items have changed, and that any
* rendered views should be updated. The collection will dispatch the
* CollectionEventType.UPDATE_ALL
event.
*
* Alternatively, the item can dispatch an event when one of its
* properties has changed, and item renderers can listen for that event
* and update themselves automatically.
*
* @see #updateItemAt()
*/
public function updateAll():void
{
this.dispatchEventWith(CollectionEventType.UPDATE_ALL);
}
/**
* Returns the item at the specified location in the collection.
*/
public function getItemAt(index:int, ...rest:Array):Object
{
rest.insertAt(0, index);
rest.insertAt(0, this._data);
return this._dataDescriptor.getItemAt.apply(null, rest);
}
/**
* Determines which location the item appears at within the collection. If
* the item isn't in the collection, returns null
.
*/
public function getItemLocation(item:Object, result:Vector. = null):Vector.
{
return this._dataDescriptor.getItemLocation(this._data, item, result);
}
/**
* Adds an item to the collection, at the specified location.
*/
public function addItemAt(item:Object, index:int, ...rest:Array):void
{
rest.insertAt(0, index);
rest.insertAt(0, item);
rest.insertAt(0, this._data);
this._dataDescriptor.addItemAt.apply(null, rest);
this.dispatchEventWith(Event.CHANGE);
rest.shift();
rest.shift();
this.dispatchEventWith(CollectionEventType.ADD_ITEM, false, rest);
}
/**
* Removes the item at the specified location from the collection and
* returns it.
*/
public function removeItemAt(index:int, ...rest:Array):Object
{
rest.insertAt(0, index);
rest.insertAt(0, this._data);
var item:Object = this._dataDescriptor.removeItemAt.apply(null, rest);
this.dispatchEventWith(Event.CHANGE);
rest.shift();
this.dispatchEventWith(CollectionEventType.REMOVE_ITEM, false, rest);
return item;
}
/**
* Removes a specific item from the collection.
*/
public function removeItem(item:Object):void
{
var location:Vector. = this.getItemLocation(item);
if(location)
{
//this is hacky. a future version probably won't use rest args.
var locationAsArray:Array = [];
var indexCount:int = location.length;
for(var i:int = 0; i < indexCount; i++)
{
locationAsArray.push(location[i]);
}
this.removeItemAt.apply(this, locationAsArray);
}
}
/**
* Removes all items from the collection.
*/
public function removeAll():void
{
if(this.getLength() == 0)
{
return;
}
this._dataDescriptor.removeAll(this._data);
this.dispatchEventWith(Event.CHANGE);
this.dispatchEventWith(CollectionEventType.RESET, false);
}
/**
* Replaces the item at the specified location with a new item.
*/
public function setItemAt(item:Object, index:int, ...rest:Array):void
{
rest.insertAt(0, index);
rest.insertAt(0, item);
rest.insertAt(0, this._data);
this._dataDescriptor.setItemAt.apply(null, rest);
rest.shift();
rest.shift();
this.dispatchEventWith(CollectionEventType.REPLACE_ITEM, false, rest);
this.dispatchEventWith(Event.CHANGE);
}
/**
* Calls a function for each group in the collection and another
* function for each item in a group, where each function handles any
* properties that require disposal on these objects. For example,
* display objects or textures may need to be disposed. You may pass in
* a value of null
for either function if you don't have
* anything to dispose in one or the other.
*
* The function to dispose a group is expected to have the following signature:
* function( group:Object ):void
*
* The function to dispose an item is expected to have the following signature:
* function( item:Object ):void
*
* In the following example, the items in the collection are disposed:
*
*
* collection.dispose( function( group:Object ):void
* {
* var content:DisplayObject = DisplayObject(group.content);
* content.dispose();
* },
* function( item:Object ):void
* {
* var accessory:DisplayObject = DisplayObject(item.accessory);
* accessory.dispose();
* },)
*
* @see http://doc.starling-framework.org/core/starling/display/DisplayObject.html#dispose() starling.display.DisplayObject.dispose()
* @see http://doc.starling-framework.org/core/starling/textures/Texture.html#dispose() starling.textures.Texture.dispose()
*/
public function dispose(disposeGroup:Function, disposeItem:Function):void
{
var groupCount:int = this.getLength();
var path:Array = [];
for(var i:int = 0; i < groupCount; i++)
{
var group:Object = this.getItemAt(i);
path[0] = i;
this.disposeGroupInternal(group, path, disposeGroup, disposeItem);
path.length = 0;
}
}
/**
* @private
*/
protected function disposeGroupInternal(group:Object, path:Array, disposeGroup:Function, disposeItem:Function):void
{
if(disposeGroup != null)
{
disposeGroup(group);
}
var itemCount:int = this.getLength.apply(this, path);
for(var i:int = 0; i < itemCount; i++)
{
path[path.length] = i;
var item:Object = this.getItemAt.apply(this, path);
if(this.isBranch(item))
{
this.disposeGroupInternal(item, path, disposeGroup, disposeItem);
}
else if(disposeItem != null)
{
disposeItem(item);
}
path.length--;
}
}
}
}