fix: social gate round 3 - authored multiline word wrap + geometric
move-cursor border band - Empty-state text (round 3): the literal-\n split was correct but each authored LINE rendered as one clipped run. Retail word-wraps each authored line within the element extent (its GlyphList draw - the same wrap RetailConfirmationDialogView already uses). Multiline authored text now wraps through UiText.WrapWords against the widget's LIVE width/font/color (cached per width+font+color, re-read per call). Single-line authored labels keep their one-run shape - re-wrapping every label is a client-wide change no gate asked for. - Move cursor (round 3): "the frame won the hit-test" is not a border test - windows whose interior is not fully covered by children (the inventory panel's empty regions) resolve those pixels to the frame too. The border is now a geometric 8 px band along the window's outer edge, AND the frame must win the hit-test so border-adjacent content keeps its own cursor. Resize-edge claim still takes precedence; whole-surface dragging unchanged. App suite 4,984/3 skips (new BuildText_MultilineAuthored wrap test). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
67fe754dd6
commit
4943484eb9
3 changed files with 113 additions and 20 deletions
|
|
@ -705,21 +705,53 @@ public static class DatWidgetFactory
|
|||
{
|
||||
// 2026-08-13 social gate: authored strings can carry embedded
|
||||
// newlines (the fellowship empty-state is three sentences over
|
||||
// '\n's). A single Line renders them as one clipped run — split
|
||||
// into one Line per authored line, exactly as retail's multiline
|
||||
// UIElement_Text draws them. Gate round 2: the DAT stores the
|
||||
// LITERAL two-character escape "\n" (0x5C 0x6E — probe-verified:
|
||||
// the dump printed backslash-n, not a line break), so normalize
|
||||
// the escape before splitting. The provider re-reads DefaultColor
|
||||
// per call (NOT captured eagerly) so state-driven font-color
|
||||
// changes keep tracking, the same live-color contract the
|
||||
// single-line provider always had.
|
||||
string[] parts = [.. authored
|
||||
// '\n's). Gate round 2: the DAT stores the LITERAL two-character
|
||||
// escape "\n" (0x5C 0x6E — probe-verified: the dump printed
|
||||
// backslash-n, not a line break), so normalize the escape first.
|
||||
// Gate round 3: retail additionally WORD-WRAPS each authored line
|
||||
// within the element extent (its GlyphList draw — the same wrap
|
||||
// the confirmation dialog view already uses), so a multiline
|
||||
// authored block re-wraps to the widget's live width instead of
|
||||
// clipping at the edge. Single-line authored labels keep the
|
||||
// one-run shape they have always had (they are authored to fit;
|
||||
// re-wrapping them is a client-wide behavior change no gate has
|
||||
// asked for). Providers re-read DefaultColor/width/font per call
|
||||
// (NOT captured eagerly) so state-driven changes keep tracking.
|
||||
string normalized = authored
|
||||
.Replace("\\n", "\n")
|
||||
.Split('\n')
|
||||
.Select(static p => p.TrimEnd('\r'))];
|
||||
t.LinesProvider = () =>
|
||||
[.. parts.Select(p => new UiText.Line(p, t.DefaultColor))];
|
||||
.Replace("\r", string.Empty);
|
||||
if (normalized.Contains('\n'))
|
||||
{
|
||||
float cachedWidth = float.NaN;
|
||||
UiDatFont? cachedFont = null;
|
||||
System.Numerics.Vector4 cachedColor = default;
|
||||
UiText.Line[]? cachedLines = null;
|
||||
t.LinesProvider = () =>
|
||||
{
|
||||
if (cachedLines is null
|
||||
|| cachedWidth != t.Width
|
||||
|| !ReferenceEquals(cachedFont, t.DatFont)
|
||||
|| cachedColor != t.DefaultColor)
|
||||
{
|
||||
cachedWidth = t.Width;
|
||||
cachedFont = t.DatFont;
|
||||
cachedColor = t.DefaultColor;
|
||||
float maximumWidth = Math.Max(1f, t.Width - 2f * t.Padding);
|
||||
Func<string, float> measure = t.DatFont is { } font
|
||||
? font.MeasureWidth
|
||||
: static value => value.Length * 8f;
|
||||
cachedLines = [.. UiText
|
||||
.WrapWords(normalized, measure, maximumWidth)
|
||||
.Select(line => new UiText.Line(line, t.DefaultColor))];
|
||||
}
|
||||
return cachedLines;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
t.LinesProvider = () =>
|
||||
[new UiText.Line(normalized, t.DefaultColor)];
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue