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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sky/sdk/lib/rendering/block.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sky/sdk/lib/widgets/block_viewport.dart
diff --git a/sky/sdk/lib/widgets/block_viewport.dart b/sky/sdk/lib/widgets/block_viewport.dart
new file mode 100644
index 0000000000000000000000000000000000000000..9b5395f798465d4420303ee1a34d43f63d36b722
--- /dev/null
+++ b/sky/sdk/lib/widgets/block_viewport.dart
@@ -0,0 +1,266 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '../rendering/block.dart';
+import '../rendering/box.dart';
+import '../rendering/object.dart';
+import 'widget.dart';
+
+// return null if index is greater than index of last entry
+typedef Widget IndexedBuilder(int index);
+
+class _Key {
+ const _Key(this.type, this.key);
+ factory _Key.fromWidget(Widget widget) => new _Key(widget.runtimeType, widget.key);
+ final Type type;
+ final String key;
+ bool operator ==(other) => other is _Key && other.type == type && other.key == key;
+ int get hashCode => 373 * 37 * type.hashCode + key.hashCode;
+}
+
+class BlockViewport extends RenderObjectWrapper {
+ BlockViewport({ this.builder, this.startOffset, this.token, String key })
+ : super(key: key);
+
+ IndexedBuilder builder;
+ double startOffset;
+ Object token;
+
+ RenderBlockViewport get root => super.root;
+ RenderBlockViewport createNode() => new RenderBlockViewport();
+
+ Map<_Key, Widget> _childrenByKey = new Map<_Key, Widget>();
+
+ void walkChildren(WidgetTreeWalker walker) {
+ for (Widget child in _childrenByKey.values)
+ walker(child);
+ }
+
+ static const _omit = const Object(); // used as a slot when it's not yet time to attach the child
+
+ void insertChildRoot(RenderObjectWrapper child, dynamic slot) {
+ if (slot == _omit)
+ return;
+ final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer
+ assert(slot == null || slot is RenderObject);
+ assert(root is ContainerRenderObjectMixin);
+ root.add(child.root, before: slot);
+ assert(root == this.root); // TODO(ianh): Remove this once the analyzer is cleverer
+ }
+
+ void detachChildRoot(RenderObjectWrapper child) {
+ final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer
+ assert(root is ContainerRenderObjectMixin);
+ if (child.root.parent != root)
+ return; // probably had slot == _omit when inserted
+ root.remove(child.root);
+ assert(root == this.root); // TODO(ianh): Remove this once the analyzer is cleverer
+ }
+
+ void remove() {
+ for (Widget child in _childrenByKey.values) {
+ assert(child != null);
+ removeChild(child);
+ }
+ super.remove();
+ }
+
+ void didMount() {
+ root.callback = layout;
+ super.didMount();
+ }
+
+ void didUnmount() {
+ root.callback = null;
+ super.didUnmount();
+ }
+
+ // _offsets contains the offsets of each child from the top of the
+ // list up to the last one we've ever created, and the offset of the
+ // end of the last one. If there's no children, then the only offset
+ // is 0.0.
+ List<double> _offsets = <double>[0.0];
+
+ int _findIndexForOffsetBeforeOrAt(double offset) {
+ int left = 0;
+ int right = _offsets.length - 1;
+ while (right >= left) {
+ int middle = left + ((right - left) ~/ 2);
+ if (_offsets[middle] < offset) {
+ left = middle + 1;
+ } else if (_offsets[middle] > offset) {
+ right = middle - 1;
+ } else {
+ return middle;
+ }
+ }
+ return right;
+ }
+
+ bool _dirty = false;
+
+ bool retainStatefulNodeIfPossible(BlockViewport newNode) {
+ retainStatefulRenderObjectWrapper(newNode);
+ if (startOffset != newNode.startOffset) {
+ _dirty = true;
+ startOffset = newNode.startOffset;
+ }
+ if (token != newNode.token || builder != newNode.builder) {
+ _dirty = true;
+ builder = newNode.builder;
+ token = newNode.token;
+ _offsets = <double>[0.0];
+ }
+ return true;
+ }
+
+ void syncRenderObject(BlockViewport old) {
+ super.syncRenderObject(old);
+ if (_dirty)
+ root.markNeedsLayout();
+ }
+
+ Widget _getWidget(int index, BoxConstraints innerConstraints) {
+ LayoutCallbackBuilderHandle handle = enterLayoutCallbackBuilder();
+ try {
+ assert(index >= 0);
+ Widget widget = builder == null ? null : builder(index);
+ if (widget == null)
+ return null;
+ assert(widget.key != null); // items in lists must have keys
+ final _Key key = new _Key.fromWidget(widget);
+ Widget oldWidget = _childrenByKey[key];
+ widget = syncChild(widget, oldWidget, _omit);
+ if (oldWidget != null)
+ _childrenByKey[key] = widget;
+ if (index >= _offsets.length - 1) {
+ assert(index == _offsets.length - 1);
+ final double widgetStartOffset = _offsets[index];
+ RenderBox widgetRoot = widget.root;
+ assert(widgetRoot is RenderBox);
+ final double widgetEndOffset = widgetStartOffset + widgetRoot.getMaxIntrinsicHeight(innerConstraints);
+ _offsets.add(widgetEndOffset);
+ }
+ return widget;
+ } finally {
+ exitLayoutCallbackBuilder(handle);
+ }
+ }
+
+ void layout(BoxConstraints constraints) {
+ _dirty = false;
+
+ Map<_Key, Widget> newChildren = new Map<_Key, Widget>();
+ Map<int, Widget> builtChildren = new Map<int, Widget>();
+
+ final double height = root.size.height;
+ final double endOffset = startOffset + height;
+ BoxConstraints innerConstraints = new BoxConstraints.tightFor(width: constraints.constrainWidth());
+
+ int startIndex;
+ bool haveChildren;
+ if (startOffset <= 0.0) {
+ startIndex = 0;
+ if (_offsets.length > 1) {
+ haveChildren = true;
+ } else {
+ Widget widget = _getWidget(startIndex, innerConstraints);
+ if (widget != null) {
+ newChildren[new _Key.fromWidget(widget)] = widget;
+ builtChildren[startIndex] = widget;
+ haveChildren = true;
+ } else {
+ haveChildren = false;
+ }
+ }
+ } else {
+ startIndex = _findIndexForOffsetBeforeOrAt(startOffset);
+ if (startIndex == _offsets.length - 1) {
+ // We don't have an offset on the list that is beyond the start offset.
+ assert(_offsets.last <= startOffset);
+ // Fill the list until this isn't true or until we know that the
+ // list is complete (and thus we are overscrolled).
+ while (true) {
+ Widget widget = _getWidget(startIndex, innerConstraints);
+ if (widget == null)
+ break;
+ _Key widgetKey = new _Key.fromWidget(widget);
+ if (_offsets.last > startOffset) {
+ newChildren[widgetKey] = widget;
+ builtChildren[startIndex] = widget;
+ break;
+ }
+ if (!_childrenByKey.containsKey(widgetKey)) {
+ // we don't actually need this one, release it
+ syncChild(null, widget, null);
+ } // else we'll get rid of it later, when we remove old children
+ startIndex += 1;
+ assert(startIndex == _offsets.length - 1);
+ }
+ if (_offsets.last > startOffset) {
+ // If we're here, we have at least one child, so our list has
+ // at least two offsets, the top of the child and the bottom
+ // of the child.
+ assert(_offsets.length >= 2);
+ assert(startIndex == _offsets.length - 2);
+ haveChildren = true;
+ } else {
+ // If we're here, there are no children to show.
+ haveChildren = false;
+ }
+ } else {
+ haveChildren = true;
+ }
+ }
+ assert(haveChildren != null);
+
+ assert(startIndex >= 0);
+ assert(startIndex < _offsets.length);
+
+ int index = startIndex;
+ if (haveChildren) {
+ // Build all the widgets we need.
+ root.startOffset = _offsets[index] - startOffset;
+ while (_offsets[index] < endOffset) {
+ if (!builtChildren.containsKey(index)) {
+ Widget widget = _getWidget(index, innerConstraints);
+ if (widget == null)
+ break; // reached the end of the list
+ newChildren[new _Key.fromWidget(widget)] = widget;
+ builtChildren[index] = widget;
+ }
+ assert(builtChildren[index] != null);
+ index += 1;
+ }
+ }
+
+ // Remove any old children.
+ for (_Key oldChildKey in _childrenByKey.keys) {
+ if (!newChildren.containsKey(oldChildKey))
+ syncChild(null, _childrenByKey[oldChildKey], null); // calls detachChildRoot()
+ }
+
+ if (haveChildren) {
+ // Place all our children in our RenderObject.
+ // All the children we are placing are in builtChildren and newChildren.
+ // We will walk them backwards so we can set the siblings at the same time.
+ RenderBox nextSibling = null;
+ while (index > startIndex) {
+ index -= 1;
+ Widget widget = builtChildren[index];
+ if (widget.root.parent == root) {
+ root.move(widget.root, before: nextSibling);
+ } else {
+ assert(widget.root.parent == null);
+ root.add(widget.root, before: nextSibling);
+ }
+ widget.updateSlot(nextSibling);
+ nextSibling = widget.root;
+ }
+ }
+
+ _childrenByKey = newChildren;
+ }
+
+}
« 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