sproutcore 1.11.0.rc3 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +6 -14
  2. data/CHANGELOG +5 -0
  3. data/VERSION.yml +1 -1
  4. data/lib/frameworks/sproutcore/Buildfile +3 -2
  5. data/lib/frameworks/sproutcore/CHANGELOG.md +59 -10
  6. data/lib/frameworks/sproutcore/apps/showcase/resources/main_page.js +1 -0
  7. data/lib/frameworks/sproutcore/apps/showcase/resources/stylesheet.css +9 -4
  8. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/manipulation.js +1 -1
  9. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +0 -10
  10. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/locale.js +1 -1
  11. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +10 -45
  12. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/selection_set.js +3 -3
  13. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/touch.js +76 -0
  14. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/{touch.js → touch_test.js} +64 -0
  15. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/pane/append_remove.js +1 -1
  16. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/pane/design_mode_test.js +61 -24
  17. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/createChildViews.js +1 -2
  18. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/destroy.js +0 -3
  19. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/{layoutStyle.js → layout_style_test.js} +4 -4
  20. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layout_test.js +602 -0
  21. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/viewDidResize.js +0 -23
  22. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +18 -17
  23. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +5 -5
  24. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/design_mode.js +64 -24
  25. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout.js +904 -871
  26. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +1 -3
  27. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/statechart.js +40 -24
  28. data/lib/frameworks/sproutcore/frameworks/datastore/data_sources/fixtures.js +2 -2
  29. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +5 -5
  30. data/lib/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +14 -14
  31. data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +1 -1
  32. data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +2 -2
  33. data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +36 -33
  34. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/writeAttribute.js +1 -1
  35. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/autonomous_dataSourceCallbacks.js +6 -6
  36. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/commitChangesFromNestedStore.js +1 -1
  37. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/commitRecord.js +1 -1
  38. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/dataSourceCallbacks.js +7 -7
  39. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/destroyRecord.js +2 -2
  40. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/recordDidChange.js +2 -2
  41. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/retrieveRecord.js +4 -4
  42. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/system/datetime.js +11 -11
  43. data/lib/frameworks/sproutcore/frameworks/designer/coders/object.js +1 -1
  44. data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +30 -1
  45. data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +12 -1
  46. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_scroll.js +5 -3
  47. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_scroller_view.js +0 -36
  48. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll_view.js +3 -3
  49. data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +1 -1
  50. data/lib/frameworks/sproutcore/frameworks/desktop/views/static_content.js +1 -1
  51. data/lib/frameworks/sproutcore/frameworks/foundation/gestures/pinch_gesture.js +286 -0
  52. data/lib/frameworks/sproutcore/frameworks/foundation/gestures/swipe_gesture.js +449 -0
  53. data/lib/frameworks/sproutcore/frameworks/foundation/gestures/tap_gesture.js +259 -0
  54. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/gesturable.js +218 -30
  55. data/lib/frameworks/sproutcore/frameworks/foundation/system/gesture.js +259 -158
  56. data/lib/frameworks/sproutcore/frameworks/foundation/system/string.js +58 -50
  57. data/lib/frameworks/sproutcore/frameworks/foundation/system/utils/string_measurement.js +1 -1
  58. data/lib/frameworks/sproutcore/frameworks/foundation/tests/gestures/pinch_gesture_test.js +321 -0
  59. data/lib/frameworks/sproutcore/frameworks/foundation/tests/gestures/swipe_gesture_test.js +154 -0
  60. data/lib/frameworks/sproutcore/frameworks/foundation/tests/gestures/tap_gesture_test.js +55 -0
  61. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/gesturable_test.js +233 -0
  62. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/staticLayout.js +2 -2
  63. data/lib/frameworks/sproutcore/frameworks/foundation/tests/system/gesture_test.js +254 -0
  64. data/lib/frameworks/sproutcore/frameworks/foundation/views/container.js +1 -0
  65. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +1 -1
  66. data/lib/frameworks/sproutcore/frameworks/legacy/object_keys_polyfill.js +51 -0
  67. data/lib/frameworks/sproutcore/frameworks/{core_foundation/system/req_anim_frame.js → legacy/request_animation_frame_polyfill.js} +10 -3
  68. data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +19 -25
  69. data/lib/frameworks/sproutcore/frameworks/media/views/controls.js +7 -7
  70. data/lib/frameworks/sproutcore/frameworks/media/views/mini_controls.js +3 -3
  71. data/lib/frameworks/sproutcore/frameworks/media/views/simple_controls.js +9 -9
  72. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
  73. data/lib/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/insertAt.js +2 -2
  74. data/lib/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/removeAt.js +1 -1
  75. data/lib/frameworks/sproutcore/frameworks/runtime/ext/array.js +1 -1
  76. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/array.js +2 -2
  77. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/freezable.js +2 -2
  78. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +66 -5
  79. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/tree.js +44 -0
  80. data/lib/frameworks/sproutcore/frameworks/runtime/private/observer_queue.js +4 -1
  81. data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +0 -25
  82. data/lib/frameworks/sproutcore/frameworks/runtime/system/enumerator.js +1 -1
  83. data/lib/frameworks/sproutcore/frameworks/runtime/system/error.js +67 -15
  84. data/lib/frameworks/sproutcore/frameworks/runtime/system/index_set.js +6 -11
  85. data/lib/frameworks/sproutcore/frameworks/runtime/system/set.js +6 -6
  86. data/lib/frameworks/sproutcore/frameworks/runtime/system/string.js +1 -1
  87. data/lib/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/{observable.js → observable_test.js} +110 -16
  88. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/error.js +21 -0
  89. data/lib/frameworks/sproutcore/frameworks/statechart/system/statechart.js +2 -2
  90. data/lib/frameworks/sproutcore/frameworks/template_view/ext/handlebars.js +1 -1
  91. data/lib/frameworks/sproutcore/frameworks/testing/system/plan.js +1 -1
  92. data/lib/sproutcore/render_engines/haml.rb +1 -1
  93. metadata +610 -604
  94. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layout.js +0 -210
  95. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutDidChange.js +0 -275
  96. data/lib/frameworks/sproutcore/frameworks/foundation/gestures/pinch.js +0 -119
  97. data/lib/frameworks/sproutcore/frameworks/foundation/gestures/swipe.js +0 -234
  98. data/lib/frameworks/sproutcore/frameworks/foundation/gestures/tap.js +0 -157
@@ -55,29 +55,6 @@ test("parentViewDidResize should only be called when the parent's layout propert
55
55
  // equals(callCount, 2, 'parentViewDidResize should invoke twice');
56
56
  });
57
57
 
58
- /**
59
- When a view's layout changes, _checkForResize determines whether the size has changed using a comparison of the previously
60
- cached layout and the new (current) layout. If it seems that the view has resized, it calls `viewDidResize`. Previously
61
- it would call viewDidResize and then update the _previousLayout cache afterward. This meant that any adjustments that
62
- were triggered by viewDidResize (which would in turn call _checkForResize) would compare the new layout against the
63
- previous previous layout, instead of just the previous layout.
64
-
65
- Long story short, to ensure that _checkForResize is checking the current layout against the *last* layout, it's important
66
- that the last layout, _previousLayout, is updated *before* continuing on.
67
- */
68
- test("SC.View.prototype._checkForResize() updates the _previousLayout cache before calling viewDidResize", function () {
69
- var view1 = SC.View.create({
70
- layout: { width: 200, height: 200 },
71
- viewDidResize: function () {
72
- ok(this._previousLayout !== originalPreviousLayout, "The previous layout should not be the same anymore.");
73
- }
74
- }),
75
- originalPreviousLayout;
76
-
77
- originalPreviousLayout = view1.get('layout');
78
- SC.run(function () { view1.adjust({ width: 100 }); });
79
- });
80
-
81
58
  test("The view's frame should only notify changes when its layout changes if the effective size or position actually change.", function () {
82
59
  var view2 = SC.View.create({
83
60
  frameCallCount: 0,
@@ -99,6 +99,14 @@ SC.CoreView.reopen(
99
99
  */
100
100
  createdByParent: false,
101
101
 
102
+ /** @deprecated Version 1.11.0 Please use parentView instead. */
103
+ owner: function () {
104
+ //@if(debug)
105
+ SC.warn("Developer Warning: The `owner` property of SC.View has been deprecated in favor of the `parentView`, which is the same value. Please use `parentView`.");
106
+ //@endif
107
+ return this.get('parentView');
108
+ }.property('parentView').cacheable(),
109
+
102
110
  /**
103
111
  The current pane.
104
112
 
@@ -1104,7 +1112,7 @@ SC.CoreView.reopen(
1104
1112
  This method is invoked whenever the clippingFrame changes, notifying
1105
1113
  each child view that its clippingFrame has also changed.
1106
1114
  */
1107
- _sc_view_clippingFrameDidChange: function () {
1115
+ _sc_clippingFrameDidChange: function () {
1108
1116
  this.notifyPropertyChange('clippingFrame');
1109
1117
  },
1110
1118
 
@@ -1320,8 +1328,6 @@ SC.CoreView.reopen(
1320
1328
  // Orphan the view if adopted.
1321
1329
  this._doOrphan();
1322
1330
 
1323
- // TODO: Deprecate owner in this sense.
1324
- this.set('owner', null);
1325
1331
  delete this.page;
1326
1332
  },
1327
1333
 
@@ -1414,8 +1420,8 @@ SC.CoreView.reopen(
1414
1420
  // clone the hash that was given so we do not pollute it if it's being reused
1415
1421
  else { attrs = SC.clone(attrs); }
1416
1422
 
1417
- // Assign the owner, parentView & page to ourself.
1418
- attrs.owner = attrs.parentView = this;
1423
+ // Assign the parentView & page to ourself.
1424
+ attrs.parentView = this;
1419
1425
  if (!attrs.page) { attrs.page = this.page; }
1420
1426
 
1421
1427
  // Track that we created this view.
@@ -1430,12 +1436,11 @@ SC.CoreView.reopen(
1430
1436
  } else {
1431
1437
  view = view.create(attrs);
1432
1438
  }
1433
- // Assign the parentView & owner if the view is an instance.
1439
+ // Assign the parentView if the view is an instance.
1434
1440
  // TODO: This should not be accepting view instances, for the purpose of lazy code elsewhere in the framework.
1435
1441
  // We should ensure users of `createChildViews` are using appendChild and other manipulation methods.
1436
1442
  } else {
1437
1443
  view.set('parentView', this);
1438
- view.set('owner', this);
1439
1444
  view._adopted();
1440
1445
 
1441
1446
  if (!view.get('page')) { view.set('page', this.page); }
@@ -2292,8 +2297,8 @@ SC.View = SC.CoreView.extend(/** @scope SC.View.prototype */{
2292
2297
  // current frame (see original computeFrameWithParentFrame in views/view.js)
2293
2298
  if (this.get('useStaticLayout')) {
2294
2299
  f = sc_super();
2295
- f = f ? this._adjustForBorder(f, layout) : null;
2296
- f = f ? this._adjustForScale(f, layout) : null;
2300
+ f = f ? this._sc_adjustForBorder(f, layout) : null;
2301
+ f = f ? this._sc_adjustForScale(f, layout) : null;
2297
2302
  return f;
2298
2303
  }
2299
2304
 
@@ -2311,15 +2316,11 @@ SC.View = SC.CoreView.extend(/** @scope SC.View.prototype */{
2311
2316
  lcY = layout.centerY;
2312
2317
 
2313
2318
  if (lW === AUTO) {
2314
- error = SC.Error.desc(("%@.layout() cannot use width:auto if staticLayout is disabled").fmt(this), "%@".fmt(this), -1);
2315
- SC.Logger.error(error.toString());
2316
- throw error;
2319
+ SC.throw(("%@.layout() cannot use width:auto if staticLayout is disabled").fmt(this), "%@".fmt(this), -1);
2317
2320
  }
2318
2321
 
2319
2322
  if (lH === AUTO) {
2320
- error = SC.Error.desc(("%@.layout() cannot use height:auto if staticLayout is disabled").fmt(this), "%@".fmt(this), -1);
2321
- SC.Logger.error(error.toString());
2322
- throw error;
2323
+ SC.throw(("%@.layout() cannot use height:auto if staticLayout is disabled").fmt(this), "%@".fmt(this), -1);
2323
2324
  }
2324
2325
 
2325
2326
  if (!pdim) { pdim = this.computeParentDimensions(layout); }
@@ -2440,7 +2441,7 @@ SC.View = SC.CoreView.extend(/** @scope SC.View.prototype */{
2440
2441
  // Okay we have all our numbers. Let's adjust them for things.
2441
2442
 
2442
2443
  // First, adjust for border.
2443
- f = this._adjustForBorder(f, layout);
2444
+ f = this._sc_adjustForBorder(f, layout);
2444
2445
 
2445
2446
  // Make sure the width/height fix their min/max (note the inlining of SC.none for performance)...
2446
2447
  /*jshint eqnull:true */
@@ -2450,7 +2451,7 @@ SC.View = SC.CoreView.extend(/** @scope SC.View.prototype */{
2450
2451
  if ((layout.minWidth != null) && (f.width < layout.minWidth)) f.width = layout.minWidth;
2451
2452
 
2452
2453
  // Finally, adjust for scale.
2453
- f = this._adjustForScale(f, layout);
2454
+ f = this._sc_adjustForScale(f, layout);
2454
2455
 
2455
2456
  return f;
2456
2457
  },
@@ -405,11 +405,6 @@ SC.View.reopen(
405
405
 
406
406
  // Always run the animation asynchronously so that the original layout is guaranteed to be applied to the DOM.
407
407
  this.invokeNext('_animate');
408
-
409
- // Route.
410
- if (this.get('viewState') === SC.CoreView.ATTACHED_SHOWN) {
411
- this.set('viewState', SC.CoreView.ATTACHED_SHOWN_ANIMATING);
412
- }
413
408
  } else if (!optionsDidChange) {
414
409
  this.invokeNext(function () {
415
410
  this.runAnimationCallback(options, null, false);
@@ -433,6 +428,11 @@ SC.View.reopen(
433
428
 
434
429
  // Apply the animation layout.
435
430
  this.set('layout', animationLayout);
431
+
432
+ // Route.
433
+ if (this.get('viewState') === SC.CoreView.ATTACHED_SHOWN) {
434
+ this.set('viewState', SC.CoreView.ATTACHED_SHOWN_ANIMATING);
435
+ }
436
436
  }
437
437
  },
438
438
 
@@ -67,6 +67,65 @@ SC.View.reopen(
67
67
  }
68
68
  },
69
69
 
70
+ _sc_assignProperty: function (key, value) {
71
+ if (key === 'layout') {
72
+ var newExplicitLayout = this._sc_computeExplicitLayout(value), // Convert the layout to an explicit layout.
73
+ layoutDiff = {},
74
+ explicitLayout = this.get('explicitLayout');
75
+ for (var layoutKey in newExplicitLayout) {
76
+ var currentValue = explicitLayout[layoutKey];
77
+
78
+ layoutDiff[layoutKey] = currentValue === undefined ? null : currentValue;
79
+
80
+ if (layoutKey === 'centerX') {
81
+ layoutDiff.left = explicitLayout.left;
82
+ layoutDiff.right = explicitLayout.right;
83
+ }
84
+
85
+ if (layoutKey === 'centerY') {
86
+ layoutDiff.top = explicitLayout.top;
87
+ layoutDiff.bottom = explicitLayout.bottom;
88
+ }
89
+ }
90
+
91
+ this._originalProperties.layout = layoutDiff;
92
+ } else {
93
+ // Get the original value of the property for reset.
94
+ this._originalProperties[key] = this.get(key);
95
+ }
96
+
97
+ // Apply the override.
98
+ if (key === 'layout') {
99
+ //@if(debug)
100
+ if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) {
101
+ SC.Logger.log(' - Adjusting %@: %@ (cached as %@)'.fmt(key, SC.inspect(value), SC.inspect(this._originalProperties[key])));
102
+ }
103
+ //@endif
104
+ this.adjust(value);
105
+ } else {
106
+ //@if(debug)
107
+ if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) {
108
+ SC.Logger.log(' - Setting %@: %@ (cached as %@)'.fmt(key, SC.inspect(value), SC.inspect(this._originalProperties[key])));
109
+ }
110
+ //@endif
111
+ this.set(key,value);
112
+ }
113
+ },
114
+
115
+ _sc_revertProperty: function (key, oldValue) {
116
+ //@if(debug)
117
+ if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) {
118
+ SC.Logger.log(' - Resetting %@ to %@'.fmt(key, SC.inspect(oldValue)));
119
+ }
120
+ //@endif
121
+
122
+ if (key === 'layout') {
123
+ this.adjust(oldValue);
124
+ } else {
125
+ this.set(key, oldValue);
126
+ }
127
+ },
128
+
70
129
  /**
71
130
  Updates the design mode for this view.
72
131
 
@@ -114,18 +173,13 @@ SC.View.reopen(
114
173
  prevProperties = this._originalProperties;
115
174
  if (prevProperties) {
116
175
  //@if(debug)
117
- if (SC.LOG_DESIGN_MODE) {
118
- SC.Logger.log('%@ — Removing previous design property overrides from "%@":'.fmt(this, lastDesignMode));
176
+ if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) {
177
+ SC.Logger.log('%@ — Removing previous design property overrides set by "%@":'.fmt(this, lastDesignMode));
119
178
  }
120
179
  //@endif
121
180
 
122
181
  for (key in prevProperties) {
123
- //@if(debug)
124
- if (SC.LOG_DESIGN_MODE) {
125
- SC.Logger.log(' - Resetting %@ to %@'.fmt(key, prevProperties[key]));
126
- }
127
- //@endif
128
- this.set(key, prevProperties[key]);
182
+ this._sc_revertProperty(key, prevProperties[key]);
129
183
  }
130
184
 
131
185
  // Remove the cache.
@@ -138,7 +192,7 @@ SC.View.reopen(
138
192
  newProperties = SC.merge(modeAdjust[size], modeAdjust[designMode]);
139
193
 
140
194
  //@if(debug)
141
- if (SC.LOG_DESIGN_MODE) {
195
+ if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) {
142
196
  SC.Logger.log('%@ — Applying design properties for "%@":'.fmt(this, designMode));
143
197
  }
144
198
  //@endif
@@ -146,21 +200,7 @@ SC.View.reopen(
146
200
  // Cache the original properties for reset.
147
201
  this._originalProperties = {};
148
202
  for (key in newProperties) {
149
- // Cache the original value for reset.
150
- this._originalProperties[key] = this.get(key);
151
-
152
- //@if(debug)
153
- if (SC.LOG_DESIGN_MODE) {
154
- SC.Logger.log(' - Setting %@: %@'.fmt(key, newProperties[key]));
155
- }
156
- //@endif
157
-
158
- // Apply the override.
159
- if (key === 'layout') {
160
- this.adjust(newProperties[key]);
161
- } else {
162
- this.set(key, newProperties[key]);
163
- }
203
+ this._sc_assignProperty(key, newProperties[key]);
164
204
  }
165
205
  }
166
206
  }
@@ -36,10 +36,12 @@ SC._SCALE_VALUE_REGEX = /^\d+(,\d+){0,2}$/;
36
36
  SC.View.reopen(
37
37
  /** @scope SC.View.prototype */ {
38
38
 
39
- /**
40
- Walks like a duck. Is YES to indicate that a view has layout support.
41
- */
42
- hasLayout: YES,
39
+ // ------------------------------------------------------------------------
40
+ // Properties
41
+ //
42
+
43
+ /* @private Internal variable used to check for layout changes that resize. */
44
+ _sc_previousLayout: null,
43
45
 
44
46
  /**
45
47
  The view's background color. Only recommended for use during prototyping and in views
@@ -55,190 +57,261 @@ SC.View.reopen(
55
57
  */
56
58
  backgroundColor: null,
57
59
 
58
- /* @private Internal variable used to check for layout changes that resize. */
59
- _previousLayout: null,
60
-
61
60
  /**
62
- Activates use of brower's static layout. To activate, set this property to YES.
63
-
64
- @type Boolean
65
- @default NO
61
+ The frame of the view including the borders and scale
66
62
  */
67
- useStaticLayout: NO,
63
+ borderFrame: function () {
64
+ var frame = this.get('frame'),
65
+ ret = null;
68
66
 
69
- // ...........................................
70
- // LAYOUT
71
- //
67
+ if (frame) {
68
+ /*jshint eqnull:true */
69
+ var layout = this.get('layout'),
70
+ defaultValue = layout.border == null ? 0 : layout.border,
71
+ borderTop = this._sc_explicitValueFor(layout.borderTop, defaultValue),
72
+ borderRight = this._sc_explicitValueFor(layout.borderRight, defaultValue),
73
+ borderBottom = this._sc_explicitValueFor(layout.borderBottom, defaultValue),
74
+ borderLeft = this._sc_explicitValueFor(layout.borderLeft, defaultValue);
72
75
 
73
- /**
74
- The 'frame' property depends on the 'layout' property as well as the
75
- parent view's frame. In order to properly invalidate any cached values,
76
- we need to invalidate the cache whenever 'layout' changes. However,
77
- observing 'layout' does not guarantee that; the observer might not be run
78
- immediately.
76
+ ret = {
77
+ x: frame.x,
78
+ y: frame.y,
79
+ width: frame.width,
80
+ height: frame.height
81
+ };
79
82
 
80
- In order to avoid any window of opportunity where the cached frame could
81
- be invalid, we need to force layoutDidChange() to always immediately run
82
- whenever 'layout' is set.
83
- */
84
- propertyDidChange: function (key, value, _keepCache) {
85
- //@if(debug)
86
- // Debug mode only property validation.
87
- switch (key) {
88
- case 'layout':
89
- // If a layout value is accidentally set to NaN, this can result in infinite loops. Help the developer out by failing
90
- // early so that they can follow the stack trace to the problem.
91
- var layout = this.get('layout');
92
- for (var property in layout) {
93
- if (!layout.hasOwnProperty(property)) { continue; }
94
-
95
- var layoutValue = layout[property];
96
- if (isNaN(layoutValue) && (layoutValue !== SC.LAYOUT_AUTO) &&
97
- !SC._ROTATION_VALUE_REGEX.exec(layoutValue) && !SC._SCALE_VALUE_REGEX.exec(layoutValue)) {
98
- throw new Error("SC.View layout property set to invalid value, %@: %@.".fmt(property, layoutValue));
99
- }
83
+ var scale = frame.scale;
84
+ /*jshint eqnull:true*/
85
+ if (scale != null) {
86
+ var scaledBorderTop = borderTop * scale,
87
+ scaledBorderRight = borderRight * scale,
88
+ scaledBorderBottom = borderBottom * scale,
89
+ scaledBorderLeft = borderLeft * scale;
90
+
91
+ ret.scale = scale;
92
+ ret.x -= scaledBorderLeft;
93
+ ret.y -= scaledBorderTop;
94
+ ret.width += scaledBorderLeft + scaledBorderRight;
95
+ ret.height += scaledBorderTop + scaledBorderBottom;
96
+ } else {
97
+ ret.x -= borderLeft;
98
+ ret.y -= borderTop;
99
+ ret.width += borderLeft + borderRight;
100
+ ret.height += borderTop + borderBottom;
100
101
  }
101
- break;
102
- }
103
- //@endif
104
102
 
105
- // If the key is 'layout', we need to call layoutDidChange() immediately
106
- // so that if the frame has changed any cached values (for both this view
107
- // and any child views) can be appropriately invalidated.
103
+ if (frame.transformOriginX != null) {
104
+ ret.transformOriginX = frame.transformOriginX;
105
+ }
108
106
 
109
- // To allow layout to be a computed property, we check if any property has
110
- // changed and if layout is dependent on the property.
111
- // If it is we call layoutDidChange.
112
- var layoutChange = false;
113
- if (typeof this.layout === "function" && this._kvo_dependents) {
114
- var dependents = this._kvo_dependents[key];
115
- if (dependents && dependents.indexOf('layout') !== -1) { layoutChange = true; }
107
+ if (frame.transformOriginY != null) {
108
+ ret.transformOriginY = frame.transformOriginY;
109
+ }
116
110
  }
117
111
 
118
- if (key === 'layout' || layoutChange) {
119
- this.layoutDidChange();
120
- }
112
+ return ret;
113
+ }.property('frame').cacheable(),
121
114
 
122
- // Resume notification as usual.
123
- sc_super();
124
- },
125
115
 
126
- /** @private Apply the adjustment to a clone of the layout (cloned unless newLayout is passed in) */
127
- _sc_applyAdjustment: function (key, newValue, layout, newLayout) {
128
- var animateLayout = this._animateLayout;
116
+ /**
117
+ This this property to YES whenever the view needs to layout its child
118
+ views. Normally this property is set automatically whenever the layout
119
+ property for a child view changes.
129
120
 
130
- // If a call to animate occurs in the same run loop, the animation layout
131
- // would still be applied in the next run loop, potentially overriding this
132
- // adjustment. So we need to cancel the animation layout.
133
- if (animateLayout) {
134
- if (newValue === null) {
135
- delete animateLayout[key];
136
- } else {
137
- animateLayout[key] = newValue;
138
- }
121
+ @type Boolean
122
+ */
123
+ childViewsNeedLayout: NO,
139
124
 
140
- if (this._pendingAnimations && this._pendingAnimations[key]) {
141
- // Adjusting a value that was about to be animated cancels the animation.
142
- delete this._pendingAnimations[key];
143
- }
125
+ /**
126
+ The child view layout plugin to use when laying out child views.
144
127
 
145
- }
128
+ You can set this property to a child layout plugin object to
129
+ automatically set and adjust the layouts of this view's child views
130
+ according to some specific layout style. For instance, SproutCore includes
131
+ two such plugins, SC.View.VERTICAL_STACK and SC.View.HORIZONTAL_STACK.
146
132
 
147
- // Ignore undefined values or values equal to the current value.
148
- /*jshint eqeqeq:false*/
149
- if (newValue !== undefined && layout[key] != newValue) { // coerced so '100' == 100
150
- // Only clone the layout if it is not given.
151
- if (!newLayout) newLayout = SC.clone(this.get('layout'));
133
+ SC.View.VERTICAL_STACK will arrange child views in order in a vertical
134
+ stack, which only requires that the height of each child view be specified.
135
+ Likewise, SC.View.HORIZONTAL_STACK does the same in the horizontal
136
+ direction, which requires that the width of each child view be specified.
152
137
 
153
- if (newValue === null) {
154
- delete newLayout[key];
155
- } else {
156
- newLayout[key] = newValue;
157
- }
158
- }
138
+ Where child layout plugins are extremely useful, besides simplifying
139
+ the amount of layout code you need to write, is that they can update the
140
+ layouts automatically as things change. For more details and examples,
141
+ please see the documentation for SC.View.VERTICAL_STACK and
142
+ SC.View.HORIZONTAL_STACK.
159
143
 
160
- return newLayout;
161
- },
144
+ To define your own child view layout plugin, simply create an object that
145
+ conforms to the SC.ChildViewLayoutProtocol protocol.
146
+
147
+ **Note** This should only be set once and is not bindable.
148
+
149
+ @type Object
150
+ @default null
151
+ */
152
+ childViewLayout: null,
162
153
 
163
154
  /**
164
- This convenience method will take the current layout, apply any changes
165
- you pass and set it again. It is more convenient than having to do this
166
- yourself sometimes.
155
+ The options for the given child view layout plugin.
167
156
 
168
- You can pass just a key/value pair or a hash with several pairs. You can
169
- also pass a null value to delete a property.
157
+ These options are specific to the current child layout plugin being used and
158
+ are used to modify the applied layouts. For example, SC.View.VERTICAL_STACK
159
+ accepts options like:
170
160
 
171
- This method will avoid actually setting the layout if the value you pass
172
- does not edit the layout.
161
+ childViewLayoutOptions: {
162
+ paddingAfter: 20,
163
+ paddingBefore: 20,
164
+ spacing: 10
165
+ }
173
166
 
174
- @param {String|Hash} key
175
- @param {Object} value
176
- @returns {SC.View} receiver
167
+ To determine what options may be used for a given plugin and to see what the
168
+ default options are, please refer to the documentation for the child layout
169
+ plugin being used.
170
+
171
+ @type Object
172
+ @default null
177
173
  */
178
- adjust: function (key, value) {
179
- if (key === undefined) { return this; } // FAST PATH! Nothing to do.
174
+ childViewLayoutOptions: null,
180
175
 
176
+ /** @private The explicit layout of the view, computed from the layout using the explicit position. */
177
+ explicitLayout: function () {
181
178
  var layout = this.get('layout'),
182
- newLayout;
183
-
184
- // Normalize arguments.
185
- if (SC.typeOf(key) === SC.T_STRING) {
186
- newLayout = this._sc_applyAdjustment(key, value, layout);
187
- } else {
188
- for (var aKey in key) {
189
- if (!key.hasOwnProperty(aKey)) { continue; }
179
+ ret = null;
190
180
 
191
- newLayout = this._sc_applyAdjustment(aKey, key[aKey], layout, newLayout);
192
- }
181
+ if (layout) {
182
+ ret = this._sc_computeExplicitLayout(layout);
193
183
  }
194
184
 
195
- // now set adjusted layout
196
- if (newLayout) {
197
- var transitionAdjust = this.get('transitionAdjust');
185
+ return ret;
186
+ }.property('layout').cacheable(),
198
187
 
199
- if (this.get('viewState') & SC.CoreView.IS_SHOWN && transitionAdjust) {
200
- // Run the adjust transition.
201
- this._transitionAdjust(newLayout);
202
- } else {
203
- this.set('layout', newLayout);
204
- }
205
- }
188
+ /**
189
+ Walks like a duck. Is `true` to indicate that a view has layout support.
190
+ */
191
+ hasLayout: true,
206
192
 
207
- return this;
208
- },
193
+ /**
194
+ Whether the view and its child views should be monitored for changes that
195
+ affect the current child view layout.
209
196
 
210
- /** @private Attempts to run a transition adjust, ensuring any showing transitions are stopped in place. */
211
- _transitionAdjust: function (layout) {
212
- var transitionAdjust = this.get('transitionAdjust'),
213
- options = this.get('transitionAdjustOptions') || {};
197
+ When `true` and using a childViewLayout plugin, the view and its child views
198
+ will be observed for any changes that would affect the layout of all the
199
+ child views. For example, if `isChildViewLayout` is true and using
200
+ SC.View.VERTICAL_STACK, if any child view's height or visibility changes
201
+ all of the child views will be re-adjusted.
214
202
 
215
- // Execute the adjusting transition.
216
- transitionAdjust.run(this, options, layout);
217
- },
203
+ If you only want to automatically layout the child views once, you can
204
+ set this to `false` to improve performance.
218
205
 
219
- /** @private */
220
- didTransitionAdjust: function () {},
206
+ @type Boolean
207
+ @default true
208
+ */
209
+ isChildViewLayoutLive: true,
221
210
 
222
211
  /**
223
- Set the layout to a hash of layout properties to describe in detail how your view
224
- should be positioned on screen. Like most application development environments,
225
- your views are laid out absolutely, relative to their parent view.
212
+ Returns whether the height is 'fixed' or not. A fixed height is defined on the layout
213
+ as an integer number of pixels. Fixed widths are therefore unaffected by changes
214
+ to their parent view's height.
226
215
 
227
- You can define your layout using combinations of the following positional properties:
216
+ @field
217
+ @returns {Boolean} YES if fixed, NO otherwise
218
+ @test in layout
219
+ */
220
+ isFixedHeight: function() {
221
+ var layout = this.get('layout');
228
222
 
229
- - left
230
- - top
231
- - right
232
- - bottom
233
- - height
234
- - width
235
- - centerX: offset from center, horizontally
236
- - centerY: offset from center, vertically
237
- - minWidth
238
- - minHeight
239
- - maxWidth
240
- - maxHeight
241
- - scale: once positioned, scales the view in place.
223
+ // Height is fixed if it has a height and it isn't SC.LAYOUT_AUTO or a percent.
224
+ return (layout.height !== undefined) &&
225
+ !SC.isPercentage(layout.height) &&
226
+ (layout.height !== SC.LAYOUT_AUTO);
227
+ }.property('layout').cacheable(),
228
+
229
+ /**
230
+ Returns whether the layout is 'fixed' or not. A fixed layout means a
231
+ fixed left & top position and fixed width & height. Fixed layouts are
232
+ therefore unaffected by changes to their parent view's layout.
233
+
234
+ @returns {Boolean} YES if fixed, NO otherwise
235
+ @test in layoutStyle
236
+ */
237
+ isFixedLayout: function () {
238
+ return this.get('isFixedPosition') && this.get('isFixedSize');
239
+ }.property('isFixedPosition', 'isFixedSize').cacheable(),
240
+
241
+ /**
242
+ Returns whether the position is 'fixed' or not. A fixed position means a
243
+ fixed left & top position within its parent's frame. Fixed positions are
244
+ therefore unaffected by changes to their parent view's size.
245
+
246
+ @field
247
+ @returns {Boolean} YES if fixed, NO otherwise
248
+ @test in layoutStyle
249
+ */
250
+ isFixedPosition: function () {
251
+ var explicitLayout = this.get('explicitLayout'),
252
+ left = explicitLayout.left,
253
+ top = explicitLayout.top,
254
+ hasFixedLeft,
255
+ hasFixedTop;
256
+
257
+ // Position is fixed if it has left + top, but not as percentages and not as SC.LAYOUT_AUTO.
258
+ hasFixedLeft = left !== undefined && !SC.isPercentage(left) && left !== SC.LAYOUT_AUTO;
259
+ hasFixedTop = top !== undefined && !SC.isPercentage(top) && top !== SC.LAYOUT_AUTO;
260
+
261
+ return hasFixedLeft && hasFixedTop;
262
+ }.property('explicitLayout').cacheable(),
263
+
264
+ /**
265
+ Returns whether the size is 'fixed' or not. A fixed size means a fixed
266
+ width and height. Fixed sizes are therefore unaffected by changes to their
267
+ parent view's size.
268
+
269
+ @field
270
+ @returns {Boolean} YES if fixed, NO otherwise
271
+ @test in layout
272
+ */
273
+ isFixedSize: function () {
274
+ return this.get('isFixedHeight') && this.get('isFixedWidth');
275
+ }.property('isFixedWidth', 'isFixedHeight').cacheable(),
276
+
277
+ /**
278
+ Returns whether the width is 'fixed' or not. A fixed width is defined on the layout
279
+ as an integer number of pixels. Fixed widths are therefore unaffected by changes
280
+ to their parent view's width.
281
+
282
+ @field
283
+ @returns {Boolean} YES if fixed, NO otherwise
284
+ @test in layout
285
+ */
286
+ isFixedWidth: function() {
287
+ var layout = this.get('layout');
288
+
289
+ // Width is fixed if it has a width and it isn't SC.LAYOUT_AUTO or a percent.
290
+ return (layout.width !== undefined) &&
291
+ !SC.isPercentage(layout.width) &&
292
+ (layout.width !== SC.LAYOUT_AUTO);
293
+ }.property('layout').cacheable(),
294
+
295
+ /**
296
+ Set the layout to a hash of layout properties to describe in detail how your view
297
+ should be positioned on screen. Like most application development environments,
298
+ your views are laid out absolutely, relative to their parent view.
299
+
300
+ You can define your layout using combinations of the following positional properties:
301
+
302
+ - left
303
+ - top
304
+ - right
305
+ - bottom
306
+ - height
307
+ - width
308
+ - centerX: offset from center, horizontally
309
+ - centerY: offset from center, vertically
310
+ - minWidth
311
+ - minHeight
312
+ - maxWidth
313
+ - maxHeight
314
+ - scale: once positioned, scales the view in place.
242
315
  - transformOriginX, transformOriginY: defines the point (as a decimal percentage) around which
243
316
  your view will scale. (Also impacts rotation; see below.)
244
317
 
@@ -286,269 +359,395 @@ SC.View.reopen(
286
359
  */
287
360
  layout: { top: 0, left: 0, bottom: 0, right: 0 },
288
361
 
289
- /** @private The explicit layout of the view, computed from the layout using the explicit position. */
290
- explicitLayout: function () {
291
- var layout = this.get('layout'),
292
- explicitPosition = this.get('explicitPosition'),
293
- ret;
362
+ /**
363
+ The view responsible for laying out this view. The default version
364
+ returns the current parent view.
365
+ */
366
+ layoutView: function () {
367
+ return this.get('parentView');
368
+ }.property('parentView').cacheable(),
294
369
 
295
- if (layout) {
296
- ret = SC.clone(layout);
370
+ /**
371
+ The transition plugin to use when this view is moved or resized by adjusting
372
+ its layout.
297
373
 
298
- // Only use the explicit position values.
299
- // delete ret.left;
300
- delete ret.right;
301
- // delete ret.top;
302
- delete ret.bottom;
374
+ SC.CoreView uses a pluggable transition architecture where the transition
375
+ setup, execution and cleanup can be handled by a plugin. This allows you
376
+ to create complex transition animations and share them across all your views
377
+ with only a single line of code.
303
378
 
304
- /*jshint eqnull:true */
305
- if (explicitPosition.left != null) { ret.left = explicitPosition.left; }
306
- if (explicitPosition.right != null) { ret.right = explicitPosition.right; }
307
- if (explicitPosition.top != null) { ret.top = explicitPosition.top; }
308
- if (explicitPosition.bottom != null) { ret.bottom = explicitPosition.bottom; }
309
- if (explicitPosition.centerX != null) { ret.centerX = explicitPosition.centerX; }
310
- if (explicitPosition.centerY != null) { ret.centerY = explicitPosition.centerY; }
311
-
312
- // BORDERS
313
- // Apply border first, so that the more specific borderX values will override it next.
314
- var border = layout.border;
315
- if (border != null) {
316
- ret.borderTop = border;
317
- ret.borderRight = border;
318
- ret.borderBottom = border;
319
- ret.borderLeft = border;
320
- delete ret.border;
321
- }
379
+ There are a number of pre-built transition adjust plugins available in
380
+ the SproutCore foundation framework:
322
381
 
323
- if (layout.borderTop != null) {
324
- ret.borderTop = layout.borderTop;
325
- }
326
- if (layout.borderRight != null) {
327
- ret.borderRight = layout.borderRight;
382
+ SC.View.SMOOTH_ADJUST
383
+ SC.View.BOUNCE_ADJUST
384
+ SC.View.SPRING_ADJUST
385
+
386
+ To create a custom transition plugin simply create a regular JavaScript
387
+ object that conforms to the SC.ViewTransitionProtocol protocol.
388
+
389
+ NOTE: When creating custom transition adjust plugins, be aware that SC.View
390
+ will not call the `setup` method of the plugin, only the `run` method.
391
+
392
+ @type Object (SC.ViewTransitionProtocol)
393
+ @default null
394
+ @since Version 1.10
395
+ */
396
+ transitionAdjust: null,
397
+
398
+ /**
399
+ The options for the given `transitionAdjust` plugin.
400
+
401
+ These options are specific to the current transition plugin used and are
402
+ used to modify the transition animation. To determine what options
403
+ may be used for a given plugin and to see what the default options are,
404
+ see the documentation for the transition plugin being used.
405
+
406
+ Most transitions will accept a duration and timing option, but may
407
+ also use other options. For example, SC.View.BOUNCE_ADJUST accepts options
408
+ like:
409
+
410
+ transitionAdjustOptions: {
411
+ bounciness: 0.5, // how much the adjustment should bounce back each time
412
+ bounces: 4, // the number of bounces
413
+ duration: 0.25,
414
+ delay: 1
415
+ }
416
+
417
+ @type Object
418
+ @default null
419
+ @since Version 1.10
420
+ */
421
+ transitionAdjustOptions: null,
422
+
423
+ /**
424
+ Activates use of brower's static layout. To activate, set this property to YES.
425
+
426
+ @type Boolean
427
+ @default NO
428
+ */
429
+ useStaticLayout: NO,
430
+
431
+ // ------------------------------------------------------------------------
432
+ // Methods
433
+ //
434
+
435
+ /** @private */
436
+ _sc_adjustForBorder: function (frame, layout) {
437
+ /*jshint eqnull:true */
438
+ var defaultValue = layout.border == null ? 0 : layout.border,
439
+ borderTop = this._sc_explicitValueFor(layout.borderTop, defaultValue),
440
+ borderLeft = this._sc_explicitValueFor(layout.borderLeft, defaultValue),
441
+ borderBottom = this._sc_explicitValueFor(layout.borderBottom, defaultValue),
442
+ borderRight = this._sc_explicitValueFor(layout.borderRight, defaultValue);
443
+
444
+ frame.x += borderLeft; // The border on the left pushes the frame to the right
445
+ frame.y += borderTop; // The border on the top pushes the frame down
446
+ frame.width -= (borderLeft + borderRight); // Border takes up space
447
+ frame.height -= (borderTop + borderBottom); // Border takes up space
448
+
449
+ return frame;
450
+ },
451
+
452
+ /** @private */
453
+ _sc_adjustForScale: function (frame, layout) {
454
+
455
+ // Scale not supported on this platform, ignore the layout values.
456
+ if (!SC.platform.supportsCSSTransforms) {
457
+ frame.scale = 1;
458
+ frame.transformOriginX = frame.transformOriginY = 0.5;
459
+
460
+ // Use scale.
461
+ } else {
462
+
463
+ // Get the scale and transform origins, if not provided. (Note inlining of SC.none for performance)
464
+ /*jshint eqnull:true*/
465
+ var scale = layout.scale,
466
+ oX = layout.transformOriginX,
467
+ oY = layout.transformOriginY;
468
+
469
+ // If the scale is set and isn't 1, do some calculations.
470
+ if (scale != null && scale !== 1) {
471
+ // Scale the rect.
472
+ frame = SC.scaleRect(frame, scale, oX, oY);
473
+
474
+ // Add the scale and original unscaled height and width.
475
+ frame.scale = scale;
328
476
  }
329
- if (layout.borderBottom != null) {
330
- ret.borderBottom = layout.borderBottom;
477
+
478
+ // If the origin is set and isn't 0.5, include it.
479
+ if (oX != null && oX !== 0.5) {
480
+ frame.transformOriginX = oX;
331
481
  }
332
- if (layout.borderLeft != null) {
333
- ret.borderLeft = layout.borderLeft;
482
+
483
+ // If the origin is set and isn't 0.5, include it.
484
+ if (oY != null && oY !== 0.5) {
485
+ frame.transformOriginY = oY;
334
486
  }
335
487
  }
336
488
 
337
- return ret;
338
- }.property('explicitPosition').cacheable(),
489
+ // Make sure width/height are never < 0.
490
+ if (frame.height < 0) frame.height = 0;
491
+ if (frame.width < 0) frame.width = 0;
339
492
 
340
- /** @private The explicit position of the view, computed from the layout property, which may be written in shortform. */
341
- explicitPosition: function () {
342
- var layout = this.get('layout'),
343
- ret;
493
+ return frame;
494
+ },
344
495
 
345
- if (layout) {
346
- ret = {};
347
-
348
- /* jshint eqnull:true */
349
- var hasBottom = (layout.bottom != null);
350
- var hasRight = (layout.right != null);
351
- var hasLeft = (layout.left != null);
352
- var hasTop = (layout.top != null);
353
- var hasCenterX = (layout.centerX != null);
354
- var hasCenterY = (layout.centerY != null);
355
- var hasHeight = (layout.height != null); // || (layout.maxHeight != null)
356
- var hasWidth = (layout.width != null); // || (layout.maxWidth != null)
357
-
358
- if (hasLeft) {
359
- ret.left = layout.left;
360
- } else if (!hasCenterX && !(hasWidth && hasRight)) {
361
- ret.left = 0;
496
+ /** @private Apply the adjustment to a clone of the layout (cloned unless newLayout is passed in) */
497
+ _sc_applyAdjustment: function (key, newValue, layout, newLayout) {
498
+ var animateLayout = this._animateLayout;
499
+
500
+ // If a call to animate occurs in the same run loop, the animation layout
501
+ // would still be applied in the next run loop, potentially overriding this
502
+ // adjustment. So we need to cancel the animation layout.
503
+ if (animateLayout) {
504
+ if (newValue === null) {
505
+ delete animateLayout[key];
506
+ } else {
507
+ animateLayout[key] = newValue;
362
508
  }
363
509
 
364
- if (hasRight && !(hasLeft && hasWidth)) {
365
- ret.right = layout.right;
366
- } else if (!hasCenterX && !hasWidth) {
367
- ret.right = 0;
510
+ if (this._pendingAnimations && this._pendingAnimations[key]) {
511
+ // Adjusting a value that was about to be animated cancels the animation.
512
+ delete this._pendingAnimations[key];
368
513
  }
369
514
 
370
- //@if(debug)
371
- // Debug-only warning when layout isn't valid.
372
- // UNUSED: This is too noisy for certain views that adjust their own layouts based on top of the default layout.
373
- // if (hasRight && hasLeft && hasWidth) {
374
- // SC.warn("Developer Warning: When setting `width` in the layout, you must only set `left` or `right`, but not both: %@".fmt(this));
375
- // }
376
- //@endif
515
+ }
377
516
 
378
- if (hasTop) {
379
- ret.top = layout.top;
380
- } else if (!hasCenterY && !(hasHeight && hasBottom)) {
381
- ret.top = 0;
382
- }
517
+ // Ignore undefined values or values equal to the current value.
518
+ /*jshint eqeqeq:false*/
519
+ if (newValue !== undefined && layout[key] != newValue) { // coerced so '100' == 100
520
+ // Only clone the layout if it is not given.
521
+ if (!newLayout) newLayout = SC.clone(this.get('layout'));
383
522
 
384
- if (hasBottom && !(hasTop && hasHeight)) {
385
- ret.bottom = layout.bottom;
386
- } else if (!hasCenterY && !hasHeight) {
387
- ret.bottom = 0;
523
+ if (newValue === null) {
524
+ delete newLayout[key];
525
+ } else {
526
+ newLayout[key] = newValue;
388
527
  }
528
+ }
389
529
 
390
- //@if(debug)
391
- // Debug-only warning when layout isn't valid.
392
- // UNUSED: This is too noisy for certain views that adjust their own layouts based on top of the default layout.
393
- // if (hasBottom && hasTop && hasHeight) {
394
- // SC.warn("Developer Warning: When setting `height` in the layout, you must only set `top` or `bottom`, but not both: %@".fmt(this));
395
- // }
396
- //@endif
530
+ return newLayout;
531
+ },
397
532
 
398
- // CENTERS
399
- if (hasCenterX) {
400
- ret.centerX = layout.centerX;
533
+ /** @private */
534
+ _sc_checkForResize: function (previousLayout, currentLayout) {
535
+ // Did our layout change in a way that could cause us to have changed size? If
536
+ // not, then there's no need to invalidate the frames of our child views.
537
+ var didResizeHeight = true,
538
+ didResizeWidth = true,
539
+ didResize = true;
401
540
 
402
- //@if(debug)
403
- // Debug-only warning when layout isn't valid.
404
- if (!hasWidth) {
405
- SC.warn("Developer Warning: When setting `centerX` in the layout, you must also set `width`: %@".fmt(this));
406
- }
407
- //@endif
408
- }
541
+ // We test the new layout to see if we believe it will affect the view's frame.
542
+ // Since all the child view frames may depend on the parent's frame, it's
543
+ // best only to notify a frame change when it actually happens.
544
+ /*jshint eqnull:true*/
409
545
 
410
- if (hasCenterY) {
411
- ret.centerY = layout.centerY;
546
+ // Simple test: Width is defined and hasn't changed.
547
+ // Complex test: No defined width, left or right haven't changed.
548
+ if (previousLayout != null &&
549
+ ((previousLayout.width != null &&
550
+ previousLayout.width === currentLayout.width) ||
551
+ (previousLayout.width == null &&
552
+ currentLayout.width == null &&
553
+ previousLayout.left === currentLayout.left &&
554
+ previousLayout.right === currentLayout.right))) {
555
+ didResizeWidth = false;
556
+ }
412
557
 
413
- //@if(debug)
414
- // Debug-only warning when layout isn't valid.
415
- if (!hasHeight) {
416
- SC.warn("Developer Warning: When setting `centerY` in the layout, you must also set `height`: %@".fmt(this));
417
- }
418
- //@endif
419
- }
558
+ // Simple test: Height is defined and hasn't changed.
559
+ // Complex test: No defined height, top or bottom haven't changed.
560
+ if (!didResizeWidth &&
561
+ ((previousLayout.height != null &&
562
+ previousLayout.height === currentLayout.height) ||
563
+ (previousLayout.height == null &&
564
+ currentLayout.height == null &&
565
+ previousLayout.top === currentLayout.top &&
566
+ previousLayout.bottom === currentLayout.bottom))) {
567
+ didResizeHeight = false;
420
568
  }
421
569
 
422
- return ret;
423
- }.property('layout').cacheable(),
570
+ // Border test: Even if the width & height haven't changed, a change in a border would be a resize.
571
+ if (!didResizeHeight && !didResizeWidth) {
572
+ didResize = !(previousLayout.border === currentLayout.border &&
573
+ previousLayout.borderTop === currentLayout.borderTop &&
574
+ previousLayout.borderLeft === currentLayout.borderLeft &&
575
+ previousLayout.borderBottom === currentLayout.borderBottom &&
576
+ previousLayout.borderRight === currentLayout.borderRight);
577
+ }
424
578
 
425
- /**
426
- Returns whether the layout is 'fixed' or not. A fixed layout means a
427
- fixed left & top position and fixed width & height. Fixed layouts are
428
- therefore unaffected by changes to their parent view's layout.
579
+ return didResize;
580
+ },
429
581
 
430
- @returns {Boolean} YES if fixed, NO otherwise
431
- @test in layoutStyle
432
- */
433
- isFixedLayout: function () {
434
- return this.get('isFixedPosition') && this.get('isFixedSize');
435
- }.property('isFixedPosition', 'isFixedSize').cacheable(),
582
+ /** @private Called when the child view layout plugin or options change. */
583
+ _cvl_childViewLayoutDidChange: function () {
584
+ this.set('childViewsNeedLayout', true);
436
585
 
437
- /**
438
- Returns whether the position is 'fixed' or not. A fixed position means a
439
- fixed left & top position within its parent's frame. Fixed positions are
440
- therefore unaffected by changes to their parent view's size.
586
+ // Filter the input channel.
587
+ this.invokeOnce(this.layoutChildViewsIfNeeded);
588
+ },
441
589
 
442
- @field
443
- @returns {Boolean} YES if fixed, NO otherwise
444
- @test in layoutStyle
445
- */
446
- isFixedPosition: function () {
447
- var explicitPosition = this.get('explicitPosition'),
448
- left = explicitPosition.left,
449
- top = explicitPosition.top,
450
- hasFixedLeft,
451
- hasFixedTop;
590
+ /** @private Called when the child views change. */
591
+ _cvl_childViewsDidChange: function () {
592
+ this._cvl_teardownChildViewsLiveLayout();
593
+ this._cvl_setupChildViewsLiveLayout();
452
594
 
453
- // Position is fixed if it has left + top, but not as percentages and not as SC.LAYOUT_AUTO.
454
- hasFixedLeft = left !== undefined && !SC.isPercentage(left) && left !== SC.LAYOUT_AUTO;
455
- hasFixedTop = top !== undefined && !SC.isPercentage(top) && top !== SC.LAYOUT_AUTO;
595
+ this.set('childViewsNeedLayout', true);
456
596
 
457
- return hasFixedLeft && hasFixedTop;
458
- }.property('explicitPosition').cacheable(),
597
+ // Filter the input channel.
598
+ this.invokeOnce(this.layoutChildViewsIfNeeded);
599
+ },
459
600
 
460
- /**
461
- Returns whether the size is 'fixed' or not. A fixed size means a fixed
462
- width and height. Fixed sizes are therefore unaffected by changes to their
463
- parent view's size.
601
+ /** @private Add observers to the child views for automatic child view layout. */
602
+ _cvl_setupChildViewsLiveLayout: function () {
603
+ var childViewLayout = this.childViewLayout,
604
+ childViews,
605
+ childLayoutProperties = childViewLayout.childLayoutProperties || [];
464
606
 
465
- @field
466
- @returns {Boolean} YES if fixed, NO otherwise
467
- @test in layout
468
- */
469
- isFixedSize: function () {
470
- return this.get('isFixedHeight') && this.get('isFixedWidth');
471
- }.property('isFixedWidth', 'isFixedHeight').cacheable(),
607
+ // Create a reference to the current child views so that we can clean them if they change.
608
+ childViews = this._cvl_childViews = this.get('childViews');
609
+ for (var i = 0, len = childLayoutProperties.length; i < len; i++) {
610
+ var observedProperty = childLayoutProperties[i];
472
611
 
473
- /**
474
- Returns whether the height is 'fixed' or not. A fixed height is defined on the layout
475
- as an integer number of pixels. Fixed widths are therefore unaffected by changes
476
- to their parent view's height.
612
+ for (var j = 0, jlen = childViews.get('length'); j < jlen; j++) {
613
+ var childView = childViews.objectAt(j);
614
+ if (!childView.get('useAbsoluteLayout') && !childView.get('useStaticLayout')) {
615
+ childView.addObserver(observedProperty, this, this._cvl_childViewLayoutDidChange);
616
+ }
617
+ }
618
+ }
619
+ },
477
620
 
478
- @field
479
- @returns {Boolean} YES if fixed, NO otherwise
480
- @test in layout
481
- */
482
- isFixedHeight: function() {
483
- var layout = this.get('layout');
621
+ /** @private Remove observers from the child views for automatic child view layout. */
622
+ _cvl_teardownChildViewsLiveLayout: function () {
623
+ var childViewLayout = this.childViewLayout,
624
+ childViews = this._cvl_childViews || [],
625
+ childLayoutProperties = childViewLayout.childLayoutProperties || [];
484
626
 
485
- // Height is fixed if it has a height and it isn't SC.LAYOUT_AUTO or a percent.
486
- return (layout.height !== undefined) &&
487
- !SC.isPercentage(layout.height) &&
488
- (layout.height !== SC.LAYOUT_AUTO);
489
- }.property('layout').cacheable(),
627
+ for (var i = 0, len = childLayoutProperties.length; i < len; i++) {
628
+ var observedProperty = childLayoutProperties[i];
490
629
 
491
- /**
492
- Returns whether the width is 'fixed' or not. A fixed width is defined on the layout
493
- as an integer number of pixels. Fixed widths are therefore unaffected by changes
494
- to their parent view's width.
630
+ for (var j = 0, jlen = childViews.get('length'); j < jlen; j++) {
631
+ var childView = childViews.objectAt(j);
632
+ if (!childView.get('useAbsoluteLayout') && !childView.get('useStaticLayout')) {
633
+ childView.removeObserver(observedProperty, this, this._cvl_childViewLayoutDidChange);
634
+ }
635
+ }
636
+ }
637
+ },
495
638
 
496
- @field
497
- @returns {Boolean} YES if fixed, NO otherwise
498
- @test in layout
499
- */
500
- isFixedWidth: function() {
501
- var layout = this.get('layout');
639
+ /** @private Computes the explicit layout. */
640
+ _sc_computeExplicitLayout: function (layout) {
641
+ var ret = SC.copy(layout);
502
642
 
503
- // Width is fixed if it has a width and it isn't SC.LAYOUT_AUTO or a percent.
504
- return (layout.width !== undefined) &&
505
- !SC.isPercentage(layout.width) &&
506
- (layout.width !== SC.LAYOUT_AUTO);
507
- }.property('layout').cacheable(),
643
+ /* jshint eqnull:true */
644
+ var hasBottom = (layout.bottom != null);
645
+ var hasRight = (layout.right != null);
646
+ var hasLeft = (layout.left != null);
647
+ var hasTop = (layout.top != null);
648
+ var hasCenterX = (layout.centerX != null);
649
+ var hasCenterY = (layout.centerY != null);
650
+ var hasHeight = (layout.height != null); // || (layout.maxHeight != null)
651
+ var hasWidth = (layout.width != null); // || (layout.maxWidth != null)
508
652
 
509
- /**
510
- Converts a frame from the receiver's offset to the target offset. Both
511
- the receiver and the target must belong to the same pane. If you pass
512
- null, the conversion will be to the pane level.
653
+ /*jshint eqnull:true */
654
+ // Left + Top take precedence (left & right & width becomes left & width).
655
+ delete ret.right; // Right will be set if needed below.
656
+ delete ret.bottom; // Bottom will be set if needed below.
513
657
 
514
- Note that the context of a view's frame is the view's parent frame. In
515
- other words, if you want to convert the frame of your view to the global
516
- frame, then you should do:
658
+ if (hasLeft) {
659
+ ret.left = layout.left;
660
+ } else if (!hasCenterX && !(hasWidth && hasRight)) {
661
+ ret.left = 0;
662
+ }
517
663
 
518
- var pv = this.get('parentView'), frame = this.get('frame');
519
- var newFrame = pv ? pv.convertFrameToView(frame, null) : frame;
664
+ if (hasRight && !(hasLeft && hasWidth)) {
665
+ ret.right = layout.right;
666
+ } else if (!hasCenterX && !hasWidth) {
667
+ ret.right = 0;
668
+ }
520
669
 
521
- @param {Rect} frame the source frame
522
- @param {SC.View} targetView the target view to convert to
523
- @returns {Rect} converted frame
524
- @test in convertFrames
525
- */
526
- convertFrameToView: function (frame, targetView) {
527
- return this._convertFrameFromViewHelper(frame, this, targetView);
528
- },
670
+ //@if(debug)
671
+ // Debug-only warning when layout isn't valid.
672
+ // UNUSED: This is too noisy for certain views that adjust their own layouts based on top of the default layout.
673
+ // if (hasRight && hasLeft && hasWidth) {
674
+ // SC.warn("Developer Warning: When setting `width` in the layout, you must only set `left` or `right`, but not both: %@".fmt(this));
675
+ // }
676
+ //@endif
529
677
 
530
- /**
531
- Converts a frame offset in the coordinates of another view system to the
532
- receiver's view.
678
+ if (hasTop) {
679
+ ret.top = layout.top;
680
+ } else if (!hasCenterY && !(hasHeight && hasBottom)) {
681
+ ret.top = 0;
682
+ }
533
683
 
534
- Note that the convext of a view's frame is relative to the view's
535
- parentFrame. For example, if you want to convert the frame of view that
536
- belongs to another view to the receiver's frame you would do:
684
+ if (hasBottom && !(hasTop && hasHeight)) {
685
+ ret.bottom = layout.bottom;
686
+ } else if (!hasCenterY && !hasHeight) {
687
+ ret.bottom = 0;
688
+ }
537
689
 
538
- var frame = view.get('frame');
539
- var newFrame = this.convertFrameFromView(frame, view.get('parentView'));
690
+ //@if(debug)
691
+ // Debug-only warning when layout isn't valid.
692
+ // UNUSED: This is too noisy for certain views that adjust their own layouts based on top of the default layout.
693
+ // if (hasBottom && hasTop && hasHeight) {
694
+ // SC.warn("Developer Warning: When setting `height` in the layout, you must only set `top` or `bottom`, but not both: %@".fmt(this));
695
+ // }
696
+ //@endif
697
+
698
+ // CENTERS
699
+ if (hasCenterX) {
700
+ ret.centerX = layout.centerX;
701
+
702
+ //@if(debug)
703
+ // Debug-only warning when layout isn't valid.
704
+ if (hasCenterX && !hasWidth) {
705
+ SC.warn("Developer Warning: When setting `centerX` in the layout, you must also define the `width`: %@".fmt(this));
706
+ }
707
+ //@endif
708
+ }
709
+
710
+ if (hasCenterY) {
711
+ ret.centerY = layout.centerY;
712
+
713
+ //@if(debug)
714
+ // Debug-only warning when layout isn't valid.
715
+ if (hasCenterY && !hasHeight) {
716
+ SC.warn("Developer Warning: When setting `centerY` in the layout, you must also define the `height`: %@".fmt(this));
717
+ }
718
+ //@endif
719
+ }
720
+
721
+ // BORDERS
722
+ // Apply border first, so that the more specific borderX values will override it next.
723
+ var border = layout.border;
724
+ if (border != null) {
725
+ ret.borderTop = border;
726
+ ret.borderRight = border;
727
+ ret.borderBottom = border;
728
+ ret.borderLeft = border;
729
+ delete ret.border;
730
+ }
731
+
732
+ // Override generic border with more specific borderX.
733
+ if (layout.borderTop != null) {
734
+ ret.borderTop = layout.borderTop;
735
+ }
736
+ if (layout.borderRight != null) {
737
+ ret.borderRight = layout.borderRight;
738
+ }
739
+ if (layout.borderBottom != null) {
740
+ ret.borderBottom = layout.borderBottom;
741
+ }
742
+ if (layout.borderLeft != null) {
743
+ ret.borderLeft = layout.borderLeft;
744
+ }
540
745
 
541
- @param {Rect} frame the source frame
542
- @param {SC.View} targetView the target view to convert to
543
- @returns {Rect} converted frame
544
- @test in converFrames
545
- */
546
- convertFrameFromView: function (frame, targetView) {
547
- return this._convertFrameFromViewHelper(frame, targetView, this);
746
+ return ret;
548
747
  },
549
748
 
550
749
  /** @private */
551
- _convertFrameFromViewHelper: function (frame, fromView, targetView) {
750
+ _sc_convertFrameFromViewHelper: function (frame, fromView, targetView) {
552
751
  var myX = frame.x, myY = frame.y, myWidth = frame.width, myHeight = frame.height, view, f;
553
752
 
554
753
  // first, walk up from the view of the frame, up to the top level
@@ -605,324 +804,150 @@ SC.View.reopen(
605
804
  return { x: myX, y: myY, width: myWidth, height: myHeight };
606
805
  },
607
806
 
608
- /**
609
- Attempt to scroll the view to visible. This will walk up the parent
610
- view hierarchy looking looking for a scrollable view. It will then
611
- call scrollToVisible() on it.
612
-
613
- Returns YES if an actual scroll took place, no otherwise.
614
-
615
- @returns {Boolean}
616
- */
617
- scrollToVisible: function () {
618
- var pv = this.get('parentView');
619
- while (pv && !pv.get('isScrollable')) { pv = pv.get('parentView'); }
620
-
621
- // found view, first make it scroll itself visible then scroll this.
622
- if (pv) {
623
- pv.scrollToVisible();
624
- return pv.scrollToVisible(this);
625
- } else {
626
- return NO;
627
- }
628
- },
629
-
630
807
  /** @private */
631
808
  _sc_explicitValueFor: function (givenValue, impliedValue) {
632
809
  return givenValue === undefined ? impliedValue : givenValue;
633
810
  },
634
811
 
635
- /** @private */
636
- _adjustForBorder: function (frame, layout) {
637
- /*jshint eqnull:true */
638
- var defaultValue = layout.border == null ? 0 : layout.border,
639
- borderTop = this._sc_explicitValueFor(layout.borderTop, defaultValue),
640
- borderLeft = this._sc_explicitValueFor(layout.borderLeft, defaultValue),
641
- borderBottom = this._sc_explicitValueFor(layout.borderBottom, defaultValue),
642
- borderRight = this._sc_explicitValueFor(layout.borderRight, defaultValue);
643
-
644
- frame.x += borderLeft; // The border on the left pushes the frame to the right
645
- frame.y += borderTop; // The border on the top pushes the frame down
646
- frame.width -= (borderLeft + borderRight); // Border takes up space
647
- frame.height -= (borderTop + borderBottom); // Border takes up space
648
-
649
- return frame;
650
- },
651
-
652
- /** @private */
653
- _adjustForScale: function (frame, layout) {
654
-
655
- // Scale not supported on this platform, ignore the layout values.
656
- if (!SC.platform.supportsCSSTransforms) {
657
- frame.scale = 1;
658
- frame.transformOriginX = frame.transformOriginY = 0.5;
659
-
660
- // Use scale.
661
- } else {
662
-
663
- // Get the scale and transform origins, if not provided. (Note inlining of SC.none for performance)
664
- /*jshint eqnull:true*/
665
- var scale = layout.scale,
666
- oX = layout.transformOriginX,
667
- oY = layout.transformOriginY;
668
-
669
- // If the scale is set and isn't 1, do some calculations.
670
- if (scale != null && scale !== 1) {
671
- // Scale the rect.
672
- frame = SC.scaleRect(frame, scale, oX, oY);
673
-
674
- // Add the scale and original unscaled height and width.
675
- frame.scale = scale;
676
- }
677
-
678
- // If the origin is set and isn't 0.5, include it.
679
- if (oX != null && oX !== 0.5) {
680
- frame.transformOriginX = oX;
681
- }
682
-
683
- // If the origin is set and isn't 0.5, include it.
684
- if (oY != null && oY !== 0.5) {
685
- frame.transformOriginY = oY;
686
- }
687
- }
688
-
689
- // Make sure width/height are never < 0.
690
- if (frame.height < 0) frame.height = 0;
691
- if (frame.width < 0) frame.width = 0;
812
+ /** @private Attempts to run a transition adjust, ensuring any showing transitions are stopped in place. */
813
+ _sc_transitionAdjust: function (layout) {
814
+ var transitionAdjust = this.get('transitionAdjust'),
815
+ options = this.get('transitionAdjustOptions') || {};
692
816
 
693
- return frame;
817
+ // Execute the adjusting transition.
818
+ transitionAdjust.run(this, options, layout);
694
819
  },
695
820
 
696
- /** */
697
- computeParentDimensions: function (frame) {
698
- var parentView = this.get('parentView'),
699
- parentFrame = (parentView) ? parentView.get('frame') : null,
700
- ret;
821
+ /** @private
822
+ Invoked by other views to notify this view that its frame has changed.
701
823
 
702
- if (parentFrame) {
703
- ret = {
704
- width: parentFrame.width,
705
- height: parentFrame.height
706
- };
707
- } else if (frame) {
708
- ret = {
709
- width: (frame.left || 0) + (frame.width || 0) + (frame.right || 0),
710
- height: (frame.top || 0) + (frame.height || 0) + (frame.bottom || 0)
711
- };
712
- } else {
713
- ret = {
714
- width: 0,
715
- height: 0
716
- };
717
- }
824
+ This notifies the view that its frame property has changed,
825
+ then notifies its child views that their clipping frames may have changed.
826
+ */
827
+ _sc_viewFrameDidChange: function () {
828
+ this.notifyPropertyChange('frame');
718
829
 
719
- return ret;
830
+ // Notify the children that their clipping frame may have changed. Top-down, because a child's
831
+ // clippingFrame is dependent on its parent's frame.
832
+ this._callOnChildViews('_sc_clippingFrameDidChange');
720
833
  },
721
834
 
722
835
  /**
723
- The frame of the view including the borders and scale
724
- */
725
- borderFrame: function () {
726
- var frame = this.get('frame'),
727
- ret = null;
728
-
729
- if (frame) {
730
- var layout = this.get('layout'),
731
- defaultValue = layout.border == null ? 0 : layout.border,
732
- borderTop = this._sc_explicitValueFor(layout.borderTop, defaultValue),
733
- borderRight = this._sc_explicitValueFor(layout.borderRight, defaultValue),
734
- borderBottom = this._sc_explicitValueFor(layout.borderBottom, defaultValue),
735
- borderLeft = this._sc_explicitValueFor(layout.borderLeft, defaultValue);
736
-
737
- ret = {
738
- x: frame.x,
739
- y: frame.y,
740
- width: frame.width,
741
- height: frame.height
742
- };
743
-
744
- var scale = frame.scale;
745
- /*jshint eqnull:true*/
746
- if (scale != null) {
747
- var scaledBorderTop = borderTop * scale,
748
- scaledBorderRight = borderRight * scale,
749
- scaledBorderBottom = borderBottom * scale,
750
- scaledBorderLeft = borderLeft * scale;
751
-
752
- ret.scale = scale;
753
- ret.x -= scaledBorderLeft;
754
- ret.y -= scaledBorderTop;
755
- ret.width += scaledBorderLeft + scaledBorderRight;
756
- ret.height += scaledBorderTop + scaledBorderBottom;
757
- } else {
758
- ret.x -= borderLeft;
759
- ret.y -= borderTop;
760
- ret.width += borderLeft + borderRight;
761
- ret.height += borderTop + borderBottom;
762
- }
763
-
764
- if (frame.transformOriginX != null) {
765
- ret.transformOriginX = frame.transformOriginX;
766
- }
767
-
768
- if (frame.transformOriginY != null) {
769
- ret.transformOriginY = frame.transformOriginY;
770
- }
771
- }
772
-
773
- return ret;
774
- }.property('frame').cacheable(),
836
+ This convenience method will take the current layout, apply any changes
837
+ you pass and set it again. It is more convenient than having to do this
838
+ yourself sometimes.
775
839
 
776
- /**
777
- This method may be called on your view whenever the parent view resizes.
840
+ You can pass just a key/value pair or a hash with several pairs. You can
841
+ also pass a null value to delete a property.
778
842
 
779
- The default version of this method will reset the frame and then call
780
- viewDidResize() if its size may have changed. You will not usually override
781
- this method, but you may override the viewDidResize() method.
843
+ This method will avoid actually setting the layout if the value you pass
844
+ does not edit the layout.
782
845
 
783
- @param {Frame} parentFrame the parent view's current frame.
784
- @returns {void}
785
- @test in viewDidResize
846
+ @param {String|Hash} key
847
+ @param {Object} value
848
+ @returns {SC.View} receiver
786
849
  */
787
- parentViewDidResize: function (parentFrame) {
788
- // Determine if our position may have changed.
789
- var positionMayHaveChanged = !this.get('isFixedPosition');
850
+ adjust: function (key, value) {
851
+ if (key === undefined) { return this; } // FAST PATH! Nothing to do.
790
852
 
791
- // Figure out if our size may have changed.
792
- var isStatic = this.get('useStaticLayout'),
793
- // Figure out whether our height may have changed.
794
- parentHeight = parentFrame ? parentFrame.height : 0,
795
- parentHeightDidChange = parentHeight !== this._scv_parentHeight,
796
- isFixedHeight = this.get('isFixedHeight'),
797
- heightMayHaveChanged = isStatic || (parentHeightDidChange && !isFixedHeight),
798
- // Figure out whether our width may have changed.
799
- parentWidth = parentFrame ? parentFrame.width : 0,
800
- parentWidthDidChange = parentWidth !== this._scv_parentWidth,
801
- isFixedWidth = this.get('isFixedWidth'),
802
- widthMayHaveChanged = isStatic || (parentWidthDidChange && !isFixedWidth);
853
+ var layout = this.get('layout'),
854
+ newLayout;
803
855
 
804
- // Update the cached parent frame.
805
- this._scv_parentHeight = parentHeight;
806
- this._scv_parentWidth = parentWidth;
856
+ // Normalize arguments.
857
+ if (SC.typeOf(key) === SC.T_STRING) {
858
+ newLayout = this._sc_applyAdjustment(key, value, layout);
859
+ } else {
860
+ for (var aKey in key) {
861
+ if (!key.hasOwnProperty(aKey)) { continue; }
807
862
 
808
- // If our height or width changed, our resulting frame change may impact our child views.
809
- if (heightMayHaveChanged || widthMayHaveChanged) {
810
- this.viewDidResize();
811
- }
812
- // If our size didn't change but our position did, our frame will change, but it won't impact our child
813
- // views' frames. (Note that the _viewFrameDidChange call is made by viewDidResize above.)
814
- else if (positionMayHaveChanged) {
815
- this._viewFrameDidChange();
863
+ newLayout = this._sc_applyAdjustment(aKey, key[aKey], layout, newLayout);
864
+ }
816
865
  }
817
- },
818
-
819
- /**
820
- This method is invoked on your view when the view resizes due to a layout
821
- change or potentially due to the parent view resizing (if your view’s size
822
- depends on the size of your parent view). You can override this method
823
- to implement your own layout if you like, such as performing a grid
824
- layout.
825
866
 
826
- The default implementation simply notifies about the change to 'frame' and
827
- then calls parentViewDidResize on all of your children.
828
-
829
- @returns {void}
830
- */
831
- viewDidResize: function () {
832
- this._viewFrameDidChange();
867
+ // now set adjusted layout
868
+ if (newLayout) {
869
+ var transitionAdjust = this.get('transitionAdjust');
833
870
 
834
- // Also notify our children.
835
- var cv = this.childViews,
836
- frame = this.get('frame'),
837
- len, idx, view;
838
- for (idx = 0; idx < (len = cv.length); ++idx) {
839
- view = cv[idx];
840
- view.tryToPerform('parentViewDidResize', frame);
871
+ if (this.get('viewState') & SC.CoreView.IS_SHOWN && transitionAdjust) {
872
+ // Run the adjust transition.
873
+ this._sc_transitionAdjust(newLayout);
874
+ } else {
875
+ this.set('layout', newLayout);
876
+ }
841
877
  }
878
+
879
+ return this;
842
880
  },
843
881
 
844
- /** @private
845
- Invoked by other views to notify this view that its frame has changed.
882
+ /** */
883
+ computeParentDimensions: function (frame) {
884
+ var parentView = this.get('parentView'),
885
+ parentFrame = (parentView) ? parentView.get('frame') : null,
886
+ ret;
846
887
 
847
- This notifies the view that its frame property has changed,
848
- then notifies its child views that their clipping frames may have changed.
849
- */
850
- _viewFrameDidChange: function () {
851
- this.notifyPropertyChange('frame');
888
+ if (parentFrame) {
889
+ ret = {
890
+ width: parentFrame.width,
891
+ height: parentFrame.height
892
+ };
893
+ } else if (frame) {
894
+ ret = {
895
+ width: (frame.left || 0) + (frame.width || 0) + (frame.right || 0),
896
+ height: (frame.top || 0) + (frame.height || 0) + (frame.bottom || 0)
897
+ };
898
+ } else {
899
+ ret = {
900
+ width: 0,
901
+ height: 0
902
+ };
903
+ }
852
904
 
853
- // Notify the children that their clipping frame may have changed. Top-down, because a child's
854
- // clippingFrame is dependent on its parent's frame.
855
- this._callOnChildViews('_sc_view_clippingFrameDidChange');
905
+ return ret;
856
906
  },
857
907
 
858
- // Implementation note: As a general rule, paired method calls, such as
859
- // beginLiveResize/endLiveResize that are called recursively on the tree
860
- // should reverse the order when doing the final half of the call. This
861
- // ensures that the calls are propertly nested for any cleanup routines.
862
- //
863
- // -> View A.beginXXX()
864
- // -> View B.beginXXX()
865
- // -> View C.beginXXX()
866
- // -> View D.beginXXX()
867
- //
868
- // ...later on, endXXX methods are called in reverse order of beginXXX...
869
- //
870
- // <- View D.endXXX()
871
- // <- View C.endXXX()
872
- // <- View B.endXXX()
873
- // <- View A.endXXX()
874
- //
875
- // See the two methods below for an example implementation.
876
-
877
908
  /**
878
- Call this method when you plan to begin a live resize. This will
879
- notify the receiver view and any of its children that are interested
880
- that the resize is about to begin.
909
+ Converts a frame from the receiver's offset to the target offset. Both
910
+ the receiver and the target must belong to the same pane. If you pass
911
+ null, the conversion will be to the pane level.
881
912
 
882
- @returns {SC.View} receiver
883
- @test in viewDidResize
884
- */
885
- beginLiveResize: function () {
886
- // call before children have been notified...
887
- if (this.willBeginLiveResize) this.willBeginLiveResize();
913
+ Note that the context of a view's frame is the view's parent frame. In
914
+ other words, if you want to convert the frame of your view to the global
915
+ frame, then you should do:
888
916
 
889
- // notify children in order
890
- var ary = this.get('childViews'), len = ary.length, idx, view;
891
- for (idx = 0; idx < len; ++idx) {
892
- view = ary[idx];
893
- if (view.beginLiveResize) view.beginLiveResize();
894
- }
895
- return this;
917
+ var pv = this.get('parentView'), frame = this.get('frame');
918
+ var newFrame = pv ? pv.convertFrameToView(frame, null) : frame;
919
+
920
+ @param {Rect} frame the source frame
921
+ @param {SC.View} targetView the target view to convert to
922
+ @returns {Rect} converted frame
923
+ @test in convertFrames
924
+ */
925
+ convertFrameToView: function (frame, targetView) {
926
+ return this._sc_convertFrameFromViewHelper(frame, this, targetView);
896
927
  },
897
928
 
898
929
  /**
899
- Call this method when you are finished with a live resize. This will
900
- notify the receiver view and any of its children that are interested
901
- that the live resize has ended.
930
+ Converts a frame offset in the coordinates of another view system to the
931
+ receiver's view.
902
932
 
903
- @returns {SC.View} receiver
904
- @test in viewDidResize
905
- */
906
- endLiveResize: function () {
907
- // notify children in *reverse* order
908
- var ary = this.get('childViews'), len = ary.length, idx, view;
909
- for (idx = len - 1; idx >= 0; --idx) { // loop backwards
910
- view = ary[idx];
911
- if (view.endLiveResize) view.endLiveResize();
912
- }
933
+ Note that the convext of a view's frame is relative to the view's
934
+ parentFrame. For example, if you want to convert the frame of view that
935
+ belongs to another view to the receiver's frame you would do:
913
936
 
914
- // call *after* all children have been notified...
915
- if (this.didEndLiveResize) this.didEndLiveResize();
916
- return this;
917
- },
937
+ var frame = view.get('frame');
938
+ var newFrame = this.convertFrameFromView(frame, view.get('parentView'));
918
939
 
919
- /**
920
- The view responsible for laying out this view. The default version
921
- returns the current parent view.
940
+ @param {Rect} frame the source frame
941
+ @param {SC.View} targetView the target view to convert to
942
+ @returns {Rect} converted frame
943
+ @test in converFrames
922
944
  */
923
- layoutView: function () {
924
- return this.get('parentView');
925
- }.property('parentView').cacheable(),
945
+ convertFrameFromView: function (frame, targetView) {
946
+ return this._sc_convertFrameFromViewHelper(frame, targetView, this);
947
+ },
948
+
949
+ /** @private */
950
+ didTransitionAdjust: function () {},
926
951
 
927
952
  /**
928
953
  This method is called whenever a property changes that invalidates the
@@ -949,8 +974,21 @@ SC.View.reopen(
949
974
  }
950
975
 
951
976
  // Optimize notifications depending on if we resized or just moved.
952
- this._checkForResize();
977
+ var didResize = this._sc_checkForResize(this._sc_previousLayout, currentLayout);
978
+
979
+ // Cache the last layout to fine-tune notifications when the layout changes.
980
+ // NOTE: Do this before continuing so that any adjustments that occur in viewDidResize or from
981
+ // _sc_viewFrameDidChange (say to the position after a resize), don't result in _sc_checkForResize
982
+ // running against the old _sc_previousLayout.
983
+ this._sc_previousLayout = currentLayout;
953
984
 
985
+ if (didResize) {
986
+ this.viewDidResize();
987
+ } else {
988
+ // Even if we didn't resize, our frame sould have changed.
989
+ // TODO: consider checking for position changes by testing the resulting frame against the cached frame. This is difficult to do.
990
+ this._sc_viewFrameDidChange();
991
+ }
954
992
 
955
993
  // Notify layoutView/parentView, unless we are transitioning.
956
994
  var layoutView = this.get('layoutView');
@@ -969,54 +1007,6 @@ SC.View.reopen(
969
1007
  return this;
970
1008
  },
971
1009
 
972
- /** @private */
973
- _checkForResize: function () {
974
- // Did our layout change in a way that could cause us to have changed size? If
975
- // not, then there's no need to invalidate the frames of our child views.
976
- var previousLayout = this._previousLayout,
977
- currentLayout = this.get('layout'),
978
- didResize = true;
979
-
980
- // We test the new layout to see if we believe it will affect the view's frame.
981
- // Since all the child view frames may depend on the parent's frame, it's
982
- // best only to notify a frame change when it actually happens.
983
- /*jshint eqnull:true*/
984
- if (previousLayout &&
985
- previousLayout.width != null &&
986
- previousLayout.height != null &&
987
- previousLayout.width === currentLayout.width &&
988
- previousLayout.height === currentLayout.height &&
989
- previousLayout.border === currentLayout.border &&
990
- previousLayout.borderTop === currentLayout.borderTop &&
991
- previousLayout.borderLeft === currentLayout.borderLeft &&
992
- previousLayout.borderBottom === currentLayout.borderBottom &&
993
- previousLayout.borderRight === currentLayout.borderRight) {
994
- didResize = false;
995
- }
996
-
997
- // Cache the last layout to fine-tune notifications when the layout changes.
998
- // NOTE: Do this before continuing so that any adjustments that occur in viewDidResize or from _viewFrameDidChange
999
- // (say to the position after a resize), don't result in _checkForResize running against the old _previousLayout.
1000
- this._previousLayout = currentLayout;
1001
-
1002
- if (didResize) {
1003
- this.viewDidResize();
1004
- } else {
1005
- // Even if we didn't resize, our frame may have changed
1006
- // TODO: consider checking for position changes by testing the resulting frame against the cached frame. This is difficult to do.
1007
- this._viewFrameDidChange();
1008
- }
1009
- },
1010
-
1011
- /**
1012
- This this property to YES whenever the view needs to layout its child
1013
- views. Normally this property is set automatically whenever the layout
1014
- property for a child view changes.
1015
-
1016
- @type Boolean
1017
- */
1018
- childViewsNeedLayout: NO,
1019
-
1020
1010
  /**
1021
1011
  One of two methods that are invoked whenever one of your childViews
1022
1012
  layout changes. This method is invoked every time a child view's layout
@@ -1093,65 +1083,225 @@ SC.View.reopen(
1093
1083
  set[i].updateLayout(force);
1094
1084
  }
1095
1085
 
1096
- set.clear(); // reset & reuse
1086
+ set.clear(); // reset & reuse
1087
+ }
1088
+ },
1089
+
1090
+ /**
1091
+ This method may be called on your view whenever the parent view resizes.
1092
+
1093
+ The default version of this method will reset the frame and then call
1094
+ viewDidResize() if its size may have changed. You will not usually override
1095
+ this method, but you may override the viewDidResize() method.
1096
+
1097
+ @param {Frame} parentFrame the parent view's current frame.
1098
+ @returns {void}
1099
+ @test in viewDidResize
1100
+ */
1101
+ parentViewDidResize: function (parentFrame) {
1102
+ // Determine if our position may have changed.
1103
+ var positionMayHaveChanged = !this.get('isFixedPosition');
1104
+
1105
+ // Figure out if our size may have changed.
1106
+ var isStatic = this.get('useStaticLayout'),
1107
+ // Figure out whether our height may have changed.
1108
+ parentHeight = parentFrame ? parentFrame.height : 0,
1109
+ parentHeightDidChange = parentHeight !== this._scv_parentHeight,
1110
+ isFixedHeight = this.get('isFixedHeight'),
1111
+ heightMayHaveChanged = isStatic || (parentHeightDidChange && !isFixedHeight),
1112
+ // Figure out whether our width may have changed.
1113
+ parentWidth = parentFrame ? parentFrame.width : 0,
1114
+ parentWidthDidChange = parentWidth !== this._scv_parentWidth,
1115
+ isFixedWidth = this.get('isFixedWidth'),
1116
+ widthMayHaveChanged = isStatic || (parentWidthDidChange && !isFixedWidth);
1117
+
1118
+ // Update the cached parent frame.
1119
+ this._scv_parentHeight = parentHeight;
1120
+ this._scv_parentWidth = parentWidth;
1121
+
1122
+ // If our height or width changed, our resulting frame change may impact our child views.
1123
+ if (heightMayHaveChanged || widthMayHaveChanged) {
1124
+ this.viewDidResize();
1125
+ }
1126
+ // If our size didn't change but our position did, our frame will change, but it won't impact our child
1127
+ // views' frames. (Note that the _sc_viewFrameDidChange call is made by viewDidResize above.)
1128
+ else if (positionMayHaveChanged) {
1129
+ this._sc_viewFrameDidChange();
1130
+ }
1131
+ },
1132
+
1133
+ /**
1134
+ The 'frame' property depends on the 'layout' property as well as the
1135
+ parent view's frame. In order to properly invalidate any cached values,
1136
+ we need to invalidate the cache whenever 'layout' changes. However,
1137
+ observing 'layout' does not guarantee that; the observer might not be run
1138
+ before all other observers.
1139
+
1140
+ In order to avoid any window of opportunity where the cached frame could
1141
+ be invalid, we need to force layoutDidChange() to immediately run
1142
+ whenever 'layout' is set.
1143
+ */
1144
+ propertyDidChange: function (key, value, _keepCache) {
1145
+ //@if(debug)
1146
+ // Debug mode only property validation.
1147
+ if (key === 'layout') {
1148
+ // If a layout value is accidentally set to NaN, this can result in infinite loops. Help the
1149
+ // developer out by failing early so that they can follow the stack trace to the problem.
1150
+ for (var property in value) {
1151
+ if (!value.hasOwnProperty(property)) { continue; }
1152
+
1153
+ var layoutValue = value[property];
1154
+ if (isNaN(layoutValue) && (layoutValue !== SC.LAYOUT_AUTO) &&
1155
+ !SC._ROTATION_VALUE_REGEX.exec(layoutValue) && !SC._SCALE_VALUE_REGEX.exec(layoutValue)) {
1156
+ throw new Error("SC.View layout property set to invalid value, %@: %@.".fmt(property, layoutValue));
1157
+ }
1158
+ }
1159
+ }
1160
+ //@endif
1161
+
1162
+ // To allow layout to be a computed property, we check if any property has
1163
+ // changed and if layout is dependent on the property.
1164
+ var layoutChange = false;
1165
+ if (typeof this.layout === "function" && this._kvo_dependents) {
1166
+ var dependents = this._kvo_dependents[key];
1167
+ if (dependents && dependents.indexOf('layout') !== -1) { layoutChange = true; }
1168
+ }
1169
+
1170
+ // If the key is 'layout', we need to call layoutDidChange() immediately
1171
+ // so that if the frame has changed any cached values (for both this view
1172
+ // and any child views) can be appropriately invalidated.
1173
+ if (key === 'layout' || layoutChange) {
1174
+ this.layoutDidChange();
1175
+ }
1176
+
1177
+ // Resume notification as usual.
1178
+ return sc_super();
1179
+ },
1180
+
1181
+ /**
1182
+ */
1183
+ // propertyWillChange: function (key) {
1184
+ // // To allow layout to be a computed property, we check if any property has
1185
+ // // changed and if layout is dependent on the property.
1186
+ // var layoutChange = false;
1187
+ // if (typeof this.layout === "function" && this._kvo_dependents) {
1188
+ // var dependents = this._kvo_dependents[key];
1189
+ // if (dependents && dependents.indexOf('layout') !== -1) { layoutChange = true; }
1190
+ // }
1191
+
1192
+ // if (key === 'layout' || layoutChange) {
1193
+ // this._sc_previousLayout = this.get('layout');
1194
+ // }
1195
+
1196
+ // return sc_super();
1197
+ // },
1198
+
1199
+ /**
1200
+ Attempt to scroll the view to visible. This will walk up the parent
1201
+ view hierarchy looking looking for a scrollable view. It will then
1202
+ call scrollToVisible() on it.
1203
+
1204
+ Returns YES if an actual scroll took place, no otherwise.
1205
+
1206
+ @returns {Boolean}
1207
+ */
1208
+ scrollToVisible: function () {
1209
+ var pv = this.get('parentView');
1210
+ while (pv && !pv.get('isScrollable')) { pv = pv.get('parentView'); }
1211
+
1212
+ // found view, first make it scroll itself visible then scroll this.
1213
+ if (pv) {
1214
+ pv.scrollToVisible();
1215
+ return pv.scrollToVisible(this);
1216
+ } else {
1217
+ return NO;
1097
1218
  }
1098
1219
  },
1099
1220
 
1100
- /** @private Called when the child view layout plugin or options change. */
1101
- _cvl_childViewLayoutDidChange: function () {
1102
- this.set('childViewsNeedLayout', true);
1103
-
1104
- // Filter the input channel.
1105
- this.invokeOnce(this.layoutChildViewsIfNeeded);
1106
- },
1221
+ /**
1222
+ This method is invoked on your view when the view resizes due to a layout
1223
+ change or potentially due to the parent view resizing (if your view’s size
1224
+ depends on the size of your parent view). You can override this method
1225
+ to implement your own layout if you like, such as performing a grid
1226
+ layout.
1107
1227
 
1108
- /** @private Called when the child views change. */
1109
- _cvl_childViewsDidChange: function () {
1110
- this._cvl_teardownChildViewsLiveLayout();
1111
- this._cvl_setupChildViewsLiveLayout();
1228
+ The default implementation simply notifies about the change to 'frame' and
1229
+ then calls parentViewDidResize on all of your children.
1112
1230
 
1113
- this.set('childViewsNeedLayout', true);
1231
+ @returns {void}
1232
+ */
1233
+ viewDidResize: function () {
1234
+ this._sc_viewFrameDidChange();
1114
1235
 
1115
- // Filter the input channel.
1116
- this.invokeOnce(this.layoutChildViewsIfNeeded);
1236
+ // Also notify our children.
1237
+ var cv = this.childViews,
1238
+ frame = this.get('frame'),
1239
+ len, idx, view;
1240
+ for (idx = 0; idx < (len = cv.length); ++idx) {
1241
+ view = cv[idx];
1242
+ view.tryToPerform('parentViewDidResize', frame);
1243
+ }
1117
1244
  },
1118
1245
 
1119
- /** @private Add observers to the child views for automatic child view layout. */
1120
- _cvl_setupChildViewsLiveLayout: function () {
1121
- var childViewLayout = this.childViewLayout,
1122
- childViews,
1123
- childLayoutProperties = childViewLayout.childLayoutProperties || [];
1246
+ // Implementation note: As a general rule, paired method calls, such as
1247
+ // beginLiveResize/endLiveResize that are called recursively on the tree
1248
+ // should reverse the order when doing the final half of the call. This
1249
+ // ensures that the calls are propertly nested for any cleanup routines.
1250
+ //
1251
+ // -> View A.beginXXX()
1252
+ // -> View B.beginXXX()
1253
+ // -> View C.beginXXX()
1254
+ // -> View D.beginXXX()
1255
+ //
1256
+ // ...later on, endXXX methods are called in reverse order of beginXXX...
1257
+ //
1258
+ // <- View D.endXXX()
1259
+ // <- View C.endXXX()
1260
+ // <- View B.endXXX()
1261
+ // <- View A.endXXX()
1262
+ //
1263
+ // See the two methods below for an example implementation.
1124
1264
 
1125
- // Create a reference to the current child views so that we can clean them if they change.
1126
- childViews = this._cvl_childViews = this.get('childViews');
1127
- for (var i = 0, len = childLayoutProperties.length; i < len; i++) {
1128
- var observedProperty = childLayoutProperties[i];
1265
+ /**
1266
+ Call this method when you plan to begin a live resize. This will
1267
+ notify the receiver view and any of its children that are interested
1268
+ that the resize is about to begin.
1129
1269
 
1130
- for (var j = 0, jlen = childViews.get('length'); j < jlen; j++) {
1131
- var childView = childViews.objectAt(j);
1132
- if (!childView.get('useAbsoluteLayout') && !childView.get('useStaticLayout')) {
1133
- childView.addObserver(observedProperty, this, this._cvl_childViewLayoutDidChange);
1134
- }
1135
- }
1270
+ @returns {SC.View} receiver
1271
+ @test in viewDidResize
1272
+ */
1273
+ beginLiveResize: function () {
1274
+ // call before children have been notified...
1275
+ if (this.willBeginLiveResize) this.willBeginLiveResize();
1276
+
1277
+ // notify children in order
1278
+ var ary = this.get('childViews'), len = ary.length, idx, view;
1279
+ for (idx = 0; idx < len; ++idx) {
1280
+ view = ary[idx];
1281
+ if (view.beginLiveResize) view.beginLiveResize();
1136
1282
  }
1283
+ return this;
1137
1284
  },
1138
1285
 
1139
- /** @private Remove observers from the child views for automatic child view layout. */
1140
- _cvl_teardownChildViewsLiveLayout: function () {
1141
- var childViewLayout = this.childViewLayout,
1142
- childViews = this._cvl_childViews || [],
1143
- childLayoutProperties = childViewLayout.childLayoutProperties || [];
1144
-
1145
- for (var i = 0, len = childLayoutProperties.length; i < len; i++) {
1146
- var observedProperty = childLayoutProperties[i];
1286
+ /**
1287
+ Call this method when you are finished with a live resize. This will
1288
+ notify the receiver view and any of its children that are interested
1289
+ that the live resize has ended.
1147
1290
 
1148
- for (var j = 0, jlen = childViews.get('length'); j < jlen; j++) {
1149
- var childView = childViews.objectAt(j);
1150
- if (!childView.get('useAbsoluteLayout') && !childView.get('useStaticLayout')) {
1151
- childView.removeObserver(observedProperty, this, this._cvl_childViewLayoutDidChange);
1152
- }
1153
- }
1291
+ @returns {SC.View} receiver
1292
+ @test in viewDidResize
1293
+ */
1294
+ endLiveResize: function () {
1295
+ // notify children in *reverse* order
1296
+ var ary = this.get('childViews'), len = ary.length, idx, view;
1297
+ for (idx = len - 1; idx >= 0; --idx) { // loop backwards
1298
+ view = ary[idx];
1299
+ if (view.endLiveResize) view.endLiveResize();
1154
1300
  }
1301
+
1302
+ // call *after* all children have been notified...
1303
+ if (this.didEndLiveResize) this.didEndLiveResize();
1304
+ return this;
1155
1305
  },
1156
1306
 
1157
1307
  /**
@@ -1187,79 +1337,6 @@ SC.View.reopen(
1187
1337
  context.setStyle(this.get('layoutStyle'));
1188
1338
  },
1189
1339
 
1190
- // ------------------------------------------------------------------------
1191
- // Child View Layouts
1192
- //
1193
-
1194
- /**
1195
- The child view layout plugin to use when laying out child views.
1196
-
1197
- You can set this property to a child layout plugin object to
1198
- automatically set and adjust the layouts of this view's child views
1199
- according to some specific layout style. For instance, SproutCore includes
1200
- two such plugins, SC.View.VERTICAL_STACK and SC.View.HORIZONTAL_STACK.
1201
-
1202
- SC.View.VERTICAL_STACK will arrange child views in order in a vertical
1203
- stack, which only requires that the height of each child view be specified.
1204
- Likewise, SC.View.HORIZONTAL_STACK does the same in the horizontal
1205
- direction, which requires that the width of each child view be specified.
1206
-
1207
- Where child layout plugins are extremely useful, besides simplifying
1208
- the amount of layout code you need to write, is that they can update the
1209
- layouts automatically as things change. For more details and examples,
1210
- please see the documentation for SC.View.VERTICAL_STACK and
1211
- SC.View.HORIZONTAL_STACK.
1212
-
1213
- To define your own child view layout plugin, simply create an object that
1214
- conforms to the SC.ChildViewLayoutProtocol protocol.
1215
-
1216
- **Note** This should only be set once and is not bindable.
1217
-
1218
- @type Object
1219
- @default null
1220
- */
1221
- childViewLayout: null,
1222
-
1223
- /**
1224
- The options for the given child view layout plugin.
1225
-
1226
- These options are specific to the current child layout plugin being used and
1227
- are used to modify the applied layouts. For example, SC.View.VERTICAL_STACK
1228
- accepts options like:
1229
-
1230
- childViewLayoutOptions: {
1231
- paddingAfter: 20,
1232
- paddingBefore: 20,
1233
- spacing: 10
1234
- }
1235
-
1236
- To determine what options may be used for a given plugin and to see what the
1237
- default options are, please refer to the documentation for the child layout
1238
- plugin being used.
1239
-
1240
- @type Object
1241
- @default null
1242
- */
1243
- childViewLayoutOptions: null,
1244
-
1245
- /**
1246
- Whether the view and its child views should be monitored for changes that
1247
- affect the current child view layout.
1248
-
1249
- When `true` and using a childViewLayout plugin, the view and its child views
1250
- will be observed for any changes that would affect the layout of all the
1251
- child views. For example, if `isChildViewLayout` is true and using
1252
- SC.View.VERTICAL_STACK, if any child view's height or visibility changes
1253
- all of the child views will be re-adjusted.
1254
-
1255
- If you only want to automatically layout the child views once, you can
1256
- set this to `false` to improve performance.
1257
-
1258
- @type Boolean
1259
- @default true
1260
- */
1261
- isChildViewLayoutLive: true,
1262
-
1263
1340
  // ------------------------------------------------------------------------
1264
1341
  // Statechart
1265
1342
  //
@@ -1313,21 +1390,33 @@ SC.View.reopen(
1313
1390
  if (this.didAppendToDocument) { this.didAppendToDocument(); }
1314
1391
  },
1315
1392
 
1316
- /** @private Override: The 'adopted' event (uses _checkForResize so our childViews are notified if our frame changes). */
1393
+ /** @private Override: The 'adopted' event (uses isFixedSize so our childViews are notified if our frame changes). */
1317
1394
  _adopted: function (beforeView) {
1318
- // Our frame may change once we've been adopted to a parent.
1319
- this._checkForResize();
1395
+ // If our size depends on our parent, it will have changed on adoption.
1396
+ var isFixedSize = this.get('isFixedSize');
1397
+ if (isFixedSize) {
1398
+ // Even if our size is fixed, our frame may have changed (in particular if the anchor is not top/left)
1399
+ this._sc_viewFrameDidChange();
1400
+ } else {
1401
+ this.viewDidResize();
1402
+ }
1320
1403
 
1321
1404
  sc_super();
1322
1405
  },
1323
1406
 
1324
- /** @private Extension: The 'orphaned' event. */
1407
+ /** @private Extension: The 'orphaned' event (uses isFixedSize so our childViews are notified if our frame changes). */
1325
1408
  _orphaned: function () {
1326
1409
  sc_super();
1327
1410
 
1328
1411
  if (!this.isDestroyed) {
1329
- // Our frame may change once we've been removed from a parent.
1330
- this._checkForResize();
1412
+ // If our size depends on our parent, it will have changed on orphaning.
1413
+ var isFixedSize = this.get('isFixedSize');
1414
+ if (isFixedSize) {
1415
+ // Even if our size is fixed, our frame may have changed (in particular if the anchor is not top/left)
1416
+ this._sc_viewFrameDidChange();
1417
+ } else {
1418
+ this.viewDidResize();
1419
+ }
1331
1420
  }
1332
1421
  },
1333
1422
 
@@ -1344,64 +1433,7 @@ SC.View.reopen(
1344
1433
  _updatedLayout: function () {
1345
1434
  // Notify.
1346
1435
  this.didRenderAnimations();
1347
- },
1348
-
1349
- // ------------------------------------------------------------------------
1350
- // Transitions
1351
- //
1352
-
1353
- /**
1354
- The transition plugin to use when this view is moved or resized by adjusting
1355
- its layout.
1356
-
1357
- SC.CoreView uses a pluggable transition architecture where the transition
1358
- setup, execution and cleanup can be handled by a plugin. This allows you
1359
- to create complex transition animations and share them across all your views
1360
- with only a single line of code.
1361
-
1362
- There are a number of pre-built transition adjust plugins available in
1363
- the SproutCore foundation framework:
1364
-
1365
- SC.View.SMOOTH_ADJUST
1366
- SC.View.BOUNCE_ADJUST
1367
- SC.View.SPRING_ADJUST
1368
-
1369
- To create a custom transition plugin simply create a regular JavaScript
1370
- object that conforms to the SC.ViewTransitionProtocol protocol.
1371
-
1372
- NOTE: When creating custom transition adjust plugins, be aware that SC.View
1373
- will not call the `setup` method of the plugin, only the `run` method.
1374
-
1375
- @type Object (SC.ViewTransitionProtocol)
1376
- @default null
1377
- @since Version 1.10
1378
- */
1379
- transitionAdjust: null,
1380
-
1381
- /**
1382
- The options for the given `transitionAdjust` plugin.
1383
-
1384
- These options are specific to the current transition plugin used and are
1385
- used to modify the transition animation. To determine what options
1386
- may be used for a given plugin and to see what the default options are,
1387
- see the documentation for the transition plugin being used.
1388
-
1389
- Most transitions will accept a duration and timing option, but may
1390
- also use other options. For example, SC.View.BOUNCE_ADJUST accepts options
1391
- like:
1392
-
1393
- transitionAdjustOptions: {
1394
- bounciness: 0.5, // how much the adjustment should bounce back each time
1395
- bounces: 4, // the number of bounces
1396
- duration: 0.25,
1397
- delay: 1
1398
- }
1399
-
1400
- @type Object
1401
- @default null
1402
- @since Version 1.10
1403
- */
1404
- transitionAdjustOptions: null
1436
+ }
1405
1437
 
1406
1438
  });
1407
1439
 
@@ -1561,4 +1593,5 @@ SC.View.mixin(
1561
1593
  convertLayoutToCustomLayout: function (layout, layoutParams, parentFrame) {
1562
1594
  // TODO: [EG] Create Top/Left/Width/Height to a Custom Layout conversion
1563
1595
  }
1596
+
1564
1597
  });