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,
 86     _passFocusToChild: false,
 87     _isFocusPassing:false,
 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.setSize(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){
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} [tag]
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
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}
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     sortAllChildren: function () {
336         ccui.Widget.prototype.sortAllChildren.call(this);
337         this._doLayout();
338     },
339 
340     stencilClippingVisit: null,
341 
342     _stencilClippingVisitForWebGL: function (ctx) {
343         var gl = ctx || cc._renderContext;
344 
345         // if stencil buffer disabled
346         /*if (cc.stencilBits < 1) {
347          // draw everything, as if there where no stencil
348          cc.Node.prototype.visit.call(this, ctx);
349          return;
350          }*/
351 
352         if (!this._clippingStencil || !this._clippingStencil.isVisible())
353             return;
354 
355         // store the current stencil layer (position in the stencil buffer),
356         // this will allow nesting up to n CCClippingNode,
357         // where n is the number of bits of the stencil buffer.
358         ccui.Layout._layer = -1;
359 
360         // all the _stencilBits are in use?
361         if (ccui.Layout._layer + 1 == cc.stencilBits) {
362             // warn once
363             ccui.Layout._visit_once = true;
364             if (ccui.Layout._visit_once) {
365                 cc.log("Nesting more than " + cc.stencilBits + "stencils is not supported. Everything will be drawn without stencil for this node and its childs.");
366                 ccui.Layout._visit_once = false;
367             }
368             // draw everything, as if there where no stencil
369             cc.Node.prototype.visit.call(this, ctx);
370             return;
371         }
372 
373         // increment the current layer
374         ccui.Layout._layer++;
375 
376         // mask of the current layer (ie: for layer 3: 00000100)
377         var mask_layer = 0x1 << ccui.Layout._layer;
378         // mask of all layers less than the current (ie: for layer 3: 00000011)
379         var mask_layer_l = mask_layer - 1;
380         // mask of all layers less than or equal to the current (ie: for layer 3: 00000111)
381         var mask_layer_le = mask_layer | mask_layer_l;
382 
383         // manually save the stencil state
384         var currentStencilEnabled = gl.isEnabled(gl.STENCIL_TEST);
385         var currentStencilWriteMask = gl.getParameter(gl.STENCIL_WRITEMASK);
386         var currentStencilFunc = gl.getParameter(gl.STENCIL_FUNC);
387         var currentStencilRef = gl.getParameter(gl.STENCIL_REF);
388         var currentStencilValueMask = gl.getParameter(gl.STENCIL_VALUE_MASK);
389         var currentStencilFail = gl.getParameter(gl.STENCIL_FAIL);
390         var currentStencilPassDepthFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL);
391         var currentStencilPassDepthPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS);
392 
393         // enable stencil use
394         gl.enable(gl.STENCIL_TEST);
395         // check for OpenGL error while enabling stencil test
396         //cc.checkGLErrorDebug();
397 
398         // all bits on the stencil buffer are readonly, except the current layer bit,
399         // this means that operation like glClear or glStencilOp will be masked with this value
400         gl.stencilMask(mask_layer);
401 
402         // manually save the depth test state
403         //GLboolean currentDepthTestEnabled = GL_TRUE;
404         //currentDepthTestEnabled = glIsEnabled(GL_DEPTH_TEST);
405         var currentDepthWriteMask = gl.getParameter(gl.DEPTH_WRITEMASK);
406 
407         // disable depth test while drawing the stencil
408         //glDisable(GL_DEPTH_TEST);
409         // disable update to the depth buffer while drawing the stencil,
410         // as the stencil is not meant to be rendered in the real scene,
411         // it should never prevent something else to be drawn,
412         // only disabling depth buffer update should do
413         gl.depthMask(false);
414 
415         // manually clear the stencil buffer by drawing a fullscreen rectangle on it
416         // setup the stencil test func like this:
417         // for each pixel in the fullscreen rectangle
418         //     never draw it into the frame buffer
419         //     if not in inverted mode: set the current layer value to 0 in the stencil buffer
420         //     if in inverted mode: set the current layer value to 1 in the stencil buffer
421         gl.stencilFunc(gl.NEVER, mask_layer, mask_layer);
422         gl.stencilOp(gl.ZERO, gl.KEEP, gl.KEEP);
423 
424         // draw a fullscreen solid rectangle to clear the stencil buffer
425         //ccDrawSolidRect(CCPointZero, ccpFromSize([[CCDirector sharedDirector] winSize]), ccc4f(1, 1, 1, 1));
426         cc._drawingUtil.drawSolidRect(cc.p(0, 0), cc.pFromSize(cc.director.getWinSize()), cc.color(255, 255, 255, 255));
427 
428         // setup the stencil test func like this:
429         // for each pixel in the stencil node
430         //     never draw it into the frame buffer
431         //     if not in inverted mode: set the current layer value to 1 in the stencil buffer
432         //     if in inverted mode: set the current layer value to 0 in the stencil buffer
433         gl.stencilFunc(gl.NEVER, mask_layer, mask_layer);
434         gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP);
435 
436         cc.kmGLPushMatrix();
437         this.transform();
438 
439         this._clippingStencil.visit();
440 
441         // restore the depth test state
442         gl.depthMask(currentDepthWriteMask);
443 
444         gl.stencilFunc(gl.EQUAL, mask_layer_le, mask_layer_le);
445         gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
446 
447         // draw (according to the stencil test func) this node and its childs
448         var i = 0;      // used by _children
449         var j = 0;      // used by _protectedChildren
450 
451         this.sortAllChildren();
452         this.sortAllProtectedChildren();
453         var locChildren = this._children, locProtectChildren = this._protectedChildren;
454         var iLen = locChildren.length, jLen = locProtectChildren.length, child;
455         for( ; i < iLen; i++ ){
456             child = locChildren[i];
457             if ( child && child.getLocalZOrder() < 0 )
458                 child.visit();
459             else
460                 break;
461         }
462         for( ; j < jLen; j++ ) {
463             child = locProtectChildren[j];
464             if ( child && child.getLocalZOrder() < 0 )
465                 child.visit();
466             else
467                 break;
468         }
469         this.draw();
470         for (; i < iLen; i++)
471             locChildren[i].visit();
472         for (; j < jLen; j++)
473             locProtectChildren[j].visit();
474 
475         // manually restore the stencil state
476         gl.stencilFunc(currentStencilFunc, currentStencilRef, currentStencilValueMask);
477         gl.stencilOp(currentStencilFail, currentStencilPassDepthFail, currentStencilPassDepthPass);
478         gl.stencilMask(currentStencilWriteMask);
479         if (!currentStencilEnabled)
480             gl.disable(gl.STENCIL_TEST);
481 
482         // we are done using this layer, decrement
483         ccui.Layout._layer--;
484 
485         cc.kmGLPopMatrix();
486     },
487 
488     _stencilClippingVisitForCanvas: function (ctx) {
489         // return fast (draw nothing, or draw everything if in inverted mode) if:
490         // - nil stencil node
491         // - or stencil node invisible:
492         if (!this._clippingStencil || !this._clippingStencil.isVisible()) {
493             return;
494         }
495         var context = ctx || cc._renderContext;
496         // Composition mode, costy but support texture stencil
497         if (this._cangodhelpme() || this._clippingStencil instanceof cc.Sprite) {
498             // Cache the current canvas, for later use (This is a little bit heavy, replace this solution with other walkthrough)
499             var canvas = context.canvas;
500             var locCache = ccui.Layout._getSharedCache();
501             locCache.width = canvas.width;
502             locCache.height = canvas.height;
503             var locCacheCtx = locCache.getContext("2d");
504             locCacheCtx.drawImage(canvas, 0, 0);
505 
506             context.save();
507             // Draw everything first using node visit function
508             cc.Node.prototype.visit.call(this, context);
509 
510             context.globalCompositeOperation = "destination-in";
511 
512             this.transform(context);
513             this._clippingStencil.visit();
514 
515             context.restore();
516 
517             // Redraw the cached canvas, so that the cliped area shows the background etc.
518             context.save();
519             context.setTransform(1, 0, 0, 1, 0, 0);
520             context.globalCompositeOperation = "destination-over";
521             context.drawImage(locCache, 0, 0);
522             context.restore();
523         } else {    // Clip mode, fast, but only support cc.DrawNode
524             var i, children = this._children, locChild;
525 
526             context.save();
527             this.transform(context);
528             this._clippingStencil.visit(context);
529             context.clip();
530 
531             // Clip mode doesn't support recusive stencil, so once we used a clip stencil,
532             // so if it has ClippingNode as a child, the child must uses composition stencil.
533             this._cangodhelpme(true);
534 
535             this.sortAllChildren();
536             this.sortAllProtectedChildren();
537 
538             var j, locProtectChildren = this._protectedChildren;
539             var iLen = children.length, jLen = locProtectChildren.length;
540 
541             // draw children zOrder < 0
542             for (i = 0; i < iLen; i++) {
543                 locChild = children[i];
544                 if (locChild && locChild._localZOrder < 0)
545                     locChild.visit(context);
546                 else
547                     break;
548             }
549             for (j = 0; j < jLen; j++) {
550                 locChild = locProtectChildren[j];
551                 if (locChild && locChild._localZOrder < 0)
552                     locChild.visit(context);
553                 else
554                     break;
555             }
556             //this.draw(context);
557             for (; i < iLen; i++)
558                 children[i].visit(context);
559             for (; j < jLen; j++)
560                 locProtectChildren[j].visit(context);
561 
562             this._cangodhelpme(false);
563             context.restore();
564         }
565     },
566 
567     _godhelpme: false,
568     _cangodhelpme: function (godhelpme) {
569         if (godhelpme === true || godhelpme === false)
570             cc.ClippingNode.prototype._godhelpme = godhelpme;
571         return cc.ClippingNode.prototype._godhelpme;
572     },
573 
574     scissorClippingVisit: null,
575     _scissorClippingVisitForWebGL: function (ctx) {
576         var clippingRect = this.getClippingRect();
577         var gl = ctx || cc._renderContext;
578         if (this._handleScissor) {
579             gl.enable(gl.SCISSOR_TEST);
580         }
581         cc.view.setScissorInPoints(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height);
582         cc.Node.prototype.visit.call(this);
583         if (this._handleScissor) {
584             gl.disable(gl.SCISSOR_TEST);
585         }
586     },
587 
588     /**
589      * Changes if layout can clip it's content and locChild.
590      * @param {Boolean} able
591      */
592     setClippingEnabled: function (able) {
593         if (able == this._clippingEnabled)
594             return;
595         this._clippingEnabled = able;
596         switch (this._clippingType) {
597             case ccui.Layout.CLIPPING_STENCIL:
598                 if (able){
599                     this._clippingStencil = cc.DrawNode.create();
600                     if(cc._renderType === cc._RENDER_TYPE_CANVAS)
601                         this._clippingStencil.draw = this.__stencilDraw.bind(this);
602                     if (this._running)
603                         this._clippingStencil.onEnter();
604                     this.setStencilClippingSize(this._contentSize);
605                 } else {
606                     if (this._running)
607                         this._clippingStencil.onExit();
608                     this._clippingStencil = null;
609                 }
610                 break;
611             default:
612                 break;
613         }
614     },
615 
616     /**
617      * Set clipping type
618      * @param {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} type
619      */
620     setClippingType: function (type) {
621         if (type == this._clippingType) {
622             return;
623         }
624         var clippingEnabled = this.isClippingEnabled();
625         this.setClippingEnabled(false);
626         this._clippingType = type;
627         this.setClippingEnabled(clippingEnabled);
628     },
629 
630     /**
631      * Get clipping type
632      * @returns {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR}
633      */
634     getClippingType: function () {
635         return this._clippingType;
636     },
637 
638     setStencilClippingSize: function (size) {
639         if (this._clippingEnabled && this._clippingType == ccui.Layout.CLIPPING_STENCIL) {
640             var rect = [];
641             rect[0] = cc.p(0, 0);
642             rect[1] = cc.p(size.width, 0);
643             rect[2] = cc.p(size.width, size.height);
644             rect[3] = cc.p(0, size.height);
645             var green = cc.color.GREEN;
646             this._clippingStencil.clear();
647             this._clippingStencil.drawPoly(rect, 4, green, 0, green);
648         }
649     },
650 
651     rendererVisitCallBack: function () {
652         this._doLayout();
653     },
654 
655     getClippingRect: function () {
656         if (this._clippingRectDirty) {
657             var worldPos = this.convertToWorldSpace(cc.p(0, 0));
658             var t = this.nodeToWorldTransform();
659             var scissorWidth = this._contentSize.width * t.a;
660             var scissorHeight = this._contentSize.height * t.d;
661             var parentClippingRect;
662             var parent = this;
663             var firstClippingParentFounded = false;
664             while (parent) {
665                 parent = parent.getParent();
666                 if (parent && parent instanceof ccui.Layout) {
667                     if (parent.isClippingEnabled()) {
668                         if (!firstClippingParentFounded) {
669                             this._clippingParent = parent;
670                             firstClippingParentFounded = true;
671                         }
672                         if (parent._clippingType == ccui.Layout.CLIPPING_SCISSOR) {
673                             this._handleScissor = false;
674                             break;
675                         }
676                     }
677                 }
678             }
679 
680             if (this._clippingParent) {
681                 parentClippingRect = this._clippingParent.getClippingRect();
682                 var finalX = worldPos.x - (scissorWidth * this._anchorPoint.x);
683                 var finalY = worldPos.y - (scissorHeight * this._anchorPoint.y);
684                 var finalWidth = scissorWidth;
685                 var finalHeight = scissorHeight;
686 
687                 var leftOffset = worldPos.x - parentClippingRect.x;
688                 if (leftOffset < 0) {
689                     finalX = parentClippingRect.x;
690                     finalWidth += leftOffset;
691                 }
692                 var rightOffset = (worldPos.x + scissorWidth) - (parentClippingRect.x + parentClippingRect.width);
693                 if (rightOffset > 0) {
694                     finalWidth -= rightOffset;
695                 }
696                 var topOffset = (worldPos.y + scissorHeight) - (parentClippingRect.y + parentClippingRect.height);
697                 if (topOffset > 0) {
698                     finalHeight -= topOffset;
699                 }
700                 var bottomOffset = worldPos.y - parentClippingRect.y;
701                 if (bottomOffset < 0) {
702                     finalY = parentClippingRect.x;
703                     finalHeight += bottomOffset;
704                 }
705                 if (finalWidth < 0) {
706                     finalWidth = 0;
707                 }
708                 if (finalHeight < 0) {
709                     finalHeight = 0;
710                 }
711                 this._clippingRect.x = finalX;
712                 this._clippingRect.y = finalY;
713                 this._clippingRect.width = finalWidth;
714                 this._clippingRect.height = finalHeight;
715             }
716             else {
717                 this._clippingRect.x = worldPos.x - (scissorWidth * this._anchorPoint.x);
718                 this._clippingRect.y = worldPos.y - (scissorHeight * this._anchorPoint.y);
719                 this._clippingRect.width = scissorWidth;
720                 this._clippingRect.height = scissorHeight;
721             }
722             this._clippingRectDirty = false;
723         }
724         return this._clippingRect;
725     },
726 
727     onSizeChanged: function () {
728         ccui.Widget.prototype.onSizeChanged.call(this);
729         this.setStencilClippingSize(this._contentSize);
730         this._doLayoutDirty = true;
731         this._clippingRectDirty = true;
732         if (this._backGroundImage) {
733             this._backGroundImage.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5);
734             if (this._backGroundScale9Enabled) {
735                 if (this._backGroundImage instanceof cc.Scale9Sprite) {
736                     this._backGroundImage.setPreferredSize(this._contentSize);
737                 }
738             }
739         }
740         if (this._colorRender) {
741             this._colorRender.setContentSize(this._contentSize);
742         }
743         if (this._gradientRender) {
744             this._gradientRender.setContentSize(this._contentSize);
745         }
746     },
747 
748     /**
749      * Sets background image use scale9 renderer.
750      * @param {Boolean} able
751      */
752     setBackGroundImageScale9Enabled: function (able) {
753         if (this._backGroundScale9Enabled == able) {
754             return;
755         }
756         this.removeProtectedChild(this._backGroundImage);
757         //cc.Node.prototype.removeChild.call(this, this._backGroundImage, true);
758         this._backGroundImage = null;
759         this._backGroundScale9Enabled = able;
760        /* if (this._backGroundScale9Enabled) {
761             this._backGroundImage = cc.Scale9Sprite.create();
762         }
763         else {
764             this._backGroundImage = cc.Sprite.create();
765         }
766         cc.Node.prototype.addChild.call(this, this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1);*/
767         this.addBackGroundImage();
768         this.setBackGroundImage(this._backGroundImageFileName, this._bgImageTexType);
769         this.setBackGroundImageCapInsets(this._backGroundImageCapInsets);
770     },
771 
772     /**
773      * Get background image is use scale9 renderer.
774      * @returns {Boolean}
775      */
776     isBackGroundImageScale9Enabled: function () {
777         return this._backGroundScale9Enabled;
778     },
779 
780     /**
781      * Sets a background image for layout
782      * @param {String} fileName
783      * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType
784      */
785     setBackGroundImage: function (fileName, texType) {
786         if (!fileName)
787             return;
788         texType = texType || ccui.Widget.LOCAL_TEXTURE;
789         if (this._backGroundImage == null)
790             this.addBackGroundImage();
791         this._backGroundImageFileName = fileName;
792         this._bgImageTexType = texType;
793         if (this._backGroundScale9Enabled) {
794             var bgiScale9 = this._backGroundImage;
795             switch (this._bgImageTexType) {
796                 case ccui.Widget.LOCAL_TEXTURE:
797                     bgiScale9.initWithFile(fileName);
798                     break;
799                 case ccui.Widget.PLIST_TEXTURE:
800                     bgiScale9.initWithSpriteFrameName(fileName);
801                     break;
802                 default:
803                     break;
804             }
805             bgiScale9.setPreferredSize(this._contentSize);
806         } else {
807             var sprite = this._backGroundImage;
808             switch (this._bgImageTexType){
809                 case ccui.Widget.LOCAL_TEXTURE:
810                     sprite.setTexture(fileName);
811                     break;
812                 case ccui.Widget.PLIST_TEXTURE:
813                     sprite.setSpriteFrame(fileName);
814                     break;
815                 default:
816                     break;
817             }
818         }
819         this._backGroundImageTextureSize = this._backGroundImage.getContentSize();
820         this._backGroundImage.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0);
821         this._updateBackGroundImageColor();
822     },
823 
824     /**
825      * Sets a background image CapInsets for layout, if the background image is a scale9 render.
826      * @param {cc.Rect} capInsets
827      */
828     setBackGroundImageCapInsets: function (capInsets) {
829         this._backGroundImageCapInsets = capInsets;
830         if (this._backGroundScale9Enabled)
831             this._backGroundImage.setCapInsets(capInsets);
832     },
833 
834     /**
835      * Gets background image cap insets.
836      * @returns {cc.Rect}
837      */
838     getBackGroundImageCapInsets: function () {
839         return this._backGroundImageCapInsets;
840     },
841 
842     supplyTheLayoutParameterLackToChild: function (locChild) {
843         if (!locChild) {
844             return;
845         }
846         switch (this._layoutType) {
847             case ccui.Layout.ABSOLUTE:
848                 break;
849             case ccui.Layout.LINEAR_HORIZONTAL:
850             case ccui.Layout.LINEAR_VERTICAL:
851                 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.LINEAR);
852                 if (!layoutParameter)
853                     locChild.setLayoutParameter(ccui.LinearLayoutParameter.create());
854                 break;
855             case ccui.Layout.RELATIVE:
856                 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.RELATIVE);
857                 if (!layoutParameter)
858                     locChild.setLayoutParameter(ccui.RelativeLayoutParameter.create());
859                 break;
860             default:
861                 break;
862         }
863     },
864 
865     /**
866      * init background image renderer.
867      */
868     addBackGroundImage: function () {
869         if (this._backGroundScale9Enabled) {
870             this._backGroundImage = cc.Scale9Sprite.create();
871             this._backGroundImage.setPreferredSize(this._contentSize);
872         } else {
873             this._backGroundImage = cc.Sprite.create();
874         }
875         this.addProtectedChild(this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1);
876         this._backGroundImage.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0);
877     },
878 
879     /**
880      * Remove the background image of layout.
881      */
882     removeBackGroundImage: function () {
883         if (!this._backGroundImage)
884             return;
885         this.removeProtectedChild(this._backGroundImage);
886         this._backGroundImage = null;
887         this._backGroundImageFileName = "";
888         this._backGroundImageTextureSize = cc.size(0, 0);
889     },
890 
891     /**
892      * Sets Color Type for layout.
893      * @param {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} type
894      */
895     setBackGroundColorType: function (type) {
896         if (this._colorType == type)
897             return;
898         switch (this._colorType) {
899             case ccui.Layout.BG_COLOR_NONE:
900                 if (this._colorRender) {
901                     this.removeProtectedChild(this._colorRender);
902                     this._colorRender = null;
903                 }
904                 if (this._gradientRender) {
905                     this.removeProtectedChild(this._gradientRender);
906                     this._gradientRender = null;
907                 }
908                 break;
909             case ccui.Layout.BG_COLOR_SOLID:
910                 if (this._colorRender) {
911                     this.removeProtectedChild(this._colorRender);
912                     this._colorRender = null;
913                 }
914                 break;
915             case ccui.Layout.BG_COLOR_GRADIENT:
916                 if (this._gradientRender) {
917                     this.removeProtectedChild(this._gradientRender);
918                     this._gradientRender = null;
919                 }
920                 break;
921             default:
922                 break;
923         }
924         this._colorType = type;
925         switch (this._colorType) {
926             case ccui.Layout.BG_COLOR_NONE:
927                 break;
928             case ccui.Layout.BG_COLOR_SOLID:
929                 this._colorRender = cc.LayerColor.create();
930                 this._colorRender.setContentSize(this._contentSize);
931                 this._colorRender.setOpacity(this._opacity);
932                 this._colorRender.setColor(this._color);
933                 this.addProtectedChild(this._colorRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1);
934                 break;
935             case ccui.Layout.BG_COLOR_GRADIENT:
936                 this._gradientRender = cc.LayerGradient.create(cc.color(255, 0, 0, 255), cc.color(0, 255, 0, 255));
937                 this._gradientRender.setContentSize(this._contentSize);
938                 this._gradientRender.setOpacity(this._opacity);
939                 this._gradientRender.setStartColor(this._startColor);
940                 this._gradientRender.setEndColor(this._endColor);
941                 this._gradientRender.setVector(this._alongVector);
942                 this.addProtectedChild(this._gradientRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1);
943                 break;
944             default:
945                 break;
946         }
947     },
948 
949     /**
950      * Get color type.
951      * @returns {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT}
952      */
953     getBackGroundColorType: function () {
954         return this._colorType;
955     },
956 
957     /**
958      * Sets background color for layout, if color type is Layout.COLOR_SOLID
959      * @param {cc.Color} color
960      * @param {cc.Color} endColor
961      */
962     setBackGroundColor: function (color, endColor) {
963         if (!endColor) {
964             this._color.r = color.r;
965             this._color.g = color.g;
966             this._color.b = color.b;
967             if (this._colorRender) {
968                 this._colorRender.setColor(color);
969             }
970         } else {
971             this._startColor.r = color.r;
972             this._startColor.g = color.g;
973             this._startColor.b = color.b;
974 
975             if (this._gradientRender) {
976                 this._gradientRender.setStartColor(color);
977             }
978             this._endColor = endColor;
979             if (this._gradientRender) {
980                 this._gradientRender.setEndColor(endColor);
981             }
982         }
983     },
984 
985     /**
986      * Get back ground color
987      * @returns {cc.Color}
988      */
989     getBackGroundColor: function () {
990         var tmpColor = this._color;
991         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
992     },
993 
994     /**
995      * Get back ground start color
996      * @returns {cc.Color}
997      */
998     getBackGroundStartColor: function () {
999         var tmpColor = this._startColor;
1000         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
1001     },
1002 
1003     /**
1004      * Get back ground end color
1005      * @returns {cc.Color}
1006      */
1007     getBackGroundEndColor: function () {
1008         var tmpColor = this._endColor;
1009         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
1010     },
1011 
1012     /**
1013      * Sets background opacity layout.
1014      * @param {number} opacity
1015      */
1016     setBackGroundColorOpacity: function (opacity) {
1017         this._opacity = opacity;
1018         switch (this._colorType) {
1019             case ccui.Layout.BG_COLOR_NONE:
1020                 break;
1021             case ccui.Layout.BG_COLOR_SOLID:
1022                 this._colorRender.setOpacity(opacity);
1023                 break;
1024             case ccui.Layout.BG_COLOR_GRADIENT:
1025                 this._gradientRender.setOpacity(opacity);
1026                 break;
1027             default:
1028                 break;
1029         }
1030     },
1031 
1032     /**
1033      * Get background opacity value.
1034      * @returns {Number}
1035      */
1036     getBackGroundColorOpacity: function () {
1037         return this._opacity;
1038     },
1039 
1040     /**
1041      * Sets background color vector for layout, if color type is Layout.COLOR_GRADIENT
1042      * @param {cc.Point} vector
1043      */
1044     setBackGroundColorVector: function (vector) {
1045         this._alongVector.x = vector.x;
1046         this._alongVector.y = vector.y;
1047         if (this._gradientRender) {
1048             this._gradientRender.setVector(vector);
1049         }
1050     },
1051 
1052     /**
1053      *  Get background color value.
1054      * @returns {cc.Point}
1055      */
1056     getBackGroundColorVector: function () {
1057         return this._alongVector;
1058     },
1059 
1060     /**
1061      * Set backGround image color
1062      * @param {cc.Color} color
1063      */
1064     setBackGroundImageColor: function (color) {
1065         this._backGroundImageColor.r = color.r;
1066         this._backGroundImageColor.g = color.g;
1067         this._backGroundImageColor.b = color.b;
1068 
1069         this._updateBackGroundImageColor();
1070     },
1071 
1072     /**
1073      * Get backGround image color
1074      * @param {Number} opacity
1075      */
1076     setBackGroundImageOpacity: function (opacity) {
1077         this._backGroundImageColor.a = opacity;
1078         this.getBackGroundImageColor();
1079     },
1080 
1081     /**
1082      * Get backGround image color
1083      * @returns {cc.Color}
1084      */
1085     getBackGroundImageColor: function () {
1086         var color = this._backGroundImageColor;
1087         return cc.color(color.r, color.g, color.b, color.a);
1088     },
1089 
1090     /**
1091      * Get backGround image opacity
1092      * @returns {Number}
1093      */
1094     getBackGroundImageOpacity: function () {
1095         return this._backGroundImageColor.a;
1096     },
1097 
1098     _updateBackGroundImageColor: function () {
1099         if(this._backGroundImage)
1100             this._backGroundImage.setColor(this._backGroundImageColor);
1101     },
1102 
1103     /**
1104      * Gets background image texture size.
1105      * @returns {cc.Size}
1106      */
1107     getBackGroundImageTextureSize: function () {
1108         return this._backGroundImageTextureSize;
1109     },
1110 
1111     /**
1112      * Sets LayoutType.
1113      * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type
1114      */
1115     setLayoutType: function (type) {
1116         this._layoutType = type;
1117         var layoutChildrenArray = this._children;
1118         var locChild = null;
1119         for (var i = 0; i < layoutChildrenArray.length; i++) {
1120             locChild = layoutChildrenArray[i];
1121             if(locChild instanceof ccui.Widget)
1122                 this.supplyTheLayoutParameterLackToChild(locChild);
1123         }
1124         this._doLayoutDirty = true;
1125     },
1126 
1127     /**
1128      * Gets LayoutType.
1129      * @returns {null}
1130      */
1131     getLayoutType: function () {
1132         return this._layoutType;
1133     },
1134 
1135     /**
1136      * request do layout
1137      */
1138     requestDoLayout: function () {
1139         this._doLayoutDirty = true;
1140     },
1141 
1142     _doLayout: function () {
1143         if (!this._doLayoutDirty)
1144             return;
1145 
1146         var executant = this._createLayoutManager();     //TODO create a layout manager every calling _doLayout?
1147         if (executant)
1148             executant._doLayout(this);
1149         this._doLayoutDirty = false;
1150     },
1151 
1152     _createLayoutManager: function(){
1153         var layoutMgr = null;
1154         switch (this._layoutType) {
1155             case ccui.Layout.LINEAR_VERTICAL:
1156                 layoutMgr = ccui.LinearVerticalLayoutManager.create();
1157                 break;
1158             case ccui.Layout.LINEAR_HORIZONTAL:
1159                 layoutMgr = ccui.LinearHorizontalLayoutManager.create();
1160                 break;
1161             case ccui.Layout.RELATIVE:
1162                 layoutMgr = ccui.RelativeLayoutManager.create();
1163                 break;
1164         }
1165         return layoutMgr;
1166     },
1167 
1168     _getLayoutContentSize: function(){
1169         return this.getContentSize();
1170     },
1171 
1172     _getLayoutElements: function(){
1173         return this.getChildren();
1174     },
1175 
1176     //clipping
1177     _onBeforeVisitStencil: function(){
1178         //TODO NEW RENDERER
1179     },
1180 
1181     _drawFullScreenQuadClearStencil:function(){
1182         //TODO NEW RENDERER
1183     },
1184 
1185     _onAfterDrawStencil: function(){
1186         //TODO NEW RENDERER
1187     },
1188 
1189     _onAfterVisitStencil: function(){
1190         //TODO NEW RENDERER
1191     },
1192 
1193     _onAfterVisitScissor: function(){
1194         //TODO NEW RENDERER
1195     },
1196 
1197     _onAfterVisitScissor: function(){
1198         //TODO NEW RENDERER
1199     },
1200 
1201     _updateBackGroundImageOpacity: function(){
1202         if (this._backGroundImage)
1203             this._backGroundImage.setOpacity(this._backGroundImageOpacity);
1204     },
1205 
1206     _updateBackGroundImageRGBA: function(){
1207         if (this._backGroundImage) {
1208             this._backGroundImage.setColor(this._backGroundImageColor);
1209             this._backGroundImage.setOpacity(this._backGroundImageOpacity);
1210         }
1211     },
1212 
1213     _getLayoutAccumulatedSize: function(){
1214         var children = this.getChildren();
1215         var  layoutSize = cc.size(0, 0);
1216         var widgetCount = 0, locSize;
1217         for(var i = 0, len = children.length; i < len; i++) {
1218             var layout = children[i];
1219             if (null != layout && layout instanceof ccui.Layout){
1220                 locSize = layout._getLayoutAccumulatedSize();
1221                 layoutSize.width += locSize.width;
1222                 layoutSize.height += locSize.height;
1223                 // C++ layoutSize = layoutSize + layout.getLayoutAccumulatedSize();
1224             } else {
1225                 if (layout instanceof ccui.Widget) {
1226                     widgetCount++;
1227                     var m = w.getLayoutParameter().getMargin();
1228                     locSize = w.getContentSize();
1229                     // c++ layoutSize = layoutSize + w.getContentSize() + cc.size(m.right + m.left,  m.top + m.bottom) * 0.5;
1230                     layoutSize.width += locSize.width +  (m.right + m.left) * 0.5;
1231                     layoutSize.height += locSize.height +  (m.top + m.bottom) * 0.5;
1232                 }
1233             }
1234         }
1235 
1236         //substract extra size
1237         var type = this.getLayoutType();
1238         if (type == ccui.Layout.LINEAR_HORIZONTAL)
1239             layoutSize.height = layoutSize.height - layoutSize.height/widgetCount * (widgetCount-1);
1240 
1241         if (type == ccui.Layout.LINEAR_VERTICAL)
1242             layoutSize.width = layoutSize.width - layoutSize.width/widgetCount * (widgetCount-1);
1243         return layoutSize;
1244     },
1245 
1246     _findNearestChildWidgetIndex: function(direction, baseWidget){
1247         if (baseWidget == null || baseWidget == this)
1248             return this._findFirstFocusEnabledWidgetIndex();
1249 
1250         var index = 0, locChildren = this.getChildren();
1251         var count = locChildren.length;
1252         var widgetPosition;
1253 
1254         var distance = cc.FLT_MAX, found = 0;
1255         if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT || direction == ccui.Widget.DOWN || direction == ccui.Widget.UP) {
1256             widgetPosition = this._getWorldCenterPoint(baseWidget);
1257             while (index < count) {
1258                 var w = locChildren[index];
1259                 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) {
1260                     var length = (w instanceof ccui.Layout)? w._calculateNearestDistance(baseWidget)
1261                         : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition));
1262 
1263                     if (length < distance){
1264                         found = index;
1265                         distance = length;
1266                     }
1267                 }
1268                 index++;
1269             }
1270             return found;
1271         }
1272         cc.assert(0, "invalid focus direction!");
1273         return 0;
1274     },
1275 
1276     _findFarestChildWidgetIndex: function(direction, baseWidget){
1277         if (baseWidget == null || baseWidget == this)
1278             return this._findFirstFocusEnabledWidgetIndex();
1279 
1280         var index = 0;
1281         var count = this.getChildren().size();
1282 
1283         var distance = -cc.FLT_MAX;
1284         var found = 0;
1285         if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT || direction == ccui.Widget.DOWN || direction == ccui.Widget.UP) {
1286             var widgetPosition =  this._getWorldCenterPoint(baseWidget);
1287             while (index <  count) {
1288                 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) {
1289                     var length = (w instanceof ccui.Layout)?w._calculateFarestDistance(baseWidget)
1290                         : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition));
1291 
1292                     if (length > distance){
1293                         found = index;
1294                         distance = length;
1295                     }
1296                 }
1297                 index++;
1298             }
1299             return  found;
1300         }
1301         cc.assert(0, "invalid focus direction!!!");
1302         return 0;
1303     },
1304 
1305     _calculateNearestDistance: function(baseWidget){
1306         var distance = cc.FLT_MAX;
1307         var widgetPosition =  this._getWorldCenterPoint(baseWidget);
1308         var locChildren = this._children;
1309 
1310         for (var i = 0, len = locChildren.length; i < len; i++) {
1311             var widget = locChildren[i];
1312             var length;
1313             if (widget instanceof ccui.Layout)
1314                 length = widget._calculateNearestDistance(baseWidget);
1315             else {
1316                 if (widget instanceof ccui.Widget && widget.isFocusEnabled())
1317                     length = cc.pLength(cc.pSub(this._getWorldCenterPoint(widget), widgetPosition));
1318                 else
1319                     continue;
1320             }
1321 
1322             if (length < distance)
1323                 distance = length;
1324         }
1325         return distance;
1326     },
1327 
1328     _calculateFarestDistance:function(baseWidget){
1329         var distance = -cc.FLT_MAX;
1330         var widgetPosition =  this._getWorldCenterPoint(baseWidget);
1331         var locChildren = this._children;
1332 
1333         for (var i = 0, len = locChildren.length; i < len; i++) {
1334             var layout = locChildren[i];
1335             var length;
1336             if (layout instanceof ccui.Layout)
1337                 length = layout._calculateFarestDistance(baseWidget);
1338             else {
1339                 if (layout instanceof ccui.Widget && layout.isFocusEnabled()) {
1340                     var wPosition = this._getWorldCenterPoint(w);
1341                     length = cc.pLength(cc.pSub(wPosition, widgetPosition));
1342                 } else
1343                     continue;
1344             }
1345 
1346             if (length > distance)
1347                 distance = length;
1348         }
1349         return distance;
1350     },
1351 
1352     _findProperSearchingFunctor: function(direction, baseWidget){
1353         if (baseWidget == null)
1354             return;
1355 
1356         var previousWidgetPosition = this._getWorldCenterPoint(baseWidget);
1357         var widgetPosition = this._getWorldCenterPoint(this._findFirstNonLayoutWidget());
1358         if (direction == ccui.Widget.LEFT) {
1359             if (previousWidgetPosition.x > widgetPosition.x)
1360                 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this);
1361             else
1362                 this.onPassFocusToChild = this._findFarestChildWidgetIndex.bind(this);
1363         }else if(direction == ccui.Widget.RIGHT){
1364             if (previousWidgetPosition.x > widgetPosition.x)
1365                 this.onPassFocusToChild = this._findFarestChildWidgetIndex.bind(this);
1366             else
1367                 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this);
1368         }else if(direction == ccui.Widget.DOWN){
1369             if (previousWidgetPosition.y > widgetPosition.y)
1370                 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this);
1371             else
1372                 this.onPassFocusToChild = this._findFarestChildWidgetIndex.bind(this);
1373         }else if(direction == ccui.Widget.UP){
1374             if (previousWidgetPosition.y < widgetPosition.y)
1375                 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this);
1376             else
1377                 this.onPassFocusToChild = this._findFarestChildWidgetIndex.bind(this);
1378         }else
1379             cc.assert(0, "invalid direction!");
1380     },
1381 
1382     _findFirstNonLayoutWidget:function(){
1383         var locChildren = this._children;
1384         for(var i = 0, len = locChildren.length; i < len; i++) {
1385             var child = locChildren[i];
1386             if (child instanceof ccui.Layout){
1387                 var widget = child._findFirstNonLayoutWidget();
1388                 if(widget)
1389                     return widget;
1390             } else{
1391                 if (child instanceof cc.Widget)
1392                     return child;
1393             }
1394         }
1395         return null;
1396     },
1397 
1398     _findFirstFocusEnabledWidgetIndex: function(){
1399         var index = 0, locChildren = this.getChildren();
1400         var count = locChildren.length;
1401         while (index < count) {
1402             var w = locChildren[index];
1403             if (w && w instanceof ccui.Widget && w.isFocusEnabled())
1404                 return index;
1405             index++;
1406         }
1407         //cc.assert(0, "invalid operation");
1408         return 0;
1409     },
1410 
1411     _findFocusEnabledChildWidgetByIndex: function(index){
1412         var widget = this._getChildWidgetByIndex(index);
1413 
1414         if (widget){
1415             if (widget.isFocusEnabled())
1416                 return widget;
1417             index = index + 1;
1418             return this._findFocusEnabledChildWidgetByIndex(index);
1419         }
1420         return null;
1421     },
1422 
1423     _getWorldCenterPoint: function(widget){
1424         //FIXEDME: we don't need to calculate the content size of layout anymore
1425         var widgetSize = widget instanceof ccui.Layout ? widget._getLayoutAccumulatedSize() :  widget.getContentSize();
1426         return widget.convertToWorldSpace(cc.p(widgetSize.width /2, widgetSize.height /2));
1427     },
1428 
1429     _getNextFocusedWidget: function(direction, current){
1430         var nextWidget = null, locChildren = this._children;
1431         var  previousWidgetPos = locChildren.indexOf(current);
1432         previousWidgetPos = previousWidgetPos + 1;
1433         if (previousWidgetPos < locChildren.length) {
1434             nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1435             //handle widget
1436             if (nextWidget) {
1437                 if (nextWidget.isFocusEnabled()) {
1438                     if (nextWidget instanceof ccui.Layout) {
1439                         nextWidget._isFocusPassing = true;
1440                         return nextWidget.findNextFocusedWidget(direction, nextWidget);
1441                     } else {
1442                         this.dispatchFocusEvent(current, nextWidget);
1443                         return nextWidget;
1444                     }
1445                 } else
1446                     return this._getNextFocusedWidget(direction, nextWidget);
1447             } else
1448                 return current;
1449         } else {
1450             if (this._loopFocus) {
1451                 if (this._checkFocusEnabledChild()) {
1452                     previousWidgetPos = 0;
1453                     nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1454                     if (nextWidget.isFocusEnabled()) {
1455                         if (nextWidget instanceof ccui.Layout) {
1456                             nextWidget._isFocusPassing = true;
1457                             return nextWidget.findNextFocusedWidget(direction, nextWidget);
1458                         } else {
1459                             this.dispatchFocusEvent(current, nextWidget);
1460                             return nextWidget;
1461                         }
1462                     } else
1463                         return this._getNextFocusedWidget(direction, nextWidget);
1464                 } else {
1465                     if (current instanceof ccui.Layout)
1466                         return current;
1467                     else
1468                         return this._focusedWidget;
1469                 }
1470             } else{
1471                 if (this._isLastWidgetInContainer(current, direction)){
1472                     if (this._isWidgetAncestorSupportLoopFocus(this, direction))
1473                         return this.findNextFocusedWidget(direction, this);
1474                     if (current instanceof ccui.Layout)
1475                         return current;
1476                     else
1477                         return this._focusedWidget;
1478                 } else
1479                     return this.findNextFocusedWidget(direction, this);
1480             }
1481         }
1482     },
1483 
1484     _getPreviousFocusedWidget: function(direction, current){
1485         var nextWidget = null, locChildren = this._children;
1486         var previousWidgetPos = locChildren.indexOf(current);
1487         previousWidgetPos = previousWidgetPos - 1;
1488         if (previousWidgetPos >= 0){
1489             nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1490             if (nextWidget.isFocusEnabled()) {
1491                 if (nextWidget instanceof ccui.Layout){
1492                     nextWidget._isFocusPassing = true;
1493                     return nextWidget.findNextFocusedWidget(direction, nextWidget);
1494                 }
1495                 this.dispatchFocusEvent(current, nextWidget);
1496                 return nextWidget;
1497             } else
1498                 //handling the disabled widget, there is no actual focus lose or get, so we don't need any envet
1499                 return this._getPreviousFocusedWidget(direction, nextWidget);
1500         }else {
1501             if (this._loopFocus){
1502                 if (this._checkFocusEnabledChild()) {
1503                     previousWidgetPos = locChildren.length -1;
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                         } else {
1510                             this.dispatchFocusEvent(current, nextWidget);
1511                             return nextWidget;
1512                         }
1513                     } else
1514                         return this._getPreviousFocusedWidget(direction, nextWidget);
1515                 } else {
1516                     if (current instanceof ccui.Layout)
1517                         return current;
1518                     else
1519                         return this._focusedWidget;
1520                 }
1521             } else {
1522                 if (this._isLastWidgetInContainer(current, direction)) {
1523                     if (this._isWidgetAncestorSupportLoopFocus(this, direction))
1524                         return this.findNextFocusedWidget(direction, this);
1525 
1526                     if (current instanceof ccui.Layout)
1527                         return current;
1528                     else
1529                         return this._focusedWidget;
1530                 } else
1531                     return this.findNextFocusedWidget(direction, this);
1532             }
1533         }
1534     },
1535 
1536     _getChildWidgetByIndex: function (index) {
1537         var locChildren = this._children;
1538         var size = locChildren.length;
1539         var count = 0, oldIndex = index;
1540         while (index < size) {
1541             var firstChild = locChildren[index];
1542             if (firstChild && firstChild instanceof ccui.Widget)
1543                 return firstChild;
1544             count++;
1545             index++;
1546         }
1547 
1548         var begin = 0;
1549         while (begin < oldIndex) {
1550             var child = locChildren[begin];
1551             if (child && child instanceof ccui.Widget)
1552                 return child;
1553             count++;
1554             begin++;
1555         }
1556         return null;
1557     },
1558 
1559     _isLastWidgetInContainer:function(widget, direction){
1560         var parent = widget.getParent();
1561         if (parent instanceof ccui.Layout)
1562             return true;
1563 
1564         var container = parent.getChildren();
1565         var index = container.indexOf(widget);
1566         if (parent.getLayoutType() == ccui.Layout.LINEAR_HORIZONTAL) {
1567             if (direction == ccui.Widget.LEFT) {
1568                 if (index == 0)
1569                     return true * this._isLastWidgetInContainer(parent, direction);
1570                 else
1571                     return false;
1572             }
1573             if (direction == ccui.Widget.RIGHT) {
1574                 if (index == container.length - 1)
1575                     return true * this._isLastWidgetInContainer(parent, direction);
1576                 else
1577                     return false;
1578             }
1579             if (direction == ccui.Widget.DOWN)
1580                 return this._isLastWidgetInContainer(parent, direction);
1581 
1582             if (direction == ccui.Widget.UP)
1583                 return this._isLastWidgetInContainer(parent, direction);
1584         } else if(parent.getLayoutType() == ccui.Layout.LINEAR_VERTICAL){
1585             if (direction == ccui.Widget.UP){
1586                 if (index == 0)
1587                     return true * this._isLastWidgetInContainer(parent, direction);
1588                 else
1589                     return false;
1590             }
1591             if (direction == ccui.Widget.DOWN) {
1592                 if (index == container.length - 1)
1593                     return true * this._isLastWidgetInContainer(parent, direction);
1594                 else
1595                     return false;
1596             }
1597             if (direction == ccui.Widget.LEFT)
1598                 return this._isLastWidgetInContainer(parent, direction);
1599 
1600             if (direction == ccui.Widget.RIGHT)
1601                 return this._isLastWidgetInContainer(parent, direction);
1602         }else {
1603             cc.assert(0, "invalid layout Type");
1604             return false;
1605         }
1606         return false;
1607     },
1608 
1609     _isWidgetAncestorSupportLoopFocus: function(widget, direction){
1610         var parent = widget.getParent();
1611         if (parent == null)
1612             return false;
1613         if (parent.isLoopFocus()) {
1614             var layoutType = parent.getLayoutType();
1615             if (layoutType == ccui.Layout.LINEAR_HORIZONTAL) {
1616                 if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT)
1617                     return true;
1618                 else
1619                     return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1620             }
1621             if (layoutType == ccui.Layout.LINEAR_VERTICAL){
1622                 if (direction == ccui.Widget.DOWN || direction == ccui.Widget.UP)
1623                     return true;
1624                 else
1625                     return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1626             } else
1627                 cc.assert(0, "invalid layout type");
1628         } else
1629             return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1630     },
1631 
1632     _passFocusToChild: function(direction, current){
1633         if (this._checkFocusEnabledChild()) {
1634             var previousWidget = this.getCurrentFocusedWidget();
1635             this._findProperSearchingFunctor(direction, previousWidget);
1636 
1637             var index = this.onPassFocusToChild(direction, previousWidget);       //TODO need check
1638 
1639             var widget = this._getChildWidgetByIndex(index);
1640             if (widget instanceof ccui.Layout) {
1641                 widget._isFocusPassing = true;
1642                 return widget.findNextFocusedWidget(direction, widget);
1643             } else {
1644                 this.dispatchFocusEvent(current, widget);
1645                 return widget;
1646             }
1647         }else
1648             return this;
1649     },
1650 
1651     _checkFocusEnabledChild: function(){
1652         var locChildren = this._children;
1653         for(var i = 0, len = locChildren.length; i < len; i++){
1654             var widget = locChildren[i];
1655             if (widget && widget instanceof ccui.Widget && widget.isFocusEnabled())
1656                 return true;
1657         }
1658         return false;
1659     },
1660 
1661     /**
1662      * Returns the "class name" of widget.
1663      * @returns {string}
1664      */
1665     getDescription: function () {
1666         return "Layout";
1667     },
1668 
1669     createCloneInstance: function () {
1670         return ccui.Layout.create();
1671     },
1672 
1673     copyClonedWidgetChildren: function (model) {
1674         ccui.Widget.prototype.copyClonedWidgetChildren.call(this, model);
1675     },
1676 
1677     copySpecialProperties: function (layout) {
1678         this.setBackGroundImageScale9Enabled(layout._backGroundScale9Enabled);
1679         this.setBackGroundImage(layout._backGroundImageFileName, layout._bgImageTexType);
1680         this.setBackGroundImageCapInsets(layout._backGroundImageCapInsets);
1681         this.setBackGroundColorType(layout._colorType);
1682         this.setBackGroundColor(layout._color);
1683         this.setBackGroundColor(layout._startColor, layout._endColor);
1684         this.setBackGroundColorOpacity(layout._opacity);
1685         this.setBackGroundColorVector(layout._alongVector);
1686         this.setLayoutType(layout._layoutType);
1687         this.setClippingEnabled(layout._clippingEnabled);
1688         this.setClippingType(layout._clippingType);
1689         this._loopFocus = layout._loopFocus;
1690         this._passFocusToChild = layout._passFocusToChild;
1691     }
1692 });
1693 ccui.Layout._init_once = null;
1694 ccui.Layout._visit_once = null;
1695 ccui.Layout._layer = null;
1696 ccui.Layout._sharedCache = null;
1697 
1698 if (cc._renderType == cc._RENDER_TYPE_WEBGL) {
1699     //WebGL
1700     ccui.Layout.prototype.stencilClippingVisit = ccui.Layout.prototype._stencilClippingVisitForWebGL;
1701     ccui.Layout.prototype.scissorClippingVisit = ccui.Layout.prototype._scissorClippingVisitForWebGL;
1702 } else {
1703     ccui.Layout.prototype.stencilClippingVisit = ccui.Layout.prototype._stencilClippingVisitForCanvas;
1704     ccui.Layout.prototype.scissorClippingVisit = ccui.Layout.prototype._stencilClippingVisitForCanvas;
1705 }
1706 ccui.Layout._getSharedCache = function () {
1707     return (cc.ClippingNode._sharedCache) || (cc.ClippingNode._sharedCache = cc.newElement("canvas"));
1708 };
1709 
1710 var _p = ccui.Layout.prototype;
1711 
1712 // Extended properties
1713 /** @expose */
1714 _p.clippingEnabled;
1715 cc.defineGetterSetter(_p, "clippingEnabled", _p.isClippingEnabled, _p.setClippingEnabled);
1716 /** @expose */
1717 _p.clippingType;
1718 cc.defineGetterSetter(_p, "clippingType", null, _p.setClippingType);
1719 /** @expose */
1720 _p.layoutType;
1721 cc.defineGetterSetter(_p, "layoutType", _p.getLayoutType, _p.setLayoutType);
1722 
1723 _p = null;
1724 
1725 /**
1726  * allocates and initializes a UILayout.
1727  * @constructs
1728  * @return {ccui.Layout}
1729  * @example
1730  * // example
1731  * var uiLayout = ccui.Layout.create();
1732  */
1733 ccui.Layout.create = function () {
1734     return new ccui.Layout();
1735 };
1736 
1737 // Constants
1738 
1739 //layoutBackGround color type
1740 ccui.Layout.BG_COLOR_NONE = 0;
1741 ccui.Layout.BG_COLOR_SOLID = 1;
1742 ccui.Layout.BG_COLOR_GRADIENT = 2;
1743 
1744 //Layout type
1745 ccui.Layout.ABSOLUTE = 0;
1746 ccui.Layout.LINEAR_VERTICAL = 1;
1747 ccui.Layout.LINEAR_HORIZONTAL = 2;
1748 ccui.Layout.RELATIVE = 3;
1749 
1750 //Layout clipping type
1751 ccui.Layout.CLIPPING_STENCIL = 0;
1752 ccui.Layout.CLIPPING_SCISSOR = 1;
1753 
1754 ccui.Layout.BACKGROUND_IMAGE_ZORDER = -2;
1755 ccui.Layout.BACKGROUND_RENDERER_ZORDER = -2;