1 /****************************************************************************
  2  Copyright (c) 2011-2012 cocos2d-x.org
  3  Copyright (c) 2013-2014 Chukong Technologies Inc.
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24  ****************************************************************************/
 25 
 26 /**
 27  * ccui.Layout is the base class of  ccui.PageView and ccui.ScrollView, it does layout by layout manager
 28  *  and clips area by its _clippingStencil when clippingEnabled is true.
 29  * @class
 30  * @extends ccui.Widget
 31  *
 32  * @property {Boolean}                  clippingEnabled - Indicate whether clipping is enabled
 33  * @property {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR}   clippingType
 34  * @property {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE}  layoutType
 35  *
 36  */
 37 ccui.Layout = ccui.Widget.extend(/** @lends ccui.Layout# */{
 38     _clippingEnabled: false,
 39     _backGroundScale9Enabled: null,
 40     _backGroundImage: null,
 41     _backGroundImageFileName: null,
 42     _backGroundImageCapInsets: null,
 43     _colorType: null,
 44     _bgImageTexType: ccui.Widget.LOCAL_TEXTURE,
 45     _colorRender: null,
 46     _gradientRender: null,
 47     _color: null,
 48     _startColor: null,
 49     _endColor: null,
 50     _alongVector: null,
 51     _opacity: 255,
 52     _backGroundImageTextureSize: null,
 53     _layoutType: null,
 54     _doLayoutDirty: true,
 55     _clippingRectDirty: true,
 56     _clippingType: null,
 57     _clippingStencil: null,
 58     _scissorRectDirty: false,
 59     _clippingRect: null,
 60     _clippingParent: null,
 61     _className: "Layout",
 62     _backGroundImageColor: null,
 63     _finalPositionX: 0,
 64     _finalPositionY: 0,
 65 
 66     _backGroundImageOpacity:0,
 67 
 68     _loopFocus: false,                                                          //whether enable loop focus or not
 69     __passFocusToChild: true,                                                  //on default, it will pass the focus to the next nearest widget
 70     _isFocusPassing:false,                                                      //when finding the next focused widget, use this variable to pass focus between layout & widget
 71     _isInterceptTouch: false,
 72 
 73     /**
 74      * Allocates and initializes an UILayout.
 75      * Constructor of ccui.Layout
 76      * @function
 77      * @example
 78      * // example
 79      * var uiLayout = new ccui.Layout();
 80      */
 81     ctor: function () {
 82         this._layoutType = ccui.Layout.ABSOLUTE;
 83         this._widgetType = ccui.Widget.TYPE_CONTAINER;
 84         this._clippingType = ccui.Layout.CLIPPING_SCISSOR;
 85         this._colorType = ccui.Layout.BG_COLOR_NONE;
 86 
 87         ccui.Widget.prototype.ctor.call(this);
 88 
 89         this.ignoreContentAdaptWithSize(false);
 90         this.setContentSize(cc.size(0, 0));
 91         this.setAnchorPoint(0, 0);
 92         this.onPassFocusToChild  = this._findNearestChildWidgetIndex.bind(this);
 93 
 94         this._backGroundImageCapInsets = cc.rect(0, 0, 0, 0);
 95 
 96         this._color = cc.color(255, 255, 255, 255);
 97         this._startColor = cc.color(255, 255, 255, 255);
 98         this._endColor = cc.color(255, 255, 255, 255);
 99         this._alongVector = cc.p(0, -1);
100         this._backGroundImageTextureSize = cc.size(0, 0);
101 
102         this._clippingRect = cc.rect(0, 0, 0, 0);
103         this._backGroundImageColor = cc.color(255, 255, 255, 255);
104     },
105 
106     /**
107      * Calls its parent's onEnter, and calls its clippingStencil's onEnter if clippingStencil isn't null.
108      * @override
109      */
110     onEnter: function(){
111         ccui.Widget.prototype.onEnter.call(this);
112         if (this._clippingStencil)
113             this._clippingStencil.onEnter();
114         this._doLayoutDirty = true;
115         this._clippingRectDirty = true;
116     },
117 
118     /**
119      *  Calls its parent's onExit, and calls its clippingStencil's onExit if clippingStencil isn't null.
120      *  @override
121      */
122     onExit: function(){
123         ccui.Widget.prototype.onExit.call(this);
124         if (this._clippingStencil)
125             this._clippingStencil.onExit();
126     },
127 
128     /**
129      * If a layout is loop focused which means that the focus movement will be inside the layout
130      * @param {Boolean} loop pass true to let the focus movement loop inside the layout
131      */
132     setLoopFocus: function(loop){
133         this._loopFocus = loop;
134     },
135 
136     /**
137      * Gets whether enable focus loop
138      * @returns {boolean}  If focus loop is enabled, then it will return true, otherwise it returns false. The default value is false.
139      */
140     isLoopFocus: function(){
141         return this._loopFocus;
142     },
143 
144     /**
145      * Specifies whether the layout pass its focus to its child
146      * @param pass To specify whether the layout pass its focus to its child
147      */
148     setPassFocusToChild: function(pass){
149         this.__passFocusToChild = pass;
150     },
151 
152     /**
153      * Returns whether the layout will pass the focus to its children or not. The default value is true
154      * @returns {boolean} To query whether the layout will pass the focus to its children or not. The default value is true
155      */
156     isPassFocusToChild: function(){
157         return this.__passFocusToChild;
158     },
159 
160     /**
161      * When a widget is in a layout, you could call this method to get the next focused widget within a specified direction.
162      * If the widget is not in a layout, it will return itself
163      * @param {Number} direction the direction to look for the next focused widget in a layout
164      * @param {ccui.Widget} current the current focused widget
165      * @returns {ccui.Widget} return the index of widget in the layout
166      */
167     findNextFocusedWidget: function(direction, current){
168         if (this._isFocusPassing || this.isFocused()) {
169             var parent = this.getParent();
170             this._isFocusPassing = false;
171             if (this.__passFocusToChild) {
172                 var w = this._passFocusToChild(direction, current);
173                 if (w instanceof ccui.Layout && parent) {
174                     parent._isFocusPassing = true;
175                     return parent.findNextFocusedWidget(direction, this);
176                 }
177                 return w;
178             }
179 
180             if (null == parent || !(parent instanceof ccui.Layout))
181                 return this;
182             parent._isFocusPassing = true;
183             return parent.findNextFocusedWidget(direction, this);
184         } else if(current.isFocused() || current instanceof ccui.Layout) {
185             if (this._layoutType === ccui.Layout.LINEAR_HORIZONTAL) {
186                 switch (direction){
187                     case ccui.Widget.LEFT:
188                         return this._getPreviousFocusedWidget(direction, current);
189                     break;
190                     case ccui.Widget.RIGHT:
191                         return this._getNextFocusedWidget(direction, current);
192                     break;
193                     case ccui.Widget.DOWN:
194                     case ccui.Widget.UP:
195                         if (this._isLastWidgetInContainer(this, direction)){
196                             if (this._isWidgetAncestorSupportLoopFocus(current, direction))
197                                 return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
198                             return current;
199                         } else {
200                             return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
201                         }
202                     break;
203                     default:
204                         cc.assert(0, "Invalid Focus Direction");
205                         return current;
206                 }
207             } else if (this._layoutType === ccui.Layout.LINEAR_VERTICAL) {
208                 switch (direction){
209                     case ccui.Widget.LEFT:
210                     case ccui.Widget.RIGHT:
211                         if (this._isLastWidgetInContainer(this, direction)) {
212                             if (this._isWidgetAncestorSupportLoopFocus(current, direction))
213                                 return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
214                             return current;
215                         }
216                         else
217                             return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
218                      break;
219                     case ccui.Widget.DOWN:
220                         return this._getNextFocusedWidget(direction, current);
221                         break;
222                     case ccui.Widget.UP:
223                         return this._getPreviousFocusedWidget(direction, current);
224                         break;
225                     default:
226                         cc.assert(0, "Invalid Focus Direction");
227                         return current;
228                 }
229             } else {
230                 cc.assert(0, "Un Supported Layout type, please use VBox and HBox instead!!!");
231                 return current;
232             }
233         } else
234             return current;
235     },
236 
237     /**
238      * To specify a user-defined functor to decide which child widget of the layout should get focused
239      * @function
240      * @param {Number} direction
241      * @param {ccui.Widget} current
242      */
243     onPassFocusToChild: null,
244 
245     /**
246      * Adds a widget to the container.
247      * @param {ccui.Widget} widget
248      * @param {Number} [zOrder]
249      * @param {Number|string} [tag] tag or name
250      * @override
251      */
252     addChild: function (widget, zOrder, tag) {
253         if ((widget instanceof ccui.Widget)) {
254             this._supplyTheLayoutParameterLackToChild(widget);
255         }
256         ccui.Widget.prototype.addChild.call(this, widget, zOrder, tag);
257         this._doLayoutDirty = true;
258     },
259 
260     /**
261      * Removes child widget from ccui.Layout, and sets the layout dirty flag to true.
262      * @param {ccui.Widget} widget
263      * @param {Boolean} [cleanup=true]
264      * @override
265      */
266     removeChild: function (widget, cleanup) {
267         ccui.Widget.prototype.removeChild.call(this, widget, cleanup);
268         this._doLayoutDirty = true;
269     },
270 
271     /**
272      * Removes all children from the container with a cleanup, and sets the layout dirty flag to true.
273      * @param {Boolean} cleanup
274      */
275     removeAllChildren: function (cleanup) {
276         ccui.Widget.prototype.removeAllChildren.call(this, cleanup);
277         this._doLayoutDirty = true;
278     },
279 
280     /**
281      * Removes all children from the container, do a cleanup to all running actions depending on the cleanup parameter,
282      * and sets the layout dirty flag to true.
283      * @param {Boolean} cleanup true if all running actions on all children nodes should be cleanup, false otherwise.
284      */
285     removeAllChildrenWithCleanup: function(cleanup){
286         ccui.Widget.prototype.removeAllChildrenWithCleanup.call(this, cleanup);
287         this._doLayoutDirty = true;
288     },
289 
290     /**
291      * Gets if layout is clipping enabled.
292      * @returns {Boolean} if layout is clipping enabled.
293      */
294     isClippingEnabled: function () {
295         return this._clippingEnabled;
296     },
297 
298     /**
299      * <p>
300      *     Calls adaptRenderers (its subclass will override it.) and do layout.
301      *     If clippingEnabled is true, it will clip/scissor area.
302      * </p>
303      * @override
304      * @param {cc.Node.RenderCmd} [parentCmd]
305      */
306     visit: function (parentCmd) {
307         if (!this._visible)
308             return;
309         this._adaptRenderers();
310         this._doLayout();
311 
312         if (this._clippingEnabled) {
313             switch (this._clippingType) {
314                 case ccui.Layout.CLIPPING_STENCIL:
315                     this._renderCmd.stencilClippingVisit(parentCmd);
316                     break;
317                 case ccui.Layout.CLIPPING_SCISSOR:
318                     this._renderCmd.scissorClippingVisit(parentCmd);
319                     break;
320                 default:
321                     break;
322             }
323         } else {
324             ccui.Widget.prototype.visit.call(this, parentCmd);
325         }
326     },
327 
328     /**
329      * Changes if layout can clip it's content and locChild.
330      * If you really need this, please enable it. But it would reduce the rendering efficiency.
331      * @param {Boolean} able clipping enabled.
332      */
333     setClippingEnabled: function (able) {
334         if (able === this._clippingEnabled)
335             return;
336         this._clippingEnabled = able;
337         switch (this._clippingType) {
338             case ccui.Layout.CLIPPING_SCISSOR:
339             case ccui.Layout.CLIPPING_STENCIL:
340                 if (able){
341                     this._clippingStencil = new cc.DrawNode();
342                     this._renderCmd.rebindStencilRendering(this._clippingStencil);
343                     if (this._running)
344                         this._clippingStencil.onEnter();
345                     this._setStencilClippingSize(this._contentSize);
346                 } else {
347                     if (this._running && this._clippingStencil)
348                         this._clippingStencil.onExit();
349                     this._clippingStencil = null;
350                 }
351                 break;
352             default:
353                 break;
354         }
355     },
356 
357     /**
358      * Sets clipping type to ccui.Layout
359      * @param {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} type
360      */
361     setClippingType: function (type) {
362         if (type === this._clippingType)
363             return;
364         var clippingEnabled = this.isClippingEnabled();
365         this.setClippingEnabled(false);
366         this._clippingType = type;
367         this.setClippingEnabled(clippingEnabled);
368     },
369 
370     /**
371      * Gets clipping type of ccui.Layout
372      * @returns {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR}
373      */
374     getClippingType: function () {
375         return this._clippingType;
376     },
377 
378     _setStencilClippingSize: function (size) {
379         if (this._clippingEnabled) {
380             var rect = [];
381             rect[0] = cc.p(0, 0);
382             rect[1] = cc.p(size.width, 0);
383             rect[2] = cc.p(size.width, size.height);
384             rect[3] = cc.p(0, size.height);
385             var green = cc.color.GREEN;
386             this._clippingStencil.clear();
387             this._clippingStencil.setLocalBB && this._clippingStencil.setLocalBB(0, 0, size.width, size.height);
388             this._clippingStencil.drawPoly(rect, 4, green, 0, green);
389         }
390     },
391 
392     _getClippingRect: function () {
393         if (this._clippingRectDirty) {
394             var worldPos = this.convertToWorldSpace(cc.p(0, 0));
395             var t = this.getNodeToWorldTransform();
396             var scissorWidth = this._contentSize.width * t.a;
397             var scissorHeight = this._contentSize.height * t.d;
398             var parentClippingRect;
399             var parent = this;
400 
401             while (parent) {
402                 parent = parent.getParent();
403                 if (parent && parent instanceof ccui.Layout && parent.isClippingEnabled()) {
404                     this._clippingParent = parent;
405                     break;
406                 }
407             }
408 
409             if (this._clippingParent) {
410                 parentClippingRect = this._clippingParent._getClippingRect();
411 
412                 this._clippingRect.x = Math.max(worldPos.x, parentClippingRect.x);
413                 this._clippingRect.y = Math.max(worldPos.y, parentClippingRect.y);
414 
415                 var right = Math.min(worldPos.x + scissorWidth, parentClippingRect.x + parentClippingRect.width);
416                 var top = Math.min(worldPos.y + scissorHeight, parentClippingRect.y + parentClippingRect.height);
417 
418                 this._clippingRect.width = Math.max(0.0, right -  this._clippingRect.x);
419                 this._clippingRect.height = Math.max(0.0, top -  this._clippingRect.y);
420             } else {
421                 this._clippingRect.x = worldPos.x;
422                 this._clippingRect.y = worldPos.y;
423                 this._clippingRect.width = scissorWidth;
424                 this._clippingRect.height = scissorHeight;
425             }
426             this._clippingRectDirty = false;
427         }
428         return this._clippingRect;
429     },
430 
431     _onSizeChanged: function () {
432         ccui.Widget.prototype._onSizeChanged.call(this);
433         var locContentSize = this._contentSize;
434         this._setStencilClippingSize(locContentSize);
435         this._doLayoutDirty = true;
436         this._clippingRectDirty = true;
437         if (this._backGroundImage) {
438             this._backGroundImage.setPosition(locContentSize.width * 0.5, locContentSize.height * 0.5);
439             if (this._backGroundScale9Enabled && this._backGroundImage instanceof ccui.Scale9Sprite)
440                 this._backGroundImage.setPreferredSize(locContentSize);
441         }
442         if (this._colorRender)
443             this._colorRender.setContentSize(locContentSize);
444         if (this._gradientRender)
445             this._gradientRender.setContentSize(locContentSize);
446     },
447 
448     /**
449      * Sets background image use scale9 renderer.
450      * @param {Boolean} able  true that use scale9 renderer, false otherwise.
451      */
452     setBackGroundImageScale9Enabled: function (able) {
453         if (this._backGroundScale9Enabled === able)
454             return;
455         this.removeProtectedChild(this._backGroundImage);
456         this._backGroundImage = null;
457         this._backGroundScale9Enabled = able;
458         this._addBackGroundImage();
459         this.setBackGroundImage(this._backGroundImageFileName, this._bgImageTexType);
460         this.setBackGroundImageCapInsets(this._backGroundImageCapInsets);
461     },
462 
463     /**
464      * Get whether background image is use scale9 renderer.
465      * @returns {Boolean}
466      */
467     isBackGroundImageScale9Enabled: function () {
468         return this._backGroundScale9Enabled;
469     },
470 
471     /**
472      * Sets a background image for layout
473      * @param {String} fileName
474      * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType
475      */
476     setBackGroundImage: function (fileName, texType) {
477         if (!fileName)
478             return;
479         texType = texType || ccui.Widget.LOCAL_TEXTURE;
480         if (this._backGroundImage === null){
481             this._addBackGroundImage();
482             this.setBackGroundImageScale9Enabled(this._backGroundScale9Enabled);
483         }
484         this._backGroundImageFileName = fileName;
485         this._bgImageTexType = texType;
486         var locBackgroundImage = this._backGroundImage;
487         switch (this._bgImageTexType) {
488             case ccui.Widget.LOCAL_TEXTURE:
489                 locBackgroundImage.initWithFile(fileName);
490                 break;
491             case ccui.Widget.PLIST_TEXTURE:
492                 locBackgroundImage.initWithSpriteFrameName(fileName);
493                 break;
494             default:
495                 break;
496         }
497         if (this._backGroundScale9Enabled)
498             locBackgroundImage.setPreferredSize(this._contentSize);
499 
500         this._backGroundImageTextureSize = locBackgroundImage.getContentSize();
501         locBackgroundImage.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5);
502         this._updateBackGroundImageColor();
503     },
504 
505     /**
506      * Sets a background image CapInsets for layout, if the background image is a scale9 render.
507      * @param {cc.Rect} capInsets capinsets of background image.
508      */
509     setBackGroundImageCapInsets: function (capInsets) {
510         if(!capInsets)
511             return;
512         var locInsets = this._backGroundImageCapInsets;
513         locInsets.x = capInsets.x;
514         locInsets.y = capInsets.y;
515         locInsets.width = capInsets.width;
516         locInsets.height = capInsets.height;
517         if (this._backGroundScale9Enabled)
518             this._backGroundImage.setCapInsets(capInsets);
519     },
520 
521     /**
522      * Gets background image capinsets of ccui.Layout.
523      * @returns {cc.Rect}
524      */
525     getBackGroundImageCapInsets: function () {
526         return cc.rect(this._backGroundImageCapInsets);
527     },
528 
529     _supplyTheLayoutParameterLackToChild: function (locChild) {
530         if (!locChild) {
531             return;
532         }
533         switch (this._layoutType) {
534             case ccui.Layout.ABSOLUTE:
535                 break;
536             case ccui.Layout.LINEAR_HORIZONTAL:
537             case ccui.Layout.LINEAR_VERTICAL:
538                 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.LINEAR);
539                 if (!layoutParameter)
540                     locChild.setLayoutParameter(new ccui.LinearLayoutParameter());
541                 break;
542             case ccui.Layout.RELATIVE:
543                 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.RELATIVE);
544                 if (!layoutParameter)
545                     locChild.setLayoutParameter(new ccui.RelativeLayoutParameter());
546                 break;
547             default:
548                 break;
549         }
550     },
551 
552     _addBackGroundImage: function () {
553         var contentSize = this._contentSize;
554         if (this._backGroundScale9Enabled) {
555             this._backGroundImage = new ccui.Scale9Sprite();
556             this._backGroundImage.setPreferredSize(contentSize);
557         } else
558             this._backGroundImage = new cc.Sprite();
559         this.addProtectedChild(this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1);
560         this._backGroundImage.setPosition(contentSize.width * 0.5, contentSize.height * 0.5);
561     },
562 
563     /**
564      * Remove the background image of ccui.Layout.
565      */
566     removeBackGroundImage: function () {
567         if (!this._backGroundImage)
568             return;
569         this.removeProtectedChild(this._backGroundImage);
570         this._backGroundImage = null;
571         this._backGroundImageFileName = "";
572         this._backGroundImageTextureSize.width = 0;
573         this._backGroundImageTextureSize.height = 0;
574     },
575 
576     /**
577      * Sets Color Type for ccui.Layout.
578      * @param {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} type
579      */
580     setBackGroundColorType: function (type) {
581         if (this._colorType === type)
582             return;
583         switch (this._colorType) {
584             case ccui.Layout.BG_COLOR_NONE:
585                 if (this._colorRender) {
586                     this.removeProtectedChild(this._colorRender);
587                     this._colorRender = null;
588                 }
589                 if (this._gradientRender) {
590                     this.removeProtectedChild(this._gradientRender);
591                     this._gradientRender = null;
592                 }
593                 break;
594             case ccui.Layout.BG_COLOR_SOLID:
595                 if (this._colorRender) {
596                     this.removeProtectedChild(this._colorRender);
597                     this._colorRender = null;
598                 }
599                 break;
600             case ccui.Layout.BG_COLOR_GRADIENT:
601                 if (this._gradientRender) {
602                     this.removeProtectedChild(this._gradientRender);
603                     this._gradientRender = null;
604                 }
605                 break;
606             default:
607                 break;
608         }
609         this._colorType = type;
610         switch (this._colorType) {
611             case ccui.Layout.BG_COLOR_NONE:
612                 break;
613             case ccui.Layout.BG_COLOR_SOLID:
614                 this._colorRender = new cc.LayerColor();
615                 this._colorRender.setContentSize(this._contentSize);
616                 this._colorRender.setOpacity(this._opacity);
617                 this._colorRender.setColor(this._color);
618                 this.addProtectedChild(this._colorRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1);
619                 break;
620             case ccui.Layout.BG_COLOR_GRADIENT:
621                 this._gradientRender = new cc.LayerGradient(cc.color(255, 0, 0, 255), cc.color(0, 255, 0, 255));
622                 this._gradientRender.setContentSize(this._contentSize);
623                 this._gradientRender.setOpacity(this._opacity);
624                 this._gradientRender.setStartColor(this._startColor);
625                 this._gradientRender.setEndColor(this._endColor);
626                 this._gradientRender.setVector(this._alongVector);
627                 this.addProtectedChild(this._gradientRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1);
628                 break;
629             default:
630                 break;
631         }
632     },
633 
634     /**
635      * Get background color type of ccui.Layout.
636      * @returns {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT}
637      */
638     getBackGroundColorType: function () {
639         return this._colorType;
640     },
641 
642     /**
643      * Sets background color for layout, if color type is Layout.COLOR_SOLID
644      * @param {cc.Color} color
645      * @param {cc.Color} [endColor]
646      */
647     setBackGroundColor: function (color, endColor) {
648         if (!endColor) {
649             this._color.r = color.r;
650             this._color.g = color.g;
651             this._color.b = color.b;
652             if (this._colorRender)
653                 this._colorRender.setColor(color);
654         } else {
655             this._startColor.r = color.r;
656             this._startColor.g = color.g;
657             this._startColor.b = color.b;
658             if (this._gradientRender)
659                 this._gradientRender.setStartColor(color);
660 
661             this._endColor.r = endColor.r;
662             this._endColor.g = endColor.g;
663             this._endColor.b = endColor.b;
664             if (this._gradientRender)
665                 this._gradientRender.setEndColor(endColor);
666         }
667     },
668 
669     /**
670      * Gets background color of ccui.Layout, if color type is Layout.COLOR_SOLID.
671      * @returns {cc.Color}
672      */
673     getBackGroundColor: function () {
674         var tmpColor = this._color;
675         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
676     },
677 
678     /**
679      * Gets background start color of ccui.Layout
680      * @returns {cc.Color}
681      */
682     getBackGroundStartColor: function () {
683         var tmpColor = this._startColor;
684         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
685     },
686 
687     /**
688      * Gets background end color of ccui.Layout
689      * @returns {cc.Color}
690      */
691     getBackGroundEndColor: function () {
692         var tmpColor = this._endColor;
693         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
694     },
695 
696     /**
697      * Sets background opacity to ccui.Layout.
698      * @param {number} opacity
699      */
700     setBackGroundColorOpacity: function (opacity) {
701         this._opacity = opacity;
702         switch (this._colorType) {
703             case ccui.Layout.BG_COLOR_NONE:
704                 break;
705             case ccui.Layout.BG_COLOR_SOLID:
706                 this._colorRender.setOpacity(opacity);
707                 break;
708             case ccui.Layout.BG_COLOR_GRADIENT:
709                 this._gradientRender.setOpacity(opacity);
710                 break;
711             default:
712                 break;
713         }
714     },
715 
716     /**
717      * Get background opacity value of ccui.Layout.
718      * @returns {Number}
719      */
720     getBackGroundColorOpacity: function () {
721         return this._opacity;
722     },
723 
724     /**
725      * Sets background color vector for layout, if color type is Layout.COLOR_GRADIENT
726      * @param {cc.Point} vector
727      */
728     setBackGroundColorVector: function (vector) {
729         this._alongVector.x = vector.x;
730         this._alongVector.y = vector.y;
731         if (this._gradientRender) {
732             this._gradientRender.setVector(vector);
733         }
734     },
735 
736     /**
737      *  Gets background color vector of ccui.Layout, if color type is Layout.COLOR_GRADIENT
738      * @returns {cc.Point}
739      */
740     getBackGroundColorVector: function () {
741         return this._alongVector;
742     },
743 
744     /**
745      * Sets backGround image color
746      * @param {cc.Color} color
747      */
748     setBackGroundImageColor: function (color) {
749         this._backGroundImageColor.r = color.r;
750         this._backGroundImageColor.g = color.g;
751         this._backGroundImageColor.b = color.b;
752 
753         this._updateBackGroundImageColor();
754     },
755 
756     /**
757      * Sets backGround image Opacity
758      * @param {Number} opacity
759      */
760     setBackGroundImageOpacity: function (opacity) {
761         this._backGroundImageColor.a = opacity;
762         this.getBackGroundImageColor();
763     },
764 
765     /**
766      * Gets backGround image color
767      * @returns {cc.Color}
768      */
769     getBackGroundImageColor: function () {
770         var color = this._backGroundImageColor;
771         return cc.color(color.r, color.g, color.b, color.a);
772     },
773 
774     /**
775      * Gets backGround image opacity
776      * @returns {Number}
777      */
778     getBackGroundImageOpacity: function () {
779         return this._backGroundImageColor.a;
780     },
781 
782     _updateBackGroundImageColor: function () {
783         if(this._backGroundImage)
784             this._backGroundImage.setColor(this._backGroundImageColor);
785     },
786 
787     /**
788      * Gets background image texture size.
789      * @returns {cc.Size}
790      */
791     getBackGroundImageTextureSize: function () {
792         return this._backGroundImageTextureSize;
793     },
794 
795     /**
796      * Sets LayoutType to ccui.Layout, LayoutManager will do layout by layout type..
797      * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type
798      */
799     setLayoutType: function (type) {
800         this._layoutType = type;
801         var layoutChildrenArray = this._children;
802         var locChild = null;
803         for (var i = 0; i < layoutChildrenArray.length; i++) {
804             locChild = layoutChildrenArray[i];
805             if(locChild instanceof ccui.Widget)
806                 this._supplyTheLayoutParameterLackToChild(locChild);
807         }
808         this._doLayoutDirty = true;
809     },
810 
811     /**
812      * Gets LayoutType of ccui.Layout.
813      * @returns {null}
814      */
815     getLayoutType: function () {
816         return this._layoutType;
817     },
818 
819     /**
820      * request to refresh widget layout, it will do layout at visit calls
821      */
822     requestDoLayout: function () {
823         this._doLayoutDirty = true;
824     },
825 
826     _doLayout: function () {
827         if (!this._doLayoutDirty)
828             return;
829 
830         this.sortAllChildren();
831 
832         var executant = ccui.getLayoutManager(this._layoutType);
833         if (executant)
834             executant._doLayout(this);
835         this._doLayoutDirty = false;
836     },
837 
838     _getLayoutContentSize: function(){
839         return this.getContentSize();
840     },
841 
842     _getLayoutElements: function(){
843         return this.getChildren();
844     },
845 
846     _updateBackGroundImageOpacity: function(){
847         if (this._backGroundImage)
848             this._backGroundImage.setOpacity(this._backGroundImageOpacity);
849     },
850 
851     _updateBackGroundImageRGBA: function(){
852         if (this._backGroundImage) {
853             this._backGroundImage.setColor(this._backGroundImageColor);
854             this._backGroundImage.setOpacity(this._backGroundImageOpacity);
855         }
856     },
857 
858     /**
859      * Gets the content size of the layout, it will accumulate all its children's content size
860      * @returns {cc.Size}
861      * @private
862      */
863     _getLayoutAccumulatedSize: function(){
864         var children = this.getChildren();
865         var  layoutSize = cc.size(0, 0);
866         var widgetCount = 0, locSize;
867         for(var i = 0, len = children.length; i < len; i++) {
868             var layout = children[i];
869             if (null !== layout && layout instanceof ccui.Layout){
870                 locSize = layout._getLayoutAccumulatedSize();
871                 layoutSize.width += locSize.width;
872                 layoutSize.height += locSize.height;
873             } else {
874                 if (layout instanceof ccui.Widget) {
875                     widgetCount++;
876                     var m = layout.getLayoutParameter().getMargin();
877                     locSize = layout.getContentSize();
878                     layoutSize.width += locSize.width +  (m.right + m.left) * 0.5;
879                     layoutSize.height += locSize.height +  (m.top + m.bottom) * 0.5;
880                 }
881             }
882         }
883 
884         //substract extra size
885         var type = this.getLayoutType();
886         if (type === ccui.Layout.LINEAR_HORIZONTAL)
887             layoutSize.height = layoutSize.height - layoutSize.height/widgetCount * (widgetCount-1);
888 
889         if (type === ccui.Layout.LINEAR_VERTICAL)
890             layoutSize.width = layoutSize.width - layoutSize.width/widgetCount * (widgetCount-1);
891         return layoutSize;
892     },
893 
894     /**
895      * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child      <br/>
896      * will get the focus.  The current algorithm to determine which child will get focus is nearest-distance-priority algorithm
897      * @param {Number} direction next focused widget direction
898      * @param {ccui.Widget} baseWidget
899      * @returns {Number}
900      * @private
901      */
902     _findNearestChildWidgetIndex: function(direction, baseWidget){
903         if (baseWidget == null || baseWidget === this)
904             return this._findFirstFocusEnabledWidgetIndex();
905 
906         var index = 0, locChildren = this.getChildren();
907         var count = locChildren.length, widgetPosition;
908 
909         var distance = cc.FLT_MAX, found = 0;
910         if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT || direction === ccui.Widget.DOWN || direction === ccui.Widget.UP) {
911             widgetPosition = this._getWorldCenterPoint(baseWidget);
912             while (index < count) {
913                 var w = locChildren[index];
914                 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) {
915                     var length = (w instanceof ccui.Layout)? w._calculateNearestDistance(baseWidget)
916                         : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition));
917                     if (length < distance){
918                         found = index;
919                         distance = length;
920                     }
921                 }
922                 index++;
923             }
924             return found;
925         }
926         cc.log("invalid focus direction!");
927         return 0;
928     },
929 
930     /**
931      * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child
932      * will get the focus.  The current algorithm to determine which child will get focus is farthest-distance-priority algorithm
933      * @param {Number} direction next focused widget direction
934      * @param {ccui.Widget} baseWidget
935      * @returns {Number} The index of child widget in the container
936      * @private
937      */
938     _findFarthestChildWidgetIndex: function(direction, baseWidget){
939         if (baseWidget == null || baseWidget === this)
940             return this._findFirstFocusEnabledWidgetIndex();
941 
942         var index = 0, locChildren = this.getChildren();
943         var count = locChildren.length;
944 
945         var distance = -cc.FLT_MAX, found = 0;
946         if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT || direction === ccui.Widget.DOWN || direction === ccui.Widget.UP) {
947             var widgetPosition =  this._getWorldCenterPoint(baseWidget);
948             while (index <  count) {
949                 var w = locChildren[index];
950                 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) {
951                     var length = (w instanceof ccui.Layout)?w._calculateFarthestDistance(baseWidget)
952                         : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition));
953                     if (length > distance){
954                         found = index;
955                         distance = length;
956                     }
957                 }
958                 index++;
959             }
960             return  found;
961         }
962         cc.log("invalid focus direction!!!");
963         return 0;
964     },
965 
966     /**
967      * calculate the nearest distance between the baseWidget and the children of the layout
968      * @param {ccui.Widget} baseWidget the base widget which will be used to calculate the distance between the layout's children and itself
969      * @returns {Number} return the nearest distance between the baseWidget and the layout's children
970      * @private
971      */
972     _calculateNearestDistance: function(baseWidget){
973         var distance = cc.FLT_MAX;
974         var widgetPosition =  this._getWorldCenterPoint(baseWidget);
975         var locChildren = this._children;
976 
977         for (var i = 0, len = locChildren.length; i < len; i++) {
978             var widget = locChildren[i], length;
979             if (widget instanceof ccui.Layout)
980                 length = widget._calculateNearestDistance(baseWidget);
981             else {
982                 if (widget instanceof ccui.Widget && widget.isFocusEnabled())
983                     length = cc.pLength(cc.pSub(this._getWorldCenterPoint(widget), widgetPosition));
984                 else
985                     continue;
986             }
987             if (length < distance)
988                 distance = length;
989         }
990         return distance;
991     },
992 
993     /**
994      * calculate the farthest distance between the baseWidget and the children of the layout
995      * @param baseWidget
996      * @returns {number}
997      * @private
998      */
999     _calculateFarthestDistance:function(baseWidget){
1000         var distance = -cc.FLT_MAX;
1001         var widgetPosition =  this._getWorldCenterPoint(baseWidget);
1002         var locChildren = this._children;
1003 
1004         for (var i = 0, len = locChildren.length; i < len; i++) {
1005             var layout = locChildren[i];
1006             var length;
1007             if (layout instanceof ccui.Layout)
1008                 length = layout._calculateFarthestDistance(baseWidget);
1009             else {
1010                 if (layout instanceof ccui.Widget && layout.isFocusEnabled()) {
1011                     var wPosition = this._getWorldCenterPoint(layout);
1012                     length = cc.pLength(cc.pSub(wPosition, widgetPosition));
1013                 } else
1014                     continue;
1015             }
1016 
1017             if (length > distance)
1018                 distance = length;
1019         }
1020         return distance;
1021     },
1022 
1023     /**
1024      * 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
1025      * @param direction
1026      * @param baseWidget
1027      * @private
1028      */
1029     _findProperSearchingFunctor: function(direction, baseWidget){
1030         if (baseWidget === undefined)
1031             return;
1032 
1033         var previousWidgetPosition = this._getWorldCenterPoint(baseWidget);
1034         var widgetPosition = this._getWorldCenterPoint(this._findFirstNonLayoutWidget());
1035         if (direction === ccui.Widget.LEFT) {
1036             this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findNearestChildWidgetIndex
1037                 : this._findFarthestChildWidgetIndex;
1038         } else if (direction === ccui.Widget.RIGHT) {
1039             this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findFarthestChildWidgetIndex
1040                 : this._findNearestChildWidgetIndex;
1041         }else if(direction === ccui.Widget.DOWN) {
1042             this.onPassFocusToChild = (previousWidgetPosition.y > widgetPosition.y) ? this._findNearestChildWidgetIndex
1043                 : this._findFarthestChildWidgetIndex;
1044         }else if(direction === ccui.Widget.UP) {
1045             this.onPassFocusToChild = (previousWidgetPosition.y < widgetPosition.y) ? this._findNearestChildWidgetIndex
1046                 : this._findFarthestChildWidgetIndex;
1047         }else
1048             cc.log("invalid direction!");
1049     },
1050 
1051     /**
1052      * find the first non-layout widget in this layout
1053      * @returns {ccui.Widget}
1054      * @private
1055      */
1056     _findFirstNonLayoutWidget:function(){
1057         var locChildren = this._children;
1058         for(var i = 0, len = locChildren.length; i < len; i++) {
1059             var child = locChildren[i];
1060             if (child instanceof ccui.Layout){
1061                 var widget = child._findFirstNonLayoutWidget();
1062                 if(widget)
1063                     return widget;
1064             } else{
1065                 if (child instanceof ccui.Widget)
1066                     return child;
1067             }
1068         }
1069         return null;
1070     },
1071 
1072     /**
1073      * find the first focus enabled widget index in the layout, it will recursive searching the child widget
1074      * @returns {number}
1075      * @private
1076      */
1077     _findFirstFocusEnabledWidgetIndex: function(){
1078         var index = 0, locChildren = this.getChildren();
1079         var count = locChildren.length;
1080         while (index < count) {
1081             var w = locChildren[index];
1082             if (w && w instanceof ccui.Widget && w.isFocusEnabled())
1083                 return index;
1084             index++;
1085         }
1086         return 0;
1087     },
1088 
1089     /**
1090      * find a focus enabled child Widget in the layout by index
1091      * @param index
1092      * @returns {*}
1093      * @private
1094      */
1095     _findFocusEnabledChildWidgetByIndex: function(index){
1096         var widget = this._getChildWidgetByIndex(index);
1097         if (widget){
1098             if (widget.isFocusEnabled())
1099                 return widget;
1100             index = index + 1;
1101             return this._findFocusEnabledChildWidgetByIndex(index);
1102         }
1103         return null;
1104     },
1105 
1106     /**
1107      * get the center point of a widget in world space
1108      * @param {ccui.Widget} widget
1109      * @returns {cc.Point}
1110      * @private
1111      */
1112     _getWorldCenterPoint: function(widget){
1113         //FIXEDME: we don't need to calculate the content size of layout anymore
1114         var widgetSize = widget instanceof ccui.Layout ? widget._getLayoutAccumulatedSize() :  widget.getContentSize();
1115         return widget.convertToWorldSpace(cc.p(widgetSize.width /2, widgetSize.height /2));
1116     },
1117 
1118     /**
1119      * this method is called internally by nextFocusedWidget. When the dir is Right/Down, then this method will be called
1120      * @param {Number} direction
1121      * @param {ccui.Widget} current the current focused widget
1122      * @returns {ccui.Widget} the next focused widget
1123      * @private
1124      */
1125     _getNextFocusedWidget: function(direction, current){
1126         var nextWidget = null, locChildren = this._children;
1127         var  previousWidgetPos = locChildren.indexOf(current);
1128         previousWidgetPos = previousWidgetPos + 1;
1129         if (previousWidgetPos < locChildren.length) {
1130             nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1131             //handle widget
1132             if (nextWidget) {
1133                 if (nextWidget.isFocusEnabled()) {
1134                     if (nextWidget instanceof ccui.Layout) {
1135                         nextWidget._isFocusPassing = true;
1136                         return nextWidget.findNextFocusedWidget(direction, nextWidget);
1137                     } else {
1138                         this.dispatchFocusEvent(current, nextWidget);
1139                         return nextWidget;
1140                     }
1141                 } else
1142                     return this._getNextFocusedWidget(direction, nextWidget);
1143             } else
1144                 return current;
1145         } else {
1146             if (this._loopFocus) {
1147                 if (this._checkFocusEnabledChild()) {
1148                     previousWidgetPos = 0;
1149                     nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1150                     if (nextWidget.isFocusEnabled()) {
1151                         if (nextWidget instanceof ccui.Layout) {
1152                             nextWidget._isFocusPassing = true;
1153                             return nextWidget.findNextFocusedWidget(direction, nextWidget);
1154                         } else {
1155                             this.dispatchFocusEvent(current, nextWidget);
1156                             return nextWidget;
1157                         }
1158                     } else
1159                         return this._getNextFocusedWidget(direction, nextWidget);
1160                 } else
1161                     return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget;
1162             } else{
1163                 if (this._isLastWidgetInContainer(current, direction)){
1164                     if (this._isWidgetAncestorSupportLoopFocus(this, direction))
1165                         return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
1166                     return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget;
1167                 } else
1168                     return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
1169             }
1170         }
1171     },
1172 
1173     /**
1174      * this method is called internally by nextFocusedWidget. When the dir is Left/Up, then this method will be called
1175      * @param direction
1176      * @param {ccui.Widget} current the current focused widget
1177      * @returns {ccui.Widget} the next focused widget
1178      * @private
1179      */
1180     _getPreviousFocusedWidget: function(direction, current){
1181         var nextWidget = null, locChildren = this._children;
1182         var previousWidgetPos = locChildren.indexOf(current);
1183         previousWidgetPos = previousWidgetPos - 1;
1184         if (previousWidgetPos >= 0){
1185             nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1186             if (nextWidget.isFocusEnabled()) {
1187                 if (nextWidget instanceof ccui.Layout){
1188                     nextWidget._isFocusPassing = true;
1189                     return nextWidget.findNextFocusedWidget(direction, nextWidget);
1190                 }
1191                 this.dispatchFocusEvent(current, nextWidget);
1192                 return nextWidget;
1193             } else
1194                 return this._getPreviousFocusedWidget(direction, nextWidget);       //handling the disabled widget, there is no actual focus lose or get, so we don't need any envet
1195         }else {
1196             if (this._loopFocus){
1197                 if (this._checkFocusEnabledChild()) {
1198                     previousWidgetPos = locChildren.length -1;
1199                     nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1200                     if (nextWidget.isFocusEnabled()){
1201                         if (nextWidget instanceof ccui.Layout){
1202                             nextWidget._isFocusPassing = true;
1203                             return nextWidget.findNextFocusedWidget(direction, nextWidget);
1204                         } else {
1205                             this.dispatchFocusEvent(current, nextWidget);
1206                             return nextWidget;
1207                         }
1208                     } else
1209                         return this._getPreviousFocusedWidget(direction, nextWidget);
1210                 } else
1211                     return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget;
1212             } else {
1213                 if (this._isLastWidgetInContainer(current, direction)) {
1214                     if (this._isWidgetAncestorSupportLoopFocus(this, direction))
1215                         return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
1216                     return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget;
1217                 } else
1218                     return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
1219             }
1220         }
1221     },
1222 
1223     /**
1224      * find the nth element in the _children array. Only the Widget descendant object will be returned
1225      * @param {Number} index
1226      * @returns {ccui.Widget}
1227      * @private
1228      */
1229     _getChildWidgetByIndex: function (index) {
1230         var locChildren = this._children;
1231         var size = locChildren.length, count = 0, oldIndex = index;
1232         while (index < size) {
1233             var firstChild = locChildren[index];
1234             if (firstChild && firstChild instanceof ccui.Widget)
1235                 return firstChild;
1236             count++;
1237             index++;
1238         }
1239 
1240         var begin = 0;
1241         while (begin < oldIndex) {
1242             var child = locChildren[begin];
1243             if (child && child instanceof ccui.Widget)
1244                 return child;
1245             count++;
1246             begin++;
1247         }
1248         return null;
1249     },
1250 
1251     /**
1252      * whether it is the last element according to all their parents
1253      * @param {ccui.Widget} widget
1254      * @param {Number} direction
1255      * @returns {Boolean}
1256      * @private
1257      */
1258     _isLastWidgetInContainer:function(widget, direction){
1259         var parent = widget.getParent();
1260         if (parent == null || !(parent instanceof ccui.Layout))
1261             return true;
1262 
1263         var container = parent.getChildren();
1264         var index = container.indexOf(widget);
1265         if (parent.getLayoutType() === ccui.Layout.LINEAR_HORIZONTAL) {
1266             if (direction === ccui.Widget.LEFT) {
1267                 if (index === 0)
1268                     return this._isLastWidgetInContainer(parent, direction);
1269                 else
1270                     return false;
1271             }
1272             if (direction === ccui.Widget.RIGHT) {
1273                 if (index === container.length - 1)
1274                     return this._isLastWidgetInContainer(parent, direction);
1275                 else
1276                     return false;
1277             }
1278             if (direction === ccui.Widget.DOWN)
1279                 return this._isLastWidgetInContainer(parent, direction);
1280 
1281             if (direction === ccui.Widget.UP)
1282                 return this._isLastWidgetInContainer(parent, direction);
1283         } else if(parent.getLayoutType() === ccui.Layout.LINEAR_VERTICAL){
1284             if (direction === ccui.Widget.UP){
1285                 if (index === 0)
1286                     return this._isLastWidgetInContainer(parent, direction);
1287                 else
1288                     return false;
1289             }
1290             if (direction === ccui.Widget.DOWN) {
1291                 if (index === container.length - 1)
1292                     return this._isLastWidgetInContainer(parent, direction);
1293                 else
1294                     return false;
1295             }
1296             if (direction === ccui.Widget.LEFT)
1297                 return this._isLastWidgetInContainer(parent, direction);
1298 
1299             if (direction === ccui.Widget.RIGHT)
1300                 return this._isLastWidgetInContainer(parent, direction);
1301         } else {
1302             cc.log("invalid layout Type");
1303             return false;
1304         }
1305     },
1306 
1307     /**
1308      * Lookup any parent widget with a layout type as the direction, if the layout is loop focused, then return true, otherwise it returns false.
1309      * @param {ccui.Widget} widget
1310      * @param {Number} direction
1311      * @returns {Boolean}
1312      * @private
1313      */
1314     _isWidgetAncestorSupportLoopFocus: function(widget, direction){
1315         var parent = widget.getParent();
1316         if (parent == null || !(parent instanceof ccui.Layout))
1317             return false;
1318         if (parent.isLoopFocus()) {
1319             var layoutType = parent.getLayoutType();
1320             if (layoutType === ccui.Layout.LINEAR_HORIZONTAL) {
1321                 if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT)
1322                     return true;
1323                 else
1324                     return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1325             }
1326             if (layoutType === ccui.Layout.LINEAR_VERTICAL){
1327                 if (direction === ccui.Widget.DOWN || direction === ccui.Widget.UP)
1328                     return true;
1329                 else
1330                     return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1331             } else{
1332                 cc.assert(0, "invalid layout type");
1333                 return false;
1334             }
1335         } else
1336             return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1337     },
1338 
1339     /**
1340      * pass the focus to the layout's next focus enabled child
1341      * @param {Number} direction
1342      * @param {ccui.Widget} current
1343      * @returns {ccui.Widget}
1344      * @private
1345      */
1346     _passFocusToChild: function(direction, current){
1347         if (this._checkFocusEnabledChild()) {
1348             var previousWidget = ccui.Widget.getCurrentFocusedWidget();
1349             this._findProperSearchingFunctor(direction, previousWidget);
1350             var index = this.onPassFocusToChild(direction, previousWidget);
1351 
1352             var widget = this._getChildWidgetByIndex(index);
1353             if (widget instanceof ccui.Layout) {
1354                 widget._isFocusPassing = true;
1355                 return widget.findNextFocusedWidget(direction, widget);
1356             } else {
1357                 this.dispatchFocusEvent(current, widget);
1358                 return widget;
1359             }
1360         }else
1361             return this;
1362     },
1363 
1364     /**
1365      * If there are no focus enabled child in the layout, it will return false, otherwise it returns true
1366      * @returns {boolean}
1367      * @private
1368      */
1369     _checkFocusEnabledChild: function(){
1370         var locChildren = this._children;
1371         for(var i = 0, len = locChildren.length; i < len; i++){
1372             var widget = locChildren[i];
1373             if (widget && widget instanceof ccui.Widget && widget.isFocusEnabled())
1374                 return true;
1375         }
1376         return false;
1377     },
1378 
1379     /**
1380      * Returns the "class name" of widget.
1381      * @returns {string}
1382      */
1383     getDescription: function () {
1384         return "Layout";
1385     },
1386 
1387     _createCloneInstance: function () {
1388         return new ccui.Layout();
1389     },
1390 
1391     _copyClonedWidgetChildren: function (model) {
1392         ccui.Widget.prototype._copyClonedWidgetChildren.call(this, model);
1393     },
1394 
1395     _copySpecialProperties: function (layout) {
1396         if(!(layout instanceof  ccui.Layout))
1397             return;
1398         this.setBackGroundImageScale9Enabled(layout._backGroundScale9Enabled);
1399         this.setBackGroundImage(layout._backGroundImageFileName, layout._bgImageTexType);
1400         this.setBackGroundImageCapInsets(layout._backGroundImageCapInsets);
1401         this.setBackGroundColorType(layout._colorType);
1402         this.setBackGroundColor(layout._color);
1403         this.setBackGroundColor(layout._startColor, layout._endColor);
1404         this.setBackGroundColorOpacity(layout._opacity);
1405         this.setBackGroundColorVector(layout._alongVector);
1406         this.setLayoutType(layout._layoutType);
1407         this.setClippingEnabled(layout._clippingEnabled);
1408         this.setClippingType(layout._clippingType);
1409         this._loopFocus = layout._loopFocus;
1410         this.__passFocusToChild = layout.__passFocusToChild;
1411         this._isInterceptTouch = layout._isInterceptTouch;
1412     },
1413 
1414     /**
1415      * force refresh widget layout
1416      */
1417     forceDoLayout: function(){
1418         this.requestDoLayout();
1419         this._doLayout();
1420     },
1421 
1422     _createRenderCmd: function(){
1423         if(cc._renderType === cc.game.RENDER_TYPE_WEBGL)
1424             return new ccui.Layout.WebGLRenderCmd(this);
1425         else
1426             return new ccui.Layout.CanvasRenderCmd(this);
1427     }
1428 });
1429 
1430 var _p = ccui.Layout.prototype;
1431 
1432 // Extended properties
1433 /** @expose */
1434 _p.clippingEnabled;
1435 cc.defineGetterSetter(_p, "clippingEnabled", _p.isClippingEnabled, _p.setClippingEnabled);
1436 /** @expose */
1437 _p.clippingType;
1438 cc.defineGetterSetter(_p, "clippingType", null, _p.setClippingType);
1439 /** @expose */
1440 _p.layoutType;
1441 cc.defineGetterSetter(_p, "layoutType", _p.getLayoutType, _p.setLayoutType);
1442 
1443 _p = null;
1444 
1445 /**
1446  * allocates and initializes a UILayout.
1447  * @deprecated since v3.0, please use new ccui.Layout() instead.
1448  * @return {ccui.Layout}
1449  */
1450 ccui.Layout.create = function () {
1451     return new ccui.Layout();
1452 };
1453 
1454 // Constants
1455 
1456 //layoutBackGround color type
1457 /**
1458  * The None of ccui.Layout's background color type
1459  * @constant
1460  * @type {number}
1461  */
1462 ccui.Layout.BG_COLOR_NONE = 0;
1463 /**
1464  * The solid of ccui.Layout's background color type, it will use a LayerColor to draw the background.
1465  * @constant
1466  * @type {number}
1467  */
1468 ccui.Layout.BG_COLOR_SOLID = 1;
1469 /**
1470  * The gradient of ccui.Layout's background color type, it will use a LayerGradient to draw the background.
1471  * @constant
1472  * @type {number}
1473  */
1474 ccui.Layout.BG_COLOR_GRADIENT = 2;
1475 
1476 //Layout type
1477 /**
1478  * The absolute of ccui.Layout's layout type.
1479  * @type {number}
1480  * @constant
1481  */
1482 ccui.Layout.ABSOLUTE = 0;
1483 /**
1484  * The vertical of ccui.Layout's layout type.
1485  * @type {number}
1486  * @constant
1487  */
1488 ccui.Layout.LINEAR_VERTICAL = 1;
1489 /**
1490  * The horizontal of ccui.Layout's layout type.
1491  * @type {number}
1492  * @constant
1493  */
1494 ccui.Layout.LINEAR_HORIZONTAL = 2;
1495 /**
1496  * The relative of ccui.Layout's layout type.
1497  * @type {number}
1498  * @constant
1499  */
1500 ccui.Layout.RELATIVE = 3;
1501 
1502 //Layout clipping type
1503 /**
1504  * The stencil of ccui.Layout's clipping type.
1505  * @type {number}
1506  * @constant
1507  */
1508 ccui.Layout.CLIPPING_STENCIL = 0;
1509 /**
1510  * The scissor of ccui.Layout's clipping type.
1511  * @type {number}
1512  * @constant
1513  */
1514 ccui.Layout.CLIPPING_SCISSOR = 1;
1515 
1516 /**
1517  * The zOrder value of ccui.Layout's image background.
1518  * @type {number}
1519  * @constant
1520  */
1521 ccui.Layout.BACKGROUND_IMAGE_ZORDER = -1;
1522 /**
1523  * The zOrder value of ccui.Layout's color background.
1524  * @type {number}
1525  * @constant
1526  */
1527 ccui.Layout.BACKGROUND_RENDERER_ZORDER = -2;