OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #library("view"); | 5 #library("view"); |
6 | 6 |
7 #import('dart:html'); | 7 #import('dart:html'); |
8 #import('../base/base.dart'); | 8 #import('../base/base.dart'); |
9 #import('../observable/observable.dart'); | 9 #import('../observable/observable.dart'); |
10 #import('../touch/touch.dart'); | 10 #import('../touch/touch.dart'); |
11 #import('../layout/layout.dart'); | 11 #import('../layout/layout.dart'); |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 /** | 161 /** |
162 * Override this to perform behavior after this View has been removed from the | 162 * Override this to perform behavior after this View has been removed from the |
163 * document. This can be a convenient time to unregister event handlers bound | 163 * document. This can be a convenient time to unregister event handlers bound |
164 * in enterDocument(). | 164 * in enterDocument(). |
165 * | 165 * |
166 * This will be called each time the View is removed from the document, if it | 166 * This will be called each time the View is removed from the document, if it |
167 * is added and removed multiple times. | 167 * is added and removed multiple times. |
168 */ | 168 */ |
169 void exitDocument() {} | 169 void exitDocument() {} |
170 | 170 |
171 /** Override this to perform behavior after the window is resized. */ | 171 /** |
| 172 * Override this to perform behavior after the window is resized. |
| 173 * This method is guaranteed to be called within a measurement frame. To |
| 174 * manipulate the DOM, return a LayoutCallback which will be execututed |
| 175 * in the normal context. |
| 176 */ |
172 // TODO(jmesserly): this isn't really the event we want. Ideally we want to | 177 // TODO(jmesserly): this isn't really the event we want. Ideally we want to |
173 // fire the event only if this particular View changed size. Also we should | 178 // fire the event only if this particular View changed size. Also we should |
174 // give a view the ability to measure itself when added to the document. | 179 // give a view the ability to measure itself when added to the document. |
175 void windowResized() {} | 180 LayoutCallback windowResized() => null; |
176 | 181 |
177 /** | 182 /** |
178 * Registers the given listener callback to the given observable. Also | 183 * Registers the given listener callback to the given observable. Also |
179 * immedially invokes the callback once as if a change has just come in. This | 184 * immedially invokes the callback once as if a change has just come in. This |
180 * lets you define a render() method that renders the skeleton of a view, then | 185 * lets you define a render() method that renders the skeleton of a view, then |
181 * register a bunch of listeners which all fire to populate the view with | 186 * register a bunch of listeners which all fire to populate the view with |
182 * model data. | 187 * model data. |
183 */ | 188 */ |
184 void watch(Observable observable, void watcher(EventSummary summary)) { | 189 void watch(Observable observable, void watcher(EventSummary summary)) { |
185 // Make a fake summary for the initial watch. | 190 // Make a fake summary for the initial watch. |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 } | 315 } |
311 | 316 |
312 void _unhookGlobalLayoutEvents() { | 317 void _unhookGlobalLayoutEvents() { |
313 if (_resizeHandler != null) { | 318 if (_resizeHandler != null) { |
314 window.on.resize.remove(_resizeHandler); | 319 window.on.resize.remove(_resizeHandler); |
315 _resizeHandler = null; | 320 _resizeHandler = null; |
316 } | 321 } |
317 } | 322 } |
318 | 323 |
319 void doLayout() { | 324 void doLayout() { |
320 _measureLayout().then((bool changed) { | 325 // Callbacks to execute after all layouts are complete. |
321 if (changed) { | 326 final callbacks = <LayoutCallback>[]; |
322 _applyLayoutToChildren(); | 327 bool changed = false; |
| 328 void _measureLayout(View v) { |
| 329 assert(window.inMeasurementFrame); |
| 330 LayoutCallback callback = v.windowResized(); |
| 331 if (callback != null) { |
| 332 callbacks.add(callback); |
323 } | 333 } |
| 334 // TODO(jmesserly): this logic is more complex than it needs to be |
| 335 // because we're taking pains to not initialize _layout if it's not |
| 336 // needed. Is that a good tradeoff? |
| 337 if (ViewLayout.hasCustomLayout(v)) { |
| 338 final rect = v._node.rect.client; |
| 339 if (v.layout.measureLayout(rect.width, rect.height)) { |
| 340 changed = true; |
| 341 } |
| 342 } else { |
| 343 for (final child in v.childViews) { |
| 344 _measureLayout(child); |
| 345 } |
| 346 } |
| 347 } |
| 348 |
| 349 window.requestMeasurementFrame(() { |
| 350 _measureLayout(this); |
| 351 return () { |
| 352 if (changed) { |
| 353 _applyLayoutToChildren(); |
| 354 } |
| 355 for (LayoutCallback callback in callbacks) { |
| 356 callback(); |
| 357 } |
| 358 }; |
324 }); | 359 }); |
325 } | 360 } |
326 | 361 |
327 Future<bool> _measureLayout() { | |
328 final changed = new Completer<bool>(); | |
329 _measureLayoutHelper(changed); | |
330 | |
331 window.requestLayoutFrame(() { | |
332 if (!changed.future.isComplete) { | |
333 changed.complete(false); | |
334 } | |
335 }); | |
336 return changed.future; | |
337 } | |
338 | |
339 void _measureLayoutHelper(Completer<bool> changed) { | |
340 windowResized(); | |
341 | |
342 // TODO(jmesserly): this logic is more complex than it needs to be because | |
343 // we're taking pains to not initialize _layout if it's not needed. Is that | |
344 // a good tradeoff? | |
345 if (ViewLayout.hasCustomLayout(this)) { | |
346 Completer sizeCompleter = new Completer<Size>(); | |
347 _node.rect.then((ElementRect rect) { | |
348 sizeCompleter.complete( | |
349 new Size(rect.client.width, rect.client.height)); | |
350 }); | |
351 layout.measureLayout(sizeCompleter.future, changed); | |
352 } else { | |
353 for (final child in childViews) { | |
354 child._measureLayoutHelper(changed); | |
355 } | |
356 } | |
357 } | |
358 | |
359 void _applyLayoutToChildren() { | 362 void _applyLayoutToChildren() { |
360 for (final child in childViews) { | 363 for (final child in childViews) { |
361 child._applyLayout(); | 364 child._applyLayout(); |
362 } | 365 } |
363 } | 366 } |
364 | 367 |
365 void _applyLayout() { | 368 void _applyLayout() { |
366 if (_layout != null) { | 369 if (_layout != null) { |
367 _layout.applyLayout(); | 370 _layout.applyLayout(); |
368 } | 371 } |
369 _applyLayoutToChildren(); | 372 _applyLayoutToChildren(); |
370 } | 373 } |
371 } | 374 } |
OLD | NEW |