diff --git a/solitaire_engine/src/cursor_plugin.rs b/solitaire_engine/src/cursor_plugin.rs index e554982..0e367ce 100644 --- a/solitaire_engine/src/cursor_plugin.rs +++ b/solitaire_engine/src/cursor_plugin.rs @@ -2,9 +2,19 @@ //! //! **Cursor icons** (`update_cursor_icon`) //! - Cards are being dragged → `Grabbing` (closed hand) +//! - A UI `Button` entity is hovered (and no drag in progress) → `Pointer` +//! (the hand-with-extended-index-finger icon). This telegraphs +//! clickability for every modal button, HUD action, mode-launcher +//! card, settings toggle, etc. //! - Cursor hovers over a face-up draggable card → `Grab` (open hand) //! - Otherwise → `Default` (arrow) //! +//! Priority order: dragging > button-hover > card-hover > default. A +//! button-overlapping-a-card edge case favours `Pointer` because UI +//! elements take precedence over world-space cards; in practice +//! buttons are always on UI nodes and cards are sprites, so they +//! cannot occupy the same hit region simultaneously. +//! //! **Drop-target highlights** (`update_drop_highlights`) //! While a drag is in progress every `PileMarker` sprite is tinted: //! - **Green** if the dragged stack can legally land there. @@ -70,6 +80,31 @@ impl Plugin for CursorPlugin { // #31 — Cursor icon // --------------------------------------------------------------------------- +/// Pure decision function for the cursor icon, separated from the Bevy +/// system so it can be unit-tested without `PrimaryWindow` / +/// `Camera` / `Time` plumbing. +/// +/// Priority order (highest first): +/// 1. `is_dragging` → `Grabbing` +/// 2. `any_button_hovered` → `Pointer` +/// 3. `any_card_hovered` → `Grab` +/// 4. otherwise → `Default` +fn pick_cursor_icon( + is_dragging: bool, + any_button_hovered: bool, + any_card_hovered: bool, +) -> SystemCursorIcon { + if is_dragging { + SystemCursorIcon::Grabbing + } else if any_button_hovered { + SystemCursorIcon::Pointer + } else if any_card_hovered { + SystemCursorIcon::Grab + } else { + SystemCursorIcon::Default + } +} + /// Updates the primary-window cursor icon based on drag state and hover. fn update_cursor_icon( drag: Res, @@ -77,32 +112,39 @@ fn update_cursor_icon( cameras: Query<(&Camera, &GlobalTransform)>, layout: Option>, game: Option>, + button_q: Query<&Interaction, With