scaffold.libs_as.starling.display.DisplayObjectContainer.as Maven / Gradle / Ivy
// =================================================================================================
//
// Starling Framework
// Copyright 2011-2015 Gamua. 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 starling.display
{
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.system.Capabilities;
import flash.utils.getQualifiedClassName;
import starling.core.starling_internal;
import starling.errors.AbstractClassError;
import starling.events.Event;
import starling.filters.FragmentFilter;
import starling.rendering.BatchToken;
import starling.rendering.Painter;
import starling.utils.MatrixUtil;
use namespace starling_internal;
/**
* A DisplayObjectContainer represents a collection of display objects.
* It is the base class of all display objects that act as a container for other objects. By
* maintaining an ordered list of children, it defines the back-to-front positioning of the
* children within the display tree.
*
* A container does not a have size in itself. The width and height properties represent the
* extents of its children. Changing those properties will scale all children accordingly.
*
* As this is an abstract class, you can't instantiate it directly, but have to
* use a subclass instead. The most lightweight container class is "Sprite".
*
* Adding and removing children
*
* The class defines methods that allow you to add or remove children. When you add a child,
* it will be added at the frontmost position, possibly occluding a child that was added
* before. You can access the children via an index. The first child will have index 0, the
* second child index 1, etc.
*
* Adding and removing objects from a container triggers non-bubbling events.
*
*
* Event.ADDED
: the object was added to a parent.
* Event.ADDED_TO_STAGE
: the object was added to a parent that is
* connected to the stage, thus becoming visible now.
* Event.REMOVED
: the object was removed from a parent.
* Event.REMOVED_FROM_STAGE
: the object was removed from a parent that
* is connected to the stage, thus becoming invisible now.
*
*
* Especially the ADDED_TO_STAGE
event is very helpful, as it allows you to
* automatically execute some logic (e.g. start an animation) when an object is rendered the
* first time.
*
* @see Sprite
* @see DisplayObject
*/
public class DisplayObjectContainer extends DisplayObject
{
// members
private var _children:Vector.;
private var _touchGroup:Boolean;
// helper objects
private static var sHelperMatrix:Matrix = new Matrix();
private static var sHelperPoint:Point = new Point();
private static var sBroadcastListeners:Vector. = new [];
private static var sSortBuffer:Vector. = new [];
private static var sCacheToken:BatchToken = new BatchToken();
// construction
/** @private */
public function DisplayObjectContainer()
{
if (Capabilities.isDebugger &&
getQualifiedClassName(this) == "starling.display::DisplayObjectContainer")
{
throw new AbstractClassError();
}
_children = new [];
}
/** Disposes the resources of all children. */
public override function dispose():void
{
for (var i:int=_children.length-1; i>=0; --i)
_children[i].dispose();
super.dispose();
}
// child management
/** Adds a child to the container. It will be at the frontmost position. */
public function addChild(child:DisplayObject):DisplayObject
{
return addChildAt(child, _children.length);
}
/** Adds a child to the container at a certain index. */
public function addChildAt(child:DisplayObject, index:int):DisplayObject
{
var numChildren:int = _children.length;
if (index >= 0 && index <= numChildren)
{
setRequiresRedraw();
if (child.parent == this)
{
setChildIndex(child, index); // avoids dispatching events
}
else
{
_children.insertAt(index, child);
child.removeFromParent();
child.setParent(this);
child.dispatchEventWith(Event.ADDED, true);
if (stage)
{
var container:DisplayObjectContainer = child as DisplayObjectContainer;
if (container) container.broadcastEventWith(Event.ADDED_TO_STAGE);
else child.dispatchEventWith(Event.ADDED_TO_STAGE);
}
}
return child;
}
else
{
throw new RangeError("Invalid child index");
}
}
/** Removes a child from the container. If the object is not a child, nothing happens.
* If requested, the child will be disposed right away. */
public function removeChild(child:DisplayObject, dispose:Boolean=false):DisplayObject
{
var childIndex:int = getChildIndex(child);
if (childIndex != -1) removeChildAt(childIndex, dispose);
return child;
}
/** Removes a child at a certain index. The index positions of any display objects above
* the child are decreased by 1. If requested, the child will be disposed right away. */
public function removeChildAt(index:int, dispose:Boolean=false):DisplayObject
{
if (index >= 0 && index < _children.length)
{
setRequiresRedraw();
var child:DisplayObject = _children[index];
child.dispatchEventWith(Event.REMOVED, true);
if (stage)
{
var container:DisplayObjectContainer = child as DisplayObjectContainer;
if (container) container.broadcastEventWith(Event.REMOVED_FROM_STAGE);
else child.dispatchEventWith(Event.REMOVED_FROM_STAGE);
}
child.setParent(null);
index = _children.indexOf(child); // index might have changed by event handler
if (index >= 0) _children.removeAt(index);
if (dispose) child.dispose();
return child;
}
else
{
throw new RangeError("Invalid child index");
}
}
/** Removes a range of children from the container (endIndex included).
* If no arguments are given, all children will be removed. */
public function removeChildren(beginIndex:int=0, endIndex:int=-1, dispose:Boolean=false):void
{
if (endIndex < 0 || endIndex >= numChildren)
endIndex = numChildren - 1;
for (var i:int=beginIndex; i<=endIndex; ++i)
removeChildAt(beginIndex, dispose);
}
/** Returns a child object at a certain index. If you pass a negative index,
* '-1' will return the last child, '-2' the second to last child, etc. */
public function getChildAt(index:int):DisplayObject
{
var numChildren:int = _children.length;
if (index < 0)
index = numChildren + index;
if (index >= 0 && index < numChildren)
return _children[index];
else
throw new RangeError("Invalid child index");
}
/** Returns a child object with a certain name (non-recursively). */
public function getChildByName(name:String):DisplayObject
{
var numChildren:int = _children.length;
for (var i:int=0; i out.x) minX = out.x;
if (maxX < out.right) maxX = out.right;
if (minY > out.y) minY = out.y;
if (maxY < out.bottom) maxY = out.bottom;
}
out.setTo(minX, minY, maxX - minX, maxY - minY);
}
return out;
}
/** @inheritDoc */
public override function hitTest(localPoint:Point):DisplayObject
{
if (!visible || !touchable || !hitTestMask(localPoint)) return null;
var target:DisplayObject = null;
var localX:Number = localPoint.x;
var localY:Number = localPoint.y;
var numChildren:int = _children.length;
for (var i:int = numChildren - 1; i >= 0; --i) // front to back!
{
var child:DisplayObject = _children[i];
if (child.isMask) continue;
sHelperMatrix.copyFrom(child.transformationMatrix);
sHelperMatrix.invert();
MatrixUtil.transformCoords(sHelperMatrix, localX, localY, sHelperPoint);
target = child.hitTest(sHelperPoint);
if (target) return _touchGroup ? this : target;
}
return null;
}
/** @inheritDoc */
public override function render(painter:Painter):void
{
var numChildren:int = _children.length;
var frameID:uint = painter.frameID;
var selfOrParentChanged:Boolean = _lastParentOrSelfChangeFrameID == frameID;
for (var i:int=0; i, compareFunc:Function,
startIndex:int, length:int,
buffer:Vector.):void
{
// This is a port of the C++ merge sort algorithm shown here:
// http://www.cprogramming.com/tutorial/computersciencetheory/mergesort.html
if (length > 1)
{
var i:int;
var endIndex:int = startIndex + length;
var halfLength:int = length / 2;
var l:int = startIndex; // current position in the left subvector
var r:int = startIndex + halfLength; // current position in the right subvector
// sort each subvector
mergeSort(input, compareFunc, startIndex, halfLength, buffer);
mergeSort(input, compareFunc, startIndex + halfLength, length - halfLength, buffer);
// merge the vectors, using the buffer vector for temporary storage
for (i = 0; i < length; i++)
{
// Check to see if any elements remain in the left vector;
// if so, we check if there are any elements left in the right vector;
// if so, we compare them. Otherwise, we know that the merge must
// take the element from the left vector. */
if (l < startIndex + halfLength &&
(r == endIndex || compareFunc(input[l], input[r]) <= 0))
{
buffer[i] = input[l];
l++;
}
else
{
buffer[i] = input[r];
r++;
}
}
// copy the sorted subvector back to the input
for(i = startIndex; i < endIndex; i++)
input[i] = buffer[int(i - startIndex)];
}
}
/** @private */
internal function getChildEventListeners(object:DisplayObject, eventType:String,
listeners:Vector.):void
{
var container:DisplayObjectContainer = object as DisplayObjectContainer;
if (object.hasEventListener(eventType))
listeners[listeners.length] = object; // avoiding 'push'
if (container)
{
var children:Vector. = container._children;
var numChildren:int = children.length;
for (var i:int=0; i