Unverified Commit 7d6ffc7c authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Add rect logic to getOffsetToReveal when onlySlivers is true (#64295)

parent e30e795a
......@@ -692,10 +692,24 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
@override
RevealedOffset getOffsetToReveal(RenderObject target, double alignment, { Rect rect }) {
double leadingScrollOffset = 0.0;
double targetMainAxisExtent;
rect ??= target.paintBounds;
// Steps to convert `rect` (from a RenderBox coordinate system) to its
// scroll offset within this viewport (not in the exact order):
//
// 1. Pick the outmost RenderBox (between which, and the viewport, there is
// nothing but RenderSlivers) as an intermediate reference frame
// (the `pivot`), convert `rect` to that coordinate space.
//
// 2. Convert `rect` from the `pivot` coordinate space to its sliver
// parent's sliver coordinate system (i.e., to a scroll offset), based on
// the axis direction and growth direction of the parent.
//
// 3. Convert the scroll offset to its sliver parent's coordinate space
// using `childScrollOffset`, until we reach the viewport.
//
// 4. Make the final conversion from the outmost sliver to the viewport
// using `scrollOffsetOf`.
double leadingScrollOffset = 0.0;
// Starting at `target` and walking towards the root:
// - `child` will be the last object before we reach this viewport, and
// - `pivot` will be the last RenderBox before we reach this viewport.
......@@ -717,89 +731,116 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
child = parent;
}
// `rect` in the new intermediate coordinate system.
Rect rectLocal;
// Our new reference frame render object's main axis extent.
double pivotExtent;
GrowthDirection growthDirection;
// `leadingScrollOffset` is currently the scrollOffset of our new reference
// frame (`pivot` or `target`), within `child`.
if (pivot != null) {
assert(pivot.parent != null);
assert(pivot.parent != this);
assert(pivot != this);
assert(pivot.parent is RenderSliver); // TODO(abarth): Support other kinds of render objects besides slivers.
final RenderSliver pivotParent = pivot.parent as RenderSliver;
final Matrix4 transform = target.getTransformTo(pivot);
final Rect bounds = MatrixUtils.transformRect(transform, rect);
final GrowthDirection growthDirection = pivotParent.constraints.growthDirection;
switch (applyGrowthDirectionToAxisDirection(axisDirection, growthDirection)) {
case AxisDirection.up:
double offset;
switch (growthDirection) {
case GrowthDirection.forward:
offset = bounds.bottom;
break;
case GrowthDirection.reverse:
offset = bounds.top;
break;
}
leadingScrollOffset += pivot.size.height - offset;
targetMainAxisExtent = bounds.height;
break;
case AxisDirection.right:
double offset;
switch (growthDirection) {
case GrowthDirection.forward:
offset = bounds.left;
break;
case GrowthDirection.reverse:
offset = bounds.right;
break;
}
leadingScrollOffset += offset;
targetMainAxisExtent = bounds.width;
break;
case AxisDirection.down:
double offset;
switch (growthDirection) {
case GrowthDirection.forward:
offset = bounds.top;
break;
case GrowthDirection.reverse:
offset = bounds.bottom;
break;
}
leadingScrollOffset += offset;
targetMainAxisExtent = bounds.height;
growthDirection = pivotParent.constraints.growthDirection;
switch (axis) {
case Axis.horizontal:
pivotExtent = pivot.size.width;
break;
case AxisDirection.left:
double offset;
switch (growthDirection) {
case GrowthDirection.forward:
offset = bounds.right;
break;
case GrowthDirection.reverse:
offset = bounds.left;
break;
}
leadingScrollOffset += pivot.size.width - offset;
targetMainAxisExtent = bounds.width;
case Axis.vertical:
pivotExtent = pivot.size.height;
break;
}
rect ??= target.paintBounds;
rectLocal = MatrixUtils.transformRect(target.getTransformTo(pivot), rect);
} else if (onlySlivers) {
// `pivot` does not exist. We'll have to make up one from `target`, the
// innermost sliver.
final RenderSliver targetSliver = target as RenderSliver;
targetMainAxisExtent = targetSliver.geometry.scrollExtent;
growthDirection = targetSliver.constraints.growthDirection;
// TODO(LongCatIsLooong): make sure this works if `targetSliver` is a
// persistent header, when #56413 relands.
pivotExtent = targetSliver.geometry.scrollExtent;
if (rect == null) {
switch (axis) {
case Axis.horizontal:
rect = Rect.fromLTWH(
0, 0,
targetSliver.geometry.scrollExtent,
targetSliver.constraints.crossAxisExtent,
);
break;
case Axis.vertical:
rect = Rect.fromLTWH(
0, 0,
targetSliver.constraints.crossAxisExtent,
targetSliver.geometry.scrollExtent,
);
break;
}
}
rectLocal = rect;
} else {
return RevealedOffset(offset: offset.pixels, rect: rect);
}
assert(pivotExtent != null);
assert(rect != null);
assert(rectLocal != null);
assert(growthDirection != null);
assert(child.parent == this);
assert(child is RenderSliver);
final RenderSliver sliver = child as RenderSliver;
final double extentOfPinnedSlivers = maxScrollObstructionExtentBefore(sliver);
double targetMainAxisExtent;
// The scroll offset within `child` to `rect`.
switch (applyGrowthDirectionToAxisDirection(axisDirection, growthDirection)) {
case AxisDirection.up:
leadingScrollOffset += pivotExtent - rectLocal.bottom;
targetMainAxisExtent = rectLocal.height;
break;
case AxisDirection.right:
leadingScrollOffset += rectLocal.left;
targetMainAxisExtent = rectLocal.width;
break;
case AxisDirection.down:
leadingScrollOffset += rectLocal.top;
targetMainAxisExtent = rectLocal.height;
break;
case AxisDirection.left:
leadingScrollOffset += pivotExtent - rectLocal.right;
targetMainAxisExtent = rectLocal.width;
break;
}
// The scroll offset in the viewport to `rect`.
leadingScrollOffset = scrollOffsetOf(sliver, leadingScrollOffset);
// This step assumes the viewport's layout is up-to-date, i.e., if
// offset.pixels is changed after the last performLayout, the new scroll
// position will not be accounted for.
final Matrix4 transform = target.getTransformTo(this);
Rect targetRect = MatrixUtils.transformRect(transform, rect);
final double extentOfPinnedSlivers = maxScrollObstructionExtentBefore(sliver);
switch (sliver.constraints.growthDirection) {
case GrowthDirection.forward:
leadingScrollOffset -= extentOfPinnedSlivers;
break;
case GrowthDirection.reverse:
// Nothing to do.
// If child's growth direction is reverse, when viewport.offset is
// `leadingScrollOffset`, it is positioned just outside of the leading
// edge of the viewport.
switch (axis) {
case Axis.vertical:
leadingScrollOffset -= targetRect.height;
break;
case Axis.horizontal:
leadingScrollOffset -= targetRect.width;
break;
}
break;
}
......@@ -816,9 +857,6 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
final double targetOffset = leadingScrollOffset - (mainAxisExtent - targetMainAxisExtent) * alignment;
final double offsetDifference = offset.pixels - targetOffset;
final Matrix4 transform = target.getTransformTo(this);
Rect targetRect = MatrixUtils.transformRect(transform, rect);
switch (axisDirection) {
case AxisDirection.down:
targetRect = targetRect.translate(0.0, offsetDifference);
......
......@@ -220,7 +220,7 @@ void main() {
);
children.add(sliver);
return SliverPadding(
padding: const EdgeInsets.all(22.0),
padding: const EdgeInsets.only(top: 22.0, bottom: 23.0),
sliver: sliver,
);
}),
......@@ -234,10 +234,16 @@ void main() {
final RenderObject target = tester.renderObject(find.byWidget(children[5], skipOffstage: false));
RevealedOffset revealed = viewport.getOffsetToReveal(target, 0.0);
expect(revealed.offset, 5 * (100 + 22 + 22) + 22);
expect(revealed.offset, 5 * (100 + 22 + 23) + 22);
revealed = viewport.getOffsetToReveal(target, 1.0);
expect(revealed.offset, 5 * (100 + 22 + 22) + 22 - 100);
expect(revealed.offset, 5 * (100 + 22 + 23) + 22 - 100);
// With rect specified.
revealed = viewport.getOffsetToReveal(target, 0.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, 5 * (100 + 22 + 23) + 22 + 2);
revealed = viewport.getOffsetToReveal(target, 1.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, 5 * (100 + 22 + 23) + 22 - (200 - 4));
});
testWidgets('Viewport getOffsetToReveal Sliver - right', (WidgetTester tester) async {
......@@ -261,7 +267,7 @@ void main() {
);
children.add(sliver);
return SliverPadding(
padding: const EdgeInsets.all(22.0),
padding: const EdgeInsets.only(left: 22.0, right: 23.0),
sliver: sliver,
);
}),
......@@ -275,10 +281,16 @@ void main() {
final RenderObject target = tester.renderObject(find.byWidget(children[5], skipOffstage: false));
RevealedOffset revealed = viewport.getOffsetToReveal(target, 0.0);
expect(revealed.offset, 5 * (100 + 22 + 22) + 22);
expect(revealed.offset, 5 * (100 + 22 + 23) + 22);
revealed = viewport.getOffsetToReveal(target, 1.0);
expect(revealed.offset, 5 * (100 + 22 + 22) + 22 - 100);
expect(revealed.offset, 5 * (100 + 22 + 23) + 22 - 100);
// With rect specified.
revealed = viewport.getOffsetToReveal(target, 0.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, 5 * (100 + 22 + 23) + 22 + 1);
revealed = viewport.getOffsetToReveal(target, 1.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, 5 * (100 + 22 + 23) + 22 - (200 - 3));
});
testWidgets('Viewport getOffsetToReveal Sliver - up', (WidgetTester tester) async {
......@@ -302,7 +314,7 @@ void main() {
);
children.add(sliver);
return SliverPadding(
padding: const EdgeInsets.all(22.0),
padding: const EdgeInsets.only(top: 22.0, bottom: 23.0),
sliver: sliver,
);
}),
......@@ -316,17 +328,25 @@ void main() {
final RenderObject target = tester.renderObject(find.byWidget(children[5], skipOffstage: false));
RevealedOffset revealed = viewport.getOffsetToReveal(target, 0.0);
expect(revealed.offset, 5 * (100 + 22 + 22) + 22);
// Does not include the bottom padding of children[5] thus + 23 instead of + 22.
expect(revealed.offset, 5 * (100 + 22 + 23) + 23);
revealed = viewport.getOffsetToReveal(target, 1.0);
expect(revealed.offset, 5 * (100 + 22 + 22) + 22 - 100);
expect(revealed.offset, 5 * (100 + 22 + 23) + 23 - 100);
// With rect specified.
revealed = viewport.getOffsetToReveal(target, 0.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, 5 * (100 + 22 + 23) + 23 + (100 - 4));
revealed = viewport.getOffsetToReveal(target, 1.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, - 200 + 6 * (100 + 22 + 23) - 22 - 2);
});
testWidgets('Viewport getOffsetToReveal Sliver - up - reverse growth', (WidgetTester tester) async {
const Key centerKey = ValueKey<String>('center');
const EdgeInsets padding = EdgeInsets.only(top: 22.0, bottom: 23.0);
final Widget centerSliver = SliverPadding(
key: centerKey,
padding: const EdgeInsets.all(22.0),
padding: padding,
sliver: SliverToBoxAdapter(
child: Container(
height: 100.0,
......@@ -339,7 +359,7 @@ void main() {
child: const Text('Tile lower'),
);
final Widget lowerSliver = SliverPadding(
padding: const EdgeInsets.all(22.0),
padding: padding,
sliver: SliverToBoxAdapter(
child: lowerItem,
),
......@@ -370,13 +390,20 @@ void main() {
revealed = viewport.getOffsetToReveal(target, 1.0);
expect(revealed.offset, - 100 - 22 - 100);
// With rect specified.
revealed = viewport.getOffsetToReveal(target, 0.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, - 22 - 4);
revealed = viewport.getOffsetToReveal(target, 1.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, -200 - 22 - 2);
});
testWidgets('Viewport getOffsetToReveal Sliver - left - reverse growth', (WidgetTester tester) async {
const Key centerKey = ValueKey<String>('center');
const EdgeInsets padding = EdgeInsets.only(left: 22.0, right: 23.0);
final Widget centerSliver = SliverPadding(
key: centerKey,
padding: const EdgeInsets.all(22.0),
padding: padding,
sliver: SliverToBoxAdapter(
child: Container(
width: 100.0,
......@@ -389,7 +416,7 @@ void main() {
child: const Text('Tile lower'),
);
final Widget lowerSliver = SliverPadding(
padding: const EdgeInsets.all(22.0),
padding: padding,
sliver: SliverToBoxAdapter(
child: lowerItem,
),
......@@ -421,6 +448,12 @@ void main() {
revealed = viewport.getOffsetToReveal(target, 1.0);
expect(revealed.offset, - 100 - 22 - 200);
// With rect specified.
revealed = viewport.getOffsetToReveal(target, 0.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, - 22 - 3);
revealed = viewport.getOffsetToReveal(target, 1.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, - 300 - 22 - 1);
});
testWidgets('Viewport getOffsetToReveal Sliver - left', (WidgetTester tester) async {
......@@ -445,7 +478,7 @@ void main() {
);
children.add(sliver);
return SliverPadding(
padding: const EdgeInsets.all(22.0),
padding: const EdgeInsets.only(left: 22.0, right: 23.0),
sliver: sliver,
);
}),
......@@ -459,10 +492,16 @@ void main() {
final RenderObject target = tester.renderObject(find.byWidget(children[5], skipOffstage: false));
RevealedOffset revealed = viewport.getOffsetToReveal(target, 0.0);
expect(revealed.offset, 5 * (100 + 22 + 22) + 22);
expect(revealed.offset, 5 * (100 + 22 + 23) + 23);
revealed = viewport.getOffsetToReveal(target, 1.0);
expect(revealed.offset, 5 * (100 + 22 + 22) + 22 - 100);
expect(revealed.offset, 5 * (100 + 22 + 23) + 23 - 100);
// With rect specified.
revealed = viewport.getOffsetToReveal(target, 0.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, 6 * (100 + 22 + 23) - 22 - 3);
revealed = viewport.getOffsetToReveal(target, 1.0, rect: const Rect.fromLTRB(1, 2, 3, 4));
expect(revealed.offset, -200 + 6 * (100 + 22 + 23) - 22 - 1);
});
testWidgets('Nested Viewports showOnScreen', (WidgetTester tester) async {
......
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