Skip to content

Commit

Permalink
Implement computeDryBaseline for cupertino RenderBoxes (flutter#1…
Browse files Browse the repository at this point in the history
…45951)

The `_debugVerifyDryBaselines` method verifies that dry baseline implementation is consistent with the wet baseline method at the current constraints.
  • Loading branch information
LongCatIsLooong authored Mar 30, 2024
1 parent d12ba5c commit ff284c5
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 26 deletions.
52 changes: 38 additions & 14 deletions packages/flutter/lib/src/cupertino/nav_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -939,11 +939,45 @@ class _RenderLargeTitle extends RenderShiftedBox {

double _scale = 1.0;

static double _computeTitleScale(Size childSize, BoxConstraints constraints) {
const double maxHeight = _kNavBarLargeTitleHeightExtension - _kNavBarBottomPadding;
final double scale = 1.0 + 0.03 * (constraints.maxHeight - maxHeight) / maxHeight;
final double maxScale = childSize.width != 0.0
? clampDouble(constraints.maxWidth / childSize.width, 1.0, 1.1)
: 1.1;
return clampDouble(scale, 1.0, maxScale);
}

@override
void performLayout() {
double? computeDistanceToActualBaseline(TextBaseline baseline) {
final double? distance = child?.getDistanceToActualBaseline(baseline);
if (distance == null) {
return null;
}
final BoxParentData childParentData = child!.parentData! as BoxParentData;
return childParentData.offset.dy + distance * _scale;
}

@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
Size childSize = Size.zero;
if (child == null) {
return null;
}
final BoxConstraints childConstraints = constraints.widthConstraints().loosen();
final double? result = child.getDryBaseline(childConstraints, baseline);
if (result == null) {
return null;
}
final Size childSize = child.getDryLayout(childConstraints);
final double scale = _computeTitleScale(childSize, constraints);
final Size scaledChildSize = childSize * scale;
return result * scale + alignment.alongOffset(constraints.biggest - scaledChildSize as Offset).dy;
}

@override
void performLayout() {
final RenderBox? child = this.child;
size = constraints.biggest;

if (child == null) {
Expand All @@ -952,19 +986,9 @@ class _RenderLargeTitle extends RenderShiftedBox {

final BoxConstraints childConstraints = constraints.widthConstraints().loosen();
child.layout(childConstraints, parentUsesSize: true);

final double maxScale = child.size.width != 0.0
? clampDouble(constraints.maxWidth / child.size.width, 1.0, 1.1)
: 1.1;
_scale = clampDouble(
1.0 + (constraints.maxHeight - (_kNavBarLargeTitleHeightExtension - _kNavBarBottomPadding)) / (_kNavBarLargeTitleHeightExtension - _kNavBarBottomPadding) * 0.03,
1.0,
maxScale,
);

childSize = child.size * _scale;
_scale = _computeTitleScale(child.size, constraints);
final BoxParentData childParentData = child.parentData! as BoxParentData;
childParentData.offset = alignment.alongOffset(size - childSize as Offset);
childParentData.offset = alignment.alongOffset(size - (child.size * _scale) as Offset);
}

@override
Expand Down
12 changes: 12 additions & 0 deletions packages/flutter/lib/src/cupertino/segmented_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,18 @@ class _RenderSegmentedControl<T> extends RenderBox
return constraints.constrain(Size(childSize.width * childCount, childSize.height));
}

@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final Size childSize = _calculateChildSize(constraints);
final BoxConstraints childConstraints = BoxConstraints.tight(childSize);

BaselineOffset baselineOffset = BaselineOffset.noBaseline;
for (RenderBox? child = firstChild; child != null; child = childAfter(child)) {
baselineOffset = baselineOffset.minOf(BaselineOffset(child.getDryBaseline(childConstraints, baseline)));
}
return baselineOffset.offset;
}

@override
Size computeDryLayout(BoxConstraints constraints) {
final Size childSize = _calculateChildSize(constraints);
Expand Down
12 changes: 12 additions & 0 deletions packages/flutter/lib/src/cupertino/sliding_segmented_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,18 @@ class _RenderSegmentedControl<T> extends RenderBox
return constraints.constrain(Size(childSize.width * childCount + totalSeparatorWidth, childSize.height));
}

@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final Size childSize = _calculateChildSize(constraints);
final BoxConstraints childConstraints = BoxConstraints.tight(childSize);

BaselineOffset baselineOffset = BaselineOffset.noBaseline;
for (RenderBox? child = firstChild; child != null; child = childAfter(child)) {
baselineOffset = baselineOffset.minOf(BaselineOffset(child.getDryBaseline(childConstraints, baseline)));
}
return baselineOffset.offset;
}

@override
Size computeDryLayout(BoxConstraints constraints) {
final Size childSize = _calculateChildSize(constraints);
Expand Down
37 changes: 27 additions & 10 deletions packages/flutter/lib/src/cupertino/text_selection_toolbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,30 @@ class _RenderCupertinoTextSelectionToolbarShape extends RenderShiftedBox {
markNeedsPaint();
}

bool get isAbove => anchorAbove.dy >= (child?.size.height ?? 0.0) - _kToolbarArrowSize.height * 2;
bool _isAbove(double childHeight) => anchorAbove.dy >= childHeight - _kToolbarArrowSize.height * 2;

BoxConstraints _constraintsForChild(BoxConstraints constraints) {
return BoxConstraints(
minWidth: _kToolbarArrowSize.width + _kToolbarBorderRadius.x * 2,
).enforce(constraints.loosen());
}

Offset _computeChildOffset(Size childSize) {
return Offset(0.0, _isAbove(childSize.height) ? -_kToolbarArrowSize.height : 0.0);
}

@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
if (child == null) {
return null;
}
final BoxConstraints enforcedConstraint = _constraintsForChild(constraints);
final double? result = child.getDryBaseline(enforcedConstraint, baseline);
return result == null
? null
: result + _computeChildOffset(child.getDryLayout(enforcedConstraint)).dy;
}

@override
void performLayout() {
Expand All @@ -285,22 +308,15 @@ class _RenderCupertinoTextSelectionToolbarShape extends RenderShiftedBox {
return;
}

final BoxConstraints enforcedConstraint = BoxConstraints(
minWidth: _kToolbarArrowSize.width + _kToolbarBorderRadius.x * 2,
).enforce(constraints.loosen());
child.layout(enforcedConstraint, parentUsesSize: true);

child.layout(_constraintsForChild(constraints), parentUsesSize: true);
// The buttons are padded on both top and bottom sufficiently to have
// the arrow clipped out of it on either side. By
// using this approach, the buttons don't need any special padding that
// depends on isAbove.
// The height of one arrow will be clipped off of the child, so adjust the
// size and position to remove that piece from the layout.
final BoxParentData childParentData = child.parentData! as BoxParentData;
childParentData.offset = Offset(
0.0,
isAbove ? -_kToolbarArrowSize.height : 0.0,
);
childParentData.offset = _computeChildOffset(child.size);
size = Size(
child.size.width,
child.size.height - _kToolbarArrowSize.height,
Expand Down Expand Up @@ -362,6 +378,7 @@ class _RenderCupertinoTextSelectionToolbarShape extends RenderShiftedBox {
return path..addRRect(rrect);
}

final bool isAbove = _isAbove(child.size.height);
final Offset localAnchor = globalToLocal(isAbove ? _anchorAbove : _anchorBelow);
final double arrowTipX = clampDouble(
localAnchor.dx,
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter/test/cupertino/list_tile_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ void main() {
final Offset foundTitle = tester.getTopLeft(find.text('CupertinoListTile'));
final Offset foundInfo = tester.getTopRight(find.text('Not Connected'));

expect(foundTitle.dx > foundInfo.dx, isTrue);
expect(foundTitle.dx, greaterThanOrEqualTo(foundInfo.dx));
});

testWidgets('trailing is on the left of additionalInfo', (WidgetTester tester) async {
Expand All @@ -476,7 +476,7 @@ void main() {
final Offset foundInfo = tester.getTopLeft(find.text('Not Connected'));
final Offset foundTrailing = tester.getTopRight(find.byType(CupertinoListTileChevron));

expect(foundInfo.dx > foundTrailing.dx, isTrue);
expect(foundInfo.dx, greaterThanOrEqualTo(foundTrailing.dx));
});
});

Expand Down

0 comments on commit ff284c5

Please sign in to comment.