fix(vt): list column fix round 11/11 — docs for the whole fix round
docs/plugin-ui-markup.md's Columns section is rewritten to match every production change from items 1-9: - New "Width semantics" section: width="*", the last-column-always-auto rule (and how it combines with an earlier explicit "*"), the non-last-column Build-time throw, and the runtime overflow clamp. - The <column> attribute grammar table gains column onclick (type="text", optional, Action<int>) and points width at the new section instead of the retired "last column ignores its own width" one-liner. - New "Short columns past their own row count" section: text/icon draw nothing, check draws unchecked. - Check-column glyph section updated for the shared UiCheckLamp primitive and its horizontal centering. - New "The PITCH convention for transcribing a VTank column table" section: VVS's WPaddingOuter=3px/WPadding=7px/16px scrollbar reserve/ forced-13px-check-columns have no direct acdream equivalent, so transcribing a real mainView.xml column table means declaring each column's PITCH (fixedwidth+7, or 13+7 for a check column regardless of its real fixedwidth) and reserving 16px on the last column. - The worked example now uses rowheight="18" (VVS's own Padding*2+ ControlHeight pitch, already the widget's default) and PITCH-computed widths (20 for a check column, 127 for a 120px text column) instead of arbitrary numbers, with a text onclick and a width="*" icon column added to demonstrate both new attributes in place. - The bindings truth table gains column onclick (type="text") and both column width throw/silent rows (non-last vs. the list's last column). - Testing conventions paragraph updated to mention width semantics, the row-bound click guard, and the two end-to-end tests. Verification: dotnet build AcDream.slnx -c Release (green), dotnet test tests/AcDream.App.Tests -c Release --filter "FullyQualifiedName~Markup|FullyQualifiedName~UiMarkupList|FullyQualifiedName~PluginSidePanel" (130/130), dotnet test tests/AcDream.Plugins.MossTank.Tests -c Release --filter "FullyQualifiedName~Markup" (9/9) — closes the fix round. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
e178c6abac
commit
026b512074
1 changed files with 113 additions and 22 deletions
|
|
@ -80,10 +80,13 @@ check those four against the markup by eye.
|
|||
| `list onchange` | Throws | `Action<int>` |
|
||||
| `column items` (`type="text"`) | Throws — REQUIRED, unlike the single-column list's own `items` sugar it mirrors | `IReadOnlyList<string>` |
|
||||
| `column colors` (`type="text"`) | **Silent** if omitted (no per-row override, same rule as `list colors`); throws if present but mistyped | `IReadOnlyList<uint>` **or** `IReadOnlyList<int>` |
|
||||
| `column onclick` (`type="text"`) | **Silent** if omitted (keeps the original select-the-row behavior); throws if present but mistyped | `Action<int>` (row index) |
|
||||
| `column values` (`type="check"`) | Throws — REQUIRED (there is no "no check column" fallback the way `list icons` has "no icon column") | `IReadOnlyList<bool>` |
|
||||
| `column values` (`type="icon"`) | Throws — REQUIRED | `IReadOnlyList<uint>` **or** `IReadOnlyList<int>` |
|
||||
| `column onchange` (`type="check"`) | Throws — REQUIRED (unlike the list's own optional `onchange`) | `Action<int>` (row index) |
|
||||
| `column onclick` (`type="icon"`) | Throws — REQUIRED | `Action<int>` (row index) |
|
||||
| `column width` (any type, NOT the list's last column) | Throws if missing, unparseable, or `<= 0` — UNLESS it is the literal `"*"` | `float`, or the literal `"*"` for auto |
|
||||
| `column width` (the list's LAST column) | **Silent** — never validated, never used for layout (it always absorbs the remainder), UNLESS it is the literal `"*"` (then it joins the auto-sharing group instead of taking 100% of the remainder alone) | `float`, or the literal `"*"` |
|
||||
|
||||
The icon-id row is the one binding here whose failure mode depends on WHEN you look: a typo'd property name is caught immediately at `Build`, but a property that exists yet holds the wrong kind of value at runtime is only ever discovered later, from inside a live draw.
|
||||
|
||||
|
|
@ -308,27 +311,33 @@ col)`. acdream's `<list>` matches this by letting a `<list>` declare
|
|||
attributes:
|
||||
|
||||
```xml
|
||||
<list x="8" y="24" w="256" h="120" rowheight="17"
|
||||
<list x="8" y="24" w="256" h="120" rowheight="18"
|
||||
selected="{SelectedMonster}" onchange="{SelectMonster}">
|
||||
<column type="text" width="140" items="{MonsterNames}"/>
|
||||
<column type="check" width="24" values="{MonsterFester}" onchange="{ToggleFester}"/>
|
||||
<column type="icon" width="24" iconkind="did" values="{MonsterIcons}" onclick="{PingMonster}"/>
|
||||
<column type="check" width="20" values="{MonsterFester}" onchange="{ToggleFester}"/>
|
||||
<column type="text" width="127" items="{MonsterNames}" onclick="{PingMonster}"/>
|
||||
<column type="icon" width="*" iconkind="did" values="{MonsterIcons}" onclick="{MoveMonsterUp}"/>
|
||||
</list>
|
||||
```
|
||||
|
||||
This mirrors VTank's own Monsters tab (a name column plus several boolean
|
||||
flag columns plus an icon-button column) — see
|
||||
This mirrors VTank's own Monsters tab (several boolean flag columns, a name
|
||||
column, and icon-button columns) — see
|
||||
`docs/research/vtank-kb/08-ui-views.md` §3's "Multi-column lists with typed
|
||||
columns" gap and its proposed extension, which this implements verbatim.
|
||||
columns" gap and its proposed extension, which this implements verbatim. The
|
||||
`rowheight="18"` above is the widget's own default (`UiMarkupList.RowHeight`),
|
||||
chosen to match VVS's own row pitch exactly: `Padding*2 + ControlHeight` =
|
||||
`1*2 + 16` = `18` (`docs/research/vtank-kb/08-ui-views.md`'s `HudList` row:
|
||||
`Padding=1px`, `ControlHeight=16px`) — an author who omits `rowheight`
|
||||
entirely already gets VVS's pitch for free.
|
||||
|
||||
### `<column>` attribute grammar
|
||||
|
||||
| Attribute | Applies to | Required | Meaning |
|
||||
|---|---|---|---|
|
||||
| `type` | every column | yes | `text`, `check`, or `icon` — any other value throws `FormatException` at `Build` |
|
||||
| `width` | every column | no (defaults to `0`) | Column width in px. **The LAST column in a `<list>` ignores its own declared width and always absorbs whatever room remains** after every earlier column — recomputed every frame off the list's live width, so a resizable list re-flows its last column like every other retained widget |
|
||||
| `width` | every column | see below | Column width in px, or `"*"` for auto. See "Width semantics" below — the rules differ for the LAST column in a `<list>` vs. every other column |
|
||||
| `items` | `type="text"` | yes | `{IReadOnlyList<string>}` — one row of text per index |
|
||||
| `colors` | `type="text"` | no | `{IReadOnlyList<uint>}`, `0xRRGGBB` per row (same grammar as the single-column list's own `colors`); omitted rows (or the whole attribute) fall back to the list's `TextColor` |
|
||||
| `onclick` | `type="text"` | no | `{Action<int>}` — fired with the ROW INDEX on a click anywhere in the cell INSTEAD of selecting the row. Omitted (the default) keeps the original select-the-row behavior; present but malformed throws `FormatException` at `Build`. None of VTank's eight lists actually relies on row selection — every real text cell in `mainView.xml` is wired as an action target — so a new column is usually written WITH an `onclick` |
|
||||
| `values` | `type="check"` | yes | `{IReadOnlyList<bool>}` — the checked state per row |
|
||||
| `onchange` | `type="check"` | yes | `{Action<int>}` — fired with the ROW INDEX on a click anywhere in the cell; the plugin flips its own bool, the column never mutates `values`' backing collection itself |
|
||||
| `values` | `type="icon"` | yes | `{IReadOnlyList<uint>}` (or `IReadOnlyList<int>`) — one icon id per row, same id-space rules as `list icons` |
|
||||
|
|
@ -342,20 +351,62 @@ error, not a silently-inert control. A `<column>` with an unrecognized `type`,
|
|||
a missing required binding for its type, or any `<list>` child element that
|
||||
isn't `<column>` at all, throws `FormatException` at `Build`. A `<list>` with
|
||||
`<column>` children cannot ALSO use the single-column `items`/`colors`/`icons`
|
||||
attributes on the `<list>` element itself — pick one form per list.
|
||||
attributes on the `<list>` element itself — pick one form per list. Every
|
||||
column-attribute throw message identifies the offending column by position
|
||||
and declared type — `column[2] type="check" values`, not just `"column
|
||||
values"` — so a list with several columns of the same type still points at
|
||||
the right one.
|
||||
|
||||
### Width semantics
|
||||
|
||||
`width="*"` means AUTO: this column shares the list's remaining width
|
||||
EQUALLY with every other auto column, VVS's own "0-width column auto-sizes"
|
||||
rule (`docs/research/vtank-kb/08-ui-views.md`'s `HudList` row: "a 0-width
|
||||
text/button/edit/list/fixedlayout/notebook column auto-sizes... share the
|
||||
remaining width equally"). `width="*"` is legal on ANY column, including the
|
||||
last.
|
||||
|
||||
The LAST column in a `<list>` is special: it is ALWAYS treated as auto —
|
||||
sharing the remaining width like every other auto column when one or more
|
||||
earlier columns also declare `width="*"`, or absorbing 100% of the remainder
|
||||
by itself when no other column does (the original, still-default behavior).
|
||||
Its own declared `width` (or omitting `width` entirely) is never validated
|
||||
and never used for layout — only an explicit `width="*"` on the last column
|
||||
actually changes anything (it makes the last column share evenly with
|
||||
earlier auto columns instead of taking the whole remainder alone). When two
|
||||
or more columns end up sharing, integer-division remainder goes to the LAST
|
||||
one — e.g. three columns sharing 100px split 33/33/34, not 33/33/33 with 1px
|
||||
unaccounted for.
|
||||
|
||||
Every OTHER (non-last) column's declared `width` is validated at `Build`: **a
|
||||
missing, unparseable, or non-positive `width` throws `FormatException`**
|
||||
naming the column's index and declared type (`column[0] type="text" width
|
||||
must be a positive number or "*", got (missing)`) — UNLESS it is `width="*"`.
|
||||
|
||||
At layout time (recomputed every frame off the list's live width, so a
|
||||
resizable list re-flows like every other retained widget), a declared width
|
||||
that would overflow the list's total width is CLAMPED to whatever room is
|
||||
actually left, walked left to right — every column after the overflow point
|
||||
gets `0` width and draws nothing (a `<=0`-width cell is skipped entirely, the
|
||||
same as today).
|
||||
|
||||
### Row count, selection, and clicks
|
||||
|
||||
Row count is the longest bound column (a text column with 20 rows next to a
|
||||
check column with only 5 simply draws 15 rows of empty checkboxes — short
|
||||
columns never truncate the whole list). The list's own `selected`/`onchange`
|
||||
attributes keep exactly their single-column meaning: a click in a **text**
|
||||
column selects that row (and fires the list's `onchange` with the row index,
|
||||
same as today). A click in a **check** or **icon** column instead fires that
|
||||
check column with only 5 simply draws 15 rows past its own data — see "Short
|
||||
columns past their own row count" below for what each column kind does
|
||||
there). The list's own `selected`/`onchange` attributes keep exactly their
|
||||
single-column meaning: a click in a **text** column without its own
|
||||
`onclick` selects that row (and fires the list's `onchange` with the row
|
||||
index, same as today). A click in a **check** or **icon** column, or a
|
||||
**text** column that DOES declare its own `onclick`, instead fires that
|
||||
column's own `onchange`/`onclick` and does **not** change the list's
|
||||
selection — VVS's per-cell `Click(row, col)` folded into a per-column
|
||||
callback, since acdream's binding model is per-attribute rather than
|
||||
per-cell. Scrolling works exactly as the single-column list already does.
|
||||
per-cell. A click landing on a row past that SPECIFIC column's own bound
|
||||
data (even though the row is valid for the list overall, because some OTHER
|
||||
column has more rows) fires nothing — no callback, no crash. Scrolling works
|
||||
exactly as the single-column list already does.
|
||||
|
||||
### No header row
|
||||
|
||||
|
|
@ -369,9 +420,44 @@ is no dedicated header markup to learn.
|
|||
### Check-column glyph
|
||||
|
||||
A `type="check"` cell draws with the exact same five-band lamp glyph as
|
||||
`<toggle>` (`UiMarkupToggle`'s checked/unchecked colors and `DrawLamp`
|
||||
primitive), so a column checkbox reads identically to every other checkbox
|
||||
in the client rather than a bespoke box-and-tick.
|
||||
`<toggle>` (both now share one `UiCheckLamp` primitive — its checked/
|
||||
unchecked colors and `Draw` method), so a column checkbox reads identically
|
||||
to every other checkbox in the client rather than a bespoke box-and-tick.
|
||||
The glyph is centered horizontally in its cell (matching how an icon cell
|
||||
already centers its sprite) — a check column is routinely declared wider
|
||||
than the glyph itself under the PITCH convention below.
|
||||
|
||||
### Short columns past their own row count
|
||||
|
||||
A **text** or **icon** cell past its own column's row count draws nothing —
|
||||
there is no sensible default string or icon to show. A **check** cell past
|
||||
its own column's row count still draws the lamp, UNCHECKED — VVS
|
||||
materializes every cell in a row regardless of which columns actually have
|
||||
data for it, and there is always a sensible default for a boolean (false).
|
||||
|
||||
### The PITCH convention for transcribing a VTank column table
|
||||
|
||||
VVS's own `HudList` reserves geometry acdream's column model doesn't have a
|
||||
separate concept for: `WPaddingOuter=3px` (the list's own left/right
|
||||
margin), `WPadding=7px` (a gap BETWEEN columns), and a themed
|
||||
`VScrollBarButtonSize=16px` (scrollbar width, reserved on the right). It
|
||||
also forces every `CheckColumn` to a fixed 13px regardless of its declared
|
||||
`fixedwidth`. acdream's column model has no separate gap/scrollbar/
|
||||
forced-width concept — every column's declared `width` is its full cell
|
||||
width, columns sit directly adjacent with no gap, and a check column uses
|
||||
whatever `width` it's given like any other column.
|
||||
|
||||
To transcribe a real VTank column table (as in
|
||||
`refs/vtank/uTank2.ViewXML.mainView.xml`) faithfully, declare each column's
|
||||
**PITCH** instead of its raw `fixedwidth`: `pitch = fixedwidth + 7` (baking
|
||||
VVS's inter-column `WPadding` into the cell width itself, since acdream has
|
||||
no separate gap). For a `CheckColumn`, use VVS's forced 13px as the
|
||||
`fixedwidth` regardless of whatever `fixedwidth` the source XML declares
|
||||
(`16 -> 13 + 7 = 20`, not `16 + 7 = 23`). Reserve VVS's 16px scrollbar width
|
||||
on the LAST column specifically (add it to that column's own pitch, or fold
|
||||
it into the list's total declared `w`) — acdream's list draws no scrollbar
|
||||
of its own today, but reserving the space keeps the transcribed proportions
|
||||
matching what a real VVS `HudList` would show once one exists.
|
||||
|
||||
### Backward compatibility
|
||||
|
||||
|
|
@ -417,7 +503,12 @@ in-test class recording which id/kind it was asked to resolve) rather than a
|
|||
live DAT — see `tests/AcDream.App.Tests/UI/`. `PluginSidePanelTests` exercises
|
||||
the shelf's drag/collapse/hide/persistence behavior against a bare `UiRoot`.
|
||||
`MarkupListColumnsTests` covers the Columns extension above: per-column
|
||||
binding-type validation, draw-level column-offset/clipping/check-glyph pins
|
||||
against the same recording-renderer apparatus, hit-test routing (text
|
||||
selects; check/icon fire their own callback and never touch selection), and
|
||||
a backward-compatibility proof that a column-less `<list>` is unaffected.
|
||||
binding-type validation (every throw naming its column by index and type),
|
||||
width semantics (`"*"` sharing, the last-column-always-auto rule, the
|
||||
overflow clamp), the per-column row-bound click guard, draw-level
|
||||
column-offset/clipping/check-glyph pins against the same recording-renderer
|
||||
apparatus, hit-test routing (text selects unless it has its own `onclick`;
|
||||
check/icon/onclick-text fire their own callback and never touch selection),
|
||||
a backward-compatibility proof that a column-less `<list>` is unaffected,
|
||||
and two full `MarkupDocument.Build` end-to-end tests transcribing VTank's
|
||||
real Monsters- and Meta-tab column shapes.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue