Hilo/docs/api-en/code/view/Container.js
2016-12-14 14:40:55 +08:00

348 lines
11 KiB
JavaScript

/**
* Hilo
* Copyright 2015 alibaba.com
* Licensed under the MIT License
*/
/**
* @class Container is the base class to all container classes. Each Container can add other view object as children.
* @augments View
* @param {Object} properties Properties parameters of the object to create. Contains all writable properties of this class.
* @module hilo/view/Container
* @requires hilo/core/Hilo
* @requires hilo/core/Class
* @requires hilo/view/View
* @property {Array} children List of children elements of the container, readonly!
* @property {Boolean} pointerChildren Whether children elements of the container can response to user interactive events, default value is true.
* @property {Boolean} clipChildren Whether clip children elements which are out of the container, default value is false.
*/
var Container = Class.create(/** @lends Container.prototype */{
Extends: View,
constructor: function(properties){
properties = properties || {};
this.id = this.id || properties.id || Hilo.getUid("Container");
Container.superclass.constructor.call(this, properties);
if(this.children) this._updateChildren();
else this.children = [];
},
children: null,
pointerChildren: true,
clipChildren: false,
/**
* Return the amount of the children elements of the container.
* @returns {Uint} The amount of the children elements of the container.
*/
getNumChildren: function(){
return this.children.length;
},
/**
* Add child element at given index.
* @param {View} child Element to add.
* @param {Number} index The given index position, range from 0.
*/
addChildAt: function(child, index){
var children = this.children,
len = children.length,
parent = child.parent;
index = index < 0 ? 0 : index > len ? len : index;
var childIndex = this.getChildIndex(child);
if(childIndex == index){
return this;
}else if(childIndex >= 0){
children.splice(childIndex, 1);
index = index == len ? len - 1 : index;
}else if(parent){
parent.removeChild(child);
}
children.splice(index, 0, child);
//直接插入,影响插入位置之后的深度
//Insert directly, this will affect depth of elements after the index.
if(childIndex < 0){
this._updateChildren(index);
}
//只是移动时影响中间段的深度
//Will affect depth of elements in the middle during moving
else{
var startIndex = childIndex < index ? childIndex : index;
var endIndex = childIndex < index ? index : childIndex;
this._updateChildren(startIndex, endIndex + 1);
}
return this;
},
/**
* Add child element at the top.
* @param {View} child Elements to add.
*/
addChild: function(child){
var total = this.children.length,
args = arguments;
for(var i = 0, len = args.length; i < len; i++){
this.addChildAt(args[i], total + i);
}
return this;
},
/**
* Remove element at the index.
* @param {Int} index Index of the element to remove, range from 0.
* @returns {View} Element had been removed.
*/
removeChildAt: function(index){
var children = this.children;
if(index < 0 || index >= children.length) return null;
var child = children[index];
if(child){
//NOTE: use `__renderer` for fixing child removal (DOMRenderer and FlashRenderer only).
//Do `not` use it in any other case.
if(!child.__renderer){
var obj = child;
while(obj = obj.parent){
//obj is stage
if(obj.renderer){
child.__renderer = obj.renderer;
break;
}
else if(obj.__renderer){
child.__renderer = obj.__renderer;
break;
}
}
}
if(child.__renderer){
child.__renderer.remove(child);
}
child.parent = null;
child.depth = -1;
}
children.splice(index, 1);
this._updateChildren(index);
return child;
},
/**
* Remove the given child element.
* @param {View} child The child element to remove.
* @returns {View} Element had been removed.
*/
removeChild: function(child){
return this.removeChildAt(this.getChildIndex(child));
},
/**
* Remove child element by its id.
* @param {String} id The id of element to remove.
* @returns {View} Element had been removed.
*/
removeChildById: function(id){
var children = this.children, child;
for(var i = 0, len = children.length; i < len; i++){
child = children[i];
if(child.id === id){
this.removeChildAt(i);
return child;
}
}
return null;
},
/**
* Remove all children elements.
* @returns {Container} Container itself.
*/
removeAllChildren: function(){
while(this.children.length) this.removeChildAt(0);
return this;
},
/**
* Return child element at the given index.
* @param {Number} index The index of the element, range from 0.
*/
getChildAt: function(index){
var children = this.children;
if(index < 0 || index >= children.length) return null;
return children[index];
},
/**
* Return child element at the given id.
* @param {String} id The id of child element to return.
*/
getChildById: function(id){
var children = this.children, child;
for(var i = 0, len = children.length; i < len; i++){
child = children[i];
if(child.id === id) return child;
}
return null;
},
/**
* Return index value of the given child element.
* @param {View} child The child element need to get its index.
*/
getChildIndex: function(child){
return this.children.indexOf(child);
},
/**
* Set the index of child element.
* @param {View} child The child element need to set index.
* @param {Number} index The index to set to the element.
*/
setChildIndex: function(child, index){
var children = this.children,
oldIndex = children.indexOf(child);
if(oldIndex >= 0 && oldIndex != index){
var len = children.length;
index = index < 0 ? 0 : index >= len ? len - 1 : index;
children.splice(oldIndex, 1);
children.splice(index, 0, child);
this._updateChildren();
}
return this;
},
/**
* Swap index between two child elements.
* @param {View} child1 Child element A.
* @param {View} child2 Child element B.
*/
swapChildren: function(child1, child2){
var children = this.children,
index1 = this.getChildIndex(child1),
index2 = this.getChildIndex(child2);
child1.depth = index2;
children[index2] = child1;
child2.depth = index1;
children[index1] = child2;
},
/**
* Swap two children elements at given indexes.
* @param {Number} index1 Given index A.
* @param {Number} index2 Given index B.
*/
swapChildrenAt: function(index1, index2){
var children = this.children,
child1 = this.getChildAt(index1),
child2 = this.getChildAt(index2);
child1.depth = index2;
children[index2] = child1;
child2.depth = index1;
children[index1] = child2;
},
/**
* Sort children elements by the given key or function.
* @param {Object} keyOrFunction If is String, sort children elements by the given property string; If is Function, sort by the function.
*/
sortChildren: function(keyOrFunction){
var fn = keyOrFunction,
children = this.children;
if(typeof fn == "string"){
var key = fn;
fn = function(a, b){
return b[key] - a[key];
};
}
children.sort(fn);
this._updateChildren();
},
/**
* Update children elements.
* @private
*/
_updateChildren: function(start, end){
var children = this.children, child;
start = start || 0;
end = end || children.length;
for(var i = start; i < end; i++){
child = children[i];
child.depth = i + 1;
child.parent = this;
}
},
/**
* Return whether this container contains the parameter described child element.
* @param {View} child The child element to test.
*/
contains: function(child){
while(child = child.parent){
if(child === this){
return true;
}
}
return false;
},
/**
* Return object at the point positioned by given values on x axis and y axis.
* @param {Number} x The point's value on the coordinate's x axis.
* @param {Number} y The point's value on the coordinate's y asix.
* @param {Boolean} usePolyCollision Whether use polygon collision detection, default value is false.
* @param {Boolean} global Whether return all elements that match the condition, default value is false.
* @param {Boolean} eventMode Whether find elements under event mode, default value is false.
*/
getViewAtPoint: function(x, y, usePolyCollision, global, eventMode){
var result = global ? [] : null,
children = this.children, child, obj;
for(var i = children.length - 1; i >= 0; i--){
child = children[i];
//skip child which is not shown or pointer enabled
if(!child || !child.visible || child.alpha <= 0 || (eventMode && !child.pointerEnabled)) continue;
//find child recursively
if(child.children && child.children.length && !(eventMode && !child.pointerChildren)){
obj = child.getViewAtPoint(x, y, usePolyCollision, global, eventMode);
}
if(obj){
if(!global) return obj;
else if(obj.length) result = result.concat(obj);
}else if(child.hitTestPoint(x, y, usePolyCollision)){
if(!global) return child;
else result.push(child);
}
}
return global && result.length ? result : null;
},
/**
* Rewrite render method.
* @private
*/
render: function(renderer, delta){
Container.superclass.render.call(this, renderer, delta);
var children = this.children.slice(0), i, len, child;
for(i = 0, len = children.length; i < len; i++){
child = children[i];
//NOTE: the child could remove or change it's parent
if(child.parent === this) child._render(renderer, delta);
}
}
});