feat(render): implement Campaign AR and terrain fidelity

This commit is contained in:
Erik 2026-08-22 13:13:29 +02:00
parent 99cf26e00c
commit 7a5f96ede5
368 changed files with 50611 additions and 950 deletions

View file

@ -39,6 +39,13 @@ public sealed class UiMenu : UiElement
/// </remarks>
public Action? OnOpen { get; set; }
/// <summary>
/// Optional interaction-boundary refresh invoked immediately before a
/// closed popup opens. Retained settings menus use it to consume an
/// event-driven catalog revision without polling during draw/update.
/// </summary>
public Action? BeforeOpen { get; set; }
/// <summary>Per-payload enabled gate (disabled rows render greyed + are inert). Null ⇒ all enabled.</summary>
public Func<object?, bool>? EnabledProvider { get; set; }
@ -55,9 +62,20 @@ public sealed class UiMenu : UiElement
/// surface for the row.</summary>
public string? TooltipText { get; set; }
/// <summary>Optional live tooltip source. Controllers use this when the
/// reason or cost behind a menu selection can change while the panel stays
/// open. A non-blank provider value takes precedence over
/// <see cref="TooltipText"/>.</summary>
public Func<string?>? TooltipTextProvider { get; set; }
/// <inheritdoc />
public override string? GetTooltipText() =>
string.IsNullOrWhiteSpace(TooltipText) ? null : TooltipText;
public override string? GetTooltipText()
{
string? live = TooltipTextProvider?.Invoke();
if (!string.IsNullOrWhiteSpace(live))
return live;
return string.IsNullOrWhiteSpace(TooltipText) ? null : TooltipText;
}
public int RowsPerColumn { get; set; } = 7; // items per column (dat item template);
// ALSO the visible-row window height when Scrollable
@ -271,7 +289,11 @@ public sealed class UiMenu : UiElement
if (_open == value) return;
// Before _open flips, so a handler that replaces Items is reflected in
// the very first measure/draw of this opening.
if (value) OnOpen?.Invoke();
if (value)
{
BeforeOpen?.Invoke();
OnOpen?.Invoke();
}
_open = value;
if (FindRoot() is not { } root) return;
if (value) root.SetActivePopup(this, () => SetOpen(false));