Commit 7ac2486a authored by Dan Field's avatar Dan Field Committed by Will Larche

Revert "Always composite PhysicalModels (#28919)" (#29314)

This reverts commit 58fb1832.
parent eb294a9c
f7a81e05c4ab87d499986427051806e280a4b0c1
e27e697cd534e9595acd4459a0f38782c4f750fc
......@@ -1571,8 +1571,12 @@ abstract class _RenderPhysicalModelBase<T> extends _RenderCustomClip<T> {
markNeedsPaint();
}
static final Paint _transparentPaint = Paint()..color = const Color(0x00000000);
// On Fuchsia, the system compositor is responsible for drawing shadows
// for physical model layers with non-zero elevation.
@override
bool get alwaysNeedsCompositing => _elevation != 0.0;
bool get alwaysNeedsCompositing => _elevation != 0.0 && defaultTargetPlatform == TargetPlatform.fuchsia;
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
......@@ -1702,14 +1706,37 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
}
return true;
}());
final PhysicalModelLayer physicalModel = PhysicalModelLayer(
clipPath: offsetRRectAsPath,
clipBehavior: clipBehavior,
elevation: paintShadows ? elevation : 0.0,
color: color,
shadowColor: shadowColor,
);
context.pushLayer(physicalModel, super.paint, offset, childPaintBounds: offsetBounds);
if (needsCompositing) {
final PhysicalModelLayer physicalModel = PhysicalModelLayer(
clipPath: offsetRRectAsPath,
clipBehavior: clipBehavior,
elevation: paintShadows ? elevation : 0.0,
color: color,
shadowColor: shadowColor,
);
context.pushLayer(physicalModel, super.paint, offset, childPaintBounds: offsetBounds);
} else {
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),
_RenderPhysicalModelBase._transparentPaint,
);
canvas.drawShadow(
offsetRRectAsPath,
shadowColor,
elevation,
color.alpha != 0xFF,
);
}
canvas.drawRRect(offsetRRect, Paint()..color = color);
context.clipRRectAndPaint(offsetRRect, clipBehavior, offsetBounds, () => super.paint(context, offset));
assert(context.canvas == canvas, 'canvas changed even though needsCompositing was false');
}
}
}
......@@ -1792,14 +1819,37 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
}
return true;
}());
final PhysicalModelLayer physicalModel = PhysicalModelLayer(
clipPath: offsetPath,
clipBehavior: clipBehavior,
elevation: paintShadows ? elevation : 0.0,
color: color,
shadowColor: shadowColor,
);
context.pushLayer(physicalModel, super.paint, offset, childPaintBounds: offsetBounds);
if (needsCompositing) {
final PhysicalModelLayer physicalModel = PhysicalModelLayer(
clipPath: offsetPath,
clipBehavior: clipBehavior,
elevation: paintShadows ? elevation : 0.0,
color: color,
shadowColor: shadowColor,
);
context.pushLayer(physicalModel, super.paint, offset, childPaintBounds: offsetBounds);
} else {
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),
_RenderPhysicalModelBase._transparentPaint,
);
canvas.drawShadow(
offsetPath,
shadowColor,
elevation,
color.alpha != 0xFF,
);
}
canvas.drawPath(offsetPath, Paint()..color = color..style = PaintingStyle.fill);
context.clipPathAndPaint(offsetPath, clipBehavior, offsetBounds, () => super.paint(context, offset));
assert(context.canvas == canvas, 'canvas changed even though needsCompositing was false');
}
}
}
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
......@@ -73,15 +71,15 @@ void main() {
await pump(FloatingActionButtonLocation.endDocked);
await expectLater(
find.byKey(key),
matchesGoldenFile('bottom_app_bar.custom_shape.1.1.png'),
matchesGoldenFile('bottom_app_bar.custom_shape.1.png'),
);
await pump(FloatingActionButtonLocation.centerDocked);
await tester.pumpAndSettle();
await expectLater(
find.byKey(key),
matchesGoldenFile('bottom_app_bar.custom_shape.2.1.png'),
matchesGoldenFile('bottom_app_bar.custom_shape.2.png'),
);
}, skip: !Platform.isLinux);
});
testWidgets('color defaults to Theme.bottomAppBarColor', (WidgetTester tester) async {
await tester.pumpWidget(
......
......@@ -82,7 +82,7 @@ void main() {
await expectLater(
find.byKey(_painterKey),
matchesGoldenFile('bottom_app_bar_theme.custom_shape.1.png'),
matchesGoldenFile('bottom_app_bar_theme.custom_shape.png'),
skip: !Platform.isLinux,
);
});
......
......@@ -139,7 +139,7 @@ void main() {
await expectLater(
find.byKey(painterKey),
matchesGoldenFile('card_theme.custom_shape.1.png'),
matchesGoldenFile('card_theme.custom_shape.png'),
skip: !Platform.isLinux,
);
});
......
......@@ -132,7 +132,7 @@ void main() {
await expectLater(
find.byKey(_painterKey),
matchesGoldenFile('dialog_theme.dialog_with_custom_border.1.png'),
matchesGoldenFile('dialog_theme.dialog_with_custom_border.png'),
skip: !Platform.isLinux,
);
});
......
......@@ -650,7 +650,7 @@ void main() {
await tester.pump(const Duration(milliseconds: 1000));
await expectLater(
find.byKey(key),
matchesGoldenFile('floating_action_button_test.clip.3.png'), // .clip.1.png is obsolete and can be removed
matchesGoldenFile('floating_action_button_test.clip.2.png'), // .clip.1.png is obsolete and can be removed
skip: !Platform.isLinux,
);
});
......
......@@ -618,7 +618,7 @@ void main() {
await expectLater(
find.byKey(painterKey),
matchesGoldenFile('material.border_paint_above.1.png'),
matchesGoldenFile('material.border_paint_above.png'),
skip: !Platform.isLinux,
);
});
......@@ -659,7 +659,7 @@ void main() {
await expectLater(
find.byKey(painterKey),
matchesGoldenFile('material.border_paint_below.1.png'),
matchesGoldenFile('material.border_paint_below.png'),
skip: !Platform.isLinux,
);
});
......
......@@ -10,27 +10,6 @@ import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
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;
}
expect(object.debugLayer, isNotNull);
expect(object.debugLayer.firstChild, isInstanceOf<PhysicalModelLayer>());
final PhysicalModelLayer layer = object.debugLayer.firstChild;
return layer.firstChild is PhysicalModelLayer ? layer.firstChild : 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)));
}
}
testWidgets('Outline button responds to tap when enabled', (WidgetTester tester) async {
int pressedCount = 0;
......@@ -135,13 +114,8 @@ void main() {
expect(
outlineButton,
paints
..clipPath(pathMatcher: coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)))
..path(color: disabledBorderColor, strokeWidth: borderWidth));
checkPhysicalLayer(
tester.element(outlineButton),
const Color(0),
clipPath: clipPath,
clipRect: clipRect,
);
// Pump a new button with a no-op onPressed callback to make it enabled.
await tester.pumpWidget(
......@@ -156,14 +130,10 @@ void main() {
expect(
outlineButton,
paints
// initially the interior of the button is transparent
..path(color: fillColor.withAlpha(0x00))
..clipPath(pathMatcher: coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)))
..path(color: borderColor, strokeWidth: borderWidth));
// initially, the interior of the button is transparent
checkPhysicalLayer(
tester.element(outlineButton),
fillColor.withAlpha(0x00),
clipPath: clipPath,
clipRect: clipRect,
);
final Offset center = tester.getCenter(outlineButton);
final TestGesture gesture = await tester.startGesture(center);
......@@ -174,13 +144,9 @@ void main() {
expect(
outlineButton,
paints
..path(color: fillColor.withAlpha(0xFF))
..clipPath(pathMatcher: coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)))
..path(color: highlightedBorderColor, strokeWidth: borderWidth));
checkPhysicalLayer(
tester.element(outlineButton),
fillColor.withAlpha(0xFF),
clipPath: clipPath,
clipRect: clipRect,
);
// Tap gesture completes, button returns to its initial configuration.
await gesture.up();
......@@ -188,13 +154,9 @@ void main() {
expect(
outlineButton,
paints
..path(color: fillColor.withAlpha(0x00))
..clipPath(pathMatcher: coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)))
..path(color: borderColor, strokeWidth: borderWidth));
checkPhysicalLayer(
tester.element(outlineButton),
fillColor.withAlpha(0x00),
clipPath: clipPath,
clipRect: clipRect,
);
});
testWidgets('OutlineButton has no clip by default', (WidgetTester tester) async {
......@@ -260,6 +222,7 @@ void main() {
semantics.dispose();
});
testWidgets('OutlineButton scales textScaleFactor', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
......@@ -377,7 +340,6 @@ void main() {
await tester.pumpWidget(buildFrame(ThemeData.dark()));
final Finder button = find.byType(OutlineButton);
final Element buttonElement = tester.element(button);
final Offset center = tester.getCenter(button);
// Default value for dark Theme.of(context).canvasColor as well as
......@@ -385,18 +347,18 @@ void main() {
Color fillColor = Colors.grey[850];
// Initially the interior of the button is transparent.
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
expect(button, paints..path(color: fillColor.withAlpha(0x00)));
// Tap-press gesture on the button triggers the fill animation.
TestGesture gesture = await tester.startGesture(center);
await tester.pump(); // Start the button fill animation.
await tester.pump(const Duration(milliseconds: 200)); // Animation is complete.
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0xFF));
expect(button, paints..path(color: fillColor.withAlpha(0xFF)));
// Tap gesture completes, button returns to its initial configuration.
await gesture.up();
await tester.pumpAndSettle();
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
expect(button, paints..path(color: fillColor.withAlpha(0x00)));
await tester.pumpWidget(buildFrame(ThemeData.light()));
await tester.pumpAndSettle(); // Finish the theme change animation.
......@@ -406,17 +368,17 @@ void main() {
fillColor = Colors.grey[50];
// Initially the interior of the button is transparent.
// expect(button, paints..path(color: fillColor.withAlpha(0x00)));
expect(button, paints..path(color: fillColor.withAlpha(0x00)));
// Tap-press gesture on the button triggers the fill animation.
gesture = await tester.startGesture(center);
await tester.pump(); // Start the button fill animation.
await tester.pump(const Duration(milliseconds: 200)); // Animation is complete.
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0xFF));
expect(button, paints..path(color: fillColor.withAlpha(0xFF)));
// Tap gesture completes, button returns to its initial configuration.
await gesture.up();
await tester.pumpAndSettle();
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
expect(button, paints..path(color: fillColor.withAlpha(0x00)));
});
}
......@@ -72,7 +72,7 @@ void main() {
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('continuous_rectangle_border.golden_test_even_radii.1.png'),
matchesGoldenFile('continuous_rectangle_border.golden_test_even_radii.png'),
skip: !Platform.isLinux,
);
});
......@@ -94,7 +94,7 @@ void main() {
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('continuous_rectangle_border.golden_test_varying_radii.1.png'),
matchesGoldenFile('continuous_rectangle_border.golden_test_varying_radii.png'),
skip: !Platform.isLinux,
);
});
......@@ -113,7 +113,7 @@ void main() {
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('continuous_rectangle_border.golden_test_large_radii.1.png'),
matchesGoldenFile('continuous_rectangle_border.golden_test_large_radii.png'),
skip: !Platform.isLinux,
);
});
......
......@@ -64,10 +64,10 @@ void main() {
layout(root, phase: EnginePhase.composite);
expect(root.needsCompositing, isFalse);
// Flutter now composites physical shapes on all platforms.
// On non-Fuchsia platforms, Flutter draws its own shadows.
root.elevation = 1.0;
pumpFrame(phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
expect(root.needsCompositing, isFalse);
root.elevation = 0.0;
pumpFrame(phase: EnginePhase.composite);
......@@ -127,10 +127,10 @@ void main() {
layout(root, phase: EnginePhase.composite);
expect(root.needsCompositing, isFalse);
// On non-Fuchsia platforms, we composite physical shape layers
// On non-Fuchsia platforms, Flutter draws its own shadows.
root.elevation = 1.0;
pumpFrame(phase: EnginePhase.composite);
expect(root.needsCompositing, isTrue);
expect(root.needsCompositing, isFalse);
root.elevation = 0.0;
pumpFrame(phase: EnginePhase.composite);
......
......@@ -6,7 +6,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/rendering.dart';
import '../rendering/mock_canvas.dart';
class _CustomPhysics extends ClampingScrollPhysics {
const _CustomPhysics({ ScrollPhysics parent }) : super(parent: parent);
......@@ -468,38 +469,12 @@ void main() {
// END
)),
);
PhysicalModelLayer _dfsFindPhysicalLayer(ContainerLayer layer) {
expect(layer, isNotNull);
Layer child = layer.firstChild;
while (child != null) {
if (child is PhysicalModelLayer) {
return child;
}
if (child is ContainerLayer) {
final PhysicalModelLayer candidate = _dfsFindPhysicalLayer(child);
if (candidate != null) {
return candidate;
}
}
child = child.nextSibling;
}
return null;
}
final ContainerLayer nestedScrollViewLayer = find.byType(NestedScrollView).evaluate().first.renderObject.debugLayer;
void _checkPhysicalLayer({@required double elevation}) {
final PhysicalModelLayer layer = _dfsFindPhysicalLayer(nestedScrollViewLayer);
expect(layer, isNotNull);
expect(layer.elevation, equals(elevation));
}
int expectedBuildCount = 0;
expectedBuildCount += 1;
expect(buildCount, expectedBuildCount);
expect(find.text('Item 2'), findsOneWidget);
expect(find.text('Item 18'), findsNothing);
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
// scroll down
final TestGesture gesture0 = await tester.startGesture(tester.getCenter(find.text('Item 2')));
await gesture0.moveBy(const Offset(0.0, -120.0)); // tiny bit more than the pinned app bar height (56px * 2)
......@@ -513,22 +488,22 @@ void main() {
expect(buildCount, expectedBuildCount);
await tester.pump(const Duration(milliseconds: 1)); // during shadow animation
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 0.00018262863159179688);
expect(find.byType(NestedScrollView), paints..shadow());
await tester.pump(const Duration(seconds: 1)); // end shadow animation
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 4);
expect(find.byType(NestedScrollView), paints..shadow());
// scroll down
final TestGesture gesture1 = await tester.startGesture(tester.getCenter(find.text('Item 2')));
await gesture1.moveBy(const Offset(0.0, -800.0));
await tester.pump();
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 4);
expect(find.byType(NestedScrollView), paints..shadow());
expect(find.text('Item 2'), findsNothing);
expect(find.text('Item 18'), findsOneWidget);
await gesture1.up();
await tester.pump(const Duration(seconds: 1));
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 4);
expect(find.byType(NestedScrollView), paints..shadow());
// swipe left to bring in tap on the right
final TestGesture gesture2 = await tester.startGesture(tester.getCenter(find.byType(NestedScrollView)));
await gesture2.moveBy(const Offset(-400.0, 0.0));
......@@ -539,7 +514,7 @@ void main() {
expect(find.text('Item 0'), findsOneWidget);
expect(tester.getTopLeft(find.ancestor(of: find.text('Item 0'), matching: find.byType(ListTile))).dy,
tester.getBottomLeft(find.byType(AppBar)).dy + 8.0);
_checkPhysicalLayer(elevation: 4);
expect(find.byType(NestedScrollView), paints..shadow());
await gesture2.up();
await tester.pump(); // start sideways scroll
await tester.pump(const Duration(seconds: 1)); // end sideways scroll, triggers shadow going away
......@@ -551,7 +526,7 @@ void main() {
expect(buildCount, expectedBuildCount);
expect(find.text('Item 18'), findsNothing);
expect(find.text('Item 2'), findsOneWidget);
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
await tester.pump(const Duration(seconds: 1)); // just checking we don't rebuild...
expect(buildCount, expectedBuildCount);
// peek left to see it's still in the right place
......@@ -564,10 +539,10 @@ void main() {
expect(buildCount, expectedBuildCount);
expect(find.text('Item 18'), findsOneWidget);
expect(find.text('Item 2'), findsOneWidget);
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
await tester.pump(const Duration(seconds: 1)); // shadow finishes coming back
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 4);
expect(find.byType(NestedScrollView), paints..shadow());
await gesture3.moveBy(const Offset(-400.0, 0.0));
await gesture3.up();
await tester.pump(); // left tab view goes away
......@@ -575,10 +550,10 @@ void main() {
await tester.pump(); // shadow goes away starting here
expectedBuildCount += 1;
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 4);
expect(find.byType(NestedScrollView), paints..shadow());
await tester.pump(const Duration(seconds: 1)); // shadow finishes going away
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
// scroll back up
final TestGesture gesture4 = await tester.startGesture(tester.getCenter(find.byType(NestedScrollView)));
await gesture4.moveBy(const Offset(0.0, 200.0)); // expands the appbar again
......@@ -586,11 +561,11 @@ void main() {
expect(buildCount, expectedBuildCount);
expect(find.text('Item 2'), findsOneWidget);
expect(find.text('Item 18'), findsNothing);
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
await gesture4.up();
await tester.pump(const Duration(seconds: 1));
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
// peek left to see it's now back at zero
final TestGesture gesture5 = await tester.startGesture(tester.getCenter(find.byType(NestedScrollView)));
await gesture5.moveBy(const Offset(400.0, 0.0));
......@@ -599,14 +574,14 @@ void main() {
expect(buildCount, expectedBuildCount);
expect(find.text('Item 18'), findsNothing);
expect(find.text('Item 2'), findsNWidgets(2));
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
await tester.pump(const Duration(seconds: 1)); // shadow would be finished coming back
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
await gesture5.up();
await tester.pump(); // right tab view goes away
await tester.pumpAndSettle();
expect(buildCount, expectedBuildCount);
_checkPhysicalLayer(elevation: 0);
expect(find.byType(NestedScrollView), isNot(paints..shadow()));
debugDisableShadows = true;
});
......
......@@ -128,7 +128,7 @@ void main() {
await tester.pumpWidget(build(elevation.toDouble()));
await expectLater(
find.byType(Container),
matchesGoldenFile('shadow.PhysicalShape.$elevation.2.png'),
matchesGoldenFile('shadow.PhysicalShape.$elevation.1.png'),
);
}
debugDisableShadows = true;
......
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