| 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 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |