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:collection'; | |
| 6 | |
| 5 import '../rendering/block.dart'; | 7 import '../rendering/block.dart'; |
| 6 import '../rendering/box.dart'; | 8 import '../rendering/box.dart'; |
| 7 import '../rendering/object.dart'; | 9 import '../rendering/object.dart'; |
| 8 import 'widget.dart'; | 10 import 'widget.dart'; |
| 9 | 11 |
| 10 // return null if index is greater than index of last entry | 12 // return null if index is greater than index of last entry |
| 11 typedef Widget IndexedBuilder(int index); | 13 typedef Widget IndexedBuilder(int index); |
| 12 | 14 |
| 15 typedef void LayoutChangedCallback( | |
| 16 int firstVisibleWidgetIndex, | |
| 17 int visibleWidgetCount, | |
| 18 UnmodifiableListView<double> widgetOffsets, | |
| 19 bool lastWidgetReached); | |
|
Hixie
2015/07/10 22:51:38
paren on its own line
hansmuller
2015/07/10 23:42:05
Done.
| |
| 20 | |
| 13 class _Key { | 21 class _Key { |
| 14 const _Key(this.type, this.key); | 22 const _Key(this.type, this.key); |
| 15 factory _Key.fromWidget(Widget widget) => new _Key(widget.runtimeType, widget. key); | 23 factory _Key.fromWidget(Widget widget) => new _Key(widget.runtimeType, widget. key); |
| 16 final Type type; | 24 final Type type; |
| 17 final String key; | 25 final String key; |
| 18 bool operator ==(other) => other is _Key && other.type == type && other.key == key; | 26 bool operator ==(other) => other is _Key && other.type == type && other.key == key; |
| 19 int get hashCode => 373 * 37 * type.hashCode + key.hashCode; | 27 int get hashCode => 373 * 37 * type.hashCode + key.hashCode; |
| 20 } | 28 } |
| 21 | 29 |
| 22 class BlockViewport extends RenderObjectWrapper { | 30 class BlockViewport extends RenderObjectWrapper { |
| 23 BlockViewport({ this.builder, this.startOffset, this.token, String key }) | 31 BlockViewport({ this.builder, this.startOffset, this.token, this.onLayoutChang ed, String key }) |
| 24 : super(key: key); | 32 : super(key: key); |
| 25 | 33 |
| 26 IndexedBuilder builder; | 34 IndexedBuilder builder; |
| 27 double startOffset; | 35 double startOffset; |
| 28 Object token; | 36 Object token; |
| 37 LayoutChangedCallback onLayoutChanged; | |
| 29 | 38 |
| 30 RenderBlockViewport get root => super.root; | 39 RenderBlockViewport get root => super.root; |
| 31 RenderBlockViewport createNode() => new RenderBlockViewport(); | 40 RenderBlockViewport createNode() => new RenderBlockViewport(); |
| 32 | 41 |
| 33 Map<_Key, Widget> _childrenByKey = new Map<_Key, Widget>(); | 42 Map<_Key, Widget> _childrenByKey = new Map<_Key, Widget>(); |
| 34 | 43 |
| 35 void walkChildren(WidgetTreeWalker walker) { | 44 void walkChildren(WidgetTreeWalker walker) { |
| 36 for (Widget child in _childrenByKey.values) | 45 for (Widget child in _childrenByKey.values) |
| 37 walker(child); | 46 walker(child); |
| 38 } | 47 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 76 super.didUnmount(); | 85 super.didUnmount(); |
| 77 } | 86 } |
| 78 | 87 |
| 79 // _offsets contains the offsets of each child from the top of the | 88 // _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 | 89 // 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 | 90 // end of the last one. If there's no children, then the only offset |
| 82 // is 0.0. | 91 // is 0.0. |
| 83 List<double> _offsets = <double>[0.0]; | 92 List<double> _offsets = <double>[0.0]; |
| 84 int _currentStartIndex = 0; | 93 int _currentStartIndex = 0; |
| 85 int _currentChildCount = 0; | 94 int _currentChildCount = 0; |
| 95 bool _lastWidgetReached = false; | |
|
Hixie
2015/07/10 22:51:37
lastWidgetReached would store a Widget. If you wan
hansmuller
2015/07/10 23:42:05
I've changed all the fooWidget names I added to fo
| |
| 86 | 96 |
| 87 int _findIndexForOffsetBeforeOrAt(double offset) { | 97 int _findIndexForOffsetBeforeOrAt(double offset) { |
| 88 int left = 0; | 98 int left = 0; |
| 89 int right = _offsets.length - 1; | 99 int right = _offsets.length - 1; |
| 90 while (right >= left) { | 100 while (right >= left) { |
| 91 int middle = left + ((right - left) ~/ 2); | 101 int middle = left + ((right - left) ~/ 2); |
| 92 if (_offsets[middle] < offset) { | 102 if (_offsets[middle] < offset) { |
| 93 left = middle + 1; | 103 left = middle + 1; |
| 94 } else if (_offsets[middle] > offset) { | 104 } else if (_offsets[middle] > offset) { |
| 95 right = middle - 1; | 105 right = middle - 1; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 106 retainStatefulRenderObjectWrapper(newNode); | 116 retainStatefulRenderObjectWrapper(newNode); |
| 107 if (startOffset != newNode.startOffset) { | 117 if (startOffset != newNode.startOffset) { |
| 108 _dirty = true; | 118 _dirty = true; |
| 109 startOffset = newNode.startOffset; | 119 startOffset = newNode.startOffset; |
| 110 } | 120 } |
| 111 if (token != newNode.token || builder != newNode.builder) { | 121 if (token != newNode.token || builder != newNode.builder) { |
| 112 _dirty = true; | 122 _dirty = true; |
| 113 builder = newNode.builder; | 123 builder = newNode.builder; |
| 114 token = newNode.token; | 124 token = newNode.token; |
| 115 _offsets = <double>[0.0]; | 125 _offsets = <double>[0.0]; |
| 126 _lastWidgetReached = false; | |
| 116 } | 127 } |
| 117 return true; | 128 return true; |
| 118 } | 129 } |
| 119 | 130 |
| 120 void syncRenderObject(BlockViewport old) { | 131 void syncRenderObject(BlockViewport old) { |
| 121 super.syncRenderObject(old); | 132 super.syncRenderObject(old); |
| 122 if (_dirty) { | 133 if (_dirty) { |
| 123 root.markNeedsLayout(); | 134 root.markNeedsLayout(); |
| 124 } else { | 135 } else { |
| 125 if (_currentChildCount > 0) { | 136 if (_currentChildCount > 0) { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 188 startIndex = 0; | 199 startIndex = 0; |
| 189 if (_offsets.length > 1) { | 200 if (_offsets.length > 1) { |
| 190 haveChildren = true; | 201 haveChildren = true; |
| 191 } else { | 202 } else { |
| 192 Widget widget = _getWidget(startIndex, innerConstraints); | 203 Widget widget = _getWidget(startIndex, innerConstraints); |
| 193 if (widget != null) { | 204 if (widget != null) { |
| 194 newChildren[new _Key.fromWidget(widget)] = widget; | 205 newChildren[new _Key.fromWidget(widget)] = widget; |
| 195 builtChildren[startIndex] = widget; | 206 builtChildren[startIndex] = widget; |
| 196 haveChildren = true; | 207 haveChildren = true; |
| 197 } else { | 208 } else { |
| 198 haveChildren = false; | 209 haveChildren = false; |
|
Hixie
2015/07/10 22:51:37
i think this also means we've reached the end, no?
hansmuller
2015/07/10 23:42:05
Yes.
| |
| 199 } | 210 } |
| 200 } | 211 } |
| 201 } else { | 212 } else { |
| 202 startIndex = _findIndexForOffsetBeforeOrAt(startOffset); | 213 startIndex = _findIndexForOffsetBeforeOrAt(startOffset); |
| 203 if (startIndex == _offsets.length - 1) { | 214 if (startIndex == _offsets.length - 1) { |
| 204 // We don't have an offset on the list that is beyond the start offset. | 215 // We don't have an offset on the list that is beyond the start offset. |
| 205 assert(_offsets.last <= startOffset); | 216 assert(_offsets.last <= startOffset); |
| 206 // Fill the list until this isn't true or until we know that the | 217 // Fill the list until this isn't true or until we know that the |
| 207 // list is complete (and thus we are overscrolled). | 218 // list is complete (and thus we are overscrolled). |
| 208 while (true) { | 219 while (true) { |
| 209 Widget widget = _getWidget(startIndex, innerConstraints); | 220 Widget widget = _getWidget(startIndex, innerConstraints); |
| 210 if (widget == null) | 221 if (widget == null) |
| 211 break; | 222 break; |
|
Hixie
2015/07/10 22:51:37
i think you have to also flag the end here?
hansmuller
2015/07/10 23:42:05
Oops, yes.
| |
| 212 _Key widgetKey = new _Key.fromWidget(widget); | 223 _Key widgetKey = new _Key.fromWidget(widget); |
| 213 if (_offsets.last > startOffset) { | 224 if (_offsets.last > startOffset) { |
| 214 newChildren[widgetKey] = widget; | 225 newChildren[widgetKey] = widget; |
| 215 builtChildren[startIndex] = widget; | 226 builtChildren[startIndex] = widget; |
| 216 break; | 227 break; |
| 217 } | 228 } |
| 218 if (!_childrenByKey.containsKey(widgetKey)) { | 229 if (!_childrenByKey.containsKey(widgetKey)) { |
| 219 // we don't actually need this one, release it | 230 // we don't actually need this one, release it |
| 220 syncChild(null, widget, null); | 231 syncChild(null, widget, null); |
| 221 } // else we'll get rid of it later, when we remove old children | 232 } // else we'll get rid of it later, when we remove old children |
| 222 startIndex += 1; | 233 startIndex += 1; |
| 223 assert(startIndex == _offsets.length - 1); | 234 assert(startIndex == _offsets.length - 1); |
| 224 } | 235 } |
| 225 if (_offsets.last > startOffset) { | 236 if (_offsets.last > startOffset) { |
| 226 // If we're here, we have at least one child, so our list has | 237 // If we're here, we have at least one child, so our list has |
| 227 // at least two offsets, the top of the child and the bottom | 238 // at least two offsets, the top of the child and the bottom |
| 228 // of the child. | 239 // of the child. |
| 229 assert(_offsets.length >= 2); | 240 assert(_offsets.length >= 2); |
| 230 assert(startIndex == _offsets.length - 2); | 241 assert(startIndex == _offsets.length - 2); |
| 231 haveChildren = true; | 242 haveChildren = true; |
| 232 } else { | 243 } else { |
| 233 // If we're here, there are no children to show. | 244 // If we're here, there are no children to show. |
| 234 haveChildren = false; | 245 haveChildren = false; |
| 235 } | 246 } |
| 236 } else { | 247 } else { |
| 237 haveChildren = true; | 248 haveChildren = true; |
| 238 } | 249 } |
| 239 } | 250 } |
| 240 assert(haveChildren != null); | 251 assert(haveChildren != null); |
|
Hixie
2015/07/10 22:51:37
assert that if haveChildren is false, that we've a
hansmuller
2015/07/10 23:42:06
Done.
| |
| 241 | 252 |
| 242 assert(startIndex >= 0); | 253 assert(startIndex >= 0); |
| 243 assert(startIndex < _offsets.length); | 254 assert(startIndex < _offsets.length); |
| 244 | 255 |
| 245 int index = startIndex; | 256 int index = startIndex; |
| 246 if (haveChildren) { | 257 if (haveChildren) { |
| 247 // Build all the widgets we need. | 258 // Build all the widgets we need. |
| 248 root.startOffset = _offsets[index] - startOffset; | 259 root.startOffset = _offsets[index] - startOffset; |
| 249 while (_offsets[index] < endOffset) { | 260 while (_offsets[index] < endOffset) { |
| 250 if (!builtChildren.containsKey(index)) { | 261 if (!builtChildren.containsKey(index)) { |
| 251 Widget widget = _getWidget(index, innerConstraints); | 262 Widget widget = _getWidget(index, innerConstraints); |
| 252 if (widget == null) | 263 if (widget == null) { |
| 253 break; // reached the end of the list | 264 _lastWidgetReached = true; |
| 265 break; | |
| 266 } | |
| 254 newChildren[new _Key.fromWidget(widget)] = widget; | 267 newChildren[new _Key.fromWidget(widget)] = widget; |
| 255 builtChildren[index] = widget; | 268 builtChildren[index] = widget; |
| 256 } | 269 } |
| 257 assert(builtChildren[index] != null); | 270 assert(builtChildren[index] != null); |
| 258 index += 1; | 271 index += 1; |
| 259 } | 272 } |
| 260 } | 273 } |
| 261 | 274 |
| 262 // Remove any old children. | 275 // Remove any old children. |
| 263 for (_Key oldChildKey in _childrenByKey.keys) { | 276 for (_Key oldChildKey in _childrenByKey.keys) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 280 root.add(widget.root, before: nextSibling); | 293 root.add(widget.root, before: nextSibling); |
| 281 } | 294 } |
| 282 widget.updateSlot(nextSibling); | 295 widget.updateSlot(nextSibling); |
| 283 nextSibling = widget.root; | 296 nextSibling = widget.root; |
| 284 } | 297 } |
| 285 } | 298 } |
| 286 | 299 |
| 287 _childrenByKey = newChildren; | 300 _childrenByKey = newChildren; |
| 288 _currentStartIndex = startIndex; | 301 _currentStartIndex = startIndex; |
| 289 _currentChildCount = _childrenByKey.length; | 302 _currentChildCount = _childrenByKey.length; |
| 303 | |
| 304 if (onLayoutChanged != null) { | |
| 305 onLayoutChanged( | |
| 306 _currentStartIndex, | |
|
Hixie
2015/07/10 22:51:38
this should be indented with two spaces.
hansmuller
2015/07/10 23:42:05
Done.
| |
| 307 _currentChildCount, | |
| 308 new UnmodifiableListView<double>(_offsets), | |
| 309 _lastWidgetReached); | |
|
Hixie
2015/07/10 22:51:37
closing paren should be on its own line.
hansmuller
2015/07/10 23:42:05
Done.
| |
| 310 } | |
| 290 } | 311 } |
| 291 | 312 |
| 292 } | 313 } |
| OLD | NEW |