The panel is sized to its card, so anything that changes the card's width drags
the panel with it. The status caption changes as a building works -- "producing"
is shorter than "missing input", which is shorter than "output full" -- and the
panel twitched every time it did. On a Smelter it also changed height, because a
narrower card wraps its item chips differently.
The pill now reserves room for the widest caption it can ever show, so the
header keeps one width whatever the state is. The captions were spelled out in
the switch that chose them, which left no way to ask for the set; they come from
one function now, and the set is what the reservation is built from.
Ship cards do the same with the behaviour names (REQ-UI-SHIP-BEHAVIOR), which
change just as often while a ship fights.
Miner card, sampled every tick for 900 ticks across two different-length
captions: one distinct width, one distinct height. The reservation is what does
it -- the same run without it sits at 138px, with it at 155px, the width of the
longest caption.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
Six things the player reported, five of them about where things sit and what is
listed:
- HP goes to the top of the runtime group, above everything else a card shows.
Only the HQ had it elsewhere; the ship and station cards already led with it.
- A construction site's progress moves out of the runtime group's place and
directly under the header, so how far along the site is reads before what it
is configured to become.
- The production bar moves between the input and output buffers, so a producing
building reads in the direction its materials flow: what goes in, what is
being made of it, what has come out. BufferSection held both buffer sections
and so could not be split around it; the two sections and the production
section are now the card's own, in that order.
- Locked items are left out of the buffers. An auto-recipe building's buffers
are sized over every recipe of its type, so a Smelter carried an input for
quartz -- which the player cannot mine yet -- and an output for the silicon it
would smelt into. Both are dropped now, as everywhere else that hides what is
not unlocked.
- An idle auto-recipe building lists what it handles rather than nothing. It has
no selected recipe to name, so a Reprocessing Plant between cycles showed no
output buffer at all. Its sections now list the unlocked items of every recipe
of its type -- the union its buffers were sized over -- without a per-cycle
denominator, since no one recipe is in force.
The sixth was the bars vanishing whenever the window lost focus, to a modal
dialog or to another application. They were filled with the palette's current
highlight, and the palette follows the window's focus: the inactive group's
highlight sits close enough to the card's background to read as gone. They ask
for the active group by name now.
Measured on a freshly built Smelter, which is the case that showed three of
these at once:
INPUT BUFFERS 0 Copper Ore | 0 Iron Ore | 0 Scrap
PRODUCTION idle
OUTPUT BUFFER 0/2 Copper Ingot | 0/2 Iron Ingot
Quartz and silicon filtered out, production between the buffers, and an idle
building still saying what it handles.
Requirements updated for the ordering, the locked-item rule and the idle
auto-recipe listing (REQ-UI-SELECTION-CARD, REQ-UI-SINGLE-SELECTION,
REQ-UI-PRODUCTION-PROGRESS, REQ-UI-HQ-PANEL).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
Clicking something sometimes left the panel collapsed to its scroll bar, most
often for field entities.
Same cause as the controls panel's collapse in d7c6734: a widget created under
an already-visible parent starts hidden, and a layout counts a hidden item as
empty. rebuildContent() built the card, added it to the body layout and
measured immediately, so the body reported nothing but its own margins. A width
of nearly zero makes heightForWidth() on the wrapped labels enormous, that
overflows the band, and refit() then caps the height and widens by the scroll
bar -- leaving the panel exactly one scroll bar wide.
It could not recover on the next tick either, because a word-wrapped label's
size hint follows its current width: once narrow, it keeps reporting narrow and
tall. So refit() now measures the body at the width cap rather than at whatever
width the panel currently has, and invalidates the body layout before reading
it -- cards are built and discarded whole, so its cached hint otherwise
describes the card before this one.
Field entities hit it more often because a field selection publishes twice,
actors then debris, so one click rebuilds the card twice.
The same defect existed one level down, where parts are rebuilt while the card
is already visible: ItemChipRow::rebuildChips() and RecipeSummaryRow::rebuild()
now show what they create. Those were measuring the buffer section and the
recipe line as empty on every recipe change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
The cards were assembled from plain labels carrying whole blocks of text.
Replace those with the widget vocabulary the requirements describe, so a part
means the same thing wherever it appears and a card is a list of parts rather
than a string builder.
The parts, all free of Simulation and GameConfig -- they take prepared values,
and the contents work out what those are:
- StatRow, BarRow, SectionBox: label/value line, captioned fill bar, captioned
group. The bar is one part for three things: construction progress,
production progress, and HP.
- ItemChip / ItemChipRow: buffered items as icon, count and sub-line
(REQ-UI-SINGLE-SELECTION). An input chip carries its per-cycle amount, an
output chip its count against the buffer capacity. The chips are rebuilt only
when the set of items changes, so a 30 Hz refresh moves numbers rather than
widgets.
- RecipeSummaryRow: inputs, arrow, outputs, cycle time (REQ-UI-RECIPE-SUMMARY),
which is now the panel's only display of the cycle time.
- CountRow, StatusPill, EmptyNote.
Behaviour that changed with them:
- The station card shows damage, range and fire rate as the requirement asks
(REQ-UI-STATION-STATS-PANEL) rather than the combined DPS it showed before.
- A ship's behaviour moves from a stats row into the card header
(REQ-UI-SHIP-BEHAVIOR). ShipStatsPanel keeps setBehavior for the balancing
tool's inspect window, which has no header to put it in.
- A construction site's card shows a progress bar and the "no buffers until
built" note (REQ-UI-SELECTION-CARD), and now also its recipe summary, since
that is configuration and a site carries it (REQ-BLD-SITE-CONFIG). Costing a
shipyard site's schematic needed computeShipyardRequiredMaterials to take a
stored configuration as well as a live building -- one overload, so the
module sum still exists once.
ShipStatsPanel is rebuilt on StatRow, BarRow and SectionBox, so the selection
card, the layout dialog's design preview and the balancing tool read alike.
Those three parts are compiled into the balancing target, which does not link
the ui library; keeping them sim-free is what makes that possible, and the
build enforces it.
Build clean, 541 tests pass, app and balancing tool both run with no Qt
warnings. Visual check pending.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
The panel rendered every selection out of a single pool of member widgets,
hidden and shown per branch, so each build path had to remember to hide the
other branches' widgets. That coupling produced the two defects fixed in
668ce0f, and the per-type detail the requirements now ask for would only add
more of it.
The pool is gone. SelectionPanel keeps the category arbitration, the float and
the hide-when-empty behaviour, and hosts exactly one SelectionContent at a
time; SelectionContentFactory picks which one from the selection alone. Each
card is one row of the catalog in REQ-UI-SELECTION-CONTENT and owns only its
own widgets.
The card structure (REQ-UI-SELECTION-CARD) lives in the base class: a header
with an identity symbol, a name and one right slot, then a configuration group
and a runtime group. A construction site keeps its configuration and has its
whole runtime group replaced by the construction progress, decided once there
rather than in every card (REQ-BLD-SITE-CONFIG).
Also implemented here:
- REQ-UI-SELECTION-STATUS: the header status dot, taken from the simulation's
own getProductionStatus() so the panel and the world's status light cannot
disagree.
- REQ-UI-SELECTION-AGGREGATE: belt-subsystem tiles and debris-only selections
collapse into one card with a count instead of a count summary.
- REQ-UI-HQ-PANEL: the HQ shows the global block stock and its HP, neither of
which is a buffer.
- BuildingIconCache, extracted from BuildButtonBar's file-local chip loading so
the card headers and the build buttons rasterize the same SVGs once.
FieldSelectionPanel is deleted: ships, stations, debris and the field count
summary are four more cards in the same factory, so the two-panel arbitration
collapses into one decision.
The card parts are still today's labels and buttons; the item chips, bars,
recipe summary and stat rows follow.
Build clean, 541 tests pass, app runs with no Qt warnings. Visual check
pending.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K