Unverified Commit 1662a14b authored by Dan Field's avatar Dan Field Committed by GitHub

More missing clipBehavior respects (#103931)

parent a633f3df
...@@ -2526,7 +2526,16 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -2526,7 +2526,16 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
final LayerHandle<ClipRectLayer> _clipRectLayer = LayerHandle<ClipRectLayer>(); final LayerHandle<ClipRectLayer> _clipRectLayer = LayerHandle<ClipRectLayer>();
@override @override
Rect? describeApproximatePaintClip(RenderObject child) => _hasVisualOverflow ? Offset.zero & size : null; Rect? describeApproximatePaintClip(RenderObject child) {
switch (clipBehavior) {
case Clip.none:
return null;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
return _hasVisualOverflow ? Offset.zero & size : null;
}
}
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
......
...@@ -1144,7 +1144,17 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -1144,7 +1144,17 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
} }
@override @override
Rect? describeApproximatePaintClip(RenderObject child) => _hasOverflow ? Offset.zero & size : null; Rect? describeApproximatePaintClip(RenderObject child) {
switch (clipBehavior) {
case Clip.none:
return null;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
return _hasOverflow ? Offset.zero & size : null;
}
}
@override @override
String toStringShort() { String toStringShort() {
......
...@@ -2788,6 +2788,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2788,6 +2788,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// ///
/// This is used in the semantics phase to avoid including children /// This is used in the semantics phase to avoid including children
/// that are not physically visible. /// that are not physically visible.
///
/// RenderObjects that respect a [Clip] behavior when painting _must_ respect
/// that same behavior when describing this value. For example, if passing
/// [Clip.none] to [PaintingContext.pushClipRect] as the `clipBehavior`, then
/// the implementation of this method must return null.
Rect? describeApproximatePaintClip(covariant RenderObject child) => null; Rect? describeApproximatePaintClip(covariant RenderObject child) => null;
/// Returns a rect in this object's coordinate system that describes /// Returns a rect in this object's coordinate system that describes
......
...@@ -826,7 +826,14 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO ...@@ -826,7 +826,14 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO
@override @override
Rect? describeApproximatePaintClip(RenderObject child) { Rect? describeApproximatePaintClip(RenderObject child) {
return _isOverflowing ? Offset.zero & size : null; switch (clipBehavior) {
case Clip.none:
return null;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
return _isOverflowing ? Offset.zero & size : null;
}
} }
@override @override
......
...@@ -636,7 +636,16 @@ class RenderStack extends RenderBox ...@@ -636,7 +636,16 @@ class RenderStack extends RenderBox
} }
@override @override
Rect? describeApproximatePaintClip(RenderObject child) => _hasVisualOverflow ? Offset.zero & size : null; Rect? describeApproximatePaintClip(RenderObject child) {
switch (clipBehavior) {
case Clip.none:
return null;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
return _hasVisualOverflow ? Offset.zero & size : null;
}
}
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
......
...@@ -562,7 +562,16 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix ...@@ -562,7 +562,16 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
} }
@override @override
Rect describeApproximatePaintClip(RenderSliver child) { Rect? describeApproximatePaintClip(RenderSliver child) {
switch (clipBehavior) {
case Clip.none:
return null;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
break;
}
final Rect viewportClip = Offset.zero & size; final Rect viewportClip = Offset.zero & size;
// The child's viewportMainAxisExtent can be infinite when a // The child's viewportMainAxisExtent can be infinite when a
// RenderShrinkWrappingViewport is given infinite constraints, such as when // RenderShrinkWrappingViewport is given infinite constraints, such as when
......
...@@ -866,7 +866,16 @@ class _RenderTheatre extends RenderBox with ContainerRenderObjectMixin<RenderBox ...@@ -866,7 +866,16 @@ class _RenderTheatre extends RenderBox with ContainerRenderObjectMixin<RenderBox
} }
@override @override
Rect? describeApproximatePaintClip(RenderObject child) => _hasVisualOverflow ? Offset.zero & size : null; Rect? describeApproximatePaintClip(RenderObject child) {
switch (clipBehavior) {
case Clip.none:
return null;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
return _hasVisualOverflow ? Offset.zero & size : null;
}
}
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
......
...@@ -533,10 +533,17 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix ...@@ -533,10 +533,17 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
bool _shouldClipAtPaintOffset(Offset paintOffset) { bool _shouldClipAtPaintOffset(Offset paintOffset) {
assert(child != null); assert(child != null);
return paintOffset.dx < 0 || switch (clipBehavior) {
paintOffset.dy < 0 || case Clip.none:
paintOffset.dx + child!.size.width > size.width || return false;
paintOffset.dy + child!.size.height > size.height; case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
return paintOffset.dx < 0 ||
paintOffset.dy < 0 ||
paintOffset.dx + child!.size.width > size.width ||
paintOffset.dy + child!.size.height > size.height;
}
} }
@override @override
...@@ -548,7 +555,7 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix ...@@ -548,7 +555,7 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
context.paintChild(child!, offset + paintOffset); context.paintChild(child!, offset + paintOffset);
} }
if (_shouldClipAtPaintOffset(paintOffset) && clipBehavior != Clip.none) { if (_shouldClipAtPaintOffset(paintOffset)) {
_clipRectLayer.layer = context.pushClipRect( _clipRectLayer.layer = context.pushClipRect(
needsCompositing, needsCompositing,
offset, offset,
......
...@@ -128,6 +128,28 @@ void main() { ...@@ -128,6 +128,28 @@ void main() {
} }
}); });
test('Editable respect clipBehavior in describeApproximatePaintClip', () {
final String longString = 'a' * 10000;
final RenderEditable editable = RenderEditable(
text: TextSpan(text: longString),
textDirection: TextDirection.ltr,
startHandleLayerLink: LayerLink(),
endHandleLayerLink: LayerLink(),
offset: ViewportOffset.zero(),
textSelectionDelegate: _FakeEditableTextState(),
selection: const TextSelection(baseOffset: 0, extentOffset: 0),
clipBehavior: Clip.none,
);
layout(editable);
bool visited = false;
editable.visitChildren((RenderObject child) {
visited = true;
expect(editable.describeApproximatePaintClip(child), null);
});
expect(visited, true);
});
test('editable intrinsics', () { test('editable intrinsics', () {
final TextSelectionDelegate delegate = _FakeEditableTextState(); final TextSelectionDelegate delegate = _FakeEditableTextState();
final RenderEditable editable = RenderEditable( final RenderEditable editable = RenderEditable(
......
...@@ -2158,4 +2158,41 @@ void main() { ...@@ -2158,4 +2158,41 @@ void main() {
await tester.drag(find.text('b'), const Offset(0, 200)); await tester.drag(find.text('b'), const Offset(0, 200));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('Viewport describeApproximateClip respects clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(const Directionality(
textDirection: TextDirection.ltr,
child: CustomScrollView(
clipBehavior: Clip.none,
slivers: <Widget>[
SliverToBoxAdapter(child: SizedBox(width: 20, height: 20)),
]
),
));
RenderViewport viewport = tester.allRenderObjects.whereType<RenderViewport>().first;
expect(viewport.clipBehavior, Clip.none);
bool visited = false;
viewport.visitChildren((RenderObject child) {
visited = true;
expect(viewport.describeApproximatePaintClip(child as RenderSliver), null);
});
expect(visited, true);
await tester.pumpWidget(const Directionality(
textDirection: TextDirection.ltr,
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(child: SizedBox(width: 20, height: 20)),
]
),
));
viewport = tester.allRenderObjects.whereType<RenderViewport>().first;
expect(viewport.clipBehavior, Clip.hardEdge);
visited = false;
viewport.visitChildren((RenderObject child) {
visited = true;
expect(viewport.describeApproximatePaintClip(child as RenderSliver), Offset.zero & viewport.size);
});
expect(visited, true);
});
} }
...@@ -1020,14 +1020,13 @@ void main() { ...@@ -1020,14 +1020,13 @@ void main() {
}); });
testWidgets('Overlay can set and update clipBehavior', (WidgetTester tester) async { testWidgets('Overlay can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( OverlayEntry(
builder: (BuildContext context) => Container(), builder: (BuildContext context) => Positioned(left: 2000, right: 2500, child: Container()),
), ),
], ],
), ),
...@@ -1035,9 +1034,9 @@ void main() { ...@@ -1035,9 +1034,9 @@ void main() {
); );
// By default, clipBehavior should be Clip.hardEdge // By default, clipBehavior should be Clip.hardEdge
final dynamic renderObject = tester.renderObject(find.byType(Overlay)); final RenderObject renderObject = tester.renderObject(find.byType(Overlay));
// ignore: avoid_dynamic_calls // ignore: avoid_dynamic_calls
expect(renderObject.clipBehavior, equals(Clip.hardEdge)); expect((renderObject as dynamic).clipBehavior, equals(Clip.hardEdge));
for (final Clip clip in Clip.values) { for (final Clip clip in Clip.values) {
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1053,8 +1052,27 @@ void main() { ...@@ -1053,8 +1052,27 @@ void main() {
), ),
), ),
); );
// ignore: avoid_dynamic_calls // ignore: avoid_dynamic_calls
expect(renderObject.clipBehavior, clip); expect((renderObject as dynamic).clipBehavior, clip);
bool visited = false;
renderObject.visitChildren((RenderObject child) {
visited = true;
switch(clip) {
case Clip.none:
expect(renderObject.describeApproximatePaintClip(child), null);
break;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
expect(
renderObject.describeApproximatePaintClip(child),
const Rect.fromLTRB(0, 0, 800, 600),
);
break;
}
});
expect(visited, true);
} }
}); });
......
...@@ -18,6 +18,7 @@ void main() { ...@@ -18,6 +18,7 @@ void main() {
child: SizedBox( child: SizedBox(
width: 100.0, width: 100.0,
child: Flex( child: Flex(
clipBehavior: Clip.hardEdge,
direction: Axis.horizontal, direction: Axis.horizontal,
children: const <Widget>[ children: const <Widget>[
SizedBox( SizedBox(
...@@ -75,6 +76,7 @@ void main() { ...@@ -75,6 +76,7 @@ void main() {
child: SizedBox( child: SizedBox(
width: 100.0, width: 100.0,
child: Flex( child: Flex(
clipBehavior: Clip.hardEdge,
direction: Axis.horizontal, direction: Axis.horizontal,
children: <Widget>[ children: <Widget>[
const SizedBox( const SizedBox(
......
...@@ -385,6 +385,29 @@ void main() { ...@@ -385,6 +385,29 @@ void main() {
expect(renderObject.clipBehavior, equals(Clip.hardEdge)); expect(renderObject.clipBehavior, equals(Clip.hardEdge));
}); });
testWidgets('Clip.none is respected by describeApproximateClip', (WidgetTester tester) async {
await tester.pumpWidget(Stack(
textDirection: TextDirection.ltr,
children: const <Widget>[Positioned(left: 1000, right: 2000, child: SizedBox(width: 2000, height: 2000))],
));
final RenderStack renderObject = tester.allRenderObjects.whereType<RenderStack>().first;
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
bool visited = false;
renderObject.visitChildren((RenderObject child) {
visited = true;
expect(renderObject.describeApproximatePaintClip(child), const Rect.fromLTRB(0.0, 0.0, 800.0, 600.0));
});
expect(visited, true);
visited = false;
renderObject.clipBehavior = Clip.none;
renderObject.visitChildren((RenderObject child) {
visited = true;
expect(renderObject.describeApproximatePaintClip(child), null);
});
expect(visited, true);
});
testWidgets('IndexedStack with null index', (WidgetTester tester) async { testWidgets('IndexedStack with null index', (WidgetTester tester) async {
bool? tapped; bool? tapped;
......
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