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      * Get the margin between each item.
353      * @returns {Number}
354      */
355     getItemsMargin:function(){
356         return this._itemsMargin;
357     },
358 
359     /**
360      * Changes scroll direction of scrollview.
361      * @param {ccs.ScrollViewDir } dir
362      */
363     setDirection: function (dir) {
364         switch (dir) {
365             case ccs.ScrollViewDir.vertical:
366                 this.setLayoutType(ccs.LayoutType.linearVertical);
367                 break;
368             case ccs.ScrollViewDir.horizontal:
369                 this.setLayoutType(ccs.LayoutType.linearHorizontal);
370                 break;
371             case ccs.ScrollViewDir.both:
372                 return;
373             default:
374                 return;
375                 break;
376         }
377         ccs.ScrollView.prototype.setDirection.call(this, dir);
378 
379     },
380 
381     /**
382      *  add event listener
383      * @param {Function} selector
384      * @param {Object} target
385      */
386     addEventListenerListView: function (selector, target) {
387         this._listViewEventListener = target;
388         this._listViewEventSelector = selector;
389     },
390 
391     selectedItemEvent: function () {
392         if(this._listViewEventSelector&&this._listViewEventListener){
393             this._listViewEventSelector.call(this._listViewEventListener, this, ccs.ListViewEventType.listViewOnselectedItem);
394         }
395     },
396 
397     interceptTouchEvent: function (handleState, sender, touchPoint) {
398         ccs.ScrollView.prototype.interceptTouchEvent.call(this, handleState, sender, touchPoint);
399         if (handleState != 1) {
400             var parent = sender;
401             while (parent) {
402                 if (parent && parent.getParent() == this._innerContainer) {
403                     this._curSelectedIndex = this.getIndex(parent);
404                     break;
405                 }
406                 parent = parent.getParent();
407             }
408             this.selectedItemEvent();
409         }
410     },
411 
412     /**
413      * get current selected index
414      * @returns {number}
415      */
416     getCurSelectedIndex: function () {
417         return this._curSelectedIndex;
418     },
419 
420     /**
421      * request refresh view
422      */
423     requestRefreshView: function () {
424         this._refreshViewDirty = true;
425     },
426 
427     refreshView: function () {
428         for (var i = 0; i < this._items.length; i++) {
429             var item = this._items[i];
430             item.setZOrder(i);
431             this.remedyLayoutParameter(item);
432         }
433         this.updateInnerContainerSize();
434     },
435 
436     sortAllChildren: function () {
437         ccs.ScrollView.prototype.sortAllChildren.call(this);
438         if (this._refreshViewDirty) {
439             this.refreshView();
440             this._refreshViewDirty = false;
441         }
442     },
443 
444     onSizeChanged: function () {
445         ccs.ScrollView.prototype.onSizeChanged.call(this);
446         this._refreshViewDirty = true;
447     },
448 
449     /**
450      * Returns the "class name" of widget.
451      * @returns {string}
452      */
453     getDescription: function () {
454         return "ListView";
455     },
456 
457     createCloneInstance: function () {
458         return ccs.ListView.create();
459     },
460 
461     copyClonedWidgetChildren: function (model) {
462         var arrayItems = model.getItems();
463         for (var i = 0; i < arrayItems.length; i++) {
464             var item = arrayItems[i];
465             this.pushBackCustomItem(item.clone());
466         }
467     },
468 
469     copySpecialProperties: function (listView) {
470         ccs.ScrollView.prototype.copySpecialProperties.call(this, listView);
471         this.setItemModel(listView._model);
472         this.setItemsMargin(listView._itemsMargin);
473         this.setGravity(listView._gravity);
474     }
475 });
476 
477 ccs.ListView.create = function () {
478     var uiListView = new ccs.ListView();
479     if (uiListView && uiListView.init()) {
480         return uiListView;
481     }
482     return null;
483 };