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 };