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;