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