| 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 |