Skip to content

Return keyboard focus to terminal when pane context menu closes - #20652

Open
Mango Yen (mangokingTW) wants to merge 1 commit into
microsoft:mainfrom
mangokingTW:pr/official-context-menu-focus-fix
Open

Return keyboard focus to terminal when pane context menu closes#20652
Mango Yen (mangokingTW) wants to merge 1 commit into
microsoft:mainfrom
mangokingTW:pr/official-context-menu-focus-fix

Conversation

@mangokingTW

Copy link
Copy Markdown

Note: English is not my native language; this PR description was written with the assistance of AI tools.

Summary of the Pull Request

When the pane context menu (opened via Menu/Apps key, Shift+F10, or Right-Click when rightClickContextMenu is enabled) is dismissed with Esc, keyboard focus currently remains trapped on the now-hidden menu item (AppBarButton or overflow MoreButton). Pressing Enter afterwards invokes the hidden command (e.g. reopening a flyout or splitting a pane) instead of sending input to the active shell.

This PR ensures keyboard focus is properly restored to the terminal (or active Find search box) when the context menu is dismissed.

References and Relevant Issues

Detailed Description of the Pull Request

Root Cause

CommandBarFlyout moves focus to its menu buttons or overflow toggle (MoreButton). When dismissed via Esc, XAML closes the flyout but does not restore focus to the underlying control, leaving focus trapped on the invisible element.

Proposed Changes

  1. Top-Level Menu Dismissal (TermControl.cpp):
    • In Closed, checks if the currently focused element belongs to the closing menu (PrimaryCommands, SecondaryCommands, child flyouts, or internal MoreButton).
    • If Find (_searchBox) is open, restores focus to its textbox; otherwise restores focus to the terminal via Focus(FocusState::Programmatic).
    • Traversal is bounded to the menu's CommandBar ancestor to prevent cross-pane focus stealing in multi-pane layouts.
  2. Submenu Dismissal (TerminalPage.cpp):
    • In handBackFocusOnClose, hands focus back to the parent button that opened the submenu using FocusState::Keyboard (falling back to FocusState::Programmatic).
    • Handles light-dismiss safely: if the parent button is detached, focus restoration defers cleanly to TermControl.
  3. Shared Helper (Utils.h):
    • Extracted IsElementInCommandBarFlyout into WinRTUtils/inc/Utils.h to eliminate logic duplication across TermControl and TerminalPage.

Generative AI Disclosure

This pull request was developed with the assistance of Google Antigravity (Gemini Flash 3.8) for code exploration, edge-case analysis, and drafting this PR description. Problem investigation, focus contract definitions, manual testing, and final code verification were reviewed and directed by me.

Validation Steps Performed

Visual Demonstration

Both recordings capture the exact same top-level dismissal test (test_esc_from_the_top_level_hands_focus_back_to_the_terminal): navigating to Split pane, dismissing with Esc, then pressing Enter.

Unpatched (focus trapped on hidden button; Enter reopens menu) Patched (focus returned to terminal; Enter reaches shell)
Before: Esc leaves focus on hidden button After: Esc returns focus to terminal
Clip: 39.4s – 48.0s of reproduction-20593-x64.mp4
From Run 34097704495 (verification-recording-windows-latest)
39.5s - 41.5s: Menu opens, nav down to Split pane
41.5s - 42.5s: Split pane entry highlighted
42.6s: Esc pressed; menu disappears
46.5s: Enter pressed
46.9s: Submenu reopens in mid-air at (0, 0)
Clip: 27.0s – 34.0s of reproduction-20593-x64.mp4
From Run 34170503162 (verification-recording-windows-latest)
27.0s - 29.2s: Menu opens, nav down to Split pane
29.2s - 30.6s: Split pane entry highlighted
30.7s: Esc pressed; menu disappears, focus returned to terminal
32.0s: Enter pressed
33.2s: Enter sends newline to shell prompt

Automated UI Testing & Key Edge Cases

Verification was conducted with an automated UI test harness running under wintegrate across Windows 11 x64 and ARM64 environments. wintegrate provides native Windows UI Automation drivers, process discovery, keyboard/mouse input injection, and automated screen recordings during CI and VM test runs.

The complete test suite (19 edge-case tests) and continuous screen recordings are maintained in companion PR #1 (fix/context-menu-focus-after-esc):

Key Test Scenario Code Line Unpatched Build (Observed Failure) Patched Build Result
Top-Level Context Menu Esc
• Apps / Menu key (default trigger)
• Shift+F10 (WCAG keybinding)
• Mouse Right-Click (with rightClickContextMenu: true)
L396
L856
L886
Failed (FocusStaysTrappedOnMenu)
Focus remained trapped on invisible AppBarButton; pressing Enter re-opened the menu
Passed
Focus immediately returned to terminal; Enter sends newline to shell
Verified
Nested Submenu 2-Stage Esc
• Split pane submenu
L345 Failed (AssertionError)
1st Esc closed submenu but focus failed to return to parent button; 2nd Esc failed to return to terminal
Passed
1st Esc returned focus to parent button; 2nd Esc returned focus to terminal
Verified
Overflow MoreButton (...) Esc L665 Failed (AssertionError)
Focus trapped on invisible MoreButton; terminal cannot receive input
Passed
Single-pass ancestor traversal matched CommandBar; focus restored to terminal
Verified
Find Search Box Focus Return
• Context menu Esc with Find open
L606 Failed (AssertionError)
Focus remained trapped on invisible menu item instead of returning to search box TextBox
Passed
Detects active _searchBox and accurately restores focus to search box TextBox
Verified
Multi-Pane Focus Isolation
• Dismissing menu in split layout
L511
L767
Failed (FocusStolenCrossPane)
Dismissing Pane B menu left focus trapped on hidden item, terminal never received focus
Passed
Bounded tree search ensures each pane only handles its own menu without cross-pane interference
Verified
Rapid Input & Concurrency
• Rapid Esc + Enter
• Rapid typing after Esc
L950
L971
Failed (AssertionError)
Rapid keystrokes swallowed or triggered hidden actions; typed characters lost
Passed
Focus returned synchronously; subsequent keys and characters 100% delivered to shell
Verified
Submenu Light-Dismiss Safety
• Clicking outside open submenu
L726 🛡️ Safety Invariant Test
Verifies behavior when owner button is detached from Visual Tree during dismissal
Passed (Safe Fallback)
handBackFocusOnClose safely falls back without crash; focus returns cleanly
Verified

PR Checklist

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

When dismissing the pane context menu (or submenus) with Esc, XAML leaves
keyboard focus on the now-hidden AppBarButton or MoreButton. Subsequent Enter
invokes the hidden item instead of reaching the shell.

Fix:
- TermControl: Restore focus on context menu Closed if the focused element
  belongs to the closing flyout; respect active search box.
- TerminalPage: Hand focus back to parent button when a sub-flyout closes.
- Utils: Add bounded VisualTree helper IsElementInCommandBarFlyout to verify
  flyout membership and prevent cross-pane focus stealing.

Closes microsoft#20593
@mangokingTW
Mango Yen (mangokingTW) force-pushed the pr/official-context-menu-focus-fix branch from 6f7a935 to a8d7e68 Compare September 9, 2026 02:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Windows Terminal - Context Menu]: Keyboard focus remains on the dismissed menu item after closing the context menu with the 'Esc' key.

1 participant