fa3eb59bbe
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 3c80f93203 |
Import map screenshots into the board, and edit entities from the map
The existing clipboard button now routes images: a map-table shot goes to the vision pipeline, anything else to the text OCR path as before. The decision runs in a worker, since even the cheap pre-filter costs ~0.3s and solve() takes 10-20s. solve() rejecting counts as "not a map" and falls through to OCR, because it is the reliable verdict (0 false positives over 122 text screenshots) where the pre-filter lets ~6% through; reporting a failure there would mean a text screenshot never got read at all. Grid first, units second. The one modal confirms or fixes the geometry only: the screenshot with the reconstructed lattice drawn over it, plus four draggable handles on one cell's corners. Four corners pin a homography exactly (8 DOF, 2 equations each), and dragging any of them refits the whole grid live. Detection deliberately does not run until this is accepted -- every unit position is expressed in grid coordinates, so detecting against a grid about to be dragged would only be thrown away. Once accepted the screenshot is rectified into board space and drawn as the map's backdrop. Pre-warping is what makes it drawable at all: cairo has no projective transform, but a rectified image places with a plain scale and translate. Detected units then appear as proposals ON the map, drawn hollow -- the same shape the map already uses for "this might be where it is", which is exactly what a proposal is. Clicking one offers accept (with the detected type or a corrected one) or reject; the header gains accept-all and remove-screenshot, and removing the screenshot drops every proposal never accepted, since they were only ever readings of it. Separately, right-clicking any entity now opens an edit menu: change type, change id, change position, delete. Which actions appear follows what the entity actually has -- only Target/Ally carry a TargetType, Spotter's id is an int, and the Nest is singular so it cannot be deleted. Changing an existing target's type or id had no UI at all before this. Also fixes warp_to_map, which composed only the lattice homography and dropped the discrete (si,sj,du,dv) mapping that pins lattice indices to named cells, so every automatically solved screenshot landed in the wrong place. It happened to test fine because manual solutions have an identity mapping. While there, the same routine had an off-by-one for a negative axis sign (si*u+du runs from col+1 down to col across a cell, so floor() named the neighbour); both now go through one shared GridSolution.grid_of. Verified end to end through the real widgets on a fixture: grid phase yields no proposals, four handles, a drag refits and still names cells correctly, reset restores, a degenerate drag survives, accept warps to a 2000x1000 overlay, detection then yields proposals that hit-test, accept and reject correctly, and removing the screenshot keeps accepted units only. Completes the FEnigma rename in app.py (APP_ID, window title, class). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| e352dff531 |
Map: render entity game icons instead of plain dots once zoomed in
Below ICON_MIN_CELL_PX (42px cell width) everything still draws as the plain colored dot it always has, a game icon at that size would just be an illegible smudge. Past that threshold, Nest/Target/Ally markers switch to the game's own unit icon for their type, drawn as a Cairo surface (raw Cairo draw_func, not GTK widgets, so this loads PNGs directly via cairo.ImageSurface.create_from_png(), cached per path so a repeat draw doesn't re-hit disk). icons.py's target_icon_path(type, is_ally) is the TargetType -> icon file mapping, best-effort guesses the same way the Shell descriptions were (flagged for correction): most types map onto the game's own Enemy_*/Friendly_* unit icons (is_ally picks which set, falling back to Enemy_ if a given type has no Friendly_ counterpart), TANK reuses the Armor_Mechanized artwork (no dedicated tank icon exists), and STRIKE gets its own crosshair (assets/icons/misc/Crosshair.png, not a unit icon at all, a planned impact point) rather than a unit icon. An ambiguous candidate (hollow, dashed-ring marker) never switches to the icon regardless of zoom, an icon there would look more confident about an unconfirmed position than the dashed ring is supposed to convey. Verified with rendered screenshots at both zoom levels: plain dots below the threshold, real icons above it (Tank/Infantry/MarineGarrison/ Nest all confirmed showing their correct icons), and the Strike crosshair specifically. |
|||
| 3b209e743e |
Shell icon picker: fix real sizing bugs, crop icon padding, inline grid for Add Strike
Several rounds of actually looking at the running app instead of just
the code, in order:
- The icon-only shell button on a firing card, and the popover-based
picker, both fought a genuine GTK sizing quirk: Gtk.Picture's natural
size is the source image's full native resolution (512x256) no
matter what set_size_request() says, so the button's own requested
size swung unpredictably depending on layout context (sometimes
comically wide, sometimes squeezed tiny). Fixed by pre-scaling the
actual pixel data with GdkPixbuf first (preserving real aspect ratio)
and wrapping the already-correctly-sized result, nothing left for
the surrounding layout to guess about.
- The shell icon PNGs themselves carry a lot of built-in transparent
padding around the actual artwork. Cropped all of them (except MSTD,
see below) to the same shared bounding box, computed as the union of
every file's real content so nothing gets clipped and every icon
stays aligned the same way.
- MSTD (mustard/YX blister agent) removed from the Shell enum entirely:
its own game icon is stamped 'UNAVAILABLE IN DEMO', it isn't actually
a real selectable shell in the game.
- Extra empty chrome around icon-only buttons turned out to be GTK CSS
specificity, not a missing property: GTK's CSS has no !important at
all (confirmed, its parser rejects it outright), and separately,
Gtk.MenuButton's CSS node is literally named 'menubutton', not
'button', so a 'button.<class>' selector silently matched zero
elements on it. The actual leftover padding lives on MenuButton's
internal child button node, reached with a child-combinator selector
('menubutton.<class> > button').
- The Add Strike dialog's shell picker is now an inline radio-style
grid (build_shell_grid(), Gtk.ToggleButton.set_group()) instead of a
button that opens a submenu popover: it's a 'pick one before
proceeding' dialog with room to just show every option up front,
hiding them behind an extra click didn't earn anything. The firing
card and its tighter row still use the popover version.
- Grid is 3 columns (was 4), sized bigger now that padding is real
content, not just empty canvas.
Also two unrelated but real bugs found and fixed while chasing this,
from actually reproducing what screenshots showed instead of taking
them as pure cosmetic complaints:
- Selecting a firing card shifted every icon/label inside it inward by
2px: only .firing-card-selected had a border, unselected cards had
none at all, so gaining a border on selection shifted the box's
content instead of just changing a color. Border is now reserved at
the same width on every card, always, selection only changes its
color from transparent.
- Changing a target's powder charge visibly shifted the whole card:
the charge segment buttons had no valign, so once the row got taller
(the bigger shell icon next to them), they silently inherited the
default FILL alignment and stretched into ovals instead of staying a
fixed circle. They also relied on plain 'flat'/'suggested-action' for
their off/on look, which draws no border at all when off, making an
unselected segment invisible against the card background. Both fixed
with a dedicated fixed-size, always-outlined CSS class and explicit
valign/halign=CENTER.
- The AZ column's screen position depended on the ELEV value's digit
count (elevation recalculates per charge picked), fixed with a
reserved width_chars on the elevation label.
Verified at each step by actually rendering the affected widget/dialog/
card to a PNG and looking at it (including a real FiringPanel card
before/after a charge change, pixel-diffed to confirm nothing but the
intended fill state moved), not by reasoning about the code alone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|||
| a8f77b98cb |
Fix the shell icon picker: real icon size, grid layout, no horizontal scroll
Three real bugs from the first pass, all from actually looking at the running app instead of just the code: - Icons rendered tiny regardless of source resolution: Gtk.Image caps displayed size to GTK's icon-size classes (built for symbolic 16/32px icons) no matter what you load into it. Switched to Gtk.Picture, which sizes by the image's real dimensions. - The popover's plain vertical list of rows squeezed down to a near- unreadable width: ScrolledWindow sizes to its content's minimum, not natural, size unless told otherwise, and a wrapping description label's minimum width can shrink to almost nothing. Replaced with a FlowBox grid (icon + blast radius under it, full description as a tooltip) with an explicit natural width. - That grid's column count was pinned to a fixed minimum, so a popover that didn't have the room for that many columns overflowed sideways and grew a horizontal scrollbar. min_children_per_line dropped to 1 (lets it reflow to fewer columns instead) plus hscrollbar_policy=NEVER as a hard backstop, not everyone has a horizontal scroll wheel. Also: the firing card's shell button now shows the icon alone (it already has the shell's short code baked in, a text label next to it was redundant on an already-tight row), sized to 64px wide/32px tall to actually be legible, and a new small CSS rule trims the excess button chrome around an icon-only face so the button isn't visibly much larger than the icon it holds. |
|||
| 2b73a96c7a |
Replace the plain-text shell dropdown/list with an icon picker
Both places the app asks for a shell (the Add Strike dialog's Adw.ComboRow, and the firing panel's per-target shell popover) used to show just the bare enum name in a plain list, nothing conveying what the shell actually is. New icons.py builds a shared MenuButton + popover from the game's own shell icon set (assets/icons/shells/, already named to match Shell.name exactly): each row shows the shell's icon, name, description, and blast radius, and the button face updates to match whichever one gets picked. Verified via a GTK smoke test that the button and its popover build and open without crashing. |