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