From bb68f6f7d6f3014459ca13d6bbc65c54f408a8b1 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 1 Nov 2026 22:05:20 +0800 Subject: [PATCH] fix: avoid duplicate focus stops in request approval menus --- flutter/lib/desktop/widgets/popup_menu.dart | 9 ++++--- flutter/test/request_approval_menu_test.dart | 27 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index 04ecd5394..c66f2b499 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -101,9 +101,12 @@ class MyPopupMenuItemState> .findAncestorStateOfType>() ?.showButtonMenu() : null; - return TextButton( - onPressed: onPressed, - child: TextButton(onPressed: onPressed, child: child), + // The popup trigger owns keyboard focus; retain the menu button sizing. + return ExcludeFocus( + child: TextButton( + onPressed: onPressed, + child: TextButton(onPressed: onPressed, child: child), + ), ); }), )); diff --git a/flutter/test/request_approval_menu_test.dart b/flutter/test/request_approval_menu_test.dart index 9a4f02050..f82d5dfcd 100644 --- a/flutter/test/request_approval_menu_test.dart +++ b/flutter/test/request_approval_menu_test.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_hbb/common/widgets/request_approval_menu.dart'; import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart' @@ -88,6 +89,32 @@ Future openMenu(WidgetTester tester, List actions, } void main() { + testWidgets('approval trigger has one Tab stop and opens with Enter', + (tester) async { + await openMenu(tester, []); + bool approvalHasFocus() => FocusManager.instance.primaryFocus?.context + ?.findAncestorWidgetOfExactType>() != null; + + for (var i = 0; i < 30 && !approvalHasFocus(); i++) { + await tester.sendKeyEvent(LogicalKeyboardKey.tab); + await tester.pump(); + } + expect(approvalHasFocus(), isTrue); + await tester.sendKeyEvent(LogicalKeyboardKey.tab); + await tester.pump(); + expect(approvalHasFocus(), isFalse, + reason: 'Tab must leave the approval row after one stop'); + + await tester.sendKeyDownEvent(LogicalKeyboardKey.shiftLeft); + await tester.sendKeyEvent(LogicalKeyboardKey.tab); + await tester.sendKeyUpEvent(LogicalKeyboardKey.shiftLeft); + await tester.pump(); + expect(approvalHasFocus(), isTrue); + await tester.sendKeyEvent(LogicalKeyboardKey.enter); + await tester.pumpAndSettle(); + expect(find.text('Terminal (Run as administrator)'), findsOneWidget); + }); + testWidgets('tapping outside the approval submenu closes both menus', (tester) async { final actions = [];