1 /****************************************************************************
  2  Copyright (c) 2013 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  * listView event type
 27  * @type {Object}
 28  */
 29 ccs.ListViewEventType = {
 30     listViewOnselectedItem: 0
 31 };
 32 
 33 /**
 34  * listView gravity
 35  * @type {Object}
 36  */
 37 ccs.ListViewGravity = {
 38     left: 0,
 39     right: 1,
 40     centerHorizontal: 2,
 41     top: 3,
 42     bottom: 4,
 43     centerVertical: 5
 44 };
 45 
 46 /**
 47  * Base class for ccs.ListView
 48  * @class
 49  * @extends ccs.ScrollView
 50  */
 51 ccs.ListView = ccs.ScrollView.extend({
 52     _model: null,
 53     _items: null,
 54     _gravity: null,
 55     _itemsMargin: 0,
 56     _listViewEventListener: null,
 57     _listViewEventSelector: null,
 58     _curSelectedIndex: 0,
 59     _refreshViewDirty: true,
 60     ctor: function () {
 61         ccs.ScrollView.prototype.ctor.call(this);
 62         this._model = null;
 63         this._items = [];
 64         this._gravity = ccs.ListViewGravity.centerHorizontal;
 65         this._itemsMargin = 0;
 66         this._listViewEventListener = null;
 67         this._listViewEventSelector = null;
 68         this._curSelectedIndex = 0;
 69         this._refreshViewDirty = true;
 70     },
 71 
 72     init: function () {
 73         if (ccs.ScrollView.prototype.init.call(this)) {
 74             this._items = [];
 75             this.setLayoutType(ccs.LayoutType.linearVertical);
 76             return true;
 77         }
 78         return false;
 79     },
 80 
 81     /**
 82      * Sets a item model for listview. A model will be cloned for adding default item.
 83      * @param {ccs.Widget} model
 84      */
 85     setItemModel: function (model) {
 86         if (!model) {
 87             return;
 88         }
 89         this._model = model;
 90     },
 91 
 92     updateInnerContainerSize: function () {
 93         switch (this._direction) {
 94             case ccs.ScrollViewDir.vertical:
 95                 var length = this._items.length;
 96                 var totalHeight = (length - 1) * this._itemsMargin;
 97                 for (var i = 0; i < length; i++) {
 98                     var item = this._items[i];
 99                     totalHeight += item.getSize().height;
100                 }
101                 var finalWidth = this._size.width;
102                 var finalHeight = totalHeight;
103                 this.setInnerContainerSize(cc.size(finalWidth, finalHeight));
104                 break;
105             case ccs.ScrollViewDir.horizontal:
106                 var length = this._items.length;
107                 var totalWidth = (length - 1) * this._itemsMargin;
108                 for (var i = 0; i < length; i++) {
109                     var item = this._items[i];
110                     totalWidth += item.getSize().width;
111                 }
112                 var finalWidth = totalWidth;
113                 var finalHeight = this._size.height;
114                 this.setInnerContainerSize(cc.size(finalWidth, finalHeight));
115                 break;
116             default:
117                 break;
118         }
119     },
120 
121     remedyLayoutParameter: function (item) {
122         if (!item) {
123             return;
124         }
125         switch (this._direction) {
126             case ccs.ScrollViewDir.vertical:
127                 var llp = item.getLayoutParameter(ccs.LayoutParameterType.linear);
128                 if (!llp) {
129                     var defaultLp = ccs.LinearLayoutParameter.create();
130                     switch (this._gravity) {
131                         case ccs.ListViewGravity.left:
132                             defaultLp.setGravity(ccs.LinearGravity.left);
133                             break;
134                         case ccs.ListViewGravity.right:
135                             defaultLp.setGravity(ccs.LinearGravity.right);
136                             break;
137                         case ccs.ListViewGravity.centerHorizontal:
138                             defaultLp.setGravity(ccs.LinearGravity.centerHorizontal);
139                             break;
140                         default:
141                             break;
142                     }
143                     if (this.getIndex(item) == 0) {
144                         defaultLp.setMargin(ccs.MarginZero());
145                     }
146                     else {
147                         defaultLp.setMargin(new ccs.Margin(0.0, this._itemsMargin, 0.0, 0.0));
148                     }
149                     item.setLayoutParameter(defaultLp);
150                 }
151                 else {
152                     if (this.getIndex(item) == 0) {
153                         llp.setMargin(ccs.MarginZero());
154                     }
155                     else {
156                         llp.setMargin(new ccs.Margin(0, this._itemsMargin, 0, 0));
157                     }
158                     switch (this._gravity) {
159                         case ccs.ListViewGravity.left:
160                             llp.setGravity(ccs.LinearGravity.left);
161                             break;
162                         case ccs.ListViewGravity.right:
163                             llp.setGravity(ccs.LinearGravity.right);
164                             break;
165                         case ccs.ListViewGravity.centerHorizontal:
166                             llp.setGravity(ccs.LinearGravity.centerHorizontal);
167                             break;
168                         default:
169                             break;
170                     }
171                 }
172                 break;
173             case ccs.ScrollViewDir.horizontal:
174                 var llp = item.getLayoutParameter(ccs.LayoutParameterType.linear);
175                 if (!llp) {
176                     var defaultLp = ccs.LinearLayoutParameter.create();
177                     switch (this._gravity) {
178                         case ccs.ListViewGravity.top:
179                             defaultLp.setGravity(ccs.LinearGravity.top);
180                             break;
181                         case ccs.ListViewGravity.bottom:
182                             defaultLp.setGravity(ccs.LinearGravity.bottom);
183                             break;
184                         case ccs.ListViewGravity.centerVertical:
185                             defaultLp.setGravity(ccs.LinearGravity.centerVertical);
186                             break;
187                         default:
188                             break;
189                     }
190                     if (this.getIndex(item) == 0) {
191                         defaultLp.setMargin(ccs.MarginZero());
192                     }
193                     else {
194                         defaultLp.setMargin(new ccs.Margin(this._itemsMargin, 0.0, 0.0, 0.0));
195                     }
196                     item.setLayoutParameter(defaultLp);
197                 }
198                 else {
199                     if (this.getIndex(item) == 0) {
200                         llp.setMargin(ccs.MarginZero());
201                     }
202                     else {
203                         llp.setMargin(new ccs.Margin(this._itemsMargin, 0.0, 0.0, 0.0));
204                     }
205                     switch (this._gravity) {
206                         case ccs.ListViewGravity.top:
207                             llp.setGravity(ccs.LinearGravity.top);
208                             break;
209                         case ccs.ListViewGravity.bottom:
210                             llp.setGravity(ccs.LinearGravity.bottom);
211                             break;
212                         case ccs.ListViewGravity.centerVertical:
213                             llp.setGravity(ccs.LinearGravity.centerVertical);
214                             break;
215                         default:
216                             break;
217                     }
218                 }
219                 break;
220             default:
221                 break;
222         }
223     },
224 
225     /**
226      * Push back a default item(create by a cloned model) into listview.
227      */
228     pushBackDefaultItem: function () {
229         if (!this._model) {
230             return;
231         }
232         var newItem = this._model.clone();
233         this._items.push(newItem);
234         this.remedyLayoutParameter(newItem);
235         this.addChild(newItem);
236         this._refreshViewDirty = true;
237     },
238 
239     /**
240      * Insert a default item(create by a cloned model) into listview.
241      * @param {Number} index
242      */
243     insertDefaultItem: function (index) {
244         if (!this._model) {
245             return;
246         }
247         var newItem = this._model.clone();
248         cc.ArrayAppendObjectToIndex(this._items, newItem, index);
249         this.remedyLayoutParameter(newItem);
250         this.addChild(newItem);
251         this._refreshViewDirty = true;
252     },
253 
254     /**
255      * Push back custom item into listview.
256      * @param {ccs.Widget} item
257      */
258     pushBackCustomItem: function (item) {
259         this._items.push(item);
260         this.remedyLayoutParameter(item);
261         this.addChild(item);
262         this._refreshViewDirty = true;
263     },
264 
265     /**
266      * Push back custom item into listview.
267      * @param {ccs.Widget} item
268      * @param {Number} index
269      */
270     insertCustomItem: function (item, index) {
271         cc.ArrayAppendObjectToIndex(this._items, item, index);
272         this.remedyLayoutParameter(item);
273         this.addChild(item);
274         this._refreshViewDirty = true;
275     },
276 
277     /**
278      * Removes a item whose index is same as the parameter.
279      * @param {Number} index
280      */
281     removeItem: function (index) {
282         var item = this.getItem(index);
283         if (!item) {
284             return;
285         }
286         cc.ArrayRemoveObject(this._items, item);
287         this.removeChild(item);
288         this._refreshViewDirty = true;
289     },
290 
291     /**
292      * Removes the last item of listview.
293      */
294     removeLastItem: function () {
295         this.removeItem(this._items.length - 1);
296     },
297 
298     /**
299      * Returns a item whose index is same as the parameter.
300      * @param {Number} index
301      * @returns {cc.Widget}
302      */
303     getItem: function (index) {
304         if (index < 0 || index >= this._items.length) {
305             return null;
306         }
307         return this._items[index];
308     },
309 
310     /**
311      * Returns the item container.
312      * @returns {Array}
313      */
314     getItems: function () {
315         return this._items;
316     },
317 
318     /**
319      * Returns the index of item.
320      * @param {ccs.Widget} item
321      * @returns {Number}
322      */
323     getIndex: function (item) {
324         return cc.ArrayGetIndexOfObject(this._items, item);
325     },
326 
327     /**
328      * Changes the gravity of listview.
329      * @param {ccs.ListViewGravity} gravity
330      */
331     setGravity: function (gravity) {
332         if (this._gravity == gravity) {
333             return;
334         }
335         this._gravity = gravity;
336         this._refreshViewDirty = true;
337     },
338 
339     /**
340      * Changes the margin between each item.
341      * @param {Number} margin
342      */
343     setItemsMargin: function (margin) {
344         if (this._itemsMargin == margin) {
345             return;
346         }
347         this._itemsMargin = margin;
348         this._refreshViewDirty = true;
349     },
350 
351     /**
352      * Changes scroll direction of scrollview.
353      * @param {ccs.ScrollViewDir } dir
354      */
355     setDirection: function (dir) {
356         switch (dir) {
357             case ccs.ScrollViewDir.vertical:
358                 this.setLayoutType(ccs.LayoutType.linearVertical);
359                 break;
360             case ccs.ScrollViewDir.horizontal:
361                 this.setLayoutType(ccs.LayoutType.linearHorizontal);
362                 break;
363             case ccs.ScrollViewDir.both:
364                 return;
365             default:
366                 return;
367                 break;
368         }
369         ccs.ScrollView.prototype.setDirection.call(this, dir);
370 
371     },
372 
373     /**
374      *  add event listener
375      * @param {Function} selector
376      * @param {Object} target
377      */
378     addEventListenerListView: function (selector, target) {
379         this._listViewEventListener = target;
380         this._listViewEventSelector = selector;
381     },
382 
383     selectedItemEvent: function () {
384         if(this._listViewEventSelector&&this._listViewEventListener){
385             this._listViewEventSelector.call(this._listViewEventListener, this, ccs.ListViewEventType.listViewOnselectedItem);
386         }
387     },
388 
389     interceptTouchEvent: function (handleState, sender, touchPoint) {
390         ccs.ScrollView.prototype.interceptTouchEvent.call(this, handleState, sender, touchPoint);
391         if (handleState != 1) {
392             var parent = sender;
393             while (parent) {
394                 if (parent && parent.getParent() == this._innerContainer) {
395                     this._curSelectedIndex = this.getIndex(parent);
396                     break;
397                 }
398                 parent = parent.getParent();
399             }
400             this.selectedItemEvent();
401         }
402     },
403 
404     /**
405      * get current selected index
406      * @returns {number}
407      */
408     getCurSelectedIndex: function () {
409         return this._curSelectedIndex;
410     },
411 
412     /**
413      * request refresh view
414      */
415     requestRefreshView: function () {
416         this._refreshViewDirty = true;
417     },
418 
419     refreshView: function () {
420         for (var i = 0; i < this._items.length; i++) {
421             var item = this._items[i];
422             item.setZOrder(i);
423             this.remedyLayoutParameter(item);
424         }
425         this.updateInnerContainerSize();
426     },
427 
428     sortAllChildren: function () {
429         ccs.ScrollView.prototype.sortAllChildren.call(this);
430         if (this._refreshViewDirty) {
431             this.refreshView();
432             this._refreshViewDirty = false;
433         }
434     },
435 
436     onSizeChanged: function () {
437         ccs.ScrollView.prototype.onSizeChanged.call(this);
438         this._refreshViewDirty = true;
439     },
440 
441     /**
442      * Returns the "class name" of widget.
443      * @returns {string}
444      */
445     getDescription: function () {
446         return "ListView";
447     },
448 
449     createCloneInstance: function () {
450         return ccs.ListView.create();
451     },
452 
453     copyClonedWidgetChildren: function (model) {
454         var arrayItems = model.getItems();
455         for (var i = 0; i < arrayItems.length; i++) {
456             var item = arrayItems[i];
457             this.pushBackCustomItem(item.clone());
458         }
459     },
460 
461     copySpecialProperties: function (listView) {
462         ccs.ScrollView.prototype.copySpecialProperties.call(this, listView);
463         this.setItemModel(listView._model);
464         this.setItemsMargin(listView._itemsMargin);
465         this.setGravity(listView._gravity);
466     }
467 });
468 
469 ccs.ListView.create = function () {
470     var uiListView = new ccs.ListView();
471     if (uiListView && uiListView.init()) {
472         return uiListView;
473     }
474     return null;
475 };