Unverified Commit fba9214d authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[framework] remove usage and deprecate physical model layer (#102274)

parent e493d5ce
......@@ -1083,20 +1083,14 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
if (size.isEmpty)
return;
if (clipBehavior == Clip.none) {
_clipRectLayer.layer = null;
defaultPaint(context, offset);
} else {
// We have overflow and the clipBehavior isn't none. Clip it.
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
defaultPaint,
clipBehavior: clipBehavior,
oldLayer: _clipRectLayer.layer,
);
}
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
defaultPaint,
clipBehavior: clipBehavior,
oldLayer: _clipRectLayer.layer,
);
assert(() {
// Only set this if it's null to save work. It gets reset to null if the
......
......@@ -388,19 +388,14 @@ class RenderFlow extends RenderBox
@override
void paint(PaintingContext context, Offset offset) {
if (clipBehavior == Clip.none) {
_clipRectLayer.layer = null;
_paintWithDelegate(context, offset);
} else {
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
_paintWithDelegate,
clipBehavior: clipBehavior,
oldLayer: _clipRectLayer.layer,
);
}
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
_paintWithDelegate,
clipBehavior: clipBehavior,
oldLayer: _clipRectLayer.layer,
);
}
final LayerHandle<ClipRectLayer> _clipRectLayer = LayerHandle<ClipRectLayer>();
......
......@@ -1972,12 +1972,20 @@ class BackdropFilterLayer extends ContainerLayer {
/// When debugging, setting [debugDisablePhysicalShapeLayers] to true will cause this
/// layer to be skipped (directly replaced by its children). This can be helpful
/// to track down the cause of performance problems.
@Deprecated(
'Use a clip and canvas operations directly (See RenderPhysicalModel). '
'This feature was deprecated after v2.13.0-0.0.pre.',
)
class PhysicalModelLayer extends ContainerLayer {
/// Creates a composited layer that uses a physical model to producing
/// lighting effects.
///
/// The [clipPath], [clipBehavior], [elevation], [color], and [shadowColor]
/// arguments must be non-null before the compositing phase of the pipeline.
@Deprecated(
'Use a clip and canvas operations directly (See RenderPhysicalModel). '
'This feature was deprecated after v2.13.0-0.0.pre.',
)
PhysicalModelLayer({
Path? clipPath,
Clip clipBehavior = Clip.none,
......
......@@ -491,6 +491,10 @@ class PaintingContext extends ClipContext {
/// (e.g. from opacity layer to a clip rect layer).
/// {@endtemplate}
ClipRectLayer? pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.hardEdge, ClipRectLayer? oldLayer }) {
if (clipBehavior == Clip.none) {
painter(this, offset);
return null;
}
final Rect offsetClipRect = clipRect.shift(offset);
if (needsCompositing) {
final ClipRectLayer layer = oldLayer ?? ClipRectLayer();
......@@ -526,6 +530,10 @@ class PaintingContext extends ClipContext {
/// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer}
ClipRRectLayer? pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.antiAlias, ClipRRectLayer? oldLayer }) {
assert(clipBehavior != null);
if (clipBehavior == Clip.none) {
painter(this, offset);
return null;
}
final Rect offsetBounds = bounds.shift(offset);
final RRect offsetClipRRect = clipRRect.shift(offset);
if (needsCompositing) {
......@@ -562,6 +570,10 @@ class PaintingContext extends ClipContext {
/// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer}
ClipPathLayer? pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter, { Clip clipBehavior = Clip.antiAlias, ClipPathLayer? oldLayer }) {
assert(clipBehavior != null);
if (clipBehavior == Clip.none) {
painter(this, offset);
return null;
}
final Rect offsetBounds = bounds.shift(offset);
final Path offsetClipPath = clipPath.shift(offset);
if (needsCompositing) {
......
......@@ -1827,9 +1827,6 @@ abstract class _RenderPhysicalModelBase<T> extends _RenderCustomClip<T> {
markNeedsPaint();
}
@override
bool get alwaysNeedsCompositing => true;
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
......@@ -1845,6 +1842,8 @@ abstract class _RenderPhysicalModelBase<T> extends _RenderCustomClip<T> {
}
}
final Paint _transparentPaint = Paint()..color = const Color(0x00000000);
/// Creates a physical model layer that clips its child to a rounded
/// rectangle.
///
......@@ -1873,9 +1872,6 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
_shape = shape,
_borderRadius = borderRadius;
@override
PhysicalModelLayer? get layer => super.layer as PhysicalModelLayer?;
/// The shape of the layer.
///
/// Defaults to [BoxShape.rectangle]. The [borderRadius] affects the corners
......@@ -1933,42 +1929,79 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
@override
void paint(PaintingContext context, Offset offset) {
if (child != null) {
_updateClip();
final RRect offsetRRect = _clip!.shift(offset);
final Rect offsetBounds = offsetRRect.outerRect;
final Path offsetRRectAsPath = Path()..addRRect(offsetRRect);
bool paintShadows = true;
assert(() {
if (debugDisableShadows) {
if (elevation > 0.0) {
context.canvas.drawRRect(
offsetRRect,
Paint()
..color = shadowColor
..style = PaintingStyle.stroke
..strokeWidth = elevation * 2.0,
);
}
paintShadows = false;
}
return true;
}());
layer ??= PhysicalModelLayer();
layer!
..clipPath = offsetRRectAsPath
..clipBehavior = clipBehavior
..elevation = paintShadows ? elevation : 0.0
..color = color
..shadowColor = shadowColor;
context.pushLayer(layer!, super.paint, offset, childPaintBounds: offsetBounds);
assert(() {
layer!.debugCreator = debugCreator;
return true;
}());
} else {
if (child == null) {
layer = null;
return;
}
_updateClip();
final RRect offsetRRect = _clip!.shift(offset);
final Rect offsetBounds = offsetRRect.outerRect;
final Path offsetRRectAsPath = Path()..addRRect(offsetRRect);
bool paintShadows = true;
assert(() {
if (debugDisableShadows) {
if (elevation > 0.0) {
context.canvas.drawRRect(
offsetRRect,
Paint()
..color = shadowColor
..style = PaintingStyle.stroke
..strokeWidth = elevation * 2.0,
);
}
paintShadows = false;
}
return true;
}());
final Canvas canvas = context.canvas;
if (elevation != 0.0 && paintShadows) {
// The drawShadow call doesn't add the region of the shadow to the
// picture's bounds, so we draw a hardcoded amount of extra space to
// account for the maximum potential area of the shadow.
// TODO(jsimmons): remove this when Skia does it for us.
canvas.drawRect(
offsetBounds.inflate(20.0),
_transparentPaint,
);
canvas.drawShadow(
offsetRRectAsPath,
shadowColor,
elevation,
color.alpha != 0xFF,
);
}
final bool usesSaveLayer = clipBehavior == Clip.antiAliasWithSaveLayer;
if (!usesSaveLayer) {
canvas.drawRRect(
offsetRRect,
Paint()..color = color
);
}
layer = context.pushClipRRect(
needsCompositing,
offset,
Offset.zero & size,
_clip!,
(PaintingContext context, Offset offset) {
if (usesSaveLayer) {
// If we want to avoid the bleeding edge artifact
// (https://github.com/flutter/flutter/issues/18057#issue-328003931)
// using saveLayer, we have to call drawPaint instead of drawPath as
// anti-aliased drawPath will always have such artifacts.
context.canvas.drawPaint( Paint()..color = color);
}
super.paint(context, offset);
},
oldLayer: layer as ClipRRectLayer?,
clipBehavior: clipBehavior,
);
assert(() {
layer?.debugCreator = debugCreator;
return true;
}());
}
@override
......@@ -2006,9 +2039,6 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
assert(color != null),
assert(shadowColor != null);
@override
PhysicalModelLayer? get layer => super.layer as PhysicalModelLayer?;
@override
Path get _defaultClip => Path()..addRect(Offset.zero & size);
......@@ -2025,41 +2055,78 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
@override
void paint(PaintingContext context, Offset offset) {
if (child != null) {
_updateClip();
final Rect offsetBounds = offset & size;
final Path offsetPath = _clip!.shift(offset);
bool paintShadows = true;
assert(() {
if (debugDisableShadows) {
if (elevation > 0.0) {
context.canvas.drawPath(
offsetPath,
Paint()
..color = shadowColor
..style = PaintingStyle.stroke
..strokeWidth = elevation * 2.0,
);
}
paintShadows = false;
}
return true;
}());
layer ??= PhysicalModelLayer();
layer!
..clipPath = offsetPath
..clipBehavior = clipBehavior
..elevation = paintShadows ? elevation : 0.0
..color = color
..shadowColor = shadowColor;
context.pushLayer(layer!, super.paint, offset, childPaintBounds: offsetBounds);
assert(() {
layer!.debugCreator = debugCreator;
return true;
}());
} else {
if (child == null) {
layer = null;
return;
}
_updateClip();
final Rect offsetBounds = offset & size;
final Path offsetPath = _clip!.shift(offset);
bool paintShadows = true;
assert(() {
if (debugDisableShadows) {
if (elevation > 0.0) {
context.canvas.drawPath(
offsetPath,
Paint()
..color = shadowColor
..style = PaintingStyle.stroke
..strokeWidth = elevation * 2.0,
);
}
paintShadows = false;
}
return true;
}());
final Canvas canvas = context.canvas;
if (elevation != 0.0 && paintShadows) {
// The drawShadow call doesn't add the region of the shadow to the
// picture's bounds, so we draw a hardcoded amount of extra space to
// account for the maximum potential area of the shadow.
// TODO(jsimmons): remove this when Skia does it for us.
canvas.drawRect(
offsetBounds.inflate(20.0),
_transparentPaint,
);
canvas.drawShadow(
offsetPath,
shadowColor,
elevation,
color.alpha != 0xFF,
);
}
final bool usesSaveLayer = clipBehavior == Clip.antiAliasWithSaveLayer;
if (!usesSaveLayer) {
canvas.drawPath(
offsetPath,
Paint()..color = color
);
}
layer = context.pushClipPath(
needsCompositing,
offset,
Offset.zero & size,
_clip!,
(PaintingContext context, Offset offset) {
if (usesSaveLayer) {
// If we want to avoid the bleeding edge artifact
// (https://github.com/flutter/flutter/issues/18057#issue-328003931)
// using saveLayer, we have to call drawPaint instead of drawPath as
// anti-aliased drawPath will always have such artifacts.
context.canvas.drawPaint( Paint()..color = color);
}
super.paint(context, offset);
},
oldLayer: layer as ClipPathLayer?,
clipBehavior: clipBehavior,
);
assert(() {
layer?.debugCreator = debugCreator;
return true;
}());
}
@override
......
......@@ -799,20 +799,15 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO
return;
}
if (clipBehavior == Clip.none) {
_clipRectLayer.layer = null;
super.paint(context, offset);
} else {
// We have overflow and the clipBehavior isn't none. Clip it.
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
super.paint,
clipBehavior: clipBehavior,
oldLayer: _clipRectLayer.layer,
);
}
// We have overflow and the clipBehavior isn't none. Clip it.
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
super.paint,
clipBehavior: clipBehavior,
oldLayer: _clipRectLayer.layer,
);
// Display the overflow indicator.
assert(() {
......
......@@ -64,6 +64,8 @@ void expectCheckmarkColor(Finder finder, Color color) {
expect(
finder,
paints
// Physical model path
..path()
// The first path that is painted is the selection overlay. We do not care
// how it is painted but it has to be added it to this pattern so that the
// check mark can be checked next.
......
......@@ -64,6 +64,8 @@ void expectCheckmarkColor(Finder finder, Color color) {
expect(
finder,
paints
// Physical model layer path
..path()
// The first path that is painted is the selection overlay. We do not care
// how it is painted but it has to be added it to this pattern so that the
// check mark can be checked next.
......
......@@ -852,10 +852,6 @@ void main() {
),
);
}
// 116 = 16 + 'button'.length * 14 + 16, horizontal padding = 16
const Rect clipRect = Rect.fromLTRB(0.0, 0.0, 116.0, 36.0);
final Path clipPath = Path()..addRect(clipRect);
final Finder outlinedButton = find.byType(OutlinedButton);
BorderSide getBorderSide() {
......@@ -873,12 +869,6 @@ void main() {
// Expect that the button is disabled and painted with the disabled border color.
expect(tester.widget<OutlinedButton>(outlinedButton).enabled, false);
expect(getBorderSide(), disabledBorderSide);
_checkPhysicalLayer(
tester.element(outlinedButton),
fillColor,
clipPath: clipPath,
clipRect: clipRect,
);
// Pump a new button with a no-op onPressed callback to make it enabled.
await tester.pumpWidget(
......@@ -896,23 +886,11 @@ void main() {
// Wait for the border's color to change to pressed
await tester.pump(const Duration(milliseconds: 200));
expect(getBorderSide(), pressedBorderSide);
_checkPhysicalLayer(
tester.element(outlinedButton),
fillColor,
clipPath: clipPath,
clipRect: clipRect,
);
// Tap gesture completes, button returns to its initial configuration.
await gesture.up();
await tester.pumpAndSettle();
expect(getBorderSide(), enabledBorderSide);
_checkPhysicalLayer(
tester.element(outlinedButton),
fillColor,
clipPath: clipPath,
clipRect: clipRect,
);
});
testWidgets('OutlinedButton has no clip by default', (WidgetTester tester) async {
......@@ -1748,29 +1726,6 @@ void main() {
});
}
PhysicalModelLayer _findPhysicalLayer(Element element) {
expect(element, isNotNull);
RenderObject? object = element.renderObject;
while (object != null && object is! RenderRepaintBoundary && object is! RenderView) {
object = object.parent as RenderObject?;
}
expect(object!.debugLayer, isNotNull);
expect(object.debugLayer!.firstChild, isA<PhysicalModelLayer>());
final PhysicalModelLayer layer = object.debugLayer!.firstChild! as PhysicalModelLayer;
final Layer child = layer.firstChild!;
return child is PhysicalModelLayer ? child : layer;
}
void _checkPhysicalLayer(Element element, Color expectedColor, { Path? clipPath, Rect? clipRect }) {
final PhysicalModelLayer expectedLayer = _findPhysicalLayer(element);
expect(expectedLayer.elevation, 0.0);
expect(expectedLayer.color, expectedColor);
if (clipPath != null) {
expect(clipRect, isNotNull);
expect(expectedLayer.clipPath, coversSameAreaAs(clipPath, areaToCompare: clipRect!.inflate(10.0)));
}
}
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
final RichText iconRichText = tester.widget<RichText>(
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
......
......@@ -1360,7 +1360,7 @@ void main() {
);
// Represents the Raised Button and Range Slider.
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 3));
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 4));
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawParagraph, 3));
await tester.tap(find.text('Next'));
......@@ -1379,7 +1379,7 @@ void main() {
);
// Represents the raised button with inner page text.
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 1));
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 2));
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawParagraph, 1));
// Don't stop holding the value indicator.
......
......@@ -784,6 +784,7 @@ void main() {
expect(
valueIndicatorBox,
paints
..rrect(color: const Color(0xfffafafa))
..rrect(color: customColor1) // active track
..rrect(color: customColor2) // inactive track
..circle(color: customColor1.withOpacity(0.12)) // overlay
......@@ -2415,7 +2416,7 @@ void main() {
..paragraph(),
);
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 2));
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 3));
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawParagraph, 2));
await tester.tap(find.text('Next'));
......@@ -2432,7 +2433,7 @@ void main() {
);
// Represents the ElevatedButton with inner Text, inner page.
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 1));
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 2));
expect(valueIndicatorBox, paintsExactlyCountTimes(#drawParagraph, 1));
// Don't stop holding the value indicator.
......
......@@ -1134,6 +1134,8 @@ void main() {
expect(
valueIndicatorBox,
paints
// physical model
..rrect()
..rrect(rrect: RRect.fromLTRBAndCorners(
24.0, 298.0, 24.0, 302.0,
topLeft: const Radius.circular(2.0),
......
......@@ -1015,6 +1015,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model
..path()
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
......@@ -1042,6 +1044,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model
..path()
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
......@@ -1068,6 +1072,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model
..path()
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
......@@ -1108,6 +1114,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model
..path()
..path(
style: PaintingStyle.stroke,
color: borderColor,
......@@ -1137,6 +1145,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model
..path()
..path(
style: PaintingStyle.stroke,
color: selectedBorderColor,
......@@ -1165,6 +1175,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model
..path()
..path(
style: PaintingStyle.stroke,
color: disabledBorderColor,
......@@ -1432,6 +1444,8 @@ void main() {
expect(
toggleButtonRenderObject[0],
paints
// physical model
..path()
// leading side, top and bottom - enabled
..path(
style: PaintingStyle.stroke,
......@@ -1445,6 +1459,8 @@ void main() {
expect(
toggleButtonRenderObject[1],
paints
// physical model
..path()
// leading side - selected
..path(
style: PaintingStyle.stroke,
......@@ -1464,6 +1480,8 @@ void main() {
expect(
toggleButtonRenderObject[2],
paints
// physical model
..path()
// leading side - selected, since previous button is selected
..path(
style: PaintingStyle.stroke,
......@@ -1515,6 +1533,8 @@ void main() {
expect(
toggleButtonRenderObject[0],
paints
// physical model
..path()
// left side, top and right - enabled.
..path(
style: PaintingStyle.stroke,
......@@ -1528,6 +1548,8 @@ void main() {
expect(
toggleButtonRenderObject[1],
paints
// physical model
..path()
// top side - selected.
..path(
style: PaintingStyle.stroke,
......@@ -1547,6 +1569,8 @@ void main() {
expect(
toggleButtonRenderObject[2],
paints
// physical model
..path()
// top side - selected, since previous button is selected.
..path(
style: PaintingStyle.stroke,
......@@ -1712,6 +1736,8 @@ void main() {
expect(
toggleButtonRenderObject[0],
paints
// physical model paints
..path()
// left side, top and right - enabled.
..path(
style: PaintingStyle.stroke,
......
......@@ -543,6 +543,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model layer paint
..path()
..path(
style: PaintingStyle.stroke,
color: borderColor,
......@@ -576,6 +578,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model layer paint
..path()
..path(
style: PaintingStyle.stroke,
color: selectedBorderColor,
......@@ -608,6 +612,8 @@ void main() {
expect(
toggleButtonRenderObject,
paints
// physical model layer paint
..path()
..path(
style: PaintingStyle.stroke,
color: disabledBorderColor,
......
......@@ -222,7 +222,7 @@ void main() {
);
final RenderOpacity root = RenderOpacity(
opacity: .5,
child: blackBox,
child: RenderRepaintBoundary(child: blackBox),
);
layout(root, phase: EnginePhase.compositingBits);
......
......@@ -5,9 +5,8 @@
import 'dart:typed_data';
import 'dart:ui' as ui show Gradient, Image, ImageFilter;
import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
......@@ -15,7 +14,6 @@ import 'rendering_tester.dart';
void main() {
TestRenderingFlutterBinding.ensureInitialized();
test('RenderFittedBox handles applying paint transform and hit-testing with empty size', () {
final RenderFittedBox fittedBox = RenderFittedBox(
child: RenderCustomPaint(
......@@ -61,47 +59,20 @@ void main() {
expect(painted, equals(false));
});
test('RenderPhysicalModel compositing on Fuchsia', () {
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
test('RenderPhysicalModel compositing', () {
final RenderPhysicalModel root = RenderPhysicalModel(color: const Color(0xffff00ff));
layout(root, phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
expect(root.needsCompositing, isFalse);
// On Fuchsia, the system compositor is responsible for drawing shadows
// for physical model layers with non-zero elevation.
root.elevation = 1.0;
pumpFrame(phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
expect(root.needsCompositing, isFalse);
root.elevation = 0.0;
pumpFrame(phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
debugDefaultTargetPlatformOverride = null;
});
test('RenderPhysicalModel compositing on non-Fuchsia', () {
for (final TargetPlatform platform in TargetPlatform.values) {
if (platform == TargetPlatform.fuchsia) {
continue;
}
debugDefaultTargetPlatformOverride = platform;
final RenderPhysicalModel root = RenderPhysicalModel(color: const Color(0xffff00ff));
layout(root, phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
// Flutter now composites physical shapes on all platforms.
root.elevation = 1.0;
pumpFrame(phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
root.elevation = 0.0;
pumpFrame(phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
}
debugDefaultTargetPlatformOverride = null;
expect(root.needsCompositing, isFalse);
});
test('RenderSemanticsGestureHandler adds/removes correct semantic actions', () {
......@@ -128,9 +99,6 @@ void main() {
group('RenderPhysicalShape', () {
test('shape change triggers repaint', () {
for (final TargetPlatform platform in TargetPlatform.values) {
if (platform == TargetPlatform.fuchsia) {
continue;
}
debugDefaultTargetPlatformOverride = platform;
final RenderPhysicalShape root = RenderPhysicalShape(
......@@ -151,27 +119,24 @@ void main() {
debugDefaultTargetPlatformOverride = null;
});
test('compositing on non-Fuchsia', () {
test('compositing', () {
for (final TargetPlatform platform in TargetPlatform.values) {
if (platform == TargetPlatform.fuchsia) {
continue;
}
debugDefaultTargetPlatformOverride = platform;
final RenderPhysicalShape root = RenderPhysicalShape(
color: const Color(0xffff00ff),
clipper: const ShapeBorderClipper(shape: CircleBorder()),
);
layout(root, phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
expect(root.needsCompositing, isFalse);
// On non-Fuchsia platforms, we composite physical shape layers
root.elevation = 1.0;
pumpFrame(phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
expect(root.needsCompositing, isFalse);
root.elevation = 0.0;
pumpFrame(phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
expect(root.needsCompositing, isFalse);
}
debugDefaultTargetPlatformOverride = null;
});
......@@ -287,7 +252,9 @@ void main() {
test('RenderOpacity reuses its layer', () {
_testLayerReuse<OpacityLayer>(RenderOpacity(
opacity: 0.5, // must not be 0 or 1.0. Otherwise, it won't create a layer
child: RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1.0, 1.0)),
), // size doesn't matter
));
});
......@@ -353,9 +320,7 @@ void main() {
test('RenderClipRect reuses its layer', () {
_testLayerReuse<ClipRectLayer>(RenderClipRect(
clipper: _TestRectClipper(),
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1.0, 1.0)),
), // size doesn't matter
));
......@@ -364,9 +329,7 @@ void main() {
test('RenderClipRRect reuses its layer', () {
_testLayerReuse<ClipRRectLayer>(RenderClipRRect(
clipper: _TestRRectClipper(),
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1.0, 1.0)),
), // size doesn't matter
));
......@@ -375,9 +338,7 @@ void main() {
test('RenderClipOval reuses its layer', () {
_testLayerReuse<ClipPathLayer>(RenderClipOval(
clipper: _TestRectClipper(),
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1.0, 1.0)),
), // size doesn't matter
));
......@@ -386,32 +347,28 @@ void main() {
test('RenderClipPath reuses its layer', () {
_testLayerReuse<ClipPathLayer>(RenderClipPath(
clipper: _TestPathClipper(),
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1.0, 1.0)),
), // size doesn't matter
));
});
test('RenderPhysicalModel reuses its layer', () {
_testLayerReuse<PhysicalModelLayer>(RenderPhysicalModel(
_testLayerReuse<ClipRRectLayer>(RenderPhysicalModel(
clipBehavior: Clip.hardEdge,
color: const Color.fromRGBO(0, 0, 0, 1.0),
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1.0, 1.0)),
), // size doesn't matter
));
});
test('RenderPhysicalShape reuses its layer', () {
_testLayerReuse<PhysicalModelLayer>(RenderPhysicalShape(
_testLayerReuse<ClipPathLayer>(RenderPhysicalShape(
clipper: _TestPathClipper(),
clipBehavior: Clip.hardEdge,
color: const Color.fromRGBO(0, 0, 0, 1.0),
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1.0, 1.0)),
), // size doesn't matter
));
......@@ -421,9 +378,7 @@ void main() {
_testLayerReuse<TransformLayer>(RenderTransform(
// Use a 3D transform to force compositing.
transform: Matrix4.rotationX(0.1),
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1.0, 1.0)),
), // size doesn't matter
));
......@@ -434,8 +389,7 @@ void main() {
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(100.0, 200.0)),
), // size doesn't matter
));
......@@ -445,8 +399,7 @@ void main() {
_testLayerReuse<TransformLayer>(RenderFittedBox(
fit: BoxFit.fill,
// Inject opacity under the clip to force compositing.
child: RenderOpacity(
opacity: 0.5,
child: RenderRepaintBoundary(
child: RenderSizedBox(const Size(1, 1)),
), // size doesn't matter
));
......@@ -768,7 +721,7 @@ void _testLayerReuse<L extends Layer>(RenderBox renderObject) {
expect(L, isNot(Layer));
expect(renderObject.debugLayer, null);
layout(renderObject, phase: EnginePhase.paint, constraints: BoxConstraints.tight(const Size(10, 10)));
final Layer layer = renderObject.debugLayer!;
final Layer? layer = renderObject.debugLayer;
expect(layer, isA<L>());
expect(layer, isNotNull);
......
......@@ -793,7 +793,7 @@ void main() {
await tester.pumpWidget(
MouseRegion(
onEnter: (PointerEnterEvent _) {},
child: const Opacity(opacity: 0.5, child: Placeholder()),
child: const RepaintBoundary(child: Placeholder()),
),
);
......
......@@ -591,35 +591,28 @@ void main() {
)),
);
PhysicalModelLayer? _dfsFindPhysicalLayer(ContainerLayer layer) {
expect(layer, isNotNull);
Layer? child = layer.firstChild;
while (child != null) {
if (child is PhysicalModelLayer) {
return child;
}
if (child is ContainerLayer) {
Layer? innerChild = child.firstChild;
while (innerChild != null) {
if (innerChild is ContainerLayer) {
final PhysicalModelLayer? candidate = _dfsFindPhysicalLayer(innerChild);
if (candidate != null) {
return candidate;
}
}
innerChild = innerChild.nextSibling;
}
Object? _dfsFindPhysicalLayer(RenderObject object) {
expect(object, isNotNull);
if (object is RenderPhysicalModel || object is RenderPhysicalShape) {
return object;
}
final List<RenderObject> children = <RenderObject>[];
object.visitChildren(children.add);
for (final RenderObject child in children) {
final Object? result = _dfsFindPhysicalLayer(child);
if (result != null) {
return result;
}
child = child.nextSibling;
}
return null;
}
final ContainerLayer nestedScrollViewLayer = find.byType(NestedScrollView).evaluate().first.renderObject!.debugLayer!;
final RenderObject nestedScrollViewLayer = find.byType(NestedScrollView).evaluate().first.renderObject!;
void _checkPhysicalLayer({required double elevation}) {
final PhysicalModelLayer? layer = _dfsFindPhysicalLayer(nestedScrollViewLayer);
expect(layer, isNotNull);
expect(layer!.elevation, equals(elevation));
final dynamic physicalModel = _dfsFindPhysicalLayer(nestedScrollViewLayer);
expect(physicalModel, isNotNull);
// ignore: avoid_dynamic_calls
expect(physicalModel.elevation, equals(elevation));
}
int expectedBuildCount = 0;
......
......@@ -43,30 +43,6 @@ void main() {
expect(renderPhysicalShape.clipBehavior, equals(Clip.antiAlias));
});
testWidgets('PhysicalModel - creates a physical model layer when it needs compositing', (WidgetTester tester) async {
debugDisableShadows = false;
await tester.pumpWidget(
MaterialApp(
home: PhysicalModel(
color: Colors.grey,
shadowColor: Colors.red,
elevation: 1.0,
child: Material(child: TextField(controller: TextEditingController())),
),
),
);
await tester.pump();
final RenderPhysicalModel renderPhysicalModel = tester.allRenderObjects.whereType<RenderPhysicalModel>().first;
expect(renderPhysicalModel.needsCompositing, true);
final PhysicalModelLayer physicalModelLayer = tester.layers.whereType<PhysicalModelLayer>().first;
expect(physicalModelLayer.shadowColor, Colors.red);
expect(physicalModelLayer.color, Colors.grey);
expect(physicalModelLayer.elevation, 1.0);
debugDisableShadows = true;
});
testWidgets('PhysicalModel - clips when overflows and elevation is 0', (WidgetTester tester) async {
const Key key = Key('test');
await tester.pumpWidget(
......
......@@ -240,8 +240,7 @@ void main() {
child: ClipRect(
child: Transform(
transform: Matrix4.diagonal3Values(0.5, 0.5, 1.0),
child: Opacity(
opacity: 0.9,
child: RepaintBoundary(
child: Container(
color: const Color(0xFF00FF00),
),
......@@ -265,7 +264,7 @@ void main() {
await tester.pumpWidget(
Transform.rotate(
angle: math.pi / 2.0,
child: Opacity(opacity: 0.5, child: Container()),
child: RepaintBoundary(child: Container()),
),
);
......@@ -305,7 +304,7 @@ void main() {
await tester.pumpWidget(
Transform.translate(
offset: const Offset(100.0, 50.0),
child: Opacity(opacity: 0.5, child: Container()),
child: RepaintBoundary(child: Container()),
),
);
......@@ -320,7 +319,7 @@ void main() {
await tester.pumpWidget(
Transform.scale(
scale: 2.0,
child: Opacity(opacity: 0.5, child: Container()),
child: RepaintBoundary(child: Container()),
),
);
......
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