Stop a stationary pointer from pinning the menu selection

A pointer parked over a menu row re-claimed the selection on every frame's
declaration, so keyboard navigation fought the mouse sixty times a second
and lost -- press Up, and the hover put the selection straight back. Found
by the UI demo's scripted tour: its mouse click leaves the pointer resting
on the menu, and the Up keystroke that followed did nothing.

Hover now moves the selection only on frames the pointer actually moved (or
clicked), through a frame-latched motion edge beside the existing press
edge. Parking the mouse claims nothing; moving it onto a row still selects,
clicking still selects and activates. Regression test drives the exact
sequence: click a row, move the selection away by keyboard, redeclare, and
assert the stationary pointer stole nothing back.

Co-Authored-By: Claude Code (Claude Fable 5, claude-fable-5) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzBDV2fqgnUAcqCKqKvc71
This commit is contained in:
2026-08-02 11:24:57 -04:00
parent 0679d40ee4
commit badaf570ab
3 changed files with 49 additions and 9 deletions

View File

@@ -841,6 +841,23 @@ akerr_ErrorContext *test_ui_menu_widget(void)
"clicking the second row moved the selection to %d, not 1", menu.selected);
TEST_ASSERT(e, menu.activated == true, "clicking a row did not activate the menu");
menu.activated = false;
// The pointer is still parked over the second row. Keyboard moves the
// selection away, and redeclaring the menu must not let the
// stationary pointer claim it back -- only motion or a click selects.
SDL_memset(&event, 0x00, sizeof(SDL_Event));
event.type = SDL_EVENT_KEY_DOWN;
event.key.key = SDLK_DOWN;
TEST_EXPECT_OK(e, akgl_ui_menu_handle_event(&menu, &event, &consumed),
"Down after the click was refused");
TEST_ASSERT(e, menu.selected == 2, "Down did not move the selection off the hovered row");
TEST_EXPECT_OK(e, akgl_ui_frame_begin(), "opening the third menu frame failed");
TEST_EXPECT_OK(e, akgl_ui_menu(&menu), "redeclaring the menu a third time failed");
TEST_EXPECT_OK(e, akgl_ui_frame_end(akgl_renderer), "closing the third menu frame failed");
TEST_ASSERT(e, menu.selected == 2,
"a stationary pointer stole the selection back to %d", menu.selected);
TEST_ASSERT(e, menu.activated == false,
"a stationary pointer activated the menu");
} CLEANUP {
if ( shot != NULL ) {
SDL_DestroySurface(shot);