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