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