1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 4 http://www.cocos2d-x.org 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 THE SOFTWARE. 23 ****************************************************************************/ 24 25 /** 26 * PageView event type 27 * @type {Object} 28 */ 29 ccs.PageViewEventType = { 30 turning: 0 31 }; 32 33 /** 34 * PageView touch direction 35 * @type {Object} 36 */ 37 ccs.PVTouchDir = { 38 touchLeft: 0, 39 touchRight: 1 40 }; 41 42 /** 43 * Base class for ccs.UIPageView 44 * @class 45 * @extends ccs.UILayout 46 */ 47 ccs.UIPageView = ccs.UILayout.extend(/** @lends ccs.UIPageView# */{ 48 _curPageIdx: 0, 49 _pages: null, 50 _touchMoveDir: null, 51 _touchStartLocation: 0, 52 _touchEndLocation: 0, 53 _touchMoveStartLocation: 0, 54 _movePagePoint: null, 55 _leftChild: null, 56 _rightChild: null, 57 _leftBoundary: 0, 58 _rightBoundary: 0, 59 _isAutoScrolling: false, 60 _autoScrollDistance: 0, 61 _autoScrollSpeed: 0, 62 _autoScrollDir: 0, 63 _childFocusCancelOffset: 0, 64 _pageViewEventListener: null, 65 _pageViewEventSelector: null, 66 ctor: function () { 67 ccs.UILayout.prototype.ctor.call(this); 68 this._curPageIdx = 0; 69 this._pages = []; 70 this._touchMoveDir = ccs.PVTouchDir.touchLeft; 71 this._touchStartLocation = 0; 72 this._touchEndLocation = 0; 73 this._touchMoveStartLocation = 0; 74 this._movePagePoint = null; 75 this._leftChild = null; 76 this._rightChild = null; 77 this._leftBoundary = 0; 78 this._rightBoundary = 0; 79 this._isAutoScrolling = false; 80 this._autoScrollDistance = 0; 81 this._autoScrollSpeed = 0; 82 this._autoScrollDir = 0; 83 this._childFocusCancelOffset = 5; 84 this._pageViewEventListener = null; 85 this._pageViewEventSelector = null; 86 }, 87 88 init: function () { 89 if (ccs.UILayout.prototype.init.call(this)) { 90 this._pages = []; 91 this.setClippingEnabled(true); 92 this.setUpdateEnabled(true); 93 this.setTouchEnabled(true); 94 return true; 95 } 96 return false; 97 }, 98 99 /** 100 * Add a widget to a page of pageview. 101 * @param {ccs.UIWidget} widget 102 * @param {number} pageIdx 103 * @param {Boolean} forceCreate 104 */ 105 addWidgetToPage: function (widget, pageIdx, forceCreate) { 106 if (!widget) { 107 return; 108 } 109 if(pageIdx<0){ 110 return; 111 } 112 var pageCount = this._pages.length; 113 if (pageIdx >= pageCount) { 114 if (forceCreate) { 115 if (pageIdx > pageCount) { 116 cc.log("pageIdx is %d, it will be added as page id [%d]", pageIdx, pageCount); 117 } 118 var newPage = this.createPage(); 119 newPage.addChild(widget); 120 this.addPage(newPage); 121 } 122 } 123 else { 124 var page = this._pages[pageIdx]; 125 if (page) { 126 page.addChild(widget); 127 } 128 } 129 }, 130 131 /** 132 * create page 133 * @returns {ccs.UILayout} 134 */ 135 createPage: function () { 136 var newPage = ccs.UILayout.create(); 137 newPage.setSize(this.getSize()); 138 return newPage; 139 }, 140 141 /** 142 * Push back a page to pageview. 143 * @param {ccs.UILayout} page 144 */ 145 addPage: function (page) { 146 if (!page) { 147 return; 148 } 149 if (page.getWidgetType() != ccs.WidgetType.container) { 150 return; 151 } 152 if (cc.ArrayContainsObject(this._pages, page)) { 153 return; 154 } 155 var pSize = page.getSize(); 156 var pvSize = this.getSize(); 157 if (!(pSize.width==pvSize.width&&pSize.height==pvSize.height)) { 158 cc.log("page size does not match pageview size, it will be force sized!"); 159 page.setSize(pvSize); 160 } 161 page.setPosition(cc.p(this.getPositionXByIndex(this._pages.length), 0)); 162 this._pages.push(page); 163 this.addChild(page); 164 this.updateBoundaryPages(); 165 }, 166 167 /** 168 * Inert a page to pageview. 169 * @param {ccs.UILayout} page 170 * @param {Number} idx 171 */ 172 insertPage: function (page, idx) { 173 if (idx < 0) { 174 return; 175 } 176 if (!page) { 177 return; 178 } 179 if (page.getWidgetType() != ccs.WidgetType.container) { 180 return; 181 } 182 if (cc.ArrayContainsObject(this._pages, page)) { 183 return; 184 } 185 186 var pageCount = this._pages.length; 187 if (idx >= pageCount) { 188 this.addPage(page); 189 } 190 else { 191 cc.ArrayAppendObjectToIndex(this._pages, page, idx); 192 page.setPosition(cc.p(this.getPositionXByIndex(idx), 0)); 193 this.addChild(page); 194 var pSize = page.getSize(); 195 var pvSize = this.getSize(); 196 if (!pSize.equals(pvSize)) { 197 cc.log("page size does not match pageview size, it will be force sized!"); 198 page.setSize(pvSize); 199 } 200 var arrayPages = this._pages; 201 var length = arrayPages.length; 202 for (var i = (idx + 1); i < length; i++) { 203 var behindPage = arrayPages[i]; 204 var formerPos = behindPage.getPosition(); 205 behindPage.setPosition(cc.p(formerPos.x + this.getSize().width, 0)); 206 } 207 this.updateBoundaryPages(); 208 } 209 }, 210 211 /** 212 * Remove a page of pageview. 213 * @param {ccs.UILayout} page 214 */ 215 removePage: function (page) { 216 if (!page) { 217 return; 218 } 219 this.removeChild(page); 220 this.updateChildrenPosition(); 221 this.updateBoundaryPages(); 222 }, 223 224 /** 225 * Remove a page at index of pageview. 226 * @param {number} index 227 */ 228 removePageAtIndex: function (index) { 229 if (index < 0 || index >= this._pages.length) { 230 return; 231 } 232 var page = this._pages[index]; 233 if (page) { 234 this.removePage(page); 235 } 236 }, 237 238 updateBoundaryPages: function () { 239 if (this._pages.length <= 0) { 240 this._leftChild = null; 241 this._rightChild = null; 242 return; 243 } 244 this._leftChild = this._pages[0]; 245 this._rightChild = this._pages[this._pages.length-1]; 246 }, 247 248 /** 249 * Get x position by index 250 * @param {number} idx 251 * @returns {number} 252 */ 253 getPositionXByIndex: function (idx) { 254 return (this.getSize().width * (idx - this._curPageIdx)); 255 }, 256 257 /** 258 * Add widget 259 * @param {ccs.UIWidget} widget 260 * @returns {boolean} 261 */ 262 addChild: function (widget) { 263 return ccs.UILayout.prototype.addChild.call(this, widget); 264 }, 265 266 /** 267 * remove widget child override 268 * @param {ccs.UIWidget} child 269 * @returns {boolean} 270 */ 271 removeChild: function (widget) { 272 if (cc.ArrayContainsObject(this._pages, widget)) { 273 cc.ArrayRemoveObject(this._pages, widget); 274 return ccs.UILayout.prototype.removeChild.call(this, widget); 275 } 276 return false; 277 }, 278 279 onSizeChanged: function () { 280 ccs.UILayout.prototype.onSizeChanged.call(this); 281 this._rightBoundary = this.getSize().width; 282 this.updateChildrenSize(); 283 this.updateChildrenPosition(); 284 }, 285 286 updateChildrenSize: function () { 287 if (!this._pages.length <= 0) { 288 return; 289 } 290 291 var selfSize = this.getSize(); 292 for (var i = 0; i < this._pages.length; i++) { 293 var page = this._pages[i]; 294 page.setSize(selfSize); 295 } 296 }, 297 298 updateChildrenPosition: function () { 299 if (!this._pages) { 300 return; 301 } 302 303 var pageCount = this._pages.length; 304 if (pageCount <= 0) { 305 this._curPageIdx = 0; 306 return; 307 } 308 if (this._curPageIdx >= pageCount) { 309 this._curPageIdx = pageCount - 1; 310 } 311 var pageWidth = this.getSize().width; 312 var arrayPages = this._pages; 313 for (var i = 0; i < pageCount; i++) { 314 var page = arrayPages[i]; 315 page.setPosition(cc.p((i - this._curPageIdx) * pageWidth, 0)); 316 } 317 }, 318 319 removeAllChildren: function () { 320 this._pages = []; 321 ccs.UILayout.prototype.removeAllChildren.call(this); 322 }, 323 324 /** 325 * scroll pageview to index. 326 * @param {number} idx 327 */ 328 scrollToPage: function (idx) { 329 if (idx < 0 || idx >= this._pages.length) { 330 return; 331 } 332 this._curPageIdx = idx; 333 var curPage = this._pages[idx]; 334 this._autoScrollDistance = -(curPage.getPosition().x); 335 this._autoScrollSpeed = Math.abs(this._autoScrollDistance) / 0.2; 336 this._autoScrollDir = this._autoScrollDistance > 0 ? 1 : 0; 337 this._isAutoScrolling = true; 338 }, 339 340 update: function (dt) { 341 if (this._isAutoScrolling) { 342 switch (this._autoScrollDir) { 343 case 0: 344 var step = this._autoScrollSpeed * dt; 345 if (this._autoScrollDistance + step >= 0.0) { 346 step = -this._autoScrollDistance; 347 this._autoScrollDistance = 0.0; 348 this._isAutoScrolling = false; 349 } 350 else { 351 this._autoScrollDistance += step; 352 } 353 this.scrollPages(-step); 354 if(!this._isAutoScrolling){ 355 this.pageTurningEvent(); 356 } 357 break; 358 break; 359 case 1: 360 var step = this._autoScrollSpeed * dt; 361 if (this._autoScrollDistance - step <= 0.0) { 362 step = this._autoScrollDistance; 363 this._autoScrollDistance = 0.0; 364 this._isAutoScrolling = false; 365 } 366 else { 367 this._autoScrollDistance -= step; 368 } 369 this.scrollPages(step); 370 if(!this._isAutoScrolling){ 371 this.pageTurningEvent(); 372 } 373 break; 374 default: 375 break; 376 } 377 } 378 }, 379 380 onTouchBegan: function (touchPoint) { 381 var pass = ccs.UILayout.prototype.onTouchBegan.call(this, touchPoint); 382 this.handlePressLogic(touchPoint); 383 return pass; 384 }, 385 386 onTouchMoved: function (touchPoint) { 387 this._touchMovePos.x = touchPoint.x; 388 this._touchMovePos.y = touchPoint.y; 389 this.handleMoveLogic(touchPoint); 390 if (this._widgetParent) { 391 this._widgetParent.checkChildInfo(1, this, touchPoint); 392 } 393 this.moveEvent(); 394 if (!this.hitTest(touchPoint)) { 395 this.setFocused(false); 396 this.onTouchEnded(touchPoint); 397 } 398 }, 399 400 onTouchEnded: function (touchPoint) { 401 ccs.UILayout.prototype.onTouchEnded.call(this, touchPoint); 402 this.handleReleaseLogic(touchPoint); 403 }, 404 405 movePages: function (offset) { 406 var arrayPages = this._pages; 407 var length = arrayPages.length; 408 for (var i = 0; i < length; i++) { 409 var child = arrayPages[i]; 410 var pos = child.getPosition(); 411 child.setPosition(cc.p(pos.x + offset, pos.y)); 412 } 413 }, 414 415 scrollPages: function (touchOffset) { 416 if (this._pages.length <= 0) { 417 return false; 418 } 419 420 if (!this._leftChild || !this._rightChild) { 421 return false; 422 } 423 424 var realOffset = touchOffset; 425 426 switch (this._touchMoveDir) { 427 case ccs.PVTouchDir.touchLeft: // left 428 if (this._rightChild.getRightInParent() + touchOffset <= this._rightBoundary) { 429 realOffset = this._rightBoundary - this._rightChild.getRightInParent(); 430 this.movePages(realOffset); 431 return false; 432 } 433 break; 434 435 case ccs.PVTouchDir.touchRight: // right 436 if (this._leftChild.getLeftInParent() + touchOffset >= this._leftBoundary) { 437 realOffset = this._leftBoundary - this._leftChild.getLeftInParent(); 438 this.movePages(realOffset); 439 return false; 440 } 441 break; 442 default: 443 break; 444 } 445 446 this.movePages(realOffset); 447 return true; 448 }, 449 450 onTouchCancelled: function (touchPoint) { 451 ccs.UILayout.prototype.onTouchCancelled.call(this, touchPoint); 452 }, 453 454 handlePressLogic: function (touchPoint) { 455 var nsp = this._renderer.convertToNodeSpace(touchPoint); 456 this._touchMoveStartLocation = nsp.x; 457 this._touchStartLocation = nsp.x; 458 }, 459 460 handleMoveLogic: function (touchPoint) { 461 var nsp = this._renderer.convertToNodeSpace(touchPoint); 462 var offset = 0.0; 463 var moveX = nsp.x; 464 offset = moveX - this._touchMoveStartLocation; 465 this._touchMoveStartLocation = moveX; 466 if (offset < 0) { 467 this._touchMoveDir = ccs.PVTouchDir.touchLeft; 468 } 469 else if (offset > 0) { 470 this._touchMoveDir = ccs.PVTouchDir.touchRight; 471 } 472 this.scrollPages(offset); 473 }, 474 475 handleReleaseLogic: function (touchPoint) { 476 if (this._pages.length <= 0) { 477 return; 478 } 479 var curPage = this._pages[this._curPageIdx]; 480 if (curPage) { 481 var curPagePos = curPage.getPosition(); 482 var pageCount = this._pages.length; 483 var curPageLocation = curPagePos.x; 484 var pageWidth = this.getSize().width; 485 var boundary = pageWidth / 2.0; 486 if (curPageLocation <= -boundary) { 487 if (this._curPageIdx >= pageCount - 1) 488 this.scrollPages(-curPageLocation); 489 else 490 this.scrollToPage(this._curPageIdx + 1); 491 } 492 else if (curPageLocation >= boundary) { 493 if (this._curPageIdx <= 0) 494 this.scrollPages(-curPageLocation); 495 else 496 this.scrollToPage(this._curPageIdx - 1); 497 } 498 else { 499 this.scrollToPage(this._curPageIdx); 500 } 501 } 502 }, 503 504 checkChildInfo: function (handleState, sender, touchPoint) { 505 this.interceptTouchEvent(handleState, sender, touchPoint); 506 }, 507 508 interceptTouchEvent: function (handleState, sender, touchPoint) { 509 switch (handleState) { 510 case 0: 511 this.handlePressLogic(touchPoint); 512 break; 513 case 1: 514 var offset = 0; 515 offset = Math.abs(sender.getTouchStartPos().x - touchPoint.x); 516 if (offset > this._childFocusCancelOffset) { 517 sender.setFocused(false); 518 this.handleMoveLogic(touchPoint); 519 } 520 break; 521 case 2: 522 this.handleReleaseLogic(touchPoint); 523 break; 524 525 case 3: 526 break; 527 } 528 }, 529 530 pageTurningEvent: function () { 531 if (this._pageViewEventListener && this._pageViewEventSelector) { 532 this._pageViewEventSelector.call(this._pageViewEventListener, this, ccs.PageViewEventType.turning); 533 } 534 }, 535 536 /** 537 * @param {Function} selector 538 * @param {Object} target 539 */ 540 addEventListenerPageView: function (selector, target) { 541 this._pageViewEventSelector = selector; 542 this._pageViewEventListener = target; 543 }, 544 545 /** 546 * get pages 547 * @returns {Array} 548 */ 549 getPages:function(){ 550 return this._pages; 551 }, 552 553 /** 554 * get cur page index 555 * @returns {number} 556 */ 557 getCurPageIndex: function () { 558 return this._curPageIdx; 559 }, 560 561 /** 562 * Returns the "class name" of widget. 563 * @returns {string} 564 */ 565 getDescription: function () { 566 return "PageView"; 567 }, 568 569 createCloneInstance: function () { 570 return ccs.UIPageView.create(); 571 }, 572 573 copyClonedWidgetChildren: function (model) { 574 var arrayPages = model.getPages(); 575 for (var i = 0; i < arrayPages.length; i++) { 576 var page = arrayPages[i]; 577 this.addPage(page.clone()); 578 } 579 }, 580 581 copySpecialProperties: function (pageView) { 582 ccs.UILayout.prototype.copySpecialProperties.call(this, pageView); 583 } 584 }); 585 /** 586 * allocates and initializes a UIPageView. 587 * @constructs 588 * @return {ccs.UIPageView} 589 * @example 590 * // example 591 * var uiPageView = ccs.UIPageView.create(); 592 */ 593 ccs.UIPageView.create = function () { 594 var uiPageView = new ccs.UIPageView(); 595 if (uiPageView && uiPageView.init()) { 596 return uiPageView; 597 } 598 return null; 599 };