Skip to content

feat(input): mouse navigation foundations + main menu support#414

Draft
adstep wants to merge 1 commit into
pnn64:mainfrom
adstep:adstep/mouse-support-plan
Draft

feat(input): mouse navigation foundations + main menu support#414
adstep wants to merge 1 commit into
pnn64:mainfrom
adstep:adstep/mouse-support-plan

Conversation

@adstep

@adstep adstep commented May 23, 2026

Copy link
Copy Markdown
Contributor

Why

Every menu in DeadSync is key/gamepad/pad-only today, but a lot of casual users (and anyone running on a laptop) reasonably expect to be able to click on UI elements. This change adds the foundations for mouse input and lights it up on the main menu so the pattern is proven end-to-end. Wider screen coverage will follow in separate PRs.

Approach

Mouse augments the existing input system rather than replacing it. The shape:

  • Pointer events convert to SM-logical (top-left origin) coordinates at the window boundary, so screens never have to think about device pixels, DPI scaling, or aspect-ratio letterboxing.
  • A new InputSource::Mouse variant lets screens synthesize regular InputEvents for clicks where it's natural. The existing per-screen handle_input paths keep owning confirm/back semantics.
  • Screens that opt in build a small Vec<HitRect> each frame next to their actor list and call hit_test to map pointer position to a row id. The same helper that lays out the visible labels (menu_list::item_rects) also produces the rects, so the two can't drift.
  • A single seam in app::dispatch_pointer_event routes each event to the active screen and gets back both the action to dispatch and a "pointer is over something interactive" flag. The flag drives a cursor swap to a magenta hover variant. Screens that haven't opted in are no-ops.

Mouse input is gated behind an EnableMouseInput config flag (default off) so this can land without affecting any existing setup. Keyboard, gamepad, and pad input are untouched on every screen.

What's in this PR

Foundations

  • engine::space::{LogicalPos, WindowPos} newtypes + WindowPos::to_logical so coordinate spaces can't be silently confused.
  • engine::input::{PointerEvent, PointerKind, MouseButton} plus a new InputSource::Mouse variant.
  • winit::WindowEvent::{CursorMoved, CursorLeft, MouseInput, MouseWheel} wired through app::dispatch_pointer_event.
  • ShellState.pointer_pos_logical cache, refreshed on resize so screens never see stale projections.
  • Pixel-source wheel scrolls (touchpads) normalize to "lines" via a PIXEL_PER_LINE = 20.0 heuristic.
  • EnableMouseInput config flag, default off — pointer events are dropped on the floor unless enabled.

Main menu support

  • Hover over a row highlights it silently (no change-sfx chirp on every mouse pixel).
  • Left-click on a row launches that row (same effect as Start).
  • Right-click anywhere acts as back (same effect as the back key).
  • Cursor swaps to a themed magenta hover variant when hovering an interactive row.

Themed cursor

  • Custom navigate-style cursor PNG (downsampled from a Flaticon source) with a hover variant in the default theme's decorative magenta. Hotspot pinned at the visual tip.
  • scripts/gen_cursors.py rebuilds both variants from the source asset, auto-detects the hotspot, and syncs them into target/<profile>/assets so tweaks don't need a cargo rebuild.
  • Honors the existing HideMouseCursor preference.

Reusable helpers (used by the main menu, ready for other screens)

  • menu_list::item_rects — hit rect math for vertical menus, next to the actor builder.
  • components::shared::menu_pointer::MenuPointer — hover/click bookkeeping for list-style screens.
  • components::shared::hitbox::{HitRect, hit_test} — geometry primitives.

@adstep adstep marked this pull request as ready for review May 23, 2026 03:08
@adstep adstep changed the title feat(input): Phase 1 — plumb winit pointer events (mouse support foundations) feat(input): plumb winit pointer events (mouse support foundations) May 23, 2026
@adstep adstep marked this pull request as draft May 23, 2026 03:10
@adstep adstep changed the title feat(input): plumb winit pointer events (mouse support foundations) feat(input): mouse navigation foundations + main menu support Jun 1, 2026
@adstep adstep force-pushed the adstep/mouse-support-plan branch from b84bed6 to 5c0b4c0 Compare June 1, 2026 03:22
Adds optional mouse support, gated behind a new EnableMouseInput config
flag that defaults off. Keyboard, gamepad, and pad input are unchanged
on every screen.

Foundations:
- engine::space::{LogicalPos, WindowPos} newtypes + to_logical conversion
  so coordinate spaces can't be silently confused.
- engine::input::{PointerEvent, PointerKind, MouseButton} plus a new
  InputSource::Mouse variant.
- winit CursorMoved / CursorLeft / MouseInput / MouseWheel routed through
  a single app::dispatch_pointer_event seam. Pixel-source wheel scrolls
  (touchpads) normalize to lines via a PIXEL_PER_LINE = 20.0 heuristic.
- ShellState.pointer_pos_logical cache, refreshed on resize so screens
  never see stale projections.

Main menu:
- Hover over a row highlights it silently (no chirp on every mouse pixel).
- Left-click on a row launches that row.
- Right-click anywhere acts as back.
- Cursor swaps to a magenta hover variant when over an interactive row.

Themed cursor:
- Custom navigate-style cursor PNG with a default-theme magenta hover
  variant, hotspot pinned at the visual tip.
- scripts/gen_cursors.py rebuilds both variants from the source asset,
  auto-detects the hotspot, and syncs into target/<profile>/assets so
  tweaks don't need a cargo rebuild.
- Source artwork attributed to zky.icon / Flaticon beside the asset.
- Honors the existing HideMouseCursor preference.

Reusable helpers (used by the main menu, ready for other screens):
- menu_list::item_rects pairs hit-rect math with the actor builder so
  layout and hit testing share constants.
- components::shared::menu_pointer::MenuPointer encapsulates hover/click
  bookkeeping for list-style screens.
- components::shared::hitbox::{HitRect, hit_test} geometry primitives.

Adds 11 passing tests across hitbox, menu_list, menu_pointer, and the
main-menu modules.
@adstep adstep force-pushed the adstep/mouse-support-plan branch from 5c0b4c0 to b8be017 Compare June 1, 2026 03:30
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.

1 participant