keep the selection panel half a tile off what it describes

The panel had one distance for the view edges, the widgets it steps around,
and the selection alike, so it stood eight pixels from a building and touched
it outright along the top. The gap from the selection is now its own value,
half a tile, and it is horizontal only -- the top edges stay level.

It is sampled where the tile size is known, in the same moment as the anchor
rectangle, and travels with it: a rectangle frozen in one moment has no
meaningful distance to a tile size measured in another. chooseSide now takes
the gap in place of the margin, the band having already taken the margin off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
2026-08-17 16:24:14 +02:00
parent abaccc45b5
commit 392a2b8d00
7 changed files with 115 additions and 61 deletions

View File

@@ -10,6 +10,12 @@
// owner places them in one ordered pass, each into the space the earlier ones left free,
// and these are the rules they place themselves by. Pure geometry -- no widget is
// involved, which is what lets the rules be tested without a display.
//
// Two distances run through these rules and are deliberately different (see
// REQ-UI-SELECTION-PANEL). marginPx is the edge margin: what a widget keeps from the
// view's edges and from the widgets it steps around. selectionGapPx is the gap the
// selection panel keeps from the selection it describes -- half a tile, which is the
// wider of the two, so the panel stands clear of the objects rather than touching them.
// The lowest bottom edge available to a widget occupying the horizontal span
// [leftPx, rightPx] inside band: the band's own bottom, or marginPx above the topmost
@@ -29,19 +35,21 @@ enum class PanelSide
// The side a panel widthPx wide takes beside anchorRect: the right of it where it fits
// within band, otherwise the left, and where it fits on neither, whichever side leaves
// more room -- the one case in which the panel ends up over the selection
// (REQ-UI-SELECTION-PANEL). Decided once when the selection starts and kept for as long
// as it lasts, so a card that grows later never flips the panel across the object.
// (REQ-UI-SELECTION-PANEL). The room a side offers is what is left of it once the panel's
// gap from the selection is taken off. Decided once when the selection starts and kept for
// as long as it lasts, so a card that grows later never flips the panel across the object.
PanelSide chooseSide(const QRect& band, const QRect& anchorRect, int widthPx,
int marginPx);
int selectionGapPx);
// Where a panel of wantedSize stands beside anchorRect on the given side: separated from
// it by marginPx and growing away from it, its top edge on the anchor's top edge, pushed
// inside band and above whatever occupies it. The returned height is short of
// wantedSize's when there was not enough room, which is the caller's cue to scroll its
// content (REQ-UI-SELECTION-PANEL).
// it by selectionGapPx and growing away from it, its top edge on the anchor's top edge,
// pushed inside band and above whatever occupies it. The gap is horizontal only -- the
// panel's top sits level with the anchor's, however wide the gap. The returned height is
// short of wantedSize's when there was not enough room, which is the caller's cue to
// scroll its content (REQ-UI-SELECTION-PANEL).
QRect placeBesideAnchor(const QRect& band, const QRect& anchorRect, PanelSide side,
QSize wantedSize, const std::vector<QRect>& occupiedRects,
int marginPx);
int selectionGapPx, int marginPx);
// Where a panel of wantedSize stands once the player has dragged it to desiredTopLeftPx:
// at that point, by the same rules that place it beside a selection -- pushed inside band,