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