OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 library fn; | |
6 | |
7 import 'app.dart'; | |
8 import 'dart:async'; | 5 import 'dart:async'; |
9 import 'dart:collection'; | 6 import 'dart:collection'; |
10 import 'dart:mirrors'; | 7 import 'dart:mirrors'; |
11 import 'dart:sky' as sky; | 8 import 'dart:sky' as sky; |
12 import 'package:vector_math/vector_math.dart'; | 9 |
13 import 'rendering/block.dart'; | 10 import '../app.dart'; |
14 import 'rendering/box.dart'; | 11 import '../rendering/box.dart'; |
15 import 'rendering/flex.dart'; | 12 import '../rendering/object.dart'; |
16 import 'rendering/object.dart'; | 13 |
17 import 'rendering/paragraph.dart'; | 14 export '../rendering/box.dart' show BoxConstraints, BoxDecoration, Border, Borde
rSide, EdgeDims; |
18 import 'rendering/stack.dart'; | 15 export '../rendering/flex.dart' show FlexDirection; |
19 export 'rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; | 16 export '../rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; |
20 export 'rendering/box.dart' show BoxConstraints, BoxDecoration, Border, BorderSi
de, EdgeDims; | 17 |
21 export 'rendering/flex.dart' show FlexDirection; | |
22 | 18 |
23 // final sky.Tracing _tracing = sky.window.tracing; | 19 // final sky.Tracing _tracing = sky.window.tracing; |
24 | 20 |
25 final bool _shouldLogRenderDuration = false; | 21 final bool _shouldLogRenderDuration = false; |
26 | 22 |
27 /* | 23 /* |
28 * All Effen nodes derive from UINode. All nodes have a _parent, a _key and | 24 * All Effen nodes derive from UINode. All nodes have a _parent, a _key and |
29 * can be sync'd. | 25 * can be sync'd. |
30 */ | 26 */ |
31 abstract class UINode { | 27 abstract class UINode { |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 } | 144 } |
149 | 145 |
150 assert(!node.mounted); | 146 assert(!node.mounted); |
151 node.setParent(this); | 147 node.setParent(this); |
152 node._sync(oldNode, slot); | 148 node._sync(oldNode, slot); |
153 assert(node.root is RenderObject); | 149 assert(node.root is RenderObject); |
154 return node; | 150 return node; |
155 } | 151 } |
156 } | 152 } |
157 | 153 |
| 154 |
158 // Descendants of TagNode provide a way to tag RenderObjectWrapper and | 155 // Descendants of TagNode provide a way to tag RenderObjectWrapper and |
159 // Component nodes with annotations, such as event listeners, | 156 // Component nodes with annotations, such as event listeners, |
160 // stylistic information, etc. | 157 // stylistic information, etc. |
161 abstract class TagNode extends UINode { | 158 abstract class TagNode extends UINode { |
162 | 159 |
163 TagNode(UINode content, { Object key }) : this.content = content, super(key: k
ey); | 160 TagNode(UINode content, { Object key }) : this.content = content, super(key: k
ey); |
164 | 161 |
165 UINode content; | 162 UINode content; |
166 | 163 |
167 void _sync(UINode old, dynamic slot) { | 164 void _sync(UINode old, dynamic slot) { |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 | 265 |
269 void _handleEvent(sky.Event e) { | 266 void _handleEvent(sky.Event e) { |
270 sky.EventListener listener = listeners[e.type]; | 267 sky.EventListener listener = listeners[e.type]; |
271 if (listener != null) { | 268 if (listener != null) { |
272 listener(e); | 269 listener(e); |
273 } | 270 } |
274 } | 271 } |
275 | 272 |
276 } | 273 } |
277 | 274 |
| 275 |
| 276 abstract class Component extends UINode { |
| 277 |
| 278 Component({ Object key, bool stateful }) |
| 279 : _stateful = stateful != null ? stateful : false, |
| 280 _order = _currentOrder + 1, |
| 281 super(key: key); |
| 282 |
| 283 Component.fromArgs(Object key, bool stateful) |
| 284 : this(key: key, stateful: stateful); |
| 285 |
| 286 static Component _currentlyBuilding; |
| 287 bool get _isBuilding => _currentlyBuilding == this; |
| 288 |
| 289 bool _stateful; |
| 290 bool _dirty = true; |
| 291 bool _disqualifiedFromEverAppearingAgain = false; |
| 292 |
| 293 UINode _built; |
| 294 dynamic _slot; // cached slot from the last time we were synced |
| 295 |
| 296 void didMount() { |
| 297 assert(!_disqualifiedFromEverAppearingAgain); |
| 298 super.didMount(); |
| 299 } |
| 300 |
| 301 void remove() { |
| 302 assert(_built != null); |
| 303 assert(root != null); |
| 304 removeChild(_built); |
| 305 _built = null; |
| 306 super.remove(); |
| 307 } |
| 308 |
| 309 bool _retainStatefulNodeIfPossible(UINode old) { |
| 310 assert(!_disqualifiedFromEverAppearingAgain); |
| 311 |
| 312 Component oldComponent = old as Component; |
| 313 if (oldComponent == null || !oldComponent._stateful) |
| 314 return false; |
| 315 |
| 316 assert(key == oldComponent.key); |
| 317 |
| 318 // Make |this|, the newly-created object, into the "old" Component, and kill
it |
| 319 _stateful = false; |
| 320 _built = oldComponent._built; |
| 321 assert(_built != null); |
| 322 _disqualifiedFromEverAppearingAgain = true; |
| 323 |
| 324 // Make |oldComponent| the "new" component |
| 325 oldComponent._built = null; |
| 326 oldComponent._dirty = true; |
| 327 oldComponent.syncFields(this); |
| 328 return true; |
| 329 } |
| 330 |
| 331 // This is called by _retainStatefulNodeIfPossible(), during |
| 332 // syncChild(), just before _sync() is called. |
| 333 // This must be implemented on any subclass that can become stateful |
| 334 // (but don't call super.syncFields() if you inherit directly from |
| 335 // Component, since that'll fire an assert). |
| 336 // If you don't ever become stateful, then don't override this. |
| 337 void syncFields(Component source) { |
| 338 assert(false); |
| 339 } |
| 340 |
| 341 final int _order; |
| 342 static int _currentOrder = 0; |
| 343 |
| 344 /* There are three cases here: |
| 345 * 1) Building for the first time: |
| 346 * assert(_built == null && old == null) |
| 347 * 2) Re-building (because a dirty flag got set): |
| 348 * assert(_built != null && old == null) |
| 349 * 3) Syncing against an old version |
| 350 * assert(_built == null && old != null) |
| 351 */ |
| 352 void _sync(UINode old, dynamic slot) { |
| 353 assert(_built == null || old == null); |
| 354 assert(!_disqualifiedFromEverAppearingAgain); |
| 355 |
| 356 Component oldComponent = old as Component; |
| 357 |
| 358 _slot = slot; |
| 359 |
| 360 var oldBuilt; |
| 361 if (oldComponent == null) { |
| 362 oldBuilt = _built; |
| 363 } else { |
| 364 assert(_built == null); |
| 365 oldBuilt = oldComponent._built; |
| 366 } |
| 367 |
| 368 int lastOrder = _currentOrder; |
| 369 _currentOrder = _order; |
| 370 _currentlyBuilding = this; |
| 371 _built = build(); |
| 372 assert(_built != null); |
| 373 _currentlyBuilding = null; |
| 374 _currentOrder = lastOrder; |
| 375 |
| 376 _built = syncChild(_built, oldBuilt, slot); |
| 377 assert(_built != null); |
| 378 _dirty = false; |
| 379 _root = _built.root; |
| 380 assert(_root == root); // in case a subclass reintroduces it |
| 381 assert(root != null); |
| 382 } |
| 383 |
| 384 void _buildIfDirty() { |
| 385 assert(!_disqualifiedFromEverAppearingAgain); |
| 386 if (!_dirty || !_mounted) |
| 387 return; |
| 388 |
| 389 assert(root != null); |
| 390 _sync(null, _slot); |
| 391 } |
| 392 |
| 393 void scheduleBuild() { |
| 394 setState(() {}); |
| 395 } |
| 396 |
| 397 void setState(Function fn()) { |
| 398 assert(!_disqualifiedFromEverAppearingAgain); |
| 399 _stateful = true; |
| 400 fn(); |
| 401 if (_isBuilding || _dirty || !_mounted) |
| 402 return; |
| 403 |
| 404 _dirty = true; |
| 405 _scheduleComponentForRender(this); |
| 406 } |
| 407 |
| 408 UINode build(); |
| 409 |
| 410 } |
| 411 |
| 412 Set<Component> _dirtyComponents = new Set<Component>(); |
| 413 bool _buildScheduled = false; |
| 414 bool _inRenderDirtyComponents = false; |
| 415 |
| 416 void _buildDirtyComponents() { |
| 417 //_tracing.begin('fn::_buildDirtyComponents'); |
| 418 |
| 419 Stopwatch sw; |
| 420 if (_shouldLogRenderDuration) |
| 421 sw = new Stopwatch()..start(); |
| 422 |
| 423 try { |
| 424 _inRenderDirtyComponents = true; |
| 425 |
| 426 List<Component> sortedDirtyComponents = _dirtyComponents.toList(); |
| 427 sortedDirtyComponents.sort((Component a, Component b) => a._order - b._order
); |
| 428 for (var comp in sortedDirtyComponents) { |
| 429 comp._buildIfDirty(); |
| 430 } |
| 431 |
| 432 _dirtyComponents.clear(); |
| 433 _buildScheduled = false; |
| 434 } finally { |
| 435 _inRenderDirtyComponents = false; |
| 436 } |
| 437 |
| 438 UINode._notifyMountStatusChanged(); |
| 439 |
| 440 if (_shouldLogRenderDuration) { |
| 441 sw.stop(); |
| 442 print('Render took ${sw.elapsedMicroseconds} microseconds'); |
| 443 } |
| 444 |
| 445 //_tracing.end('fn::_buildDirtyComponents'); |
| 446 } |
| 447 |
| 448 void _scheduleComponentForRender(Component c) { |
| 449 assert(!_inRenderDirtyComponents); |
| 450 _dirtyComponents.add(c); |
| 451 |
| 452 if (!_buildScheduled) { |
| 453 _buildScheduled = true; |
| 454 new Future.microtask(_buildDirtyComponents); |
| 455 } |
| 456 } |
| 457 |
| 458 |
278 /* | 459 /* |
279 * RenderObjectWrappers correspond to a desired state of a RenderObject. | 460 * RenderObjectWrappers correspond to a desired state of a RenderObject. |
280 * They are fully immutable, with one exception: A UINode which is a | 461 * They are fully immutable, with one exception: A UINode which is a |
281 * Component which lives within an MultiChildRenderObjectWrapper's | 462 * Component which lives within an MultiChildRenderObjectWrapper's |
282 * children list, may be replaced with the "old" instance if it has | 463 * children list, may be replaced with the "old" instance if it has |
283 * become stateful. | 464 * become stateful. |
284 */ | 465 */ |
285 abstract class RenderObjectWrapper extends UINode { | 466 abstract class RenderObjectWrapper extends UINode { |
286 | 467 |
287 RenderObjectWrapper({ | 468 RenderObjectWrapper({ |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
371 } | 552 } |
372 | 553 |
373 void remove() { | 554 void remove() { |
374 if (child != null) | 555 if (child != null) |
375 removeChild(child); | 556 removeChild(child); |
376 super.remove(); | 557 super.remove(); |
377 } | 558 } |
378 | 559 |
379 } | 560 } |
380 | 561 |
381 class Opacity extends OneChildRenderObjectWrapper { | |
382 Opacity({ this.opacity, UINode child, Object key }) | |
383 : super(child: child, key: key); | |
384 | |
385 RenderOpacity get root { RenderOpacity result = super.root; return result; } | |
386 final double opacity; | |
387 | |
388 RenderOpacity createNode() => new RenderOpacity(opacity: opacity); | |
389 | |
390 void syncRenderObject(Opacity old) { | |
391 super.syncRenderObject(old); | |
392 root.opacity = opacity; | |
393 } | |
394 } | |
395 | |
396 class ClipRect extends OneChildRenderObjectWrapper { | |
397 | |
398 ClipRect({ UINode child, Object key }) | |
399 : super(child: child, key: key); | |
400 | |
401 RenderClipRect get root { RenderClipRect result = super.root; return result; } | |
402 RenderClipRect createNode() => new RenderClipRect(); | |
403 } | |
404 | |
405 class ClipOval extends OneChildRenderObjectWrapper { | |
406 | |
407 ClipOval({ UINode child, Object key }) | |
408 : super(child: child, key: key); | |
409 | |
410 RenderClipOval get root { RenderClipOval result = super.root; return result; } | |
411 RenderClipOval createNode() => new RenderClipOval(); | |
412 } | |
413 | |
414 class Padding extends OneChildRenderObjectWrapper { | |
415 | |
416 Padding({ this.padding, UINode child, Object key }) | |
417 : super(child: child, key: key); | |
418 | |
419 RenderPadding get root { RenderPadding result = super.root; return result; } | |
420 final EdgeDims padding; | |
421 | |
422 RenderPadding createNode() => new RenderPadding(padding: padding); | |
423 | |
424 void syncRenderObject(Padding old) { | |
425 super.syncRenderObject(old); | |
426 root.padding = padding; | |
427 } | |
428 | |
429 } | |
430 | |
431 class DecoratedBox extends OneChildRenderObjectWrapper { | |
432 | |
433 DecoratedBox({ this.decoration, UINode child, Object key }) | |
434 : super(child: child, key: key); | |
435 | |
436 RenderDecoratedBox get root { RenderDecoratedBox result = super.root; return r
esult; } | |
437 final BoxDecoration decoration; | |
438 | |
439 RenderDecoratedBox createNode() => new RenderDecoratedBox(decoration: decorati
on); | |
440 | |
441 void syncRenderObject(DecoratedBox old) { | |
442 super.syncRenderObject(old); | |
443 root.decoration = decoration; | |
444 } | |
445 | |
446 } | |
447 | |
448 class SizedBox extends OneChildRenderObjectWrapper { | |
449 | |
450 SizedBox({ | |
451 double width: double.INFINITY, | |
452 double height: double.INFINITY, | |
453 UINode child, | |
454 Object key | |
455 }) : desiredSize = new Size(width, height), super(child: child, key: key); | |
456 | |
457 RenderSizedBox get root { RenderSizedBox result = super.root; return result; } | |
458 final Size desiredSize; | |
459 | |
460 RenderSizedBox createNode() => new RenderSizedBox(desiredSize: desiredSize); | |
461 | |
462 void syncRenderObject(SizedBox old) { | |
463 super.syncRenderObject(old); | |
464 root.desiredSize = desiredSize; | |
465 } | |
466 | |
467 } | |
468 | |
469 class ConstrainedBox extends OneChildRenderObjectWrapper { | |
470 | |
471 ConstrainedBox({ this.constraints, UINode child, Object key }) | |
472 : super(child: child, key: key); | |
473 | |
474 RenderConstrainedBox get root { RenderConstrainedBox result = super.root; retu
rn result; } | |
475 final BoxConstraints constraints; | |
476 | |
477 RenderConstrainedBox createNode() => new RenderConstrainedBox(additionalConstr
aints: constraints); | |
478 | |
479 void syncRenderObject(ConstrainedBox old) { | |
480 super.syncRenderObject(old); | |
481 root.additionalConstraints = constraints; | |
482 } | |
483 | |
484 } | |
485 | |
486 class ShrinkWrapWidth extends OneChildRenderObjectWrapper { | |
487 | |
488 ShrinkWrapWidth({ UINode child, Object key }) : super(child: child, key: key); | |
489 | |
490 RenderShrinkWrapWidth get root { RenderShrinkWrapWidth result = super.root; re
turn result; } | |
491 | |
492 RenderShrinkWrapWidth createNode() => new RenderShrinkWrapWidth(); | |
493 | |
494 } | |
495 | |
496 class Transform extends OneChildRenderObjectWrapper { | |
497 | |
498 Transform({ this.transform, UINode child, Object key }) | |
499 : super(child: child, key: key); | |
500 | |
501 RenderTransform get root { RenderTransform result = super.root; return result;
} | |
502 final Matrix4 transform; | |
503 | |
504 RenderTransform createNode() => new RenderTransform(transform: transform); | |
505 | |
506 void syncRenderObject(Transform old) { | |
507 super.syncRenderObject(old); | |
508 root.transform = transform; | |
509 } | |
510 | |
511 } | |
512 | |
513 class SizeObserver extends OneChildRenderObjectWrapper { | |
514 | |
515 SizeObserver({ this.callback, UINode child, Object key }) | |
516 : super(child: child, key: key); | |
517 | |
518 RenderSizeObserver get root { RenderSizeObserver result = super.root; return r
esult; } | |
519 final SizeChangedCallback callback; | |
520 | |
521 RenderSizeObserver createNode() => new RenderSizeObserver(callback: callback); | |
522 | |
523 void syncRenderObject(SizeObserver old) { | |
524 super.syncRenderObject(old); | |
525 root.callback = callback; | |
526 } | |
527 | |
528 void remove() { | |
529 root.callback = null; | |
530 super.remove(); | |
531 } | |
532 | |
533 } | |
534 | |
535 // TODO(jackson) need a mechanism for marking the RenderCustomPaint as needing p
aint | |
536 class CustomPaint extends OneChildRenderObjectWrapper { | |
537 | |
538 CustomPaint({ this.callback, UINode child, Object key }) | |
539 : super(child: child, key: key); | |
540 | |
541 RenderCustomPaint get root { RenderCustomPaint result = super.root; return res
ult; } | |
542 final CustomPaintCallback callback; | |
543 | |
544 RenderCustomPaint createNode() => new RenderCustomPaint(callback: callback); | |
545 | |
546 void syncRenderObject(CustomPaint old) { | |
547 super.syncRenderObject(old); | |
548 root.callback = callback; | |
549 } | |
550 | |
551 void remove() { | |
552 root.callback = null; | |
553 super.remove(); | |
554 } | |
555 | |
556 } | |
557 | |
558 abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper { | 562 abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper { |
559 | 563 |
560 // In MultiChildRenderObjectWrapper subclasses, slots are RenderObject nodes | 564 // In MultiChildRenderObjectWrapper subclasses, slots are RenderObject nodes |
561 // to use as the "insert before" sibling in ContainerRenderObjectMixin.add() c
alls | 565 // to use as the "insert before" sibling in ContainerRenderObjectMixin.add() c
alls |
562 | 566 |
563 MultiChildRenderObjectWrapper({ | 567 MultiChildRenderObjectWrapper({ |
564 Object key, | 568 Object key, |
565 List<UINode> children | 569 List<UINode> children |
566 }) : this.children = children == null ? const [] : children, | 570 }) : this.children = children == null ? const [] : children, |
567 super(key: key) { | 571 super(key: key) { |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
733 oldNode = oldChildren[oldStartIndex]; | 737 oldNode = oldChildren[oldStartIndex]; |
734 removeChild(oldNode); | 738 removeChild(oldNode); |
735 advanceOldStartIndex(); | 739 advanceOldStartIndex(); |
736 } | 740 } |
737 | 741 |
738 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer | 742 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer |
739 } | 743 } |
740 | 744 |
741 } | 745 } |
742 | 746 |
743 class Block extends MultiChildRenderObjectWrapper { | |
744 | |
745 Block(List<UINode> children, { Object key }) | |
746 : super(key: key, children: children); | |
747 | |
748 RenderBlock get root { RenderBlock result = super.root; return result; } | |
749 RenderBlock createNode() => new RenderBlock(); | |
750 | |
751 } | |
752 | |
753 class Stack extends MultiChildRenderObjectWrapper { | |
754 | |
755 Stack(List<UINode> children, { Object key }) | |
756 : super(key: key, children: children); | |
757 | |
758 RenderStack get root { RenderStack result = super.root; return result; } | |
759 RenderStack createNode() => new RenderStack(); | |
760 | |
761 } | |
762 | |
763 class StackPositionedChild extends ParentDataNode { | |
764 StackPositionedChild(UINode content, { | |
765 double top, double right, double bottom, double left | |
766 }) : super(content, new StackParentData()..top = top | |
767 ..right = right | |
768 ..bottom = bottom | |
769 ..left = left); | |
770 } | |
771 | |
772 class Paragraph extends RenderObjectWrapper { | |
773 | |
774 Paragraph({ Object key, this.text }) : super(key: key); | |
775 | |
776 RenderParagraph get root { RenderParagraph result = super.root; return result;
} | |
777 RenderParagraph createNode() => new RenderParagraph(text: text); | |
778 | |
779 final String text; | |
780 | |
781 void syncRenderObject(UINode old) { | |
782 super.syncRenderObject(old); | |
783 root.text = text; | |
784 } | |
785 | |
786 void insert(RenderObjectWrapper child, dynamic slot) { | |
787 assert(false); | |
788 // Paragraph does not support having children currently | |
789 } | |
790 | |
791 } | |
792 | |
793 class Text extends Component { | |
794 Text(this.data) : super(key: '*text*'); | |
795 final String data; | |
796 bool get interchangeable => true; | |
797 UINode build() => new Paragraph(text: data); | |
798 } | |
799 | |
800 class Flex extends MultiChildRenderObjectWrapper { | |
801 | |
802 Flex(List<UINode> children, { | |
803 Object key, | |
804 this.direction: FlexDirection.horizontal, | |
805 this.justifyContent: FlexJustifyContent.flexStart, | |
806 this.alignItems: FlexAlignItems.center | |
807 }) : super(key: key, children: children); | |
808 | |
809 RenderFlex get root { RenderFlex result = super.root; return result; } | |
810 RenderFlex createNode() => new RenderFlex(direction: this.direction); | |
811 | |
812 final FlexDirection direction; | |
813 final FlexJustifyContent justifyContent; | |
814 final FlexAlignItems alignItems; | |
815 | |
816 void syncRenderObject(UINode old) { | |
817 super.syncRenderObject(old); | |
818 root.direction = direction; | |
819 root.justifyContent = justifyContent; | |
820 root.alignItems = alignItems; | |
821 } | |
822 | |
823 } | |
824 | |
825 class FlexExpandingChild extends ParentDataNode { | |
826 FlexExpandingChild(UINode content, { int flex: 1, Object key }) | |
827 : super(content, new FlexBoxParentData()..flex = flex, key: key); | |
828 } | |
829 | |
830 class Image extends RenderObjectWrapper { | |
831 | |
832 Image({ | |
833 Object key, | |
834 this.src, | |
835 this.size | |
836 }) : super(key: key); | |
837 | |
838 RenderImage get root { RenderImage result = super.root; return result; } | |
839 RenderImage createNode() => new RenderImage(this.src, this.size); | |
840 | |
841 final String src; | |
842 final Size size; | |
843 | |
844 void syncRenderObject(UINode old) { | |
845 super.syncRenderObject(old); | |
846 root.src = src; | |
847 root.requestedSize = size; | |
848 } | |
849 | |
850 void insert(RenderObjectWrapper child, dynamic slot) { | |
851 assert(false); | |
852 // Image does not support having children currently | |
853 } | |
854 | |
855 } | |
856 | |
857 Set<Component> _dirtyComponents = new Set<Component>(); | |
858 bool _buildScheduled = false; | |
859 bool _inRenderDirtyComponents = false; | |
860 | |
861 void _buildDirtyComponents() { | |
862 //_tracing.begin('fn::_buildDirtyComponents'); | |
863 | |
864 Stopwatch sw; | |
865 if (_shouldLogRenderDuration) | |
866 sw = new Stopwatch()..start(); | |
867 | |
868 try { | |
869 _inRenderDirtyComponents = true; | |
870 | |
871 List<Component> sortedDirtyComponents = _dirtyComponents.toList(); | |
872 sortedDirtyComponents.sort((Component a, Component b) => a._order - b._order
); | |
873 for (var comp in sortedDirtyComponents) { | |
874 comp._buildIfDirty(); | |
875 } | |
876 | |
877 _dirtyComponents.clear(); | |
878 _buildScheduled = false; | |
879 } finally { | |
880 _inRenderDirtyComponents = false; | |
881 } | |
882 | |
883 UINode._notifyMountStatusChanged(); | |
884 | |
885 if (_shouldLogRenderDuration) { | |
886 sw.stop(); | |
887 print('Render took ${sw.elapsedMicroseconds} microseconds'); | |
888 } | |
889 | |
890 //_tracing.end('fn::_buildDirtyComponents'); | |
891 } | |
892 | |
893 void _scheduleComponentForRender(Component c) { | |
894 assert(!_inRenderDirtyComponents); | |
895 _dirtyComponents.add(c); | |
896 | |
897 if (!_buildScheduled) { | |
898 _buildScheduled = true; | |
899 new Future.microtask(_buildDirtyComponents); | |
900 } | |
901 } | |
902 | |
903 abstract class Component extends UINode { | |
904 | |
905 Component({ Object key, bool stateful }) | |
906 : _stateful = stateful != null ? stateful : false, | |
907 _order = _currentOrder + 1, | |
908 super(key: key); | |
909 | |
910 Component.fromArgs(Object key, bool stateful) | |
911 : this(key: key, stateful: stateful); | |
912 | |
913 static Component _currentlyBuilding; | |
914 bool get _isBuilding => _currentlyBuilding == this; | |
915 | |
916 bool _stateful; | |
917 bool _dirty = true; | |
918 bool _disqualifiedFromEverAppearingAgain = false; | |
919 | |
920 UINode _built; | |
921 dynamic _slot; // cached slot from the last time we were synced | |
922 | |
923 void didMount() { | |
924 assert(!_disqualifiedFromEverAppearingAgain); | |
925 super.didMount(); | |
926 } | |
927 | |
928 void remove() { | |
929 assert(_built != null); | |
930 assert(root != null); | |
931 removeChild(_built); | |
932 _built = null; | |
933 super.remove(); | |
934 } | |
935 | |
936 bool _retainStatefulNodeIfPossible(UINode old) { | |
937 assert(!_disqualifiedFromEverAppearingAgain); | |
938 | |
939 Component oldComponent = old as Component; | |
940 if (oldComponent == null || !oldComponent._stateful) | |
941 return false; | |
942 | |
943 assert(key == oldComponent.key); | |
944 | |
945 // Make |this|, the newly-created object, into the "old" Component, and kill
it | |
946 _stateful = false; | |
947 _built = oldComponent._built; | |
948 assert(_built != null); | |
949 _disqualifiedFromEverAppearingAgain = true; | |
950 | |
951 // Make |oldComponent| the "new" component | |
952 oldComponent._built = null; | |
953 oldComponent._dirty = true; | |
954 oldComponent.syncFields(this); | |
955 return true; | |
956 } | |
957 | |
958 // This is called by _retainStatefulNodeIfPossible(), during | |
959 // syncChild(), just before _sync() is called. | |
960 // This must be implemented on any subclass that can become stateful | |
961 // (but don't call super.syncFields() if you inherit directly from | |
962 // Component, since that'll fire an assert). | |
963 // If you don't ever become stateful, then don't override this. | |
964 void syncFields(Component source) { | |
965 assert(false); | |
966 } | |
967 | |
968 final int _order; | |
969 static int _currentOrder = 0; | |
970 | |
971 /* There are three cases here: | |
972 * 1) Building for the first time: | |
973 * assert(_built == null && old == null) | |
974 * 2) Re-building (because a dirty flag got set): | |
975 * assert(_built != null && old == null) | |
976 * 3) Syncing against an old version | |
977 * assert(_built == null && old != null) | |
978 */ | |
979 void _sync(UINode old, dynamic slot) { | |
980 assert(_built == null || old == null); | |
981 assert(!_disqualifiedFromEverAppearingAgain); | |
982 | |
983 Component oldComponent = old as Component; | |
984 | |
985 _slot = slot; | |
986 | |
987 var oldBuilt; | |
988 if (oldComponent == null) { | |
989 oldBuilt = _built; | |
990 } else { | |
991 assert(_built == null); | |
992 oldBuilt = oldComponent._built; | |
993 } | |
994 | |
995 int lastOrder = _currentOrder; | |
996 _currentOrder = _order; | |
997 _currentlyBuilding = this; | |
998 _built = build(); | |
999 assert(_built != null); | |
1000 _currentlyBuilding = null; | |
1001 _currentOrder = lastOrder; | |
1002 | |
1003 _built = syncChild(_built, oldBuilt, slot); | |
1004 assert(_built != null); | |
1005 _dirty = false; | |
1006 _root = _built.root; | |
1007 assert(_root == root); // in case a subclass reintroduces it | |
1008 assert(root != null); | |
1009 } | |
1010 | |
1011 void _buildIfDirty() { | |
1012 assert(!_disqualifiedFromEverAppearingAgain); | |
1013 if (!_dirty || !_mounted) | |
1014 return; | |
1015 | |
1016 assert(root != null); | |
1017 _sync(null, _slot); | |
1018 } | |
1019 | |
1020 void scheduleBuild() { | |
1021 setState(() {}); | |
1022 } | |
1023 | |
1024 void setState(Function fn()) { | |
1025 assert(!_disqualifiedFromEverAppearingAgain); | |
1026 _stateful = true; | |
1027 fn(); | |
1028 if (_isBuilding || _dirty || !_mounted) | |
1029 return; | |
1030 | |
1031 _dirty = true; | |
1032 _scheduleComponentForRender(this); | |
1033 } | |
1034 | |
1035 UINode build(); | |
1036 | |
1037 } | |
1038 | |
1039 class Container extends Component { | |
1040 | |
1041 Container({ | |
1042 Object key, | |
1043 this.child, | |
1044 this.constraints, | |
1045 this.decoration, | |
1046 this.width, | |
1047 this.height, | |
1048 this.margin, | |
1049 this.padding, | |
1050 this.transform | |
1051 }) : super(key: key); | |
1052 | |
1053 final UINode child; | |
1054 final BoxConstraints constraints; | |
1055 final BoxDecoration decoration; | |
1056 final EdgeDims margin; | |
1057 final EdgeDims padding; | |
1058 final Matrix4 transform; | |
1059 final double width; | |
1060 final double height; | |
1061 | |
1062 UINode build() { | |
1063 UINode current = child; | |
1064 | |
1065 if (child == null && width == null && height == null) | |
1066 current = new SizedBox(); | |
1067 | |
1068 if (padding != null) | |
1069 current = new Padding(padding: padding, child: current); | |
1070 | |
1071 if (decoration != null) | |
1072 current = new DecoratedBox(decoration: decoration, child: current); | |
1073 | |
1074 if (width != null || height != null) | |
1075 current = new SizedBox( | |
1076 width: width == null ? double.INFINITY : width, | |
1077 height: height == null ? double.INFINITY : height, | |
1078 child: current | |
1079 ); | |
1080 | |
1081 if (constraints != null) | |
1082 current = new ConstrainedBox(constraints: constraints, child: current); | |
1083 | |
1084 if (margin != null) | |
1085 current = new Padding(padding: margin, child: current); | |
1086 | |
1087 if (transform != null) | |
1088 current = new Transform(transform: transform, child: current); | |
1089 | |
1090 return current; | |
1091 } | |
1092 | |
1093 } | |
1094 | 747 |
1095 class UINodeAppView extends AppView { | 748 class UINodeAppView extends AppView { |
1096 | 749 |
1097 UINodeAppView() { | 750 UINodeAppView() { |
1098 assert(_appView == null); | 751 assert(_appView == null); |
1099 } | 752 } |
1100 | 753 |
1101 static UINodeAppView _appView; | 754 static UINodeAppView _appView; |
1102 static void initUINodeAppView() { | 755 static void initUINodeAppView() { |
1103 if (_appView == null) | 756 if (_appView == null) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1157 // we haven't attached it yet | 810 // we haven't attached it yet |
1158 UINodeAppView._appView.root = root; | 811 UINodeAppView._appView.root = root; |
1159 } | 812 } |
1160 assert(root.parent is RenderView); | 813 assert(root.parent is RenderView); |
1161 } | 814 } |
1162 | 815 |
1163 } | 816 } |
1164 | 817 |
1165 typedef UINode Builder(); | 818 typedef UINode Builder(); |
1166 | 819 |
1167 class RenderNodeToUINodeAdapter extends AbstractUINodeRoot { | 820 class RenderObjectToUINodeAdapter extends AbstractUINodeRoot { |
1168 | 821 |
1169 RenderNodeToUINodeAdapter( | 822 RenderObjectToUINodeAdapter( |
1170 RenderObjectWithChildMixin<RenderBox> container, | 823 RenderObjectWithChildMixin<RenderBox> container, |
1171 this.builder | 824 this.builder |
1172 ) : _container = container { | 825 ) : _container = container { |
1173 assert(builder != null); | 826 assert(builder != null); |
1174 } | 827 } |
1175 | 828 |
1176 RenderObjectWithChildMixin<RenderBox> _container; | 829 RenderObjectWithChildMixin<RenderBox> _container; |
1177 RenderObjectWithChildMixin<RenderBox> get container => _container; | 830 RenderObjectWithChildMixin<RenderBox> get container => _container; |
1178 void set container(RenderObjectWithChildMixin<RenderBox> value) { | 831 void set container(RenderObjectWithChildMixin<RenderBox> value) { |
1179 if (_container != value) { | 832 if (_container != value) { |
(...skipping 18 matching lines...) Expand all Loading... |
1198 // we haven't attached it yet | 851 // we haven't attached it yet |
1199 assert(_container.child == null); | 852 assert(_container.child == null); |
1200 _container.child = root; | 853 _container.child = root; |
1201 } | 854 } |
1202 assert(root.parent == _container); | 855 assert(root.parent == _container); |
1203 } | 856 } |
1204 | 857 |
1205 UINode build() => builder(); | 858 UINode build() => builder(); |
1206 | 859 |
1207 } | 860 } |
OLD | NEW |