Stop a subject's name from setting the width of the develop column

The column moved sideways the moment segmentation finished. It takes the
widest minimum any panel declares, each panel publishes
`layout.preferred-width` as that minimum, and `MaskPanel` gained rows
whose width came from the model's output -- so a photograph the user was
looking at jumped because a label said "traffic light".

`overflow: elide` did not prevent it and was never going to. Eliding is
what a `Text` does when it draws; at layout time it still asks for the
width of its whole string, and it is the asking that reaches the column.

Both the subject rows and the mask entries are bounded, because a mask is
itself a segmentation result -- without the second half the column moved
when a subject was clicked instead of when one was found.

The bound is stated for the reason `ChipGrid` declares its width from its
column count rather than from its options (ff6c313): what a panel asks
for must follow from its structure, never from its data. 160px is the
same judgement as that commit's 88px chip, and it is a policy rather than
a measurement -- there is no harness in the tree that measures a panel's
width, and the 482-to-351 figure in ff6c313 was read off the running app.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-29 21:49:33 +02:00
co-authored by Claude Opus 5
parent b52be080ff
commit 3e19628324
+34
View File
@@ -137,10 +137,18 @@ component MaskEntry inherits Rectangle {
alignment: center;
spacing: 0px;
// Both lines below are bounded for the reason the subject
// row is: a mask's label and kind come from the model, and an
// unbounded `Text` asks for its whole string at layout time
// even when `elide` means it will never draw it. A mask *is* a
// segmentation result, so without this the column moved when a
// subject was clicked as well as when one was found.
Label {
text: root.data.label;
emphasised: root.data.selected || touch.has-hover;
overflow: elide;
min-width: 0px;
max-width: 160px;
}
Caption {
@@ -148,6 +156,8 @@ component MaskEntry inherits Rectangle {
// targets in a row, and wrapping would give the rows of a
// stack different heights for no gain.
overflow: elide;
min-width: 0px;
max-width: 160px;
// Three states worth distinguishing, and each has a
// different remedy: stale needs the segmentation re-run,
// unadjusted needs a slider moved, and the ordinary case
@@ -418,6 +428,30 @@ export component MaskPanel inherits Rectangle {
emphasised: subject-row.has-hover;
horizontal-stretch: 1;
overflow: elide;
// **`elide` is a paint-time behaviour, and this is a
// layout-time problem.** A `Text` asks for the width of
// its whole string whether or not it will draw all of
// it, so without a stated maximum this row asked for
// whatever the model happened to return, that became
// `layout.preferred-width`, the panel publishes that as
// its `min-width`, and the develop column takes the
// widest minimum any panel declares. The column
// therefore moved the instant segmentation finished —
// a photograph the user was looking at, jumping
// sideways because a label said "traffic light".
//
// Stated as a maximum for the reason `ChipGrid`
// declares its width from its column count rather than
// from its options: what a panel asks for must follow
// from its structure, never from its data. Past this
// the row elides, which is what `elide` was for.
//
// 160px is the same judgement as `ChipGrid`'s 88px
// chip — comfortable for the class names this model
// returns, and narrow enough that a subject list
// cannot be what sets the column.
min-width: 0px;
max-width: 160px;
}
Value { text: round(subject.score * 100) + "%"; }
}