Map: draw the fine 10x10 subdivision grid for the cell the cursor is in

Tracks the hovered large cell (col, row) separately from marker
hit-testing, since it needs to update on any cursor motion, not just
when crossing a marker's hit radius, redrawn only when the actual cell
changes, not on every pixel of motion within the same one. Lines drawn
inside the existing clip region so they never bleed past the visible
viewport at any zoom/pan state, dimmer than the main grid lines
(0.6x alpha, tuned down further after an initial too-loud pass) so
they read as a subtle aid, not competing visual noise.

Verified with a rendered screenshot: the subdivision lines appear only
within the hovered cell, correctly clipped, and stay faint against the
main grid.
This commit is contained in:
Dominik Moritz Roth 2026-08-09 20:48:48 +02:00
parent e352dff531
commit 558e9d117a

View File

@ -117,6 +117,12 @@ class GridCanvas(Gtk.DrawingArea):
self.on_right_click = None # callback(Coord, x, y), fired on right-click (unless placing) self.on_right_click = None # callback(Coord, x, y), fired on right-click (unless placing)
self.hide_dead_from_map = False # off by default; toggled from the firing panel toolbar self.hide_dead_from_map = False # off by default; toggled from the firing panel toolbar
# Which large cell the cursor is currently over, (col, row) both
# floored, or None off the map/off the widget entirely. Redrawn
# only when this actually changes cell (not on every pixel of
# motion within the same one), see _draw_hover_subgrid().
self._hover_cell: tuple[int, int] | None = None
# Placement mode: while armed, the next left-click calls # Placement mode: while armed, the next left-click calls
# placement_callback(Coord) instead of doing the normal # placement_callback(Coord) instead of doing the normal
# select/hit-test, and (if placement_preview_radius_km is set) a # select/hit-test, and (if placement_preview_radius_km is set) a
@ -423,6 +429,12 @@ class GridCanvas(Gtk.DrawingArea):
if self.on_cursor_move is not None: if self.on_cursor_move is not None:
self.on_cursor_move(cursor_km) self.on_cursor_move(cursor_km)
col, row = cursor_km
new_cell = (int(col), int(row)) if (0 <= col < COLS and 0 <= row < ROWS) else None
if new_cell != self._hover_cell:
self._hover_cell = new_cell
self.queue_draw()
if self.placement_callback is not None: if self.placement_callback is not None:
self._placement_cursor_km = cursor_km self._placement_cursor_km = cursor_km
self.queue_draw() self.queue_draw()
@ -440,6 +452,9 @@ class GridCanvas(Gtk.DrawingArea):
self._last_pointer_px = None self._last_pointer_px = None
if self.on_cursor_move is not None: if self.on_cursor_move is not None:
self.on_cursor_move(None) self.on_cursor_move(None)
if self._hover_cell is not None:
self._hover_cell = None
self.queue_draw()
if self.placement_callback is not None: if self.placement_callback is not None:
self._placement_cursor_km = None self._placement_cursor_km = None
self.queue_draw() self.queue_draw()
@ -552,6 +567,7 @@ class GridCanvas(Gtk.DrawingArea):
cr.rectangle(MARGIN_LEFT + view.pad_x, MARGIN_TOP + view.pad_y, view.grid_w, view.grid_h) cr.rectangle(MARGIN_LEFT + view.pad_x, MARGIN_TOP + view.pad_y, view.grid_w, view.grid_h)
cr.clip() cr.clip()
self._draw_hover_subgrid(cr, view)
self._draw_geo_overlays(cr, view) self._draw_geo_overlays(cr, view)
self._draw_firing_arrows(cr, view) self._draw_firing_arrows(cr, view)
self._draw_blast_radius(cr, view) self._draw_blast_radius(cr, view)
@ -600,6 +616,29 @@ class GridCanvas(Gtk.DrawingArea):
cr.restore() cr.restore()
def _draw_hover_subgrid(self, cr, view) -> None:
"""The fine 10x10 x:y subdivision lines for whichever large
cell the cursor is currently over, for precise sub-cell
targeting, most useful once zoomed in enough that a single
large cell actually has room to show them meaningfully."""
if self._hover_cell is None:
return
cell_col, cell_row = self._hover_cell
r, g, b, a = GRID_LINE
cr.set_source_rgba(r, g, b, a * 0.6) # fainter than the plain grid lines, not louder
cr.set_line_width(1)
x0, y0 = self._km_to_px(view, (cell_col, cell_row))
x1, y1 = self._km_to_px(view, (cell_col + 1, cell_row + 1))
for i in range(1, 10):
x, _ = self._km_to_px(view, (cell_col + i / 10, cell_row))
cr.move_to(x, y0)
cr.line_to(x, y1)
for i in range(1, 10):
_, y = self._km_to_px(view, (cell_col, cell_row + i / 10))
cr.move_to(x0, y)
cr.line_to(x1, y)
cr.stroke()
def _draw_marker(self, cr, view, point_km, color, label, def _draw_marker(self, cr, view, point_km, color, label,
canvas_width, canvas_height, *, hollow=False, dim=False, canvas_width, canvas_height, *, hollow=False, dim=False,
selected=False, coord=None, extra_line=None, icon_surface=None) -> None: selected=False, coord=None, extra_line=None, icon_surface=None) -> None: