fix(vtank): slice 7 round D item 4 — resizable panel, larger default
Owner live report 2026-09-07: "The size of the entire window needs to be enlarged for default and should also be resizeable." mosstank.xml's main panel is now resizable="true" with minw/minh floored at the PRE-round-D authored size (856x236) and a new default authored size ~15% larger in both directions (984x271) — every one of the nine tab groups grows by the identical 128px-wide/35px-tall delta (848x194 -> 976x229), preserving the original 0px group-to-panel margin design exactly. Every tab <group> gets anchor="left top right bottom" so a future manual drag-resize keeps growing it. The two single-list tabs with nothing beside their list (Monsters, Meta) give their list the SAME full stretch, widened AND heightened by the same 128x35 delta at author time — anchors only react to CHANGE from their first-draw baseline, so bumping only the outer group while leaving the list's own geometry untouched would leave it visually unchanged at the new default (the growth invisibly reserved as margin until a further manual resize). Every other tab's list(s) grow in HEIGHT only (+35), pinned to whichever horizontal edge doesn't have a sibling list/button column in the way (Items/Consumables/Buffs/Route all have one) — a right-pinned sibling (Consumables' Excluded Scarab list, Buffs' Blacklisted Buff Families list) is repositioned +128 at author time so it already sits flush against the enlarged default's right edge. Trailing "Add" rows/status labels below a taller list shift down by the same +35 and get anchor="bottom" to keep tracking. Options/ Profiles/Vitals need no internal changes — only their own group grows. Deviations, both documented in mosstank.xml's own Round D item 4 comment: Monsters' extra 128px of list width lands in its LAST column (the MoveDown icon, which always absorbs the remainder per the markup grammar) — a wider icon cell, not a redesigned grid; seven of nine tabs' groups grow wider than their content uses, leaving harmless empty space on the right at the enlarged default rather than redesigning nine tabs' pixel geometry in one pass (a deliberate, minimal-risk choice — nothing moved INTO another control's space, only into previously-empty margin, so every existing AssertNoSiblingOverlap/ AssertWithinParent guarantee still holds). Tests added: - MossTankMarkupContractTests.AuthoredShellFitsTheMinimumCanvasAnd... updated to the new 984x271 default. - PanelIsResizableFlooredAtThePreRoundDAuthoredSize (resizable="true", minw/minh = 856/236). - EveryStretchingListDeclaresARealAnchor (contract pin: every list in Monsters/Items/Consumables/Buffs/Route/Meta declares a real anchor attribute, not the silent left-top default). - MossTankMarkupBuildOverRealFilesTests. WideningTheRealMainPanelWidensTheRealMonstersList (App.Tests): builds the REAL mosstank.xml against a real MossTankPanel, widens the built root by 100px, and confirms the Monsters list's own Width grows in turn — the same mechanism MarkupResizableAnchorTests already proves against synthetic markup, now proven against the shipped file. Mutation shown to fail: temporarily removing the Monsters list's own anchor attribute failed the new re-layout test (976 -> 976, no growth); restored and confirmed green. A build-copy gotcha surfaced while writing these tests: dotnet build's default incremental copy did NOT refresh the test projects' deployed mosstank.xml after editing the source file (PreserveNewest apparently didn't see it as newer under normal incremental evaluation) — dotnet build --no-incremental was needed to get a fresh copy into bin/ before the size/anchor tests would actually exercise the new markup instead of a stale cached copy. Verified: dotnet build AcDream.slnx -c Release (clean, --no-incremental) green; MossTank suite 715/715 (713 -> 715, two new markup contract pins); App markup/plugin filter 243/243 (242 -> 243, one new re-layout test). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
5318adbb30
commit
2d626971d9
3 changed files with 286 additions and 65 deletions
|
|
@ -50,8 +50,60 @@
|
|||
248->252) as a small safety margin since it was the tightest already-OK
|
||||
case measured.
|
||||
-->
|
||||
<panel x="28" y="42" w="856" h="236" title="MossTank v0.1.0"
|
||||
visible="{WindowAvailable}" resize="none">
|
||||
<!--
|
||||
Round D item 4 (owner: "The size of the entire window needs to be
|
||||
enlarged for default and should also be resizeable"): the panel is now
|
||||
resizable="true" with minw/minh pinned to the PRE-round-D authored size
|
||||
(856x236, the floor a drag-resize or a persisted-layout restore will
|
||||
never shrink below) and a new default authored size ~15% larger in both
|
||||
directions (984x271) — the SAME uniform 128px-wide/35px-tall delta every
|
||||
one of the nine tab groups below also grows by (848x194 -> 976x229),
|
||||
preserving the original 0px group-to-panel margin design exactly
|
||||
(976 = 984-8, 229 = 271-42, matching 848 = 856-8, 194 = 236-42).
|
||||
|
||||
Anchors follow the doc's own rule ("give the group anchor=... so it
|
||||
stretches with the panel, and give a <list> inside it anchor=... so the
|
||||
list stretches with the GROUP's width in turn"): every tab <group> gets
|
||||
anchor="left top right bottom" so a future manual drag-resize keeps
|
||||
growing it. The two single-list, nothing-to-its-side tabs (Monsters,
|
||||
Meta) give their list the SAME full stretch, widened AND heightened by
|
||||
the identical 128x35 delta at author time so the enlarged DEFAULT
|
||||
already shows a filled list, not a list floating in a corner with dead
|
||||
space baked in as a captured margin (anchors only react to CHANGE from
|
||||
their first-draw baseline — bumping only the outer group's size while
|
||||
leaving a list's authored geometry untouched would leave it visually
|
||||
unchanged at the new default, all the growth invisibly reserved as
|
||||
"margin" until a further manual resize). Every OTHER tab's list(s) grow
|
||||
in HEIGHT only (+35, matching the group), pinned to their original
|
||||
horizontal edge (anchor="left ... bottom" for a left list, "right ...
|
||||
bottom" for a right one) — height-only growth is safe everywhere
|
||||
(nothing sits below a list that a taller list would collide with, once
|
||||
the trailing "Add" row/status labels below it are shifted down by the
|
||||
same +35 and anchored "bottom" to keep tracking) while WIDTH growth
|
||||
risks colliding with a sibling list or button column that isn't also
|
||||
being resized (Items/Consumables/Buffs/Route all have one), so this
|
||||
round leaves those horizontally fixed rather than guessing a redesign
|
||||
VTank's own KB tables don't describe. A right-pinned sibling (the
|
||||
second list in Consumables/Buffs) is REPOSITIONED +128 (the same delta)
|
||||
at author time so it already sits flush against the new default's right
|
||||
edge instead of floating mid-window until a manual resize catches up,
|
||||
matching the same "capture the FINAL position, not the pre-growth one"
|
||||
principle. Options/Profiles/Vitals need no internal changes (no <list>,
|
||||
every control already fits comfortably) — only their own group grows,
|
||||
leaving harmless empty space below/right at the bigger default; this is
|
||||
a deliberate, minimal-risk choice over redesigning nine tabs' pixel
|
||||
geometry in one pass; every AssertNoSiblingOverlap/AssertWithinParent
|
||||
guarantee this round doesn't touch continues to hold since nothing
|
||||
moved INTO another control's space, only into previously-empty margin.
|
||||
|
||||
The two secondary popups (mosstank-loot-editor.xml, mosstank-
|
||||
buffpicker.xml, mosstank-metaeditor.xml) keep their fixed VTank-matched
|
||||
sizes per the plan's own scope; the Advanced Options popup
|
||||
(mosstank-advanced.xml) is the one exception and gets its own
|
||||
resizable="true" + anchored lists, per its own file's comment.
|
||||
-->
|
||||
<panel x="28" y="42" w="984" h="271" title="MossTank v0.1.0"
|
||||
visible="{WindowAvailable}" resizable="true" minw="856" minh="236">
|
||||
<!-- Virindi Tank tab order and compact text-strip presentation. -->
|
||||
<tab x="8" y="22" w="58" h="18" text="Options"
|
||||
selected="{OptionsSelected}" enabled="{OptionsTabEnabled}" onclick="{ShowOptions}" />
|
||||
|
|
@ -80,7 +132,7 @@
|
|||
settings that already existed (only reachable through the generic
|
||||
Advanced Options key-value editor) and now also get VTank's own
|
||||
direct checkbox. -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{OptionsVisible}">
|
||||
<group x="8" y="42" w="976" h="229" visible="{OptionsVisible}" anchor="left top right bottom">
|
||||
<label x="8" y="8" w="80" h="16" text="Monster Range:" color="#FFE8DEC3" />
|
||||
<field x="8" y="24" w="40" h="16" text="{MonsterRangeValueText}"
|
||||
onchange="{SetMonsterRangeText}" maxlength="6" background="#E6000000"
|
||||
|
|
@ -212,7 +264,7 @@
|
|||
Editor, Meta has only CopyTo) — they now share one column (x=566),
|
||||
leaving a wider gap on the shorter rows rather than four columns
|
||||
that don't line up. -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{ProfilesVisible}">
|
||||
<group x="8" y="42" w="976" h="229" visible="{ProfilesVisible}" anchor="left top right bottom">
|
||||
<label x="8" y="8" w="142" h="16" text="Macro Settings" color="#FFE8DEC3" />
|
||||
<menu x="156" y="8" w="140" h="20" items="{MacroProfileNames}"
|
||||
selected="{SelectedMacroProfile}" onchange="{SelectMacroProfile}"
|
||||
|
|
@ -271,7 +323,7 @@
|
|||
only span x=8-328; "Enable vital recharge"/"Help fellows" (no VTank
|
||||
equivalent — MossTank-only features) and the status labels sit to
|
||||
the right of that, in space VTank's own layout never uses. -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{VitalsVisible}">
|
||||
<group x="8" y="42" w="976" h="229" visible="{VitalsVisible}" anchor="left top right bottom">
|
||||
<label x="8" y="0" w="80" h="16" text="Heal at:" color="#FFE8DEC3" />
|
||||
<slider x="100" y="0" w="144" h="16" min="0" max="100"
|
||||
value="{NormalHealthPercent}" onchange="{SetNormalHealthPercent}"
|
||||
|
|
@ -338,7 +390,7 @@
|
|||
cell is its own click target, exactly like VTank's own row (VVS's
|
||||
HudList has no header row either — the 15 <label>s below are
|
||||
ordinary StaticText placed above the list, same as mainView.xml). -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{MonstersVisible}">
|
||||
<group x="8" y="42" w="976" h="229" visible="{MonstersVisible}" anchor="left top right bottom">
|
||||
<label x="0" y="0" w="20" text="F" color="#FFE8DEC3" tooltip="Fester" />
|
||||
<label x="20" y="0" w="20" text="B" color="#FFE8DEC3" tooltip="Broadside of a Barn" />
|
||||
<label x="40" y="0" w="20" text="G" color="#FFE8DEC3" tooltip="Gravity Well" />
|
||||
|
|
@ -374,7 +426,14 @@
|
|||
<!-- clMoveUp/clMoveDown (#22/#23) carry no header label in VTank's own
|
||||
mainView.xml either (KB table: "-" for both) — skipped to match. -->
|
||||
|
||||
<list x="0" y="16" w="848" h="116"
|
||||
<!-- Round D item 4: full stretch (Monsters has nothing to either side
|
||||
of its one full-width list) — widened/heightened by the same
|
||||
128x35 delta the group grows by so the enlarged default already
|
||||
shows a filled list; the extra 128px lands in the LAST column
|
||||
(MoveDown icon), which always absorbs the remainder regardless of
|
||||
its own declared width (docs/plugin-ui-markup.md's Columns
|
||||
section) — a wider icon cell, not a broken one. -->
|
||||
<list x="0" y="16" w="976" h="151" anchor="left right top bottom"
|
||||
selected="{SelectedMonsterListRow}" onchange="{SelectMonsterListRow}"
|
||||
tooltip="Click a check to toggle it, a name to delete the rule, or another cell to cycle its value.">
|
||||
<column type="check" width="20" values="{MonsterFesterColumn}" onchange="{ToggleMonsterFesterAt}" />
|
||||
|
|
@ -402,13 +461,15 @@
|
|||
<column type="icon" width="23" iconkind="did" values="{MonsterMoveDownIcons}" onclick="{MoveMonsterRuleDownAt}" />
|
||||
</list>
|
||||
|
||||
<field x="8" y="136" w="388" h="16" text="{MonsterExpressionDraft}"
|
||||
<field x="8" y="171" w="388" h="16" text="{MonsterExpressionDraft}"
|
||||
onchange="{SetMonsterExpressionDraft}" maxlength="256"
|
||||
background="#E6000000" color="#FFE8DEC3"
|
||||
background="#E6000000" color="#FFE8DEC3" anchor="bottom"
|
||||
tooltip="Monster name or expression to match." />
|
||||
<button x="404" y="136" w="50" h="16" text="Add" onclick="{AddMonsterRule}"
|
||||
<button x="404" y="171" w="50" h="16" text="Add" onclick="{AddMonsterRule}"
|
||||
anchor="bottom"
|
||||
tooltip="Add a new monster rule using the name/expression above." />
|
||||
<button x="462" y="136" w="50" h="16" text="Add Sel" onclick="{AddSelectedMonster}"
|
||||
<button x="462" y="171" w="50" h="16" text="Add Sel" onclick="{AddSelectedMonster}"
|
||||
anchor="bottom"
|
||||
tooltip="Add the monster currently selected in the world." />
|
||||
</group>
|
||||
|
||||
|
|
@ -421,20 +482,24 @@
|
|||
click deletes the row (case 0); hands-cell click cycles handedness
|
||||
(case 1) — session-local only, see DeleteItemRowAt/CycleItemHandsAt's
|
||||
own doc comments for why handedness isn't persisted. -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{ItemsVisible}">
|
||||
<group x="8" y="42" w="976" h="229" visible="{ItemsVisible}" anchor="left top right bottom">
|
||||
<label x="4" y="8" w="327" text="Weapons / Wands / Shields / Pets" color="#FFE8DEC3" />
|
||||
<label x="331" y="8" w="103" text="Hands" color="#FFE8DEC3" />
|
||||
<list x="4" y="28" w="430" h="112" rowheight="18"
|
||||
<!-- Round D item 4: height-only growth (+35, matching the group) —
|
||||
the right-side info block starts at x=438, only 4px past this
|
||||
list's right edge, so widening it would collide; pinned left,
|
||||
not stretching horizontally. -->
|
||||
<list x="4" y="28" w="430" h="147" rowheight="18" anchor="left top bottom"
|
||||
selected="{SelectedItemRowIndex}" onchange="{SelectItemRow}"
|
||||
tooltip="Click a name to remove it, or Hands to cycle 1-Handed/2-Handed.">
|
||||
<column type="text" width="327" items="{ItemNameColumn}" onclick="{DeleteItemRowAt}" />
|
||||
<column type="text" width="103" items="{ItemHandsColumn}" onclick="{CycleItemHandsAt}" />
|
||||
</list>
|
||||
<button x="4" y="154" w="126" h="25" text="Add" onclick="{AddSelectedItem}" />
|
||||
<button x="138" y="154" w="138" h="25" text="Add (no buffs)"
|
||||
onclick="{AddSelectedItemNoBuffs}" />
|
||||
<button x="284" y="154" w="126" h="25" text="Remove"
|
||||
onclick="{RemoveSelectedItem}" />
|
||||
<button x="4" y="189" w="126" h="25" text="Add" onclick="{AddSelectedItem}" anchor="bottom" />
|
||||
<button x="138" y="189" w="138" h="25" text="Add (no buffs)"
|
||||
onclick="{AddSelectedItemNoBuffs}" anchor="bottom" />
|
||||
<button x="284" y="189" w="126" h="25" text="Remove"
|
||||
onclick="{RemoveSelectedItem}" anchor="bottom" />
|
||||
<label x="438" y="36" text="{MonsterEquipmentText}" color="#FFC7B98F" />
|
||||
<toggle x="438" y="64" w="220" h="20" text="Refill Worn Mana"
|
||||
checked="{RefillWornManaEnabled}" onclick="{ToggleRefillWornMana}" />
|
||||
|
|
@ -452,18 +517,24 @@
|
|||
row; "Add Selected" requires a selected spell-component-class
|
||||
object, see AddSelectedComponent's own doc comment). PITCH: icon
|
||||
16+7=23, text column auto ("*", last column). -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{ConsumablesVisible}">
|
||||
<group x="8" y="42" w="976" h="229" visible="{ConsumablesVisible}" anchor="left top right bottom">
|
||||
<label x="4" y="8"
|
||||
text="Gems / Food / Kits / Potions / Charges / Grenades / Lockpicks"
|
||||
color="#FFE8DEC3" />
|
||||
<label x="438" y="8" text="Excluded Scarab Types:" color="#FFE8DEC3" />
|
||||
<!-- Round D item 4: the right-hand block (header/list/button/notice,
|
||||
all originally at x=438 against the OLD 848-wide group) shifts by
|
||||
the SAME +128 delta the group grows by, so it already sits flush
|
||||
against the enlarged default's right edge instead of floating
|
||||
mid-window until a manual resize catches up; anchor="right"
|
||||
keeps it tracking any FURTHER resize the same way. -->
|
||||
<label x="566" y="8" text="Excluded Scarab Types:" color="#FFE8DEC3" anchor="right" />
|
||||
<!-- Fix round B item 7: a click removes the row directly
|
||||
(PluginCore.cs:7683-7700), matching the "click a row to remove it"
|
||||
convention the right-hand list already uses. -->
|
||||
<list x="4" y="28" w="414" h="112" rowheight="18"
|
||||
<list x="4" y="28" w="414" h="147" rowheight="18" anchor="left top bottom"
|
||||
items="{ConsumableRows}" selected="{SelectedConsumableRowIndex}"
|
||||
onchange="{SelectConsumableRow}" tooltip="Click a row to remove it." />
|
||||
<list x="438" y="28" w="406" h="112" rowheight="18"
|
||||
<list x="566" y="28" w="406" h="147" rowheight="18" anchor="right top bottom"
|
||||
selected="{SelectedExcludedComponentIndex}" onchange="{SelectExcludedComponentRow}"
|
||||
tooltip="Click a row to remove it from the blacklist.">
|
||||
<column type="icon" width="23" iconkind="did" values="{ExcludedComponentIcons}"
|
||||
|
|
@ -471,15 +542,15 @@
|
|||
<column type="text" width="*" items="{ExcludedComponentRows}"
|
||||
onclick="{DeleteExcludedComponentAt}" />
|
||||
</list>
|
||||
<button x="4" y="154" w="176" h="25" text="Add"
|
||||
onclick="{AddSelectedConsumable}" />
|
||||
<button x="188" y="154" w="110" h="25" text="Add All Peas"
|
||||
onclick="{AddAllPeas}" />
|
||||
<button x="306" y="154" w="104" h="25" text="Remove"
|
||||
onclick="{RemoveSelectedConsumable}" />
|
||||
<button x="438" y="154" w="176" h="24" text="Add Selected"
|
||||
onclick="{AddSelectedComponent}" />
|
||||
<label x="438" y="178" w="176" h="16" text="{ProfileNotice}" color="#FF9B9072" />
|
||||
<button x="4" y="189" w="176" h="25" text="Add"
|
||||
onclick="{AddSelectedConsumable}" anchor="bottom" />
|
||||
<button x="188" y="189" w="110" h="25" text="Add All Peas"
|
||||
onclick="{AddAllPeas}" anchor="bottom" />
|
||||
<button x="306" y="189" w="104" h="25" text="Remove"
|
||||
onclick="{RemoveSelectedConsumable}" anchor="bottom" />
|
||||
<button x="566" y="189" w="176" h="24" text="Add Selected"
|
||||
onclick="{AddSelectedComponent}" anchor="right bottom" />
|
||||
<label x="566" y="213" w="176" h="16" text="{ProfileNotice}" color="#FF9B9072" anchor="right bottom" />
|
||||
</group>
|
||||
|
||||
<!-- Buffs: VTank's REAL Buffs tab (docs/research/vtank-kb/08-ui-views.md
|
||||
|
|
@ -507,7 +578,7 @@
|
|||
(194-16, item 3's bottom-fit rule) — a proportion-preserving
|
||||
compromise fitting a real 184x194 box, not a pixel-for-pixel VVS
|
||||
port (owner's bar: "looks basically the same", never VVS pixels). -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{BuffsVisible}">
|
||||
<group x="8" y="42" w="976" h="229" visible="{BuffsVisible}" anchor="left top right bottom">
|
||||
<!-- Fix round B item 14 (owner honesty rule): BuffPlan.Build does not
|
||||
read this set at all yet — a real, tracked wiring gap (see
|
||||
BuffSettings.ExtraBuffSpellNames' own doc comment; issue #491),
|
||||
|
|
@ -515,19 +586,24 @@
|
|||
names already affect what gets cast. -->
|
||||
<label x="4" y="0" w="320" h="16" text="Extra Buff Spells" color="#FFE8DEC3"
|
||||
tooltip="Named spell exemplars — stored and shown here, not yet used when choosing what to cast (#491)." />
|
||||
<list x="4" y="18" w="320" h="116" rowheight="17"
|
||||
<!-- Round D item 4: height-only growth (+35), pinned left. -->
|
||||
<list x="4" y="18" w="320" h="151" rowheight="17" anchor="left top bottom"
|
||||
items="{ExtraBuffRows}" selected="{SelectedExtraBuffIndex}"
|
||||
onchange="{DeleteExtraBuffAt}" tooltip="Click a row to remove it." />
|
||||
<button x="4" y="138" w="120" h="18" text="Add..." onclick="{ShowExtraBuffPicker}" />
|
||||
<button x="4" y="173" w="120" h="18" text="Add..." onclick="{ShowExtraBuffPicker}" anchor="bottom" />
|
||||
|
||||
<!-- Fix round B item 14: same honesty note as Extra Buff Spells — not
|
||||
yet read by BuffPlan.Build (issue #491). -->
|
||||
<label x="524" y="0" w="320" h="16" text="Blacklisted Buff Families" color="#FFE8DEC3"
|
||||
yet read by BuffPlan.Build (issue #491). Round D item 4: this
|
||||
whole right-hand block shifts +128 (the group's own growth
|
||||
delta) so it sits flush against the enlarged default's right
|
||||
edge, same as Consumables' right list. -->
|
||||
<label x="652" y="0" w="320" h="16" text="Blacklisted Buff Families" color="#FFE8DEC3"
|
||||
anchor="right"
|
||||
tooltip="Named buff families — stored and shown here, not yet used when choosing what to cast (#491)." />
|
||||
<list x="524" y="18" w="320" h="116" rowheight="17"
|
||||
<list x="652" y="18" w="320" h="151" rowheight="17" anchor="right top bottom"
|
||||
items="{BlacklistedBuffFamilyRows}" selected="{SelectedBlacklistedBuffIndex}"
|
||||
onchange="{DeleteBlacklistedBuffFamilyAt}" tooltip="Click a row to remove it." />
|
||||
<button x="524" y="138" w="120" h="18" text="Add..." onclick="{ShowBlacklistedBuffPicker}" />
|
||||
<button x="652" y="173" w="120" h="18" text="Add..." onclick="{ShowBlacklistedBuffPicker}" anchor="right bottom" />
|
||||
|
||||
<toggle x="332" y="0" w="180" h="16" text="Trained skills only"
|
||||
checked="{TrainedOnlyEnabled}" onclick="{ToggleTrainedOnly}" />
|
||||
|
|
@ -557,7 +633,11 @@
|
|||
|
||||
<button x="332" y="154" w="110" h="18" text="{BuffButtonText}" onclick="{Buff}" />
|
||||
<label x="446" y="158" w="70" text="{BuffStatus}" color="#FFC7B98F" />
|
||||
<label x="332" y="178" w="184" h="16" text="{Coverage}" color="#FF9B9072" />
|
||||
<!-- Round D item 4: the "bottom-fit rule" the original comment names
|
||||
(194-16) now targets the enlarged group's own bottom edge
|
||||
(229-16), anchor="bottom" so a further manual resize keeps it
|
||||
pinned there. -->
|
||||
<label x="332" y="213" w="184" h="16" text="{Coverage}" color="#FF9B9072" anchor="bottom" />
|
||||
</group>
|
||||
|
||||
<!-- Route: VTank's own 2-across button grid, row order, and bottom
|
||||
|
|
@ -587,31 +667,34 @@
|
|||
real editable field (RoutePauseSecondsFieldText, default "5") plus
|
||||
a "seconds" label, matching VTank's txtPauseWaypointTime/Label52
|
||||
instead of the old "-"/"+" stepper. -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{RouteVisible}">
|
||||
<group x="8" y="42" w="976" h="229" visible="{RouteVisible}" anchor="left top right bottom">
|
||||
<label x="4" y="0" w="370" h="16" text="Navigation Waypoints" color="#FFE8DEC3" />
|
||||
<list x="4" y="16" w="370" h="116" rowheight="17"
|
||||
<!-- Round D item 4: height-only growth (+35) — the Add-button block
|
||||
starts at x=380, only 6px past this list's right edge, so it
|
||||
stays pinned left rather than stretching into it. -->
|
||||
<list x="4" y="16" w="370" h="151" rowheight="17" anchor="left top bottom"
|
||||
selected="{SelectedRouteWaypointIndex}" onchange="{SelectRouteWaypoint}"
|
||||
tooltip="Click a waypoint to remove it.">
|
||||
<column type="text" width="337" items="{RouteWaypointTextColumn}" onclick="{DeleteRouteWaypointAt}" />
|
||||
<column type="text" width="30" items="{RouteWaypointCountColumn}" onclick="{DeleteRouteWaypointAt}" />
|
||||
</list>
|
||||
|
||||
<menu x="4" y="136" w="80" h="16" items="{RouteModeNames}"
|
||||
<menu x="4" y="171" w="80" h="16" items="{RouteModeNames}" anchor="bottom"
|
||||
selected="{SelectedRouteMode}" onchange="{SelectRouteMode}"
|
||||
rows="4" openupward="true" tooltip="Choose Circular, Linear, Follow, or Once route behavior." />
|
||||
<menu x="90" y="136" w="120" h="16" items="{RouteInsertModeNames}"
|
||||
<menu x="90" y="171" w="120" h="16" items="{RouteInsertModeNames}" anchor="bottom"
|
||||
selected="{SelectedRouteInsertMode}" onchange="{SelectRouteInsertMode}"
|
||||
rows="3" openupward="true" tooltip="Where Add buttons insert the new waypoint." />
|
||||
<button x="214" y="136" w="16" h="16" icon="0x060028FD" iconkind="did"
|
||||
<button x="214" y="171" w="16" h="16" icon="0x060028FD" iconkind="did" anchor="bottom"
|
||||
onclick="{MoveRouteWaypointDown}" tooltip="Advance the current point." />
|
||||
<button x="230" y="136" w="16" h="16" icon="0x060028FC" iconkind="did"
|
||||
<button x="230" y="171" w="16" h="16" icon="0x060028FC" iconkind="did" anchor="bottom"
|
||||
onclick="{MoveRouteWaypointUp}" tooltip="Regress the current point." />
|
||||
<!-- VTank's own btnNavResetPoint (icon 0x060011F7, "Select Nearest
|
||||
Point") reassigns the LIVE navigation cursor; MossTank has no
|
||||
mutable cursor to expose to a plugin, so this moves the tab's
|
||||
own edit selection to the closest waypoint instead — see
|
||||
SelectNearestRouteWaypoint's own doc comment. -->
|
||||
<button x="246" y="136" w="16" h="16" icon="0x060011F7" iconkind="did"
|
||||
<button x="246" y="171" w="16" h="16" icon="0x060011F7" iconkind="did" anchor="bottom"
|
||||
onclick="{SelectNearestRouteWaypoint}" tooltip="Select the nearest waypoint." />
|
||||
|
||||
<button x="380" y="16" w="88" h="16" text="Add" onclick="{AddRoutePoint}" />
|
||||
|
|
@ -642,8 +725,8 @@
|
|||
maxlength="128" clearonsubmit="false" background="#E6000000"
|
||||
color="#FFE8DEC3" tooltip="Chat command added by Add Chat." />
|
||||
|
||||
<label x="4" y="160" w="300" h="16" text="{NavigationStatus}" color="#FF9B9072" />
|
||||
<label x="310" y="160" w="300" h="16" text="{RouteNotice}" color="#FF9B9072" />
|
||||
<label x="4" y="195" w="300" h="16" text="{NavigationStatus}" color="#FF9B9072" anchor="bottom" />
|
||||
<label x="310" y="195" w="300" h="16" text="{RouteNotice}" color="#FF9B9072" anchor="bottom" />
|
||||
</group>
|
||||
|
||||
<!-- Meta: ordered VTank state-machine rules. Rules fire once per state
|
||||
|
|
@ -670,8 +753,14 @@
|
|||
isn't representable in the plain retail UI font (0x40000000 has no
|
||||
bold face) — "Current State:" uses the same bright caption color
|
||||
other tab headers do instead. -->
|
||||
<group x="8" y="42" w="848" h="194" visible="{MetaVisible}">
|
||||
<list x="4" y="16" w="840" h="116" rowheight="17"
|
||||
<group x="8" y="42" w="976" h="229" visible="{MetaVisible}" anchor="left top right bottom">
|
||||
<!-- Round D item 4: full stretch (Meta has nothing beside its one
|
||||
full-width list, like Monsters) — widened/heightened by the same
|
||||
128x35 delta the group grows by. The extra width lands in the
|
||||
Condition/Action columns' own "*" auto-share (docs/plugin-ui-
|
||||
markup.md's Columns section), which is the intended fill target,
|
||||
unlike Monsters' icon-column remainder. -->
|
||||
<list x="4" y="16" w="968" h="151" rowheight="17" anchor="left right top bottom"
|
||||
selected="{SelectedMetaRuleIndex}" onchange="{SelectMetaRule}"
|
||||
tooltip="Click State/Condition/Action to edit; delete/move-up/move-down cells act immediately.">
|
||||
<column type="text" width="23" items="{MetaDeleteColumn}" onclick="{DeleteMetaRuleAt}" />
|
||||
|
|
@ -682,14 +771,14 @@
|
|||
<column type="text" width="*" items="{MetaActionColumn}" onclick="{SelectMetaRule}" />
|
||||
</list>
|
||||
|
||||
<label x="4" y="136" w="136" h="16" text="Current State:" color="#FFE8DEC3" />
|
||||
<menu x="140" y="136" w="120" h="16" items="{MetaCurrentStateNames}"
|
||||
<label x="4" y="171" w="136" h="16" text="Current State:" color="#FFE8DEC3" anchor="bottom" />
|
||||
<menu x="140" y="171" w="120" h="16" items="{MetaCurrentStateNames}" anchor="bottom"
|
||||
selected="{SelectedMetaCurrentState}" onchange="{SetMetaCurrentState}"
|
||||
rows="8" openupward="true"
|
||||
tooltip="Force the live Meta engine into a state directly." />
|
||||
<button x="462" y="136" w="50" h="16" text="Create" onclick="{CreateMetaRule}" />
|
||||
<button x="462" y="171" w="50" h="16" text="Create" onclick="{CreateMetaRule}" anchor="bottom" />
|
||||
|
||||
<label x="4" y="178" w="500" h="16" text="{MetaStatus}" color="#FFC7B98F" />
|
||||
<label x="4" y="213" w="500" h="16" text="{MetaStatus}" color="#FFC7B98F" anchor="bottom" />
|
||||
</group>
|
||||
|
||||
</panel>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Gpu;
|
||||
using AcDream.App.Tests.Rendering.Gpu;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.Plugin.Abstractions;
|
||||
using AcDream.Plugins.MossTank;
|
||||
|
|
@ -47,6 +51,73 @@ public sealed class MossTankMarkupBuildOverRealFilesTests
|
|||
Assert.NotEmpty(built.Children);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Round D item 4's own re-layout proof: the Monsters tab's real
|
||||
/// mosstank.xml list carries <c>anchor="left right top bottom"</c>
|
||||
/// (EveryStretchingListDeclaresARealAnchor, MossTankMarkupContractTests,
|
||||
/// pins the attribute is present; this proves the attribute actually
|
||||
/// DOES something through the real anchor machinery) — widening the
|
||||
/// built root panel widens the Monsters list in turn, the same
|
||||
/// mechanism MarkupResizableAnchorTests.ResizingPanel_LeftRightList_
|
||||
/// WidensWithThePanel proves against synthetic markup, now proven
|
||||
/// against the real shipped file.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WideningTheRealMainPanelWidensTheRealMonstersList()
|
||||
{
|
||||
string xml = File.ReadAllText(
|
||||
Path.Combine(MossTankMarkupDirectory, "mosstank.xml"));
|
||||
var panel = new MossTankPanel(new StubHost());
|
||||
|
||||
UiNineSlicePanel built = MarkupDocument.Build(xml, panel, static id => (id, 32, 32));
|
||||
|
||||
// Every tab's group is visibility-bound ("visible={XVisible}") to
|
||||
// StubHost's own IsAvailable=false automation, so relying on the
|
||||
// normal VisibleSource/TickSelfAndChildren reconciliation would
|
||||
// hide every tab (including the root). This test cares about the
|
||||
// anchor mechanism, not the tab-switching one — it makes the
|
||||
// Monsters group (the 4th of the nine tab groups in file order:
|
||||
// Options/Profiles/Vitals/Monsters/...) visible directly. Exact
|
||||
// type match, not OfType<UiPanel>() — UiSimpleButton/
|
||||
// UiMarkupTabButton (the tab strip) are ALSO UiPanel subtypes;
|
||||
// only a bare <group> compiles to the base UiPanel type itself.
|
||||
UiPanel[] tabGroups = built.Children
|
||||
.Where(static child => child.GetType() == typeof(UiPanel))
|
||||
.Cast<UiPanel>()
|
||||
.ToArray();
|
||||
Assert.Equal(9, tabGroups.Length);
|
||||
foreach (UiPanel group in tabGroups)
|
||||
group.Visible = false;
|
||||
UiPanel monstersGroup = tabGroups[3];
|
||||
monstersGroup.Visible = true;
|
||||
UiMarkupList monstersList = Assert.Single(monstersGroup.Children.OfType<UiMarkupList>());
|
||||
|
||||
var device = new RecordingGpuDevice();
|
||||
var renderer = new TextRenderer(device, new NullGpuFrameSource(), "unused");
|
||||
renderer.Begin(new Vector2(1400f, 900f));
|
||||
var ctx = new UiRenderContext(renderer, new Vector2(1400f, 900f));
|
||||
|
||||
// First draw at the authored (already-enlarged, 984 wide) default
|
||||
// captures the list's anchor baseline.
|
||||
built.DrawSelfAndChildren(ctx);
|
||||
float widthAtAuthoredDefault = monstersList.Width;
|
||||
|
||||
// A live drag-resize (RetailWindowManager.ResizeTo) mutates Width
|
||||
// directly; the next draw re-applies the captured margins against
|
||||
// the NEW panel width.
|
||||
built.Width += 100f;
|
||||
built.DrawSelfAndChildren(ctx);
|
||||
|
||||
Assert.True(
|
||||
monstersList.Width > widthAtAuthoredDefault,
|
||||
$"Monsters list width did not grow: {widthAtAuthoredDefault} -> {monstersList.Width}");
|
||||
}
|
||||
|
||||
private sealed class NullGpuFrameSource : ICurrentGpuFrameSource
|
||||
{
|
||||
public IGpuFrame? CurrentFrame => null;
|
||||
}
|
||||
|
||||
private sealed class StubHost : IPluginHost
|
||||
{
|
||||
public bool HasUi => false;
|
||||
|
|
|
|||
|
|
@ -259,20 +259,81 @@ public sealed class MossTankMarkupContractTests
|
|||
Path.Combine(AppContext.BaseDirectory, "mosstank.xml"));
|
||||
XElement root = Assert.IsType<XElement>(document.Root);
|
||||
|
||||
// Campaign VT slice 7 S7.1: 856 wide, matching VTank's own
|
||||
// mainView.xml content (856x210, docs/research/vtank-kb/
|
||||
// 08-ui-views.md §0). Fix round A (2026-09-07) moved the Advanced
|
||||
// Options / Loot Editor popups OUT into their own plugin panels
|
||||
// (mosstank-advanced.xml / mosstank-loot-editor.xml), so the main
|
||||
// window is back to VTank's own proportion: 236 tall (42px header +
|
||||
// tab strip, unchanged since S7.1, + 194px per-tab content,
|
||||
// unchanged) with no dead space below the tabs — see mosstank.xml's
|
||||
// own panel-level comment.
|
||||
Assert.Equal(856f, Number(root, "w"));
|
||||
Assert.Equal(236f, Number(root, "h"));
|
||||
// Round D item 4 (owner: "The size of the entire window needs to
|
||||
// be enlarged for default and should also be resizeable"): the
|
||||
// authored default grew ~15% in both directions from the S7.1/
|
||||
// fix-round-A proportion (856x236, still the panel's own
|
||||
// minw/minh floor — see PanelIsResizableFlooredAtThePreRoundDAuthoredSize
|
||||
// below) to 984x271. See mosstank.xml's own Round D item 4
|
||||
// panel-level comment for the exact delta math (128 wide, 35
|
||||
// tall) every tab group and stretching list shares.
|
||||
Assert.Equal(984f, Number(root, "w"));
|
||||
Assert.Equal(271f, Number(root, "h"));
|
||||
AssertWithinParent(root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Round D item 4: the panel itself is resizable, floored at the
|
||||
/// PRE-round-D authored size (856x236) — a drag-resize or a
|
||||
/// persisted-layout restore must never shrink it past what every tab
|
||||
/// was actually authored/tested at.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void PanelIsResizableFlooredAtThePreRoundDAuthoredSize()
|
||||
{
|
||||
XDocument document = XDocument.Load(
|
||||
Path.Combine(AppContext.BaseDirectory, "mosstank.xml"));
|
||||
XElement root = Assert.IsType<XElement>(document.Root);
|
||||
|
||||
Assert.Equal("true", (string?)root.Attribute("resizable"));
|
||||
Assert.Equal(856f, Number(root, "minw"));
|
||||
Assert.Equal(236f, Number(root, "minh"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Round D item 4's own contract pin: every list this round calls out
|
||||
/// as "should grow" (Monsters, Items, Consumables x2, Buffs x2, Route,
|
||||
/// Meta) declares a real anchor — not the silent "left top" default,
|
||||
/// which would leave it a fixed size forever regardless of how far the
|
||||
/// user drags the window. Doesn't prescribe WHICH edges (Monsters/Meta
|
||||
/// stretch both axes; the others grow height-only, pinned to whichever
|
||||
/// horizontal edge a sibling list/button column doesn't block — see
|
||||
/// mosstank.xml's own Round D item 4 comment for why per list).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EveryStretchingListDeclaresARealAnchor()
|
||||
{
|
||||
(string GroupVisible, int ExpectedListCount)[] stretchingListGroups =
|
||||
[
|
||||
("MonstersVisible", 1),
|
||||
("ItemsVisible", 1),
|
||||
("ConsumablesVisible", 2),
|
||||
("BuffsVisible", 2),
|
||||
("RouteVisible", 1),
|
||||
("MetaVisible", 1),
|
||||
];
|
||||
|
||||
XDocument document = XDocument.Load(
|
||||
Path.Combine(AppContext.BaseDirectory, "mosstank.xml"));
|
||||
XElement root = Assert.IsType<XElement>(document.Root);
|
||||
|
||||
foreach ((string groupVisible, int expectedListCount) in stretchingListGroups)
|
||||
{
|
||||
XElement group = root.Elements("group")
|
||||
.Single(g => (string?)g.Attribute("visible") == $"{{{groupVisible}}}");
|
||||
XElement[] lists = group.Elements("list").ToArray();
|
||||
Assert.Equal(expectedListCount, lists.Length);
|
||||
foreach (XElement list in lists)
|
||||
{
|
||||
string? anchor = (string?)list.Attribute("anchor");
|
||||
Assert.False(
|
||||
string.IsNullOrEmpty(anchor),
|
||||
$"A <list> in the {groupVisible} tab has no anchor attribute — "
|
||||
+ "it will never grow with the window.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fix round C item D3: direct pin on <see cref="AssertWithinParent"/>'s
|
||||
/// height check, independent of any real markup file. A
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue