OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 /** | 171 /** Override this to perform behavior after the window is resized. */ |
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 */ | |
177 // TODO(jmesserly): this isn't really the event we want. Ideally we want to | 172 // TODO(jmesserly): this isn't really the event we want. Ideally we want to |
178 // fire the event only if this particular View changed size. Also we should | 173 // fire the event only if this particular View changed size. Also we should |
179 // give a view the ability to measure itself when added to the document. | 174 // give a view the ability to measure itself when added to the document. |
180 LayoutCallback windowResized() => null; | 175 void windowResized() {} |
181 | 176 |
182 /** | 177 /** |
183 * Registers the given listener callback to the given observable. Also | 178 * Registers the given listener callback to the given observable. Also |
184 * immedially invokes the callback once as if a change has just come in. This | 179 * immedially invokes the callback once as if a change has just come in. This |
185 * lets you define a render() method that renders the skeleton of a view, then | 180 * lets you define a render() method that renders the skeleton of a view, then |
186 * register a bunch of listeners which all fire to populate the view with | 181 * register a bunch of listeners which all fire to populate the view with |
187 * model data. | 182 * model data. |
188 */ | 183 */ |
189 void watch(Observable observable, void watcher(EventSummary summary)) { | 184 void watch(Observable observable, void watcher(EventSummary summary)) { |
190 // Make a fake summary for the initial watch. | 185 // Make a fake summary for the initial watch. |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
315 } | 310 } |
316 | 311 |
317 void _unhookGlobalLayoutEvents() { | 312 void _unhookGlobalLayoutEvents() { |
318 if (_resizeHandler != null) { | 313 if (_resizeHandler != null) { |
319 window.on.resize.remove(_resizeHandler); | 314 window.on.resize.remove(_resizeHandler); |
320 _resizeHandler = null; | 315 _resizeHandler = null; |
321 } | 316 } |
322 } | 317 } |
323 | 318 |
324 void doLayout() { | 319 void doLayout() { |
325 // Callbacks to execute after all layouts are complete. | 320 _measureLayout().then((bool changed) { |
326 final callbacks = <LayoutCallback>[]; | 321 if (changed) { |
327 bool changed = false; | 322 _applyLayoutToChildren(); |
328 void _measureLayout(View v) { | |
329 assert(window.inMeasurementFrame); | |
330 LayoutCallback callback = v.windowResized(); | |
331 if (callback != null) { | |
332 callbacks.add(callback); | |
333 } | 323 } |
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 }; | |
359 }); | 324 }); |
360 } | 325 } |
361 | 326 |
| 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 |
362 void _applyLayoutToChildren() { | 359 void _applyLayoutToChildren() { |
363 for (final child in childViews) { | 360 for (final child in childViews) { |
364 child._applyLayout(); | 361 child._applyLayout(); |
365 } | 362 } |
366 } | 363 } |
367 | 364 |
368 void _applyLayout() { | 365 void _applyLayout() { |
369 if (_layout != null) { | 366 if (_layout != null) { |
370 _layout.applyLayout(); | 367 _layout.applyLayout(); |
371 } | 368 } |
372 _applyLayoutToChildren(); | 369 _applyLayoutToChildren(); |
373 } | 370 } |
374 } | 371 } |
OLD | NEW |