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