feat(ui): port retained widget foundations
This commit is contained in:
parent
44f9ec13d9
commit
d825572e31
44 changed files with 84813 additions and 292 deletions
|
|
@ -121,7 +121,17 @@ public abstract class UiElement
|
|||
|
||||
// ── State flags ─────────────────────────────────────────────────────
|
||||
public bool Visible { get; set; } = true;
|
||||
public bool Enabled { get; set; } = true;
|
||||
private bool _enabled = true;
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (_enabled == value) return;
|
||||
_enabled = value;
|
||||
OnEnabledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, <see cref="HitTest"/> skips this element — the event
|
||||
|
|
@ -206,7 +216,28 @@ public abstract class UiElement
|
|||
|
||||
/// <summary>Edges this element anchors to in its parent. Default Left|Top
|
||||
/// (pinned top-left, fixed size — no reflow). Left|Right stretches width.</summary>
|
||||
public AnchorEdges Anchors { get; set; } = AnchorEdges.Left | AnchorEdges.Top;
|
||||
private AnchorEdges _anchors = AnchorEdges.Left | AnchorEdges.Top;
|
||||
|
||||
/// <summary>Edges this programmatic element anchors to in its parent. Assigning
|
||||
/// this compatibility policy explicitly opts out of an imported DAT
|
||||
/// <see cref="LayoutPolicy"/>.</summary>
|
||||
public AnchorEdges Anchors
|
||||
{
|
||||
get => _anchors;
|
||||
set
|
||||
{
|
||||
_anchors = value;
|
||||
LayoutPolicy = null;
|
||||
_anchorCaptured = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exact raw-edge policy for imported DAT elements. Null for roots and
|
||||
/// programmatic widgets. Controllers may set null to opt out or call
|
||||
/// <see cref="UiLayoutPolicy.Rebase"/> after intentional geometry changes.
|
||||
/// </summary>
|
||||
public UiLayoutPolicy? LayoutPolicy { get; set; }
|
||||
|
||||
// ── Tree structure ──────────────────────────────────────────────────
|
||||
public UiElement? Parent { get; private set; }
|
||||
|
|
@ -223,7 +254,9 @@ public abstract class UiElement
|
|||
|
||||
public virtual bool RemoveChild(UiElement child)
|
||||
{
|
||||
if (!_children.Remove(child)) return false;
|
||||
if (!_children.Contains(child)) return false;
|
||||
FindRoot()?.OnSubtreeRemoving(child);
|
||||
_children.Remove(child);
|
||||
child.Parent = null;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -271,6 +304,9 @@ public abstract class UiElement
|
|||
/// <summary>Per-frame tick (animations, timers, caret blink).</summary>
|
||||
protected virtual void OnTick(double deltaSeconds) { }
|
||||
|
||||
/// <summary>Called synchronously after <see cref="Enabled"/> changes.</summary>
|
||||
protected virtual void OnEnabledChanged() { }
|
||||
|
||||
/// <summary>
|
||||
/// Custom hit-test override. Default is a rectangle containment
|
||||
/// check on (<see cref="Width"/>, <see cref="Height"/>).
|
||||
|
|
@ -296,8 +332,8 @@ public abstract class UiElement
|
|||
public virtual (uint tex, int w, int h)? GetDragGhost() => null;
|
||||
|
||||
/// <summary>
|
||||
/// Tooltip text for this widget. Retail fires event 0x07 after
|
||||
/// ~1000ms hover, then queries the widget's virtual "GetString"
|
||||
/// Tooltip text for this widget. Retail fires event 0x07 after the configured
|
||||
/// hover delay (0.25 seconds by default), then queries the widget's virtual "GetString"
|
||||
/// (vtable +0x88) to render the tooltip body.
|
||||
/// </summary>
|
||||
public virtual string? GetTooltipText() => null;
|
||||
|
|
@ -418,6 +454,22 @@ public abstract class UiElement
|
|||
/// Called by the parent each frame before drawing children.</summary>
|
||||
internal void ApplyAnchor(float parentW, float parentH)
|
||||
{
|
||||
if (LayoutPolicy is not null)
|
||||
{
|
||||
var current = UiPixelRect.FromPositionAndSize(
|
||||
(int)Left,
|
||||
(int)Top,
|
||||
(int)Width,
|
||||
(int)Height);
|
||||
var parent = UiPixelRect.FromPositionAndSize(0, 0, (int)parentW, (int)parentH);
|
||||
var next = LayoutPolicy.Apply(current, parent);
|
||||
Left = next.X0;
|
||||
Top = next.Y0;
|
||||
Width = next.Width;
|
||||
Height = next.Height;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Anchors == AnchorEdges.None) return;
|
||||
if (!_anchorCaptured)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue