feat(ui): importer carries dat justification (0x14/0x15) onto text widgets; drop character footer-title height hack
LayoutImporter.ReadState now reads Properties[0x14] (HorizontalJustification, EnumBaseProperty: 0=Left, 1=Center, 3/5=Right) and Properties[0x15] (VerticalJustification: 2=Top, 4=Bottom; else Center) into two new ElementInfo fields HJustify/VJustify. Merge propagates them with the same non-default-wins rule used for FontDid. DatWidgetFactory.BuildText applies the resolved justify at build time: HJustify=Center sets Centered=true, HJustify=Right sets RightAligned=true, VJustify=Top/Bottom sets VerticalJustify. Controllers that FindElement and set those properties afterward continue to override - backward-compat preserved. UiText gains VerticalJustify (Top/Center/Bottom, default Center). The Centered and RightAligned single-line paths call UiText.VOffset() for the Y coordinate, so VJustify.Top renders text at y=Padding rather than the fixed (H-lh)/2 center. CharacterStatController: footer title (0x1000024E, H=55 dat box) previously used Height=18 + Anchors=None to prevent center-vertical overlap with line-1/2. Diagnostic confirmed dat says HJustify=Center, VJustify=Center. The hack is replaced with one minimal explicit override: VerticalJustify=Top on the already-Centered element. Text now renders at top of the 55px box natively. Centered=false and RightAligned=false hand-sets removed where dat supplies them. Dat justify values (studio diagnostic, 2026-06-26): 0x1000024E footer title: HJustify=Center, VJustify=Center 0x10000235/0x10000243/0x10000245 value fields: HJustify=Right header name/heritage/pk/level: HJustify=Center +13 tests: ElementReader Merge propagation; DatWidgetFactory BuildText justify application + controller-override backward-compat; UiText VOffset. 696 passed, 0 failed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8f222f7506
commit
41430420b3
8 changed files with 328 additions and 18 deletions
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Numerics;
|
||||
using System.Text;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.UI.Layout;
|
||||
|
||||
namespace AcDream.App.UI;
|
||||
|
||||
|
|
@ -78,6 +79,23 @@ public sealed class UiText : UiElement
|
|||
/// precedence. Pair with <c>ClickThrough = true</c> for non-interactive labels.</summary>
|
||||
public bool RightAligned { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vertical position of the text within the element rect in single-line mode
|
||||
/// (<see cref="Centered"/> or <see cref="RightAligned"/>).
|
||||
/// <list type="bullet">
|
||||
/// <item><description><b>Center</b> (default) — vertically centered, matching the original
|
||||
/// behavior of the centered/right-aligned paths.</description></item>
|
||||
/// <item><description><b>Top</b> — text is placed at <c>y = Padding</c> (top of the content
|
||||
/// area), so the text sits at the top of the element rather than centering in it.
|
||||
/// Used for footer title elements whose dat box is the full footer height (55 px) but
|
||||
/// the text should render near the top.</description></item>
|
||||
/// <item><description><b>Bottom</b> — text is placed at <c>y = Height - lineHeight - Padding</c>.</description></item>
|
||||
/// </list>
|
||||
/// Only meaningful when <see cref="Centered"/> or <see cref="RightAligned"/> is true.
|
||||
/// Has no effect on the scrollable multi-line path.
|
||||
/// </summary>
|
||||
public VJustify VerticalJustify { get; set; } = VJustify.Center;
|
||||
|
||||
/// <summary>The scroll model — also read by the linked UiScrollbar.</summary>
|
||||
public UiScrollable Scroll { get; } = new();
|
||||
|
||||
|
|
@ -138,8 +156,8 @@ public sealed class UiText : UiElement
|
|||
ctx.DrawFill(0, 0, Width, Height, BackgroundColor);
|
||||
|
||||
// Static centered single-line mode (vitals cur/max numbers etc.): draw the first
|
||||
// line centered H+V with the SAME formula UIElement_Meter used for its label, then
|
||||
// skip the scroll/selection machinery entirely.
|
||||
// line centered H+V (or H+Top/Bottom per VerticalJustify) with the SAME formula
|
||||
// UIElement_Meter used for its label, then skip the scroll/selection machinery entirely.
|
||||
if (Centered)
|
||||
{
|
||||
var cLines = LinesProvider();
|
||||
|
|
@ -148,20 +166,20 @@ public sealed class UiText : UiElement
|
|||
if (DatFont is { } cdf)
|
||||
{
|
||||
float cx = (Width - cdf.MeasureWidth(line0.Text)) * 0.5f;
|
||||
float cy = (Height - cdf.LineHeight) * 0.5f;
|
||||
float cy = VOffset(Height, cdf.LineHeight, Padding, VerticalJustify);
|
||||
ctx.DrawStringDat(cdf, line0.Text, cx, cy, line0.Color);
|
||||
}
|
||||
else if ((Font ?? ctx.DefaultFont) is { } cbf)
|
||||
{
|
||||
float cx = (Width - cbf.MeasureWidth(line0.Text)) * 0.5f;
|
||||
float cy = (Height - cbf.LineHeight) * 0.5f;
|
||||
float cy = VOffset(Height, cbf.LineHeight, Padding, VerticalJustify);
|
||||
ctx.DrawString(line0.Text, cx, cy, line0.Color, cbf);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Static right-aligned single-line mode: draw the first line flush with the right
|
||||
// edge, vertically centered, then skip the scroll/selection machinery.
|
||||
// edge, vertical position per VerticalJustify, then skip the scroll/selection machinery.
|
||||
if (RightAligned)
|
||||
{
|
||||
var rLines = LinesProvider();
|
||||
|
|
@ -170,13 +188,13 @@ public sealed class UiText : UiElement
|
|||
if (DatFont is { } rdf)
|
||||
{
|
||||
float rx = Width - rdf.MeasureWidth(line0.Text) - Padding;
|
||||
float ry = (Height - rdf.LineHeight) * 0.5f;
|
||||
float ry = VOffset(Height, rdf.LineHeight, Padding, VerticalJustify);
|
||||
ctx.DrawStringDat(rdf, line0.Text, rx, ry, line0.Color);
|
||||
}
|
||||
else if ((Font ?? ctx.DefaultFont) is { } rbf)
|
||||
{
|
||||
float rx = Width - rbf.MeasureWidth(line0.Text) - Padding;
|
||||
float ry = (Height - rbf.LineHeight) * 0.5f;
|
||||
float ry = VOffset(Height, rbf.LineHeight, Padding, VerticalJustify);
|
||||
ctx.DrawString(line0.Text, rx, ry, line0.Color, rbf);
|
||||
}
|
||||
return;
|
||||
|
|
@ -366,6 +384,23 @@ public sealed class UiText : UiElement
|
|||
|
||||
// ── Pure, testable logic (no GL / no font texture) ───────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Compute the Y offset (local space) for a single line in the Centered/RightAligned
|
||||
/// single-line path, given the element height, font line-height, padding, and
|
||||
/// vertical justification.
|
||||
/// </summary>
|
||||
/// <param name="height">Element height in pixels.</param>
|
||||
/// <param name="lineHeight">Font line height in pixels.</param>
|
||||
/// <param name="padding">Content padding.</param>
|
||||
/// <param name="vj">Vertical justification.</param>
|
||||
public static float VOffset(float height, float lineHeight, float padding, VJustify vj)
|
||||
=> vj switch
|
||||
{
|
||||
VJustify.Top => padding,
|
||||
VJustify.Bottom => height - lineHeight - padding,
|
||||
_ => (height - lineHeight) * 0.5f, // Center (default)
|
||||
};
|
||||
|
||||
/// <summary>Order two caret positions so the first is <= the second (by line,
|
||||
/// then column).</summary>
|
||||
public static (Pos start, Pos end) Order(Pos a, Pos b)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue