mirror of
https://github.com/hiloteam/Hilo.git
synced 2025-12-08 20:35:59 +00:00
348 lines
11 KiB
JavaScript
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);
|
|
}
|
|
}
|
|
|
|
});
|