feat(ui): port retained widget foundations
This commit is contained in:
parent
44f9ec13d9
commit
d825572e31
44 changed files with 84813 additions and 292 deletions
|
|
@ -15,10 +15,9 @@ namespace AcDream.App.UI;
|
|||
/// text inside the window.
|
||||
///
|
||||
/// <para>
|
||||
/// Supports Windows-like text selection: a left-click-drag inside the transcript
|
||||
/// selects characters (the <see cref="UiElement.CapturesPointerDrag"/> opt-out
|
||||
/// stops that interior drag from moving the host window), and Ctrl+C copies the
|
||||
/// selected span to the clipboard. Ctrl+A selects everything.
|
||||
/// When retail Selectable property `0x27` is enabled, left-click-drag selects
|
||||
/// characters, Ctrl+C copies the selected span, and Ctrl+A selects everything.
|
||||
/// Display-only text remains click-through and cannot steal focus or window drag.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class UiText : UiElement
|
||||
|
|
@ -80,6 +79,37 @@ public sealed class UiText : UiElement
|
|||
/// <summary>Inner text inset from the view edges, px.</summary>
|
||||
public float Padding { get; set; } = 4f;
|
||||
|
||||
/// <summary>Retail property 0x20. Independent of horizontal/vertical
|
||||
/// justification; false permits the normal multi-line layout path.</summary>
|
||||
public bool OneLine { get; set; }
|
||||
|
||||
private bool _selectable;
|
||||
|
||||
/// <summary>
|
||||
/// Retail property 0x27. A display-only text element does not claim the mouse,
|
||||
/// focus, or pointer drag. Selection and clipboard behavior are enabled only
|
||||
/// when this capability is true.
|
||||
/// </summary>
|
||||
public bool Selectable
|
||||
{
|
||||
get => _selectable;
|
||||
set
|
||||
{
|
||||
if (_selectable == value) return;
|
||||
_selectable = value;
|
||||
ClickThrough = !value;
|
||||
AcceptsFocus = value;
|
||||
IsEditControl = value;
|
||||
CapturesPointerDrag = value;
|
||||
if (!value)
|
||||
{
|
||||
_selecting = false;
|
||||
_selAnchor = null;
|
||||
_selCaret = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Static centered single-line mode (retail <c>UIElement_Text</c> center
|
||||
/// justification): draws the FIRST line centered horizontally AND vertically in the
|
||||
/// element rect, with NO scroll/selection machinery. Used for static labels such as
|
||||
|
|
@ -135,9 +165,11 @@ public sealed class UiText : UiElement
|
|||
|
||||
public UiText()
|
||||
{
|
||||
AcceptsFocus = true;
|
||||
IsEditControl = true; // absorb keys (Ctrl+C) while focused
|
||||
CapturesPointerDrag = true; // interior drag selects, doesn't move the window
|
||||
// UIElement_Text starts display-only (m_bitField = DIRTY|CURSOR_VISIBLE).
|
||||
ClickThrough = true;
|
||||
AcceptsFocus = false;
|
||||
IsEditControl = false;
|
||||
CapturesPointerDrag = false;
|
||||
}
|
||||
|
||||
/// <summary>The text view draws its own lines + background; any dat sub-elements
|
||||
|
|
@ -174,7 +206,7 @@ public sealed class UiText : UiElement
|
|||
// Static centered single-line mode (vitals cur/max numbers etc.): draw the first
|
||||
// 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)
|
||||
if (OneLine && Centered)
|
||||
{
|
||||
var cLines = LinesProvider();
|
||||
if (cLines.Count == 0) return;
|
||||
|
|
@ -196,7 +228,7 @@ public sealed class UiText : UiElement
|
|||
|
||||
// Static right-aligned single-line mode: draw the first line flush with the right
|
||||
// edge, vertical position per VerticalJustify, then skip the scroll/selection machinery.
|
||||
if (RightAligned)
|
||||
if (OneLine && RightAligned)
|
||||
{
|
||||
var rLines = LinesProvider();
|
||||
if (rLines.Count == 0) return;
|
||||
|
|
@ -216,6 +248,26 @@ public sealed class UiText : UiElement
|
|||
return;
|
||||
}
|
||||
|
||||
// One-line is an independent text-layout property. Left justification uses
|
||||
// the same vertical policy without turning alignment into a line-count flag.
|
||||
if (OneLine)
|
||||
{
|
||||
var singleLines = LinesProvider();
|
||||
if (singleLines.Count == 0) return;
|
||||
var line0 = singleLines[0];
|
||||
if (DatFont is { } datSingle)
|
||||
{
|
||||
float y = VOffset(Height, datSingle.LineHeight, Padding, VerticalJustify);
|
||||
ctx.DrawStringDat(datSingle, line0.Text, Padding, y, line0.Color);
|
||||
}
|
||||
else if ((Font ?? ctx.DefaultFont) is { } bitmapSingle)
|
||||
{
|
||||
float y = VOffset(Height, bitmapSingle.LineHeight, Padding, VerticalJustify);
|
||||
ctx.DrawString(line0.Text, Padding, y, line0.Color, bitmapSingle);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Prefer the retail dat font when set; fall back to BitmapFont.
|
||||
var datFont = DatFont;
|
||||
var bitmapFont = datFont is null ? (Font ?? ctx.DefaultFont) : null;
|
||||
|
|
@ -301,6 +353,7 @@ public sealed class UiText : UiElement
|
|||
{
|
||||
case UiEventType.Scroll:
|
||||
{
|
||||
if (!Selectable) return false;
|
||||
// Silk wheel +Y = scroll up = reveal older = toward the TOP = decrease ScrollY.
|
||||
// ScrollByLines sign: +down/newer, -up/older.
|
||||
// e.Data0 > 0 → wheel up → want older → ScrollByLines with negative lines.
|
||||
|
|
@ -311,6 +364,7 @@ public sealed class UiText : UiElement
|
|||
|
||||
case UiEventType.MouseDown:
|
||||
{
|
||||
if (!Selectable) return false;
|
||||
// Data1/Data2 = local-to-target coords (UiRoot.OnMouseDown).
|
||||
var p = HitChar(e.Data1, e.Data2);
|
||||
_selAnchor = p;
|
||||
|
|
@ -321,6 +375,7 @@ public sealed class UiText : UiElement
|
|||
|
||||
case UiEventType.MouseMove:
|
||||
{
|
||||
if (!Selectable) return false;
|
||||
if (_selecting)
|
||||
{
|
||||
// Data1/Data2 = local-to-target coords (DispatchMouseMove).
|
||||
|
|
@ -332,12 +387,14 @@ public sealed class UiText : UiElement
|
|||
|
||||
case UiEventType.MouseUp:
|
||||
{
|
||||
if (!Selectable) return false;
|
||||
_selecting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
case UiEventType.KeyDown:
|
||||
{
|
||||
if (!Selectable) return false;
|
||||
var key = (Silk.NET.Input.Key)e.Data0;
|
||||
bool ctrl = Keyboard is not null
|
||||
&& (Keyboard.IsKeyPressed(Silk.NET.Input.Key.ControlLeft)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue