import {Box} from "awayjs-core/lib/geom/Box"; import {Point} from "awayjs-core/lib/geom/Point"; import {ArgumentError} from "awayjs-core/lib/errors/ArgumentError"; import {RangeError} from "awayjs-core/lib/errors/RangeError"; import {Extensions} from "awayjs-core/lib/utils/Extensions"; import {DisplayObject} from "../display/DisplayObject"; import {HierarchicalProperties} from "../base/HierarchicalProperties"; import {PartitionBase} from "../partition/PartitionBase"; import {Scene} from "../display/Scene"; /** * The DisplayObjectContainer class is the base class for all objects that can * serve as display object containers on the display list. The display list * manages all objects displayed in the Flash runtimes. Use the * DisplayObjectContainer class to arrange the display objects in the display * list. Each DisplayObjectContainer object has its own child list for * organizing the z-order of the objects. The z-order is the front-to-back * order that determines which object is drawn in front, which is behind, and * so on. * *
DisplayObject is an abstract base class; therefore, you cannot call
* DisplayObject directly. Invoking new DisplayObject() throws an
* ArgumentError exception.
new DisplayObjectContainer() constructor throws an
* ArgumentError exception.
*
* For more information, see the "Display Programming" chapter of the * ActionScript 3.0 Developer's Guide.
*/ export class DisplayObjectContainer extends DisplayObject { public static assetType:string = "[asset DisplayObjectContainer]"; private _mouseChildren:boolean = true; private _depth_childs:Object = {}; private _nextHighestDepth:number = 0; private _nextHighestDepthDirty:boolean; public _children:Arraytrue.
*
* This property is useful when you create a button with an instance of
* the Sprite class(instead of using the SimpleButton class). When you use a
* Sprite instance to create a button, you can choose to decorate the button
* by using the addChild() method to add additional Sprite
* instances. This process can cause unexpected behavior with mouse events
* because the Sprite instances you add as children can become the target
* object of a mouse event when you expect the parent instance to be the
* target object. To ensure that the parent instance serves as the target
* objects for mouse events, you can set the mouseChildren
* property of the parent instance to false.
No event is dispatched by setting this property. You must use the
* addEventListener() method to create interactive
* functionality.
true.
*
* Note: Do not use the tabChildren property with
* Flex. Instead, use the
* mx.core.UIComponent.hasFocusableChildren property.
new DisplayObjectContainer() constructor throws
* an ArgumentError exception. You can, however, call
* constructors for the following subclasses of DisplayObjectContainer:
* new Loader()new Sprite()new MovieClip()addChildAt() method.)
*
* If you add a child object that already has a different display object * container as a parent, the object is removed from the child list of the * other display object container.
* *Note: The command stage.addChild() can cause
* problems with a published SWF file, including security problems and
* conflicts with other loaded SWF files. There is only one Stage within a
* Flash runtime instance, no matter how many SWF files you load into the
* runtime. So, generally, objects should not be added to the Stage,
* directly, at all. The only object the Stage should contain is the root
* object. Create a DisplayObjectContainer to contain all of the items on the
* display list. Then, if necessary, add that DisplayObjectContainer instance
* to the Stage.
child
* parameter.
* @throws ArgumentError Throws if the child is the same as the parent. Also
* throws if the caller is a child(or grandchild etc.)
* of the child being added.
* @event added Dispatched when a display object is added to the display
* list.
*/
public addChild(child:DisplayObject):DisplayObject
{
return this.addChildAt(child, this._children.length);
}
public addChildAtDepth(child:DisplayObject, depth:number, replace:boolean = true):DisplayObject
{
if (child == null)
throw new ArgumentError("Parameter child cannot be null.");
//if child already has a parent, remove it.
if (child._pParent)
child._pParent.removeChildAtInternal(child._pParent.getChildIndex(child));
var index = this.getDepthIndexInternal(depth);
if (index != -1) {
if (replace) {
this.removeChildAt(index);
} else {
//move depth of existing child up by 1
this.addChildAtDepth(this._children[index], depth + 1, false);
}
}
if (this._nextHighestDepth < depth + 1)
this._nextHighestDepth = depth + 1;
this._depth_childs[depth] = child;
this._children.push(child);
child._depthID = depth;
child.iSetParent(this);
this._invalidateChildren();
return child;
}
/**
* Adds a child DisplayObject instance to this DisplayObjectContainer
* instance. The child is added at the index position specified. An index of
* 0 represents the back(bottom) of the display list for this
* DisplayObjectContainer object.
*
* For example, the following example shows three display objects, labeled * a, b, and c, at index positions 0, 2, and 1, respectively:
* *If you add a child object that already has a different display object * container as a parent, the object is removed from the child list of the * other display object container.
* * @param child The DisplayObject instance to add as a child of this * DisplayObjectContainer instance. * @param index The index position to which the child is added. If you * specify a currently occupied index position, the child object * that exists at that position and all higher positions are * moved up one position in the child list. * @return The DisplayObject instance that you pass in thechild
* parameter.
* @throws ArgumentError Throws if the child is the same as the parent. Also
* throws if the caller is a child(or grandchild etc.)
* of the child being added.
* @throws RangeError Throws if the index position does not exist in the
* child list.
* @event added Dispatched when a display object is added to the display
* list.
*/
public addChildAt(child:DisplayObject, index:number):DisplayObject
{
return this.addChildAtDepth(child, (index < this._children.length)? this._children[index]._depthID : this.getNextHighestDepth(), false);
}
public addChildren(...childarray:Arraytrue.
*
* @param child The child object to test.
* @return true if the child object is a child of
* the DisplayObjectContainer or the container itself; otherwise
* false.
*/
public contains(child:DisplayObject):boolean
{
return this._children.indexOf(child) >= 0;
}
/**
*
*/
public disposeValues():void
{
for (var i:number = this._children.length - 1; i >= 0; i--)
this.removeChild(this._children[i]);
super.disposeValues();
}
public getChildAtDepth(depth:number):DisplayObject
{
return this._depth_childs[depth];
}
/**
* Returns the child display object instance that exists at the specified
* index.
*
* @param index The index position of the child object.
* @return The child display object at the specified index position.
* @throws RangeError Throws if the index does not exist in the child
* list.
*/
public getChildAt(index:number):DisplayObject
{
var child:DisplayObject = this._children[index];
if (child == null)
throw new RangeError("Index does not exist in the child list of the caller");
return child;
}
/**
* Returns the child display object that exists with the specified name. If
* more that one child display object has the specified name, the method
* returns the first object in the child list.
*
* The getChildAt() method is faster than the
* getChildByName() method. The getChildAt() method
* accesses a child from a cached array, whereas the
* getChildByName() method has to traverse a linked list to
* access a child.
child DisplayObject instance.
*
* @param child The DisplayObject instance to identify.
* @return The index position of the child display object to identify.
* @throws ArgumentError Throws if the child parameter is not a child of this
* object.
*/
public getChildIndex(child:DisplayObject):number
{
var childIndex:number = this._children.indexOf(child);
if (childIndex == -1)
throw new ArgumentError("Child parameter is not a child of the caller");
return childIndex;
}
public getNextHighestDepth():number
{
if (this._nextHighestDepthDirty)
this._updateNextHighestDepth();
return this._nextHighestDepth;
}
/**
* Returns an array of objects that lie under the specified point and are
* children(or grandchildren, and so on) of this DisplayObjectContainer
* instance. Any child objects that are inaccessible for security reasons are
* omitted from the returned array. To determine whether this security
* restriction affects the returned array, call the
* areInaccessibleObjectsUnderPoint() method.
*
* The point parameter is in the coordinate space of the
* Stage, which may differ from the coordinate space of the display object
* container(unless the display object container is the Stage). You can use
* the globalToLocal() and the localToGlobal()
* methods to convert points between these coordinate spaces.
child DisplayObject instance from the
* child list of the DisplayObjectContainer instance. The parent
* property of the removed child is set to null , and the object
* is garbage collected if no other references to the child exist. The index
* positions of any display objects above the child in the
* DisplayObjectContainer are decreased by 1.
*
* The garbage collector reallocates unused memory space. When a variable * or object is no longer actively referenced or stored somewhere, the * garbage collector sweeps through and wipes out the memory space it used to * occupy if no other references to it exist.
* * @param child The DisplayObject instance to remove. * @return The DisplayObject instance that you pass in thechild
* parameter.
* @throws ArgumentError Throws if the child parameter is not a child of this
* object.
*/
public removeChild(child:DisplayObject):DisplayObject
{
if (child == null)
throw new ArgumentError("Parameter child cannot be null");
this.removeChildAt(this.getChildIndex(child));
return child;
}
public removeChildAtDepth(depth:number):DisplayObject
{
return this.removeChildAt(this.getDepthIndexInternal(depth));
}
/**
* Removes a child DisplayObject from the specified index
* position in the child list of the DisplayObjectContainer. The
* parent property of the removed child is set to
* null, and the object is garbage collected if no other
* references to the child exist. The index positions of any display objects
* above the child in the DisplayObjectContainer are decreased by 1.
*
* The garbage collector reallocates unused memory space. When a variable * or object is no longer actively referenced or stored somewhere, the * garbage collector sweeps through and wipes out the memory space it used to * occupy if no other references to it exist.
* * @param index The child index of the DisplayObject to remove. * @return The DisplayObject instance that was removed. * @throws RangeError Throws if the index does not exist in the child * list. * @throws SecurityError This child display object belongs to a sandbox to * which the calling object does not have access. You * can avoid this situation by having the child movie * call theSecurity.allowDomain() method.
*/
public removeChildAt(index:number):DisplayObject
{
var child:DisplayObject = this.removeChildAtInternal(index);
child.iSetParent(null);
this._invalidateChildren();
return child;
}
/**
* Removes all child DisplayObject instances from the child list
* of the DisplayObjectContainer instance. The parent property
* of the removed children is set to null, and the objects are
* garbage collected if no other references to the children exist.
*
* The garbage collector reallocates unused memory space. When a variable or
* object is no longer actively referenced or stored somewhere, the garbage
* collector sweeps through and wipes out the memory space it used to occupy
* if no other references to it exist.
*
* @param beginIndex The beginning position. A value smaller than 0 throws a RangeError.
* @param endIndex The ending position. A value smaller than 0 throws a RangeError.
* @throws RangeError Throws if the beginIndex or endIndex positions do
* not exist in the child list.
*/
public removeChildren(beginIndex:number = 0, endIndex:number = 2147483647):void
{
if (beginIndex < 0)
throw new RangeError("beginIndex is out of range of the child list");
if (endIndex > this._children.length)
throw new RangeError("endIndex is out of range of the child list");
for(var i:number /*uint*/ = beginIndex; i < endIndex; i++)
this.removeChild(this._children[i]);
}
/**
* Changes the position of an existing child in the display object container.
* This affects the layering of child objects. For example, the following
* example shows three display objects, labeled a, b, and c, at index
* positions 0, 1, and 2, respectively:
*
* When you use the setChildIndex() method and specify an
* index position that is already occupied, the only positions that change
* are those in between the display object's former and new position. All
* others will stay the same. If a child is moved to an index LOWER than its
* current index, all children in between will INCREASE by 1 for their index
* reference. If a child is moved to an index HIGHER than its current index,
* all children in between will DECREASE by 1 for their index reference. For
* example, if the display object container in the previous example is named
* container, you can swap the position of the display objects
* labeled a and b by calling the following code:
This code results in the following arrangement of objects:
* * @param child The child DisplayObject instance for which you want to change * the index number. * @param index The resulting index number for thechild display
* object.
* @throws ArgumentError Throws if the child parameter is not a child of this
* object.
* @throws RangeError Throws if the index does not exist in the child
* list.
*/
public setChildIndex(child:DisplayObject, index:number):void
{
//TODO
}
/**
* Swaps the z-order (front-to-back order) of the two specified child
* objects. All other child objects in the display object container remain in
* the same index positions.
*
* @param child1 The first child object.
* @param child2 The second child object.
* @throws ArgumentError Throws if either child parameter is not a child of
* this object.
*/
public swapChildren(child1:DisplayObject, child2:DisplayObject):void
{
this.swapChildrenAt(this.getChildIndex(child1), this.getChildIndex(child2))
}
/**
* Swaps the z-order(front-to-back order) of the child objects at the two
* specified index positions in the child list. All other child objects in
* the display object container remain in the same index positions.
*
* @param index1 The index position of the first child object.
* @param index2 The index position of the second child object.
* @throws RangeError If either index does not exist in the child list.
*/
public swapChildrenAt(index1:number, index2:number):void
{
var depth:number = this._children[index2]._depthID;
var child:DisplayObject = this._children[index1];
this.addChildAtDepth(this._children[index2], this._children[index1]._depthID);
this.addChildAtDepth(child, depth);
}
/**
* //TODO
*
* @protected
*/
public _pUpdateBoxBounds():void
{
super._pUpdateBoxBounds();
var box:Box;
var numChildren:number = this._children.length;
if (numChildren > 0) {
var min:number;
var max:number;
var minX:number, minY:number, minZ:number;
var maxX:number, maxY:number, maxZ:number;
for (var i:number = 0; i < numChildren; ++i) {
box = this._children[i].getBox(this);
if (i == 0) {
maxX = box.width + (minX = box.x);
maxY = box.height + (minY = box.y);
maxZ = box.depth + (minZ = box.z);
} else {
max = box.width + (min = box.x);
if (min < minX)
minX = min;
if (max > maxX)
maxX = max;
max = box.height + (min = box.y);
if (min < minY)
minY = min;
if (max > maxY)
maxY = max;
max = box.depth + (min = box.z);
if (min < minZ)
minZ = min;
if (max > maxZ)
maxZ = max;
}
}
this._pBoxBounds.width = maxX - (this._pBoxBounds.x = minX);
this._pBoxBounds.height = maxY - (this._pBoxBounds.y = minY);
this._pBoxBounds.depth = maxZ - (this._pBoxBounds.z = minZ);
} else {
this._pBoxBounds.setBoundIdentity();
}
}
/**
* @protected
*/
public pInvalidateHierarchicalProperties(bitFlag:number):boolean
{
if (super.pInvalidateHierarchicalProperties(bitFlag))
return true;
var len:number = this._children.length;
for (var i:number = 0; i < len; ++i)
this._children[i].pInvalidateHierarchicalProperties(bitFlag);
return false;
}
/**
* @internal
*/
public _iSetScene(value:Scene, partition:PartitionBase):void
{
super._iSetScene(value, partition);
var len:number = this._children.length;
for (var i:number = 0; i < len; ++i)
this._children[i]._iSetScene(value, partition);
}
/**
* @private
*
* @param child
*/
public removeChildAtInternal(index:number):DisplayObject
{
var child:DisplayObject = this._children.splice(index, 1)[0];
//update next highest depth
if (this._nextHighestDepth == child._depthID + 1)
this._nextHighestDepthDirty = true;
delete this._depth_childs[child._depthID];
child._depthID = -16384;
return child;
}
public getDepthIndexInternal(depth:number):number
{
if (!this._depth_childs[depth])
return -1;
return this._children.indexOf(this._depth_childs[depth]);
}
private _updateNextHighestDepth():void
{
this._nextHighestDepthDirty = false;
this._nextHighestDepth = 0;
var len:number = this._children.length;
for (var i:number = 0; i < len; i++)
if (this._nextHighestDepth < this._children[i]._depthID)
this._nextHighestDepth = this._children[i]._depthID;
this._nextHighestDepth += 1;
}
public _hitTestPointInternal(x:number, y:number, shapeFlag:boolean, masksFlag:boolean):boolean
{
var numChildren:number = this._children.length;
for(var i:number = 0; i < numChildren; i++)
if(this._children[i].hitTestPoint(x,y, shapeFlag, masksFlag))
return true;
return false;
}
public _updateMaskMode():void
{
if (this.maskMode)
this.mouseChildren = false;
super._updateMaskMode();
}
private _invalidateChildren():void
{
if (this._pIsContainer != Boolean(this._children.length)) {
if (this._pImplicitPartition)
this._pImplicitPartition._iUnregisterEntity(this);
this._pIsContainer = Boolean(this._children.length);
if (this._pImplicitPartition)
this._pImplicitPartition._iRegisterEntity(this);
}
this._pInvalidateBounds();
}
}