From 36f73cf645b18558e97aa6beaaff3c75bb2bdbf1 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 7 Jun 2023 16:40:17 -0700 Subject: [PATCH] Disable context menu (#128365) ## Description Changes the context menu example for `MenuAnchor` so that it uses right-click, or (on macOS and iOS only) ctrl-left-click, for the context menu. Also disables the browser context menu on web platforms. ## Tests - Updated test to reflect new triggers. --- .../material/menu_anchor/menu_anchor.1.dart | 59 +++++++++++++++++-- .../menu_anchor/menu_anchor.1_test.dart | 10 ++-- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/examples/api/lib/material/menu_anchor/menu_anchor.1.dart b/examples/api/lib/material/menu_anchor/menu_anchor.1.dart index 53ad226eaeb..7ce3e78c118 100644 --- a/examples/api/lib/material/menu_anchor/menu_anchor.1.dart +++ b/examples/api/lib/material/menu_anchor/menu_anchor.1.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -41,6 +42,7 @@ class _MyContextMenuState extends State { final FocusNode _buttonFocusNode = FocusNode(debugLabel: 'Menu Button'); final MenuController _menuController = MenuController(); ShortcutRegistryEntry? _shortcutsEntry; + bool _menuWasEnabled = false; Color get backgroundColor => _backgroundColor; Color _backgroundColor = Colors.red; @@ -62,6 +64,12 @@ class _MyContextMenuState extends State { } } + @override + void initState() { + super.initState(); + _disableContextMenu(); + } + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -84,15 +92,38 @@ class _MyContextMenuState extends State { void dispose() { _shortcutsEntry?.dispose(); _buttonFocusNode.dispose(); + _reenableContextMenu(); super.dispose(); } + Future _disableContextMenu() async { + if (!kIsWeb) { + // Does nothing on non-web platforms. + return; + } + _menuWasEnabled = BrowserContextMenu.enabled; + if (_menuWasEnabled) { + await BrowserContextMenu.disableContextMenu(); + } + } + + void _reenableContextMenu() { + if (!kIsWeb) { + // Does nothing on non-web platforms. + return; + } + if (_menuWasEnabled && !BrowserContextMenu.enabled) { + BrowserContextMenu.enableContextMenu(); + } + } + @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(50), child: GestureDetector( onTapDown: _handleTapDown, + onSecondaryTapDown: _handleSecondaryTapDown, child: MenuAnchor( controller: _menuController, anchorTapClosesMenu: true, @@ -142,7 +173,7 @@ class _MyContextMenuState extends State { children: [ const Padding( padding: EdgeInsets.all(8.0), - child: Text('Ctrl-click anywhere on the background to show the menu.'), + child: Text('Right-click anywhere on the background to show the menu.'), ), Padding( padding: const EdgeInsets.all(12.0), @@ -185,13 +216,29 @@ class _MyContextMenuState extends State { } } - void _handleTapDown(TapDownDetails details) { - if (!HardwareKeyboard.instance.logicalKeysPressed.contains(LogicalKeyboardKey.controlLeft) && - !HardwareKeyboard.instance.logicalKeysPressed.contains(LogicalKeyboardKey.controlRight)) { - return; - } + void _handleSecondaryTapDown(TapDownDetails details) { _menuController.open(position: details.localPosition); } + + void _handleTapDown(TapDownDetails details) { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.windows: + // Don't open the menu on these platforms with a Ctrl-tap (or a + // tap). + break; + case TargetPlatform.iOS: + case TargetPlatform.macOS: + // Only open the menu on these platforms if the control button is down + // when the tap occurs. + if (HardwareKeyboard.instance.logicalKeysPressed.contains(LogicalKeyboardKey.controlLeft) || + HardwareKeyboard.instance.logicalKeysPressed.contains(LogicalKeyboardKey.controlRight)) { + _menuController.open(position: details.localPosition); + } + } + } } class ContextMenuApp extends StatelessWidget { diff --git a/examples/api/test/material/menu_anchor/menu_anchor.1_test.dart b/examples/api/test/material/menu_anchor/menu_anchor.1_test.dart index d1433d54ce9..b9dc670d1c8 100644 --- a/examples/api/test/material/menu_anchor/menu_anchor.1_test.dart +++ b/examples/api/test/material/menu_anchor/menu_anchor.1_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_api_samples/material/menu_anchor/menu_anchor.1.dart' as example; @@ -18,15 +19,13 @@ void main() { await tester.pumpWidget(const example.ContextMenuApp()); - await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight); - await tester.tapAt(const Offset(100, 200)); + await tester.tapAt(const Offset(100, 200), buttons: kSecondaryButton); await tester.pump(); expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(100.0, 200.0, 433.0, 360.0))); // Make sure tapping in a different place causes the menu to move. - await tester.tapAt(const Offset(200, 100)); + await tester.tapAt(const Offset(200, 100), buttons: kSecondaryButton); await tester.pump(); - await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight); expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(200.0, 100.0, 533.0, 260.0))); @@ -67,8 +66,7 @@ void main() { ); // Open the menu so we can look for state changes reflected in the menu. - await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight); - await tester.tapAt(const Offset(100, 200)); + await tester.tapAt(const Offset(100, 200), buttons: kSecondaryButton); await tester.pump(); expect(find.text(example.MenuEntry.showMessage.label), findsOneWidget);