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  * Base class for ccui.ListView
 28  * @class
 29  * @extends ccui.ScrollView
 30  */
 31 ccui.ListView = ccui.ScrollView.extend(/** @lends ccui.ListView# */{
 32     _model: null,
 33     _items: null,
 34     _gravity: null,
 35     _itemsMargin: 0,
 36 
 37     _curSelectedIndex: 0,
 38     _refreshViewDirty: true,
 39 
 40     _listViewEventListener: null,
 41     _listViewEventSelector: null,
 42     _eventCallback: null,
 43     /**
 44      * allocates and initializes a UIListView.
 45      * Constructor of ccui.ListView
 46      * @example
 47      * // example
 48      * var uiPageView = new ccui.ListView();
 49      */
 50     ctor: function () {
 51         ccui.ScrollView.prototype.ctor.call(this);
 52         this._items = [];
 53         this._gravity = ccui.ListView.GRAVITY_CENTER_HORIZONTAL;
 54         this.setTouchEnabled(true);
 55 
 56         this.init();
 57     },
 58 
 59     init: function () {
 60         if (ccui.ScrollView.prototype.init.call(this)) {
 61             this.setLayoutType(ccui.Layout.LINEAR_VERTICAL);
 62             return true;
 63         }
 64         return false;
 65     },
 66 
 67     /**
 68      * Sets a item model for ListView. A model will be cloned for adding default item.
 69      * @param {ccui.Widget} model
 70      */
 71     setItemModel: function (model) {
 72         if (!model)
 73             return;
 74         this._model = model;
 75     },
 76 
 77     _updateInnerContainerSize: function () {
 78         var locItems = this._items, length, i;
 79         switch (this.direction) {
 80             case ccui.ScrollView.DIR_VERTICAL:
 81                 length = locItems.length;
 82                 var totalHeight = (length - 1) * this._itemsMargin;
 83                 for (i = 0; i < length; i++) {
 84                     totalHeight += locItems[i].getContentSize().height;
 85                 }
 86                 this.setInnerContainerSize(cc.size(this._contentSize.width, totalHeight));
 87                 break;
 88             case ccui.ScrollView.DIR_HORIZONTAL:
 89                 length = locItems.length;
 90                 var totalWidth = (length - 1) * this._itemsMargin;
 91                 for (i = 0; i < length; i++) {
 92                     totalWidth += locItems[i].getContentSize().width;
 93                 }
 94                 this.setInnerContainerSize(cc.size(totalWidth, this._contentSize.height));
 95                 break;
 96             default:
 97                 break;
 98         }
 99     },
100 
101     _remedyLayoutParameter: function (item) {
102         if (!item)
103             return;
104         var llp;
105         switch (this.direction) {
106             case ccui.ScrollView.DIR_VERTICAL:
107                 llp = item.getLayoutParameter();
108                 if (!llp) {
109                     var defaultLp = ccui.LinearLayoutParameter.create();
110                     switch (this._gravity) {
111                         case ccui.ListView.GRAVITY_LEFT:
112                             defaultLp.setGravity(ccui.LinearLayoutParameter.LEFT);
113                             break;
114                         case ccui.ListView.GRAVITY_RIGHT:
115                             defaultLp.setGravity(ccui.LinearLayoutParameter.RIGHT);
116                             break;
117                         case ccui.ListView.GRAVITY_CENTER_HORIZONTAL:
118                             defaultLp.setGravity(ccui.LinearLayoutParameter.CENTER_HORIZONTAL);
119                             break;
120                         default:
121                             break;
122                     }
123                     if (this.getIndex(item) == 0)
124                         defaultLp.setMargin(ccui.MarginZero());
125                     else
126                         defaultLp.setMargin(new ccui.Margin(0.0, this._itemsMargin, 0.0, 0.0));
127                     item.setLayoutParameter(defaultLp);
128                 } else {
129                     if (this.getIndex(item) == 0)
130                         llp.setMargin(ccui.MarginZero());
131                     else
132                         llp.setMargin(new ccui.Margin(0, this._itemsMargin, 0, 0));
133                     switch (this._gravity) {
134                         case ccui.ListView.GRAVITY_LEFT:
135                             llp.setGravity(ccui.LinearLayoutParameter.LEFT);
136                             break;
137                         case ccui.ListView.GRAVITY_RIGHT:
138                             llp.setGravity(ccui.LinearLayoutParameter.RIGHT);
139                             break;
140                         case ccui.ListView.GRAVITY_CENTER_HORIZONTAL:
141                             llp.setGravity(ccui.LinearLayoutParameter.CENTER_HORIZONTAL);
142                             break;
143                         default:
144                             break;
145                     }
146                 }
147                 break;
148             case ccui.ScrollView.DIR_HORIZONTAL:
149                 llp = item.getLayoutParameter();
150                 if (!llp) {
151                     var defaultLp = ccui.LinearLayoutParameter.create();
152                     switch (this._gravity) {
153                         case ccui.ListView.GRAVITY_TOP:
154                             defaultLp.setGravity(ccui.LinearLayoutParameter.TOP);
155                             break;
156                         case ccui.ListView.GRAVITY_BOTTOM:
157                             defaultLp.setGravity(ccui.LinearLayoutParameter.BOTTOM );
158                             break;
159                         case ccui.ListView.GRAVITY_CENTER_VERTICAL:
160                             defaultLp.setGravity(ccui.LinearLayoutParameter.CENTER_VERTICAL);
161                             break;
162                         default:
163                             break;
164                     }
165                     if (this.getIndex(item) == 0)
166                         defaultLp.setMargin(ccui.MarginZero());
167                     else
168                         defaultLp.setMargin(new ccui.Margin(this._itemsMargin, 0.0, 0.0, 0.0));
169                     item.setLayoutParameter(defaultLp);
170                 } else {
171                     if (this.getIndex(item) == 0)
172                         llp.setMargin(ccui.MarginZero());
173                     else
174                         llp.setMargin(new ccui.Margin(this._itemsMargin, 0.0, 0.0, 0.0));
175                     switch (this._gravity) {
176                         case ccui.ListView.GRAVITY_TOP:
177                             llp.setGravity(ccui.LinearLayoutParameter.TOP);
178                             break;
179                         case ccui.ListView.GRAVITY_BOTTOM:
180                             llp.setGravity(ccui.LinearLayoutParameter.BOTTOM);
181                             break;
182                         case ccui.ListView.GRAVITY_CENTER_VERTICAL:
183                             llp.setGravity(ccui.LinearLayoutParameter.CENTER_VERTICAL);
184                             break;
185                         default:
186                             break;
187                     }
188                 }
189                 break;
190             default:
191                 break;
192         }
193     },
194 
195     /**
196      * Push back a default item(create by a cloned model) into ListView.
197      */
198     pushBackDefaultItem: function () {
199         if (!this._model)
200             return;
201         var newItem = this._model.clone();
202         this._remedyLayoutParameter(newItem);
203         this.addChild(newItem);
204         this._refreshViewDirty = true;
205     },
206 
207     /**
208      * Insert a default item(create by a cloned model) into ListView.
209      * @param {Number} index
210      */
211     insertDefaultItem: function (index) {
212         if (!this._model)
213             return;
214         var newItem = this._model.clone();
215         this._items.splice(index, 0, newItem);
216         ccui.ScrollView.prototype.addChild.call(this, newItem);
217         this._remedyLayoutParameter(newItem);
218 
219         this._refreshViewDirty = true;
220     },
221 
222     /**
223      * Push back custom item into ListView.
224      * @param {ccui.Widget} item
225      */
226     pushBackCustomItem: function (item) {
227         this._remedyLayoutParameter(item);
228         this.addChild(item);
229         this._refreshViewDirty = true;
230     },
231 
232     /**
233      * add child to ListView
234      * @param {cc.Node} widget
235      * @param {Number} [zOrder]
236      * @param {Number|String} [tag]  tag or name
237      */
238     addChild: function (widget, zOrder, tag) {
239         if (widget) {
240             zOrder = zOrder || widget.getLocalZOrder();
241             tag = tag || widget.getName();
242             ccui.ScrollView.prototype.addChild.call(this, widget, zOrder, tag);
243             if(widget instanceof ccui.Widget)
244                 this._items.push(widget);
245         }
246     },
247 
248     /**
249      * remove child from ListView
250      * @param {cc.Node} widget
251      * @param {Boolean} [cleanup=true]
252      */
253     removeChild: function(widget, cleanup){
254         if (widget) {
255             var index = this._items.indexOf(widget);
256             if(index > -1)
257                 this._items.splice(index, 1);
258             ccui.ScrollView.prototype.removeChild.call(this, widget, cleanup);
259         }
260     },
261 
262     removeAllChildren: function(){
263         this.removeAllChildrenWithCleanup(true);
264     },
265 
266     removeAllChildrenWithCleanup: function(cleanup){
267         ccui.ScrollView.prototype.removeAllChildrenWithCleanup.call(this, cleanup);
268         this._items = [];
269     },
270 
271     /**
272      * Push back custom item into ListView.
273      * @param {ccui.Widget} item
274      * @param {Number} index
275      */
276     insertCustomItem: function (item, index) {
277         this._items.splice(index, 0, item);
278         ccui.ScrollView.prototype.addChild.call(this, item);
279         this._remedyLayoutParameter(item);
280         this._refreshViewDirty = true;
281     },
282 
283     /**
284      * Removes a item whose index is same as the parameter.
285      * @param {Number} index
286      */
287     removeItem: function (index) {
288         var item = this.getItem(index);
289         if (!item)
290             return;
291         this.removeChild(item, true);
292         this._refreshViewDirty = true;
293     },
294 
295     /**
296      * Removes the last item of ListView.
297      */
298     removeLastItem: function () {
299         this.removeItem(this._items.length - 1);
300     },
301 
302     removeAllItems: function(){
303         this.removeAllChildren();
304     },
305 
306     /**
307      * Returns a item whose index is same as the parameter.
308      * @param {Number} index
309      * @returns {ccui.Widget}
310      */
311     getItem: function (index) {
312         if (index < 0 || index >= this._items.length)
313             return null;
314         return this._items[index];
315     },
316 
317     /**
318      * Returns the item container.
319      * @returns {Array}
320      */
321     getItems: function () {
322         return this._items;
323     },
324 
325     /**
326      * Returns the index of item.
327      * @param {ccui.Widget} item the item which need to be checked.
328      * @returns {Number} the index of item.
329      */
330     getIndex: function (item) {
331         return this._items.indexOf(item);
332     },
333 
334     /**
335      * Changes the gravity of ListView.
336      * @param {ccui.ListView.GRAVITY_LEFT|ccui.ListView.GRAVITY_RIGHT|ccui.ListView.GRAVITY_CENTER_HORIZONTAL|ccui.ListView.GRAVITY_BOTTOM|ccui.ListView.GRAVITY_CENTER_VERTICAL} gravity
337      */
338     setGravity: function (gravity) {
339         if (this._gravity == gravity) {
340             return;
341         }
342         this._gravity = gravity;
343         this._refreshViewDirty = true;
344     },
345 
346     /**
347      * Changes the margin between each item.
348      * @param {Number} margin
349      */
350     setItemsMargin: function (margin) {
351         if (this._itemsMargin == margin) {
352             return;
353         }
354         this._itemsMargin = margin;
355         this._refreshViewDirty = true;
356     },
357 
358     /**
359      * Get the margin between each item.
360      * @returns {Number}
361      */
362     getItemsMargin:function(){
363         return this._itemsMargin;
364     },
365 
366     /**
367      * Changes scroll direction of scrollview.
368      * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} dir
369      */
370     setDirection: function (dir) {
371         switch (dir) {
372             case ccui.ScrollView.DIR_VERTICAL:
373                 this.setLayoutType(ccui.Layout.LINEAR_VERTICAL);
374                 break;
375             case ccui.ScrollView.DIR_HORIZONTAL:
376                 this.setLayoutType(ccui.Layout.LINEAR_HORIZONTAL);
377                 break;
378             case ccui.ScrollView.DIR_BOTH:
379                 return;
380             default:
381                 return;
382                 break;
383         }
384         ccui.ScrollView.prototype.setDirection.call(this, dir);
385     },
386 
387     /**
388      * request refresh view
389      */
390     requestRefreshView: function () {
391         this._refreshViewDirty = true;
392     },
393 
394     refreshView: function () {
395         var locItems = this._items;
396         for (var i = 0; i < locItems.length; i++) {
397             var item = locItems[i];
398             item.setLocalZOrder(i);
399             this._remedyLayoutParameter(item);
400         }
401         this._updateInnerContainerSize();
402     },
403 
404     /**
405      * public the _doLayout for Editor
406      */
407     doLayout: function(){
408         this._doLayout();
409     },
410 
411     _doLayout: function(){
412         ccui.Layout.prototype._doLayout.call(this);
413         if (this._refreshViewDirty) {
414             this.refreshView();
415             this._refreshViewDirty = false;
416         }
417     },
418 
419     /**
420      *  add event listener
421      * @param {Function} selector
422      * @param {Object} target
423      * @deprecated
424      */
425     addEventListenerListView: function (selector, target) {
426         this._listViewEventListener = target;
427         this._listViewEventSelector = selector;
428     },
429 
430     addEventListener: function(callback){
431         this._eventCallback = callback;
432     },
433 
434     _selectedItemEvent: function (event) {
435         var eventEnum = (event == ccui.Widget.TOUCH_BEGAN) ? ccui.ListView.ON_SELECTED_ITEM_START : ccui.ListView.ON_SELECTED_ITEM_END;
436         if (this._listViewEventListener && this._listViewEventSelector)
437             this._listViewEventSelector.call(this._listViewEventListener, this, eventEnum);
438         if(this._eventCallback)
439             this._eventCallback(this, eventEnum);
440     },
441 
442     interceptTouchEvent: function (handleState, sender, touch) {
443         ccui.ScrollView.prototype.interceptTouchEvent.call(this, handleState, sender, touch);
444         if (handleState != ccui.Widget.TOUCH_MOVED) {
445             var parent = sender;
446             while (parent) {
447                 if (parent && parent.getParent() == this._innerContainer) {
448                     this._curSelectedIndex = this.getIndex(parent);
449                     break;
450                 }
451                 parent = parent.getParent();
452             }
453             if (sender.isHighlighted())
454                 this._selectedItemEvent(handleState);
455         }
456     },
457 
458     /**
459      * get current selected index
460      * @returns {number}
461      */
462     getCurSelectedIndex: function () {
463         return this._curSelectedIndex;
464     },
465 
466     _onSizeChanged: function () {
467         ccui.ScrollView.prototype._onSizeChanged.call(this);
468         this._refreshViewDirty = true;
469     },
470 
471     /**
472      * Returns the "class name" of widget.
473      * @returns {string}
474      */
475     getDescription: function () {
476         return "ListView";
477     },
478 
479     _createCloneInstance: function () {
480         return ccui.ListView.create();
481     },
482 
483     _copyClonedWidgetChildren: function (model) {
484         var arrayItems = model.getItems();
485         for (var i = 0; i < arrayItems.length; i++) {
486             var item = arrayItems[i];
487             this.pushBackCustomItem(item.clone());
488         }
489     },
490 
491     _copySpecialProperties: function (listView) {
492         if(listView instanceof ccui.ListView){
493             ccui.ScrollView.prototype._copySpecialProperties.call(this, listView);
494             this.setItemModel(listView._model);
495             this.setItemsMargin(listView._itemsMargin);
496             this.setGravity(listView._gravity);
497 
498             this._listViewEventListener = listView._listViewEventListener;
499             this._listViewEventSelector = listView._listViewEventSelector;
500             this._eventCallback = listView._eventCallback;
501         }
502     }
503 });
504 
505 /**
506  * allocates and initializes a UIListView.
507  * @deprecated
508  * @example
509  * // example
510  * var uiPageView = ccui.ListView.create();
511  */
512 ccui.ListView.create = function () {
513     return new ccui.ListView();
514 };
515 
516 // Constants
517 //listView event type
518 ccui.ListView.EVENT_SELECTED_ITEM = 0;
519 
520 ccui.ListView.ON_SELECTED_ITEM_START = 0;
521 ccui.ListView.ON_SELECTED_ITEM_END = 1;
522 
523 //listView gravity
524 ccui.ListView.GRAVITY_LEFT = 0;
525 ccui.ListView.GRAVITY_RIGHT = 1;
526 ccui.ListView.GRAVITY_CENTER_HORIZONTAL = 2;
527 ccui.ListView.GRAVITY_TOP = 3;
528 ccui.ListView.GRAVITY_BOTTOM = 4;
529 ccui.ListView.GRAVITY_CENTER_VERTICAL = 5;