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 * The PageView control of Cocos UI. 28 * @class 29 * @extends ccui.Layout 30 * @exmaple 31 * var pageView = new ccui.PageView(); 32 * pageView.setTouchEnabled(true); 33 * pageView.addPage(new ccui.Layout()); 34 * this.addChild(pageView); 35 */ 36 ccui.PageView = ccui.Layout.extend(/** @lends ccui.PageView# */{ 37 _curPageIdx: 0, 38 _pages: null, 39 _touchMoveDirection: null, 40 _touchStartLocation: 0, 41 _touchMoveStartLocation: 0, 42 _movePagePoint: null, 43 _leftBoundaryChild: null, 44 _rightBoundaryChild: null, 45 _leftBoundary: 0, 46 _rightBoundary: 0, 47 48 _isAutoScrolling: false, 49 _autoScrollDistance: 0, 50 _autoScrollSpeed: 0, 51 _autoScrollDirection: 0, 52 53 _childFocusCancelOffset: 0, 54 _pageViewEventListener: null, 55 _pageViewEventSelector: null, 56 _className:"PageView", 57 //v3.2 58 _customScrollThreshold: 0, 59 _usingCustomScrollThreshold: false, 60 61 /** 62 * Allocates and initializes a UIPageView. 63 * Constructor of ccui.PageView. please do not call this function by yourself, you should pass the parameters to constructor to initialize it
. 64 * @example 65 * // example 66 * var uiPageView = new ccui.PageView(); 67 */ 68 ctor: function () { 69 ccui.Layout.prototype.ctor.call(this); 70 this._pages = []; 71 this._touchMoveDirection = ccui.PageView.TOUCH_DIR_LEFT; 72 73 this._movePagePoint = null; 74 this._leftBoundaryChild = null; 75 this._rightBoundaryChild = null; 76 77 this._childFocusCancelOffset = 5; 78 this._pageViewEventListener = null; 79 this._pageViewEventSelector = null; 80 this.setTouchEnabled(true); 81 }, 82 83 /** 84 * Initializes a ccui.PageView. Please do not call this function by yourself, you should pass the parameters to constructor to initialize it. 85 * @returns {boolean} 86 */ 87 init: function () { 88 if (ccui.Layout.prototype.init.call(this)) { 89 this.setClippingEnabled(true); 90 return true; 91 } 92 return false; 93 }, 94 95 /** 96 * Calls the parent class' onEnter and schedules update function. 97 * @override 98 */ 99 onEnter:function(){ 100 ccui.Layout.prototype.onEnter.call(this); 101 this.scheduleUpdate(true); 102 }, 103 104 /** 105 * Add a widget to a page of PageView. 106 * @param {ccui.Widget} widget widget to be added to PageView. 107 * @param {number} pageIdx index of page. 108 * @param {Boolean} forceCreate if force create and there is no page exist, PageView would create a default page for adding widget. 109 */ 110 addWidgetToPage: function (widget, pageIdx, forceCreate) { 111 if (!widget || pageIdx < 0) 112 return; 113 114 var pageCount = this._getPageCount(); 115 if (pageIdx < 0 || 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 var newPage = this._createPage(); 120 newPage.addChild(widget); 121 this.addPage(newPage); 122 } 123 } else { 124 var page = this._pages[pageIdx]; 125 if (page) 126 page.addChild(widget); 127 } 128 }, 129 130 _createPage: function () { 131 var newPage = new ccui.Layout(); 132 newPage.setContentSize(this.getContentSize()); 133 return newPage; 134 }, 135 136 /** 137 * Adds a page to ccui.PageView. 138 * @param {ccui.Layout} page 139 */ 140 addPage: function (page) { 141 if (!page || this._pages.indexOf(page) !== -1) 142 return; 143 144 this.addChild(page); 145 this._pages.push(page); 146 this._doLayoutDirty = true; 147 }, 148 149 /** 150 * Inserts a page in the specified location. 151 * @param {ccui.Layout} page page to be added to PageView. 152 * @param {Number} idx index 153 */ 154 insertPage: function (page, idx) { 155 if (idx < 0 || !page || this._pages.indexOf(page) !== -1) 156 return; 157 158 var pageCount = this._getPageCount(); 159 if (idx >= pageCount) 160 this.addPage(page); 161 else { 162 this._pages[idx] = page; 163 this.addChild(page); 164 } 165 this._doLayoutDirty = true; 166 }, 167 168 /** 169 * Removes a page from PageView. 170 * @param {ccui.Layout} page 171 */ 172 removePage: function (page) { 173 if (!page) 174 return; 175 this.removeChild(page); 176 var index = this._pages.indexOf(page); 177 if(index > -1) 178 this._pages.splice(index, 1); 179 this._doLayoutDirty = true; 180 }, 181 182 /** 183 * Removes a page at index of PageView. 184 * @param {number} index 185 */ 186 removePageAtIndex: function (index) { 187 if (index < 0 || index >= this._pages.length) 188 return; 189 var page = this._pages[index]; 190 if (page) 191 this.removePage(page); 192 }, 193 194 /** 195 * Removes all pages from PageView 196 */ 197 removeAllPages: function(){ 198 var locPages = this._pages; 199 for(var i = 0, len = locPages.length; i < len; i++) 200 this.removeChild(locPages[i]); 201 this._pages.length = 0; 202 }, 203 204 _updateBoundaryPages: function () { 205 var locPages = this._pages; 206 if (locPages.length <= 0) { 207 this._leftBoundaryChild = null; 208 this._rightBoundaryChild = null; 209 return; 210 } 211 this._leftBoundaryChild = locPages[0]; 212 this._rightBoundaryChild = locPages[locPages.length - 1]; 213 }, 214 215 _getPageCount: function(){ 216 return this._pages.length; 217 }, 218 219 /** 220 * Get x position by index 221 * @param {number} idx 222 * @returns {number} 223 */ 224 _getPositionXByIndex: function (idx) { 225 return (this.getContentSize().width * (idx - this._curPageIdx)); 226 }, 227 228 _onSizeChanged: function () { 229 ccui.Layout.prototype._onSizeChanged.call(this); 230 this._rightBoundary = this.getContentSize().width; 231 this._doLayoutDirty = true; 232 }, 233 234 _updateAllPagesSize: function(){ 235 var selfSize = this.getContentSize(); 236 var locPages = this._pages; 237 for (var i = 0, len = locPages.length; i < len; i++) 238 locPages[i].setContentSize(selfSize); 239 }, 240 241 _updateAllPagesPosition: function(){ 242 var pageCount = this._getPageCount(); 243 if (pageCount <= 0) { 244 this._curPageIdx = 0; 245 return; 246 } 247 248 if (this._curPageIdx >= pageCount) 249 this._curPageIdx = pageCount-1; 250 251 var pageWidth = this.getContentSize().width; 252 var locPages = this._pages; 253 for (var i=0; i< pageCount; i++) 254 locPages[i].setPosition(cc.p((i - this._curPageIdx) * pageWidth, 0)); 255 }, 256 257 /** 258 * scroll PageView to index. 259 * @param {number} idx index of page. 260 */ 261 scrollToPage: function (idx) { 262 if (idx < 0 || idx >= this._pages.length) 263 return; 264 this._curPageIdx = idx; 265 var curPage = this._pages[idx]; 266 this._autoScrollDistance = -(curPage.getPosition().x); 267 this._autoScrollSpeed = Math.abs(this._autoScrollDistance) / 0.2; 268 this._autoScrollDirection = this._autoScrollDistance > 0 ? ccui.PageView.DIRECTION_RIGHT : ccui.PageView.DIRECTION_LEFT; 269 this._isAutoScrolling = true; 270 }, 271 272 /** 273 * Called once per frame. Time is the number of seconds of a frame interval. 274 * @override 275 * @param {Number} dt 276 */ 277 update: function (dt) { 278 if (this._isAutoScrolling) 279 this._autoScroll(dt); 280 }, 281 282 /** 283 * Does nothing. ccui.PageView's layout type is ccui.Layout.ABSOLUTE. 284 * @override 285 * @param {Number} type 286 */ 287 setLayoutType:function(type){ 288 }, 289 290 /** 291 * Returns the layout type of ccui.PageView. it's always ccui.Layout.ABSOLUTE. 292 * @returns {number} 293 */ 294 getLayoutType: function(){ 295 return ccui.Layout.ABSOLUTE; 296 }, 297 298 _autoScroll: function(dt){ 299 var step; 300 switch (this._autoScrollDirection) { 301 case ccui.PageView.DIRECTION_LEFT: 302 step = this._autoScrollSpeed * dt; 303 if (this._autoScrollDistance + step >= 0.0) { 304 step = -this._autoScrollDistance; 305 this._autoScrollDistance = 0.0; 306 this._isAutoScrolling = false; 307 } else 308 this._autoScrollDistance += step; 309 this._scrollPages(-step); 310 if(!this._isAutoScrolling) 311 this._pageTurningEvent(); 312 break; 313 break; 314 case ccui.PageView.DIRECTION_RIGHT: 315 step = this._autoScrollSpeed * dt; 316 if (this._autoScrollDistance - step <= 0.0) { 317 step = this._autoScrollDistance; 318 this._autoScrollDistance = 0.0; 319 this._isAutoScrolling = false; 320 } else 321 this._autoScrollDistance -= step; 322 this._scrollPages(step); 323 if(!this._isAutoScrolling) 324 this._pageTurningEvent(); 325 break; 326 default: 327 break; 328 } 329 }, 330 331 /** 332 * The touch moved event callback handler of ccui.PageView. 333 * @override 334 * @param {cc.Touch} touch 335 * @param {cc.Event} event 336 */ 337 onTouchMoved: function (touch, event) { 338 ccui.Layout.prototype.onTouchMoved.call(this, touch, event); 339 if (!this._isInterceptTouch) 340 this._handleMoveLogic(touch); 341 }, 342 343 /** 344 * The touch ended event callback handler of ccui.PageView. 345 * @override 346 * @param {cc.Touch} touch 347 * @param {cc.Event} event 348 */ 349 onTouchEnded: function (touch, event) { 350 ccui.Layout.prototype.onTouchEnded.call(this, touch, event); 351 if (!this._isInterceptTouch) 352 this._handleReleaseLogic(touch); 353 this._isInterceptTouch = false; 354 }, 355 356 /** 357 * The touch canceled event callback handler of ccui.PageView. 358 * @param {cc.Touch} touch 359 * @param {cc.Event} event 360 */ 361 onTouchCancelled: function (touch, event) { 362 ccui.Layout.prototype.onTouchCancelled.call(this, touch, event); 363 if (!this._isInterceptTouch) 364 this._handleReleaseLogic(touch); 365 this._isInterceptTouch = false; 366 }, 367 368 _doLayout: function(){ 369 if (!this._doLayoutDirty) 370 return; 371 372 this._updateAllPagesPosition(); 373 this._updateAllPagesSize(); 374 this._updateBoundaryPages(); 375 this._doLayoutDirty = false; 376 }, 377 378 _movePages: function (offset) { 379 var arrayPages = this._pages; 380 var length = arrayPages.length; 381 for (var i = 0; i < length; i++) { 382 var child = arrayPages[i]; 383 //var pos = child.getPosition(); 384 //child.setPosition(pos.x + offset, pos.y); 385 child.setPositionX(child.getPositionX() + offset); 386 } 387 }, 388 389 _scrollPages: function (touchOffset) { 390 if (this._pages.length <= 0) 391 return false; 392 if (!this._leftBoundaryChild || !this._rightBoundaryChild) 393 return false; 394 395 var realOffset = touchOffset; 396 switch (this._touchMoveDirection) { 397 case ccui.PageView.TOUCH_DIR_LEFT: // left 398 var rightBoundary = this._rightBoundaryChild.getRightBoundary(); 399 if (rightBoundary + touchOffset <= this._rightBoundary) { 400 realOffset = this._rightBoundary - rightBoundary; 401 this._movePages(realOffset); 402 return false; 403 } 404 break; 405 case ccui.PageView.TOUCH_DIR_RIGHT: // right 406 var leftBoundary = this._leftBoundaryChild.getLeftBoundary(); 407 if (leftBoundary + touchOffset >= this._leftBoundary) { 408 realOffset = this._leftBoundary - leftBoundary; 409 this._movePages(realOffset); 410 return false; 411 } 412 break; 413 default: 414 break; 415 } 416 417 this._movePages(realOffset); 418 return true; 419 }, 420 421 _handleMoveLogic: function (touch) { 422 var offset = touch.getLocation().x - touch.getPreviousLocation().x; 423 if (offset < 0) 424 this._touchMoveDirection = ccui.PageView.TOUCH_DIR_LEFT; 425 else if (offset > 0) 426 this._touchMoveDirection = ccui.PageView.TOUCH_DIR_RIGHT; 427 this._scrollPages(offset); 428 }, 429 430 /** 431 * Set custom scroll threshold to page view. If you don't specify the value, the pageView will scroll when half page view width reached. 432 * @since v3.2 433 * @param threshold 434 */ 435 setCustomScrollThreshold: function(threshold){ 436 cc.assert(threshold>0, "Invalid threshold!"); 437 this._customScrollThreshold = threshold; 438 this.setUsingCustomScrollThreshold(true); 439 }, 440 441 /** 442 * Returns user defined scroll page threshold. 443 * @since v3.2 444 */ 445 getCustomScrollThreshold: function(){ 446 return this._customScrollThreshold; 447 }, 448 449 /** 450 * Set using user defined scroll page threshold or not. If you set it to false, then the default scroll threshold is pageView.width / 2. 451 * @since v3.2 452 */ 453 setUsingCustomScrollThreshold: function(flag){ 454 this._usingCustomScrollThreshold = flag; 455 }, 456 457 /** 458 * Queries whether we are using user defined scroll page threshold or not 459 */ 460 isUsingCustomScrollThreshold: function(){ 461 return this._usingCustomScrollThreshold; 462 }, 463 464 _handleReleaseLogic: function (touchPoint) { 465 if (this._pages.length <= 0) 466 return; 467 var curPage = this._pages[this._curPageIdx]; 468 if (curPage) { 469 var curPagePos = curPage.getPosition(); 470 var pageCount = this._pages.length; 471 var curPageLocation = curPagePos.x; 472 var pageWidth = this.getSize().width; 473 if (!this._usingCustomScrollThreshold) 474 this._customScrollThreshold = pageWidth / 2.0; 475 var boundary = this._customScrollThreshold; 476 if (curPageLocation <= -boundary) { 477 if (this._curPageIdx >= pageCount - 1) 478 this._scrollPages(-curPageLocation); 479 else 480 this.scrollToPage(this._curPageIdx + 1); 481 } else if (curPageLocation >= boundary) { 482 if (this._curPageIdx <= 0) 483 this._scrollPages(-curPageLocation); 484 else 485 this.scrollToPage(this._curPageIdx - 1); 486 } else 487 this.scrollToPage(this._curPageIdx); 488 } 489 }, 490 491 /** 492 * Intercept touch event, handle its child's touch event. 493 * @param {Number} eventType event type 494 * @param {ccui.Widget} sender 495 * @param {cc.Touch} touch 496 */ 497 interceptTouchEvent: function (eventType, sender, touch) { 498 if(!this._touchEnabled) 499 { 500 ccui.Layout.prototype.interceptTouchEvent.call(this, eventType, sender, touch); 501 return; 502 } 503 var touchPoint = touch.getLocation(); 504 switch (eventType) { 505 case ccui.Widget.TOUCH_BEGAN: 506 this._touchBeganPosition.x = touchPoint.x; 507 this._touchBeganPosition.y = touchPoint.y; 508 this._isInterceptTouch = true; 509 break; 510 case ccui.Widget.TOUCH_MOVED: 511 this._touchMovePosition.x = touchPoint.x; 512 this._touchMovePosition.y = touchPoint.y; 513 var offset = 0; 514 offset = Math.abs(sender.getTouchBeganPosition().x - touchPoint.x); 515 if (offset > this._childFocusCancelOffset) { 516 sender.setHighlighted(false); 517 this._handleMoveLogic(touch); 518 } 519 break; 520 case ccui.Widget.TOUCH_ENDED: 521 case ccui.Widget.TOUCH_CANCELED: 522 this._touchEndPosition.x = touchPoint.x; 523 this._touchEndPosition.y = touchPoint.y; 524 this._handleReleaseLogic(touch); 525 if (sender.isSwallowTouches()) 526 this._isInterceptTouch = false; 527 break; 528 } 529 }, 530 531 _pageTurningEvent: function () { 532 if(this._pageViewEventSelector){ 533 if (this._pageViewEventListener) 534 this._pageViewEventSelector.call(this._pageViewEventListener, this, ccui.PageView.EVENT_TURNING); 535 else 536 this._pageViewEventSelector(this, ccui.PageView.EVENT_TURNING); 537 } 538 if(this._ccEventCallback) 539 this._ccEventCallback(this, ccui.PageView.EVENT_TURNING); 540 }, 541 542 /** 543 * Adds event listener to ccui.PageView. 544 * @param {Function} selector 545 * @param {Object} [target=] 546 * @deprecated since v3.0, please use addEventListener instead. 547 */ 548 addEventListenerPageView: function (selector, target) { 549 this.addEventListener(selector, target); 550 }, 551 552 /** 553 * Adds event listener to ccui.PageView. 554 * @param {Function} selector 555 * @param {Object} [target=] 556 */ 557 addEventListener: function(selector, target){ 558 this._pageViewEventSelector = selector; 559 this._pageViewEventListener = target; 560 }, 561 562 /** 563 * Returns current page index 564 * @returns {number} 565 */ 566 getCurPageIndex: function () { 567 return this._curPageIdx; 568 }, 569 570 /** 571 * Returns all pages of PageView 572 * @returns {Array} 573 */ 574 getPages:function(){ 575 return this._pages; 576 }, 577 578 /** 579 * Returns a page from PageView by index 580 * @param {Number} index 581 * @returns {ccui.Layout} 582 */ 583 getPage: function(index){ 584 if (index < 0 || index >= this._pages.length) 585 return null; 586 return this._pages[index]; 587 }, 588 589 /** 590 * Returns the "class name" of ccui.PageView. 591 * @returns {string} 592 */ 593 getDescription: function () { 594 return "PageView"; 595 }, 596 597 _createCloneInstance: function () { 598 return new ccui.PageView(); 599 }, 600 601 _copyClonedWidgetChildren: function (model) { 602 var arrayPages = model.getPages(); 603 for (var i = 0; i < arrayPages.length; i++) { 604 var page = arrayPages[i]; 605 this.addPage(page.clone()); 606 } 607 }, 608 609 _copySpecialProperties: function (pageView) { 610 ccui.Layout.prototype._copySpecialProperties.call(this, pageView); 611 this._ccEventCallback = pageView._ccEventCallback; 612 this._pageViewEventListener = pageView._pageViewEventListener; 613 this._pageViewEventSelector = pageView._pageViewEventSelector; 614 this._usingCustomScrollThreshold = pageView._usingCustomScrollThreshold; 615 this._customScrollThreshold = pageView._customScrollThreshold; 616 } 617 }); 618 /** 619 * allocates and initializes a UIPageView. 620 * @deprecated since v3.0, please use new ccui.PageView() instead. 621 * @return {ccui.PageView} 622 */ 623 ccui.PageView.create = function () { 624 return new ccui.PageView(); 625 }; 626 627 // Constants 628 //PageView event 629 /** 630 * The turning flag of ccui.PageView's event. 631 * @constant 632 * @type {number} 633 */ 634 ccui.PageView.EVENT_TURNING = 0; 635 636 //PageView touch direction 637 /** 638 * The left flag of ccui.PageView's touch direction. 639 * @constant 640 * @type {number} 641 */ 642 ccui.PageView.TOUCH_DIR_LEFT = 0; 643 /** 644 * The right flag of ccui.PageView's touch direction. 645 * @constant 646 * @type {number} 647 */ 648 ccui.PageView.TOUCH_DIR_RIGHT = 1; 649 650 //PageView auto scroll direction 651 /** 652 * The right flag of ccui.PageView's auto scroll direction. 653 * @constant 654 * @type {number} 655 */ 656 ccui.PageView.DIRECTION_LEFT = 0; 657 /** 658 * The right flag of ccui.PageView's auto scroll direction. 659 * @constant 660 * @type {number} 661 */ 662 ccui.PageView.DIRECTION_RIGHT = 1; 663