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