package component.list
{
import caurina.transitions.Tweener;
import component.layout.FluidLayout;
import component.layout.ILayout;
import component.layout.VerticalLayout;
import flash.display.DisplayObject;
import flash.external.ExternalInterface;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
import mx.core.Container;
import mx.core.IDataRenderer;
import mx.core.IFactory;
import mx.core.UIComponent;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.events.FlexEvent;
public class CustomLayoutList extends Container
{
public static const TWEEN_DURATION_SECONDS : Number = 1;
public var itemRenderer : IFactory;
private var isLayoutChanged : Boolean = false;
private var _layout : ILayout;
[Bindable]
public function set layout(value : ILayout) : void
{
if (value != null && value != _layout)
{
_layout = value;
isLayoutChanged = true;
invalidateProperties();
}
}
public function get layout() : ILayout
{
return _layout;
}
private var isDataProviderChanged : Boolean = false;
private var _dataProvider : ArrayCollection;
[Bindable]
public function set dataProvider(value : ArrayCollection) : void
{
if (value != null && value != _dataProvider)
{
if (_dataProvider != null)
{
_dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChange);
}
_dataProvider = value;
_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChange);
isDataProviderChanged = true;
invalidateProperties();
}
}
public function get dataProvider() : ArrayCollection
{
return _dataProvider;
}
public var listItems : ArrayCollection = new ArrayCollection();
public var dataToItemMap : Dictionary = new Dictionary();
public var removedItems : ArrayCollection = new ArrayCollection();
private var creationCompleted : Boolean = false;
public function CustomLayoutList()
{
super();
addEventListener(FlexEvent.CREATION_COMPLETE, handleCreationComplete);
initializeExternalInterface();
}
public function collapseAll() : void
{
for each (var item : Object in listItems)
{
if (item is IExpandableListItem)
{
IExpandableListItem(item).collapse();
}
}
}
public function expandAll() : void
{
for each (var item : Object in listItems)
{
if (item is IExpandableListItem)
{
IExpandableListItem(item).expand();
}
}
}
override protected function commitProperties() : void
{
super.commitProperties();
if (isDataProviderChanged)
{
updateList();
isDataProviderChanged = false;
}
if (isLayoutChanged && creationCompleted)
{
var dimensions : Array = layout.updateLayout(this, listItems, removedItems);
tweenToNewDimensions(dimensions);
isLayoutChanged = false;
}
}
private function tweenToNewDimensions(dimensions : Array) : void
{
for (var i : int = 0; i < dimensions.length; i++)
{
var dimension : Rectangle = Rectangle(dimensions[i]);
if (dimension == null)
continue;
var item : DisplayObject = DisplayObject(listItems.getItemAt(i));
Tweener.addTween(item,
{x : dimension.x, y : dimension.y, width : dimension.width, height : dimension.height,
time : TWEEN_DURATION_SECONDS,
onUpdate : function() : void { invalidateSize(); invalidateDisplayList(); }});
}
}
private function updateList() : void
{
resetList();
initializeListItems();
isLayoutChanged = true;
invalidateProperties();
}
private function resetList() : void
{
removeAllChildren();
dataToItemMap = new Dictionary();
listItems.removeAll();
removedItems.removeAll();
}
private function initializeListItems() : void
{
for each (var itemData : Object in _dataProvider)
{
addItem(itemData);
}
}
private function addItems(itemsData : Array) : void
{
for each (var itemData : Object in itemsData)
{
addItem(itemData);
}
}
private function removeItems(itemsData : Array) : void
{
for each (var itemData : Object in itemsData)
{
removeItem(itemData);
}
}
private function addItem(itemData : Object) : void
{
var item : IDataRenderer = itemRenderer.newInstance() as IDataRenderer;
item.data = itemData;
listItems.addItem(item);
dataToItemMap[itemData] = item;
UIComponent(item).addEventListener(ResizableItemChangeEvent.DIMENSIONS_CHANGE, handleItemDimensionsChange);
addChild(UIComponent(item));
}
private function removeItem(itemData : Object) : void
{
var item : UIComponent = UIComponent(dataToItemMap[itemData]);
listItems.removeItemAt(listItems.getItemIndex(item));
removeChild(item);
delete dataToItemMap[itemData];
item.removeEventListener(ResizableItemChangeEvent.DIMENSIONS_CHANGE, handleItemDimensionsChange);
isLayoutChanged = true;
invalidateProperties();
}
private function tempRemoveItems(itemsData : Array) : void
{
for each (var itemData : Object in itemsData)
{
tempRemoveItem(itemData);
}
}
private function tempRemoveItem(itemData : Object) : void
{
var item : UIComponent = UIComponent(dataToItemMap[itemData]);
removeChild(item);
item.x = item.y = 0;
removedItems.addItem(item);
isLayoutChanged = true;
invalidateProperties();
}
private function restoreItems(items : Array) : void
{
for each (var item : Object in items)
{
restoreItem(item);
}
}
private function restoreItem(item : Object) : void
{
removedItems.removeItemAt(removedItems.getItemIndex(item));
addChild(UIComponent(item));
isLayoutChanged = true;
invalidateProperties();
}
private function refreshList() : void
{
var itemsDataToRemove : Array = [];
var item : IDataRenderer;
var itemData : Object;
for each (item in listItems)
{
itemData = item.data;
if (_dataProvider.getItemIndex(itemData) == -1 && DisplayObject(item).parent != null)
{
itemsDataToRemove.push(itemData);
}
}
tempRemoveItems(itemsDataToRemove);
var itemsToRestore : Array = [];
for each (item in removedItems)
{
itemData = item.data;
if (_dataProvider.getItemIndex(itemData) != -1)
{
itemsToRestore.push(item);
}
}
restoreItems(itemsToRestore);
}
private function handleCollectionChange(e : CollectionEvent) : void
{
switch (e.kind)
{
case CollectionEventKind.ADD:
addItems(e.items);
break;
case CollectionEventKind.REMOVE:
removeItems(e.items);
break;
case CollectionEventKind.REFRESH:
refreshList();
break;
}
}
private function handleItemDimensionsChange(e : ResizableItemChangeEvent) : void
{
isLayoutChanged = true;
invalidateProperties();
}
private function handleCreationComplete(e : FlexEvent) : void
{
creationCompleted = true;
isLayoutChanged = true;
invalidateProperties();
}
private function initializeExternalInterface() : void
{
ExternalInterface.addCallback("getCustomLayoutListData", getCustomLayoutListData);
ExternalInterface.addCallback("getCustomLayoutListLayout", getCustomLayoutListLayout);
}
public function getCustomLayoutListData(delimiter : String, ...rest) : String
{
var result : String = "";
for (var i : int = 0; i < _dataProvider.length; i++)
{
var itemData : Object = _dataProvider.getItemAt(i);
result += itemData.toString();
if (i < _dataProvider.length - 1)
{
result += delimiter;
}
}
return result;
}
public function getCustomLayoutListLayout(...rest) : String
{
var result : String;
if (layout is FluidLayout)
{
return "FluidLayout";
}
else if (layout is VerticalLayout)
{
return "VerticalLayout";
}
return result;
}
}
}