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
|
|
@ -149,14 +149,36 @@ public sealed class UiRoot : UiElement
|
|||
if (window is not { Draggable: true })
|
||||
return false;
|
||||
// 2026-08-13 gate (user-directed, all windows): the move cursor
|
||||
// advertises ONLY on the window's own chrome — the frame's border
|
||||
// pixels are the only place the frame element itself wins the
|
||||
// hit-test (interior points resolve to content children), which
|
||||
// matches retail's Dragbar-chrome-only move cursor. Whole-surface
|
||||
// dragging still WORKS; it just no longer advertises over content.
|
||||
return ReferenceEquals(target, window);
|
||||
// advertises ONLY on the window's own border chrome. Round 3
|
||||
// correction: "the frame element won the hit-test" is NOT a
|
||||
// border test — a window whose interior is not fully covered by
|
||||
// content children (the inventory panel's empty regions) resolves
|
||||
// those interior pixels to the frame too. The border is a
|
||||
// GEOMETRIC band along the window's outer edge; the frame must
|
||||
// still be the hit-test winner so border-adjacent content keeps
|
||||
// its own cursor. Whole-surface dragging still WORKS; it just no
|
||||
// longer advertises over content or empty interior.
|
||||
return ReferenceEquals(target, window)
|
||||
&& WithinBorderBand(window, MouseX, MouseY, MoveBorderBand);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The point lies inside the window and within <paramref name="band"/>
|
||||
/// pixels of one of its outer edges — the frame chrome the move cursor
|
||||
/// advertises on. Unlike <see cref="HitEdges"/>, no resize-axis masking:
|
||||
/// a non-resizable window's border still moves it.</summary>
|
||||
private static bool WithinBorderBand(UiElement w, int x, int y, int band)
|
||||
{
|
||||
float l = w.Left, t = w.Top, r = w.Left + w.Width, b = w.Top + w.Height;
|
||||
if (x < l || x >= r || y < t || y >= b)
|
||||
return false;
|
||||
return x - l < band || r - x <= band || y - t < band || b - y <= band;
|
||||
}
|
||||
|
||||
/// <summary>Move-cursor border thickness in px — sized to the retail frame
|
||||
/// chrome art so the ring stays visible inside the <see cref="ResizeGrip"/>
|
||||
/// claim on resizable edges.</summary>
|
||||
private const int MoveBorderBand = 8;
|
||||
private (uint tex, int w, int h)? _dragGhost;
|
||||
/// <summary>Snapshotted drag-ghost (tex,w,h), exposed for tests. See BeginDrag.</summary>
|
||||
internal (uint tex, int w, int h)? DragGhostForTest => _dragGhost;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue