Repository navigation
Conversation
A menu opened from the keyboard focuses its first enabled item (its last for Up on a Menu's trigger) in the handler of its order watch, once its items are in the tree's order. The core tells that handler the order only when it differs from the order the items registered in, and a menu's items register last-first: a menu of several items is always out of order, so its item got the focus, while the only item of a one-item menu was in order already, the handler never ran and the next key went to the trigger. That is why context-menu-press.test.tsx's Shift+F10 test, whose menu has one item, failed on every run since longbridge#978, headless and in the browser. OrderWatch::request asks the core to tell the handler the order even when it is the list's own. The menu requests it while an opening from the keyboard waits for its item. The other watches (the roving focus, the carousel) only sort, and keep skipping orders that are in order already, as most of a toolbar's are: their mounts cost no more rounds.
There was a problem hiding this comment.
✅ The one-item keyboard-focus fix passes the targeted review and independent Linux behavior checks. Requesting the order callback explicitly is a reasonable way to apply pending focus while preserving the identity-permutation fast path for the other order watchers. The hidden OrderWatch::request() API is documented and limited to this operation; no new dependency or public struct is introduced.
Integration needs coordination: the shared menu fix already pushed to #1006/#1009/#1010/#1011 addresses the same bug with MenuItem layout callbacks. A local merge-tree check of this head and that implementation (254f65075) conflicts in crates/nsh-base/src/menu.rs. Please select one pending-focus approach when integrating these branches. Chromium keyboard context menus also require the separate event-classification fix in stacked #1018.
Verified on dbc55fc284a87366cb8cb2288b5a07e28f2cee57:
- Built
nsh-cliandnsh-linux-host; all 19 Rust menu tests and both order-watch tests pass. Replacing onlymenu.rswith main's version makes exactly the three added one-item menu/submenu/context-menu regressions fail (16 existing tests still pass), then restoring it returns the worktree to the reviewed source. - All-targets Clippy for
nsh-baseandnsh-corewith-D warningspasses. All six changed Rust files pass nightly rustfmt. Workspace fmt reports eight unrelated files, each byte-identical to this PR's main parent. - Real GTK host under isolated Xvfb, driven with XTest keyboard events: baseline Tab→Return→Space leaves
chose: none; this PR selectsOpen. The same behavior passes in light and dark appearances and at 2× scaling. Right→Space selects the sole submenu item, Up opens the sole item, and Escape restores trigger focus so reopening and selecting succeeds. Screenshots were inspected and baseline/PR comparisons retained.
The Linux screenshots cover the shared menu-focus change, not native macOS/Windows execution. Independent real Chromium testing also confirms the dependency: this exact PR runtime passes two of the three context-menu tests, with Shift+F10 still failing; applying only #1018's JavaScript change to the same runtime passes all three and an additional screenshot check. The GitHub Web CI check was still running at review time. This is a comment review, not an approval or a merge recommendation for the overlapping branches.
|
Thanks for the review. On the overlap with 254f650 (#1006, #1009–#1011), I'd keep this PR's approach:
So 254f650 could leave those four branches, and this PR's three tests cover the case. For Chrome, I agree #1010's 1137dd5 is the better classifier (see #1018). |
Description
A menu opened from the keyboard puts the keyboard focus on an item, as WAI-ARIA has it: its first enabled one
(Return, Space or Down on a
Menu's trigger; Shift+F10 or the Menu key on aContextMenu's, #978; Right, Return orSpace on a
Submenu's), or its last (Up). A menu of one item got no focus at all, so the next key went to thetrigger.
context-menu-press.test.tsx› "Shift+F10 on the focused trigger opens the menu at its first item" opens aone-item context menu, and its Space presses the trigger ("tapped: solo") instead of choosing the item: it fails the
Rust applicationworkflow'swebjob (the gallery's tests, headless) onmainand on every pull request since#978.
The menu picks the item in the handler of its order watch (
watch_order, since #869), once its items are in thetree's order. The core tells that handler the order only when it differs from the order the items registered in
(#702: "in order already: nothing to tell, no round to run"). A menu's items register last-first, so a menu of
several items is always out of order and its handler runs; a single item is always in order, the handler never runs
and nothing is focused. A submenu's
focus_firstasks for the pick withinvalidate()the same way.OrderWatchgainsrequest(): asinvalidate(), and the core tells the handler the order at the end of the roundeven when it is the list's own. The menu requests the order instead of invalidating it while an opening from the
keyboard waits for its item (items added then, a submenu's
focus_first). The other watches (the roving focus oftoolbars, tabs, radios and toggle groups; the carousel) only sort in their handlers and keep skipping orders that
are in order already: in
toolbar.test.tsxandtree.test.tsx, 38 of their looks are, and making everyinvalidation tell would cost each of those a handler run and a commit round.
In Chrome the test needs one more change, in the web runtime (Chromium's
contextmenufor the keys names the mouseas its pointer): #1010's 1137dd5.
Public API
nsh_core::element::OrderWatch::request(&self)(#[doc(hidden)], aswatch_orderandOrderWatchare):as
invalidate, and the handler hears the order even when it is the list's own.Menu,SubmenuorContextMenuof one item opened from the keyboard focuses it, as one of severalfocuses its first enabled item.
How to Test
./target/debug/nsh test examples/gallery/tests/context-menu-press.test.tsx: "Shift+F10 on the focused triggeropens the menu at its first item" fails on
main(5.4 s, the timeout above) and passes with this change (442 ms).cargo test -p nsh-base --test base menu:::an_opening_from_the_keyboard_focuses_a_menus_only_item,right_on_a_submenus_trigger_focuses_its_only_itemandshift_f10_focuses_a_context_menus_only_item(the web andLinux) fail without the menu's change and pass with it; the other 16 pass either way.
cargo test -p nsh-core --lib order_watch:a_requested_order_watch_hears_its_order_even_unchanged(aninvalidation of an in-order watch tells nothing, a request tells once).
cargo test -p nsh-base --test base(772 passed) andcargo test -p nsh-core --lib(1136 passed); clippy with-D warningson both crates' targets;cargo xtask compat.Checklist
cargo testfornsh-coreandnsh-base,cargo +nightly fmton the files it touches and clippy forboth (no platform code changes).
cargo xtask compatpasses; what apps use keepsits shape).
examples/gallerydemos on iOS and Android (if the change is visible or platform-specific).