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 * Base class for ccui.Layout 28 * @class 29 * @extends ccui.Widget 30 * 31 * @property {Boolean} clippingEnabled - Indicate whether clipping is enabled 32 * @property {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} clippingType 33 * @property {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} layoutType 34 * 35 */ 36 ccui.Layout = ccui.Widget.extend(/** @lends ccui.Layout# */{ 37 _clippingEnabled: false, 38 _backGroundScale9Enabled: null, 39 _backGroundImage: null, 40 _backGroundImageFileName: null, 41 _backGroundImageCapInsets: null, 42 _colorType: null, 43 _bgImageTexType: ccui.Widget.LOCAL_TEXTURE, 44 _colorRender: null, 45 _gradientRender: null, 46 _color: null, 47 _startColor: null, 48 _endColor: null, 49 _alongVector: null, 50 _opacity: 255, 51 _backGroundImageTextureSize: null, 52 _layoutType: null, 53 _doLayoutDirty: true, 54 _clippingRectDirty: true, 55 _clippingType: null, 56 _clippingStencil: null, 57 _handleScissor: false, 58 _scissorRectDirty: false, 59 _clippingRect: null, 60 _clippingParent: null, 61 _className: "Layout", 62 _backGroundImageColor: null, 63 _finalPositionX: 0, 64 _finalPositionY: 0, 65 66 //clipping 67 _currentStencilEnabled: 0, 68 _currentStencilWriteMask: 0, 69 _currentStencilFunc: 0, 70 _currentStencilRef:0, 71 _currentStencilValueMask:0, 72 _currentStencilFail:0, 73 _currentStencilPassDepthFail:0, 74 _currentStencilPassDepthPass:0, 75 _currentDepthWriteMask:0, 76 77 _currentAlphaTestEnabled:0, 78 _currentAlphaTestFunc:0, 79 _currentAlphaTestRef:0, 80 81 _backGroundImageOpacity:0, 82 83 _mask_layer_le: 0, 84 85 _loopFocus: false, 86 _passFocusToChild: false, 87 _isFocusPassing:false, 88 89 /** 90 * allocates and initializes a UILayout. 91 * Constructor of ccui.Layout 92 * @example 93 * // example 94 * var uiLayout = new ccui.Layout(); 95 */ 96 ctor: function () { 97 this._layoutType = ccui.Layout.ABSOLUTE; 98 this._widgetType = ccui.Widget.TYPE_CONTAINER; 99 this._clippingType = ccui.Layout.CLIPPING_STENCIL; 100 this._colorType = ccui.Layout.BG_COLOR_NONE; 101 102 ccui.Widget.prototype.ctor.call(this); 103 this._backGroundImageCapInsets = cc.rect(0, 0, 0, 0); 104 105 this._color = cc.color(255, 255, 255, 255); 106 this._startColor = cc.color(255, 255, 255, 255); 107 this._endColor = cc.color(255, 255, 255, 255); 108 this._alongVector = cc.p(0, -1); 109 this._backGroundImageTextureSize = cc.size(0, 0); 110 111 this._clippingRect = cc.rect(0, 0, 0, 0); 112 this._backGroundImageColor = cc.color(255, 255, 255, 255); 113 }, 114 onEnter: function(){ 115 ccui.Widget.prototype.onEnter.call(this); 116 if (this._clippingStencil) 117 this._clippingStencil.onEnter(); 118 this._doLayoutDirty = true; 119 this._clippingRectDirty = true; 120 }, 121 onExit: function(){ 122 ccui.Widget.prototype.onExit.call(this); 123 if (this._clippingStencil) 124 this._clippingStencil.onExit(); 125 }, 126 127 /** 128 * If a layout is loop focused which means that the focus movement will be inside the layout 129 * @param {Boolean} loop pass true to let the focus movement loop inside the layout 130 */ 131 setLoopFocus: function(loop){ 132 this._loopFocus = loop; 133 }, 134 135 /** 136 * Gets whether enable focus loop 137 * @returns {boolean} If focus loop is enabled, then it will return true, otherwise it returns false. The default value is false. 138 */ 139 isLoopFocus: function(){ 140 return this._loopFocus; 141 }, 142 143 /** 144 * @param pass To specify whether the layout pass its focus to its child 145 */ 146 setPassFocusToChild: function(pass){ 147 this._passFocusToChild = pass; 148 }, 149 150 /** 151 * @returns {boolean} To query whether the layout will pass the focus to its children or not. The default value is true 152 */ 153 isPassFocusToChild: function(){ 154 return this._passFocusToChild; 155 }, 156 157 /** 158 * When a widget is in a layout, you could call this method to get the next focused widget within a specified direction. 159 * If the widget is not in a layout, it will return itself 160 * @param direction the direction to look for the next focused widget in a layout 161 * @param current the current focused widget 162 * @returns {ccui.Widget} return the index of widget in the layout 163 */ 164 findNextFocusedWidget: function(direction, current){ 165 if (this._isFocusPassing || this.isFocused()) { 166 var parent = this.getParent(); 167 this._isFocusPassing = false; 168 169 if (this._passFocusToChild) { 170 var w = this._passFocusToChild(direction, current); 171 if (w instanceof ccui.Layout && parent) { 172 parent._isFocusPassing = true; 173 return parent.findNextFocusedWidget(direction, this); 174 } 175 return w; 176 } 177 178 if (null == parent) 179 return this; 180 parent._isFocusPassing = true; 181 return parent.findNextFocusedWidget(direction, this); 182 } else if(current.isFocused() || current instanceof ccui.Layout) { 183 if (this._layoutType == ccui.Layout.LINEAR_HORIZONTAL) { 184 switch (direction){ 185 case ccui.Widget.LEFT: 186 return this._getPreviousFocusedWidget(direction, current); 187 break; 188 case ccui.Widget.RIGHT: 189 return this._getNextFocusedWidget(direction, current); 190 break; 191 case ccui.Widget.DOWN: 192 case ccui.Widget.UP: 193 if (this._isLastWidgetInContainer(this, direction)){ 194 if (this._isWidgetAncestorSupportLoopFocus(current, direction)) 195 return this.findNextFocusedWidget(direction, this); 196 return current; 197 } else { 198 return this.findNextFocusedWidget(direction, this); 199 } 200 break; 201 default: 202 cc.assert(0, "Invalid Focus Direction"); 203 return current; 204 } 205 } else if (this._layoutType == ccui.Layout.LINEAR_VERTICAL) { 206 switch (direction){ 207 case ccui.Widget.LEFT: 208 case ccui.Widget.RIGHT: 209 if (this._isLastWidgetInContainer(this, direction)) { 210 if (this._isWidgetAncestorSupportLoopFocus(current, direction)) 211 return this.findNextFocusedWidget(direction, this); 212 return current; 213 } 214 else 215 return this.findNextFocusedWidget(direction, this); 216 break; 217 case ccui.Widget.DOWN: 218 return this._getNextFocusedWidget(direction, current); 219 break; 220 case ccui.Widget.UP: 221 return this._getPreviousFocusedWidget(direction, current); 222 break; 223 default: 224 cc.assert(0, "Invalid Focus Direction"); 225 return current; 226 } 227 } else { 228 cc.assert(0, "Un Supported Layout type, please use VBox and HBox instead!!!"); 229 return current; 230 } 231 } else 232 return current; 233 }, 234 235 onPassFocusToChild: null, 236 237 init: function () { 238 if (ccui.Widget.prototype.init.call(this)) { 239 this.ignoreContentAdaptWithSize(false); 240 this.setSize(cc.size(0, 0)); 241 this.setAnchorPoint(0, 0); 242 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this); 243 return true; 244 } 245 return false; 246 }, 247 248 __stencilDraw: function(ctx){ 249 var locContext = ctx || cc._renderContext; 250 var stencil = this._clippingStencil; 251 var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY(); 252 for (var i = 0; i < stencil._buffer.length; i++) { 253 var element = stencil._buffer[i]; 254 var vertices = element.verts; 255 var firstPoint = vertices[0]; 256 locContext.beginPath(); 257 locContext.moveTo(firstPoint.x * locEGL_ScaleX, -firstPoint.y * locEGL_ScaleY); 258 for (var j = 1, len = vertices.length; j < len; j++) 259 locContext.lineTo(vertices[j].x * locEGL_ScaleX, -vertices[j].y * locEGL_ScaleY); 260 } 261 }, 262 263 /** 264 * Adds a widget to the container. 265 * @param {ccui.Widget} widget 266 * @param {Number} [zOrder] 267 * @param {Number} [tag] 268 */ 269 addChild: function (widget, zOrder, tag) { 270 if ((widget instanceof ccui.Widget)) { 271 this.supplyTheLayoutParameterLackToChild(widget); 272 } 273 ccui.Widget.prototype.addChild.call(this, widget, zOrder, tag); 274 this._doLayoutDirty = true; 275 }, 276 277 /** 278 * Remove child widget from ccui.Layout 279 * @param {ccui.Widget} widget 280 * @param {Boolean} cleanup 281 */ 282 removeChild: function (widget, cleanup) { 283 ccui.Widget.prototype.removeChild.call(this, widget, cleanup); 284 this._doLayoutDirty = true; 285 }, 286 287 /** 288 * Removes all children from the container with a cleanup. 289 * @param {Boolean} cleanup 290 */ 291 removeAllChildren: function (cleanup) { 292 ccui.Widget.prototype.removeAllChildren.call(this, cleanup); 293 this._doLayoutDirty = true; 294 }, 295 296 /** 297 * Removes all children from the container, and do a cleanup to all running actions depending on the cleanup parameter. 298 * @param {Boolean} cleanup true if all running actions on all children nodes should be cleanup, false otherwise. 299 */ 300 removeAllChildrenWithCleanup: function(cleanup){ 301 ccui.Widget.prototype.removeAllChildrenWithCleanup(cleanup); 302 this._doLayoutDirty = true; 303 }, 304 305 /** 306 * Gets if layout is clipping enabled. 307 * @returns {Boolean} 308 */ 309 isClippingEnabled: function () { 310 return this._clippingEnabled; 311 }, 312 313 visit: function (ctx) { 314 if (!this._visible) 315 return; 316 this.adaptRenderers(); 317 this._doLayout(); 318 319 if (this._clippingEnabled) { 320 switch (this._clippingType) { 321 case ccui.Layout.CLIPPING_STENCIL: 322 this.stencilClippingVisit(ctx); 323 break; 324 case ccui.Layout.CLIPPING_SCISSOR: 325 this.scissorClippingVisit(ctx); 326 break; 327 default: 328 break; 329 } 330 } else { 331 ccui.Widget.prototype.visit.call(this, ctx); 332 } 333 }, 334 335 sortAllChildren: function () { 336 ccui.Widget.prototype.sortAllChildren.call(this); 337 this._doLayout(); 338 }, 339 340 stencilClippingVisit: null, 341 342 _stencilClippingVisitForWebGL: function (ctx) { 343 var gl = ctx || cc._renderContext; 344 345 // if stencil buffer disabled 346 /*if (cc.stencilBits < 1) { 347 // draw everything, as if there where no stencil 348 cc.Node.prototype.visit.call(this, ctx); 349 return; 350 }*/ 351 352 if (!this._clippingStencil || !this._clippingStencil.isVisible()) 353 return; 354 355 // store the current stencil layer (position in the stencil buffer), 356 // this will allow nesting up to n CCClippingNode, 357 // where n is the number of bits of the stencil buffer. 358 ccui.Layout._layer = -1; 359 360 // all the _stencilBits are in use? 361 if (ccui.Layout._layer + 1 == cc.stencilBits) { 362 // warn once 363 ccui.Layout._visit_once = true; 364 if (ccui.Layout._visit_once) { 365 cc.log("Nesting more than " + cc.stencilBits + "stencils is not supported. Everything will be drawn without stencil for this node and its childs."); 366 ccui.Layout._visit_once = false; 367 } 368 // draw everything, as if there where no stencil 369 cc.Node.prototype.visit.call(this, ctx); 370 return; 371 } 372 373 // increment the current layer 374 ccui.Layout._layer++; 375 376 // mask of the current layer (ie: for layer 3: 00000100) 377 var mask_layer = 0x1 << ccui.Layout._layer; 378 // mask of all layers less than the current (ie: for layer 3: 00000011) 379 var mask_layer_l = mask_layer - 1; 380 // mask of all layers less than or equal to the current (ie: for layer 3: 00000111) 381 var mask_layer_le = mask_layer | mask_layer_l; 382 383 // manually save the stencil state 384 var currentStencilEnabled = gl.isEnabled(gl.STENCIL_TEST); 385 var currentStencilWriteMask = gl.getParameter(gl.STENCIL_WRITEMASK); 386 var currentStencilFunc = gl.getParameter(gl.STENCIL_FUNC); 387 var currentStencilRef = gl.getParameter(gl.STENCIL_REF); 388 var currentStencilValueMask = gl.getParameter(gl.STENCIL_VALUE_MASK); 389 var currentStencilFail = gl.getParameter(gl.STENCIL_FAIL); 390 var currentStencilPassDepthFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); 391 var currentStencilPassDepthPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); 392 393 // enable stencil use 394 gl.enable(gl.STENCIL_TEST); 395 // check for OpenGL error while enabling stencil test 396 //cc.checkGLErrorDebug(); 397 398 // all bits on the stencil buffer are readonly, except the current layer bit, 399 // this means that operation like glClear or glStencilOp will be masked with this value 400 gl.stencilMask(mask_layer); 401 402 // manually save the depth test state 403 //GLboolean currentDepthTestEnabled = GL_TRUE; 404 //currentDepthTestEnabled = glIsEnabled(GL_DEPTH_TEST); 405 var currentDepthWriteMask = gl.getParameter(gl.DEPTH_WRITEMASK); 406 407 // disable depth test while drawing the stencil 408 //glDisable(GL_DEPTH_TEST); 409 // disable update to the depth buffer while drawing the stencil, 410 // as the stencil is not meant to be rendered in the real scene, 411 // it should never prevent something else to be drawn, 412 // only disabling depth buffer update should do 413 gl.depthMask(false); 414 415 // manually clear the stencil buffer by drawing a fullscreen rectangle on it 416 // setup the stencil test func like this: 417 // for each pixel in the fullscreen rectangle 418 // never draw it into the frame buffer 419 // if not in inverted mode: set the current layer value to 0 in the stencil buffer 420 // if in inverted mode: set the current layer value to 1 in the stencil buffer 421 gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); 422 gl.stencilOp(gl.ZERO, gl.KEEP, gl.KEEP); 423 424 // draw a fullscreen solid rectangle to clear the stencil buffer 425 //ccDrawSolidRect(CCPointZero, ccpFromSize([[CCDirector sharedDirector] winSize]), ccc4f(1, 1, 1, 1)); 426 cc._drawingUtil.drawSolidRect(cc.p(0, 0), cc.pFromSize(cc.director.getWinSize()), cc.color(255, 255, 255, 255)); 427 428 // setup the stencil test func like this: 429 // for each pixel in the stencil node 430 // never draw it into the frame buffer 431 // if not in inverted mode: set the current layer value to 1 in the stencil buffer 432 // if in inverted mode: set the current layer value to 0 in the stencil buffer 433 gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); 434 gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP); 435 436 cc.kmGLPushMatrix(); 437 this.transform(); 438 439 this._clippingStencil.visit(); 440 441 // restore the depth test state 442 gl.depthMask(currentDepthWriteMask); 443 444 gl.stencilFunc(gl.EQUAL, mask_layer_le, mask_layer_le); 445 gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); 446 447 // draw (according to the stencil test func) this node and its childs 448 var i = 0; // used by _children 449 var j = 0; // used by _protectedChildren 450 451 this.sortAllChildren(); 452 this.sortAllProtectedChildren(); 453 var locChildren = this._children, locProtectChildren = this._protectedChildren; 454 var iLen = locChildren.length, jLen = locProtectChildren.length, child; 455 for( ; i < iLen; i++ ){ 456 child = locChildren[i]; 457 if ( child && child.getLocalZOrder() < 0 ) 458 child.visit(); 459 else 460 break; 461 } 462 for( ; j < jLen; j++ ) { 463 child = locProtectChildren[j]; 464 if ( child && child.getLocalZOrder() < 0 ) 465 child.visit(); 466 else 467 break; 468 } 469 this.draw(); 470 for (; i < iLen; i++) 471 locChildren[i].visit(); 472 for (; j < jLen; j++) 473 locProtectChildren[j].visit(); 474 475 // manually restore the stencil state 476 gl.stencilFunc(currentStencilFunc, currentStencilRef, currentStencilValueMask); 477 gl.stencilOp(currentStencilFail, currentStencilPassDepthFail, currentStencilPassDepthPass); 478 gl.stencilMask(currentStencilWriteMask); 479 if (!currentStencilEnabled) 480 gl.disable(gl.STENCIL_TEST); 481 482 // we are done using this layer, decrement 483 ccui.Layout._layer--; 484 485 cc.kmGLPopMatrix(); 486 }, 487 488 _stencilClippingVisitForCanvas: function (ctx) { 489 // return fast (draw nothing, or draw everything if in inverted mode) if: 490 // - nil stencil node 491 // - or stencil node invisible: 492 if (!this._clippingStencil || !this._clippingStencil.isVisible()) { 493 return; 494 } 495 var context = ctx || cc._renderContext; 496 // Composition mode, costy but support texture stencil 497 if (this._cangodhelpme() || this._clippingStencil instanceof cc.Sprite) { 498 // Cache the current canvas, for later use (This is a little bit heavy, replace this solution with other walkthrough) 499 var canvas = context.canvas; 500 var locCache = ccui.Layout._getSharedCache(); 501 locCache.width = canvas.width; 502 locCache.height = canvas.height; 503 var locCacheCtx = locCache.getContext("2d"); 504 locCacheCtx.drawImage(canvas, 0, 0); 505 506 context.save(); 507 // Draw everything first using node visit function 508 cc.Node.prototype.visit.call(this, context); 509 510 context.globalCompositeOperation = "destination-in"; 511 512 this.transform(context); 513 this._clippingStencil.visit(); 514 515 context.restore(); 516 517 // Redraw the cached canvas, so that the cliped area shows the background etc. 518 context.save(); 519 context.setTransform(1, 0, 0, 1, 0, 0); 520 context.globalCompositeOperation = "destination-over"; 521 context.drawImage(locCache, 0, 0); 522 context.restore(); 523 } else { // Clip mode, fast, but only support cc.DrawNode 524 var i, children = this._children, locChild; 525 526 context.save(); 527 this.transform(context); 528 this._clippingStencil.visit(context); 529 context.clip(); 530 531 // Clip mode doesn't support recusive stencil, so once we used a clip stencil, 532 // so if it has ClippingNode as a child, the child must uses composition stencil. 533 this._cangodhelpme(true); 534 535 this.sortAllChildren(); 536 this.sortAllProtectedChildren(); 537 538 var j, locProtectChildren = this._protectedChildren; 539 var iLen = children.length, jLen = locProtectChildren.length; 540 541 // draw children zOrder < 0 542 for (i = 0; i < iLen; i++) { 543 locChild = children[i]; 544 if (locChild && locChild._localZOrder < 0) 545 locChild.visit(context); 546 else 547 break; 548 } 549 for (j = 0; j < jLen; j++) { 550 locChild = locProtectChildren[j]; 551 if (locChild && locChild._localZOrder < 0) 552 locChild.visit(context); 553 else 554 break; 555 } 556 //this.draw(context); 557 for (; i < iLen; i++) 558 children[i].visit(context); 559 for (; j < jLen; j++) 560 locProtectChildren[j].visit(context); 561 562 this._cangodhelpme(false); 563 context.restore(); 564 } 565 }, 566 567 _godhelpme: false, 568 _cangodhelpme: function (godhelpme) { 569 if (godhelpme === true || godhelpme === false) 570 cc.ClippingNode.prototype._godhelpme = godhelpme; 571 return cc.ClippingNode.prototype._godhelpme; 572 }, 573 574 scissorClippingVisit: null, 575 _scissorClippingVisitForWebGL: function (ctx) { 576 var clippingRect = this.getClippingRect(); 577 var gl = ctx || cc._renderContext; 578 if (this._handleScissor) { 579 gl.enable(gl.SCISSOR_TEST); 580 } 581 cc.view.setScissorInPoints(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height); 582 cc.Node.prototype.visit.call(this); 583 if (this._handleScissor) { 584 gl.disable(gl.SCISSOR_TEST); 585 } 586 }, 587 588 /** 589 * Changes if layout can clip it's content and locChild. 590 * @param {Boolean} able 591 */ 592 setClippingEnabled: function (able) { 593 if (able == this._clippingEnabled) 594 return; 595 this._clippingEnabled = able; 596 switch (this._clippingType) { 597 case ccui.Layout.CLIPPING_STENCIL: 598 if (able){ 599 this._clippingStencil = cc.DrawNode.create(); 600 if(cc._renderType === cc._RENDER_TYPE_CANVAS) 601 this._clippingStencil.draw = this.__stencilDraw.bind(this); 602 if (this._running) 603 this._clippingStencil.onEnter(); 604 this.setStencilClippingSize(this._contentSize); 605 } else { 606 if (this._running) 607 this._clippingStencil.onExit(); 608 this._clippingStencil = null; 609 } 610 break; 611 default: 612 break; 613 } 614 }, 615 616 /** 617 * Set clipping type 618 * @param {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} type 619 */ 620 setClippingType: function (type) { 621 if (type == this._clippingType) { 622 return; 623 } 624 var clippingEnabled = this.isClippingEnabled(); 625 this.setClippingEnabled(false); 626 this._clippingType = type; 627 this.setClippingEnabled(clippingEnabled); 628 }, 629 630 /** 631 * Get clipping type 632 * @returns {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} 633 */ 634 getClippingType: function () { 635 return this._clippingType; 636 }, 637 638 setStencilClippingSize: function (size) { 639 if (this._clippingEnabled && this._clippingType == ccui.Layout.CLIPPING_STENCIL) { 640 var rect = []; 641 rect[0] = cc.p(0, 0); 642 rect[1] = cc.p(size.width, 0); 643 rect[2] = cc.p(size.width, size.height); 644 rect[3] = cc.p(0, size.height); 645 var green = cc.color.GREEN; 646 this._clippingStencil.clear(); 647 this._clippingStencil.drawPoly(rect, 4, green, 0, green); 648 } 649 }, 650 651 rendererVisitCallBack: function () { 652 this._doLayout(); 653 }, 654 655 getClippingRect: function () { 656 if (this._clippingRectDirty) { 657 var worldPos = this.convertToWorldSpace(cc.p(0, 0)); 658 var t = this.nodeToWorldTransform(); 659 var scissorWidth = this._contentSize.width * t.a; 660 var scissorHeight = this._contentSize.height * t.d; 661 var parentClippingRect; 662 var parent = this; 663 var firstClippingParentFounded = false; 664 while (parent) { 665 parent = parent.getParent(); 666 if (parent && parent instanceof ccui.Layout) { 667 if (parent.isClippingEnabled()) { 668 if (!firstClippingParentFounded) { 669 this._clippingParent = parent; 670 firstClippingParentFounded = true; 671 } 672 if (parent._clippingType == ccui.Layout.CLIPPING_SCISSOR) { 673 this._handleScissor = false; 674 break; 675 } 676 } 677 } 678 } 679 680 if (this._clippingParent) { 681 parentClippingRect = this._clippingParent.getClippingRect(); 682 var finalX = worldPos.x - (scissorWidth * this._anchorPoint.x); 683 var finalY = worldPos.y - (scissorHeight * this._anchorPoint.y); 684 var finalWidth = scissorWidth; 685 var finalHeight = scissorHeight; 686 687 var leftOffset = worldPos.x - parentClippingRect.x; 688 if (leftOffset < 0) { 689 finalX = parentClippingRect.x; 690 finalWidth += leftOffset; 691 } 692 var rightOffset = (worldPos.x + scissorWidth) - (parentClippingRect.x + parentClippingRect.width); 693 if (rightOffset > 0) { 694 finalWidth -= rightOffset; 695 } 696 var topOffset = (worldPos.y + scissorHeight) - (parentClippingRect.y + parentClippingRect.height); 697 if (topOffset > 0) { 698 finalHeight -= topOffset; 699 } 700 var bottomOffset = worldPos.y - parentClippingRect.y; 701 if (bottomOffset < 0) { 702 finalY = parentClippingRect.x; 703 finalHeight += bottomOffset; 704 } 705 if (finalWidth < 0) { 706 finalWidth = 0; 707 } 708 if (finalHeight < 0) { 709 finalHeight = 0; 710 } 711 this._clippingRect.x = finalX; 712 this._clippingRect.y = finalY; 713 this._clippingRect.width = finalWidth; 714 this._clippingRect.height = finalHeight; 715 } 716 else { 717 this._clippingRect.x = worldPos.x - (scissorWidth * this._anchorPoint.x); 718 this._clippingRect.y = worldPos.y - (scissorHeight * this._anchorPoint.y); 719 this._clippingRect.width = scissorWidth; 720 this._clippingRect.height = scissorHeight; 721 } 722 this._clippingRectDirty = false; 723 } 724 return this._clippingRect; 725 }, 726 727 onSizeChanged: function () { 728 ccui.Widget.prototype.onSizeChanged.call(this); 729 this.setStencilClippingSize(this._contentSize); 730 this._doLayoutDirty = true; 731 this._clippingRectDirty = true; 732 if (this._backGroundImage) { 733 this._backGroundImage.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5); 734 if (this._backGroundScale9Enabled) { 735 if (this._backGroundImage instanceof cc.Scale9Sprite) { 736 this._backGroundImage.setPreferredSize(this._contentSize); 737 } 738 } 739 } 740 if (this._colorRender) { 741 this._colorRender.setContentSize(this._contentSize); 742 } 743 if (this._gradientRender) { 744 this._gradientRender.setContentSize(this._contentSize); 745 } 746 }, 747 748 /** 749 * Sets background image use scale9 renderer. 750 * @param {Boolean} able 751 */ 752 setBackGroundImageScale9Enabled: function (able) { 753 if (this._backGroundScale9Enabled == able) { 754 return; 755 } 756 this.removeProtectedChild(this._backGroundImage); 757 //cc.Node.prototype.removeChild.call(this, this._backGroundImage, true); 758 this._backGroundImage = null; 759 this._backGroundScale9Enabled = able; 760 /* if (this._backGroundScale9Enabled) { 761 this._backGroundImage = cc.Scale9Sprite.create(); 762 } 763 else { 764 this._backGroundImage = cc.Sprite.create(); 765 } 766 cc.Node.prototype.addChild.call(this, this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1);*/ 767 this.addBackGroundImage(); 768 this.setBackGroundImage(this._backGroundImageFileName, this._bgImageTexType); 769 this.setBackGroundImageCapInsets(this._backGroundImageCapInsets); 770 }, 771 772 /** 773 * Get background image is use scale9 renderer. 774 * @returns {Boolean} 775 */ 776 isBackGroundImageScale9Enabled: function () { 777 return this._backGroundScale9Enabled; 778 }, 779 780 /** 781 * Sets a background image for layout 782 * @param {String} fileName 783 * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType 784 */ 785 setBackGroundImage: function (fileName, texType) { 786 if (!fileName) 787 return; 788 texType = texType || ccui.Widget.LOCAL_TEXTURE; 789 if (this._backGroundImage == null) 790 this.addBackGroundImage(); 791 this._backGroundImageFileName = fileName; 792 this._bgImageTexType = texType; 793 if (this._backGroundScale9Enabled) { 794 var bgiScale9 = this._backGroundImage; 795 switch (this._bgImageTexType) { 796 case ccui.Widget.LOCAL_TEXTURE: 797 bgiScale9.initWithFile(fileName); 798 break; 799 case ccui.Widget.PLIST_TEXTURE: 800 bgiScale9.initWithSpriteFrameName(fileName); 801 break; 802 default: 803 break; 804 } 805 bgiScale9.setPreferredSize(this._contentSize); 806 } else { 807 var sprite = this._backGroundImage; 808 switch (this._bgImageTexType){ 809 case ccui.Widget.LOCAL_TEXTURE: 810 sprite.setTexture(fileName); 811 break; 812 case ccui.Widget.PLIST_TEXTURE: 813 sprite.setSpriteFrame(fileName); 814 break; 815 default: 816 break; 817 } 818 } 819 this._backGroundImageTextureSize = this._backGroundImage.getContentSize(); 820 this._backGroundImage.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0); 821 this._updateBackGroundImageColor(); 822 }, 823 824 /** 825 * Sets a background image CapInsets for layout, if the background image is a scale9 render. 826 * @param {cc.Rect} capInsets 827 */ 828 setBackGroundImageCapInsets: function (capInsets) { 829 this._backGroundImageCapInsets = capInsets; 830 if (this._backGroundScale9Enabled) 831 this._backGroundImage.setCapInsets(capInsets); 832 }, 833 834 /** 835 * Gets background image cap insets. 836 * @returns {cc.Rect} 837 */ 838 getBackGroundImageCapInsets: function () { 839 return this._backGroundImageCapInsets; 840 }, 841 842 supplyTheLayoutParameterLackToChild: function (locChild) { 843 if (!locChild) { 844 return; 845 } 846 switch (this._layoutType) { 847 case ccui.Layout.ABSOLUTE: 848 break; 849 case ccui.Layout.LINEAR_HORIZONTAL: 850 case ccui.Layout.LINEAR_VERTICAL: 851 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.LINEAR); 852 if (!layoutParameter) 853 locChild.setLayoutParameter(ccui.LinearLayoutParameter.create()); 854 break; 855 case ccui.Layout.RELATIVE: 856 var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.RELATIVE); 857 if (!layoutParameter) 858 locChild.setLayoutParameter(ccui.RelativeLayoutParameter.create()); 859 break; 860 default: 861 break; 862 } 863 }, 864 865 /** 866 * init background image renderer. 867 */ 868 addBackGroundImage: function () { 869 if (this._backGroundScale9Enabled) { 870 this._backGroundImage = cc.Scale9Sprite.create(); 871 this._backGroundImage.setPreferredSize(this._contentSize); 872 } else { 873 this._backGroundImage = cc.Sprite.create(); 874 } 875 this.addProtectedChild(this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1); 876 this._backGroundImage.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0); 877 }, 878 879 /** 880 * Remove the background image of layout. 881 */ 882 removeBackGroundImage: function () { 883 if (!this._backGroundImage) 884 return; 885 this.removeProtectedChild(this._backGroundImage); 886 this._backGroundImage = null; 887 this._backGroundImageFileName = ""; 888 this._backGroundImageTextureSize = cc.size(0, 0); 889 }, 890 891 /** 892 * Sets Color Type for layout. 893 * @param {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} type 894 */ 895 setBackGroundColorType: function (type) { 896 if (this._colorType == type) 897 return; 898 switch (this._colorType) { 899 case ccui.Layout.BG_COLOR_NONE: 900 if (this._colorRender) { 901 this.removeProtectedChild(this._colorRender); 902 this._colorRender = null; 903 } 904 if (this._gradientRender) { 905 this.removeProtectedChild(this._gradientRender); 906 this._gradientRender = null; 907 } 908 break; 909 case ccui.Layout.BG_COLOR_SOLID: 910 if (this._colorRender) { 911 this.removeProtectedChild(this._colorRender); 912 this._colorRender = null; 913 } 914 break; 915 case ccui.Layout.BG_COLOR_GRADIENT: 916 if (this._gradientRender) { 917 this.removeProtectedChild(this._gradientRender); 918 this._gradientRender = null; 919 } 920 break; 921 default: 922 break; 923 } 924 this._colorType = type; 925 switch (this._colorType) { 926 case ccui.Layout.BG_COLOR_NONE: 927 break; 928 case ccui.Layout.BG_COLOR_SOLID: 929 this._colorRender = cc.LayerColor.create(); 930 this._colorRender.setContentSize(this._contentSize); 931 this._colorRender.setOpacity(this._opacity); 932 this._colorRender.setColor(this._color); 933 this.addProtectedChild(this._colorRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1); 934 break; 935 case ccui.Layout.BG_COLOR_GRADIENT: 936 this._gradientRender = cc.LayerGradient.create(cc.color(255, 0, 0, 255), cc.color(0, 255, 0, 255)); 937 this._gradientRender.setContentSize(this._contentSize); 938 this._gradientRender.setOpacity(this._opacity); 939 this._gradientRender.setStartColor(this._startColor); 940 this._gradientRender.setEndColor(this._endColor); 941 this._gradientRender.setVector(this._alongVector); 942 this.addProtectedChild(this._gradientRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1); 943 break; 944 default: 945 break; 946 } 947 }, 948 949 /** 950 * Get color type. 951 * @returns {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} 952 */ 953 getBackGroundColorType: function () { 954 return this._colorType; 955 }, 956 957 /** 958 * Sets background color for layout, if color type is Layout.COLOR_SOLID 959 * @param {cc.Color} color 960 * @param {cc.Color} endColor 961 */ 962 setBackGroundColor: function (color, endColor) { 963 if (!endColor) { 964 this._color.r = color.r; 965 this._color.g = color.g; 966 this._color.b = color.b; 967 if (this._colorRender) { 968 this._colorRender.setColor(color); 969 } 970 } else { 971 this._startColor.r = color.r; 972 this._startColor.g = color.g; 973 this._startColor.b = color.b; 974 975 if (this._gradientRender) { 976 this._gradientRender.setStartColor(color); 977 } 978 this._endColor = endColor; 979 if (this._gradientRender) { 980 this._gradientRender.setEndColor(endColor); 981 } 982 } 983 }, 984 985 /** 986 * Get back ground color 987 * @returns {cc.Color} 988 */ 989 getBackGroundColor: function () { 990 var tmpColor = this._color; 991 return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); 992 }, 993 994 /** 995 * Get back ground start color 996 * @returns {cc.Color} 997 */ 998 getBackGroundStartColor: function () { 999 var tmpColor = this._startColor; 1000 return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); 1001 }, 1002 1003 /** 1004 * Get back ground end color 1005 * @returns {cc.Color} 1006 */ 1007 getBackGroundEndColor: function () { 1008 var tmpColor = this._endColor; 1009 return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); 1010 }, 1011 1012 /** 1013 * Sets background opacity layout. 1014 * @param {number} opacity 1015 */ 1016 setBackGroundColorOpacity: function (opacity) { 1017 this._opacity = opacity; 1018 switch (this._colorType) { 1019 case ccui.Layout.BG_COLOR_NONE: 1020 break; 1021 case ccui.Layout.BG_COLOR_SOLID: 1022 this._colorRender.setOpacity(opacity); 1023 break; 1024 case ccui.Layout.BG_COLOR_GRADIENT: 1025 this._gradientRender.setOpacity(opacity); 1026 break; 1027 default: 1028 break; 1029 } 1030 }, 1031 1032 /** 1033 * Get background opacity value. 1034 * @returns {Number} 1035 */ 1036 getBackGroundColorOpacity: function () { 1037 return this._opacity; 1038 }, 1039 1040 /** 1041 * Sets background color vector for layout, if color type is Layout.COLOR_GRADIENT 1042 * @param {cc.Point} vector 1043 */ 1044 setBackGroundColorVector: function (vector) { 1045 this._alongVector.x = vector.x; 1046 this._alongVector.y = vector.y; 1047 if (this._gradientRender) { 1048 this._gradientRender.setVector(vector); 1049 } 1050 }, 1051 1052 /** 1053 * Get background color value. 1054 * @returns {cc.Point} 1055 */ 1056 getBackGroundColorVector: function () { 1057 return this._alongVector; 1058 }, 1059 1060 /** 1061 * Set backGround image color 1062 * @param {cc.Color} color 1063 */ 1064 setBackGroundImageColor: function (color) { 1065 this._backGroundImageColor.r = color.r; 1066 this._backGroundImageColor.g = color.g; 1067 this._backGroundImageColor.b = color.b; 1068 1069 this._updateBackGroundImageColor(); 1070 }, 1071 1072 /** 1073 * Get backGround image color 1074 * @param {Number} opacity 1075 */ 1076 setBackGroundImageOpacity: function (opacity) { 1077 this._backGroundImageColor.a = opacity; 1078 this.getBackGroundImageColor(); 1079 }, 1080 1081 /** 1082 * Get backGround image color 1083 * @returns {cc.Color} 1084 */ 1085 getBackGroundImageColor: function () { 1086 var color = this._backGroundImageColor; 1087 return cc.color(color.r, color.g, color.b, color.a); 1088 }, 1089 1090 /** 1091 * Get backGround image opacity 1092 * @returns {Number} 1093 */ 1094 getBackGroundImageOpacity: function () { 1095 return this._backGroundImageColor.a; 1096 }, 1097 1098 _updateBackGroundImageColor: function () { 1099 if(this._backGroundImage) 1100 this._backGroundImage.setColor(this._backGroundImageColor); 1101 }, 1102 1103 /** 1104 * Gets background image texture size. 1105 * @returns {cc.Size} 1106 */ 1107 getBackGroundImageTextureSize: function () { 1108 return this._backGroundImageTextureSize; 1109 }, 1110 1111 /** 1112 * Sets LayoutType. 1113 * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type 1114 */ 1115 setLayoutType: function (type) { 1116 this._layoutType = type; 1117 var layoutChildrenArray = this._children; 1118 var locChild = null; 1119 for (var i = 0; i < layoutChildrenArray.length; i++) { 1120 locChild = layoutChildrenArray[i]; 1121 if(locChild instanceof ccui.Widget) 1122 this.supplyTheLayoutParameterLackToChild(locChild); 1123 } 1124 this._doLayoutDirty = true; 1125 }, 1126 1127 /** 1128 * Gets LayoutType. 1129 * @returns {null} 1130 */ 1131 getLayoutType: function () { 1132 return this._layoutType; 1133 }, 1134 1135 /** 1136 * request do layout 1137 */ 1138 requestDoLayout: function () { 1139 this._doLayoutDirty = true; 1140 }, 1141 1142 _doLayout: function () { 1143 if (!this._doLayoutDirty) 1144 return; 1145 1146 var executant = this._createLayoutManager(); //TODO create a layout manager every calling _doLayout? 1147 if (executant) 1148 executant._doLayout(this); 1149 this._doLayoutDirty = false; 1150 }, 1151 1152 _createLayoutManager: function(){ 1153 var layoutMgr = null; 1154 switch (this._layoutType) { 1155 case ccui.Layout.LINEAR_VERTICAL: 1156 layoutMgr = ccui.LinearVerticalLayoutManager.create(); 1157 break; 1158 case ccui.Layout.LINEAR_HORIZONTAL: 1159 layoutMgr = ccui.LinearHorizontalLayoutManager.create(); 1160 break; 1161 case ccui.Layout.RELATIVE: 1162 layoutMgr = ccui.RelativeLayoutManager.create(); 1163 break; 1164 } 1165 return layoutMgr; 1166 }, 1167 1168 _getLayoutContentSize: function(){ 1169 return this.getContentSize(); 1170 }, 1171 1172 _getLayoutElements: function(){ 1173 return this.getChildren(); 1174 }, 1175 1176 //clipping 1177 _onBeforeVisitStencil: function(){ 1178 //TODO NEW RENDERER 1179 }, 1180 1181 _drawFullScreenQuadClearStencil:function(){ 1182 //TODO NEW RENDERER 1183 }, 1184 1185 _onAfterDrawStencil: function(){ 1186 //TODO NEW RENDERER 1187 }, 1188 1189 _onAfterVisitStencil: function(){ 1190 //TODO NEW RENDERER 1191 }, 1192 1193 _onAfterVisitScissor: function(){ 1194 //TODO NEW RENDERER 1195 }, 1196 1197 _onAfterVisitScissor: function(){ 1198 //TODO NEW RENDERER 1199 }, 1200 1201 _updateBackGroundImageOpacity: function(){ 1202 if (this._backGroundImage) 1203 this._backGroundImage.setOpacity(this._backGroundImageOpacity); 1204 }, 1205 1206 _updateBackGroundImageRGBA: function(){ 1207 if (this._backGroundImage) { 1208 this._backGroundImage.setColor(this._backGroundImageColor); 1209 this._backGroundImage.setOpacity(this._backGroundImageOpacity); 1210 } 1211 }, 1212 1213 _getLayoutAccumulatedSize: function(){ 1214 var children = this.getChildren(); 1215 var layoutSize = cc.size(0, 0); 1216 var widgetCount = 0, locSize; 1217 for(var i = 0, len = children.length; i < len; i++) { 1218 var layout = children[i]; 1219 if (null != layout && layout instanceof ccui.Layout){ 1220 locSize = layout._getLayoutAccumulatedSize(); 1221 layoutSize.width += locSize.width; 1222 layoutSize.height += locSize.height; 1223 // C++ layoutSize = layoutSize + layout.getLayoutAccumulatedSize(); 1224 } else { 1225 if (layout instanceof ccui.Widget) { 1226 widgetCount++; 1227 var m = w.getLayoutParameter().getMargin(); 1228 locSize = w.getContentSize(); 1229 // c++ layoutSize = layoutSize + w.getContentSize() + cc.size(m.right + m.left, m.top + m.bottom) * 0.5; 1230 layoutSize.width += locSize.width + (m.right + m.left) * 0.5; 1231 layoutSize.height += locSize.height + (m.top + m.bottom) * 0.5; 1232 } 1233 } 1234 } 1235 1236 //substract extra size 1237 var type = this.getLayoutType(); 1238 if (type == ccui.Layout.LINEAR_HORIZONTAL) 1239 layoutSize.height = layoutSize.height - layoutSize.height/widgetCount * (widgetCount-1); 1240 1241 if (type == ccui.Layout.LINEAR_VERTICAL) 1242 layoutSize.width = layoutSize.width - layoutSize.width/widgetCount * (widgetCount-1); 1243 return layoutSize; 1244 }, 1245 1246 _findNearestChildWidgetIndex: function(direction, baseWidget){ 1247 if (baseWidget == null || baseWidget == this) 1248 return this._findFirstFocusEnabledWidgetIndex(); 1249 1250 var index = 0, locChildren = this.getChildren(); 1251 var count = locChildren.length; 1252 var widgetPosition; 1253 1254 var distance = cc.FLT_MAX, found = 0; 1255 if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT || direction == ccui.Widget.DOWN || direction == ccui.Widget.UP) { 1256 widgetPosition = this._getWorldCenterPoint(baseWidget); 1257 while (index < count) { 1258 var w = locChildren[index]; 1259 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) { 1260 var length = (w instanceof ccui.Layout)? w._calculateNearestDistance(baseWidget) 1261 : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition)); 1262 1263 if (length < distance){ 1264 found = index; 1265 distance = length; 1266 } 1267 } 1268 index++; 1269 } 1270 return found; 1271 } 1272 cc.assert(0, "invalid focus direction!"); 1273 return 0; 1274 }, 1275 1276 _findFarestChildWidgetIndex: function(direction, baseWidget){ 1277 if (baseWidget == null || baseWidget == this) 1278 return this._findFirstFocusEnabledWidgetIndex(); 1279 1280 var index = 0; 1281 var count = this.getChildren().size(); 1282 1283 var distance = -cc.FLT_MAX; 1284 var found = 0; 1285 if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT || direction == ccui.Widget.DOWN || direction == ccui.Widget.UP) { 1286 var widgetPosition = this._getWorldCenterPoint(baseWidget); 1287 while (index < count) { 1288 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) { 1289 var length = (w instanceof ccui.Layout)?w._calculateFarestDistance(baseWidget) 1290 : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition)); 1291 1292 if (length > distance){ 1293 found = index; 1294 distance = length; 1295 } 1296 } 1297 index++; 1298 } 1299 return found; 1300 } 1301 cc.assert(0, "invalid focus direction!!!"); 1302 return 0; 1303 }, 1304 1305 _calculateNearestDistance: function(baseWidget){ 1306 var distance = cc.FLT_MAX; 1307 var widgetPosition = this._getWorldCenterPoint(baseWidget); 1308 var locChildren = this._children; 1309 1310 for (var i = 0, len = locChildren.length; i < len; i++) { 1311 var widget = locChildren[i]; 1312 var length; 1313 if (widget instanceof ccui.Layout) 1314 length = widget._calculateNearestDistance(baseWidget); 1315 else { 1316 if (widget instanceof ccui.Widget && widget.isFocusEnabled()) 1317 length = cc.pLength(cc.pSub(this._getWorldCenterPoint(widget), widgetPosition)); 1318 else 1319 continue; 1320 } 1321 1322 if (length < distance) 1323 distance = length; 1324 } 1325 return distance; 1326 }, 1327 1328 _calculateFarestDistance:function(baseWidget){ 1329 var distance = -cc.FLT_MAX; 1330 var widgetPosition = this._getWorldCenterPoint(baseWidget); 1331 var locChildren = this._children; 1332 1333 for (var i = 0, len = locChildren.length; i < len; i++) { 1334 var layout = locChildren[i]; 1335 var length; 1336 if (layout instanceof ccui.Layout) 1337 length = layout._calculateFarestDistance(baseWidget); 1338 else { 1339 if (layout instanceof ccui.Widget && layout.isFocusEnabled()) { 1340 var wPosition = this._getWorldCenterPoint(w); 1341 length = cc.pLength(cc.pSub(wPosition, widgetPosition)); 1342 } else 1343 continue; 1344 } 1345 1346 if (length > distance) 1347 distance = length; 1348 } 1349 return distance; 1350 }, 1351 1352 _findProperSearchingFunctor: function(direction, baseWidget){ 1353 if (baseWidget == null) 1354 return; 1355 1356 var previousWidgetPosition = this._getWorldCenterPoint(baseWidget); 1357 var widgetPosition = this._getWorldCenterPoint(this._findFirstNonLayoutWidget()); 1358 if (direction == ccui.Widget.LEFT) { 1359 if (previousWidgetPosition.x > widgetPosition.x) 1360 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this); 1361 else 1362 this.onPassFocusToChild = this._findFarestChildWidgetIndex.bind(this); 1363 }else if(direction == ccui.Widget.RIGHT){ 1364 if (previousWidgetPosition.x > widgetPosition.x) 1365 this.onPassFocusToChild = this._findFarestChildWidgetIndex.bind(this); 1366 else 1367 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this); 1368 }else if(direction == ccui.Widget.DOWN){ 1369 if (previousWidgetPosition.y > widgetPosition.y) 1370 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this); 1371 else 1372 this.onPassFocusToChild = this._findFarestChildWidgetIndex.bind(this); 1373 }else if(direction == ccui.Widget.UP){ 1374 if (previousWidgetPosition.y < widgetPosition.y) 1375 this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this); 1376 else 1377 this.onPassFocusToChild = this._findFarestChildWidgetIndex.bind(this); 1378 }else 1379 cc.assert(0, "invalid direction!"); 1380 }, 1381 1382 _findFirstNonLayoutWidget:function(){ 1383 var locChildren = this._children; 1384 for(var i = 0, len = locChildren.length; i < len; i++) { 1385 var child = locChildren[i]; 1386 if (child instanceof ccui.Layout){ 1387 var widget = child._findFirstNonLayoutWidget(); 1388 if(widget) 1389 return widget; 1390 } else{ 1391 if (child instanceof cc.Widget) 1392 return child; 1393 } 1394 } 1395 return null; 1396 }, 1397 1398 _findFirstFocusEnabledWidgetIndex: function(){ 1399 var index = 0, locChildren = this.getChildren(); 1400 var count = locChildren.length; 1401 while (index < count) { 1402 var w = locChildren[index]; 1403 if (w && w instanceof ccui.Widget && w.isFocusEnabled()) 1404 return index; 1405 index++; 1406 } 1407 //cc.assert(0, "invalid operation"); 1408 return 0; 1409 }, 1410 1411 _findFocusEnabledChildWidgetByIndex: function(index){ 1412 var widget = this._getChildWidgetByIndex(index); 1413 1414 if (widget){ 1415 if (widget.isFocusEnabled()) 1416 return widget; 1417 index = index + 1; 1418 return this._findFocusEnabledChildWidgetByIndex(index); 1419 } 1420 return null; 1421 }, 1422 1423 _getWorldCenterPoint: function(widget){ 1424 //FIXEDME: we don't need to calculate the content size of layout anymore 1425 var widgetSize = widget instanceof ccui.Layout ? widget._getLayoutAccumulatedSize() : widget.getContentSize(); 1426 return widget.convertToWorldSpace(cc.p(widgetSize.width /2, widgetSize.height /2)); 1427 }, 1428 1429 _getNextFocusedWidget: function(direction, current){ 1430 var nextWidget = null, locChildren = this._children; 1431 var previousWidgetPos = locChildren.indexOf(current); 1432 previousWidgetPos = previousWidgetPos + 1; 1433 if (previousWidgetPos < locChildren.length) { 1434 nextWidget = this._getChildWidgetByIndex(previousWidgetPos); 1435 //handle widget 1436 if (nextWidget) { 1437 if (nextWidget.isFocusEnabled()) { 1438 if (nextWidget instanceof ccui.Layout) { 1439 nextWidget._isFocusPassing = true; 1440 return nextWidget.findNextFocusedWidget(direction, nextWidget); 1441 } else { 1442 this.dispatchFocusEvent(current, nextWidget); 1443 return nextWidget; 1444 } 1445 } else 1446 return this._getNextFocusedWidget(direction, nextWidget); 1447 } else 1448 return current; 1449 } else { 1450 if (this._loopFocus) { 1451 if (this._checkFocusEnabledChild()) { 1452 previousWidgetPos = 0; 1453 nextWidget = this._getChildWidgetByIndex(previousWidgetPos); 1454 if (nextWidget.isFocusEnabled()) { 1455 if (nextWidget instanceof ccui.Layout) { 1456 nextWidget._isFocusPassing = true; 1457 return nextWidget.findNextFocusedWidget(direction, nextWidget); 1458 } else { 1459 this.dispatchFocusEvent(current, nextWidget); 1460 return nextWidget; 1461 } 1462 } else 1463 return this._getNextFocusedWidget(direction, nextWidget); 1464 } else { 1465 if (current instanceof ccui.Layout) 1466 return current; 1467 else 1468 return this._focusedWidget; 1469 } 1470 } else{ 1471 if (this._isLastWidgetInContainer(current, direction)){ 1472 if (this._isWidgetAncestorSupportLoopFocus(this, direction)) 1473 return this.findNextFocusedWidget(direction, this); 1474 if (current instanceof ccui.Layout) 1475 return current; 1476 else 1477 return this._focusedWidget; 1478 } else 1479 return this.findNextFocusedWidget(direction, this); 1480 } 1481 } 1482 }, 1483 1484 _getPreviousFocusedWidget: function(direction, current){ 1485 var nextWidget = null, locChildren = this._children; 1486 var previousWidgetPos = locChildren.indexOf(current); 1487 previousWidgetPos = previousWidgetPos - 1; 1488 if (previousWidgetPos >= 0){ 1489 nextWidget = this._getChildWidgetByIndex(previousWidgetPos); 1490 if (nextWidget.isFocusEnabled()) { 1491 if (nextWidget instanceof ccui.Layout){ 1492 nextWidget._isFocusPassing = true; 1493 return nextWidget.findNextFocusedWidget(direction, nextWidget); 1494 } 1495 this.dispatchFocusEvent(current, nextWidget); 1496 return nextWidget; 1497 } else 1498 //handling the disabled widget, there is no actual focus lose or get, so we don't need any envet 1499 return this._getPreviousFocusedWidget(direction, nextWidget); 1500 }else { 1501 if (this._loopFocus){ 1502 if (this._checkFocusEnabledChild()) { 1503 previousWidgetPos = locChildren.length -1; 1504 nextWidget = this._getChildWidgetByIndex(previousWidgetPos); 1505 if (nextWidget.isFocusEnabled()){ 1506 if (nextWidget instanceof ccui.Layout){ 1507 nextWidget._isFocusPassing = true; 1508 return nextWidget.findNextFocusedWidget(direction, nextWidget); 1509 } else { 1510 this.dispatchFocusEvent(current, nextWidget); 1511 return nextWidget; 1512 } 1513 } else 1514 return this._getPreviousFocusedWidget(direction, nextWidget); 1515 } else { 1516 if (current instanceof ccui.Layout) 1517 return current; 1518 else 1519 return this._focusedWidget; 1520 } 1521 } else { 1522 if (this._isLastWidgetInContainer(current, direction)) { 1523 if (this._isWidgetAncestorSupportLoopFocus(this, direction)) 1524 return this.findNextFocusedWidget(direction, this); 1525 1526 if (current instanceof ccui.Layout) 1527 return current; 1528 else 1529 return this._focusedWidget; 1530 } else 1531 return this.findNextFocusedWidget(direction, this); 1532 } 1533 } 1534 }, 1535 1536 _getChildWidgetByIndex: function (index) { 1537 var locChildren = this._children; 1538 var size = locChildren.length; 1539 var count = 0, oldIndex = index; 1540 while (index < size) { 1541 var firstChild = locChildren[index]; 1542 if (firstChild && firstChild instanceof ccui.Widget) 1543 return firstChild; 1544 count++; 1545 index++; 1546 } 1547 1548 var begin = 0; 1549 while (begin < oldIndex) { 1550 var child = locChildren[begin]; 1551 if (child && child instanceof ccui.Widget) 1552 return child; 1553 count++; 1554 begin++; 1555 } 1556 return null; 1557 }, 1558 1559 _isLastWidgetInContainer:function(widget, direction){ 1560 var parent = widget.getParent(); 1561 if (parent instanceof ccui.Layout) 1562 return true; 1563 1564 var container = parent.getChildren(); 1565 var index = container.indexOf(widget); 1566 if (parent.getLayoutType() == ccui.Layout.LINEAR_HORIZONTAL) { 1567 if (direction == ccui.Widget.LEFT) { 1568 if (index == 0) 1569 return true * this._isLastWidgetInContainer(parent, direction); 1570 else 1571 return false; 1572 } 1573 if (direction == ccui.Widget.RIGHT) { 1574 if (index == container.length - 1) 1575 return true * this._isLastWidgetInContainer(parent, direction); 1576 else 1577 return false; 1578 } 1579 if (direction == ccui.Widget.DOWN) 1580 return this._isLastWidgetInContainer(parent, direction); 1581 1582 if (direction == ccui.Widget.UP) 1583 return this._isLastWidgetInContainer(parent, direction); 1584 } else if(parent.getLayoutType() == ccui.Layout.LINEAR_VERTICAL){ 1585 if (direction == ccui.Widget.UP){ 1586 if (index == 0) 1587 return true * this._isLastWidgetInContainer(parent, direction); 1588 else 1589 return false; 1590 } 1591 if (direction == ccui.Widget.DOWN) { 1592 if (index == container.length - 1) 1593 return true * this._isLastWidgetInContainer(parent, direction); 1594 else 1595 return false; 1596 } 1597 if (direction == ccui.Widget.LEFT) 1598 return this._isLastWidgetInContainer(parent, direction); 1599 1600 if (direction == ccui.Widget.RIGHT) 1601 return this._isLastWidgetInContainer(parent, direction); 1602 }else { 1603 cc.assert(0, "invalid layout Type"); 1604 return false; 1605 } 1606 return false; 1607 }, 1608 1609 _isWidgetAncestorSupportLoopFocus: function(widget, direction){ 1610 var parent = widget.getParent(); 1611 if (parent == null) 1612 return false; 1613 if (parent.isLoopFocus()) { 1614 var layoutType = parent.getLayoutType(); 1615 if (layoutType == ccui.Layout.LINEAR_HORIZONTAL) { 1616 if (direction == ccui.Widget.LEFT || direction == ccui.Widget.RIGHT) 1617 return true; 1618 else 1619 return this._isWidgetAncestorSupportLoopFocus(parent, direction); 1620 } 1621 if (layoutType == ccui.Layout.LINEAR_VERTICAL){ 1622 if (direction == ccui.Widget.DOWN || direction == ccui.Widget.UP) 1623 return true; 1624 else 1625 return this._isWidgetAncestorSupportLoopFocus(parent, direction); 1626 } else 1627 cc.assert(0, "invalid layout type"); 1628 } else 1629 return this._isWidgetAncestorSupportLoopFocus(parent, direction); 1630 }, 1631 1632 _passFocusToChild: function(direction, current){ 1633 if (this._checkFocusEnabledChild()) { 1634 var previousWidget = this.getCurrentFocusedWidget(); 1635 this._findProperSearchingFunctor(direction, previousWidget); 1636 1637 var index = this.onPassFocusToChild(direction, previousWidget); //TODO need check 1638 1639 var widget = this._getChildWidgetByIndex(index); 1640 if (widget instanceof ccui.Layout) { 1641 widget._isFocusPassing = true; 1642 return widget.findNextFocusedWidget(direction, widget); 1643 } else { 1644 this.dispatchFocusEvent(current, widget); 1645 return widget; 1646 } 1647 }else 1648 return this; 1649 }, 1650 1651 _checkFocusEnabledChild: function(){ 1652 var locChildren = this._children; 1653 for(var i = 0, len = locChildren.length; i < len; i++){ 1654 var widget = locChildren[i]; 1655 if (widget && widget instanceof ccui.Widget && widget.isFocusEnabled()) 1656 return true; 1657 } 1658 return false; 1659 }, 1660 1661 /** 1662 * Returns the "class name" of widget. 1663 * @returns {string} 1664 */ 1665 getDescription: function () { 1666 return "Layout"; 1667 }, 1668 1669 createCloneInstance: function () { 1670 return ccui.Layout.create(); 1671 }, 1672 1673 copyClonedWidgetChildren: function (model) { 1674 ccui.Widget.prototype.copyClonedWidgetChildren.call(this, model); 1675 }, 1676 1677 copySpecialProperties: function (layout) { 1678 this.setBackGroundImageScale9Enabled(layout._backGroundScale9Enabled); 1679 this.setBackGroundImage(layout._backGroundImageFileName, layout._bgImageTexType); 1680 this.setBackGroundImageCapInsets(layout._backGroundImageCapInsets); 1681 this.setBackGroundColorType(layout._colorType); 1682 this.setBackGroundColor(layout._color); 1683 this.setBackGroundColor(layout._startColor, layout._endColor); 1684 this.setBackGroundColorOpacity(layout._opacity); 1685 this.setBackGroundColorVector(layout._alongVector); 1686 this.setLayoutType(layout._layoutType); 1687 this.setClippingEnabled(layout._clippingEnabled); 1688 this.setClippingType(layout._clippingType); 1689 this._loopFocus = layout._loopFocus; 1690 this._passFocusToChild = layout._passFocusToChild; 1691 } 1692 }); 1693 ccui.Layout._init_once = null; 1694 ccui.Layout._visit_once = null; 1695 ccui.Layout._layer = null; 1696 ccui.Layout._sharedCache = null; 1697 1698 if (cc._renderType == cc._RENDER_TYPE_WEBGL) { 1699 //WebGL 1700 ccui.Layout.prototype.stencilClippingVisit = ccui.Layout.prototype._stencilClippingVisitForWebGL; 1701 ccui.Layout.prototype.scissorClippingVisit = ccui.Layout.prototype._scissorClippingVisitForWebGL; 1702 } else { 1703 ccui.Layout.prototype.stencilClippingVisit = ccui.Layout.prototype._stencilClippingVisitForCanvas; 1704 ccui.Layout.prototype.scissorClippingVisit = ccui.Layout.prototype._stencilClippingVisitForCanvas; 1705 } 1706 ccui.Layout._getSharedCache = function () { 1707 return (cc.ClippingNode._sharedCache) || (cc.ClippingNode._sharedCache = cc.newElement("canvas")); 1708 }; 1709 1710 var _p = ccui.Layout.prototype; 1711 1712 // Extended properties 1713 /** @expose */ 1714 _p.clippingEnabled; 1715 cc.defineGetterSetter(_p, "clippingEnabled", _p.isClippingEnabled, _p.setClippingEnabled); 1716 /** @expose */ 1717 _p.clippingType; 1718 cc.defineGetterSetter(_p, "clippingType", null, _p.setClippingType); 1719 /** @expose */ 1720 _p.layoutType; 1721 cc.defineGetterSetter(_p, "layoutType", _p.getLayoutType, _p.setLayoutType); 1722 1723 _p = null; 1724 1725 /** 1726 * allocates and initializes a UILayout. 1727 * @constructs 1728 * @return {ccui.Layout} 1729 * @example 1730 * // example 1731 * var uiLayout = ccui.Layout.create(); 1732 */ 1733 ccui.Layout.create = function () { 1734 return new ccui.Layout(); 1735 }; 1736 1737 // Constants 1738 1739 //layoutBackGround color type 1740 ccui.Layout.BG_COLOR_NONE = 0; 1741 ccui.Layout.BG_COLOR_SOLID = 1; 1742 ccui.Layout.BG_COLOR_GRADIENT = 2; 1743 1744 //Layout type 1745 ccui.Layout.ABSOLUTE = 0; 1746 ccui.Layout.LINEAR_VERTICAL = 1; 1747 ccui.Layout.LINEAR_HORIZONTAL = 2; 1748 ccui.Layout.RELATIVE = 3; 1749 1750 //Layout clipping type 1751 ccui.Layout.CLIPPING_STENCIL = 0; 1752 ccui.Layout.CLIPPING_SCISSOR = 1; 1753 1754 ccui.Layout.BACKGROUND_IMAGE_ZORDER = -2; 1755 ccui.Layout.BACKGROUND_RENDERER_ZORDER = -2;