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