Grid Cell Mouse Events #142

Closed
opened 2025-11-27 18:34:44 +00:00 by john · 0 comments
Owner

Grid-level callbacks for mouse interaction with individual cells.

Context

Games need to know which grid cell the mouse is over for:

  • Highlighting the hovered cell
  • Showing entity/terrain info in a tooltip or panel
  • Click-to-select gameplay
  • Pathfinding destination selection

Events are at the Grid level, not per-cell (per-cell would be enormously heavyweight).

Definition of Done

Callbacks

  • grid.on_cell_enter - called with (cell_x, cell_y) when mouse enters a new cell
  • grid.on_cell_exit - called with (cell_x, cell_y) when mouse leaves a cell
  • grid.on_cell_click - called with (cell_x, cell_y) when cell is clicked

Queryable State

  • grid.hovered_cell property → (cell_x, cell_y) tuple or None if mouse not over grid

Edge Cases

  • Mouse over grid but outside valid cell range (pan/zoom edge) → hovered_cell = None, no cell events
  • Mouse moves from cell A to cell B → exit(A), then enter(B)
  • Mouse leaves grid entirely → exit(last_cell), hovered_cell = None

Technical Notes

Coordinate Transformation

Grid has complex coordinate system:

  • grid.center is in grid-space pixel coordinates
  • Grid can be panned and zoomed
  • Center can be negative or beyond cell bounds

C++ must transform screen coords → grid-space → cell coords:

screen_pos = mouse position
local_pos = screen_pos - grid.global_position
grid_space_pos = local_pos + grid.center - (grid.size / 2)
cell_x = floor(grid_space_pos.x / cell_width)
cell_y = floor(grid_space_pos.y / cell_height)

if cell in valid range:
    return (cell_x, cell_y)
else:
    return None  # Mouse over grid background, not a cell

State Tracking (C++ side)

// Per grid, tracked each frame
struct GridMouseState {
    std::optional<sf::Vector2i> current_cell;
    std::optional<sf::Vector2i> previous_cell;
};

// On mouse move within grid bounds:
new_cell = screen_to_cell(mouse_pos)
if new_cell != current_cell:
    if current_cell.has_value() && on_cell_exit:
        call_python(on_cell_exit, current_cell)
    if new_cell.has_value() && on_cell_enter:
        call_python(on_cell_enter, new_cell)
    current_cell = new_cell

Callback Validation

Apply same signature checking as other callbacks:

grid.on_cell_enter = lambda: print("entered")
# Warning: on_cell_enter expects (cell_x, cell_y), got 0 args

Example Usage

def on_hover_cell(cx, cy):
    entity = grid.entity_at(cx, cy)
    if entity:
        info_panel.text = f"Entity: {entity.name}"
    else:
        tile = grid.at(cx, cy)
        info_panel.text = f"Terrain: {tile.name}"

def on_click_cell(cx, cy):
    player.move_to(cx, cy)

grid.on_cell_enter = on_hover_cell
grid.on_cell_click = on_click_cell

Dependencies

  • Blocked by: AABB / Hit Testing System (NEW) - need grid hit testing
  • Blocked by: #102 (Global Position) - grid may be nested in Frame
  • Blocked by: on_move Event (NEW) - internal mouse tracking
  • Extends: #111 (Click Events) - adds cell-level click
  • Enables: #45 (Accessibility) - "what's on this cell" affordance
Grid-level callbacks for mouse interaction with individual cells. ## Context Games need to know which grid cell the mouse is over for: - Highlighting the hovered cell - Showing entity/terrain info in a tooltip or panel - Click-to-select gameplay - Pathfinding destination selection Events are at the Grid level, not per-cell (per-cell would be enormously heavyweight). ## Definition of Done ### Callbacks - [ ] `grid.on_cell_enter` - called with `(cell_x, cell_y)` when mouse enters a new cell - [ ] `grid.on_cell_exit` - called with `(cell_x, cell_y)` when mouse leaves a cell - [ ] `grid.on_cell_click` - called with `(cell_x, cell_y)` when cell is clicked ### Queryable State - [ ] `grid.hovered_cell` property → `(cell_x, cell_y)` tuple or `None` if mouse not over grid ### Edge Cases - [ ] Mouse over grid but outside valid cell range (pan/zoom edge) → `hovered_cell = None`, no cell events - [ ] Mouse moves from cell A to cell B → exit(A), then enter(B) - [ ] Mouse leaves grid entirely → exit(last_cell), `hovered_cell = None` ## Technical Notes ### Coordinate Transformation Grid has complex coordinate system: - `grid.center` is in grid-space pixel coordinates - Grid can be panned and zoomed - Center can be negative or beyond cell bounds C++ must transform screen coords → grid-space → cell coords: ``` screen_pos = mouse position local_pos = screen_pos - grid.global_position grid_space_pos = local_pos + grid.center - (grid.size / 2) cell_x = floor(grid_space_pos.x / cell_width) cell_y = floor(grid_space_pos.y / cell_height) if cell in valid range: return (cell_x, cell_y) else: return None # Mouse over grid background, not a cell ``` ### State Tracking (C++ side) ```cpp // Per grid, tracked each frame struct GridMouseState { std::optional<sf::Vector2i> current_cell; std::optional<sf::Vector2i> previous_cell; }; // On mouse move within grid bounds: new_cell = screen_to_cell(mouse_pos) if new_cell != current_cell: if current_cell.has_value() && on_cell_exit: call_python(on_cell_exit, current_cell) if new_cell.has_value() && on_cell_enter: call_python(on_cell_enter, new_cell) current_cell = new_cell ``` ### Callback Validation Apply same signature checking as other callbacks: ```python grid.on_cell_enter = lambda: print("entered") # Warning: on_cell_enter expects (cell_x, cell_y), got 0 args ``` ## Example Usage ```python def on_hover_cell(cx, cy): entity = grid.entity_at(cx, cy) if entity: info_panel.text = f"Entity: {entity.name}" else: tile = grid.at(cx, cy) info_panel.text = f"Terrain: {tile.name}" def on_click_cell(cx, cy): player.move_to(cx, cy) grid.on_cell_enter = on_hover_cell grid.on_cell_click = on_click_cell ``` ## Dependencies - **Blocked by**: AABB / Hit Testing System (NEW) - need grid hit testing - **Blocked by**: #102 (Global Position) - grid may be nested in Frame - **Blocked by**: on_move Event (NEW) - internal mouse tracking ## Related Issues - Extends: #111 (Click Events) - adds cell-level click - Enables: #45 (Accessibility) - "what's on this cell" affordance
john added this to the Cursor, Grid, and Callback project 2025-11-27 19:30:43 +00:00
john closed this issue 2025-11-28 18:40:15 +00:00
Sign in to join this conversation.
No Milestone
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: john/McRogueFace#142
No description provided.