1 /**************************************************************************** 2 Copyright (c) 2008-2010 Ricardo Quesada 3 Copyright (c) 2011-2012 cocos2d-x.org 4 Copyright (c) 2013-2014 Chukong Technologies Inc. 5 Copyright (c) 2012 Neofect. All rights reserved. 6 7 http://www.cocos2d-x.org 8 9 Permission is hereby granted, free of charge, to any person obtaining a copy 10 of this software and associated documentation files (the "Software"), to deal 11 in the Software without restriction, including without limitation the rights 12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 copies of the Software, and to permit persons to whom the Software is 14 furnished to do so, subject to the following conditions: 15 16 The above copyright notice and this permission notice shall be included in 17 all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 THE SOFTWARE. 26 27 Created by Jung Sang-Taik on 2012-03-16 28 ****************************************************************************/ 29 30 /** 31 * <p> 32 * A 9-slice sprite for cocos2d UI. <br/> 33 * <br/> 34 * 9-slice scaling allows you to specify how scaling is applied <br/> 35 * to specific areas of a sprite. With 9-slice scaling (3x3 grid), <br/> 36 * you can ensure that the sprite does not become distorted when <br/> 37 * scaled. <br/> 38 * @note: it will refactor in v3.1 <br/> 39 * @see http://yannickloriot.com/library/ios/cccontrolextension/Classes/CCScale9Sprite.html <br/> 40 * </p> 41 * @class 42 * @extends cc.Node 43 * 44 * @property {cc.Size} preferredSize - The preferred size of the 9-slice sprite 45 * @property {cc.Rect} capInsets - The cap insets of the 9-slice sprite 46 * @property {Number} insetLeft - The left inset of the 9-slice sprite 47 * @property {Number} insetTop - The top inset of the 9-slice sprite 48 * @property {Number} insetRight - The right inset of the 9-slice sprite 49 * @property {Number} insetBottom - The bottom inset of the 9-slice sprite 50 */ 51 52 //todo checking here. Maybe need synchronous. 53 54 ccui.Scale9Sprite = cc.Node.extend(/** @lends ccui.Scale9Sprite# */{ 55 _spriteRect: null, 56 _capInsetsInternal: null, 57 _positionsAreDirty: false, 58 59 _scale9Image: null, 60 _topLeft: null, 61 _top: null, 62 _topRight: null, 63 _left: null, 64 _centre: null, 65 _right: null, 66 _bottomLeft: null, 67 _bottom: null, 68 _bottomRight: null, 69 70 _scale9Dirty: true, 71 72 _opacityModifyRGB: false, 73 74 _originalSize: null, 75 _preferredSize: null, 76 _opacity: 0, 77 _color: null, 78 _capInsets: null, 79 _insetLeft: 0, 80 _insetTop: 0, 81 _insetRight: 0, 82 _insetBottom: 0, 83 84 _spritesGenerated: false, 85 _spriteFrameRotated: false, 86 _textureLoaded:false, 87 _className:"Scale9Sprite", 88 89 //v3.3 90 _flippedX: false, 91 _flippedY: false, 92 93 /** 94 * return texture is loaded 95 * @returns {boolean} 96 */ 97 textureLoaded:function(){ 98 return this._textureLoaded; 99 }, 100 101 /** 102 * add texture loaded event listener 103 * @param {Function} callback 104 * @param {Object} target 105 * @deprecated since 3.1, please use addEventListener instead 106 */ 107 addLoadedEventListener:function(callback, target){ 108 this.addEventListener("load", callback, target); 109 }, 110 111 _updateCapInset: function () { 112 var insets, locInsetLeft = this._insetLeft, locInsetTop = this._insetTop, locInsetRight = this._insetRight; 113 var locSpriteRect = this._spriteRect, locInsetBottom = this._insetBottom; 114 if (locInsetLeft === 0 && locInsetTop === 0 && locInsetRight === 0 && locInsetBottom === 0) { 115 insets = cc.rect(0, 0, 0, 0); 116 } else { 117 insets = this._spriteFrameRotated ? cc.rect(locInsetBottom, locInsetLeft, 118 locSpriteRect.width - locInsetRight - locInsetLeft, 119 locSpriteRect.height - locInsetTop - locInsetBottom) : 120 cc.rect(locInsetLeft, locInsetTop, 121 locSpriteRect.width - locInsetLeft - locInsetRight, 122 locSpriteRect.height - locInsetTop - locInsetBottom); 123 } 124 this.setCapInsets(insets); 125 }, 126 127 _updatePositions: function () { 128 // Check that instances are non-NULL 129 if (!((this._topLeft) && (this._topRight) && (this._bottomRight) && 130 (this._bottomLeft) && (this._centre))) { 131 // if any of the above sprites are NULL, return 132 return; 133 } 134 135 var size = this._contentSize; 136 var locTopLeft = this._topLeft, locTopRight = this._topRight, locBottomRight = this._bottomRight, locBottomLeft = this._bottomLeft; 137 var locCenter = this._centre, locCenterContentSize = this._centre.getContentSize(); 138 var locTopLeftContentSize = locTopLeft.getContentSize(); 139 var locBottomLeftContentSize = locBottomLeft.getContentSize(); 140 141 var sizableWidth = size.width - locTopLeftContentSize.width - locTopRight.getContentSize().width; 142 var sizableHeight = size.height - locTopLeftContentSize.height - locBottomRight.getContentSize().height; 143 144 var horizontalScale = sizableWidth / locCenterContentSize.width; 145 var verticalScale = sizableHeight / locCenterContentSize.height; 146 147 var rescaledWidth = locCenterContentSize.width * horizontalScale; 148 var rescaledHeight = locCenterContentSize.height * verticalScale; 149 150 var leftWidth = locBottomLeftContentSize.width; 151 var bottomHeight = locBottomLeftContentSize.height; 152 153 if (cc._renderType === cc._RENDER_TYPE_WEBGL) { 154 //browser is in canvas mode, need to manually control rounding to prevent overlapping pixels 155 var roundedRescaledWidth = Math.round(rescaledWidth); 156 if (rescaledWidth !== roundedRescaledWidth) { 157 rescaledWidth = roundedRescaledWidth; 158 horizontalScale = rescaledWidth / locCenterContentSize.width; 159 } 160 var roundedRescaledHeight = Math.round(rescaledHeight); 161 if (rescaledHeight !== roundedRescaledHeight) { 162 rescaledHeight = roundedRescaledHeight; 163 verticalScale = rescaledHeight / locCenterContentSize.height; 164 } 165 } 166 167 locCenter.setScaleX(horizontalScale); 168 locCenter.setScaleY(verticalScale); 169 170 var locLeft = this._left, locRight = this._right, locTop = this._top, locBottom = this._bottom; 171 var tempAP = cc.p(0, 0); 172 locBottomLeft.setAnchorPoint(tempAP); 173 locBottomRight.setAnchorPoint(tempAP); 174 locTopLeft.setAnchorPoint(tempAP); 175 locTopRight.setAnchorPoint(tempAP); 176 locLeft.setAnchorPoint(tempAP); 177 locRight.setAnchorPoint(tempAP); 178 locTop.setAnchorPoint(tempAP); 179 locBottom.setAnchorPoint(tempAP); 180 locCenter.setAnchorPoint(tempAP); 181 182 // Position corners 183 locBottomLeft.setPosition(0, 0); 184 locBottomRight.setPosition(leftWidth + rescaledWidth, 0); 185 locTopLeft.setPosition(0, bottomHeight + rescaledHeight); 186 locTopRight.setPosition(leftWidth + rescaledWidth, bottomHeight + rescaledHeight); 187 188 // Scale and position borders 189 locLeft.setPosition(0, bottomHeight); 190 locLeft.setScaleY(verticalScale); 191 locRight.setPosition(leftWidth + rescaledWidth, bottomHeight); 192 locRight.setScaleY(verticalScale); 193 locBottom.setPosition(leftWidth, 0); 194 locBottom.setScaleX(horizontalScale); 195 locTop.setPosition(leftWidth, bottomHeight + rescaledHeight); 196 locTop.setScaleX(horizontalScale); 197 198 // Position centre 199 locCenter.setPosition(leftWidth, bottomHeight); 200 }, 201 202 /** 203 * Constructor function. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. 204 * @function 205 * @param {string|cc.SpriteFrame} file file name of texture or a SpriteFrame 206 * @param {cc.Rect} rect 207 * @param {cc.Rect} capInsets 208 * @returns {Scale9Sprite} 209 */ 210 ctor: function (file, rect, capInsets) { 211 cc.Node.prototype.ctor.call(this); 212 this._spriteRect = cc.rect(0, 0, 0, 0); 213 this._capInsetsInternal = cc.rect(0, 0, 0, 0); 214 215 this._originalSize = cc.size(0, 0); 216 this._preferredSize = cc.size(0, 0); 217 this._capInsets = cc.rect(0, 0, 0, 0); 218 219 if(file != undefined){ 220 if(file instanceof cc.SpriteFrame) 221 this.initWithSpriteFrame(file, rect); 222 else{ 223 var frame = cc.spriteFrameCache.getSpriteFrame(file); 224 if(frame != null) 225 this.initWithSpriteFrame(frame, rect); 226 else 227 this.initWithFile(file, rect, capInsets); 228 } 229 }else{ 230 this.init(); 231 } 232 }, 233 234 getSprite: function () { 235 return this._scale9Image; 236 }, 237 238 /** Original sprite's size. */ 239 getOriginalSize: function () { 240 return cc.size(this._originalSize); 241 }, 242 243 //if the preferredSize component is given as -1, it is ignored 244 getPreferredSize: function () { 245 return cc.size(this._preferredSize); 246 }, 247 _getPreferredWidth: function () { 248 return this._preferredSize.width; 249 }, 250 _getPreferredHeight: function () { 251 return this._preferredSize.height; 252 }, 253 setPreferredSize: function (preferredSize) { 254 this.setContentSize(preferredSize); 255 this._preferredSize = preferredSize; 256 257 if (this._positionsAreDirty) { 258 this._updatePositions(); 259 this._positionsAreDirty = false; 260 this._scale9Dirty = true; 261 } 262 }, 263 _setPreferredWidth: function (value) { 264 this._setWidth(value); 265 this._preferredSize.width = value; 266 }, 267 _setPreferredHeight: function (value) { 268 this._setHeight(value); 269 this._preferredSize.height = value; 270 }, 271 272 /** Opacity: conforms to CCRGBAProtocol protocol */ 273 setOpacity: function (opacity) { 274 if(!this._scale9Image) 275 return; 276 cc.Node.prototype.setOpacity.call(this, opacity); 277 var scaleChildren = this._scale9Image.getChildren(); 278 for (var i = 0; i < scaleChildren.length; i++) { 279 var selChild = scaleChildren[i]; 280 if (selChild) 281 selChild.setOpacity(opacity); 282 } 283 this._scale9Dirty = true; 284 }, 285 286 /** Color: conforms to CCRGBAProtocol protocol */ 287 setColor: function (color) { 288 if(!this._scale9Image) 289 return; 290 291 cc.Node.prototype.setColor.call(this, color); 292 var scaleChildren = this._scale9Image.getChildren(); 293 for (var i = 0; i < scaleChildren.length; i++) { 294 var selChild = scaleChildren[i]; 295 if (selChild) 296 selChild.setColor(color); 297 } 298 this._scale9Dirty = true; 299 }, 300 301 getCapInsets: function () { 302 return cc.rect(this._capInsets); 303 }, 304 305 setCapInsets: function (capInsets) { 306 if(!this._scale9Image) 307 return; 308 //backup the contentSize 309 var contentSize = this._contentSize; 310 var tempWidth = contentSize.width, tempHeight = contentSize.height; 311 312 this.updateWithBatchNode(this._scale9Image, this._spriteRect, this._spriteFrameRotated, capInsets); 313 //restore the contentSize 314 this.setContentSize(tempWidth, tempHeight); 315 }, 316 317 /** 318 * Gets the left side inset 319 * @returns {number} 320 */ 321 getInsetLeft: function () { 322 return this._insetLeft; 323 }, 324 325 /** 326 * Sets the left side inset 327 * @param {Number} insetLeft 328 */ 329 setInsetLeft: function (insetLeft) { 330 this._insetLeft = insetLeft; 331 this._updateCapInset(); 332 }, 333 334 /** 335 * Gets the top side inset 336 * @returns {number} 337 */ 338 getInsetTop: function () { 339 return this._insetTop; 340 }, 341 342 /** 343 * Sets the top side inset 344 * @param {Number} insetTop 345 */ 346 setInsetTop: function (insetTop) { 347 this._insetTop = insetTop; 348 this._updateCapInset(); 349 }, 350 351 /** 352 * Gets the right side inset 353 * @returns {number} 354 */ 355 getInsetRight: function () { 356 return this._insetRight; 357 }, 358 /** 359 * Sets the right side inset 360 * @param {Number} insetRight 361 */ 362 setInsetRight: function (insetRight) { 363 this._insetRight = insetRight; 364 this._updateCapInset(); 365 }, 366 367 /** 368 * Gets the bottom side inset 369 * @returns {number} 370 */ 371 getInsetBottom: function () { 372 return this._insetBottom; 373 }, 374 /** 375 * Sets the bottom side inset 376 * @param {number} insetBottom 377 */ 378 setInsetBottom: function (insetBottom) { 379 this._insetBottom = insetBottom; 380 this._updateCapInset(); 381 }, 382 383 /** 384 * Sets the untransformed size of the Scale9Sprite. 385 * @override 386 * @param {cc.Size|Number} size The untransformed size of the Scale9Sprite or The untransformed size's width of the Scale9Sprite. 387 * @param {Number} [height] The untransformed size's height of the Scale9Sprite. 388 */ 389 setContentSize: function (size, height) { 390 cc.Node.prototype.setContentSize.call(this, size, height); 391 this._positionsAreDirty = true; 392 }, 393 394 _setWidth: function (value) { 395 cc.Node.prototype._setWidth.call(this, value); 396 this._positionsAreDirty = true; 397 }, 398 399 _setHeight: function (value) { 400 cc.Node.prototype._setHeight.call(this, value); 401 this._positionsAreDirty = true; 402 }, 403 404 /** 405 * Initializes a ccui.Scale9Sprite. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. 406 * @returns {boolean} 407 */ 408 init: function () { 409 return this.initWithBatchNode(null, cc.rect(0, 0, 0, 0), false, cc.rect(0, 0, 0, 0)); 410 }, 411 412 /** 413 * Initializes a 9-slice sprite with a SpriteBatchNode. 414 * @param {cc.SpriteBatchNode} batchNode 415 * @param {cc.Rect} rect 416 * @param {boolean|cc.Rect} rotated 417 * @param {cc.Rect} [capInsets] 418 * @returns {boolean} 419 */ 420 initWithBatchNode: function (batchNode, rect, rotated, capInsets) { 421 if (capInsets === undefined) { 422 capInsets = rotated; 423 rotated = false; 424 } 425 426 if (batchNode) 427 this.updateWithBatchNode(batchNode, rect, rotated, capInsets); 428 this.setCascadeColorEnabled(true); 429 this.setCascadeOpacityEnabled(true); 430 this.setAnchorPoint(0.5, 0.5); 431 this._positionsAreDirty = true; 432 return true; 433 }, 434 435 /** 436 * Initializes a 9-slice sprite with a texture file, a delimitation zone and 437 * with the specified cap insets. 438 * Once the sprite is created, you can then call its "setContentSize:" method 439 * to resize the sprite will all it's 9-slice goodness intact. 440 * It respects the anchorPoint too. 441 * 442 * @param {String} file The name of the texture file. 443 * @param {cc.Rect} rect The rectangle that describes the sub-part of the texture that 444 * is the whole image. If the shape is the whole texture, set this to the texture's full rect. 445 * @param {cc.Rect} capInsets The values to use for the cap insets. 446 */ 447 initWithFile: function (file, rect, capInsets) { 448 if (file instanceof cc.Rect) { 449 file = arguments[1]; 450 capInsets = arguments[0]; 451 rect = cc.rect(0, 0, 0, 0); 452 } else { 453 rect = rect || cc.rect(0, 0, 0, 0); 454 capInsets = capInsets || cc.rect(0, 0, 0, 0); 455 } 456 457 if(!file) 458 throw new Error("ccui.Scale9Sprite.initWithFile(): file should be non-null"); 459 460 var texture = cc.textureCache.getTextureForKey(file); 461 if (!texture) { 462 texture = cc.textureCache.addImage(file); 463 } 464 465 var locLoaded = texture.isLoaded(); 466 this._textureLoaded = locLoaded; 467 if(!locLoaded){ 468 texture.addEventListener("load", function(sender){ 469 // the texture is rotated on Canvas render mode, so isRotated always is false. 470 var preferredSize = this._preferredSize, restorePreferredSize = preferredSize.width !== 0 && preferredSize.height !== 0; 471 if (restorePreferredSize) preferredSize = cc.size(preferredSize.width, preferredSize.height); 472 var size = sender.getContentSize(); 473 this.updateWithBatchNode(this._scale9Image, cc.rect(0,0,size.width,size.height), false, this._capInsets); 474 if (restorePreferredSize)this.setPreferredSize(preferredSize); 475 this._positionsAreDirty = true; 476 this.dispatchEvent("load"); 477 }, this); 478 } 479 480 return this.initWithBatchNode(new cc.SpriteBatchNode(file, 9), rect, false, capInsets); 481 }, 482 483 /** 484 * Initializes a 9-slice sprite with an sprite frame and with the specified 485 * cap insets. 486 * Once the sprite is created, you can then call its "setContentSize:" method 487 * to resize the sprite will all it's 9-slice goodness interact. 488 * It respects the anchorPoint too. 489 * 490 * @param spriteFrame The sprite frame object. 491 * @param capInsets The values to use for the cap insets. 492 */ 493 initWithSpriteFrame: function (spriteFrame, capInsets) { 494 if(!spriteFrame || !spriteFrame.getTexture()) 495 throw new Error("ccui.Scale9Sprite.initWithSpriteFrame(): spriteFrame should be non-null and its texture should be non-null"); 496 497 capInsets = capInsets || cc.rect(0, 0, 0, 0); 498 var locLoaded = spriteFrame.textureLoaded(); 499 this._textureLoaded = locLoaded; 500 if(!locLoaded){ 501 spriteFrame.addEventListener("load", function(sender){ 502 // the texture is rotated on Canvas render mode, so isRotated always is false. 503 var preferredSize = this._preferredSize, restorePreferredSize = preferredSize.width !== 0 && preferredSize.height !== 0; 504 if (restorePreferredSize) preferredSize = cc.size(preferredSize.width, preferredSize.height); 505 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc._renderType === cc._RENDER_TYPE_WEBGL && sender.isRotated(), this._capInsets); 506 if (restorePreferredSize)this.setPreferredSize(preferredSize); 507 this._positionsAreDirty = true; 508 this.dispatchEvent("load"); 509 },this); 510 } 511 var batchNode = new cc.SpriteBatchNode(spriteFrame.getTexture(), 9); 512 // the texture is rotated on Canvas render mode, so isRotated always is false. 513 return this.initWithBatchNode(batchNode, spriteFrame.getRect(), cc._renderType === cc._RENDER_TYPE_WEBGL && spriteFrame.isRotated(), capInsets); 514 }, 515 516 /** 517 * Initializes a 9-slice sprite with an sprite frame name and with the specified 518 * cap insets. 519 * Once the sprite is created, you can then call its "setContentSize:" method 520 * to resize the sprite will all it's 9-slice goodness interact. 521 * It respects the anchorPoint too. 522 * 523 * @param spriteFrameName The sprite frame name. 524 * @param capInsets The values to use for the cap insets. 525 */ 526 initWithSpriteFrameName: function (spriteFrameName, capInsets) { 527 if(!spriteFrameName) 528 throw new Error("ccui.Scale9Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null"); 529 capInsets = capInsets || cc.rect(0, 0, 0, 0); 530 531 var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName); 532 if (frame == null) { 533 cc.log("ccui.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName"); 534 return false; 535 } 536 537 return this.initWithSpriteFrame(frame, capInsets); 538 }, 539 540 /** 541 * Creates and returns a new sprite object with the specified cap insets. 542 * You use this method to add cap insets to a sprite or to change the existing 543 * cap insets of a sprite. In both cases, you get back a new image and the 544 * original sprite remains untouched. 545 * 546 * @param {cc.Rect} capInsets The values to use for the cap insets. 547 */ 548 resizableSpriteWithCapInsets: function (capInsets) { 549 var pReturn = new ccui.Scale9Sprite(); 550 if (pReturn && pReturn.initWithBatchNode(this._scale9Image, this._spriteRect, false, capInsets)) 551 return pReturn; 552 return null; 553 }, 554 555 /** sets the premultipliedAlphaOpacity property. 556 If set to NO then opacity will be applied as: glColor(R,G,B,opacity); 557 If set to YES then opacity will be applied as: glColor(opacity, opacity, opacity, opacity ); 558 Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO 559 @since v0.8 560 */ 561 setOpacityModifyRGB: function (value) { 562 if(!this._scale9Image) 563 return; 564 this._opacityModifyRGB = value; 565 var scaleChildren = this._scale9Image.getChildren(); 566 if (scaleChildren) { 567 for (var i = 0, len = scaleChildren.length; i < len; i++) 568 scaleChildren[i].setOpacityModifyRGB(value); 569 } 570 }, 571 572 /** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity); 573 @since v0.8 574 */ 575 isOpacityModifyRGB: function () { 576 return this._opacityModifyRGB; 577 }, 578 579 /** 580 * Update the scale9Sprite with a SpriteBatchNode. 581 * @param {cc.SpriteBatchNode} batchNode 582 * @param {cc.Rect} originalRect 583 * @param {boolean} rotated 584 * @param {cc.Rect} capInsets 585 * @returns {boolean} 586 */ 587 updateWithBatchNode: function (batchNode, originalRect, rotated, capInsets) { 588 var opacity = this.getOpacity(); 589 var color = this.getColor(); 590 var rect = cc.rect(originalRect.x, originalRect.y, originalRect.width, originalRect.height); 591 592 // Release old sprites 593 this.removeAllChildren(true); 594 595 if (this._scale9Image !== batchNode) 596 this._scale9Image = batchNode; 597 598 if(!this._scale9Image) 599 return false; 600 601 var tmpTexture = batchNode.getTexture(); 602 var locLoaded = tmpTexture.isLoaded(); 603 this._textureLoaded = locLoaded; 604 605 //this._capInsets = capInsets; 606 var locCapInsets = this._capInsets; 607 locCapInsets.x = capInsets.x; 608 locCapInsets.y = capInsets.y; 609 locCapInsets.width = capInsets.width; 610 locCapInsets.height = capInsets.height; 611 612 if(!locLoaded){ 613 tmpTexture.addEventListener("load", function(sender){ 614 this._positionsAreDirty = true; 615 this.dispatchEvent("load"); 616 },this); 617 return true; 618 } 619 var locScale9Image = this._scale9Image; 620 locScale9Image.removeAllChildren(true); 621 622 this._spriteFrameRotated = rotated; 623 624 var selTexture = locScale9Image.getTexture(); 625 626 // If there is no given rect 627 if (cc._rectEqualToZero(rect)) { 628 // Get the texture size as original 629 var textureSize = selTexture.getContentSize(); 630 rect = cc.rect(0, 0, textureSize.width, textureSize.height); 631 } 632 633 // Set the given rect's size as original size 634 this._spriteRect = rect; 635 var locSpriteRect = this._spriteRect; 636 locSpriteRect.x = rect.x; 637 locSpriteRect.y = rect.y; 638 locSpriteRect.width = rect.width; 639 locSpriteRect.height = rect.height; 640 641 this._originalSize.width = rect.width; 642 this._originalSize.height = rect.height; 643 644 var locPreferredSize = this._preferredSize; 645 if(locPreferredSize.width === 0 && locPreferredSize.height === 0){ 646 locPreferredSize.width = rect.width; 647 locPreferredSize.height = rect.height; 648 } 649 650 var locCapInsetsInternal = this._capInsetsInternal; 651 if(capInsets){ 652 locCapInsetsInternal.x = capInsets.x; 653 locCapInsetsInternal.y = capInsets.y; 654 locCapInsetsInternal.width = capInsets.width; 655 locCapInsetsInternal.height = capInsets.height; 656 } 657 var w = rect.width, h = rect.height; 658 659 // If there is no specified center region 660 if (cc._rectEqualToZero(locCapInsetsInternal)) { 661 // CCLog("... cap insets not specified : using default cap insets ..."); 662 locCapInsetsInternal.x = w / 3; 663 locCapInsetsInternal.y = h / 3; 664 locCapInsetsInternal.width = w / 3; 665 locCapInsetsInternal.height = h / 3; 666 } 667 668 var left_w = locCapInsetsInternal.x, center_w = locCapInsetsInternal.width, right_w = w - (left_w + center_w); 669 670 var top_h = locCapInsetsInternal.y, center_h = locCapInsetsInternal.height, bottom_h = h - (top_h + center_h); 671 672 // calculate rects 673 // ... top row 674 var x = 0.0, y = 0.0; 675 676 // top left 677 var lefttopbounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, left_w + 0.5 | 0, top_h + 0.5 | 0); 678 679 // top center 680 x += left_w; 681 var centertopbounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, center_w + 0.5 | 0, top_h + 0.5 | 0); 682 683 // top right 684 x += center_w; 685 var righttopbounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, right_w + 0.5 | 0, top_h + 0.5 | 0); 686 687 // ... center row 688 x = 0.0; 689 y = 0.0; 690 691 y += top_h; 692 // center left 693 var leftcenterbounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, left_w + 0.5 | 0, center_h + 0.5 | 0); 694 695 // center center 696 x += left_w; 697 var centerbounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, center_w + 0.5 | 0, center_h + 0.5 | 0); 698 699 // center right 700 x += center_w; 701 var rightcenterbounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, right_w + 0.5 | 0, center_h + 0.5 | 0); 702 703 // ... bottom row 704 x = 0.0; 705 y = 0.0; 706 y += top_h; 707 y += center_h; 708 709 // bottom left 710 var leftbottombounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, left_w + 0.5 | 0, bottom_h + 0.5 | 0); 711 712 // bottom center 713 x += left_w; 714 var centerbottombounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, center_w + 0.5 | 0, bottom_h + 0.5 | 0); 715 716 // bottom right 717 x += center_w; 718 var rightbottombounds = cc.rect(x + 0.5 | 0, y + 0.5 | 0, right_w + 0.5 | 0, bottom_h + 0.5 | 0); 719 720 var t = cc.affineTransformMakeIdentity(); 721 if (!rotated) { 722 // CCLog("!rotated"); 723 t = cc.affineTransformTranslate(t, rect.x, rect.y); 724 725 cc._rectApplyAffineTransformIn(centerbounds, t); 726 cc._rectApplyAffineTransformIn(rightbottombounds, t); 727 cc._rectApplyAffineTransformIn(leftbottombounds, t); 728 cc._rectApplyAffineTransformIn(righttopbounds, t); 729 cc._rectApplyAffineTransformIn(lefttopbounds, t); 730 cc._rectApplyAffineTransformIn(rightcenterbounds, t); 731 cc._rectApplyAffineTransformIn(leftcenterbounds, t); 732 cc._rectApplyAffineTransformIn(centerbottombounds, t); 733 cc._rectApplyAffineTransformIn(centertopbounds, t); 734 735 // Centre 736 this._centre = new cc.Sprite(); 737 this._centre.initWithTexture(selTexture, centerbounds); 738 locScale9Image.addChild(this._centre, 0, ccui.Scale9Sprite.POSITIONS_CENTRE); 739 740 // Top 741 this._top = new cc.Sprite(); 742 this._top.initWithTexture(selTexture, centertopbounds); 743 locScale9Image.addChild(this._top, 1, ccui.Scale9Sprite.POSITIONS_TOP); 744 745 // Bottom 746 this._bottom = new cc.Sprite(); 747 this._bottom.initWithTexture(selTexture, centerbottombounds); 748 locScale9Image.addChild(this._bottom, 1, ccui.Scale9Sprite.POSITIONS_BOTTOM); 749 750 // Left 751 this._left = new cc.Sprite(); 752 this._left.initWithTexture(selTexture, leftcenterbounds); 753 locScale9Image.addChild(this._left, 1, ccui.Scale9Sprite.POSITIONS_LEFT); 754 755 // Right 756 this._right = new cc.Sprite(); 757 this._right.initWithTexture(selTexture, rightcenterbounds); 758 locScale9Image.addChild(this._right, 1, ccui.Scale9Sprite.POSITIONS_RIGHT); 759 760 // Top left 761 this._topLeft = new cc.Sprite(); 762 this._topLeft.initWithTexture(selTexture, lefttopbounds); 763 locScale9Image.addChild(this._topLeft, 2, ccui.Scale9Sprite.POSITIONS_TOPLEFT); 764 765 // Top right 766 this._topRight = new cc.Sprite(); 767 this._topRight.initWithTexture(selTexture, righttopbounds); 768 locScale9Image.addChild(this._topRight, 2, ccui.Scale9Sprite.POSITIONS_TOPRIGHT); 769 770 // Bottom left 771 this._bottomLeft = new cc.Sprite(); 772 this._bottomLeft.initWithTexture(selTexture, leftbottombounds); 773 locScale9Image.addChild(this._bottomLeft, 2, ccui.Scale9Sprite.POSITIONS_BOTTOMLEFT); 774 775 // Bottom right 776 this._bottomRight = new cc.Sprite(); 777 this._bottomRight.initWithTexture(selTexture, rightbottombounds); 778 locScale9Image.addChild(this._bottomRight, 2, ccui.Scale9Sprite.POSITIONS_BOTTOMRIGHT); 779 } else { 780 // set up transformation of coordinates 781 // to handle the case where the sprite is stored rotated 782 // in the spritesheet 783 // CCLog("rotated"); 784 var rotatedcenterbounds = centerbounds; 785 var rotatedrightbottombounds = rightbottombounds; 786 var rotatedleftbottombounds = leftbottombounds; 787 var rotatedrighttopbounds = righttopbounds; 788 var rotatedlefttopbounds = lefttopbounds; 789 var rotatedrightcenterbounds = rightcenterbounds; 790 var rotatedleftcenterbounds = leftcenterbounds; 791 var rotatedcenterbottombounds = centerbottombounds; 792 var rotatedcentertopbounds = centertopbounds; 793 794 t = cc.affineTransformTranslate(t, rect.height + rect.x, rect.y); 795 t = cc.affineTransformRotate(t, 1.57079633); 796 797 centerbounds = cc.rectApplyAffineTransform(centerbounds, t); 798 rightbottombounds = cc.rectApplyAffineTransform(rightbottombounds, t); 799 leftbottombounds = cc.rectApplyAffineTransform(leftbottombounds, t); 800 righttopbounds = cc.rectApplyAffineTransform(righttopbounds, t); 801 lefttopbounds = cc.rectApplyAffineTransform(lefttopbounds, t); 802 rightcenterbounds = cc.rectApplyAffineTransform(rightcenterbounds, t); 803 leftcenterbounds = cc.rectApplyAffineTransform(leftcenterbounds, t); 804 centerbottombounds = cc.rectApplyAffineTransform(centerbottombounds, t); 805 centertopbounds = cc.rectApplyAffineTransform(centertopbounds, t); 806 807 rotatedcenterbounds.x = centerbounds.x; 808 rotatedcenterbounds.y = centerbounds.y; 809 810 rotatedrightbottombounds.x = rightbottombounds.x; 811 rotatedrightbottombounds.y = rightbottombounds.y; 812 813 rotatedleftbottombounds.x = leftbottombounds.x; 814 rotatedleftbottombounds.y = leftbottombounds.y; 815 816 rotatedrighttopbounds.x = righttopbounds.x; 817 rotatedrighttopbounds.y = righttopbounds.y; 818 819 rotatedlefttopbounds.x = lefttopbounds.x; 820 rotatedlefttopbounds.y = lefttopbounds.y; 821 822 rotatedrightcenterbounds.x = rightcenterbounds.x; 823 rotatedrightcenterbounds.y = rightcenterbounds.y; 824 825 rotatedleftcenterbounds.x = leftcenterbounds.x; 826 rotatedleftcenterbounds.y = leftcenterbounds.y; 827 828 rotatedcenterbottombounds.x = centerbottombounds.x; 829 rotatedcenterbottombounds.y = centerbottombounds.y; 830 831 rotatedcentertopbounds.x = centertopbounds.x; 832 rotatedcentertopbounds.y = centertopbounds.y; 833 834 // Centre 835 this._centre = new cc.Sprite(); 836 this._centre.initWithTexture(selTexture, rotatedcenterbounds, true); 837 locScale9Image.addChild(this._centre, 0, ccui.Scale9Sprite.POSITIONS_CENTRE); 838 839 // Top 840 this._top = new cc.Sprite(); 841 this._top.initWithTexture(selTexture, rotatedcentertopbounds, true); 842 locScale9Image.addChild(this._top, 1, ccui.Scale9Sprite.POSITIONS_TOP); 843 844 // Bottom 845 this._bottom = new cc.Sprite(); 846 this._bottom.initWithTexture(selTexture, rotatedcenterbottombounds, true); 847 locScale9Image.addChild(this._bottom, 1, ccui.Scale9Sprite.POSITIONS_BOTTOM); 848 849 // Left 850 this._left = new cc.Sprite(); 851 this._left.initWithTexture(selTexture, rotatedleftcenterbounds, true); 852 locScale9Image.addChild(this._left, 1, ccui.Scale9Sprite.POSITIONS_LEFT); 853 854 // Right 855 this._right = new cc.Sprite(); 856 this._right.initWithTexture(selTexture, rotatedrightcenterbounds, true); 857 locScale9Image.addChild(this._right, 1, ccui.Scale9Sprite.POSITIONS_RIGHT); 858 859 // Top left 860 this._topLeft = new cc.Sprite(); 861 this._topLeft.initWithTexture(selTexture, rotatedlefttopbounds, true); 862 locScale9Image.addChild(this._topLeft, 2, ccui.Scale9Sprite.POSITIONS_TOPLEFT); 863 864 // Top right 865 this._topRight = new cc.Sprite(); 866 this._topRight.initWithTexture(selTexture, rotatedrighttopbounds, true); 867 locScale9Image.addChild(this._topRight, 2, ccui.Scale9Sprite.POSITIONS_TOPRIGHT); 868 869 // Bottom left 870 this._bottomLeft = new cc.Sprite(); 871 this._bottomLeft.initWithTexture(selTexture, rotatedleftbottombounds, true); 872 locScale9Image.addChild(this._bottomLeft, 2, ccui.Scale9Sprite.POSITIONS_BOTTOMLEFT); 873 874 // Bottom right 875 this._bottomRight = new cc.Sprite(); 876 this._bottomRight.initWithTexture(selTexture, rotatedrightbottombounds, true); 877 locScale9Image.addChild(this._bottomRight, 2, ccui.Scale9Sprite.POSITIONS_BOTTOMRIGHT); 878 } 879 880 this.setContentSize(rect.width, rect.height); 881 if(cc._renderType === cc._RENDER_TYPE_WEBGL) 882 this.addChild(locScale9Image); 883 884 if (this._spritesGenerated) { 885 // Restore color and opacity 886 this.setOpacity(opacity); 887 this.setColor(color); 888 } 889 this._spritesGenerated = true; 890 return true; 891 }, 892 893 /** 894 * set the sprite frame of ccui.Scale9Sprite 895 * @param {cc.SpriteFrame} spriteFrame 896 */ 897 setSpriteFrame: function (spriteFrame) { 898 var batchNode = new cc.SpriteBatchNode(spriteFrame.getTexture(), 9); 899 // the texture is rotated on Canvas render mode, so isRotated always is false. 900 var locLoaded = spriteFrame.textureLoaded(); 901 this._textureLoaded = locLoaded; 902 if(!locLoaded){ 903 spriteFrame.addEventListener("load", function(sender){ 904 // the texture is rotated on Canvas render mode, so isRotated always is false. 905 var preferredSize = this._preferredSize, restorePreferredSize = preferredSize.width !== 0 && preferredSize.height !== 0; 906 if (restorePreferredSize) preferredSize = cc.size(preferredSize.width, preferredSize.height); 907 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc._renderType === cc._RENDER_TYPE_WEBGL && sender.isRotated(), this._capInsets); 908 if (restorePreferredSize)this.setPreferredSize(preferredSize); 909 this._positionsAreDirty = true; 910 this.dispatchEvent("load"); 911 },this); 912 } 913 this.updateWithBatchNode(batchNode, spriteFrame.getRect(), cc._renderType === cc._RENDER_TYPE_WEBGL && spriteFrame.isRotated(), cc.rect(0, 0, 0, 0)); 914 915 // Reset insets 916 this._insetLeft = 0; 917 this._insetTop = 0; 918 this._insetRight = 0; 919 this._insetBottom = 0; 920 }, 921 922 //v3.3 923 /** 924 * Sets ccui.Scale9Sprite's state 925 * @since v3.3 926 * @param {Number} state 927 */ 928 setState: function(state){ 929 this._renderCmd.setState(state); 930 }, 931 932 //setScale9Enabled implement late 933 934 /** 935 * Sets whether the widget should be flipped horizontally or not. 936 * @since v3.3 937 * @param flippedX true if the widget should be flipped horizontally, false otherwise. 938 */ 939 setFlippedX: function(flippedX){ 940 var realScale = this.getScaleX(); 941 this._flippedX = flippedX; 942 this.setScaleX(realScale); 943 }, 944 945 /** 946 * <p> 947 * Returns the flag which indicates whether the widget is flipped horizontally or not. <br/> 948 * <br/> 949 * It only flips the texture of the widget, and not the texture of the widget's children. <br/> 950 * Also, flipping the texture doesn't alter the anchorPoint. <br/> 951 * If you want to flip the anchorPoint too, and/or to flip the children too use: <br/> 952 * widget->setScaleX(sprite->getScaleX() * -1); <br/> 953 * </p> 954 * @since v3.3 955 * @return {Boolean} true if the widget is flipped horizontally, false otherwise. 956 */ 957 isFlippedX: function(){ 958 return this._flippedX; 959 }, 960 961 /** 962 * Sets whether the widget should be flipped vertically or not. 963 * @since v3.3 964 * @param flippedY true if the widget should be flipped vertically, false otherwise. 965 */ 966 setFlippedY:function(flippedY){ 967 var realScale = this.getScaleY(); 968 this._flippedY = flippedY; 969 this.setScaleY(realScale); 970 }, 971 972 /** 973 * <p> 974 * Return the flag which indicates whether the widget is flipped vertically or not. <br/> 975 * <br/> 976 * It only flips the texture of the widget, and not the texture of the widget's children. <br/> 977 * Also, flipping the texture doesn't alter the anchorPoint. <br/> 978 * If you want to flip the anchorPoint too, and/or to flip the children too use: <br/> 979 * widget->setScaleY(widget->getScaleY() * -1); <br/> 980 * </p> 981 * @since v3.3 982 * @return {Boolean} true if the widget is flipped vertically, false otherwise. 983 */ 984 isFlippedY:function(){ 985 return this._flippedY; 986 }, 987 988 setScaleX: function (scaleX) { 989 if (this._flippedX) 990 scaleX = scaleX * -1; 991 cc.Node.prototype.setScaleX.call(this, scaleX); 992 }, 993 994 setScaleY: function (scaleY) { 995 if (this._flippedY) 996 scaleY = scaleY * -1; 997 cc.Node.prototype.setScaleY.call(this, scaleY); 998 }, 999 1000 setScale: function (scaleX, scaleY) { 1001 if(scaleY === undefined) 1002 scaleY = scaleX; 1003 this.setScaleX(scaleX); 1004 this.setScaleY(scaleY); 1005 }, 1006 1007 getScaleX: function () { 1008 var originalScale = cc.Node.prototype.getScaleX.call(this); 1009 if (this._flippedX) 1010 originalScale = originalScale * -1.0; 1011 return originalScale; 1012 }, 1013 1014 getScaleY: function () { 1015 var originalScale = cc.Node.prototype.getScaleY.call(this); 1016 if (this._flippedY) 1017 originalScale = originalScale * -1.0; 1018 return originalScale; 1019 }, 1020 1021 getScale: function () { 1022 if(this.getScaleX() !== this.getScaleY()) 1023 cc.log("Scale9Sprite#scale. ScaleX != ScaleY. Don't know which one to return"); 1024 return this.getScaleX(); 1025 }, 1026 1027 _createRenderCmd: function(){ 1028 if(cc._renderType === cc._RENDER_TYPE_CANVAS) 1029 return new ccui.Scale9Sprite.CanvasRenderCmd(this); 1030 else 1031 return new ccui.Scale9Sprite.WebGLRenderCmd(this); 1032 } 1033 }); 1034 1035 var _p = ccui.Scale9Sprite.prototype; 1036 cc.EventHelper.prototype.apply(_p); 1037 1038 // Extended properties 1039 /** @expose */ 1040 _p.preferredSize; 1041 cc.defineGetterSetter(_p, "preferredSize", _p.getPreferredSize, _p.setPreferredSize); 1042 /** @expose */ 1043 _p.capInsets; 1044 cc.defineGetterSetter(_p, "capInsets", _p.getCapInsets, _p.setCapInsets); 1045 /** @expose */ 1046 _p.insetLeft; 1047 cc.defineGetterSetter(_p, "insetLeft", _p.getInsetLeft, _p.setInsetLeft); 1048 /** @expose */ 1049 _p.insetTop; 1050 cc.defineGetterSetter(_p, "insetTop", _p.getInsetTop, _p.setInsetTop); 1051 /** @expose */ 1052 _p.insetRight; 1053 cc.defineGetterSetter(_p, "insetRight", _p.getInsetRight, _p.setInsetRight); 1054 /** @expose */ 1055 _p.insetBottom; 1056 cc.defineGetterSetter(_p, "insetBottom", _p.getInsetBottom, _p.setInsetBottom); 1057 1058 _p = null; 1059 1060 /** 1061 * Creates a 9-slice sprite with a texture file, a delimitation zone and 1062 * with the specified cap insets. 1063 * @deprecated since v3.0, please use new ccui.Scale9Sprite(file, rect, capInsets) instead. 1064 * @param {String|cc.SpriteFrame} file file name of texture or a cc.Sprite object 1065 * @param {cc.Rect} rect the rect of the texture 1066 * @param {cc.Rect} capInsets the cap insets of ccui.Scale9Sprite 1067 * @returns {ccui.Scale9Sprite} 1068 */ 1069 ccui.Scale9Sprite.create = function (file, rect, capInsets) { 1070 return new ccui.Scale9Sprite(file, rect, capInsets); 1071 }; 1072 1073 /** 1074 * create a ccui.Scale9Sprite with Sprite frame. 1075 * @deprecated since v3.0, please use "new ccui.Scale9Sprite(spriteFrame, capInsets)" instead. 1076 * @param {cc.SpriteFrame} spriteFrame 1077 * @param {cc.Rect} capInsets 1078 * @returns {ccui.Scale9Sprite} 1079 */ 1080 ccui.Scale9Sprite.createWithSpriteFrame = function (spriteFrame, capInsets) { 1081 return new ccui.Scale9Sprite(spriteFrame, capInsets); 1082 }; 1083 1084 /** 1085 * create a ccui.Scale9Sprite with a Sprite frame name 1086 * @deprecated since v3.0, please use "new ccui.Scale9Sprite(spriteFrameName, capInsets)" instead. 1087 * @param {string} spriteFrameName 1088 * @param {cc.Rect} capInsets 1089 * @returns {Scale9Sprite} 1090 */ 1091 ccui.Scale9Sprite.createWithSpriteFrameName = function (spriteFrameName, capInsets) { 1092 return new ccui.Scale9Sprite(spriteFrameName, capInsets); 1093 }; 1094 1095 /** 1096 * @ignore 1097 */ 1098 ccui.Scale9Sprite.POSITIONS_CENTRE = 0; 1099 ccui.Scale9Sprite.POSITIONS_TOP = 1; 1100 ccui.Scale9Sprite.POSITIONS_LEFT = 2; 1101 ccui.Scale9Sprite.POSITIONS_RIGHT = 3; 1102 ccui.Scale9Sprite.POSITIONS_BOTTOM = 4; 1103 ccui.Scale9Sprite.POSITIONS_TOPRIGHT = 5; 1104 ccui.Scale9Sprite.POSITIONS_TOPLEFT = 6; 1105 ccui.Scale9Sprite.POSITIONS_BOTTOMRIGHT = 7; 1106 1107 ccui.Scale9Sprite.state = {NORMAL: 0, GRAY: 1}; 1108