1 /****************************************************************************
  2  Copyright (c) 2011-2012 cocos2d-x.org
  3  Copyright (c) 2013-2014 Chukong Technologies Inc.
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24  ****************************************************************************/
 25 
 26 /**
 27  * ccui.Layout is the base class of  ccui.PageView and ccui.ScrollView, it does layout by layout manager
 28  *  and clips area by its _clippingStencil when clippingEnabled is true.
 29  * @class
 30  * @extends ccui.Widget
 31  *
 32  * @property {Boolean}                  clippingEnabled - Indicate whether clipping is enabled
 33  * @property {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR}   clippingType
 34  * @property {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE}  layoutType
 35  *
 36  */
 37 ccui.Layout = ccui.Widget.extend(/** @lends ccui.Layout# */{
 38     _clippingEnabled: false,
 39     _backGroundScale9Enabled: null,
 40     _backGroundImage: null,
 41     _backGroundImageFileName: null,
 42     _backGroundImageCapInsets: null,
 43     _colorType: null,
 44     _bgImageTexType: ccui.Widget.LOCAL_TEXTURE,
 45     _colorRender: null,
 46     _gradientRender: null,
 47     _color: null,
 48     _startColor: null,
 49     _endColor: null,
 50     _alongVector: null,
 51     _opacity: 255,
 52     _backGroundImageTextureSize: null,
 53     _layoutType: null,
 54     _doLayoutDirty: true,
 55     _clippingRectDirty: true,
 56     _clippingType: null,
 57     _clippingStencil: null,
 58     _handleScissor: false,
 59     _scissorRectDirty: false,
 60     _clippingRect: null,
 61     _clippingParent: null,
 62     _className: "Layout",
 63     _backGroundImageColor: null,
 64     _finalPositionX: 0,
 65     _finalPositionY: 0,
 66 
 67     //clipping
 68     _currentStencilEnabled: 0,
 69     _currentStencilWriteMask: 0,
 70     _currentStencilFunc: 0,
 71     _currentStencilRef:0,
 72     _currentStencilValueMask:0,
 73     _currentStencilFail:0,
 74     _currentStencilPassDepthFail:0,
 75     _currentStencilPassDepthPass:0,
 76     _currentDepthWriteMask:0,
 77 
 78     _currentAlphaTestEnabled:0,
 79     _currentAlphaTestFunc:0,
 80     _currentAlphaTestRef:0,
 81 
 82     _backGroundImageOpacity:0,
 83 
 84     _mask_layer_le: 0,
 85 
 86     _loopFocus: false,                                                          //whether enable loop focus or not
 87     __passFocusToChild: false,                                                  //on default, it will pass the focus to the next nearest widget
 88     _isFocusPassing:false,                                                      //when finding the next focused widget, use this variable to pass focus between layout & widget
 89 
 90     /**
 91      * Allocates and initializes an UILayout.
 92      * Constructor of ccui.Layout
 93      * @function
 94      * @example
 95      * // example
 96      * var uiLayout = new ccui.Layout();
 97      */
 98     ctor: function () {
 99         this._layoutType = ccui.Layout.ABSOLUTE;
100         this._widgetType = ccui.Widget.TYPE_CONTAINER;
101         this._clippingType = ccui.Layout.CLIPPING_STENCIL;
102         this._colorType = ccui.Layout.BG_COLOR_NONE;
103 
104         ccui.Widget.prototype.ctor.call(this);
105         this._backGroundImageCapInsets = cc.rect(0, 0, 0, 0);
106 
107         this._color = cc.color(255, 255, 255, 255);
108         this._startColor = cc.color(255, 255, 255, 255);
109         this._endColor = cc.color(255, 255, 255, 255);
110         this._alongVector = cc.p(0, -1);
111         this._backGroundImageTextureSize = cc.size(0, 0);
112 
113         this._clippingRect = cc.rect(0, 0, 0, 0);
114         this._backGroundImageColor = cc.color(255, 255, 255, 255);
115     },
116 
117     /**
118      * Calls its parent's onEnter, and calls its clippingStencil's onEnter if clippingStencil isn't null.
119      * @override
120      */
121     onEnter: function(){
122         ccui.Widget.prototype.onEnter.call(this);
123         if (this._clippingStencil)
124             this._clippingStencil.onEnter();
125         this._doLayoutDirty = true;
126         this._clippingRectDirty = true;
127     },
128 
129     /**
130      *  Calls its parent's onExit, and calls its clippingStencil's onExit if clippingStencil isn't null.
131      *  @override
132      */
133     onExit: function(){
134         ccui.Widget.prototype.onExit.call(this);
135         if (this._clippingStencil)
136             this._clippingStencil.onExit();
137     },
138 
139     /**
140      * If a layout is loop focused which means that the focus movement will be inside the layout
141      * @param {Boolean} loop pass true to let the focus movement loop inside the layout
142      */
143     setLoopFocus: function(loop){
144         this._loopFocus = loop;
145     },
146 
147     /**
148      * Gets whether enable focus loop
149      * @returns {boolean}  If focus loop is enabled, then it will return true, otherwise it returns false. The default value is false.
150      */
151     isLoopFocus: function(){
152         return this._loopFocus;
153     },
154 
155     /**
156      * Specifies whether the layout pass its focus to its child
157      * @param pass To specify whether the layout pass its focus to its child
158      */
159     setPassFocusToChild: function(pass){
160         this.__passFocusToChild = pass;
161     },
162 
163     /**
164      * Returns whether the layout will pass the focus to its children or not. The default value is true
165      * @returns {boolean} To query whether the layout will pass the focus to its children or not. The default value is true
166      */
167     isPassFocusToChild: function(){
168         return this.__passFocusToChild;
169     },
170 
171     /**
172      * When a widget is in a layout, you could call this method to get the next focused widget within a specified direction.
173      * If the widget is not in a layout, it will return itself
174      * @param {Number} direction the direction to look for the next focused widget in a layout
175      * @param {ccui.Widget} current the current focused widget
176      * @returns {ccui.Widget} return the index of widget in the layout
177      */
178     findNextFocusedWidget: function(direction, current){
179         if (this._isFocusPassing || this.isFocused()) {
180             var parent = this.getParent();
181             this._isFocusPassing = false;
182 
183             if (this.__passFocusToChild) {
184                 var w = this._passFocusToChild(direction, current);
185                 if (w instanceof ccui.Layout && parent) {
186                     parent._isFocusPassing = true;
187                     return parent.findNextFocusedWidget(direction, this);
188                 }
189                 return w;
190             }
191 
192             if (null == parent)
193                 return this;
194             parent._isFocusPassing = true;
195             return parent.findNextFocusedWidget(direction, this);
196         } else if(current.isFocused() || current instanceof ccui.Layout) {
197             if (this._layoutType == ccui.Layout.LINEAR_HORIZONTAL) {
198                 switch (direction){
199                     case ccui.Widget.LEFT:
200                         return this._getPreviousFocusedWidget(direction, current);
201                     break;
202                     case ccui.Widget.RIGHT:
203                         return this._getNextFocusedWidget(direction, current);
204                     break;
205                     case ccui.Widget.DOWN:
206                     case ccui.Widget.UP:
207                         if (this._isLastWidgetInContainer(this, direction)){
208                             if (this._isWidgetAncestorSupportLoopFocus(current, direction))
209                                 return this.findNextFocusedWidget(direction, this);
210                             return current;
211                         } else {
212                             return this.findNextFocusedWidget(direction, this);
213                         }
214                     break;
215                     default:
216                         cc.assert(0, "Invalid Focus Direction");
217                         return current;
218                 }
219             } else if (this._layoutType == ccui.Layout.LINEAR_VERTICAL) {
220                 switch (direction){
221                     case ccui.Widget.LEFT:
222                     case ccui.Widget.RIGHT:
223                         if (this._isLastWidgetInContainer(this, direction)) {
224                             if (this._isWidgetAncestorSupportLoopFocus(current, direction))
225                                 return this.findNextFocusedWidget(direction, this);
226                             return current;
227                         }
228                         else
229                             return this.findNextFocusedWidget(direction, this);
230                      break;
231                     case ccui.Widget.DOWN:
232                         return this._getNextFocusedWidget(direction, current);
233                         break;
234                     case ccui.Widget.UP:
235                         return this._getPreviousFocusedWidget(direction, current);
236                         break;
237                     default:
238                         cc.assert(0, "Invalid Focus Direction");
239                         return current;
240                 }
241             } else {
242                 cc.assert(0, "Un Supported Layout type, please use VBox and HBox instead!!!");
243                 return current;
244             }
245         } else
246             return current;
247     },
248 
249     /**
250      * To specify a user-defined functor to decide which child widget of the layout should get focused
251      * @function
252      * @param {Number} direction
253      * @param {ccui.Widget} current
254      */
255     onPassFocusToChild: null,
256 
257     /**
258      * override "init" method of widget. please do not call this function by yourself, you should pass the parameters to constructor to initialize it.
259      * @returns {boolean}
260      * @override
261      */
262     init: function () {
263         if (ccui.Widget.prototype.init.call(this)) {
264             this.ignoreContentAdaptWithSize(false);
265             this.setContentSize(cc.size(0, 0));
266             this.setAnchorPoint(0, 0);
267             this.onPassFocusToChild  = this._findNearestChildWidgetIndex.bind(this);
268             return true;
269         }
270         return false;
271     },
272 
273     __stencilDraw: function(ctx){          //Only for Canvas
274         var locContext = ctx || cc._renderContext;
275         var stencil = this._clippingStencil;
276         var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY();
277         for (var i = 0; i < stencil._buffer.length; i++) {
278             var element = stencil._buffer[i];
279             var vertices = element.verts;
280             var firstPoint = vertices[0];
281             locContext.beginPath();
282             locContext.moveTo(firstPoint.x * locEGL_ScaleX, -firstPoint.y * locEGL_ScaleY);
283             for (var j = 1, len = vertices.length; j < len; j++)
284                 locContext.lineTo(vertices[j].x * locEGL_ScaleX, -vertices[j].y * locEGL_ScaleY);
285         }
286     },
287 
288     /**
289      * Adds a widget to the container.
290      * @param {ccui.Widget} widget
291      * @param {Number} [zOrder]
292      * @param {Number|string} [tag] tag or name
293      * @override
294      */
295     addChild: function (widget, zOrder, tag) {
296         if ((widget instanceof ccui.Widget)) {
297             this._supplyTheLayoutParameterLackToChild(widget);
298         }
299         ccui.Widget.prototype.addChild.call(this, widget, zOrder, tag);
300         this._doLayoutDirty = true;
301     },
302 
303     /**
304      * Removes child widget from ccui.Layout, and sets the layout dirty flag to true.
305      * @param {ccui.Widget} widget
306      * @param {Boolean} [cleanup=true]
307      * @override
308      */
309     removeChild: function (widget, cleanup) {
310         ccui.Widget.prototype.removeChild.call(this, widget, cleanup);
311         this._doLayoutDirty = true;
312     },
313 
314     /**
315      * Removes all children from the container with a cleanup, and sets the layout dirty flag to true.
316      * @param {Boolean} cleanup
317      */
318     removeAllChildren: function (cleanup) {
319         ccui.Widget.prototype.removeAllChildren.call(this, cleanup);
320         this._doLayoutDirty = true;
321     },
322 
323     /**
324      * Removes all children from the container, do a cleanup to all running actions depending on the cleanup parameter,
325      * and sets the layout dirty flag to true.
326      * @param {Boolean} cleanup true if all running actions on all children nodes should be cleanup, false otherwise.
327      */
328     removeAllChildrenWithCleanup: function(cleanup){
329         ccui.Widget.prototype.removeAllChildrenWithCleanup.call(this, cleanup);
330         this._doLayoutDirty = true;
331     },
332 
333     /**
334      * Gets if layout is clipping enabled.
335      * @returns {Boolean} if layout is clipping enabled.
336      */
337     isClippingEnabled: function () {
338         return this._clippingEnabled;
339     },
340 
341     /**
342      * <p>
343      *     Calls adaptRenderers (its subclass will override it.) and do layout.
344      *     If clippingEnabled is true, it will clip/scissor area.
345      * </p>
346      * @override
347      * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx
348      */
349     visit: function (ctx) {
350         if (!this._visible)
351             return;
352         this._adaptRenderers();
353         this._doLayout();
354 
355         if (this._clippingEnabled) {
356             switch (this._clippingType) {
357                 case ccui.Layout.CLIPPING_STENCIL:
358                     this._stencilClippingVisit(ctx);
359                     break;
360                 case ccui.Layout.CLIPPING_SCISSOR:
361                     this._scissorClippingVisit(ctx);
362                     break;
363                 default:
364                     break;
365             }
366         } else
367             ccui.Widget.prototype.visit.call(this, ctx);
368     },
369 
370     _stencilClippingVisit: null,
371 
372     _stencilClippingVisitForWebGL: function (ctx) {
373         var gl = ctx || cc._renderContext;
374 
375         if (!this._clippingStencil || !this._clippingStencil.isVisible())
376             return;
377 
378         // all the _stencilBits are in use?
379         if (ccui.Layout._layer + 1 == cc.stencilBits) {
380             // warn once
381             ccui.Layout._visit_once = true;
382             if (ccui.Layout._visit_once) {
383                 cc.log("Nesting more than " + cc.stencilBits + "stencils is not supported. Everything will be drawn without stencil for this node and its childs.");
384                 ccui.Layout._visit_once = false;
385             }
386             // draw everything, as if there where no stencil
387             cc.Node.prototype.visit.call(this, ctx);
388             return;
389         }
390 
391         ccui.Layout._layer++;
392 
393         var mask_layer = 0x1 << ccui.Layout._layer;
394         var mask_layer_l = mask_layer - 1;
395         var mask_layer_le = mask_layer | mask_layer_l;
396 
397         // manually save the stencil state
398         var currentStencilEnabled = gl.isEnabled(gl.STENCIL_TEST);
399         var currentStencilWriteMask = gl.getParameter(gl.STENCIL_WRITEMASK);
400         var currentStencilFunc = gl.getParameter(gl.STENCIL_FUNC);
401         var currentStencilRef = gl.getParameter(gl.STENCIL_REF);
402         var currentStencilValueMask = gl.getParameter(gl.STENCIL_VALUE_MASK);
403         var currentStencilFail = gl.getParameter(gl.STENCIL_FAIL);
404         var currentStencilPassDepthFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL);
405         var currentStencilPassDepthPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS);
406 
407         gl.enable(gl.STENCIL_TEST);
408 
409         gl.stencilMask(mask_layer);
410 
411         var currentDepthWriteMask = gl.getParameter(gl.DEPTH_WRITEMASK);
412 
413         gl.depthMask(false);
414 
415         gl.stencilFunc(gl.NEVER, mask_layer, mask_layer);
416         gl.stencilOp(gl.ZERO, gl.KEEP, gl.KEEP);
417 
418         // draw a fullscreen solid rectangle to clear the stencil buffer
419         cc.kmGLMatrixMode(cc.KM_GL_PROJECTION);
420         cc.kmGLPushMatrix();
421         cc.kmGLLoadIdentity();
422         cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW);
423         cc.kmGLPushMatrix();
424         cc.kmGLLoadIdentity();
425         cc._drawingUtil.drawSolidRect(cc.p(-1,-1), cc.p(1,1), cc.color(255, 255, 255, 255));
426         cc.kmGLMatrixMode(cc.KM_GL_PROJECTION);
427         cc.kmGLPopMatrix();
428         cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW);
429         cc.kmGLPopMatrix();
430 
431         gl.stencilFunc(gl.NEVER, mask_layer, mask_layer);
432         gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP);
433 
434         cc.kmGLPushMatrix();
435         this.transform();
436         this._clippingStencil.visit();
437 
438         gl.depthMask(currentDepthWriteMask);
439         gl.stencilFunc(gl.EQUAL, mask_layer_le, mask_layer_le);
440         gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
441 
442         // draw (according to the stencil test func) this node and its childs
443         var i = 0;      // used by _children
444         var j = 0;      // used by _protectedChildren
445 
446         this.sortAllChildren();
447         this.sortAllProtectedChildren();
448         var locChildren = this._children, locProtectChildren = this._protectedChildren;
449         var iLen = locChildren.length, jLen = locProtectChildren.length, child;
450         for( ; i < iLen; i++ ){
451             child = locChildren[i];
452             if ( child && child.getLocalZOrder() < 0 )
453                 child.visit();
454             else
455                 break;
456         }
457         for( ; j < jLen; j++ ) {
458             child = locProtectChildren[j];
459             if ( child && child.getLocalZOrder() < 0 )
460                 child.visit();
461             else
462                 break;
463         }
464         this.draw();
465         for (; i < iLen; i++)
466             locChildren[i].visit();
467         for (; j < jLen; j++)
468             locProtectChildren[j].visit();
469 
470         // manually restore the stencil state
471         gl.stencilFunc(currentStencilFunc, currentStencilRef, currentStencilValueMask);
472         gl.stencilOp(currentStencilFail, currentStencilPassDepthFail, currentStencilPassDepthPass);
473         gl.stencilMask(currentStencilWriteMask);
474         if (!currentStencilEnabled)
475             gl.disable(gl.STENCIL_TEST);
476         ccui.Layout._layer--;
477 
478         cc.kmGLPopMatrix();
479     },
480 
481     _stencilClippingVisitForCanvas: function (ctx) {
482         // return fast (draw nothing, or draw everything if in inverted mode) if:
483         // - nil stencil node
484         // - or stencil node invisible:
485         if (!this._clippingStencil || !this._clippingStencil.isVisible()) {
486             return;
487         }
488         var context = ctx || cc._renderContext;
489         // Composition mode, costy but support texture stencil
490         if (this._clippingStencil instanceof cc.Sprite) {
491             // Cache the current canvas, for later use (This is a little bit heavy, replace this solution with other walkthrough)
492             var canvas = context.canvas;
493             var locCache = ccui.Layout._getSharedCache();
494             locCache.width = canvas.width;
495             locCache.height = canvas.height;
496             var locCacheCtx = locCache.getContext("2d");
497             locCacheCtx.drawImage(canvas, 0, 0);
498 
499             context.save();
500             // Draw everything first using node visit function
501             cc.ProtectedNode.prototype.visit.call(this, context);
502 
503             context.globalCompositeOperation = "destination-in";
504 
505             this.transform(context);
506             this._clippingStencil.visit();
507 
508             context.restore();
509 
510             // Redraw the cached canvas, so that the cliped area shows the background etc.
511             context.save();
512             context.setTransform(1, 0, 0, 1, 0, 0);
513             context.globalCompositeOperation = "destination-over";
514             context.drawImage(locCache, 0, 0);
515             context.restore();
516         } else {    // Clip mode, fast, but only support cc.DrawNode
517             var i, children = this._children, locChild;
518 
519             context.save();
520             this.transform(context);
521             this._clippingStencil.visit(context);
522             context.clip();
523 
524             // Clip mode doesn't support recusive stencil, so once we used a clip stencil,
525             // so if it has ClippingNode as a child, the child must uses composition stencil.
526             this.sortAllChildren();
527             this.sortAllProtectedChildren();
528 
529             var j, locProtectChildren = this._protectedChildren;
530             var iLen = children.length, jLen = locProtectChildren.length;
531 
532             // draw children zOrder < 0
533             for (i = 0; i < iLen; i++) {
534                 locChild = children[i];
535                 if (locChild && locChild._localZOrder < 0)
536                     locChild.visit(context);
537                 else
538                     break;
539             }
540             for (j = 0; j < jLen; j++) {
541                 locChild = locProtectChildren[j];
542                 if (locChild && locChild._localZOrder < 0)
543                     locChild.visit(context);
544                 else
545                     break;
546             }
547             //this.draw(context);
548             for (; i < iLen; i++)
549                 children[i].visit(context);
550             for (; j < jLen; j++)
551                 locProtectChildren[j].visit(context);
552 
553             context.restore();
554         }
555     },
556 
557     _scissorClippingVisit: null,
558     _scissorClippingVisitForWebGL: function (ctx) {
559         var clippingRect = this._getClippingRect();
560         var gl = ctx || cc._renderContext;
561         if (this._handleScissor) {
562             gl.enable(gl.SCISSOR_TEST);
563         }
564         cc.view.setScissorInPoints(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height);
565         cc.Node.prototype.visit.call(this);
566         if (this._handleScissor) {
567             gl.disable(gl.SCISSOR_TEST);
568         }
569     },
570 
571     /**
572      * Changes if layout can clip it's content and locChild.
573      * If you really need this, please enable it. But it would reduce the rendering efficiency.
574      * @param {Boolean} able clipping enabled.
575      */
576     setClippingEnabled: function (able) {
577         if (able == this._clippingEnabled)
578             return;
579         this._clippingEnabled = able;
580         switch (this._clippingType) {
581             case ccui.Layout.CLIPPING_STENCIL:
582                 if (able){
583                     this._clippingStencil = cc.DrawNode.create();
584                     if(cc._renderType === cc._RENDER_TYPE_CANVAS)
585                         this._clippingStencil.draw = this.__stencilDraw.bind(this);
586                     if (this._running)
587                         this._clippingStencil.onEnter();
588                     this._setStencilClippingSize(this._contentSize);
589                 } else {
590                     if (this._running && this._clippingStencil)
591                         this._clippingStencil.onExit();
592                     this._clippingStencil = null;
593                 }
594                 break;
595             default:
596                 break;
597         }
598     },
599 
600     /**
601      * Sets clipping type to ccui.Layout
602      * @param {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} type
603      */
604     setClippingType: function (type) {
605         if (type == this._clippingType)
606             return;
607         var clippingEnabled = this.isClippingEnabled();
608         this.setClippingEnabled(false);
609         this._clippingType = type;
610         this.setClippingEnabled(clippingEnabled);
611     },
612 
613     /**
614      * Gets clipping type of ccui.Layout
615      * @returns {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR}
616      */
617     getClippingType: function () {
618         return this._clippingType;
619     },
620 
621     _setStencilClippingSize: function (size) {
622         if (this._clippingEnabled && this._clippingType == ccui.Layout.CLIPPING_STENCIL) {
623             var rect = [];
624             rect[0] = cc.p(0, 0);
625             rect[1] = cc.p(size.width, 0);
626             rect[2] = cc.p(size.width, size.height);
627             rect[3] = cc.p(0, size.height);
628             var green = cc.color.GREEN;
629             this._clippingStencil.clear();
630             this._clippingStencil.drawPoly(rect, 4, green, 0, green);
631         }
632     },
633 
634     _getClippingRect: function () {
635         if (this._clippingRectDirty) {
636             var worldPos = this.convertToWorldSpace(cc.p(0, 0));
637             var t = this.nodeToWorldTransform();
638             var scissorWidth = this._contentSize.width * t.a;
639             var scissorHeight = this._contentSize.height * t.d;
640             var parentClippingRect;
641             var parent = this;
642 
643             while (parent) {
644                 parent = parent.getParent();
645                 if (parent && parent instanceof ccui.Layout && parent.isClippingEnabled()) {
646                     this._clippingParent = parent;
647                     break;
648                 }
649             }
650 
651             if (this._clippingParent) {
652                 parentClippingRect = this._clippingParent._getClippingRect();
653                 var finalX = worldPos.x - (scissorWidth * this._anchorPoint.x);
654                 var finalY = worldPos.y - (scissorHeight * this._anchorPoint.y);
655                 var finalWidth = scissorWidth;
656                 var finalHeight = scissorHeight;
657 
658                 var leftOffset = worldPos.x - parentClippingRect.x;
659                 if (leftOffset < 0) {
660                     finalX = parentClippingRect.x;
661                     finalWidth += leftOffset;
662                 }
663                 var rightOffset = (worldPos.x + scissorWidth) - (parentClippingRect.x + parentClippingRect.width);
664                 if (rightOffset > 0)
665                     finalWidth -= rightOffset;
666                 var topOffset = (worldPos.y + scissorHeight) - (parentClippingRect.y + parentClippingRect.height);
667                 if (topOffset > 0)
668                     finalHeight -= topOffset;
669                 var bottomOffset = worldPos.y - parentClippingRect.y;
670                 if (bottomOffset < 0) {
671                     finalY = parentClippingRect.x;
672                     finalHeight += bottomOffset;
673                 }
674                 if (finalWidth < 0)
675                     finalWidth = 0;
676                 if (finalHeight < 0)
677                     finalHeight = 0;
678                 this._clippingRect.x = finalX;
679                 this._clippingRect.y = finalY;
680                 this._clippingRect.width = finalWidth;
681                 this._clippingRect.height = finalHeight;
682             } else {
683                 this._clippingRect.x = worldPos.x - (scissorWidth * this._anchorPoint.x);
684                 this._clippingRect.y = worldPos.y - (scissorHeight * this._anchorPoint.y);
685                 this._clippingRect.width = scissorWidth;
686                 this._clippingRect.height = scissorHeight;
687             }
688             this._clippingRectDirty = false;
689         }
690         return this._clippingRect;
691     },
692 
693     _onSizeChanged: function () {
694         ccui.Widget.prototype._onSizeChanged.call(this);
695         var locContentSize = this._contentSize;
696         this._setStencilClippingSize(locContentSize);
697         this._doLayoutDirty = true;
698         this._clippingRectDirty = true;
699         if (this._backGroundImage) {
700             this._backGroundImage.setPosition(locContentSize.width * 0.5, locContentSize.height * 0.5);
701             if (this._backGroundScale9Enabled && this._backGroundImage instanceof ccui.Scale9Sprite)
702                 this._backGroundImage.setPreferredSize(locContentSize);
703         }
704         if (this._colorRender)
705             this._colorRender.setContentSize(locContentSize);
706         if (this._gradientRender)
707             this._gradientRender.setContentSize(locContentSize);
708     },
709 
710     /**
711      * Sets background image use scale9 renderer.
712      * @param {Boolean} able  true that use scale9 renderer, false otherwise.
713      */
714     setBackGroundImageScale9Enabled: function (able) {
715         if (this._backGroundScale9Enabled == able)
716             return;
717         this.removeProtectedChild(this._backGroundImage);
718         this._backGroundImage = null;
719         this._backGroundScale9Enabled = able;
720         this._addBackGroundImage();
721         this.setBackGroundImage(this._backGroundImageFileName, this._bgImageTexType);
722         this.setBackGroundImageCapInsets(this._backGroundImageCapInsets);
723     },
724 
725     /**
726      * Get whether background image is use scale9 renderer.
727      * @returns {Boolean}
728      */
729     isBackGroundImageScale9Enabled: function () {
730         return this._backGroundScale9Enabled;
731     },
732 
733     /**
734      * Sets a background image for layout
735      * @param {String} fileName
736      * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType
737      */
738     setBackGroundImage: function (fileName, texType) {
739         if (!fileName)
740             return;
741         texType = texType || ccui.Widget.LOCAL_TEXTURE;
742         if (this._backGroundImage == null)
743             this._addBackGroundImage();
744         this._backGroundImageFileName = fileName;
745         this._bgImageTexType = texType;
746         var locBackgroundImage = this._backGroundImage;
747         if (this._backGroundScale9Enabled) {
748             var bgiScale9 = locBackgroundImage;
749             switch (this._bgImageTexType) {
750                 case ccui.Widget.LOCAL_TEXTURE:
751                     bgiScale9.initWithFile(fileName);
752                     break;
753                 case ccui.Widget.PLIST_TEXTURE:
754                     bgiScale9.initWithSpriteFrameName(fileName);
755                     break;
756                 default:
757                     break;
758             }
759             bgiScale9.setPreferredSize(this._contentSize);
760         } else {
761             var sprite = locBackgroundImage;
762             switch (this._bgImageTexType){
763                 case ccui.Widget.LOCAL_TEXTURE:
764                     //SetTexture cannot load resource
765                     sprite.initWithFile(fileName);
766                     break;
767                 case ccui.Widget.PLIST_TEXTURE:
768                     //SetTexture cannot load resource
769                     sprite.initWithSpriteFrameName(fileName);
770                     break;
771                 default:
772                     break;
773             }
774         }
775         this._backGroundImageTextureSize = locBackgroundImage.getContentSize();
776         locBackgroundImage.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5);
777         this._updateBackGroundImageColor();
778 
779         /*//async load callback
780         var self = this;
781         if(!locBackgroundImage.texture || !locBackgroundImage.texture.isLoaded()){
782             locBackgroundImage.addLoadedEventListener(function(){
783                 self._backGroundImageTextureSize = locBackgroundImage.getContentSize();
784                 locBackgroundImage.setPosition(self._contentSize.width * 0.5, self._contentSize.height * 0.5);
785                 self._updateBackGroundImageColor();
786 
787                 self._imageRendererAdaptDirty = true;
788                 self._findLayout();
789             });
790         }*/
791     },
792 
793     /**
794      * Sets a background image CapInsets for layout, if the background image is a scale9 render.
795      * @param {cc.Rect} capInsets capinsets of background image.
796      */
797     setBackGroundImageCapInsets: function (capInsets) {
798         if(!capInsets)
799             return;
800         var locInsets = this._backGroundImageCapInsets;
801         locInsets.x = capInsets.x;
802         locInsets.y = capInsets.y;
803         locInsets.width = capInsets.width;
804         locInsets.height = capInsets.height;
805         if (this._backGroundScale9Enabled)
806             this._backGroundImage.setCapInsets(capInsets);
807     },
808 
809     /**
810      * Gets background image capinsets of ccui.Layout.
811      * @returns {cc.Rect}
812      */
813     getBackGroundImageCapInsets: function () {
814         return cc.rect(this._backGroundImageCapInsets);
815     },
816 
817     _supplyTheLayoutParameterLackToChild: function (locChild) {
818         if (!locChild) {
819             return;
820         }
821         switch (this._layoutType) {
822             case ccui.Layout.ABSOLUTE:
823                 break;
824             case ccui.Layout.LINEAR_HORIZONTAL:
825             case ccui.Layout.LINEAR_VERTICAL:
826                 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.LINEAR);
827                 if (!layoutParameter)
828                     locChild.setLayoutParameter(ccui.LinearLayoutParameter.create());
829                 break;
830             case ccui.Layout.RELATIVE:
831                 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.RELATIVE);
832                 if (!layoutParameter)
833                     locChild.setLayoutParameter(ccui.RelativeLayoutParameter.create());
834                 break;
835             default:
836                 break;
837         }
838     },
839 
840     _addBackGroundImage: function () {
841         if (this._backGroundScale9Enabled) {
842             this._backGroundImage = ccui.Scale9Sprite.create();
843             this._backGroundImage.setPreferredSize(this._contentSize);
844         } else
845             this._backGroundImage = cc.Sprite.create();
846         this.addProtectedChild(this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1);
847         this._backGroundImage.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0);
848     },
849 
850     /**
851      * Remove the background image of ccui.Layout.
852      */
853     removeBackGroundImage: function () {
854         if (!this._backGroundImage)
855             return;
856         this.removeProtectedChild(this._backGroundImage);
857         this._backGroundImage = null;
858         this._backGroundImageFileName = "";
859         this._backGroundImageTextureSize.width = 0;
860         this._backGroundImageTextureSize.height = 0;
861     },
862 
863     /**
864      * Sets Color Type for ccui.Layout.
865      * @param {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} type
866      */
867     setBackGroundColorType: function (type) {
868         if (this._colorType == type)
869             return;
870         switch (this._colorType) {
871             case ccui.Layout.BG_COLOR_NONE:
872                 if (this._colorRender) {
873                     this.removeProtectedChild(this._colorRender);
874                     this._colorRender = null;
875                 }
876                 if (this._gradientRender) {
877                     this.removeProtectedChild(this._gradientRender);
878                     this._gradientRender = null;
879                 }
880                 break;
881             case ccui.Layout.BG_COLOR_SOLID:
882                 if (this._colorRender) {
883                     this.removeProtectedChild(this._colorRender);
884                     this._colorRender = null;
885                 }
886                 break;
887             case ccui.Layout.BG_COLOR_GRADIENT:
888                 if (this._gradientRender) {
889                     this.removeProtectedChild(this._gradientRender);
890                     this._gradientRender = null;
891                 }
892                 break;
893             default:
894                 break;
895         }
896         this._colorType = type;
897         switch (this._colorType) {
898             case ccui.Layout.BG_COLOR_NONE:
899                 break;
900             case ccui.Layout.BG_COLOR_SOLID:
901                 this._colorRender = new cc.LayerColor();
902                 this._colorRender.setContentSize(this._contentSize);
903                 this._colorRender.setOpacity(this._opacity);
904                 this._colorRender.setColor(this._color);
905                 this.addProtectedChild(this._colorRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1);
906                 break;
907             case ccui.Layout.BG_COLOR_GRADIENT:
908                 this._gradientRender = new cc.LayerGradient(cc.color(255, 0, 0, 255), cc.color(0, 255, 0, 255));
909                 this._gradientRender.setContentSize(this._contentSize);
910                 this._gradientRender.setOpacity(this._opacity);
911                 this._gradientRender.setStartColor(this._startColor);
912                 this._gradientRender.setEndColor(this._endColor);
913                 this._gradientRender.setVector(this._alongVector);
914                 this.addProtectedChild(this._gradientRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1);
915                 break;
916             default:
917                 break;
918         }
919     },
920 
921     /**
922      * Get background color type of ccui.Layout.
923      * @returns {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT}
924      */
925     getBackGroundColorType: function () {
926         return this._colorType;
927     },
928 
929     /**
930      * Sets background color for layout, if color type is Layout.COLOR_SOLID
931      * @param {cc.Color} color
932      * @param {cc.Color} [endColor]
933      */
934     setBackGroundColor: function (color, endColor) {
935         if (!endColor) {
936             this._color.r = color.r;
937             this._color.g = color.g;
938             this._color.b = color.b;
939             if (this._colorRender)
940                 this._colorRender.setColor(color);
941         } else {
942             this._startColor.r = color.r;
943             this._startColor.g = color.g;
944             this._startColor.b = color.b;
945             if (this._gradientRender)
946                 this._gradientRender.setStartColor(color);
947 
948             this._endColor.r = endColor.r;
949             this._endColor.g = endColor.g;
950             this._endColor.b = endColor.b;
951             if (this._gradientRender)
952                 this._gradientRender.setEndColor(endColor);
953         }
954     },
955 
956     /**
957      * Gets background color of ccui.Layout, if color type is Layout.COLOR_SOLID.
958      * @returns {cc.Color}
959      */
960     getBackGroundColor: function () {
961         var tmpColor = this._color;
962         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
963     },
964 
965     /**
966      * Gets background start color of ccui.Layout
967      * @returns {cc.Color}
968      */
969     getBackGroundStartColor: function () {
970         var tmpColor = this._startColor;
971         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
972     },
973 
974     /**
975      * Gets background end color of ccui.Layout
976      * @returns {cc.Color}
977      */
978     getBackGroundEndColor: function () {
979         var tmpColor = this._endColor;
980         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
981     },
982 
983     /**
984      * Sets background opacity to ccui.Layout.
985      * @param {number} opacity
986      */
987     setBackGroundColorOpacity: function (opacity) {
988         this._opacity = opacity;
989         switch (this._colorType) {
990             case ccui.Layout.BG_COLOR_NONE:
991                 break;
992             case ccui.Layout.BG_COLOR_SOLID:
993                 this._colorRender.setOpacity(opacity);
994                 break;
995             case ccui.Layout.BG_COLOR_GRADIENT:
996                 this._gradientRender.setOpacity(opacity);
997                 break;
998             default:
999                 break;
1000         }
1001     },
1002 
1003     /**
1004      * Get background opacity value of ccui.Layout.
1005      * @returns {Number}
1006      */
1007     getBackGroundColorOpacity: function () {
1008         return this._opacity;
1009     },
1010 
1011     /**
1012      * Sets background color vector for layout, if color type is Layout.COLOR_GRADIENT
1013      * @param {cc.Point} vector
1014      */
1015     setBackGroundColorVector: function (vector) {
1016         this._alongVector.x = vector.x;
1017         this._alongVector.y = vector.y;
1018         if (this._gradientRender) {
1019             this._gradientRender.setVector(vector);
1020         }
1021     },
1022 
1023     /**
1024      *  Gets background color vector of ccui.Layout, if color type is Layout.COLOR_GRADIENT
1025      * @returns {cc.Point}
1026      */
1027     getBackGroundColorVector: function () {
1028         return this._alongVector;
1029     },
1030 
1031     /**
1032      * Sets backGround image color
1033      * @param {cc.Color} color
1034      */
1035     setBackGroundImageColor: function (color) {
1036         this._backGroundImageColor.r = color.r;
1037         this._backGroundImageColor.g = color.g;
1038         this._backGroundImageColor.b = color.b;
1039 
1040         this._updateBackGroundImageColor();
1041     },
1042 
1043     /**
1044      * Sets backGround image Opacity
1045      * @param {Number} opacity
1046      */
1047     setBackGroundImageOpacity: function (opacity) {
1048         this._backGroundImageColor.a = opacity;
1049         this.getBackGroundImageColor();
1050     },
1051 
1052     /**
1053      * Gets backGround image color
1054      * @returns {cc.Color}
1055      */
1056     getBackGroundImageColor: function () {
1057         var color = this._backGroundImageColor;
1058         return cc.color(color.r, color.g, color.b, color.a);
1059     },
1060 
1061     /**
1062      * Gets backGround image opacity
1063      * @returns {Number}
1064      */
1065     getBackGroundImageOpacity: function () {
1066         return this._backGroundImageColor.a;
1067     },
1068 
1069     _updateBackGroundImageColor: function () {
1070         if(this._backGroundImage)
1071             this._backGroundImage.setColor(this._backGroundImageColor);
1072     },
1073 
1074     /**
1075      * Gets background image texture size.
1076      * @returns {cc.Size}
1077      */
1078     getBackGroundImageTextureSize: function () {
1079         return this._backGroundImageTextureSize;
1080     },
1081 
1082     /**
1083      * Sets LayoutType to ccui.Layout, LayoutManager will do layout by layout type..
1084      * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type
1085      */
1086     setLayoutType: function (type) {
1087         this._layoutType = type;
1088         var layoutChildrenArray = this._children;
1089         var locChild = null;
1090         for (var i = 0; i < layoutChildrenArray.length; i++) {
1091             locChild = layoutChildrenArray[i];
1092             if(locChild instanceof ccui.Widget)
1093                 this._supplyTheLayoutParameterLackToChild(locChild);
1094         }
1095         this._doLayoutDirty = true;
1096     },
1097 
1098     /**
1099      * Gets LayoutType of ccui.Layout.
1100      * @returns {null}
1101      */
1102     getLayoutType: function () {
1103         return this._layoutType;
1104     },
1105 
1106     /**
1107      * request do layout, it will do layout at visit calls
1108      */
1109     requestDoLayout: function () {
1110         this._doLayoutDirty = true;
1111     },
1112 
1113     _doLayout: function () {
1114         if (!this._doLayoutDirty)
1115             return;
1116 
1117         this.sortAllChildren();
1118 
1119         var executant = ccui.getLayoutManager(this._layoutType);
1120         if (executant)
1121             executant._doLayout(this);
1122         this._doLayoutDirty = false;
1123     },
1124 
1125     _getLayoutContentSize: function(){
1126         return this.getContentSize();
1127     },
1128 
1129     _getLayoutElements: function(){
1130         return this.getChildren();
1131     },
1132 
1133     //clipping
1134     _onBeforeVisitStencil: function(){
1135         //TODO NEW RENDERER
1136     },
1137 
1138     _drawFullScreenQuadClearStencil:function(){
1139         //TODO NEW RENDERER
1140     },
1141 
1142     _onAfterDrawStencil: function(){
1143         //TODO NEW RENDERER
1144     },
1145 
1146     _onAfterVisitStencil: function(){
1147         //TODO NEW RENDERER
1148     },
1149 
1150     _onAfterVisitScissor: function(){
1151         //TODO NEW RENDERER
1152     },
1153 
1154     _onAfterVisitScissor: function(){
1155         //TODO NEW RENDERER
1156     },
1157 
1158     _updateBackGroundImageOpacity: function(){
1159         if (this._backGroundImage)
1160             this._backGroundImage.setOpacity(this._backGroundImageOpacity);
1161     },
1162 
1163     _updateBackGroundImageRGBA: function(){
1164         if (this._backGroundImage) {
1165             this._backGroundImage.setColor(this._backGroundImageColor);
1166             this._backGroundImage.setOpacity(this._backGroundImageOpacity);
1167         }
1168     },
1169 
1170     /**
1171      * Gets the content size of the layout, it will accumulate all its children's content size
1172      * @returns {cc.Size}
1173      * @private
1174      */
1175     _getLayoutAccumulatedSize: function(){
1176         var children = this.getChildren();
1177         var  layoutSize = cc.size(0, 0);
1178         var widgetCount = 0, locSize;
1179         for(var i = 0, len = children.length; i < len; i++) {
1180             var layout = children[i];
1181             if (null != layout && layout instanceof ccui.Layout){
1182                 locSize = layout._getLayoutAccumulatedSize();
1183                 layoutSize.width += locSize.width;
1184                 layoutSize.height += locSize.height;
1185             } else {
1186                 if (layout instanceof ccui.Widget) {
1187                     widgetCount++;
1188                     var m = layout.getLayoutParameter().getMargin();
1189                     locSize = layout.getContentSize();
1190                     layoutSize.width += locSize.width +  (m.right + m.left) * 0.5;
1191                     layoutSize.height += locSize.height +  (m.top + m.bottom) * 0.5;
1192                 }
1193             }
1194         }
1195 
1196         //substract extra size
1197         var type = this.getLayoutType();
1198         if (type == ccui.Layout.LINEAR_HORIZONTAL)
1199             layoutSize.height = layoutSize.height - layoutSize.height/widgetCount * (widgetCount-1);
1200 
1201         if (type == ccui.Layout.LINEAR_VERTICAL)
1202             layoutSize.width = layoutSize.width - layoutSize.width/widgetCount * (widgetCount-1);
1203         return layoutSize;
1204     },
1205 
1206     /**
1207      * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child      <br/>
1208      * will get the focus.  The current algorithm to determine which child will get focus is nearest-distance-priority algorithm
1209      * @param {Number} direction next focused widget direction
1210      * @param {ccui.Widget} baseWidget
1211      * @returns {Number}
1212      * @private
1213      */
1214     _findNearestChildWidgetIndex: function(direction, baseWidget){
1215         if (baseWidget == null || baseWidget == this)
1216             return this._findFirstFocusEnabledWidgetIndex();
1217 
1218         var index = 0, locChildren = this.getChildren();
1219         var count = locChildren.length, widgetPosition;
1220 
1221         var distance = cc.FLT_MAX, found = 0;
1222         if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT || direction == ccui.Widget.DOWN || direction == ccui.Widget.UP) {
1223             widgetPosition = this._getWorldCenterPoint(baseWidget);
1224             while (index < count) {
1225                 var w = locChildren[index];
1226                 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) {
1227                     var length = (w instanceof ccui.Layout)? w._calculateNearestDistance(baseWidget)
1228                         : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition));
1229                     if (length < distance){
1230                         found = index;
1231                         distance = length;
1232                     }
1233                 }
1234                 index++;
1235             }
1236             return found;
1237         }
1238         cc.log("invalid focus direction!");
1239         return 0;
1240     },
1241 
1242     /**
1243      * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child
1244      * will get the focus.  The current algorithm to determine which child will get focus is farthest-distance-priority algorithm
1245      * @param {Number} direction next focused widget direction
1246      * @param {ccui.Widget} baseWidget
1247      * @returns {Number} The index of child widget in the container
1248      * @private
1249      */
1250     _findFarthestChildWidgetIndex: function(direction, baseWidget){
1251         if (baseWidget == null || baseWidget == this)
1252             return this._findFirstFocusEnabledWidgetIndex();
1253 
1254         var index = 0, locChildren = this.getChildren();
1255         var count = locChildren.length;
1256 
1257         var distance = -cc.FLT_MAX, found = 0;
1258         if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT || direction == ccui.Widget.DOWN || direction == ccui.Widget.UP) {
1259             var widgetPosition =  this._getWorldCenterPoint(baseWidget);
1260             while (index <  count) {
1261                 var w = locChildren[index];
1262                 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) {
1263                     var length = (w instanceof ccui.Layout)?w._calculateFarthestDistance(baseWidget)
1264                         : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition));
1265                     if (length > distance){
1266                         found = index;
1267                         distance = length;
1268                     }
1269                 }
1270                 index++;
1271             }
1272             return  found;
1273         }
1274         cc.log("invalid focus direction!!!");
1275         return 0;
1276     },
1277 
1278     /**
1279      * calculate the nearest distance between the baseWidget and the children of the layout
1280      * @param {ccui.Widget} baseWidget the base widget which will be used to calculate the distance between the layout's children and itself
1281      * @returns {Number} return the nearest distance between the baseWidget and the layout's children
1282      * @private
1283      */
1284     _calculateNearestDistance: function(baseWidget){
1285         var distance = cc.FLT_MAX;
1286         var widgetPosition =  this._getWorldCenterPoint(baseWidget);
1287         var locChildren = this._children;
1288 
1289         for (var i = 0, len = locChildren.length; i < len; i++) {
1290             var widget = locChildren[i], length;
1291             if (widget instanceof ccui.Layout)
1292                 length = widget._calculateNearestDistance(baseWidget);
1293             else {
1294                 if (widget instanceof ccui.Widget && widget.isFocusEnabled())
1295                     length = cc.pLength(cc.pSub(this._getWorldCenterPoint(widget), widgetPosition));
1296                 else
1297                     continue;
1298             }
1299             if (length < distance)
1300                 distance = length;
1301         }
1302         return distance;
1303     },
1304 
1305     /**
1306      * calculate the farthest distance between the baseWidget and the children of the layout
1307      * @param baseWidget
1308      * @returns {number}
1309      * @private
1310      */
1311     _calculateFarthestDistance:function(baseWidget){
1312         var distance = -cc.FLT_MAX;
1313         var widgetPosition =  this._getWorldCenterPoint(baseWidget);
1314         var locChildren = this._children;
1315 
1316         for (var i = 0, len = locChildren.length; i < len; i++) {
1317             var layout = locChildren[i];
1318             var length;
1319             if (layout instanceof ccui.Layout)
1320                 length = layout._calculateFarthestDistance(baseWidget);
1321             else {
1322                 if (layout instanceof ccui.Widget && layout.isFocusEnabled()) {
1323                     var wPosition = this._getWorldCenterPoint(layout);
1324                     length = cc.pLength(cc.pSub(wPosition, widgetPosition));
1325                 } else
1326                     continue;
1327             }
1328 
1329             if (length > distance)
1330                 distance = length;
1331         }
1332         return distance;
1333     },
1334 
1335     /**
1336      * when a layout pass the focus to it's child, use this method to determine which algorithm to use, nearest or farthest distance algorithm or not
1337      * @param direction
1338      * @param baseWidget
1339      * @private
1340      */
1341     _findProperSearchingFunctor: function(direction, baseWidget){
1342         if (baseWidget == null)
1343             return;
1344 
1345         var previousWidgetPosition = this._getWorldCenterPoint(baseWidget);
1346         var widgetPosition = this._getWorldCenterPoint(this._findFirstNonLayoutWidget());
1347         if (direction == ccui.Widget.LEFT) {
1348             this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findNearestChildWidgetIndex.bind(this)
1349                 : this._findFarthestChildWidgetIndex.bind(this);
1350         } else if (direction == ccui.Widget.RIGHT) {
1351             this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findFarthestChildWidgetIndex.bind(this)
1352                 : this._findNearestChildWidgetIndex.bind(this);
1353         }else if(direction == ccui.Widget.DOWN) {
1354             this.onPassFocusToChild = (previousWidgetPosition.y > widgetPosition.y) ? this._findNearestChildWidgetIndex.bind(this)
1355                 : this._findFarthestChildWidgetIndex.bind(this);
1356         }else if(direction == ccui.Widget.UP) {
1357             this.onPassFocusToChild = (previousWidgetPosition.y < widgetPosition.y) ? this._findNearestChildWidgetIndex.bind(this)
1358                 : this._findFarthestChildWidgetIndex.bind(this);
1359         }else
1360             cc.log("invalid direction!");
1361     },
1362 
1363     /**
1364      * find the first non-layout widget in this layout
1365      * @returns {ccui.Widget}
1366      * @private
1367      */
1368     _findFirstNonLayoutWidget:function(){
1369         var locChildren = this._children;
1370         for(var i = 0, len = locChildren.length; i < len; i++) {
1371             var child = locChildren[i];
1372             if (child instanceof ccui.Layout){
1373                 var widget = child._findFirstNonLayoutWidget();
1374                 if(widget)
1375                     return widget;
1376             } else{
1377                 if (child instanceof cc.Widget)
1378                     return child;
1379             }
1380         }
1381         return null;
1382     },
1383 
1384     /**
1385      * find the first focus enabled widget index in the layout, it will recursive searching the child widget
1386      * @returns {number}
1387      * @private
1388      */
1389     _findFirstFocusEnabledWidgetIndex: function(){
1390         var index = 0, locChildren = this.getChildren();
1391         var count = locChildren.length;
1392         while (index < count) {
1393             var w = locChildren[index];
1394             if (w && w instanceof ccui.Widget && w.isFocusEnabled())
1395                 return index;
1396             index++;
1397         }
1398         return 0;
1399     },
1400 
1401     /**
1402      * find a focus enabled child Widget in the layout by index
1403      * @param index
1404      * @returns {*}
1405      * @private
1406      */
1407     _findFocusEnabledChildWidgetByIndex: function(index){
1408         var widget = this._getChildWidgetByIndex(index);
1409         if (widget){
1410             if (widget.isFocusEnabled())
1411                 return widget;
1412             index = index + 1;
1413             return this._findFocusEnabledChildWidgetByIndex(index);
1414         }
1415         return null;
1416     },
1417 
1418     /**
1419      * get the center point of a widget in world space
1420      * @param {ccui.Widget} widget
1421      * @returns {cc.Point}
1422      * @private
1423      */
1424     _getWorldCenterPoint: function(widget){
1425         //FIXEDME: we don't need to calculate the content size of layout anymore
1426         var widgetSize = widget instanceof ccui.Layout ? widget._getLayoutAccumulatedSize() :  widget.getContentSize();
1427         return widget.convertToWorldSpace(cc.p(widgetSize.width /2, widgetSize.height /2));
1428     },
1429 
1430     /**
1431      * this method is called internally by nextFocusedWidget. When the dir is Right/Down, then this method will be called
1432      * @param {Number} direction
1433      * @param {ccui.Widget} current the current focused widget
1434      * @returns {ccui.Widget} the next focused widget
1435      * @private
1436      */
1437     _getNextFocusedWidget: function(direction, current){
1438         var nextWidget = null, locChildren = this._children;
1439         var  previousWidgetPos = locChildren.indexOf(current);
1440         previousWidgetPos = previousWidgetPos + 1;
1441         if (previousWidgetPos < locChildren.length) {
1442             nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1443             //handle widget
1444             if (nextWidget) {
1445                 if (nextWidget.isFocusEnabled()) {
1446                     if (nextWidget instanceof ccui.Layout) {
1447                         nextWidget._isFocusPassing = true;
1448                         return nextWidget.findNextFocusedWidget(direction, nextWidget);
1449                     } else {
1450                         this.dispatchFocusEvent(current, nextWidget);
1451                         return nextWidget;
1452                     }
1453                 } else
1454                     return this._getNextFocusedWidget(direction, nextWidget);
1455             } else
1456                 return current;
1457         } else {
1458             if (this._loopFocus) {
1459                 if (this._checkFocusEnabledChild()) {
1460                     previousWidgetPos = 0;
1461                     nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1462                     if (nextWidget.isFocusEnabled()) {
1463                         if (nextWidget instanceof ccui.Layout) {
1464                             nextWidget._isFocusPassing = true;
1465                             return nextWidget.findNextFocusedWidget(direction, nextWidget);
1466                         } else {
1467                             this.dispatchFocusEvent(current, nextWidget);
1468                             return nextWidget;
1469                         }
1470                     } else
1471                         return this._getNextFocusedWidget(direction, nextWidget);
1472                 } else {
1473                     if (current instanceof ccui.Layout)
1474                         return current;
1475                     else
1476                         return this._focusedWidget;
1477                 }
1478             } else{
1479                 if (this._isLastWidgetInContainer(current, direction)){
1480                     if (this._isWidgetAncestorSupportLoopFocus(this, direction))
1481                         return this.findNextFocusedWidget(direction, this);
1482                     if (current instanceof ccui.Layout)
1483                         return current;
1484                     else
1485                         return this._focusedWidget;
1486                 } else
1487                     return this.findNextFocusedWidget(direction, this);
1488             }
1489         }
1490     },
1491 
1492     /**
1493      * this method is called internally by nextFocusedWidget. When the dir is Left/Up, then this method will be called
1494      * @param direction
1495      * @param {ccui.Widget} current the current focused widget
1496      * @returns {ccui.Widget} the next focused widget
1497      * @private
1498      */
1499     _getPreviousFocusedWidget: function(direction, current){
1500         var nextWidget = null, locChildren = this._children;
1501         var previousWidgetPos = locChildren.indexOf(current);
1502         previousWidgetPos = previousWidgetPos - 1;
1503         if (previousWidgetPos >= 0){
1504             nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1505             if (nextWidget.isFocusEnabled()) {
1506                 if (nextWidget instanceof ccui.Layout){
1507                     nextWidget._isFocusPassing = true;
1508                     return nextWidget.findNextFocusedWidget(direction, nextWidget);
1509                 }
1510                 this.dispatchFocusEvent(current, nextWidget);
1511                 return nextWidget;
1512             } else
1513                 return this._getPreviousFocusedWidget(direction, nextWidget);       //handling the disabled widget, there is no actual focus lose or get, so we don't need any envet
1514         }else {
1515             if (this._loopFocus){
1516                 if (this._checkFocusEnabledChild()) {
1517                     previousWidgetPos = locChildren.length -1;
1518                     nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1519                     if (nextWidget.isFocusEnabled()){
1520                         if (nextWidget instanceof ccui.Layout){
1521                             nextWidget._isFocusPassing = true;
1522                             return nextWidget.findNextFocusedWidget(direction, nextWidget);
1523                         } else {
1524                             this.dispatchFocusEvent(current, nextWidget);
1525                             return nextWidget;
1526                         }
1527                     } else
1528                         return this._getPreviousFocusedWidget(direction, nextWidget);
1529                 } else
1530                     return (current instanceof ccui.Layout) ? current : this._focusedWidget;
1531             } else {
1532                 if (this._isLastWidgetInContainer(current, direction)) {
1533                     if (this._isWidgetAncestorSupportLoopFocus(this, direction))
1534                         return this.findNextFocusedWidget(direction, this);
1535                     return (current instanceof ccui.Layout) ? current : this._focusedWidget;
1536                 } else
1537                     return this.findNextFocusedWidget(direction, this);
1538             }
1539         }
1540     },
1541 
1542     /**
1543      * find the nth element in the _children array. Only the Widget descendant object will be returned
1544      * @param {Number} index
1545      * @returns {ccui.Widget}
1546      * @private
1547      */
1548     _getChildWidgetByIndex: function (index) {
1549         var locChildren = this._children;
1550         var size = locChildren.length, count = 0, oldIndex = index;
1551         while (index < size) {
1552             var firstChild = locChildren[index];
1553             if (firstChild && firstChild instanceof ccui.Widget)
1554                 return firstChild;
1555             count++;
1556             index++;
1557         }
1558 
1559         var begin = 0;
1560         while (begin < oldIndex) {
1561             var child = locChildren[begin];
1562             if (child && child instanceof ccui.Widget)
1563                 return child;
1564             count++;
1565             begin++;
1566         }
1567         return null;
1568     },
1569 
1570     /**
1571      * whether it is the last element according to all their parents
1572      * @param {ccui.Widget} widget
1573      * @param {Number} direction
1574      * @returns {Boolean}
1575      * @private
1576      */
1577     _isLastWidgetInContainer:function(widget, direction){
1578         var parent = widget.getParent();
1579         if (parent instanceof ccui.Layout)
1580             return true;
1581 
1582         var container = parent.getChildren();
1583         var index = container.indexOf(widget);
1584         if (parent.getLayoutType() == ccui.Layout.LINEAR_HORIZONTAL) {
1585             if (direction == ccui.Widget.LEFT) {
1586                 if (index == 0)
1587                     return this._isLastWidgetInContainer(parent, direction);
1588                 else
1589                     return false;
1590             }
1591             if (direction == ccui.Widget.RIGHT) {
1592                 if (index == container.length - 1)
1593                     return this._isLastWidgetInContainer(parent, direction);
1594                 else
1595                     return false;
1596             }
1597             if (direction == ccui.Widget.DOWN)
1598                 return this._isLastWidgetInContainer(parent, direction);
1599 
1600             if (direction == ccui.Widget.UP)
1601                 return this._isLastWidgetInContainer(parent, direction);
1602         } else if(parent.getLayoutType() == ccui.Layout.LINEAR_VERTICAL){
1603             if (direction == ccui.Widget.UP){
1604                 if (index == 0)
1605                     return this._isLastWidgetInContainer(parent, direction);
1606                 else
1607                     return false;
1608             }
1609             if (direction == ccui.Widget.DOWN) {
1610                 if (index == container.length - 1)
1611                     return this._isLastWidgetInContainer(parent, direction);
1612                 else
1613                     return false;
1614             }
1615             if (direction == ccui.Widget.LEFT)
1616                 return this._isLastWidgetInContainer(parent, direction);
1617 
1618             if (direction == ccui.Widget.RIGHT)
1619                 return this._isLastWidgetInContainer(parent, direction);
1620         } else {
1621             cc.log("invalid layout Type");
1622             return false;
1623         }
1624     },
1625 
1626     /**
1627      * Lookup any parent widget with a layout type as the direction, if the layout is loop focused, then return true, otherwise it returns false.
1628      * @param {ccui.Widget} widget
1629      * @param {Number} direction
1630      * @returns {Boolean}
1631      * @private
1632      */
1633     _isWidgetAncestorSupportLoopFocus: function(widget, direction){
1634         var parent = widget.getParent();
1635         if (parent == null)
1636             return false;
1637         if (parent.isLoopFocus()) {
1638             var layoutType = parent.getLayoutType();
1639             if (layoutType == ccui.Layout.LINEAR_HORIZONTAL) {
1640                 if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT)
1641                     return true;
1642                 else
1643                     return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1644             }
1645             if (layoutType == ccui.Layout.LINEAR_VERTICAL){
1646                 if (direction == ccui.Widget.DOWN || direction == ccui.Widget.UP)
1647                     return true;
1648                 else
1649                     return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1650             } else
1651                 cc.assert(0, "invalid layout type");
1652         } else
1653             return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1654     },
1655 
1656     /**
1657      * pass the focus to the layout's next focus enabled child
1658      * @param {Number} direction
1659      * @param {ccui.Widget} current
1660      * @returns {ccui.Widget}
1661      * @private
1662      */
1663     _passFocusToChild: function(direction, current){
1664         if (this._checkFocusEnabledChild()) {
1665             var previousWidget = ccui.Widget.getCurrentFocusedWidget();
1666             this._findProperSearchingFunctor(direction, previousWidget);
1667             var index = this.onPassFocusToChild(direction, previousWidget);
1668 
1669             var widget = this._getChildWidgetByIndex(index);
1670             if (widget instanceof ccui.Layout) {
1671                 widget._isFocusPassing = true;
1672                 return widget.findNextFocusedWidget(direction, widget);
1673             } else {
1674                 this.dispatchFocusEvent(current, widget);
1675                 return widget;
1676             }
1677         }else
1678             return this;
1679     },
1680 
1681     /**
1682      * If there are no focus enabled child in the layout, it will return false, otherwise it returns true
1683      * @returns {boolean}
1684      * @private
1685      */
1686     _checkFocusEnabledChild: function(){
1687         var locChildren = this._children;
1688         for(var i = 0, len = locChildren.length; i < len; i++){
1689             var widget = locChildren[i];
1690             if (widget && widget instanceof ccui.Widget && widget.isFocusEnabled())
1691                 return true;
1692         }
1693         return false;
1694     },
1695 
1696     /**
1697      * Returns the "class name" of widget.
1698      * @returns {string}
1699      */
1700     getDescription: function () {
1701         return "Layout";
1702     },
1703 
1704     _createCloneInstance: function () {
1705         return ccui.Layout.create();
1706     },
1707 
1708     _copyClonedWidgetChildren: function (model) {
1709         ccui.Widget.prototype._copyClonedWidgetChildren.call(this, model);
1710     },
1711 
1712     _copySpecialProperties: function (layout) {
1713         if(!(layout instanceof  ccui.Layout))
1714             return;
1715         this.setBackGroundImageScale9Enabled(layout._backGroundScale9Enabled);
1716         this.setBackGroundImage(layout._backGroundImageFileName, layout._bgImageTexType);
1717         this.setBackGroundImageCapInsets(layout._backGroundImageCapInsets);
1718         this.setBackGroundColorType(layout._colorType);
1719         this.setBackGroundColor(layout._color);
1720         this.setBackGroundColor(layout._startColor, layout._endColor);
1721         this.setBackGroundColorOpacity(layout._opacity);
1722         this.setBackGroundColorVector(layout._alongVector);
1723         this.setLayoutType(layout._layoutType);
1724         this.setClippingEnabled(layout._clippingEnabled);
1725         this.setClippingType(layout._clippingType);
1726         this._loopFocus = layout._loopFocus;
1727         this.__passFocusToChild = layout.__passFocusToChild;
1728     }
1729 });
1730 ccui.Layout._init_once = null;
1731 ccui.Layout._visit_once = null;
1732 ccui.Layout._layer = -1;
1733 ccui.Layout._sharedCache = null;
1734 
1735 if (cc._renderType == cc._RENDER_TYPE_WEBGL) {
1736     //WebGL
1737     ccui.Layout.prototype._stencilClippingVisit = ccui.Layout.prototype._stencilClippingVisitForWebGL;
1738     ccui.Layout.prototype._scissorClippingVisit = ccui.Layout.prototype._scissorClippingVisitForWebGL;
1739 } else {
1740     ccui.Layout.prototype._stencilClippingVisit = ccui.Layout.prototype._stencilClippingVisitForCanvas;
1741     ccui.Layout.prototype._scissorClippingVisit = ccui.Layout.prototype._stencilClippingVisitForCanvas;
1742 }
1743 ccui.Layout._getSharedCache = function () {
1744     return (cc.ClippingNode._sharedCache) || (cc.ClippingNode._sharedCache = cc.newElement("canvas"));
1745 };
1746 
1747 var _p = ccui.Layout.prototype;
1748 
1749 // Extended properties
1750 /** @expose */
1751 _p.clippingEnabled;
1752 cc.defineGetterSetter(_p, "clippingEnabled", _p.isClippingEnabled, _p.setClippingEnabled);
1753 /** @expose */
1754 _p.clippingType;
1755 cc.defineGetterSetter(_p, "clippingType", null, _p.setClippingType);
1756 /** @expose */
1757 _p.layoutType;
1758 cc.defineGetterSetter(_p, "layoutType", _p.getLayoutType, _p.setLayoutType);
1759 
1760 _p = null;
1761 
1762 /**
1763  * allocates and initializes a UILayout.
1764  * @deprecated since v3.0, please use new ccui.Layout() instead.
1765  * @return {ccui.Layout}
1766  * @example
1767  * // example
1768  * var uiLayout = ccui.Layout.create();
1769  */
1770 ccui.Layout.create = function () {
1771     return new ccui.Layout();
1772 };
1773 
1774 // Constants
1775 
1776 //layoutBackGround color type
1777 /**
1778  * The None of ccui.Layout's background color type
1779  * @constant
1780  * @type {number}
1781  */
1782 ccui.Layout.BG_COLOR_NONE = 0;
1783 /**
1784  * The solid of ccui.Layout's background color type, it will use a LayerColor to draw the background.
1785  * @constant
1786  * @type {number}
1787  */
1788 ccui.Layout.BG_COLOR_SOLID = 1;
1789 /**
1790  * The gradient of ccui.Layout's background color type, it will use a LayerGradient to draw the background.
1791  * @constant
1792  * @type {number}
1793  */
1794 ccui.Layout.BG_COLOR_GRADIENT = 2;
1795 
1796 //Layout type
1797 /**
1798  * The absolute of ccui.Layout's layout type.
1799  * @type {number}
1800  * @constant
1801  */
1802 ccui.Layout.ABSOLUTE = 0;
1803 /**
1804  * The vertical of ccui.Layout's layout type.
1805  * @type {number}
1806  * @constant
1807  */
1808 ccui.Layout.LINEAR_VERTICAL = 1;
1809 /**
1810  * The horizontal of ccui.Layout's layout type.
1811  * @type {number}
1812  * @constant
1813  */
1814 ccui.Layout.LINEAR_HORIZONTAL = 2;
1815 /**
1816  * The relative of ccui.Layout's layout type.
1817  * @type {number}
1818  * @constant
1819  */
1820 ccui.Layout.RELATIVE = 3;
1821 
1822 //Layout clipping type
1823 /**
1824  * The stencil of ccui.Layout's clipping type.
1825  * @type {number}
1826  * @constant
1827  */
1828 ccui.Layout.CLIPPING_STENCIL = 0;
1829 /**
1830  * The scissor of ccui.Layout's clipping type.
1831  * @type {number}
1832  * @constant
1833  */
1834 ccui.Layout.CLIPPING_SCISSOR = 1;
1835 
1836 /**
1837  * The zOrder value of ccui.Layout's image background.
1838  * @type {number}
1839  * @constant
1840  */
1841 ccui.Layout.BACKGROUND_IMAGE_ZORDER = -2;
1842 /**
1843  * The zOrder value of ccui.Layout's color background.
1844  * @type {number}
1845  * @constant
1846  */
1847 ccui.Layout.BACKGROUND_RENDERER_ZORDER = -2;