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