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

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
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;
19 int get hashCode => 373 * 37 * type.hashCode + key.hashCode;
20 }
21
22 class BlockViewport extends RenderObjectWrapper {
23 BlockViewport({ IndexedBuilder this.builder, this.startOffset, this.token, Str ing key })
abarth-chromium 2015/07/08 21:56:20 Is IndexedBuilder needed here?
24 : super(key: key);
25
26 IndexedBuilder builder;
27 double startOffset;
28 Object token;
abarth-chromium 2015/07/08 21:56:20 final?
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
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) {
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;
abarth-chromium 2015/07/08 21:56:20 We usually put spaces around binary operators like
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 }
abarth-chromium 2015/07/08 21:56:20 Seems like a function we should factor out to some
100
101 void syncRenderObject(BlockViewport old) {
102 super.syncRenderObject(old);
103 root.markNeedsLayout();
abarth-chromium 2015/07/08 21:56:20 Do we need to do a layout every time we sync? I w
104 }
105
106 Widget _getWidget(int index, BoxConstraints innerConstraints) {
107 LayoutCallbackBuilderHandle handle = enterLayoutCallbackBuilder();
108 try {
109 assert(index >= 0);
110 Widget widget = builder == null ? null : builder(index);
111 if (widget == null)
112 return null;
113 assert(widget.key != null); // items in lists must have keys
114 final _Key key = new _Key.fromWidget(widget);
115 Widget oldWidget = _childrenByKey[key];
116 widget = syncChild(widget, oldWidget, omit);
117 if (oldWidget != null)
118 _childrenByKey[key] = widget;
119 if (index >= _offsets.length-1) {
120 assert(index == _offsets.length-1);
121 final double widgetStartOffset = _offsets[index];
122 RenderBox widgetRoot = widget.root;
123 assert(widgetRoot is RenderBox);
124 final double widgetEndOffset = widgetStartOffset + widgetRoot.getMaxIntr insicHeight(innerConstraints);
125 _offsets.add(widgetEndOffset);
126 }
127 return widget;
128 } finally {
129 exitLayoutCallbackBuilder(handle);
130 }
131 }
132
133 void layout(BoxConstraints constraints) {
134 Map<_Key, Widget> newChildren = new Map<_Key, Widget>();
135 Map<int, Widget> builtChildren = new Map<int, Widget>();
136
137 double height = root.size.height;
138 double endOffset = startOffset + height;
139 BoxConstraints innerConstraints = new BoxConstraints.tightFor(width: constra ints.maxWidth);
abarth-chromium 2015/07/08 21:56:20 There's a bug here if constraints.minWidth is grea
140
141 int startIndex;
142 bool haveChildren;
143 if (startOffset <= 0.0) {
144 startIndex = 0;
145 if (_offsets.length > 1) {
146 haveChildren = true;
147 } else {
148 Widget widget = _getWidget(startIndex, innerConstraints);
149 if (widget != null) {
150 newChildren[new _Key.fromWidget(widget)] = widget;
151 builtChildren[startIndex] = widget;
152 haveChildren = true;
153 } else {
154 haveChildren = false;
155 }
156 }
157 } else {
158 startIndex = _findIndexForOffsetBeforeOrAt(startOffset);
159 if (startIndex == _offsets.length-1) {
160 // We don't have an offset on the list that is beyond the start offset.
161 assert(_offsets.last <= startOffset);
162 // Fill the list until this isn't true or until we know that the
163 // list is complete (and thus we are overscrolled).
164 while (true) {
165 Widget widget = _getWidget(startIndex, innerConstraints);
166 if (widget == null)
167 break;
168 _Key widgetKey = new _Key.fromWidget(widget);
169 if (_offsets.last > startOffset) {
170 newChildren[widgetKey] = widget;
171 builtChildren[startIndex] = widget;
172 break;
173 }
174 if (!_childrenByKey.containsKey(widgetKey)) {
175 // we don't actually need this one, release it
176 syncChild(null, widget, null);
177 } // else we'll get rid of it later, when we remove old children
178 startIndex += 1;
179 assert(startIndex == _offsets.length-1);
180 }
181 if (_offsets.last > startOffset) {
182 // If we're here, we have at least one child, so our list has
183 // at least two offsets, the top of the child and the bottom
184 // of the child.
185 assert(_offsets.length >= 2);
186 assert(startIndex == _offsets.length-2);
187 haveChildren = true;
188 } else {
189 // If we're here, there are no children to show.
190 haveChildren = false;
191 }
192 } else {
193 haveChildren = true;
194 }
195 }
196 assert(haveChildren != null);
197
198 assert(startIndex >= 0);
199 assert(startIndex < _offsets.length);
200
201 int index = startIndex;
202 if (haveChildren) {
203 // Build all the widgets we need.
204 root.startOffset = _offsets[index]-startOffset;
205 while (_offsets[index] < endOffset) {
206 if (!builtChildren.containsKey(index)) {
207 Widget widget = _getWidget(index, innerConstraints);
208 if (widget == null)
209 break; // reached the end of the list
210 newChildren[new _Key.fromWidget(widget)] = widget;
211 builtChildren[index] = widget;
212 }
213 assert(builtChildren[index] != null);
214 index += 1;
215 }
216 }
217
218 // Remove any old children.
219 for (_Key oldChildKey in _childrenByKey.keys) {
220 if (!newChildren.containsKey(oldChildKey))
221 syncChild(null, _childrenByKey[oldChildKey], null); // calls detachChild Root()
222 }
223
224 if (haveChildren) {
225 // Place all our children in our RenderObject.
226 // All the children we are placing are in builtChildren and newChildren.
227 // We will walk them backwards so we can set the siblings at the same time .
228 RenderBox nextSibling = null;
229 while (index > startIndex) {
230 index -= 1;
231 Widget widget = builtChildren[index];
232 if (widget.root.parent == root) {
233 root.move(widget.root, before: nextSibling);
234 } else {
235 assert(widget.root.parent == null);
236 root.add(widget.root, before: nextSibling);
237 }
238 widget.updateSlot(nextSibling);
239 nextSibling = widget.root;
240 }
241 }
242
243 _childrenByKey = newChildren;
244 }
245
246 bool retainStatefulNodeIfPossible(BlockViewport newNode) {
247 doRetainStatefulNode(newNode);
248 startOffset = newNode.startOffset;
249 if (token != newNode.token || builder != newNode.builder) {
250 builder = newNode.builder;
251 token = newNode.token;
252 _offsets = <double>[0.0];
253 }
254 return true;
255 }
256
257 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698