merge(vt): bring in retail scrollbar chrome + resizable/anchor markup
Merges claude/latest-main-sync-497549 into the slice-7 panel worktree so round D can build on both:b71a8ea37(retail scrollbar chrome on plain <menu> popups and overflowing <list>s — chat/inventory sprite ids, always-scrollable single-column menu) and2e63391cc(<panel resizable minw minh> + anchor="left top right bottom" markup). Conflicts resolved keeping both intents: - Ledger (docs/plans/2026-09-07-campaign-vt-slice7-tabs.md): unioned both branches' entries into one chronological timeline instead of picking a side. - docs/plugin-ui-markup.md: kept both attribute additions per element (slider min/max/style, menu scroll/style) AND anchor on every row. - src/AcDream.App/UI/UiMarkupList.cs: kept this branch's fix round B item 10 (VVS HudList grids have no row-selection highlight) over the sync branch's older SelectedColor band draw in the <column> grid path — the legacy single-column list path is unaffected either way. - src/AcDream.App/UI/MarkupDocument.cs: the auto-merge left two `Scrollable =` initializers on the same <menu> object (CS1912). Kept the sync branch's `Scrollable = true` (VTank's HudCombo is always a single scrolling column, never a wrapping grid) and dropped this branch's `Scrollable = B(el, "scroll", false)` opt-in, since the owner-driven always-scrollable design supersedes the S7.2 opt-in one. Updated MarkupDocumentTests.cs to match: removed Build_MenuWithNoScrollAttribute_KeepsScrollableFalse (asserted the now-false opt-in default) and Menu_Scroll_DrawsAPlainFlatThumbFillWhenTheMarkupItemCountOverflowsTheVisibleRows (asserted a flat DrawFill thumb; the scrollbar is sprite-chrome for every menu style now) — both fully superseded by Menu_Markup_IsAlwaysScrollable_WithRetailScrollbarChromeWired and UiMenuPlainStyleTests.Plain_OpenPopup_ScrollableOverflow_ DrawsRetailScrollbarChrome_RowsStayPlain. Verified: dotnet build AcDream.slnx -c Release green; MossTank suite 678/678; App markup/plugin filter 242/242 (241 before this commit's test-file trim, +1 net from the merge's own new tests, 0 red). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
4ba0a557f6
14 changed files with 1431 additions and 166 deletions
|
|
@ -74,6 +74,22 @@ after each deliberate `Top` write for the imported-layout element. Precedent:
|
|||
`MapPageController.cs:235-249` (the same fix already landed for other
|
||||
runtime-repositioned imported/programmatic elements).
|
||||
|
||||
## #491 — MossTank extra/blacklisted buff lists are shown but not consumed by BuffPlan.Build
|
||||
|
||||
**Status:** OPEN — found 2026-09-07 at the slice 7 architecture review.
|
||||
**Severity:** LOW (honest UI after fix round B; behavior owed)
|
||||
**Component:** `src/AcDream.Plugins.MossTank/BuffPlan.cs` (`Build`), `BuffSettings.ExtraBuffSpellNames`/`BlacklistedBuffFamilyNames`
|
||||
|
||||
**Description.** Slice 7 added VTank's "Extra Buff Spells" and "Blacklisted Buff Families" lists to the Buffs tab. They are stored (persisted after fix round B) but `BuffPlan.Build` never reads them: extra exemplars are not cast, blacklisted families are not skipped. Belongs to VT2 slice 4 (buffs); wire `Build` to add the "best similar" spell per extra exemplar and to skip blacklisted families, with plan tests.
|
||||
|
||||
## #490 — Plugin panel host: `StartVisible=false` + `ShowInSidePanel=false` is permanently unshowable; layout persistence has no revision bump
|
||||
|
||||
**Status:** OPEN — found live 2026-09-07 at slice 7 fix round A (`78b42a519`), workaround in the plugin.
|
||||
**Severity:** LOW/MEDIUM (host defect; silent)
|
||||
**Component:** `src/AcDream.App/UI/RetailUiRuntime.cs` (~5735-5757, `PluginWindowVisibilityController`), `RetailWindowLayoutPersistence`
|
||||
|
||||
**Description.** (1) `_requestedVisible = startVisible` and only `OnShown()` ever sets it true; a window registered with `ShowInSidePanel=false` has no shelf entry to raise `OnShown`, so `StartVisible=false` can never become visible, with no error. MossTank works around it by registering its popups `StartVisible=true` and gating on its own visibility bindings. Fix shape: validate the combination at `AddPanel` (throw, or coerce). (2) Plugin windows restore a stale persisted position over a changed authored default with no revision bump — the slice 7 screenshots needed an isolated `ACDREAM_CONFIG_DIR` to open at the authored 28,42. Fix shape: key the persisted layout by the panel's authored geometry hash (or a plugin-declared layout revision) so a redesign resets the stored position once.
|
||||
|
||||
## #489 — Headless: SpewBox pending queue grows unbounded when no console ticks it; console polish
|
||||
|
||||
**Status:** OPEN — found 2026-09-07 by the Opus re-check of the headless console (`738111239`).
|
||||
|
|
|
|||
|
|
@ -86,3 +86,4 @@ Linux-clean by construction; the Ubuntu CI closure runs the MossTank tests.
|
|||
- 2026-09-06 VT0 complete. VT1 docs 01/02/03/08 dispatched first, 04–07/09 next.
|
||||
- 2026-09-06 VT1: all nine catalog docs landed; index written; Opus citation pass in flight.
|
||||
- 2026-09-06 Files decision amended by the owner: metas and navs are metaf `.af`, not `.met`/`.nav` (see Decisions). Docs 06 §1 and 07 §1 stay as the binary-format record (they describe what the metaf tool converts from); the semantics sections (06 §2-5, 07 §2-4) remain the behavior oracle. VT2 doc: `2026-09-06-campaign-vt-vt2-gap-audit.md`; slice-1 contract: `2026-09-06-campaign-vt-slice1-files-and-columns.md`.
|
||||
- 2026-09-07 owner asked whether every VTank format should get a human-readable twin like `.af`. Lead's answer, recorded as a post-campaign idea: VTank's `.usd`/`.ugd`/`.utl`/`.met` are already text (the one-value-per-line table grammar) and `.nav` is plain lines; only metas/navs had a community-authored readable format (metaf) with a converter both ways, which is why `.af` is safe as storage. For settings/loot a second format would be our own invention with two sources of truth. Instead: a one-way readable EXPORT + a profile DIFF command (`/vt settings export`, `key = value` lines) for inspection and version control, VTank's format staying the only storage. Revisit only if hand-editing loot rules becomes routine.
|
||||
|
|
|
|||
|
|
@ -134,6 +134,10 @@ re-review, merge to the campaign branch, then the owner's visual gate.
|
|||
- 2026-09-07 09:10 S7.3 Monsters landed on the panel worktree (`57ced0aff`, `c3b4f7862`; MossTank suite 645 → 651): the 23-column grid with VTank's exact cycle lists (P −1…4; Dmg type 14 values; Ex. Vuln 9; PetDmg 10; name click deletes; arrows reorder with DEFAULT pinned). Implementer deviations for the review: Weapon/Offhand cycle MossTank's registered item roster instead of VTank's opaque weapon-type ids (MossTank models concrete owned items); the move-up/down DEFAULT guard is symmetric. Fix round A (grid scaling, Profiles leftovers, 260-tall window, Advanced Options / Loot Editor as their own panels, blank trailing slots, fresh screenshots) dispatched on the same worktree after merging the plain-menu style in. S7.4–S7.6 follow.
|
||||
- 2026-09-07 10:10 fix round A landed on the panel worktree (`045cd0a19` merge of the plain menu, `565a33d78` column shifts + Profiles cleanup + 236-tall window + popup panel files, `e414b2f56` csproj plugin-copy fix, `78b42a519` popups actually render (`StartVisible` gotcha) + fresh screenshots, `66b070def` ledger; MossTank suite 651 → 654). Owner's two complaints verified fixed on the new screenshots. Deviation for the review: Macro/Nav CopyTo lost their in-UI target-name field with the deleted block (VTank has none either). S7.4–S7.6 dispatched on the same worktree.
|
||||
- 2026-09-07 10:20 owner, live: "Drop down menus look horrible, there is also a checkmark on the text there." — the OPEN popup still draws retail art (tan gradient panel, ornate gold scrollbar, checkmark on the selected row). Plain open state (dark list rows, selected fill, plain scrollbar, no checkmark) dispatched on the plain-menu worktree; merges to the campaign branch, then into the panel worktree at fix round B.
|
||||
- 2026-09-07 fix round B item 16 landed on the panel worktree at `8d3c6ad7c` (dedupe, tab-switch popup cleanup, slider validation).
|
||||
- 2026-09-07 fix round C (architecture re-check of fix round B `8d3c6ad7c`) landed on the panel worktree, seven commits: `466fac426` (D1 — Advanced Options popup's four bindings, previously re-filtering the 163-entry VtankOptionCatalog and re-running GetMetaOption/ToDisplayString per row on every draw, materialized once by a new RefreshAdvancedOptions() called from category toggle/edit-apply/selection-change/profile-load), `23d4376cc` (D4 — AdvancedOptionCategoryEnabled no longer hands out the mutable backing bool[] directly; a ReadOnlyCollection view built once over the same array), `f15667db5` (D2 — `/mt refresh` now calls RefreshMonsterEditor after EnsureDefaultMonsterRule, the one gap found auditing every `_combatSettings.Rules` mutation site in the plugin), `3b8d02194` (D3 — AssertWithinParent's bottom-band check now counts each widget kind's own implicit default height when `h` is absent, label/field 16 toggle 20 button 16, instead of reading an absent `h` as zero), `37055bddc` (F7+F12 — ItemHandsColumn materialized once in RefreshItemEditors from the SAME base-name array that builds the decorated `_itemRows`, replacing a per-frame `.Select().ToArray()` plus a suffix-reparsing BaseItemName helper with one forward-only decoration; CycleItemHandsAtCore now refreshes the cache it mutates), `0724761ad` (F3 — SecondaryPopupPanelsFitTheirOwnBoundsAndEveryBindingResolves discovers popup files with the same glob the sibling-overlap and arrow-glyph tests already use instead of a hand-maintained InlineData list), `d20ad47c5` (F4 — the ExtraBuffSpellNames/BlacklistedBuffFamilyNames field comments and their two Buffs-tab tooltips now cite issue #491). D3's grep audit of every real `mosstank*.xml` file found no label/toggle/field/button missing `h`, so the stricter bottom-band check introduced zero new failures against shipped markup. Every new/changed pin was shown to fail against a targeted mutation (property/getter reverted, or the assertion target broken) before the fix was restored; D1's own fix broke one PRE-EXISTING test (`AdvancedOptionValueColumnMirrorsTheLiveSettingValue`, which expected a setting toggled OUTSIDE the popup's own mutators to appear on the very next read) — updated it to exercise the "selection change" mutator afterward, with a comment explaining the narrowed contract. MossTank suite 677 (D1) → 678 (D4) → 679 (D2) → 680 (D3) → 681 (F7+F12) → 678 (F3, four InlineData cases collapsed into one Fact, no coverage loss) → 678 (F4, comment/tooltip only) → 678 (F13, no new test, an assertion removed). Full solution build green after every commit; App markup/plugin filter held 203/203 throughout.
|
||||
- 2026-09-07 fix round C item F13 folded the `EveryInteractiveControlDeclaresARealHandlerBinding` test's exact-control-count history here, in the same commit that removed it from the test file: Round 3 item 10 added +3 for the Route/Loot/Meta Delete buttons (Settings already had one from round 2 step 5). Campaign VT slice 7 S7.1/S7.2 transcribed Options/Profiles/Vitals control-for-control from VTank's own tabs (docs/research/vtank-kb/08-ui-views.md §1) and repositioned the Advanced Options/Loot Editor popups to VTank's exact 392x300/268x300 geometry — net 194 → 205. The Options tab gained four direct checkboxes for settings that already existed but were only reachable through the generic Advanced Options key-value editor (Enable Autostack, Fastcast Buffs, Don't Shoot at Walls, Fallback Debuffs if Blocked); the Profiles tab gained a real Meta-profile combo/CopyTo row (previously a stub label) and per-type name fields/New buttons, and lost the Loot Priority Boost toggle as a duplicate of the Options-tab control of the same name; the Loot Editor popup lost LootEditorNotice (a status label, not an action). S7.3 replaced the whole Monsters tab body with VTank's own 23-column grid: the select-then-edit surface (1 list + 1 field + 8 buttons + 3 menus + 14 toggles + 3 equip buttons = 30 controls) is gone, replaced by 1 list + 23 columns + 1 field + 2 buttons (27) — net 205 → 202. Fix round A (2026-09-07) moved the Advanced Options and Loot Editor groups OUT of mosstank.xml entirely into their own plugin panels and deleted the Profiles tab's three stacked name-draft field+"New"-button rows and the acdream-only "Loot engine" classifier menu — net 202 → 167. S7.4 replaced the Items tab's single-column list with VTank's own clWeaponName/clHandedness 2-column grid (+2), added the Consumables tab's "Excluded Scarab Types" 2-column grid plus its "Add Selected" button (+4), and added the Buffs tab's Extra Buff Spells / Blacklisted Buff Families lists plus their two "Add..." buttons (+4) — net 167 → 177. S7.5 replaced the Route tab's single-column waypoint list with VTank's own clWP/clWPc 2-column grid (+2) and added the "Select Nearest Point" button (+1) — net 177 → 180. S7.6 replaced the Meta tab's single-column rules list with VTank's own 6-column lstMetaRules grid (+6) — net 180 → 186. Fix round B item 5: VTank's real Meta tab is only 5 controls (the grid, "Create", a caption, and the settable current-state menu). Removed entirely: the profile toolbar (menu/name-draft/New/CopyTo/Clear/Delete, 6) and "Enable Meta" (1) — 7 controls gone for good. Moved into mosstank-metaeditor.xml (a separate file this test doesn't scan): the State/Condition/Action editor (2 fields + 2 menus + 1 field for state + 1 secondary-text field + 4 numeric-stepper buttons + Apply/Remove/MoveUp/MoveDown, 14). Added: the settable cmbMetaCurrentState menu (+1). Net 186 → 166 (-7 -14 +1). Fix round B item 8: the Route tab collapses to VTank's own 2-across button grid + bottom nav-control row, 28 → 18 interactive controls within mosstank.xml. Checkpoint/Jump/Remove/Set Follow Target/Follow Corners/Open Doors/Nav Priority/the Follow-Nav-Min-Distance stepper moved to mosstank-advanced.xml's own new "MossTank Extras" section. The 2-state ToggleRouteAddPosition button became the real 3-option cmbNavInsertMode `<menu>`, and the pause "-"/"+" stepper became one editable field. Net 166 → 156 (the count the test asserted immediately before this fix round removed the assertion). The test's own per-control handler/enabled loop remains the live guarantee; a bare `Assert.NotEmpty(controls)` guards the selector itself.
|
||||
- 2026-09-07 11:20 S7.4–S7.6 landed on the panel worktree (`f5409530f` Items/Consumables/Buffs + buff picker popup, `6118062a7` Route grid, `cc323f6a5` Meta grid, `41fc1d88d` screenshots; MossTank suite 654 → 660); the plain popup merged in at `dbdde0783`. Both Opus lenses dispatched on the whole slice. Lead's read of the new Route and Meta screenshots for fix round B: VTank's Route tab is the waypoint list + one small row under it (nav-type menu, insert-mode menu, three icon buttons) + the right-hand Add grid — ours still carries MossTank-only controls on the tab (Checkpoint / Jump / Remove / Set Follow Target / Follow target / Nav Priority / Follow Corners / Open Doors / Use NPC / Follow-Nav Min Distance spinners; "Add to End" rendered as a button instead of the insert-mode menu) that VTank does not show there — move them out (Advanced Options popup, or drop what VTank has no equivalent for). VTank's Meta tab is the six-column grid + Create + "Current State:" menu — ours adds a profile row (By char / New / CopyTo / Clear / Delete / Enable Meta / State) that belongs on Profiles, and an INLINE rule editor (state, condition/action menus, three fields, N/N2 spinners, Apply/Add/Remove, arrows) where VTank opens a separate rule-editor view on click — make it a popup panel like the buff picker. Implementer deviations to judge: Hands column session-local; extra/blacklisted buff names stored but not consumed by the plan; recall menu 4 kinds vs VTank's 27; "Select Nearest Point" moves the edit selection; Meta delete cell is a text "X".
|
||||
- 2026-09-07 12:00 both Opus lenses on `dbdde0783`. Resemblance: APPROVE-WITH-FIXES — Options, Vitals and the buff picker read as VTank; Monsters one header fix away; Buffs (VTank's two lists shrunk for a MossTank block, a new "+"/"Add…" overlap) and Meta (grid buried under a toolbar and an inline editor; "Current State:" not settable) do not read as VTank; clipped status lines on three tabs + the Loot Editor; "CopyTo" overflowing its button; Route's grid re-flowed 3-across with "Add NPC Talk" renamed and the nav icons moved; the gold slider track; the 4-vs-27 recall menu is a data hole (slice 6). Owner complaints 1 and 2 closed on the screenshots; 3 closed in code but needs a dropdown-open re-capture. Architecture: APPROVE-WITH-FIXES — one RED App test at HEAD (the `<menu scroll>` sprite pin invalidated by the plain popup merge; the ledger's 192/192 was stale), `ExcludedComponentIcons` captures the whole inventory per row per frame, derived columns allocate per frame, the markup file list hardcoded in four places, the buff lists look live but are neither consumed nor persisted, two host defects found live (filed as #490), the `MarkupDocument.Build`-over-real-files seam untested. Lead decisions for round B: VTank's own controls never shrunk/moved for MossTank extras (extras → Advanced Options popup or dropped); plain sliders; no grid selection band; the Meta rule editor as its own popup; the four Deletes stay, aligned. #491 filed for the buff-list wiring (slice 4). Fix round B (18 items) dispatched.
|
||||
- 2026-09-07 14:50 fix round B landed 16/18 on the panel worktree (`040d5f3d8`…`8d3c6ad7c`; MossTank suite → 676, App markup/plugin filter 285/3 skipped); the agent was stopped after three hours failing the screenshot step (malformed session config, disk-wide search) — nothing lost, tree clean. Item 16 was the dedupe, tab-switch popup cleanup, and slider validation. Architecture narrow re-check at `8d3c6ad7c`: NOT merge-ready — D1 the Advanced Options popup re-materializes the 163-entry catalog per frame (the class item 12 killed elsewhere), D2 `/mt refresh` mutates Rules without `RefreshMonsterEditor()`, D3 the bottom-band pin skips unsized children; carried nits (hands column per frame, popup-size theory hardcoded, #491 not cited, mutable bool[]). Fix round C dispatched. Screenshots: replaced by the owner testing LIVE from the gate build at `8d3c6ad7c` (all five plugin windows load); the owner's findings feed round C/D; the resemblance re-check runs on the final state.
|
||||
- 2026-09-07 15:20 OWNER LIVE LOOK at `8d3c6ad7c` (gate build): "Looks mostly fine, a lot of polish can be done though. But we do that later." Findings: (1) Advanced Options' category filter shows the raw bitmask values (0x1, 0x2, …) with lamps instead of the category NAMES (Misc, Recharge, MeleeCombat, SpellCombat, Ranges, Navigation, Buffing, Crafting, Looting); (2) Advanced Options must follow VTank's model (owner's screenshots 2/3): name + value columns, clicking a bool/enum VALUE flips it in place, only NUMBERS open the edit field at the bottom (Enter applies) with the description box under it, no Apply/Back buttons; (3) scrollable dropdowns and the popup windows use the SAME scrollbar/window assets as the chat and inventory windows (not the flat scrollbar); (4) the whole window: larger default size AND resizable (window-manager resize like chat, lists stretching); (5) Route's Add Recall is missing most of VTank's recalls and recalls do not work in routes yet. Dispatched now (App side, own worktrees): retail scrollbar assets for plain popups/lists; resizable plugin panels + `anchor` markup. Queued on the panel worktree after fix round C: Advanced Options model + category names; the full VTank recall table with spell ids (metaf `NRecall`, py:10981-11008 — 26 spells: Primary Portal Recall 48, Secondary 2647, Lifestone 1635, Lifestone Sending 1636, Portal Recall 2645, Aphus 2931, Sanctuary 2023, Singularity Caul 2943, Glenden Wood 3865, Aerlinthe 2041, Mount Lethe 2813, Ulgrim's 2941, Bur 4084, PtOIA 4198, Mhoire Forge 4128, Colosseum 4213, Facility Hub 5175, Gear Knight Camp 5330, Neftet 5541, Return to the Keep 4214, Rynthid 6150, Viridian Rise 6321, Viridian Rise Great Tree 6322, Celestial Hand 6325, Radiant Blood 6327, Eldrytch Web 6326) wired to the existing recall execution; adopt anchors + the larger default.
|
||||
- 2026-09-07 16:10 App-side pieces merged into the campaign branch: `b71a8ea37` retail scrollbar chrome on plain `<menu>` popups and overflowing `<list>`s (chat/inventory skin ids `0x06004C5F`/`6C`/`69`/`60`/`63`/`66`; markup menus are one-column scrollable), `2e63391cc` `<panel resizable minw minh>` + `anchor="left top right bottom"` on every element (window-manager resize + persistence already generic). App markup/menu/list/window/anchor filter 294/1 skipped. Panel round D (after fix round C on the panel worktree): merge the campaign branch in, Advanced Options model + category names, VTank's 26-recall table wired, `resizable="true"` + anchors on the main panel with a larger default.
|
||||
- 2026-09-07 16:30 fix round C (architecture re-check of fix round B `8d3c6ad7c`) landed on the panel worktree, seven commits: `466fac426` (D1 — Advanced Options popup's four bindings, previously re-filtering the 163-entry VtankOptionCatalog and re-running GetMetaOption/ToDisplayString per row on every draw, materialized once by a new RefreshAdvancedOptions() called from category toggle/edit-apply/selection-change/profile-load), `23d4376cc` (D4 — AdvancedOptionCategoryEnabled no longer hands out the mutable backing bool[] directly; a ReadOnlyCollection view built once over the same array), `f15667db5` (D2 — `/mt refresh` now calls RefreshMonsterEditor after EnsureDefaultMonsterRule, the one gap found auditing every `_combatSettings.Rules` mutation site in the plugin), `3b8d02194` (D3 — AssertWithinParent's bottom-band check now counts each widget kind's own implicit default height when `h` is absent, label/field 16 toggle 20 button 16, instead of reading an absent `h` as zero), `37055bddc` (F7+F12 — ItemHandsColumn materialized once in RefreshItemEditors from the SAME base-name array that builds the decorated `_itemRows`, replacing a per-frame `.Select().ToArray()` plus a suffix-reparsing BaseItemName helper with one forward-only decoration; CycleItemHandsAtCore now refreshes the cache it mutates), `0724761ad` (F3 — SecondaryPopupPanelsFitTheirOwnBoundsAndEveryBindingResolves discovers popup files with the same glob the sibling-overlap and arrow-glyph tests already use instead of a hand-maintained InlineData list), `d20ad47c5` (F4 — the ExtraBuffSpellNames/BlacklistedBuffFamilyNames field comments and their two Buffs-tab tooltips now cite issue #491). D3's grep audit of every real `mosstank*.xml` file found no label/toggle/field/button missing `h`, so the stricter bottom-band check introduced zero new failures against shipped markup. Every new/changed pin was shown to fail against a targeted mutation (property/getter reverted, or the assertion target broken) before the fix was restored; D1's own fix broke one PRE-EXISTING test (`AdvancedOptionValueColumnMirrorsTheLiveSettingValue`, which expected a setting toggled OUTSIDE the popup's own mutators to appear on the very next read) — updated it to exercise the "selection change" mutator afterward, with a comment explaining the narrowed contract. MossTank suite 677 (D1) → 678 (D4) → 679 (D2) → 680 (D3) → 681 (F7+F12) → 678 (F3, four InlineData cases collapsed into one Fact, no coverage loss) → 678 (F4, comment/tooltip only) → 678 (F13, no new test, an assertion removed). Full solution build green after every commit; App markup/plugin filter held 203/203 throughout.
|
||||
- 2026-09-07 fix round C item F13 folded the `EveryInteractiveControlDeclaresARealHandlerBinding` test's exact-control-count history here, in the same commit that removed it from the test file: Round 3 item 10 added +3 for the Route/Loot/Meta Delete buttons (Settings already had one from round 2 step 5). Campaign VT slice 7 S7.1/S7.2 transcribed Options/Profiles/Vitals control-for-control from VTank's own tabs (docs/research/vtank-kb/08-ui-views.md §1) and repositioned the Advanced Options/Loot Editor popups to VTank's exact 392x300/268x300 geometry — net 194 → 205. The Options tab gained four direct checkboxes for settings that already existed but were only reachable through the generic Advanced Options key-value editor (Enable Autostack, Fastcast Buffs, Don't Shoot at Walls, Fallback Debuffs if Blocked); the Profiles tab gained a real Meta-profile combo/CopyTo row (previously a stub label) and per-type name fields/New buttons, and lost the Loot Priority Boost toggle as a duplicate of the Options-tab control of the same name; the Loot Editor popup lost LootEditorNotice (a status label, not an action). S7.3 replaced the whole Monsters tab body with VTank's own 23-column grid: the select-then-edit surface (1 list + 1 field + 8 buttons + 3 menus + 14 toggles + 3 equip buttons = 30 controls) is gone, replaced by 1 list + 23 columns + 1 field + 2 buttons (27) — net 205 → 202. Fix round A (2026-09-07) moved the Advanced Options and Loot Editor groups OUT of mosstank.xml entirely into their own plugin panels and deleted the Profiles tab's three stacked name-draft field+"New"-button rows and the acdream-only "Loot engine" classifier menu — net 202 → 167. S7.4 replaced the Items tab's single-column list with VTank's own clWeaponName/clHandedness 2-column grid (+2), added the Consumables tab's "Excluded Scarab Types" 2-column grid plus its "Add Selected" button (+4), and added the Buffs tab's Extra Buff Spells / Blacklisted Buff Families lists plus their two "Add..." buttons (+4) — net 167 → 177. S7.5 replaced the Route tab's single-column waypoint list with VTank's own clWP/clWPc 2-column grid (+2) and added the "Select Nearest Point" button (+1) — net 177 → 180. S7.6 replaced the Meta tab's single-column rules list with VTank's own 6-column lstMetaRules grid (+6) — net 180 → 186. Fix round B item 5: VTank's real Meta tab is only 5 controls (the grid, "Create", a caption, and the settable current-state menu). Removed entirely: the profile toolbar (menu/name-draft/New/CopyTo/Clear/Delete, 6) and "Enable Meta" (1) — 7 controls gone for good. Moved into mosstank-metaeditor.xml (a separate file this test doesn't scan): the State/Condition/Action editor (2 fields + 2 menus + 1 field for state + 1 secondary-text field + 4 numeric-stepper buttons + Apply/Remove/MoveUp/MoveDown, 14). Added: the settable cmbMetaCurrentState menu (+1). Net 186 → 166 (-7 -14 +1). Fix round B item 8: the Route tab collapses to VTank's own 2-across button grid + bottom nav-control row, 28 → 18 interactive controls within mosstank.xml. Checkpoint/Jump/Remove/Set Follow Target/Follow Corners/Open Doors/Nav Priority/the Follow-Nav-Min-Distance stepper moved to mosstank-advanced.xml's own new "MossTank Extras" section. The 2-state ToggleRouteAddPosition button became the real 3-option cmbNavInsertMode `<menu>`, and the pause "-"/"+" stepper became one editable field. Net 166 → 156 (the count the test asserted immediately before this fix round removed the assertion). The test's own per-control handler/enabled loop remains the live guarantee; a bare `Assert.NotEmpty(controls)` guards the selector itself. Round D dispatched on the same worktree (merges the campaign branch first): category names + VTank's Advanced Options interaction model, the 26-recall table wired to route execution, resizable main panel at a ~15% larger default with anchored lists.
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ in-memory KSML string instead of a file path — the route to reach for when a
|
|||
panel is small enough not to need its own shipped `.xml` asset.
|
||||
|
||||
Every registered window gets a stable persisted key
|
||||
(`plugin:{pluginId}:{windowId}`), drag, resize (where the markup opts in),
|
||||
the global UI lock, and a button in the shared plugin shelf
|
||||
(`plugin:{pluginId}:{windowId}`), drag, resize (where the markup opts in —
|
||||
`<panel resizable="true">`, see "Resizable panels and anchors" below), the
|
||||
global UI lock, and a button in the shared plugin shelf
|
||||
(`ShowInSidePanel = true`, the default). Hiding or minimizing a window never
|
||||
disables the plugin or pauses its `Tick`.
|
||||
|
||||
|
|
@ -98,18 +99,18 @@ vanishing from the built tree.
|
|||
|
||||
| Element | Purpose | Key attributes |
|
||||
|---|---|---|
|
||||
| `panel` (root) | The window itself | `x y w h title resize visible` |
|
||||
| `group` | Transparent layout container | `x y w h background border visible` |
|
||||
| `label` | Static or bound text | `x y text color` |
|
||||
| `button` | Clickable rect + caption (+ Slice B icon) | `x y w h text color background border onclick icon iconkind` |
|
||||
| `icon` | Slice B: a standalone DAT icon | `x y w h did spell item tooltip` |
|
||||
| `panel` (root) | The window itself | `x y w h title resize resizable minw minh visible` |
|
||||
| `group` | Transparent layout container | `x y w h background border visible anchor` |
|
||||
| `label` | Static or bound text | `x y text color anchor` |
|
||||
| `button` | Clickable rect + caption (+ Slice B icon) | `x y w h text color background border onclick icon iconkind anchor` |
|
||||
| `icon` | Slice B: a standalone DAT icon | `x y w h did spell item tooltip anchor` |
|
||||
| `meter` | Retail-style nine-slice bar | `x y w h fill cur max color anchor backleft/backtile/backright frontleft/fronttile/frontright` |
|
||||
| `tab` | Selectable tab button | `x y w h text selected onclick` |
|
||||
| `toggle` | Lamp-style checkbox | `x y w h text checked onclick color` |
|
||||
| `slider` | Horizontal scalar | `x y w h value onchange min max style` |
|
||||
| `field` | Single-line editable text | `x y w h text maxlength clearonsubmit onchange onsubmit color background` |
|
||||
| `menu` | Dropdown selector | `x y w h items selected onchange rows rowheight openupward scroll style` |
|
||||
| `list` | Scrollable row list (+ Slice B icon column, + Campaign VT slice 1 multi-column) | `x y w h selected onchange rowheight` + either the single-column `items colors icons iconkind`, or one-to-many `<column>` children (see "Columns" below) — never both |
|
||||
| `tab` | Selectable tab button | `x y w h text selected onclick anchor` |
|
||||
| `toggle` | Lamp-style checkbox | `x y w h text checked onclick color anchor` |
|
||||
| `slider` | Horizontal scalar | `x y w h value onchange min max style anchor` |
|
||||
| `field` | Single-line editable text | `x y w h text maxlength clearonsubmit onchange onsubmit color background anchor` |
|
||||
| `menu` | Dropdown selector | `x y w h items selected onchange rows rowheight openupward scroll style anchor` |
|
||||
| `list` | Scrollable row list (+ Slice B icon column, + Campaign VT slice 1 multi-column) | `x y w h selected onchange rowheight anchor` + either the single-column `items colors icons iconkind`, or one-to-many `<column>` children (see "Columns" below) — never both |
|
||||
|
||||
`menu style` is `plain` (the default) or `retail`: retail's gold pushbutton
|
||||
art read as an out-of-place "big yellow button" next to a plugin's own dark
|
||||
|
|
@ -125,10 +126,17 @@ scrollbar, and a baked checkmark glyph on the current entry even with
|
|||
`style="plain"`. The open popup now matches `<list>`'s own chrome too: a
|
||||
flat fill + 1px border, one row per entry in the list text color, the
|
||||
current entry filled like a list selection, the hovered entry a slightly
|
||||
lighter fill, and no checkmark; more entries than the row cap show a plain
|
||||
1px-bordered scrollbar track with a flat thumb, no DAT scrollbar art.
|
||||
`style="retail"` keeps the sprite popup (gradient panel, checkmark-bearing
|
||||
row art, ornate scrollbar) exactly as before, unchanged.
|
||||
lighter fill, and no checkmark. A `<menu>` popup always scrolls a single
|
||||
column (rather than wrapping into more grid columns) once its item count
|
||||
exceeds `rows`; a further owner directive (still 2026-09-07 — "For
|
||||
scrollable dropdown or the meta window we use the same assets as we do in
|
||||
for example chat or inventory window") moved that overflow scrollbar to
|
||||
retail's own chrome — the exact sprites the chat window's transcript and
|
||||
the inventory list already use — while the rows themselves stay the flat
|
||||
style described above; a menu with too few items to overflow shows no bar
|
||||
at all. `style="retail"` keeps the sprite popup rows (gradient panel,
|
||||
checkmark-bearing row art) exactly as before, unchanged — only the
|
||||
scrollbar chrome is shared between the two styles.
|
||||
|
||||
`slider style` is the same `plain`/`retail` grammar (fix round B item 11,
|
||||
2026-09-07): `plain` (the default) draws a flat dark track, a 1px border,
|
||||
|
|
@ -140,12 +148,15 @@ same rule as `menu style`.
|
|||
|
||||
Common to every element via `ApplyCommon`: `name`/`id` (a stable control
|
||||
name), `visible` (literal `true`/`false` or a bound `bool` property),
|
||||
`enabled` (same rule), and `tooltip` (a literal string or `{Binding}` shown
|
||||
`enabled` (same rule), `tooltip` (a literal string or `{Binding}` shown
|
||||
through retail's own runtime tooltip popup, empty/whitespace treated as no
|
||||
tooltip). The root `<panel>` is the one exception: it does **not** go
|
||||
through `ApplyCommon` (no `name`/`enabled`/`tooltip`), and its `visible`
|
||||
attribute accepts a `{Binding}` only — a literal `visible="true"` on the
|
||||
root is not parsed (unlike every child element, where a literal is fine).
|
||||
tooltip), and `anchor` (which edges of the element's PARENT it keeps a fixed
|
||||
margin to on resize — see "Resizable panels and anchors" below). The root
|
||||
`<panel>` is the one exception: it does **not** go through `ApplyCommon` (no
|
||||
`name`/`enabled`/`tooltip`/`anchor` — a top-level window is never anchored to
|
||||
its own parent, only dragged/resized directly), and its `visible` attribute
|
||||
accepts a `{Binding}` only — a literal `visible="true"` on the root is not
|
||||
parsed (unlike every child element, where a literal is fine).
|
||||
|
||||
Multi-column lists are real (Campaign VT slice 1 Part B, below) — a `<list>`
|
||||
with `<column>` children is no longer limited to one padded text column. A
|
||||
|
|
@ -162,6 +173,67 @@ the `0x` prefix to parse as hex; an all-digit string with no prefix
|
|||
(`did="165"`) parses as **decimal**, not hex — `did="165"` and `did="0x165"`
|
||||
are different ids.
|
||||
|
||||
## Resizable panels and anchors
|
||||
|
||||
A plugin panel is **fixed-size by default** — this matches every panel
|
||||
shipped before 2026-09-07 (e.g. `mosstank.xml`'s `resize="none"`). A window
|
||||
opts into real user drag-resize with `<panel resizable="true">`, and every
|
||||
non-root element opts its OWN geometry into following that resize with
|
||||
`anchor="..."`. The two attributes are independent: a resizable panel whose
|
||||
children have no `anchor` just gets bigger/smaller with empty space at the
|
||||
bottom-right (today's default placement, `Left|Top`); a panel with anchored
|
||||
children but `resizable` left at its default `false` never actually resizes,
|
||||
so the anchors never have anything to react to.
|
||||
|
||||
| Attribute | Element | Meaning |
|
||||
|---|---|---|
|
||||
| `resizable` | `panel` (root) | `"true"` arms the window for user drag-resize on both axes (edges + corners, same mechanism chat windows use); default `false` — fixed size, exactly as before this attribute existed |
|
||||
| `minw` / `minh` | `panel` (root) | The floor a drag-resize (and a persisted-layout restore) will not shrink below. Default: the panel's own authored `w`/`h` — a resizable panel never shrinks past the layout its author actually tested. Only meaningful when `resizable="true"` |
|
||||
| `resize` | `panel` (root) | Pre-existing per-axis lock (`x`/`y`/`both`/`none`) that narrows `resizable="true"` to one axis; has no effect on its own now that `resizable` (default `false`) is the master switch |
|
||||
| `anchor` | `group` `list` `menu` `field` `label` `button` `icon` (and `meter`/`tab`/`toggle`/`slider`) | Space-separated subset of `left top right bottom` (case-insensitive), naming which edges of the element's **direct parent** it keeps a fixed margin to as that parent resizes. Default (attribute absent) is `left top` — today's fixed placement, unchanged |
|
||||
|
||||
`anchor` semantics are exactly `AcDream.App.UI.UiElement.Anchors`/
|
||||
`AnchorEdges`/`ApplyAnchor` (already used by every retail-imported window):
|
||||
|
||||
- `left top` (the default) — pinned top-left at a fixed size; never stretches.
|
||||
- `left right` — stretches WIDTH to track the parent (both side margins stay
|
||||
fixed).
|
||||
- `top bottom` — stretches HEIGHT the same way, vertically.
|
||||
- `left top right bottom` — stretches on both axes.
|
||||
- `right` alone (no `left`) — pins to the parent's right edge at a FIXED
|
||||
width, moving as the parent resizes rather than stretching. `bottom` alone
|
||||
is the same, vertically.
|
||||
|
||||
An element's parent is whatever markup element directly contains it — for a
|
||||
`<group>`'s children, that is the GROUP, not the panel. This is how a group
|
||||
propagates resize to its own contents: give the group
|
||||
`anchor="left top right bottom"` so it stretches with the panel, and give a
|
||||
`<list>` inside it `anchor="left right"` so the list stretches with the
|
||||
GROUP's width in turn. An unrecognized token (a typo like
|
||||
`anchor="left rihgt"`) throws `FormatException` at `Build`, naming the
|
||||
offending element by its `name`/`id` — the same "malformed markup throws"
|
||||
rule every other attribute in this grammar follows.
|
||||
|
||||
No other markup or host wiring is needed to make a panel resizable: once
|
||||
`resizable="true"` sets the window's `Resizable`/`ResizeX`/`ResizeY`/
|
||||
`MinWidth`/`MinHeight`, the SAME drag-resize, persistence (save/restore
|
||||
across sessions, clamped to `minw`/`minh`), and UI-lock behavior every other
|
||||
retained window already has just applies.
|
||||
|
||||
```xml
|
||||
<panel x="0" y="0" w="420" h="320" title="MossTank" resizable="true" minw="360" minh="260">
|
||||
<group anchor="left top right bottom" x="8" y="8" w="404" h="304" border="#FF4A3A14">
|
||||
<label x="4" y="4" text="Monsters"/>
|
||||
<list anchor="left right top bottom" x="4" y="24" w="396" h="276"
|
||||
items="{MonsterNames}" selected="{SelectedMonster}" onchange="{SelectMonster}"/>
|
||||
</group>
|
||||
</panel>
|
||||
```
|
||||
|
||||
Here the outer `<group>` stretches with the panel on every edge, and the
|
||||
`<list>` inside it stretches with the GROUP on every edge in turn — dragging
|
||||
the window's corner grows the whole list, not just empty panel background.
|
||||
|
||||
## The icon-id grammar (Slice B)
|
||||
|
||||
Decal/VirindiViewService plugins (the reference usage this ported:
|
||||
|
|
@ -469,10 +541,12 @@ 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.
|
||||
`fixedwidth`. acdream's column model has no separate gap/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. The 16px scrollbar column IS now
|
||||
automatic (owner directive, 2026-09-07 — see "Scrollbar" below): a plugin
|
||||
author never reserves it by hand.
|
||||
|
||||
To transcribe a real VTank column table (as in
|
||||
`refs/vtank/uTank2.ViewXML.mainView.xml`) faithfully, declare each column's
|
||||
|
|
@ -480,11 +554,26 @@ To transcribe a real VTank column table (as in
|
|||
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.
|
||||
(`16 -> 13 + 7 = 20`, not `16 + 7 = 23`). Do NOT also fold VVS's 16px
|
||||
scrollbar width into the last column's pitch or the list's total `w` — the
|
||||
list reserves that width itself, automatically, only while its rows
|
||||
actually overflow (see "Scrollbar" below); doing both would double-reserve
|
||||
it and starve the last column once the list has few enough rows to hide
|
||||
the bar.
|
||||
|
||||
### Scrollbar
|
||||
|
||||
Once a `<list>`'s rows overflow its own height (either the single-column or
|
||||
the `<column>` form), it reserves a 16px column at its right edge — VVS's
|
||||
own `VScrollBarButtonSize` placement — and draws retail's scrollbar chrome
|
||||
there: the same sprite ids the chat window's transcript and the inventory
|
||||
list already draw through (owner live-client report 2026-09-07: "For
|
||||
scrollable dropdown or the meta window we use the same assets as we do in
|
||||
for example chat or inventory window"). Mouse wheel keeps working as
|
||||
before; the bar itself is also fully interactive (up/down arrow clicks,
|
||||
track paging, and thumb drag). A list whose rows all fit reserves no width
|
||||
and draws no bar at all — the reservation and the chrome both come and go
|
||||
together with actual overflow, never present "just in case."
|
||||
|
||||
### Backward compatibility
|
||||
|
||||
|
|
@ -587,4 +676,14 @@ 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.
|
||||
real Monsters- and Meta-tab column shapes. `MarkupResizableAnchorTests`
|
||||
covers `resizable`/`minw`/`minh` parsing, the `anchor` grammar (default,
|
||||
every token combination, the unknown-token throw) across every element
|
||||
listed above, live re-layout against the same recording-renderer apparatus
|
||||
(a stretching list, a right-anchored button that moves, a group whose resize
|
||||
propagates to its own anchored children), and a golden proving a panel with
|
||||
none of these attributes draws byte-identically to itself across repeated
|
||||
builds. `RetailWindowManagerTests`/`RetailWindowLayoutPersistenceTests`
|
||||
cover a resizable markup panel through the real `ResizeTo`/save-restore
|
||||
paths (accepts within `minw`/`minh`, a fixed panel refuses, a restored size
|
||||
below the CURRENT floor clamps up to it).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue