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