Commit 37106ea6 authored by Adam Barth's avatar Adam Barth

Complete features of ScrollableList2

This patch implements the remaining missing features of ScrollableList2.
It should now be nearly a drop-in replacement for ScrollableList. The
next patch will switch callers over to the new machinery.
parent f72c8f6d
...@@ -30,22 +30,25 @@ class ScrollbarPainter extends ScrollableListPainter { ...@@ -30,22 +30,25 @@ class ScrollbarPainter extends ScrollableListPainter {
Point thumbOrigin; Point thumbOrigin;
Size thumbSize; Size thumbSize;
if (isVertical) { switch (scrollDirection) {
double thumbHeight = viewportBounds.height * viewportBounds.height / contentExtent; case ScrollDirection.vertical:
thumbHeight = thumbHeight.clamp(_kMinScrollbarThumbLength, viewportBounds.height); double thumbHeight = viewportBounds.height * viewportBounds.height / contentExtent;
final double maxThumbTop = viewportBounds.height - thumbHeight; thumbHeight = thumbHeight.clamp(_kMinScrollbarThumbLength, viewportBounds.height);
double thumbTop = (scrollOffset / (contentExtent - viewportBounds.height)) * maxThumbTop; final double maxThumbTop = viewportBounds.height - thumbHeight;
thumbTop = viewportBounds.top + thumbTop.clamp(0.0, maxThumbTop); double thumbTop = (scrollOffset / (contentExtent - viewportBounds.height)) * maxThumbTop;
thumbOrigin = new Point(viewportBounds.right - _kScrollbarThumbGirth, thumbTop); thumbTop = viewportBounds.top + thumbTop.clamp(0.0, maxThumbTop);
thumbSize = new Size(_kScrollbarThumbGirth, thumbHeight); thumbOrigin = new Point(viewportBounds.right - _kScrollbarThumbGirth, thumbTop);
} else { thumbSize = new Size(_kScrollbarThumbGirth, thumbHeight);
double thumbWidth = viewportBounds.width * viewportBounds.width / contentExtent; break;
thumbWidth = thumbWidth.clamp(_kMinScrollbarThumbLength, viewportBounds.width); case ScrollDirection.horizontal:
final double maxThumbLeft = viewportBounds.width - thumbWidth; double thumbWidth = viewportBounds.width * viewportBounds.width / contentExtent;
double thumbLeft = (scrollOffset / (contentExtent - viewportBounds.width)) * maxThumbLeft; thumbWidth = thumbWidth.clamp(_kMinScrollbarThumbLength, viewportBounds.width);
thumbLeft = viewportBounds.left + thumbLeft.clamp(0.0, maxThumbLeft); final double maxThumbLeft = viewportBounds.width - thumbWidth;
thumbOrigin = new Point(thumbLeft, viewportBounds.height - _kScrollbarThumbGirth); double thumbLeft = (scrollOffset / (contentExtent - viewportBounds.width)) * maxThumbLeft;
thumbSize = new Size(thumbWidth, _kScrollbarThumbGirth); thumbLeft = viewportBounds.left + thumbLeft.clamp(0.0, maxThumbLeft);
thumbOrigin = new Point(thumbLeft, viewportBounds.height - _kScrollbarThumbGirth);
thumbSize = new Size(thumbWidth, _kScrollbarThumbGirth);
break;
} }
paintThumb(context, thumbOrigin & thumbSize); paintThumb(context, thumbOrigin & thumbSize);
...@@ -65,7 +68,7 @@ class ScrollbarPainter extends ScrollableListPainter { ...@@ -65,7 +68,7 @@ class ScrollbarPainter extends ScrollableListPainter {
..variable = new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.ease) ..variable = new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.ease)
..addListener(() { ..addListener(() {
_opacity = _fade.value; _opacity = _fade.value;
renderer?.markNeedsPaint(); renderObject?.markNeedsPaint();
}); });
return _fade.forward(); return _fade.forward();
} }
......
...@@ -8,6 +8,7 @@ import 'package:vector_math/vector_math_64.dart'; ...@@ -8,6 +8,7 @@ import 'package:vector_math/vector_math_64.dart';
import 'box.dart'; import 'box.dart';
import 'object.dart'; import 'object.dart';
import 'viewport.dart';
/// Parent data for use with [RenderBlockBase]. /// Parent data for use with [RenderBlockBase].
class BlockParentData extends ContainerBoxParentDataMixin<RenderBox> { } class BlockParentData extends ContainerBoxParentDataMixin<RenderBox> { }
...@@ -32,8 +33,10 @@ typedef double _Constrainer(double value); ...@@ -32,8 +33,10 @@ typedef double _Constrainer(double value);
/// children. Because blocks expand in the main axis, blocks must be given /// children. Because blocks expand in the main axis, blocks must be given
/// unlimited space in the main axis, typically by being contained in a /// unlimited space in the main axis, typically by being contained in a
/// viewport with a scrolling direction that matches the block's main axis. /// viewport with a scrolling direction that matches the block's main axis.
abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin<RenderBox, BlockParentData>, abstract class RenderBlockBase extends RenderBox
RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData> { with ContainerRenderObjectMixin<RenderBox, BlockParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData>
implements RenderScrollable {
RenderBlockBase({ RenderBlockBase({
List<RenderBox> children, List<RenderBox> children,
...@@ -82,6 +85,9 @@ abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin ...@@ -82,6 +85,9 @@ abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin
/// Whether the main axis is vertical. /// Whether the main axis is vertical.
bool get isVertical => _direction == BlockDirection.vertical; bool get isVertical => _direction == BlockDirection.vertical;
// TODO(abarth): Remove BlockDirection in favor of ScrollDirection.
ScrollDirection get scrollDirection => isVertical ? ScrollDirection.vertical : ScrollDirection.horizontal;
BoxConstraints _getInnerConstraints(BoxConstraints constraints) { BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
if (isVertical) if (isVertical)
return new BoxConstraints.tightFor(width: constraints.constrainWidth(constraints.maxWidth), return new BoxConstraints.tightFor(width: constraints.constrainWidth(constraints.maxWidth),
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'box.dart'; import 'box.dart';
import 'object.dart'; import 'object.dart';
import 'viewport.dart'; import 'viewport.dart';
...@@ -9,18 +11,23 @@ import 'viewport.dart'; ...@@ -9,18 +11,23 @@ import 'viewport.dart';
/// Parent data for use with [RenderList]. /// Parent data for use with [RenderList].
class ListParentData extends ContainerBoxParentDataMixin<RenderBox> { } class ListParentData extends ContainerBoxParentDataMixin<RenderBox> { }
class RenderList extends RenderVirtualViewport<ListParentData> { class RenderList extends RenderVirtualViewport<ListParentData> implements RenderScrollable {
RenderList({ RenderList({
List<RenderBox> children, List<RenderBox> children,
double itemExtent, double itemExtent,
EdgeDims padding,
int virtualChildCount, int virtualChildCount,
Offset paintOffset: Offset.zero, Offset paintOffset: Offset.zero,
ScrollDirection scrollDirection: ScrollDirection.vertical,
LayoutCallback callback LayoutCallback callback
}) : _itemExtent = itemExtent, super( }) : _itemExtent = itemExtent,
virtualChildCount: virtualChildCount, _padding = padding,
paintOffset: paintOffset, _scrollDirection = scrollDirection,
callback: callback super(
) { virtualChildCount: virtualChildCount,
paintOffset: paintOffset,
callback: callback
) {
assert(itemExtent != null); assert(itemExtent != null);
addAll(children); addAll(children);
} }
...@@ -35,50 +42,124 @@ class RenderList extends RenderVirtualViewport<ListParentData> { ...@@ -35,50 +42,124 @@ class RenderList extends RenderVirtualViewport<ListParentData> {
markNeedsLayout(); markNeedsLayout();
} }
EdgeDims get padding => _padding;
EdgeDims _padding;
void set padding (EdgeDims newValue) {
if (_padding == newValue)
return;
_padding = newValue;
markNeedsLayout();
}
ScrollDirection get scrollDirection => _scrollDirection;
ScrollDirection _scrollDirection;
void set scrollDirection (ScrollDirection newValue) {
if (_scrollDirection == newValue)
return;
_scrollDirection = newValue;
markNeedsLayout();
}
void setupParentData(RenderBox child) { void setupParentData(RenderBox child) {
if (child.parentData is! ListParentData) if (child.parentData is! ListParentData)
child.parentData = new ListParentData(); child.parentData = new ListParentData();
} }
double get _preferredMainAxisExtent => itemExtent * virtualChildCount; double get _scrollAxisPadding {
switch (scrollDirection) {
case ScrollDirection.vertical:
return padding.vertical;
case ScrollDirection.horizontal:
return padding.horizontal;
}
}
double getMinIntrinsicWidth(BoxConstraints constraints) { double get _preferredExtent {
double extent = itemExtent * virtualChildCount;
if (padding != null)
extent += _scrollAxisPadding;
return extent;
}
double _getIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.isNormalized); assert(constraints.isNormalized);
return constraints.constrainWidth(0.0); switch (scrollDirection) {
case ScrollDirection.vertical:
return constraints.constrainWidth(0.0);
case ScrollDirection.horizontal:
return constraints.constrainWidth(_preferredExtent);
}
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
return _getIntrinsicWidth(constraints);
} }
double getMaxIntrinsicWidth(BoxConstraints constraints) { double getMaxIntrinsicWidth(BoxConstraints constraints) {
return _getIntrinsicWidth(constraints);
}
double _getIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.isNormalized); assert(constraints.isNormalized);
return constraints.constrainWidth(0.0); switch (scrollDirection) {
case ScrollDirection.vertical:
return constraints.constrainHeight(_preferredExtent);
case ScrollDirection.horizontal:
return constraints.constrainHeight(0.0);
}
} }
double getMinIntrinsicHeight(BoxConstraints constraints) { double getMinIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.isNormalized); return _getIntrinsicHeight(constraints);
return constraints.constrainHeight(_preferredMainAxisExtent);
} }
double getMaxIntrinsicHeight(BoxConstraints constraints) { double getMaxIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.isNormalized); return _getIntrinsicHeight(constraints);
return constraints.constrainHeight(_preferredMainAxisExtent);
} }
void performLayout() { void performLayout() {
double height = _preferredMainAxisExtent; size = new Size(constraints.maxWidth,
size = new Size(constraints.maxWidth, constraints.constrainHeight(height)); constraints.constrainHeight(_preferredExtent));
if (callback != null) if (callback != null)
invokeLayoutCallback(callback); invokeLayoutCallback(callback);
double itemWidth;
double itemHeight;
double x = 0.0;
double dx = 0.0;
double y = 0.0;
double dy = 0.0;
switch (scrollDirection) {
case ScrollDirection.vertical:
itemWidth = math.max(0, size.width - (padding == null ? 0.0 : padding.horizontal));
itemHeight = itemExtent;
y = padding != null ? padding.top : 0.0;
dy = itemExtent;
break;
case ScrollDirection.horizontal:
itemWidth = itemExtent;
itemHeight = math.max(0, size.height - (padding == null ? 0.0 : padding.vertical));
x = padding != null ? padding.left : 0.0;
dx = itemExtent;
break;
}
BoxConstraints innerConstraints = BoxConstraints innerConstraints =
new BoxConstraints.tightFor(width: size.width, height: itemExtent); new BoxConstraints.tightFor(width: itemWidth, height: itemHeight);
int childIndex = 0;
RenderBox child = firstChild; RenderBox child = firstChild;
while (child != null) { while (child != null) {
child.layout(innerConstraints); child.layout(innerConstraints);
final ListParentData childParentData = child.parentData; final ListParentData childParentData = child.parentData;
childParentData.offset = new Offset(0.0, childIndex * itemExtent); childParentData.offset = new Offset(x, y);
childIndex += 1; x += dx;
y += dy;
assert(child.parentData == childParentData); assert(child.parentData == childParentData);
child = childParentData.nextSibling; child = childParentData.nextSibling;
} }
......
...@@ -18,6 +18,10 @@ enum ScrollDirection { ...@@ -18,6 +18,10 @@ enum ScrollDirection {
vertical, vertical,
} }
abstract class RenderScrollable {
ScrollDirection get scrollDirection;
}
/// A render object that's bigger on the inside. /// A render object that's bigger on the inside.
/// ///
/// The child of a viewport can layout to a larger size than the viewport /// The child of a viewport can layout to a larger size than the viewport
...@@ -27,7 +31,8 @@ enum ScrollDirection { ...@@ -27,7 +31,8 @@ enum ScrollDirection {
/// ///
/// Viewport is the core scrolling primitive in the system, but it can be used /// Viewport is the core scrolling primitive in the system, but it can be used
/// in other situations. /// in other situations.
class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox> { class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox>
implements RenderScrollable {
RenderViewport({ RenderViewport({
RenderBox child, RenderBox child,
...@@ -177,10 +182,12 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende ...@@ -177,10 +182,12 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
RenderVirtualViewport({ RenderVirtualViewport({
int virtualChildCount, int virtualChildCount,
Offset paintOffset, Offset paintOffset,
LayoutCallback callback LayoutCallback callback,
Painter overlayPainter
}) : _virtualChildCount = virtualChildCount, }) : _virtualChildCount = virtualChildCount,
_paintOffset = paintOffset, _paintOffset = paintOffset,
_callback = callback; _callback = callback,
_overlayPainter = overlayPainter;
int get virtualChildCount => _virtualChildCount ?? childCount; int get virtualChildCount => _virtualChildCount ?? childCount;
int _virtualChildCount; int _virtualChildCount;
...@@ -217,8 +224,31 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende ...@@ -217,8 +224,31 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
markNeedsLayout(); markNeedsLayout();
} }
Painter get overlayPainter => _overlayPainter;
Painter _overlayPainter;
void set overlayPainter(Painter value) {
if (_overlayPainter == value)
return;
if (attached)
_overlayPainter?.detach();
_overlayPainter = value;
if (attached)
_overlayPainter?.attach(this);
markNeedsPaint();
}
void attach() {
super.attach();
_overlayPainter?.attach(this);
}
void detach() {
super.detach();
_overlayPainter?.detach();
}
void applyPaintTransform(RenderBox child, Matrix4 transform) { void applyPaintTransform(RenderBox child, Matrix4 transform) {
super.applyPaintTransform(child, transform.translate(paintOffset)); super.applyPaintTransform(child, transform.translate(paintOffset.dx, paintOffset.dy));
} }
bool hitTestChildren(HitTestResult result, { Point position }) { bool hitTestChildren(HitTestResult result, { Point position }) {
...@@ -227,6 +257,7 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende ...@@ -227,6 +257,7 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
void _paintContents(PaintingContext context, Offset offset) { void _paintContents(PaintingContext context, Offset offset) {
defaultPaint(context, offset + paintOffset); defaultPaint(context, offset + paintOffset);
_overlayPainter?.paint(context, offset);
} }
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
......
...@@ -445,15 +445,18 @@ class Block extends StatelessComponent { ...@@ -445,15 +445,18 @@ class Block extends StatelessComponent {
abstract class ScrollableListPainter extends Painter { abstract class ScrollableListPainter extends Painter {
void attach(RenderObject renderObject) { void attach(RenderObject renderObject) {
assert(renderObject is RenderBlockViewport); assert(renderObject is RenderBox);
assert(renderObject is RenderScrollable);
super.attach(renderObject); super.attach(renderObject);
} }
RenderBlockViewport get renderer => renderObject; RenderBox get renderObject => super.renderObject;
bool get isVertical => renderer.isVertical; ScrollDirection get scrollDirection {
return (renderObject as RenderScrollable)?.scrollDirection;
}
Size get viewportSize => renderer.size; Size get viewportSize => renderObject.size;
double get contentExtent => _contentExtent; double get contentExtent => _contentExtent;
double _contentExtent = 0.0; double _contentExtent = 0.0;
...@@ -463,7 +466,7 @@ abstract class ScrollableListPainter extends Painter { ...@@ -463,7 +466,7 @@ abstract class ScrollableListPainter extends Painter {
if (_contentExtent == value) if (_contentExtent == value)
return; return;
_contentExtent = value; _contentExtent = value;
renderer?.markNeedsPaint(); renderObject?.markNeedsPaint();
} }
double get scrollOffset => _scrollOffset; double get scrollOffset => _scrollOffset;
...@@ -473,7 +476,7 @@ abstract class ScrollableListPainter extends Painter { ...@@ -473,7 +476,7 @@ abstract class ScrollableListPainter extends Painter {
if (_scrollOffset == value) if (_scrollOffset == value)
return; return;
_scrollOffset = value; _scrollOffset = value;
renderer?.markNeedsPaint(); renderObject?.markNeedsPaint();
} }
/// Called when a scroll starts. Subclasses may override this method to /// Called when a scroll starts. Subclasses may override this method to
...@@ -675,7 +678,7 @@ class ScrollableList<T> extends ScrollableWidgetList { ...@@ -675,7 +678,7 @@ class ScrollableList<T> extends ScrollableWidgetList {
double snapAlignmentOffset: 0.0, double snapAlignmentOffset: 0.0,
this.items, this.items,
this.itemBuilder, this.itemBuilder,
itemsWrap: false, bool itemsWrap: false,
double itemExtent, double itemExtent,
EdgeDims padding, EdgeDims padding,
ScrollableListPainter scrollableListPainter ScrollableListPainter scrollableListPainter
......
...@@ -79,6 +79,9 @@ class GridViewport extends VirtualViewport { ...@@ -79,6 +79,9 @@ class GridViewport extends VirtualViewport {
final ExtentsChangedCallback onExtentsChanged; final ExtentsChangedCallback onExtentsChanged;
final List<Widget> children; final List<Widget> children;
// TODO(abarth): Support horizontal scrolling;
ScrollDirection get scrollDirection => ScrollDirection.vertical;
RenderGrid createRenderObject() => new RenderGrid(delegate: delegate); RenderGrid createRenderObject() => new RenderGrid(delegate: delegate);
_GridViewportElement createElement() => new _GridViewportElement(this); _GridViewportElement createElement() => new _GridViewportElement(this);
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'framework.dart'; import 'framework.dart';
import 'scrollable.dart'; import 'scrollable.dart';
import 'virtual_viewport.dart'; import 'virtual_viewport.dart';
...@@ -13,22 +15,30 @@ class ScrollableList2 extends Scrollable { ...@@ -13,22 +15,30 @@ class ScrollableList2 extends Scrollable {
ScrollableList2({ ScrollableList2({
Key key, Key key,
double initialScrollOffset, double initialScrollOffset,
ScrollDirection scrollDirection: ScrollDirection.vertical,
ScrollListener onScroll, ScrollListener onScroll,
SnapOffsetCallback snapOffsetCallback, SnapOffsetCallback snapOffsetCallback,
double snapAlignmentOffset: 0.0, double snapAlignmentOffset: 0.0,
this.itemExtent, this.itemExtent,
this.itemsWrap: false,
this.padding,
this.scrollableListPainter,
this.children this.children
}) : super( }) : super(
key: key, key: key,
initialScrollOffset: initialScrollOffset, initialScrollOffset: initialScrollOffset,
// TODO(abarth): Support horizontal offsets. scrollDirection: scrollDirection,
scrollDirection: ScrollDirection.vertical,
onScroll: onScroll, onScroll: onScroll,
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback,
snapAlignmentOffset: snapAlignmentOffset snapAlignmentOffset: snapAlignmentOffset
); ) {
assert(itemExtent != null);
}
final double itemExtent; final double itemExtent;
final bool itemsWrap;
final EdgeDims padding;
final ScrollableListPainter scrollableListPainter;
final List<Widget> children; final List<Widget> children;
ScrollableState createState() => new _ScrollableList2State(); ScrollableState createState() => new _ScrollableList2State();
...@@ -39,20 +49,40 @@ class _ScrollableList2State extends ScrollableState<ScrollableList2> { ...@@ -39,20 +49,40 @@ class _ScrollableList2State extends ScrollableState<ScrollableList2> {
ExtentScrollBehavior get scrollBehavior => super.scrollBehavior; ExtentScrollBehavior get scrollBehavior => super.scrollBehavior;
void _handleExtentsChanged(double contentExtent, double containerExtent) { void _handleExtentsChanged(double contentExtent, double containerExtent) {
config.scrollableListPainter?.contentExtent = contentExtent;
setState(() { setState(() {
scrollTo(scrollBehavior.updateExtents( scrollTo(scrollBehavior.updateExtents(
contentExtent: contentExtent, contentExtent: config.itemsWrap ? double.INFINITY : contentExtent,
containerExtent: containerExtent, containerExtent: containerExtent,
scrollOffset: scrollOffset scrollOffset: scrollOffset
)); ));
}); });
} }
void dispatchOnScrollStart() {
super.dispatchOnScrollStart();
config.scrollableListPainter?.scrollStarted();
}
void dispatchOnScroll() {
super.dispatchOnScroll();
config.scrollableListPainter?.scrollOffset = scrollOffset;
}
void dispatchOnScrollEnd() {
super.dispatchOnScrollEnd();
config.scrollableListPainter?.scrollEnded();
}
Widget buildContent(BuildContext context) { Widget buildContent(BuildContext context) {
return new ListViewport( return new ListViewport(
onExtentsChanged: _handleExtentsChanged,
startOffset: scrollOffset, startOffset: scrollOffset,
scrollDirection: config.scrollDirection,
itemExtent: config.itemExtent, itemExtent: config.itemExtent,
onExtentsChanged: _handleExtentsChanged, itemsWrap: config.itemsWrap,
padding: config.padding,
overlayPainter: config.scrollableListPainter,
children: config.children children: config.children
); );
} }
...@@ -61,15 +91,26 @@ class _ScrollableList2State extends ScrollableState<ScrollableList2> { ...@@ -61,15 +91,26 @@ class _ScrollableList2State extends ScrollableState<ScrollableList2> {
class ListViewport extends VirtualViewport { class ListViewport extends VirtualViewport {
ListViewport({ ListViewport({
Key key, Key key,
this.startOffset,
this.itemExtent,
this.onExtentsChanged, this.onExtentsChanged,
this.startOffset: 0.0,
this.scrollDirection: ScrollDirection.vertical,
this.itemExtent,
this.itemsWrap: false,
this.padding,
this.overlayPainter,
this.children this.children
}); }) {
assert(scrollDirection != null);
assert(itemExtent != null);
}
final ExtentsChangedCallback onExtentsChanged;
final double startOffset; final double startOffset;
final ScrollDirection scrollDirection;
final double itemExtent; final double itemExtent;
final ExtentsChangedCallback onExtentsChanged; final bool itemsWrap;
final EdgeDims padding;
final Painter overlayPainter;
final List<Widget> children; final List<Widget> children;
RenderList createRenderObject() => new RenderList(itemExtent: itemExtent); RenderList createRenderObject() => new RenderList(itemExtent: itemExtent);
...@@ -95,21 +136,39 @@ class _ListViewportElement extends VirtualViewportElement<ListViewport> { ...@@ -95,21 +136,39 @@ class _ListViewportElement extends VirtualViewportElement<ListViewport> {
double _repaintOffsetLimit; double _repaintOffsetLimit;
void updateRenderObject() { void updateRenderObject() {
renderObject.scrollDirection = widget.scrollDirection;
renderObject.itemExtent = widget.itemExtent; renderObject.itemExtent = widget.itemExtent;
renderObject.padding = widget.padding;
renderObject.overlayPainter = widget.overlayPainter;
super.updateRenderObject(); super.updateRenderObject();
} }
double _contentExtent; double _contentExtent;
double _containerExtent; double _containerExtent;
double _getContainerExtentFromRenderObject() {
switch (widget.scrollDirection) {
case ScrollDirection.vertical:
return renderObject.size.height;
case ScrollDirection.horizontal:
return renderObject.size.width;
}
}
void layout(BoxConstraints constraints) { void layout(BoxConstraints constraints) {
double contentExtent = widget.itemExtent * widget.children.length; double contentExtent = widget.itemExtent * widget.children.length;
double containerExtent = renderObject.size.height; double containerExtent = _getContainerExtentFromRenderObject();
_materializedChildBase = (widget.startOffset ~/ widget.itemExtent).clamp(0, widget.children.length); _materializedChildBase = math.max(0, widget.startOffset ~/ widget.itemExtent);
int materializedChildLimit = ((widget.startOffset + containerExtent) / widget.itemExtent).ceil().clamp(0, widget.children.length); int materializedChildLimit = math.max(0, ((widget.startOffset + containerExtent) / widget.itemExtent).ceil());
_materializedChildCount = materializedChildLimit - _materializedChildBase;
if (!widget.itemsWrap) {
int length = widget.children.length;
_materializedChildBase = math.min(length, _materializedChildBase);
materializedChildLimit = math.min(length, materializedChildLimit);
}
_materializedChildCount = materializedChildLimit - _materializedChildBase;
_repaintOffsetBase = _materializedChildBase * widget.itemExtent; _repaintOffsetBase = _materializedChildBase * widget.itemExtent;
_repaintOffsetLimit = materializedChildLimit * widget.itemExtent; _repaintOffsetLimit = materializedChildLimit * widget.itemExtent;
......
...@@ -11,6 +11,7 @@ typedef void ExtentsChangedCallback(double contentExtent, double containerExtent ...@@ -11,6 +11,7 @@ typedef void ExtentsChangedCallback(double contentExtent, double containerExtent
abstract class VirtualViewport extends RenderObjectWidget { abstract class VirtualViewport extends RenderObjectWidget {
double get startOffset; double get startOffset;
ScrollDirection get scrollDirection;
List<Widget> get children; List<Widget> get children;
} }
...@@ -52,8 +53,23 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO ...@@ -52,8 +53,23 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO
} }
void _updatePaintOffset() { void _updatePaintOffset() {
renderObject.paintOffset = switch (widget.scrollDirection) {
renderObject.paintOffset = new Offset(0.0, -(widget.startOffset - repaintOffsetBase)); case ScrollDirection.vertical:
renderObject.paintOffset = new Offset(0.0, -(widget.startOffset - repaintOffsetBase));
break;
case ScrollDirection.horizontal:
renderObject.paintOffset = new Offset(-(widget.startOffset - repaintOffsetBase), 0.0);
break;
}
}
double get _containerExtent {
switch (widget.scrollDirection) {
case ScrollDirection.vertical:
return renderObject.size.height;
case ScrollDirection.horizontal:
return renderObject.size.width;
}
} }
void updateRenderObject() { void updateRenderObject() {
...@@ -67,7 +83,7 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO ...@@ -67,7 +83,7 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO
if (!renderObject.needsLayout) { if (!renderObject.needsLayout) {
if (repaintOffsetBase != null && widget.startOffset < repaintOffsetBase) if (repaintOffsetBase != null && widget.startOffset < repaintOffsetBase)
renderObject.markNeedsLayout(); renderObject.markNeedsLayout();
else if (repaintOffsetLimit != null && widget.startOffset + renderObject.size.height > repaintOffsetLimit) else if (repaintOffsetLimit != null && widget.startOffset + _containerExtent > repaintOffsetLimit)
renderObject.markNeedsLayout(); renderObject.markNeedsLayout();
} }
} }
...@@ -88,7 +104,7 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO ...@@ -88,7 +104,7 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO
List<Widget> newWidgets = new List<Widget>(count); List<Widget> newWidgets = new List<Widget>(count);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
int childIndex = base + i; int childIndex = base + i;
Widget child = widget.children[childIndex]; Widget child = widget.children[childIndex % widget.children.length];
Key key = new ValueKey(child.key ?? childIndex); Key key = new ValueKey(child.key ?? childIndex);
newWidgets[i] = new RepaintBoundary(key: key, child: child); newWidgets[i] = new RepaintBoundary(key: key, child: child);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment