Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(113)

Side by Side Diff: sky/sdk/lib/widgets/widget.dart

Issue 1225123005: Teach Widget some new tricks to enable build-during-layout. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:collection'; 6 import 'dart:collection';
7 import 'dart:sky' as sky; 7 import 'dart:sky' as sky;
8 8
9 import 'package:sky/mojo/activity.dart' as activity; 9 import 'package:sky/mojo/activity.dart' as activity;
10 10
11 import '../base/debug.dart';
11 import '../base/hit_test.dart'; 12 import '../base/hit_test.dart';
12 import '../rendering/box.dart'; 13 import '../rendering/box.dart';
13 import '../rendering/object.dart'; 14 import '../rendering/object.dart';
14 import '../rendering/sky_binding.dart'; 15 import '../rendering/sky_binding.dart';
15 16
16 export '../rendering/box.dart' show BoxConstraints, BoxDecoration, Border, Borde rSide, EdgeDims; 17 export '../rendering/box.dart' show BoxConstraints, BoxDecoration, Border, Borde rSide, EdgeDims;
17 export '../rendering/flex.dart' show FlexDirection; 18 export '../rendering/flex.dart' show FlexDirection;
18 export '../rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path; 19 export '../rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
19 20
20 final bool _shouldLogRenderDuration = false; 21 final bool _shouldLogRenderDuration = false;
21 22
23 typedef Widget Builder();
22 typedef void WidgetTreeWalker(Widget); 24 typedef void WidgetTreeWalker(Widget);
23 25
24 /// A base class for elements of the widget tree 26 /// A base class for elements of the widget tree
25 abstract class Widget { 27 abstract class Widget {
26 28
27 Widget({ String key }) : _key = key { 29 Widget({ String key }) : _key = key {
28 assert(_isConstructedDuringBuild()); 30 assert(_isConstructedDuringBuild());
29 } 31 }
30 32
31 // TODO(jackson): Remove this workaround for limitation of Dart mixins 33 // TODO(jackson): Remove this workaround for limitation of Dart mixins
32 Widget._withKey(String key) : _key = key { 34 Widget._withKey(String key) : _key = key {
33 assert(_isConstructedDuringBuild()); 35 assert(_isConstructedDuringBuild());
34 } 36 }
35 37
36 // you should not build the UI tree ahead of time, build it only during build( ) 38 // you should not build the UI tree ahead of time, build it only during build( )
37 bool _isConstructedDuringBuild() => this is AbstractWidgetRoot || this is App || _inRenderDirtyComponents; 39 bool _isConstructedDuringBuild() => this is AbstractWidgetRoot || this is App || _inRenderDirtyComponents || _inLayoutCallbackBuilder > 0;
38 40
39 String _key; 41 String _key;
40 42
41 /// A semantic identifer for this widget 43 /// A semantic identifer for this widget
42 /// 44 ///
43 /// Keys are used to find matches when synchronizing two widget trees, for 45 /// Keys are used to find matches when synchronizing two widget trees, for
44 /// example after a [Component] rebuilds. Without keys, two widgets can match 46 /// example after a [Component] rebuilds. Without keys, two widgets can match
45 /// if their runtimeType matches. With keys, the keys must match as well. 47 /// if their runtimeType matches. With keys, the keys must match as well.
46 /// Assigning a key to a widget can improve performance by causing the 48 /// Assigning a key to a widget can improve performance by causing the
47 /// framework to sync widgets that share a lot of common structure and can 49 /// framework to sync widgets that share a lot of common structure and can
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 115
114 /// Override this function to learn when this [Widget] leaves the widget tree. 116 /// Override this function to learn when this [Widget] leaves the widget tree.
115 void didUnmount() { } 117 void didUnmount() { }
116 118
117 RenderObject _root; 119 RenderObject _root;
118 120
119 /// The underlying [RenderObject] associated with this [Widget]. 121 /// The underlying [RenderObject] associated with this [Widget].
120 RenderObject get root => _root; 122 RenderObject get root => _root;
121 123
122 // Subclasses which implements Nodes that become stateful may return true 124 // Subclasses which implements Nodes that become stateful may return true
123 // if the |old| node has become stateful and should be retained. 125 // if the node has become stateful and should be retained.
124 // This is called immediately before _sync(). 126 // This is called immediately before _sync().
125 // Component._retainStatefulNodeIfPossible() calls syncFields(). 127 // Component.retainStatefulNodeIfPossible() calls syncFields().
126 bool _retainStatefulNodeIfPossible(Widget old) => false; 128 bool retainStatefulNodeIfPossible(Widget newNode) => false;
127 129
128 void _sync(Widget old, dynamic slot); 130 void _sync(Widget old, dynamic slot);
129 void updateSlot(dynamic newSlot); 131 void updateSlot(dynamic newSlot);
130 // 'slot' is the identifier that the ancestor RenderObjectWrapper uses to know 132 // 'slot' is the identifier that the ancestor RenderObjectWrapper uses to know
131 // where to put this descendant. If you just defer to a child, then make sure 133 // where to put this descendant. If you just defer to a child, then make sure
132 // to pass them the slot. 134 // to pass them the slot.
133 135
134 Widget findAncestorRenderObjectWrapper() { 136 Widget findAncestorRenderObjectWrapper() {
135 var ancestor = _parent; 137 var ancestor = _parent;
136 while (ancestor != null && ancestor is! RenderObjectWrapper) 138 while (ancestor != null && ancestor is! RenderObjectWrapper)
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 // the child in this slot has gone away 174 // the child in this slot has gone away
173 assert(oldNode.mounted); 175 assert(oldNode.mounted);
174 oldNode.detachRoot(); 176 oldNode.detachRoot();
175 removeChild(oldNode); 177 removeChild(oldNode);
176 assert(!oldNode.mounted); 178 assert(!oldNode.mounted);
177 return null; 179 return null;
178 } 180 }
179 181
180 if (oldNode != null) { 182 if (oldNode != null) {
181 if (oldNode.runtimeType == newNode.runtimeType && oldNode.key == newNode.k ey) { 183 if (oldNode.runtimeType == newNode.runtimeType && oldNode.key == newNode.k ey) {
182 if (newNode._retainStatefulNodeIfPossible(oldNode)) { 184 if (oldNode.retainStatefulNodeIfPossible(newNode)) {
183 assert(oldNode.mounted); 185 assert(oldNode.mounted);
184 assert(!newNode.mounted); 186 assert(!newNode.mounted);
185 oldNode.setParent(this); 187 oldNode.setParent(this);
186 oldNode._sync(newNode, slot); 188 oldNode._sync(newNode, slot);
187 assert(oldNode.root is RenderObject); 189 assert(oldNode.root is RenderObject);
188 return oldNode; 190 return oldNode;
189 } else { 191 } else {
190 oldNode.setParent(null); 192 oldNode.setParent(null);
191 } 193 }
192 } else { 194 } else {
(...skipping 19 matching lines...) Expand all
212 if (children.length > 0) { 214 if (children.length > 0) {
213 Widget lastChild = children.removeLast(); 215 Widget lastChild = children.removeLast();
214 String nextStartPrefix = prefix + ' +-'; 216 String nextStartPrefix = prefix + ' +-';
215 String nextPrefix = prefix + ' | '; 217 String nextPrefix = prefix + ' | ';
216 for (Widget child in children) 218 for (Widget child in children)
217 childrenString += child.toString(nextPrefix, nextStartPrefix); 219 childrenString += child.toString(nextPrefix, nextStartPrefix);
218 String lastStartPrefix = prefix + ' \'-'; 220 String lastStartPrefix = prefix + ' \'-';
219 String lastPrefix = prefix + ' '; 221 String lastPrefix = prefix + ' ';
220 childrenString += lastChild.toString(lastPrefix, lastStartPrefix); 222 childrenString += lastChild.toString(lastPrefix, lastStartPrefix);
221 } 223 }
224 return '$startPrefix${toStringName()}\n$childrenString';
225 }
226 String toStringName() {
222 if (key == null) 227 if (key == null)
223 return '$startPrefix$runtimeType(unkeyed)\n$childrenString'; 228 return '$runtimeType(unkeyed; hashCode=$hashCode)';
224 return '$startPrefix$runtimeType("$key")\n$childrenString'; 229 return '$runtimeType("$key"; hashCode=$hashCode)';
225 } 230 }
226 231
227 } 232 }
228 233
229 234
230 // Descendants of TagNode provide a way to tag RenderObjectWrapper and 235 // Descendants of TagNode provide a way to tag RenderObjectWrapper and
231 // Component nodes with annotations, such as event listeners, 236 // Component nodes with annotations, such as event listeners,
232 // stylistic information, etc. 237 // stylistic information, etc.
233 abstract class TagNode extends Widget { 238 abstract class TagNode extends Widget {
234 239
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after
530 void _sync(Widget old, dynamic slot) { 535 void _sync(Widget old, dynamic slot) {
531 assert(!_disqualifiedFromEverAppearingAgain); 536 assert(!_disqualifiedFromEverAppearingAgain);
532 super._sync(old, slot); 537 super._sync(old, slot);
533 } 538 }
534 539
535 Widget syncChild(Widget node, Widget oldNode, dynamic slot) { 540 Widget syncChild(Widget node, Widget oldNode, dynamic slot) {
536 assert(!_disqualifiedFromEverAppearingAgain); 541 assert(!_disqualifiedFromEverAppearingAgain);
537 return super.syncChild(node, oldNode, slot); 542 return super.syncChild(node, oldNode, slot);
538 } 543 }
539 544
540 bool _retainStatefulNodeIfPossible(StatefulComponent old) { 545 bool retainStatefulNodeIfPossible(StatefulComponent newNode) {
541 assert(!_disqualifiedFromEverAppearingAgain); 546 assert(!_disqualifiedFromEverAppearingAgain);
547 assert(newNode != null);
548 assert(runtimeType == newNode.runtimeType);
549 assert(key == newNode.key);
550 assert(_built != null);
551 newNode._disqualifiedFromEverAppearingAgain = true;
542 552
543 if (old == null) 553 newNode._built = _built;
544 return false; 554 _built = null;
545 555
546 assert(runtimeType == old.runtimeType); 556 syncFields(newNode);
547 assert(key == old.key); 557 _dirty = true;
548 558
549 // Make |this|, the newly-created object, into the "old" Component, and kill it
550 _built = old._built;
551 assert(_built != null);
552 _disqualifiedFromEverAppearingAgain = true;
553
554 // Make |old| the "new" component
555 old._built = null;
556 old._dirty = true;
557 old.syncFields(this);
558 return true; 559 return true;
559 } 560 }
560 561
561 // This is called by _retainStatefulNodeIfPossible(), during 562 // This is called by retainStatefulNodeIfPossible(), during
562 // syncChild(), just before _sync() is called. Derived 563 // syncChild(), just before _sync() is called. Derived
563 // classes should override this method to update `this` to 564 // classes should override this method to update `this` to
564 // account for the new values the parent passed to `source`. 565 // account for the new values the parent passed to `source`.
565 // Make sure to call super.syncFields(source) unless you are 566 // Make sure to call super.syncFields(source) unless you are
566 // extending StatefulComponent directly. 567 // extending StatefulComponent directly.
567 void syncFields(Component source); 568 void syncFields(Component source);
568 569
569 void setState(Function fn()) { 570 void setState(Function fn()) {
570 assert(!_disqualifiedFromEverAppearingAgain); 571 assert(!_disqualifiedFromEverAppearingAgain);
571 fn(); 572 fn();
572 scheduleBuild(); 573 scheduleBuild();
573 } 574 }
574 } 575 }
575 576
576 Set<Component> _dirtyComponents = new Set<Component>(); 577 Set<Component> _dirtyComponents = new Set<Component>();
577 bool _buildScheduled = false; 578 bool _buildScheduled = false;
578 bool _inRenderDirtyComponents = false; 579 bool _inRenderDirtyComponents = false;
580 int _inLayoutCallbackBuilder = 0;
581
582 class LayoutCallbackBuilderHandle { bool _active = true; }
583 LayoutCallbackBuilderHandle enterLayoutCallbackBuilder() {
584 if (!inDebugBuild)
585 return null;
586 _inLayoutCallbackBuilder += 1;
587 return new LayoutCallbackBuilderHandle();
588 }
589 void exitLayoutCallbackBuilder(LayoutCallbackBuilderHandle handle) {
590 if (!inDebugBuild)
591 return;
592 assert(handle._active);
593 handle._active = false;
594 _inLayoutCallbackBuilder -= 1;
595 }
579 596
580 List<int> _debugFrameTimes = <int>[]; 597 List<int> _debugFrameTimes = <int>[];
581 598
582 void _absorbDirtyComponents(List<Component> list) { 599 void _absorbDirtyComponents(List<Component> list) {
583 list.addAll(_dirtyComponents); 600 list.addAll(_dirtyComponents);
584 _dirtyComponents.clear(); 601 _dirtyComponents.clear();
585 list.sort((Component a, Component b) => a._order - b._order); 602 list.sort((Component a, Component b) => a._order - b._order);
586 } 603 }
587 604
588 void _buildDirtyComponents() { 605 void _buildDirtyComponents() {
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
649 RenderObject createNode(); 666 RenderObject createNode();
650 667
651 static final Map<RenderObject, RenderObjectWrapper> _nodeMap = 668 static final Map<RenderObject, RenderObjectWrapper> _nodeMap =
652 new HashMap<RenderObject, RenderObjectWrapper>(); 669 new HashMap<RenderObject, RenderObjectWrapper>();
653 static RenderObjectWrapper _getMounted(RenderObject node) => _nodeMap[node]; 670 static RenderObjectWrapper _getMounted(RenderObject node) => _nodeMap[node];
654 671
655 RenderObjectWrapper _ancestor; 672 RenderObjectWrapper _ancestor;
656 void insertChildRoot(RenderObjectWrapper child, dynamic slot); 673 void insertChildRoot(RenderObjectWrapper child, dynamic slot);
657 void detachChildRoot(RenderObjectWrapper child); 674 void detachChildRoot(RenderObjectWrapper child);
658 675
676 void retainStatefulRenderObjectWrapper(RenderObjectWrapper newNode) {
677 newNode._root = _root;
678 newNode._ancestor = _ancestor;
679 }
680
659 void _sync(RenderObjectWrapper old, dynamic slot) { 681 void _sync(RenderObjectWrapper old, dynamic slot) {
660 // TODO(abarth): We should split RenderObjectWrapper into two pieces so that 682 // TODO(abarth): We should split RenderObjectWrapper into two pieces so that
661 // RenderViewObject doesn't need to inherit all this code it 683 // RenderViewObject doesn't need to inherit all this code it
662 // doesn't need. 684 // doesn't need.
663 assert(parent != null || this is RenderViewWrapper); 685 assert(parent != null || this is RenderViewWrapper);
664 if (old == null) { 686 if (old == null) {
665 _root = createNode(); 687 _root = createNode();
688 assert(_root != null);
666 _ancestor = findAncestorRenderObjectWrapper(); 689 _ancestor = findAncestorRenderObjectWrapper();
667 if (_ancestor is RenderObjectWrapper) 690 if (_ancestor is RenderObjectWrapper)
668 _ancestor.insertChildRoot(this, slot); 691 _ancestor.insertChildRoot(this, slot);
669 } else { 692 } else {
670 assert(old is RenderObjectWrapper); 693 assert(old is RenderObjectWrapper);
671 _root = old.root; 694 _root = old.root;
672 _ancestor = old._ancestor; 695 _ancestor = old._ancestor;
696 assert(_root != null);
673 } 697 }
674 assert(_root == root); // in case a subclass reintroduces it 698 assert(_root == root); // in case a subclass reintroduces it
675 assert(root != null); 699 assert(root != null);
676 assert(mounted); 700 assert(mounted);
677 _nodeMap[root] = this; 701 _nodeMap[root] = this;
678 syncRenderObject(old); 702 syncRenderObject(old);
679 } 703 }
680 704
681 void updateSlot(dynamic newSlot) { 705 void updateSlot(dynamic newSlot) {
682 // We never use the slot except during sync(), in which 706 // We never use the slot except during sync(), in which
(...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after
1066 void runApp(App app, { RenderView renderViewOverride, bool enableProfilingLoop: false }) { 1090 void runApp(App app, { RenderView renderViewOverride, bool enableProfilingLoop: false }) {
1067 WidgetSkyBinding.initWidgetSkyBinding(renderViewOverride: renderViewOverride); 1091 WidgetSkyBinding.initWidgetSkyBinding(renderViewOverride: renderViewOverride);
1068 new AppContainer(app); 1092 new AppContainer(app);
1069 if (enableProfilingLoop) { 1093 if (enableProfilingLoop) {
1070 new Timer.periodic(const Duration(milliseconds: 20), (_) { 1094 new Timer.periodic(const Duration(milliseconds: 20), (_) {
1071 app.scheduleBuild(); 1095 app.scheduleBuild();
1072 }); 1096 });
1073 } 1097 }
1074 } 1098 }
1075 1099
1076 typedef Widget Builder();
1077
1078 class RenderBoxToWidgetAdapter extends AbstractWidgetRoot { 1100 class RenderBoxToWidgetAdapter extends AbstractWidgetRoot {
1079 1101
1080 RenderBoxToWidgetAdapter( 1102 RenderBoxToWidgetAdapter(
1081 RenderObjectWithChildMixin<RenderBox> container, 1103 RenderObjectWithChildMixin<RenderBox> container,
1082 this.builder 1104 this.builder
1083 ) : _container = container, super() { 1105 ) : _container = container, super() {
1084 assert(builder != null); 1106 assert(builder != null);
1085 } 1107 }
1086 1108
1087 RenderObjectWithChildMixin<RenderBox> _container; 1109 RenderObjectWithChildMixin<RenderBox> _container;
(...skipping 20 matching lines...) Expand all
1108 if (root.parent == null) { 1130 if (root.parent == null) {
1109 // we haven't attached it yet 1131 // we haven't attached it yet
1110 assert(_container.child == null); 1132 assert(_container.child == null);
1111 _container.child = root; 1133 _container.child = root;
1112 } 1134 }
1113 assert(root.parent == _container); 1135 assert(root.parent == _container);
1114 } 1136 }
1115 1137
1116 Widget build() => builder(); 1138 Widget build() => builder();
1117 } 1139 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698