1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2010 Sprout Systems, Inc. and contributors.
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 /** @private
  9   SC.SplitChild and SC.SplitThumb both share this code, but we don't want it
 10   included twice if the view mixes in both SC.SplitChild and SC.SplitThumb.
 11 */
 12 SC.NeedsSplitParent = {
 13 
 14   /**
 15    * The SplitView that contains the child views to be adjusted.
 16    *
 17    * This is computed to be the first SplitView found in a search
 18    * up the view hierarchy. You can substitute your own SplitView
 19    *
 20    * @property SC.SplitView
 21   */
 22   splitView: function () {
 23     var view = this.get('parentView');
 24     while (view && !view.isSplitView) view = view.get('parentView');
 25     return view;
 26   }.property('parentView').cacheable(),
 27 
 28   /**
 29    * The layoutDirection of the SplitView. This is observed so that we
 30    * can update positioning if the layoutDirection changes but the position
 31    * and size properties do not.
 32    *
 33    * @type {LayoutDirection}
 34   */
 35   // NOTE: While an edge case, this is implemented because it makes it _much_
 36   // easier to write the sample in the Test Controls app.
 37   splitViewLayoutDirection: null,
 38   splitViewLayoutDirectionBinding: SC.Binding.oneWay('*splitView.layoutDirection')
 39 
 40 };
 41 
 42 
 43 /**
 44   @namespace
 45   Child views of SplitViews should use this mixin to get their positioning
 46   logic and default settings.
 47 
 48   By default, SplitView merely sets the position and size properties on its
 49   child views. This mixin observes the position and size properties and
 50   calls adjust().
 51 */
 52 SC.SplitChild =
 53   /** @scope SC.SplitChild.prototype */{
 54   /**
 55    * Set to YES if your Split Child is a divider view.
 56    *
 57    * @type Boolean
 58   */
 59   isSplitDivider: NO,
 60 
 61   /**
 62    * The minimum size for the SC.SplitView child. This view will
 63    * be unable to be resized smaller than this size.
 64    *
 65    * @default 100
 66    *
 67    * @type {Number}
 68   */
 69   minimumSize: 100,
 70 
 71   /**
 72    * The maximum size for the SC.SplitView child. This view will
 73    * be unable to be resized to a size larger than this value.
 74    *
 75    * If undefined, there is no limit.
 76    *
 77    * @default 100
 78    *
 79    * @type {Number}
 80   */
 81   maximumSize: undefined,
 82 
 83   /**
 84    * The current size of the SC.SplitView child. Use this to set the default
 85    * size.
 86    *
 87    * If you specify a size, autoResizeStyle will default to SC.RESIZE_MANUAL;
 88    * if you don't, SplitView will assume you want it to fill the available space
 89    * and use SC.RESIZE_AUTOMATIC.
 90    *
 91    * @default undefined
 92    *
 93    * @type Number
 94   */
 95   size: undefined,
 96 
 97   /**
 98    * [RO] The current position of the SC.SplitView. This is read-only, and is set
 99    * by the SplitView as it positions the views.
100    *
101    * @type Number
102   */
103   position: 0,
104 
105   /**
106    * An amount to add to the position when adjusting layout.
107    *
108    * For example, if SC.SplitView positions this view at 100, and
109    * positionOffset is -5, the view's layout will will have a position of 95.
110    *
111    * This can be used in conjunction with sizeOffset to make dividers
112    * overlap the other views in the SplitView to have a larger grab area.
113    *
114    * TODO: Get this value from theme.
115    *
116    * @type Number
117   */
118   positionOffset: SC.propertyFromRenderDelegate('splitPositionOffset', 0),
119 
120   /**
121    * An amount to add to the assigned size when adjusting layout.
122    *
123    * For example, if SC.SplitView gives this view a size of 1, but the
124    * sizeOffset is 10, the view's layout will be given a width or height of 11.
125    *
126    * This can be used in conjunction with positionOffset to make dividers
127    * overlap the other views in the SplitView to have a larger grab area.
128    *
129    * TODO: Get this value from theme.
130    *
131    * @type Number
132   */
133   sizeOffset: SC.propertyFromRenderDelegate('splitSizeOffset', 0),
134 
135   /**
136    * If YES, the SC.SplitView can collapse this view when the user
137    * double-clicks an adjacent divider, or when the view is resized
138    * to a size smaller than the collapseAtSize.
139    *
140    * @type {Boolean}
141   */
142   canCollapse: NO,
143 
144   /**
145    * If the user attempts to resize the view to a size below this number,
146    * the view will collapse.
147    *
148    * If undefined, the view cannot be collapsed due to a resize.
149    *
150    * @type {Number}
151   */
152   collapseAtSize: undefined,
153 
154   /**
155    * When (and if) the view should automatically resize due to the SplitView changing size.
156    *
157    * - SC.RESIZE_AUTOMATIC: always resize when the SplitView changes size.
158    * - SC.RESIZE_MANUAL: resize only when the user moves a divider, or all
159    *   SC.RESIZE_AUTOMATIC views have already been resized as much as possible.
160    * - SC.FIXED_SIZE: Never resize.
161    *
162    * If you specify an autoResizeStyle, it will be used. If you leave it at `undefined`,
163    * it will look at `size`: if a `size` is supplied, it will use `SC.RESIZE_MANUAL`; otherwise
164    * it will use `SC.RESIZE_AUTOMATIC`.
165    *
166    * @default based on size
167    * @type {AutoResizeStyle}
168   */
169   autoResizeStyle: undefined,
170 
171   /**
172    * If NO, moving the divider before this view will not resize the view, but
173    * instead, move it—causing further views to move as well. Most SplitView
174    * children will want this set to YES.
175    *
176    * @type Boolean
177   */
178   compensatesForMovement: YES,
179 
180   /**
181    * When NO, the view can only be adjusted when the user drags a divider
182    * immediately adjacent to it. If YES, the view can be adjusted indirectly
183    * from earlier dividers as well.
184    *
185    * For example, assume you have a 3-pane setup. Increasing the size of left pane will
186    * resize the middle one until its minimum size is reached. SplitView will then
187    * try to adjust the right pane. If the right pane has allowsIndirectAdjustments
188    * set to YES, SplitView will shrink the right pane to make way. If NO, the
189    * left pane will be unable to resize further.
190    *
191    * Default: YES.
192    *
193    * @type {Boolean}
194   */
195   allowsIndirectAdjustments: YES,
196 
197   /** @private Include SC.NeedsSplitParent if it hasn't already been included. */
198   initMixin: function () {
199     if (!this.splitView) {
200       this.mixin(SC.NeedsSplitParent);
201     }
202   },
203 
204   //
205   // Positioning logic
206   //
207   _scsvc_positionOrSizeDidChange: function() {
208     this.invokeOnce('splitChildLayoutDidChange');
209   }.observes('position', 'size'),
210 
211   /**
212    * Called when either the position or size of the child has changed, and layout
213    * needs to be updated accordingly. You may override this method to take into
214    * account any custom layout. The default handles the position- and sizeOffset
215    * properties by adding them to the position and size, respectively.
216    *
217    * For instance, while the default implementation changes left/right/width/height
218    * to fill in one direction and fit in the allocated position in the other, you could
219    * make it only set left/width or top/height.
220    *
221   */
222   splitChildLayoutDidChange: function() {
223     var split = this.get('splitView');
224     if (!split) return;
225 
226     var position = this.get('position') + this.get('positionOffset'),
227         size = this.get('size') + this.get('sizeOffset');
228 
229 
230     if (split.get('layoutDirection') === SC.LAYOUT_HORIZONTAL) {
231       this.set('layout', {
232         left: position,
233         width: size,
234         top: 0, bottom: 0
235       });
236     } else {
237       this.set('layout', {
238         top: position,
239         height: size,
240         right: 0, left: 0
241       });
242     }
243   },
244 
245   splitViewLayoutDirectionDidChange: function() {
246     this.invokeOnce('splitChildLayoutDidChange');
247   }.observes('splitViewLayoutDirection')
248 };
249