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

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

Issue 1222913013: Introduce BlockViewport. (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 | « sky/sdk/lib/rendering/block.dart ('k') | 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
(Empty)
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
3 // found in the LICENSE file.
4
5 import '../rendering/block.dart';
6 import '../rendering/box.dart';
7 import '../rendering/object.dart';
8 import 'widget.dart';
9
10 // return null if index is greater than index of last entry
11 typedef Widget IndexedBuilder(int index);
12
13 class _Key {
14 const _Key(this.type, this.key);
15 factory _Key.fromWidget(Widget widget) => new _Key(widget.runtimeType, widget. key);
16 final Type type;
17 final String key;
18 bool operator ==(_Key other) => other is _Key && other.type == type && other.k ey == key;
abarth-chromium 2015/07/09 03:15:10 operator ==(_Key other) should be operator ==(ot
19 int get hashCode => 373 * 37 * type.hashCode + key.hashCode;
20 }
21
22 class BlockViewport extends RenderObjectWrapper {
23 BlockViewport({ this.builder, this.startOffset, this.token, String key })
24 : super(key: key);
25
26 IndexedBuilder builder;
27 double startOffset;
28 Object token;
29
30 RenderBlockViewport get root => super.root;
31 RenderBlockViewport createNode() => new RenderBlockViewport();
32
33 Map<_Key, Widget> _childrenByKey = new Map<_Key, Widget>();
34
35 void walkChildren(WidgetTreeWalker walker) {
36 for (Widget child in _childrenByKey.values)
37 walker(child);
38 }
39
40 static const omit = const Object(); // used as a slot when it's not yet time t o attach the child
abarth-chromium 2015/07/09 03:15:09 omit -> _omit Presumably we don't want other peop
41
42 void insertChildRoot(RenderObjectWrapper child, dynamic slot) {
43 if (slot == omit)
44 return;
45 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev erer
46 assert(slot == null || slot is RenderObject);
47 assert(root is ContainerRenderObjectMixin);
48 root.add(child.root, before: slot);
49 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c leverer
50 }
51
52 void detachChildRoot(RenderObjectWrapper child) {
53 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev erer
54 assert(root is ContainerRenderObjectMixin);
55 if (child.root.parent != root)
56 return; // probably had slot == omit when inserted
57 root.remove(child.root);
58 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c leverer
59 }
60
61 void remove() {
62 for (var child in _childrenByKey.values) {
abarth-chromium 2015/07/09 03:15:10 s/var/Widget/
63 assert(child != null);
64 removeChild(child);
65 }
66 super.remove();
67 }
68
69 void didMount() {
70 root.callback = layout;
71 super.didMount();
72 }
73
74 void didUnmount() {
75 root.callback = null;
76 super.didUnmount();
77 }
78
79 // _offsets contains the offsets of each child from the top of the
80 // list up to the last one we've ever created, and the offset of the
81 // end of the last one. If there's no children, then the only offset
82 // is 0.0.
83 List<double> _offsets = <double>[0.0];
84
85 int _findIndexForOffsetBeforeOrAt(double offset) {
86 int left = 0;
87 int right = _offsets.length - 1;
88 while (right >= left) {
89 int middle = left + ((right-left) ~/ 2);
90 if (_offsets[middle] < offset) {
91 left = middle + 1;
92 } else if (_offsets[middle] > offset) {
93 right = middle - 1;
94 } else {
95 return middle;
96 }
97 }
98 return right;
99 }
100
101 bool _dirty = false;
102
103 bool retainStatefulNodeIfPossible(BlockViewport newNode) {
104 retainStatefulRenderObjectWrapper(newNode);
105 if (startOffset != newNode.startOffset) {
106 _dirty = true;
107 startOffset = newNode.startOffset;
108 }
109 if (token != newNode.token || builder != newNode.builder) {
110 _dirty = true;
111 builder = newNode.builder;
112 token = newNode.token;
113 _offsets = <double>[0.0];
114 }
115 return true;
116 }
117
118 void syncRenderObject(BlockViewport old) {
119 super.syncRenderObject(old);
120 if (_dirty)
121 root.markNeedsLayout();
122 }
123
124 Widget _getWidget(int index, BoxConstraints innerConstraints) {
125 LayoutCallbackBuilderHandle handle = enterLayoutCallbackBuilder();
126 try {
127 assert(index >= 0);
128 Widget widget = builder == null ? null : builder(index);
129 if (widget == null)
130 return null;
131 assert(widget.key != null); // items in lists must have keys
132 final _Key key = new _Key.fromWidget(widget);
133 Widget oldWidget = _childrenByKey[key];
134 widget = syncChild(widget, oldWidget, omit);
135 if (oldWidget != null)
136 _childrenByKey[key] = widget;
137 if (index >= _offsets.length - 1) {
138 assert(index == _offsets.length - 1);
139 final double widgetStartOffset = _offsets[index];
140 RenderBox widgetRoot = widget.root;
141 assert(widgetRoot is RenderBox);
142 final double widgetEndOffset = widgetStartOffset + widgetRoot.getMaxIntr insicHeight(innerConstraints);
143 _offsets.add(widgetEndOffset);
144 }
145 return widget;
146 } finally {
147 exitLayoutCallbackBuilder(handle);
148 }
149 }
150
151 void layout(BoxConstraints constraints) {
152 _dirty = false;
153
154 Map<_Key, Widget> newChildren = new Map<_Key, Widget>();
155 Map<int, Widget> builtChildren = new Map<int, Widget>();
156
157 double height = root.size.height;
158 double endOffset = startOffset + height;
abarth-chromium 2015/07/09 03:15:09 I'd mark these as final. Normally I wouldn't both
159 BoxConstraints innerConstraints = new BoxConstraints.tightFor(width: constra ints.constrainWidth());
160
161 int startIndex;
162 bool haveChildren;
163 if (startOffset <= 0.0) {
164 startIndex = 0;
165 if (_offsets.length > 1) {
166 haveChildren = true;
167 } else {
168 Widget widget = _getWidget(startIndex, innerConstraints);
169 if (widget != null) {
170 newChildren[new _Key.fromWidget(widget)] = widget;
171 builtChildren[startIndex] = widget;
172 haveChildren = true;
173 } else {
174 haveChildren = false;
175 }
176 }
177 } else {
178 startIndex = _findIndexForOffsetBeforeOrAt(startOffset);
179 if (startIndex == _offsets.length - 1) {
180 // We don't have an offset on the list that is beyond the start offset.
181 assert(_offsets.last <= startOffset);
182 // Fill the list until this isn't true or until we know that the
183 // list is complete (and thus we are overscrolled).
184 while (true) {
185 Widget widget = _getWidget(startIndex, innerConstraints);
186 if (widget == null)
187 break;
188 _Key widgetKey = new _Key.fromWidget(widget);
189 if (_offsets.last > startOffset) {
190 newChildren[widgetKey] = widget;
191 builtChildren[startIndex] = widget;
192 break;
193 }
194 if (!_childrenByKey.containsKey(widgetKey)) {
195 // we don't actually need this one, release it
196 syncChild(null, widget, null);
197 } // else we'll get rid of it later, when we remove old children
198 startIndex += 1;
199 assert(startIndex == _offsets.length - 1);
200 }
201 if (_offsets.last > startOffset) {
202 // If we're here, we have at least one child, so our list has
203 // at least two offsets, the top of the child and the bottom
204 // of the child.
205 assert(_offsets.length >= 2);
206 assert(startIndex == _offsets.length-2);
207 haveChildren = true;
208 } else {
209 // If we're here, there are no children to show.
210 haveChildren = false;
211 }
212 } else {
213 haveChildren = true;
214 }
215 }
216 assert(haveChildren != null);
217
218 assert(startIndex >= 0);
219 assert(startIndex < _offsets.length);
220
221 int index = startIndex;
222 if (haveChildren) {
223 // Build all the widgets we need.
224 root.startOffset = _offsets[index]-startOffset;
abarth-chromium 2015/07/09 03:15:10 s/-/ - /
225 while (_offsets[index] < endOffset) {
226 if (!builtChildren.containsKey(index)) {
227 Widget widget = _getWidget(index, innerConstraints);
228 if (widget == null)
229 break; // reached the end of the list
230 newChildren[new _Key.fromWidget(widget)] = widget;
231 builtChildren[index] = widget;
232 }
233 assert(builtChildren[index] != null);
234 index += 1;
235 }
236 }
237
238 // Remove any old children.
239 for (_Key oldChildKey in _childrenByKey.keys) {
240 if (!newChildren.containsKey(oldChildKey))
241 syncChild(null, _childrenByKey[oldChildKey], null); // calls detachChild Root()
242 }
243
244 if (haveChildren) {
245 // Place all our children in our RenderObject.
246 // All the children we are placing are in builtChildren and newChildren.
247 // We will walk them backwards so we can set the siblings at the same time .
248 RenderBox nextSibling = null;
249 while (index > startIndex) {
250 index -= 1;
251 Widget widget = builtChildren[index];
252 if (widget.root.parent == root) {
253 root.move(widget.root, before: nextSibling);
254 } else {
255 assert(widget.root.parent == null);
256 root.add(widget.root, before: nextSibling);
257 }
258 widget.updateSlot(nextSibling);
259 nextSibling = widget.root;
260 }
261 }
262
263 _childrenByKey = newChildren;
264 }
265
266 }
OLDNEW
« no previous file with comments | « sky/sdk/lib/rendering/block.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698