sproutcore 1.11.0.rc3 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  });