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