From b5321d182a6bf89fd3d86ceee52179867ffa7c89 Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Wed, 4 May 2022 05:13:21 -0400 Subject: [PATCH] Reland "Fix position of CupertinoContextMenu within Transform.scale" (#102943) * Fix position of CupertinoContextMenu within Transform.scale (#97896) * Fix after rebase --- .../lib/src/cupertino/context_menu.dart | 7 ++- .../test/cupertino/context_menu_test.dart | 56 +++++++++++++++++++ .../material/flexible_space_bar_test.dart | 8 +-- .../test/material/input_decorator_test.dart | 10 ++-- packages/flutter_test/lib/src/controller.dart | 2 +- 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/context_menu.dart b/packages/flutter/lib/src/cupertino/context_menu.dart index 194b76e8ebca..a706df7cd108 100644 --- a/packages/flutter/lib/src/cupertino/context_menu.dart +++ b/packages/flutter/lib/src/cupertino/context_menu.dart @@ -47,10 +47,11 @@ typedef _ContextMenuPreviewBuilderChildless = Widget Function( Rect _getRect(GlobalKey globalKey) { assert(globalKey.currentContext != null); final RenderBox renderBoxContainer = globalKey.currentContext!.findRenderObject()! as RenderBox; - final Offset containerOffset = renderBoxContainer.localToGlobal( + return Rect.fromPoints(renderBoxContainer.localToGlobal( renderBoxContainer.paintBounds.topLeft, - ); - return containerOffset & renderBoxContainer.paintBounds.size; + ), renderBoxContainer.localToGlobal( + renderBoxContainer.paintBounds.bottomRight + )); } // The context menu arranges itself slightly differently based on the location diff --git a/packages/flutter/test/cupertino/context_menu_test.dart b/packages/flutter/test/cupertino/context_menu_test.dart index 902a11411e21..16b4537e3c5e 100644 --- a/packages/flutter/test/cupertino/context_menu_test.dart +++ b/packages/flutter/test/cupertino/context_menu_test.dart @@ -228,6 +228,62 @@ void main() { kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic, ); }); + + testWidgets('CupertinoContextMenu is in the correct position when within a Transform.scale', (WidgetTester tester) async { + final Widget child = getChild(); + await tester.pumpWidget(CupertinoApp( + home: CupertinoPageScaffold( + child: MediaQuery( + data: const MediaQueryData(size: Size(800, 600)), + child: Transform.scale( + scale: 0.5, + child: Align( + //alignment: Alignment.bottomRight, + child: CupertinoContextMenu( + actions: const [ + CupertinoContextMenuAction( + child: Text('CupertinoContextMenuAction'), + ), + ], + child: child + ), + ) + ) + ) + ) + )); + expect(find.byWidget(child), findsOneWidget); + final Rect childRect = tester.getRect(find.byWidget(child)); + expect(find.byType(ShaderMask), findsNothing); + + // Start a press on the child. + final TestGesture gesture = await tester.startGesture(childRect.center); + await tester.pump(); + + // The _DecoyChild is showing directly on top of the child. + expect(findDecoyChild(child), findsOneWidget); + Rect decoyChildRect = tester.getRect(findDecoyChild(child)); + expect(childRect, equals(decoyChildRect)); + + expect(find.byType(ShaderMask), findsOneWidget); + + // After a small delay, the _DecoyChild has begun to animate. + await tester.pump(const Duration(milliseconds: 100)); + decoyChildRect = tester.getRect(findDecoyChild(child)); + expect(childRect, isNot(equals(decoyChildRect))); + + // Eventually the decoy fully scales by _kOpenSize. + await tester.pump(const Duration(milliseconds: 500)); + decoyChildRect = tester.getRect(findDecoyChild(child)); + expect(childRect, isNot(equals(decoyChildRect))); + expect(decoyChildRect.width, childRect.width * kOpenScale); + + // Then the CupertinoContextMenu opens. + await tester.pumpAndSettle(); + await gesture.up(); + await tester.pumpAndSettle(); + expect(findStatic(), findsOneWidget); + }); }); group('CupertinoContextMenu when open', () { diff --git a/packages/flutter/test/material/flexible_space_bar_test.dart b/packages/flutter/test/material/flexible_space_bar_test.dart index 51368236461f..c55e4252cc71 100644 --- a/packages/flutter/test/material/flexible_space_bar_test.dart +++ b/packages/flutter/test/material/flexible_space_bar_test.dart @@ -473,8 +473,8 @@ void main() { Rect.fromLTRB( 0, height - titleFontSize - 10, - (width / 1.5).floorToDouble(), - height - 10, + (width / 1.5).floorToDouble() * 1.5, + height, ), ); }); @@ -545,8 +545,8 @@ void main() { Rect.fromLTRB( 0, height - titleFontSize - bottomMargin, - (collapsedWidth / 3).floorToDouble(), - height - bottomMargin, + (collapsedWidth / 3).floorToDouble() * 3, + height, ), ); }); diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index 4b608b2c7fe3..809b6a408943 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -4321,17 +4321,17 @@ void main() { await pumpDecorator(focused: false, empty: false); await tester.pumpAndSettle(); expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); - expect(getLabelRect(tester).size, equals(const Size(80, 16))); + expect(getLabelRect(tester).size, equals(const Size(80, 16) * 0.75)); await pumpDecorator(focused: true); await tester.pumpAndSettle(); expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); - expect(getLabelRect(tester).size, equals(const Size(80, 16))); + expect(getLabelRect(tester).size, equals(const Size(80, 16) * 0.75)); await pumpDecorator(focused: true, empty: false); await tester.pumpAndSettle(); expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); - expect(getLabelRect(tester).size, equals(const Size(80, 16))); + expect(getLabelRect(tester).size, equals(const Size(80, 16) * 0.75)); await pumpDecorator(focused: false, enabled: false); await tester.pumpAndSettle(); @@ -4341,7 +4341,7 @@ void main() { await pumpDecorator(focused: false, empty: false, enabled: false); await tester.pumpAndSettle(); expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); - expect(getLabelRect(tester).size, equals(const Size(80, 16))); + expect(getLabelRect(tester).size, equals(const Size(80, 16) * 0.75)); // Focused and disabled happens with NavigationMode.directional. await pumpDecorator(focused: true, enabled: false); @@ -4352,7 +4352,7 @@ void main() { await pumpDecorator(focused: true, empty: false, enabled: false); await tester.pumpAndSettle(); expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); - expect(getLabelRect(tester).size, equals(const Size(80, 16))); + expect(getLabelRect(tester).size, equals(const Size(80, 16) * 0.75)); }); testWidgets('InputDecorationTheme.toString()', (WidgetTester tester) async { diff --git a/packages/flutter_test/lib/src/controller.dart b/packages/flutter_test/lib/src/controller.dart index f7b8162ab8e3..0e0bfed8516c 100644 --- a/packages/flutter_test/lib/src/controller.dart +++ b/packages/flutter_test/lib/src/controller.dart @@ -1138,7 +1138,7 @@ abstract class WidgetController { /// Returns the rect of the given widget. This is only valid once /// the widget's render object has been laid out at least once. - Rect getRect(Finder finder) => getTopLeft(finder) & getSize(finder); + Rect getRect(Finder finder) => Rect.fromPoints(getTopLeft(finder), getBottomRight(finder)); /// Attempts to find the [SemanticsNode] of first result from `finder`. ///