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.drawPoly(rect, 4, green, 0, green);
388         }
389     },
390 
391     _getClippingRect: function () {
392         if (this._clippingRectDirty) {
393             var worldPos = this.convertToWorldSpace(cc.p(0, 0));
394             var t = this.getNodeToWorldTransform();
395             var scissorWidth = this._contentSize.width * t.a;
396             var scissorHeight = this._contentSize.height * t.d;
397             var parentClippingRect;
398             var parent = this;
399 
400             while (parent) {
401                 parent = parent.getParent();
402                 if (parent && parent instanceof ccui.Layout && parent.isClippingEnabled()) {
403                     this._clippingParent = parent;
404                     break;
405                 }
406             }
407 
408             if (this._clippingParent) {
409                 parentClippingRect = this._clippingParent._getClippingRect();
410 
411                 this._clippingRect.x = Math.max(worldPos.x, parentClippingRect.x);
412                 this._clippingRect.y = Math.max(worldPos.y, parentClippingRect.y);
413 
414                 var right = Math.min(worldPos.x + scissorWidth, parentClippingRect.x + parentClippingRect.width);
415                 var top = Math.min(worldPos.y + scissorHeight, parentClippingRect.y + parentClippingRect.height);
416 
417                 this._clippingRect.width = Math.max(0.0, right -  this._clippingRect.x);
418                 this._clippingRect.height = Math.max(0.0, top -  this._clippingRect.y);
419             } else {
420                 this._clippingRect.x = worldPos.x;
421                 this._clippingRect.y = worldPos.y;
422                 this._clippingRect.width = scissorWidth;
423                 this._clippingRect.height = scissorHeight;
424             }
425             this._clippingRectDirty = false;
426         }
427         return this._clippingRect;
428     },
429 
430     _onSizeChanged: function () {
431         ccui.Widget.prototype._onSizeChanged.call(this);
432         var locContentSize = this._contentSize;
433         this._setStencilClippingSize(locContentSize);
434         this._doLayoutDirty = true;
435         this._clippingRectDirty = true;
436         if (this._backGroundImage) {
437             this._backGroundImage.setPosition(locContentSize.width * 0.5, locContentSize.height * 0.5);
438             if (this._backGroundScale9Enabled && this._backGroundImage instanceof ccui.Scale9Sprite)
439                 this._backGroundImage.setPreferredSize(locContentSize);
440         }
441         if (this._colorRender)
442             this._colorRender.setContentSize(locContentSize);
443         if (this._gradientRender)
444             this._gradientRender.setContentSize(locContentSize);
445     },
446 
447     /**
448      * Sets background image use scale9 renderer.
449      * @param {Boolean} able  true that use scale9 renderer, false otherwise.
450      */
451     setBackGroundImageScale9Enabled: function (able) {
452         if (this._backGroundScale9Enabled === able)
453             return;
454         this.removeProtectedChild(this._backGroundImage);
455         this._backGroundImage = null;
456         this._backGroundScale9Enabled = able;
457         this._addBackGroundImage();
458         this.setBackGroundImage(this._backGroundImageFileName, this._bgImageTexType);
459         this.setBackGroundImageCapInsets(this._backGroundImageCapInsets);
460     },
461 
462     /**
463      * Get whether background image is use scale9 renderer.
464      * @returns {Boolean}
465      */
466     isBackGroundImageScale9Enabled: function () {
467         return this._backGroundScale9Enabled;
468     },
469 
470     /**
471      * Sets a background image for layout
472      * @param {String} fileName
473      * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType
474      */
475     setBackGroundImage: function (fileName, texType) {
476         if (!fileName)
477             return;
478         texType = texType || ccui.Widget.LOCAL_TEXTURE;
479         if (this._backGroundImage === null){
480             this._addBackGroundImage();
481             this.setBackGroundImageScale9Enabled(this._backGroundScale9Enabled);
482         }
483         this._backGroundImageFileName = fileName;
484         this._bgImageTexType = texType;
485         var locBackgroundImage = this._backGroundImage;
486         switch (this._bgImageTexType) {
487             case ccui.Widget.LOCAL_TEXTURE:
488                 locBackgroundImage.initWithFile(fileName);
489                 break;
490             case ccui.Widget.PLIST_TEXTURE:
491                 locBackgroundImage.initWithSpriteFrameName(fileName);
492                 break;
493             default:
494                 break;
495         }
496         if (this._backGroundScale9Enabled)
497             locBackgroundImage.setPreferredSize(this._contentSize);
498 
499         this._backGroundImageTextureSize = locBackgroundImage.getContentSize();
500         locBackgroundImage.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5);
501         this._updateBackGroundImageColor();
502     },
503 
504     /**
505      * Sets a background image CapInsets for layout, if the background image is a scale9 render.
506      * @param {cc.Rect} capInsets capinsets of background image.
507      */
508     setBackGroundImageCapInsets: function (capInsets) {
509         if(!capInsets)
510             return;
511         var locInsets = this._backGroundImageCapInsets;
512         locInsets.x = capInsets.x;
513         locInsets.y = capInsets.y;
514         locInsets.width = capInsets.width;
515         locInsets.height = capInsets.height;
516         if (this._backGroundScale9Enabled)
517             this._backGroundImage.setCapInsets(capInsets);
518     },
519 
520     /**
521      * Gets background image capinsets of ccui.Layout.
522      * @returns {cc.Rect}
523      */
524     getBackGroundImageCapInsets: function () {
525         return cc.rect(this._backGroundImageCapInsets);
526     },
527 
528     _supplyTheLayoutParameterLackToChild: function (locChild) {
529         if (!locChild) {
530             return;
531         }
532         switch (this._layoutType) {
533             case ccui.Layout.ABSOLUTE:
534                 break;
535             case ccui.Layout.LINEAR_HORIZONTAL:
536             case ccui.Layout.LINEAR_VERTICAL:
537                 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.LINEAR);
538                 if (!layoutParameter)
539                     locChild.setLayoutParameter(new ccui.LinearLayoutParameter());
540                 break;
541             case ccui.Layout.RELATIVE:
542                 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.RELATIVE);
543                 if (!layoutParameter)
544                     locChild.setLayoutParameter(new ccui.RelativeLayoutParameter());
545                 break;
546             default:
547                 break;
548         }
549     },
550 
551     _addBackGroundImage: function () {
552         var contentSize = this._contentSize;
553         if (this._backGroundScale9Enabled) {
554             this._backGroundImage = new ccui.Scale9Sprite();
555             this._backGroundImage.setPreferredSize(contentSize);
556         } else
557             this._backGroundImage = new cc.Sprite();
558         this.addProtectedChild(this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1);
559         this._backGroundImage.setPosition(contentSize.width * 0.5, contentSize.height * 0.5);
560     },
561 
562     /**
563      * Remove the background image of ccui.Layout.
564      */
565     removeBackGroundImage: function () {
566         if (!this._backGroundImage)
567             return;
568         this.removeProtectedChild(this._backGroundImage);
569         this._backGroundImage = null;
570         this._backGroundImageFileName = "";
571         this._backGroundImageTextureSize.width = 0;
572         this._backGroundImageTextureSize.height = 0;
573     },
574 
575     /**
576      * Sets Color Type for ccui.Layout.
577      * @param {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} type
578      */
579     setBackGroundColorType: function (type) {
580         if (this._colorType === type)
581             return;
582         switch (this._colorType) {
583             case ccui.Layout.BG_COLOR_NONE:
584                 if (this._colorRender) {
585                     this.removeProtectedChild(this._colorRender);
586                     this._colorRender = null;
587                 }
588                 if (this._gradientRender) {
589                     this.removeProtectedChild(this._gradientRender);
590                     this._gradientRender = null;
591                 }
592                 break;
593             case ccui.Layout.BG_COLOR_SOLID:
594                 if (this._colorRender) {
595                     this.removeProtectedChild(this._colorRender);
596                     this._colorRender = null;
597                 }
598                 break;
599             case ccui.Layout.BG_COLOR_GRADIENT:
600                 if (this._gradientRender) {
601                     this.removeProtectedChild(this._gradientRender);
602                     this._gradientRender = null;
603                 }
604                 break;
605             default:
606                 break;
607         }
608         this._colorType = type;
609         switch (this._colorType) {
610             case ccui.Layout.BG_COLOR_NONE:
611                 break;
612             case ccui.Layout.BG_COLOR_SOLID:
613                 this._colorRender = new cc.LayerColor();
614                 this._colorRender.setContentSize(this._contentSize);
615                 this._colorRender.setOpacity(this._opacity);
616                 this._colorRender.setColor(this._color);
617                 this.addProtectedChild(this._colorRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1);
618                 break;
619             case ccui.Layout.BG_COLOR_GRADIENT:
620                 this._gradientRender = new cc.LayerGradient(cc.color(255, 0, 0, 255), cc.color(0, 255, 0, 255));
621                 this._gradientRender.setContentSize(this._contentSize);
622                 this._gradientRender.setOpacity(this._opacity);
623                 this._gradientRender.setStartColor(this._startColor);
624                 this._gradientRender.setEndColor(this._endColor);
625                 this._gradientRender.setVector(this._alongVector);
626                 this.addProtectedChild(this._gradientRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1);
627                 break;
628             default:
629                 break;
630         }
631     },
632 
633     /**
634      * Get background color type of ccui.Layout.
635      * @returns {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT}
636      */
637     getBackGroundColorType: function () {
638         return this._colorType;
639     },
640 
641     /**
642      * Sets background color for layout, if color type is Layout.COLOR_SOLID
643      * @param {cc.Color} color
644      * @param {cc.Color} [endColor]
645      */
646     setBackGroundColor: function (color, endColor) {
647         if (!endColor) {
648             this._color.r = color.r;
649             this._color.g = color.g;
650             this._color.b = color.b;
651             if (this._colorRender)
652                 this._colorRender.setColor(color);
653         } else {
654             this._startColor.r = color.r;
655             this._startColor.g = color.g;
656             this._startColor.b = color.b;
657             if (this._gradientRender)
658                 this._gradientRender.setStartColor(color);
659 
660             this._endColor.r = endColor.r;
661             this._endColor.g = endColor.g;
662             this._endColor.b = endColor.b;
663             if (this._gradientRender)
664                 this._gradientRender.setEndColor(endColor);
665         }
666     },
667 
668     /**
669      * Gets background color of ccui.Layout, if color type is Layout.COLOR_SOLID.
670      * @returns {cc.Color}
671      */
672     getBackGroundColor: function () {
673         var tmpColor = this._color;
674         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
675     },
676 
677     /**
678      * Gets background start color of ccui.Layout
679      * @returns {cc.Color}
680      */
681     getBackGroundStartColor: function () {
682         var tmpColor = this._startColor;
683         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
684     },
685 
686     /**
687      * Gets background end color of ccui.Layout
688      * @returns {cc.Color}
689      */
690     getBackGroundEndColor: function () {
691         var tmpColor = this._endColor;
692         return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a);
693     },
694 
695     /**
696      * Sets background opacity to ccui.Layout.
697      * @param {number} opacity
698      */
699     setBackGroundColorOpacity: function (opacity) {
700         this._opacity = opacity;
701         switch (this._colorType) {
702             case ccui.Layout.BG_COLOR_NONE:
703                 break;
704             case ccui.Layout.BG_COLOR_SOLID:
705                 this._colorRender.setOpacity(opacity);
706                 break;
707             case ccui.Layout.BG_COLOR_GRADIENT:
708                 this._gradientRender.setOpacity(opacity);
709                 break;
710             default:
711                 break;
712         }
713     },
714 
715     /**
716      * Get background opacity value of ccui.Layout.
717      * @returns {Number}
718      */
719     getBackGroundColorOpacity: function () {
720         return this._opacity;
721     },
722 
723     /**
724      * Sets background color vector for layout, if color type is Layout.COLOR_GRADIENT
725      * @param {cc.Point} vector
726      */
727     setBackGroundColorVector: function (vector) {
728         this._alongVector.x = vector.x;
729         this._alongVector.y = vector.y;
730         if (this._gradientRender) {
731             this._gradientRender.setVector(vector);
732         }
733     },
734 
735     /**
736      *  Gets background color vector of ccui.Layout, if color type is Layout.COLOR_GRADIENT
737      * @returns {cc.Point}
738      */
739     getBackGroundColorVector: function () {
740         return this._alongVector;
741     },
742 
743     /**
744      * Sets backGround image color
745      * @param {cc.Color} color
746      */
747     setBackGroundImageColor: function (color) {
748         this._backGroundImageColor.r = color.r;
749         this._backGroundImageColor.g = color.g;
750         this._backGroundImageColor.b = color.b;
751 
752         this._updateBackGroundImageColor();
753     },
754 
755     /**
756      * Sets backGround image Opacity
757      * @param {Number} opacity
758      */
759     setBackGroundImageOpacity: function (opacity) {
760         this._backGroundImageColor.a = opacity;
761         this.getBackGroundImageColor();
762     },
763 
764     /**
765      * Gets backGround image color
766      * @returns {cc.Color}
767      */
768     getBackGroundImageColor: function () {
769         var color = this._backGroundImageColor;
770         return cc.color(color.r, color.g, color.b, color.a);
771     },
772 
773     /**
774      * Gets backGround image opacity
775      * @returns {Number}
776      */
777     getBackGroundImageOpacity: function () {
778         return this._backGroundImageColor.a;
779     },
780 
781     _updateBackGroundImageColor: function () {
782         if(this._backGroundImage)
783             this._backGroundImage.setColor(this._backGroundImageColor);
784     },
785 
786     /**
787      * Gets background image texture size.
788      * @returns {cc.Size}
789      */
790     getBackGroundImageTextureSize: function () {
791         return this._backGroundImageTextureSize;
792     },
793 
794     /**
795      * Sets LayoutType to ccui.Layout, LayoutManager will do layout by layout type..
796      * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type
797      */
798     setLayoutType: function (type) {
799         this._layoutType = type;
800         var layoutChildrenArray = this._children;
801         var locChild = null;
802         for (var i = 0; i < layoutChildrenArray.length; i++) {
803             locChild = layoutChildrenArray[i];
804             if(locChild instanceof ccui.Widget)
805                 this._supplyTheLayoutParameterLackToChild(locChild);
806         }
807         this._doLayoutDirty = true;
808     },
809 
810     /**
811      * Gets LayoutType of ccui.Layout.
812      * @returns {null}
813      */
814     getLayoutType: function () {
815         return this._layoutType;
816     },
817 
818     /**
819      * request to refresh widget layout, it will do layout at visit calls
820      */
821     requestDoLayout: function () {
822         this._doLayoutDirty = true;
823     },
824 
825     _doLayout: function () {
826         if (!this._doLayoutDirty)
827             return;
828 
829         this.sortAllChildren();
830 
831         var executant = ccui.getLayoutManager(this._layoutType);
832         if (executant)
833             executant._doLayout(this);
834         this._doLayoutDirty = false;
835     },
836 
837     _getLayoutContentSize: function(){
838         return this.getContentSize();
839     },
840 
841     _getLayoutElements: function(){
842         return this.getChildren();
843     },
844 
845     _updateBackGroundImageOpacity: function(){
846         if (this._backGroundImage)
847             this._backGroundImage.setOpacity(this._backGroundImageOpacity);
848     },
849 
850     _updateBackGroundImageRGBA: function(){
851         if (this._backGroundImage) {
852             this._backGroundImage.setColor(this._backGroundImageColor);
853             this._backGroundImage.setOpacity(this._backGroundImageOpacity);
854         }
855     },
856 
857     /**
858      * Gets the content size of the layout, it will accumulate all its children's content size
859      * @returns {cc.Size}
860      * @private
861      */
862     _getLayoutAccumulatedSize: function(){
863         var children = this.getChildren();
864         var  layoutSize = cc.size(0, 0);
865         var widgetCount = 0, locSize;
866         for(var i = 0, len = children.length; i < len; i++) {
867             var layout = children[i];
868             if (null !== layout && layout instanceof ccui.Layout){
869                 locSize = layout._getLayoutAccumulatedSize();
870                 layoutSize.width += locSize.width;
871                 layoutSize.height += locSize.height;
872             } else {
873                 if (layout instanceof ccui.Widget) {
874                     widgetCount++;
875                     var m = layout.getLayoutParameter().getMargin();
876                     locSize = layout.getContentSize();
877                     layoutSize.width += locSize.width +  (m.right + m.left) * 0.5;
878                     layoutSize.height += locSize.height +  (m.top + m.bottom) * 0.5;
879                 }
880             }
881         }
882 
883         //substract extra size
884         var type = this.getLayoutType();
885         if (type === ccui.Layout.LINEAR_HORIZONTAL)
886             layoutSize.height = layoutSize.height - layoutSize.height/widgetCount * (widgetCount-1);
887 
888         if (type === ccui.Layout.LINEAR_VERTICAL)
889             layoutSize.width = layoutSize.width - layoutSize.width/widgetCount * (widgetCount-1);
890         return layoutSize;
891     },
892 
893     /**
894      * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child      <br/>
895      * will get the focus.  The current algorithm to determine which child will get focus is nearest-distance-priority algorithm
896      * @param {Number} direction next focused widget direction
897      * @param {ccui.Widget} baseWidget
898      * @returns {Number}
899      * @private
900      */
901     _findNearestChildWidgetIndex: function(direction, baseWidget){
902         if (baseWidget == null || baseWidget === this)
903             return this._findFirstFocusEnabledWidgetIndex();
904 
905         var index = 0, locChildren = this.getChildren();
906         var count = locChildren.length, widgetPosition;
907 
908         var distance = cc.FLT_MAX, found = 0;
909         if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT || direction === ccui.Widget.DOWN || direction === ccui.Widget.UP) {
910             widgetPosition = this._getWorldCenterPoint(baseWidget);
911             while (index < count) {
912                 var w = locChildren[index];
913                 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) {
914                     var length = (w instanceof ccui.Layout)? w._calculateNearestDistance(baseWidget)
915                         : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition));
916                     if (length < distance){
917                         found = index;
918                         distance = length;
919                     }
920                 }
921                 index++;
922             }
923             return found;
924         }
925         cc.log("invalid focus direction!");
926         return 0;
927     },
928 
929     /**
930      * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child
931      * will get the focus.  The current algorithm to determine which child will get focus is farthest-distance-priority algorithm
932      * @param {Number} direction next focused widget direction
933      * @param {ccui.Widget} baseWidget
934      * @returns {Number} The index of child widget in the container
935      * @private
936      */
937     _findFarthestChildWidgetIndex: function(direction, baseWidget){
938         if (baseWidget == null || baseWidget === this)
939             return this._findFirstFocusEnabledWidgetIndex();
940 
941         var index = 0, locChildren = this.getChildren();
942         var count = locChildren.length;
943 
944         var distance = -cc.FLT_MAX, found = 0;
945         if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT || direction === ccui.Widget.DOWN || direction === ccui.Widget.UP) {
946             var widgetPosition =  this._getWorldCenterPoint(baseWidget);
947             while (index <  count) {
948                 var w = locChildren[index];
949                 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) {
950                     var length = (w instanceof ccui.Layout)?w._calculateFarthestDistance(baseWidget)
951                         : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition));
952                     if (length > distance){
953                         found = index;
954                         distance = length;
955                     }
956                 }
957                 index++;
958             }
959             return  found;
960         }
961         cc.log("invalid focus direction!!!");
962         return 0;
963     },
964 
965     /**
966      * calculate the nearest distance between the baseWidget and the children of the layout
967      * @param {ccui.Widget} baseWidget the base widget which will be used to calculate the distance between the layout's children and itself
968      * @returns {Number} return the nearest distance between the baseWidget and the layout's children
969      * @private
970      */
971     _calculateNearestDistance: function(baseWidget){
972         var distance = cc.FLT_MAX;
973         var widgetPosition =  this._getWorldCenterPoint(baseWidget);
974         var locChildren = this._children;
975 
976         for (var i = 0, len = locChildren.length; i < len; i++) {
977             var widget = locChildren[i], length;
978             if (widget instanceof ccui.Layout)
979                 length = widget._calculateNearestDistance(baseWidget);
980             else {
981                 if (widget instanceof ccui.Widget && widget.isFocusEnabled())
982                     length = cc.pLength(cc.pSub(this._getWorldCenterPoint(widget), widgetPosition));
983                 else
984                     continue;
985             }
986             if (length < distance)
987                 distance = length;
988         }
989         return distance;
990     },
991 
992     /**
993      * calculate the farthest distance between the baseWidget and the children of the layout
994      * @param baseWidget
995      * @returns {number}
996      * @private
997      */
998     _calculateFarthestDistance:function(baseWidget){
999         var distance = -cc.FLT_MAX;
1000         var widgetPosition =  this._getWorldCenterPoint(baseWidget);
1001         var locChildren = this._children;
1002 
1003         for (var i = 0, len = locChildren.length; i < len; i++) {
1004             var layout = locChildren[i];
1005             var length;
1006             if (layout instanceof ccui.Layout)
1007                 length = layout._calculateFarthestDistance(baseWidget);
1008             else {
1009                 if (layout instanceof ccui.Widget && layout.isFocusEnabled()) {
1010                     var wPosition = this._getWorldCenterPoint(layout);
1011                     length = cc.pLength(cc.pSub(wPosition, widgetPosition));
1012                 } else
1013                     continue;
1014             }
1015 
1016             if (length > distance)
1017                 distance = length;
1018         }
1019         return distance;
1020     },
1021 
1022     /**
1023      * 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
1024      * @param direction
1025      * @param baseWidget
1026      * @private
1027      */
1028     _findProperSearchingFunctor: function(direction, baseWidget){
1029         if (baseWidget == null)
1030             return;
1031 
1032         var previousWidgetPosition = this._getWorldCenterPoint(baseWidget);
1033         var widgetPosition = this._getWorldCenterPoint(this._findFirstNonLayoutWidget());
1034         if (direction === ccui.Widget.LEFT) {
1035             this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findNearestChildWidgetIndex.bind(this)
1036                 : this._findFarthestChildWidgetIndex.bind(this);
1037         } else if (direction === ccui.Widget.RIGHT) {
1038             this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findFarthestChildWidgetIndex.bind(this)
1039                 : this._findNearestChildWidgetIndex.bind(this);
1040         }else if(direction === ccui.Widget.DOWN) {
1041             this.onPassFocusToChild = (previousWidgetPosition.y > widgetPosition.y) ? this._findNearestChildWidgetIndex.bind(this)
1042                 : this._findFarthestChildWidgetIndex.bind(this);
1043         }else if(direction === ccui.Widget.UP) {
1044             this.onPassFocusToChild = (previousWidgetPosition.y < widgetPosition.y) ? this._findNearestChildWidgetIndex.bind(this)
1045                 : this._findFarthestChildWidgetIndex.bind(this);
1046         }else
1047             cc.log("invalid direction!");
1048     },
1049 
1050     /**
1051      * find the first non-layout widget in this layout
1052      * @returns {ccui.Widget}
1053      * @private
1054      */
1055     _findFirstNonLayoutWidget:function(){
1056         var locChildren = this._children;
1057         for(var i = 0, len = locChildren.length; i < len; i++) {
1058             var child = locChildren[i];
1059             if (child instanceof ccui.Layout){
1060                 var widget = child._findFirstNonLayoutWidget();
1061                 if(widget)
1062                     return widget;
1063             } else{
1064                 if (child instanceof ccui.Widget)
1065                     return child;
1066             }
1067         }
1068         return null;
1069     },
1070 
1071     /**
1072      * find the first focus enabled widget index in the layout, it will recursive searching the child widget
1073      * @returns {number}
1074      * @private
1075      */
1076     _findFirstFocusEnabledWidgetIndex: function(){
1077         var index = 0, locChildren = this.getChildren();
1078         var count = locChildren.length;
1079         while (index < count) {
1080             var w = locChildren[index];
1081             if (w && w instanceof ccui.Widget && w.isFocusEnabled())
1082                 return index;
1083             index++;
1084         }
1085         return 0;
1086     },
1087 
1088     /**
1089      * find a focus enabled child Widget in the layout by index
1090      * @param index
1091      * @returns {*}
1092      * @private
1093      */
1094     _findFocusEnabledChildWidgetByIndex: function(index){
1095         var widget = this._getChildWidgetByIndex(index);
1096         if (widget){
1097             if (widget.isFocusEnabled())
1098                 return widget;
1099             index = index + 1;
1100             return this._findFocusEnabledChildWidgetByIndex(index);
1101         }
1102         return null;
1103     },
1104 
1105     /**
1106      * get the center point of a widget in world space
1107      * @param {ccui.Widget} widget
1108      * @returns {cc.Point}
1109      * @private
1110      */
1111     _getWorldCenterPoint: function(widget){
1112         //FIXEDME: we don't need to calculate the content size of layout anymore
1113         var widgetSize = widget instanceof ccui.Layout ? widget._getLayoutAccumulatedSize() :  widget.getContentSize();
1114         return widget.convertToWorldSpace(cc.p(widgetSize.width /2, widgetSize.height /2));
1115     },
1116 
1117     /**
1118      * this method is called internally by nextFocusedWidget. When the dir is Right/Down, then this method will be called
1119      * @param {Number} direction
1120      * @param {ccui.Widget} current the current focused widget
1121      * @returns {ccui.Widget} the next focused widget
1122      * @private
1123      */
1124     _getNextFocusedWidget: function(direction, current){
1125         var nextWidget = null, locChildren = this._children;
1126         var  previousWidgetPos = locChildren.indexOf(current);
1127         previousWidgetPos = previousWidgetPos + 1;
1128         if (previousWidgetPos < locChildren.length) {
1129             nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1130             //handle widget
1131             if (nextWidget) {
1132                 if (nextWidget.isFocusEnabled()) {
1133                     if (nextWidget instanceof ccui.Layout) {
1134                         nextWidget._isFocusPassing = true;
1135                         return nextWidget.findNextFocusedWidget(direction, nextWidget);
1136                     } else {
1137                         this.dispatchFocusEvent(current, nextWidget);
1138                         return nextWidget;
1139                     }
1140                 } else
1141                     return this._getNextFocusedWidget(direction, nextWidget);
1142             } else
1143                 return current;
1144         } else {
1145             if (this._loopFocus) {
1146                 if (this._checkFocusEnabledChild()) {
1147                     previousWidgetPos = 0;
1148                     nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1149                     if (nextWidget.isFocusEnabled()) {
1150                         if (nextWidget instanceof ccui.Layout) {
1151                             nextWidget._isFocusPassing = true;
1152                             return nextWidget.findNextFocusedWidget(direction, nextWidget);
1153                         } else {
1154                             this.dispatchFocusEvent(current, nextWidget);
1155                             return nextWidget;
1156                         }
1157                     } else
1158                         return this._getNextFocusedWidget(direction, nextWidget);
1159                 } else
1160                     return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget;
1161             } else{
1162                 if (this._isLastWidgetInContainer(current, direction)){
1163                     if (this._isWidgetAncestorSupportLoopFocus(this, direction))
1164                         return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
1165                     return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget;
1166                 } else
1167                     return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
1168             }
1169         }
1170     },
1171 
1172     /**
1173      * this method is called internally by nextFocusedWidget. When the dir is Left/Up, then this method will be called
1174      * @param direction
1175      * @param {ccui.Widget} current the current focused widget
1176      * @returns {ccui.Widget} the next focused widget
1177      * @private
1178      */
1179     _getPreviousFocusedWidget: function(direction, current){
1180         var nextWidget = null, locChildren = this._children;
1181         var previousWidgetPos = locChildren.indexOf(current);
1182         previousWidgetPos = previousWidgetPos - 1;
1183         if (previousWidgetPos >= 0){
1184             nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1185             if (nextWidget.isFocusEnabled()) {
1186                 if (nextWidget instanceof ccui.Layout){
1187                     nextWidget._isFocusPassing = true;
1188                     return nextWidget.findNextFocusedWidget(direction, nextWidget);
1189                 }
1190                 this.dispatchFocusEvent(current, nextWidget);
1191                 return nextWidget;
1192             } else
1193                 return this._getPreviousFocusedWidget(direction, nextWidget);       //handling the disabled widget, there is no actual focus lose or get, so we don't need any envet
1194         }else {
1195             if (this._loopFocus){
1196                 if (this._checkFocusEnabledChild()) {
1197                     previousWidgetPos = locChildren.length -1;
1198                     nextWidget = this._getChildWidgetByIndex(previousWidgetPos);
1199                     if (nextWidget.isFocusEnabled()){
1200                         if (nextWidget instanceof ccui.Layout){
1201                             nextWidget._isFocusPassing = true;
1202                             return nextWidget.findNextFocusedWidget(direction, nextWidget);
1203                         } else {
1204                             this.dispatchFocusEvent(current, nextWidget);
1205                             return nextWidget;
1206                         }
1207                     } else
1208                         return this._getPreviousFocusedWidget(direction, nextWidget);
1209                 } else
1210                     return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget;
1211             } else {
1212                 if (this._isLastWidgetInContainer(current, direction)) {
1213                     if (this._isWidgetAncestorSupportLoopFocus(this, direction))
1214                         return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
1215                     return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget;
1216                 } else
1217                     return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this);
1218             }
1219         }
1220     },
1221 
1222     /**
1223      * find the nth element in the _children array. Only the Widget descendant object will be returned
1224      * @param {Number} index
1225      * @returns {ccui.Widget}
1226      * @private
1227      */
1228     _getChildWidgetByIndex: function (index) {
1229         var locChildren = this._children;
1230         var size = locChildren.length, count = 0, oldIndex = index;
1231         while (index < size) {
1232             var firstChild = locChildren[index];
1233             if (firstChild && firstChild instanceof ccui.Widget)
1234                 return firstChild;
1235             count++;
1236             index++;
1237         }
1238 
1239         var begin = 0;
1240         while (begin < oldIndex) {
1241             var child = locChildren[begin];
1242             if (child && child instanceof ccui.Widget)
1243                 return child;
1244             count++;
1245             begin++;
1246         }
1247         return null;
1248     },
1249 
1250     /**
1251      * whether it is the last element according to all their parents
1252      * @param {ccui.Widget} widget
1253      * @param {Number} direction
1254      * @returns {Boolean}
1255      * @private
1256      */
1257     _isLastWidgetInContainer:function(widget, direction){
1258         var parent = widget.getParent();
1259         if (parent == null || !(parent instanceof ccui.Layout))
1260             return true;
1261 
1262         var container = parent.getChildren();
1263         var index = container.indexOf(widget);
1264         if (parent.getLayoutType() === ccui.Layout.LINEAR_HORIZONTAL) {
1265             if (direction === ccui.Widget.LEFT) {
1266                 if (index === 0)
1267                     return this._isLastWidgetInContainer(parent, direction);
1268                 else
1269                     return false;
1270             }
1271             if (direction === ccui.Widget.RIGHT) {
1272                 if (index === container.length - 1)
1273                     return this._isLastWidgetInContainer(parent, direction);
1274                 else
1275                     return false;
1276             }
1277             if (direction === ccui.Widget.DOWN)
1278                 return this._isLastWidgetInContainer(parent, direction);
1279 
1280             if (direction === ccui.Widget.UP)
1281                 return this._isLastWidgetInContainer(parent, direction);
1282         } else if(parent.getLayoutType() === ccui.Layout.LINEAR_VERTICAL){
1283             if (direction === ccui.Widget.UP){
1284                 if (index === 0)
1285                     return this._isLastWidgetInContainer(parent, direction);
1286                 else
1287                     return false;
1288             }
1289             if (direction === ccui.Widget.DOWN) {
1290                 if (index === container.length - 1)
1291                     return this._isLastWidgetInContainer(parent, direction);
1292                 else
1293                     return false;
1294             }
1295             if (direction === ccui.Widget.LEFT)
1296                 return this._isLastWidgetInContainer(parent, direction);
1297 
1298             if (direction === ccui.Widget.RIGHT)
1299                 return this._isLastWidgetInContainer(parent, direction);
1300         } else {
1301             cc.log("invalid layout Type");
1302             return false;
1303         }
1304     },
1305 
1306     /**
1307      * Lookup any parent widget with a layout type as the direction, if the layout is loop focused, then return true, otherwise it returns false.
1308      * @param {ccui.Widget} widget
1309      * @param {Number} direction
1310      * @returns {Boolean}
1311      * @private
1312      */
1313     _isWidgetAncestorSupportLoopFocus: function(widget, direction){
1314         var parent = widget.getParent();
1315         if (parent == null || !(parent instanceof ccui.Layout))
1316             return false;
1317         if (parent.isLoopFocus()) {
1318             var layoutType = parent.getLayoutType();
1319             if (layoutType === ccui.Layout.LINEAR_HORIZONTAL) {
1320                 if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT)
1321                     return true;
1322                 else
1323                     return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1324             }
1325             if (layoutType === ccui.Layout.LINEAR_VERTICAL){
1326                 if (direction === ccui.Widget.DOWN || direction === ccui.Widget.UP)
1327                     return true;
1328                 else
1329                     return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1330             } else{
1331                 cc.assert(0, "invalid layout type");
1332                 return false;
1333             }
1334         } else
1335             return this._isWidgetAncestorSupportLoopFocus(parent, direction);
1336     },
1337 
1338     /**
1339      * pass the focus to the layout's next focus enabled child
1340      * @param {Number} direction
1341      * @param {ccui.Widget} current
1342      * @returns {ccui.Widget}
1343      * @private
1344      */
1345     _passFocusToChild: function(direction, current){
1346         if (this._checkFocusEnabledChild()) {
1347             var previousWidget = ccui.Widget.getCurrentFocusedWidget();
1348             this._findProperSearchingFunctor(direction, previousWidget);
1349             var index = this.onPassFocusToChild(direction, previousWidget);
1350 
1351             var widget = this._getChildWidgetByIndex(index);
1352             if (widget instanceof ccui.Layout) {
1353                 widget._isFocusPassing = true;
1354                 return widget.findNextFocusedWidget(direction, widget);
1355             } else {
1356                 this.dispatchFocusEvent(current, widget);
1357                 return widget;
1358             }
1359         }else
1360             return this;
1361     },
1362 
1363     /**
1364      * If there are no focus enabled child in the layout, it will return false, otherwise it returns true
1365      * @returns {boolean}
1366      * @private
1367      */
1368     _checkFocusEnabledChild: function(){
1369         var locChildren = this._children;
1370         for(var i = 0, len = locChildren.length; i < len; i++){
1371             var widget = locChildren[i];
1372             if (widget && widget instanceof ccui.Widget && widget.isFocusEnabled())
1373                 return true;
1374         }
1375         return false;
1376     },
1377 
1378     /**
1379      * Returns the "class name" of widget.
1380      * @returns {string}
1381      */
1382     getDescription: function () {
1383         return "Layout";
1384     },
1385 
1386     _createCloneInstance: function () {
1387         return new ccui.Layout();
1388     },
1389 
1390     _copyClonedWidgetChildren: function (model) {
1391         ccui.Widget.prototype._copyClonedWidgetChildren.call(this, model);
1392     },
1393 
1394     _copySpecialProperties: function (layout) {
1395         if(!(layout instanceof  ccui.Layout))
1396             return;
1397         this.setBackGroundImageScale9Enabled(layout._backGroundScale9Enabled);
1398         this.setBackGroundImage(layout._backGroundImageFileName, layout._bgImageTexType);
1399         this.setBackGroundImageCapInsets(layout._backGroundImageCapInsets);
1400         this.setBackGroundColorType(layout._colorType);
1401         this.setBackGroundColor(layout._color);
1402         this.setBackGroundColor(layout._startColor, layout._endColor);
1403         this.setBackGroundColorOpacity(layout._opacity);
1404         this.setBackGroundColorVector(layout._alongVector);
1405         this.setLayoutType(layout._layoutType);
1406         this.setClippingEnabled(layout._clippingEnabled);
1407         this.setClippingType(layout._clippingType);
1408         this._loopFocus = layout._loopFocus;
1409         this.__passFocusToChild = layout.__passFocusToChild;
1410         this._isInterceptTouch = layout._isInterceptTouch;
1411     },
1412 
1413     /**
1414      * force refresh widget layout
1415      */
1416     forceDoLayout: function(){
1417         this.requestDoLayout();
1418         this._doLayout();
1419     },
1420 
1421     _createRenderCmd: function(){
1422         if(cc._renderType === cc.game.RENDER_TYPE_WEBGL)
1423             return new ccui.Layout.WebGLRenderCmd(this);
1424         else
1425             return new ccui.Layout.CanvasRenderCmd(this);
1426     }
1427 });
1428 
1429 var _p = ccui.Layout.prototype;
1430 
1431 // Extended properties
1432 /** @expose */
1433 _p.clippingEnabled;
1434 cc.defineGetterSetter(_p, "clippingEnabled", _p.isClippingEnabled, _p.setClippingEnabled);
1435 /** @expose */
1436 _p.clippingType;
1437 cc.defineGetterSetter(_p, "clippingType", null, _p.setClippingType);
1438 /** @expose */
1439 _p.layoutType;
1440 cc.defineGetterSetter(_p, "layoutType", _p.getLayoutType, _p.setLayoutType);
1441 
1442 _p = null;
1443 
1444 /**
1445  * allocates and initializes a UILayout.
1446  * @deprecated since v3.0, please use new ccui.Layout() instead.
1447  * @return {ccui.Layout}
1448  */
1449 ccui.Layout.create = function () {
1450     return new ccui.Layout();
1451 };
1452 
1453 // Constants
1454 
1455 //layoutBackGround color type
1456 /**
1457  * The None of ccui.Layout's background color type
1458  * @constant
1459  * @type {number}
1460  */
1461 ccui.Layout.BG_COLOR_NONE = 0;
1462 /**
1463  * The solid of ccui.Layout's background color type, it will use a LayerColor to draw the background.
1464  * @constant
1465  * @type {number}
1466  */
1467 ccui.Layout.BG_COLOR_SOLID = 1;
1468 /**
1469  * The gradient of ccui.Layout's background color type, it will use a LayerGradient to draw the background.
1470  * @constant
1471  * @type {number}
1472  */
1473 ccui.Layout.BG_COLOR_GRADIENT = 2;
1474 
1475 //Layout type
1476 /**
1477  * The absolute of ccui.Layout's layout type.
1478  * @type {number}
1479  * @constant
1480  */
1481 ccui.Layout.ABSOLUTE = 0;
1482 /**
1483  * The vertical of ccui.Layout's layout type.
1484  * @type {number}
1485  * @constant
1486  */
1487 ccui.Layout.LINEAR_VERTICAL = 1;
1488 /**
1489  * The horizontal of ccui.Layout's layout type.
1490  * @type {number}
1491  * @constant
1492  */
1493 ccui.Layout.LINEAR_HORIZONTAL = 2;
1494 /**
1495  * The relative of ccui.Layout's layout type.
1496  * @type {number}
1497  * @constant
1498  */
1499 ccui.Layout.RELATIVE = 3;
1500 
1501 //Layout clipping type
1502 /**
1503  * The stencil of ccui.Layout's clipping type.
1504  * @type {number}
1505  * @constant
1506  */
1507 ccui.Layout.CLIPPING_STENCIL = 0;
1508 /**
1509  * The scissor of ccui.Layout's clipping type.
1510  * @type {number}
1511  * @constant
1512  */
1513 ccui.Layout.CLIPPING_SCISSOR = 1;
1514 
1515 /**
1516  * The zOrder value of ccui.Layout's image background.
1517  * @type {number}
1518  * @constant
1519  */
1520 ccui.Layout.BACKGROUND_IMAGE_ZORDER = -1;
1521 /**
1522  * The zOrder value of ccui.Layout's color background.
1523  * @type {number}
1524  * @constant
1525  */
1526 ccui.Layout.BACKGROUND_RENDERER_ZORDER = -2;