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,
final LayerHandle<ClipRectLayer> _clipRectLayer = LayerHandle<ClipRectLayer>();
@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
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
......
......@@ -1144,7 +1144,17 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
}
@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
String toStringShort() {
......
......@@ -2788,6 +2788,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
///
/// This is used in the semantics phase to avoid including children
/// 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;
/// Returns a rect in this object's coordinate system that describes
......
......@@ -826,8 +826,15 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO
@override
Rect? describeApproximatePaintClip(RenderObject child) {
switch (clipBehavior) {
case Clip.none:
return null;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
return _isOverflowing ? Offset.zero & size : null;
}
}
@override
String toStringShort() {
......
......@@ -636,7 +636,16 @@ class RenderStack extends RenderBox
}
@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
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
......
......@@ -562,7 +562,16 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
}
@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;
// The child's viewportMainAxisExtent can be infinite when a
// RenderShrinkWrappingViewport is given infinite constraints, such as when
......
......@@ -866,7 +866,16 @@ class _RenderTheatre extends RenderBox with ContainerRenderObjectMixin<RenderBox
}
@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
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
......
......@@ -533,11 +533,18 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
bool _shouldClipAtPaintOffset(Offset paintOffset) {
assert(child != null);
switch (clipBehavior) {
case Clip.none:
return false;
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
void paint(PaintingContext context, Offset offset) {
......@@ -548,7 +555,7 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
context.paintChild(child!, offset + paintOffset);
}
if (_shouldClipAtPaintOffset(paintOffset) && clipBehavior != Clip.none) {
if (_shouldClipAtPaintOffset(paintOffset)) {
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
......
......@@ -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', () {
final TextSelectionDelegate delegate = _FakeEditableTextState();
final RenderEditable editable = RenderEditable(
......
......@@ -2158,4 +2158,41 @@ void main() {
await tester.drag(find.text('b'), const Offset(0, 200));
await tester.pumpAndSettle();
}, 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() {
});
testWidgets('Overlay can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
builder: (BuildContext context) => Container(),
builder: (BuildContext context) => Positioned(left: 2000, right: 2500, child: Container()),
),
],
),
......@@ -1035,9 +1034,9 @@ void main() {
);
// 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
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
expect((renderObject as dynamic).clipBehavior, equals(Clip.hardEdge));
for (final Clip clip in Clip.values) {
await tester.pumpWidget(
......@@ -1053,8 +1052,27 @@ void main() {
),
),
);
// 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() {
child: SizedBox(
width: 100.0,
child: Flex(
clipBehavior: Clip.hardEdge,
direction: Axis.horizontal,
children: const <Widget>[
SizedBox(
......@@ -75,6 +76,7 @@ void main() {
child: SizedBox(
width: 100.0,
child: Flex(
clipBehavior: Clip.hardEdge,
direction: Axis.horizontal,
children: <Widget>[
const SizedBox(
......
......@@ -385,6 +385,29 @@ void main() {
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 {
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