1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2011 Strobe Inc. and contributors.
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 sc_require('views/collection');
  9 
 10 /**
 11   @class
 12 
 13   A StackedView is a CollectionView that expects its content to use static
 14   layout to stack vertically.  This type of collection view is not designed
 15   for use with large size collections, but it can be very useful for
 16   collections with complex displays and variable heights such as comments or
 17   small notification queues.
 18 
 19   ## Static Layout
 20 
 21   This view makes no attempt to size or position your child views.  It assumes
 22   you are using StaticLayout for your child views.  If you don't enable static
 23   layout your views will probably overlay on top of each other and will look
 24   incorrect.
 25 
 26   Note also that the default layout for this view set's the height to "auto".
 27   This is usually the behavior you will want.
 28 
 29   @extends SC.CollectionView
 30   @since SproutCore 0.9
 31 */
 32 SC.StackedView = SC.CollectionView.extend(
 33 /** @scope SC.StackedView.prototype */ {
 34 
 35   /**
 36     @type Array
 37     @default ['sc-stacked-view']
 38     @see SC.View#classNames
 39   */
 40   classNames: ['sc-stacked-view'],
 41 
 42   /**
 43     Default layout for a stacked view will fill the parent view but auto-
 44     adjust the height of the view.
 45 
 46     @type Hash
 47     @default `{ top: 0, left: 0, right: 0, height: 1 }`
 48     @see SC.View#layout
 49   */
 50   layout: { top: 0, left: 0, right: 0, height: 1 },
 51 
 52   /**
 53     Return full range of its indexes for nowShowing
 54 
 55     @param {Rect} rect
 56     @returns {SC.IndexSet} full range of indexes
 57   */
 58   computeNowShowing: function () {
 59     return this.get('allContentIndexes');
 60   },
 61 
 62   /**
 63     Updates the height of the stacked view to reflect the current content of
 64     the view.  This is called automatically whenever an item view is reloaded.
 65     You can also call this method directly if the height of one of your views
 66     has changed.
 67 
 68     The height will be recomputed based on the actual location and dimensions
 69     of the last child view.
 70 
 71     Note that normally this method will defer actually updating the height
 72     of the view until the end of the run loop.  You can force an immediate
 73     update by passing YES to the "immediately" parameter.
 74 
 75     @param {Boolean} immediately YES to update immediately
 76     @returns {SC.StackedView} receiver
 77   */
 78   updateHeight: function (immediately) {
 79     if (immediately) this._updateHeight();
 80     else this.invokeLast(this._updateHeight);
 81     // ^ use invokeLast() here because we need to wait until all rendering has
 82     //   completed.
 83 
 84     return this;
 85   },
 86 
 87   /** @private */
 88   _updateHeight: function () {
 89 
 90     var childViews = this.get('childViews'),
 91         len        = childViews.get('length'),
 92         view, layer, height;
 93 
 94     if (len === 0) {
 95       height = 1;
 96     } else {
 97       view = childViews.objectAt(len - 1);
 98       layer = view ? view.get('layer') : null;
 99       height = layer ? (layer.offsetTop + layer.offsetHeight) : 1;
100       layer = null; // avoid memory leaks
101     }
102     this.adjust('minHeight', height);
103     this.set('calculatedHeight', height);
104   },
105 
106   // ..........................................................
107   // INTERNAL SUPPORT
108   //
109 
110   /** @private
111     Whenever the collection view reloads some views, reset the cache on the
112     frame as well so that it will recalculate.
113   */
114   reloadIfNeeded: function () {
115     sc_super();
116 
117     return this.updateHeight();
118   },
119 
120   /** @private
121     When layer is first created, make sure we update the height using the
122     newly calculated value.
123   */
124   didCreateLayer: function () { return this.updateHeight(); }
125 
126 });
127