merge(vt): plain <menu> style from latest-main-sync into slice7 panel work
Brings cfa703065's owner-driven fix (plugin <menu> renders VTank's plain flat combo by default; style="retail" opts back into the gold pushbutton art) into the S7.1-S7.3 window/tabs work so the fix round below can build on both. Resolved conflicts: the ledger keeps both worktrees' entries in chronological order (07:55/08:05 owner-read entries appended after this worktree's own S7.1/S7.2/S7.3 entries); plugin-ui-markup.md's <menu> row lists both new attributes (scroll, style); MarkupDocumentTests.cs keeps both new test blocks (slider min/max + menu scroll from this worktree, menu style from latest-main-sync). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
045cd0a195
17 changed files with 385 additions and 1 deletions
|
|
@ -481,6 +481,7 @@ public static class MarkupDocument
|
|||
Func<string?> menuSelected = BindString(
|
||||
(string?)el.Attribute("selected"),
|
||||
binding);
|
||||
bool menuRetailButtonArt = ValidateMenuStyle((string?)el.Attribute("style"));
|
||||
var menu = new UiMenu
|
||||
{
|
||||
Left = F(el, "x"),
|
||||
|
|
@ -515,6 +516,7 @@ public static class MarkupDocument
|
|||
PopupBgSprite = 0x0600124Cu,
|
||||
ItemNormalSprite = 0x0600124Eu,
|
||||
ItemHighlightSprite = 0x0600124Du,
|
||||
RetailButtonArt = menuRetailButtonArt,
|
||||
ButtonLabelProvider = () => menuSelected() ?? string.Empty,
|
||||
OnSelect = payload =>
|
||||
{
|
||||
|
|
@ -668,6 +670,25 @@ public static class MarkupDocument
|
|||
$"{context} must be did, spell, or item (got \"{other}\")"),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Owner live-client report 2026-09-07 ("Those BIG gold/yellow buttons HAS
|
||||
/// to go. That is not how vtank looks."): validates <c><menu
|
||||
/// style="..."></c> and returns the <see cref="UiMenu.RetailButtonArt"/>
|
||||
/// value it selects. Default (attribute absent, or explicit
|
||||
/// <c>style="plain"</c>) is the flat VTank/Decal <c>HudCombo</c> box
|
||||
/// (<c>false</c>) — retail's gold pushbutton art is now an explicit
|
||||
/// <c>style="retail"</c> opt-in for a plugin panel that genuinely wants
|
||||
/// it. Any other value is a Build-time author error, same rule as
|
||||
/// <see cref="ValidateIconKind"/>.
|
||||
/// </summary>
|
||||
private static bool ValidateMenuStyle(string? style) => style switch
|
||||
{
|
||||
null or "plain" => false,
|
||||
"retail" => true,
|
||||
var other => throw new FormatException(
|
||||
$"<menu style=\"{other}\"> must be plain or retail"),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Builds the zero-argument icon resolver the <c><icon></c> element
|
||||
/// uses: dispatch by <c>iconkind</c> (default <c>"did"</c>) to the
|
||||
|
|
|
|||
|
|
@ -304,6 +304,41 @@ public sealed class UiMenu : UiElement
|
|||
/// StateDesc (not a code symbol); ~0.5 neutral grey here pending a live cdb dump.</summary>
|
||||
public Vector4 TextColorGhosted { get; set; } = new(0.5f, 0.5f, 0.5f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Owner live-client report 2026-09-07 ("Those BIG gold/yellow buttons HAS
|
||||
/// to go. That is not how vtank looks."): whether the CLOSED-state button
|
||||
/// face draws retail's gold pushbutton art (<see cref="NormalSprite"/>/
|
||||
/// <see cref="PressedSprite"/> + the arrow-cap overlay) or the plain flat
|
||||
/// box below (<see cref="DrawPlainClosedState"/>) that reads as the same
|
||||
/// widget family as <see cref="UiMarkupList"/>'s dark list boxes — the
|
||||
/// VTank/Decal <c>HudCombo</c> shape (<c>docs/research/vtank-kb/08-ui-views.md</c>
|
||||
/// §2: a flat box, left-aligned current value, small down-arrow at the
|
||||
/// right edge). Default TRUE so every existing non-plugin <see cref="UiMenu"/>
|
||||
/// user (chat's channel menu, vendor's category dropdown, the Config
|
||||
/// option menus, the retail confirmation-dialog menu, and every generic
|
||||
/// dat Type-6 element built by <c>DatWidgetFactory</c>) keeps its exact
|
||||
/// retail look untouched; <see cref="MarkupDocument"/> flips this to
|
||||
/// <c>false</c> for plugin <c><menu></c> markup by default, with
|
||||
/// <c>style="retail"</c> as the opt-out back to this art.
|
||||
/// </summary>
|
||||
public bool RetailButtonArt { get; set; } = true;
|
||||
|
||||
// ── Plain closed-state chrome (RetailButtonArt = false). Colors mirror
|
||||
// UiMarkupList's own list chrome (BackgroundColor/BorderColor/TextColor)
|
||||
// so a plugin's dropdown reads as the same widget family as its lists.
|
||||
public Vector4 PlainBackgroundColor { get; set; } = new(0f, 0f, 0f, 0.92f);
|
||||
public Vector4 PlainBorderColor { get; set; } = new(0.46f, 0.37f, 0.16f, 1f);
|
||||
/// <summary>Border tint while the popup is open or the face is physically
|
||||
/// pressed — the only visual change those states make in plain mode (no
|
||||
/// gold art swap, ever).</summary>
|
||||
public Vector4 PlainOpenBorderColor { get; set; } = new(0.70f, 0.58f, 0.24f, 1f);
|
||||
public Vector4 PlainTextColor { get; set; } = new(0.91f, 0.87f, 0.76f, 1f);
|
||||
public Vector4 PlainTriangleColor { get; set; } = new(0.91f, 0.87f, 0.76f, 1f);
|
||||
/// <summary>Left-inset of the plain box's value text — matches
|
||||
/// <see cref="UiMarkupList.Padding"/>'s default so a combo's text lines up
|
||||
/// with the list rows beneath it.</summary>
|
||||
public const float PlainPadding = 3f;
|
||||
|
||||
private bool _open;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -457,6 +492,12 @@ public sealed class UiMenu : UiElement
|
|||
|
||||
protected override void OnDraw(UiRenderContext ctx)
|
||||
{
|
||||
if (!RetailButtonArt)
|
||||
{
|
||||
DrawPlainClosedState(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
var resolve = SpriteResolve;
|
||||
|
||||
// Button face (3-sliced so it can widen to fit the label) + the active-target label.
|
||||
|
|
@ -486,6 +527,52 @@ public sealed class UiMenu : UiElement
|
|||
if (resolve is not null) DrawArrowCap(ctx, resolve);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The VTank/Decal <c>HudCombo</c> closed-state shape (see
|
||||
/// <see cref="RetailButtonArt"/>'s doc comment): flat fill, 1px border,
|
||||
/// left-aligned value text at <see cref="PlainPadding"/>, and a small ▾
|
||||
/// triangle right-aligned — no sprite or DAT quad at all, drawn entirely
|
||||
/// with <see cref="UiRenderContext.DrawFill"/>/<see cref="UiRenderContext.DrawRectOutline"/>
|
||||
/// (the same untextured sprite-bucket primitives <see cref="UiMarkupList"/>
|
||||
/// already uses for its own chrome). Open/pressed only tints the border —
|
||||
/// never a sprite swap.
|
||||
/// </summary>
|
||||
private void DrawPlainClosedState(UiRenderContext ctx)
|
||||
{
|
||||
ctx.DrawFill(0f, 0f, Width, Height, PlainBackgroundColor);
|
||||
Vector4 border = (_open || _facePressed) ? PlainOpenBorderColor : PlainBorderColor;
|
||||
ctx.DrawRectOutline(0f, 0f, Width, Height, border, 1f);
|
||||
|
||||
string caption = ButtonLabelProvider?.Invoke() ?? "";
|
||||
UiDatFont? captionFont = ButtonDatFont ?? DatFont;
|
||||
float captionLineH = captionFont?.LineHeight ?? Font?.LineHeight ?? 14f;
|
||||
float textY = (Height - captionLineH) * 0.5f;
|
||||
if (captionFont is { } cf)
|
||||
ctx.DrawStringDat(cf, caption, PlainPadding, textY, PlainTextColor, Outline, OutlineColor);
|
||||
else
|
||||
ctx.DrawString(caption, PlainPadding, textY, PlainTextColor, Font);
|
||||
|
||||
DrawPlainTriangle(ctx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A 7px-wide, 4px-tall ▾ glyph built from four stacked
|
||||
/// <see cref="UiRenderContext.DrawFill"/> bands — the same "no DAT art,
|
||||
/// just fills" technique <see cref="UiCheckLamp.Draw"/> uses for its lamp
|
||||
/// glyph. Right-aligned with a small margin so it never crowds the box's
|
||||
/// own border.
|
||||
/// </summary>
|
||||
private void DrawPlainTriangle(UiRenderContext ctx)
|
||||
{
|
||||
const float w = 7f, rightMargin = 6f;
|
||||
float x = Width - rightMargin - w;
|
||||
float y = (Height - 4f) * 0.5f;
|
||||
ctx.DrawFill(x, y, w, 1f, PlainTriangleColor);
|
||||
ctx.DrawFill(x + 1f, y + 1f, w - 2f, 1f, PlainTriangleColor);
|
||||
ctx.DrawFill(x + 2f, y + 2f, w - 4f, 1f, PlainTriangleColor);
|
||||
ctx.DrawFill(x + 3f, y + 3f, w - 6f, 1f, PlainTriangleColor);
|
||||
}
|
||||
|
||||
// 3-slice caps for the 46px LED-arrow button face (0x06004D65): a LEFT cap holding the
|
||||
// round LED socket, a stretchable plain-gold MIDDLE, and a RIGHT cap holding the arrow
|
||||
// point. Slicing keeps the LED + arrow undistorted when the button widens to its label.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue