Move menu tab projection into runtime

This commit is contained in:
Meta Vens 2026-06-21 21:42:06 +01:00
commit 0bd2fbb749
3 changed files with 86 additions and 17 deletions

View file

@ -3897,15 +3897,19 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root)
"HandleTabSurfaceCommand(",
"AetheriaEveUnitySurfaceHost.RenderRuntime(",
"AetheriaEveUnitySurfaceHost.Hide(_tabSurfaceDocument)",
"AetheriaRuntimeMenuTabsSurfaceBuilder.Build(ProjectTabSurfaceState())",
"ProjectTabSurfaceState(",
"AetheriaRuntimeMenuTabsSurfaceBuilder.Build(ProjectTabSurface())",
"ProjectTabSurface(",
"AetheriaRuntimeMenuTabsSurfaceBuilder.Project(",
"new AetheriaRuntimeMenuTabProjectionOption(",
"ResolveVisibleTabs(",
"GameManager.IsObservedDocked",
"GameManager.TryGetObservedDockedLocalStory(out _)",
"GetTabLabel(",
"ToRuntimeTabKey(",
"AetheriaRuntimeMenuTabsSurfaceBuilder.NormalizeTabKey(tab.ToString())",
"AetheriaRuntimeMenuTabsSurfaceCommands.TryRead(request, out var command)",
"AetheriaRuntimeMenuTabCommandKind.SelectTab",
"string.Equals(command.TabKey, TabKey(tab), StringComparison.Ordinal)",
"string.Equals(command.TabKey, ToRuntimeTabKey(tab), StringComparison.Ordinal)",
"TabButtons.gameObject.SetActive(false)"
};
@ -3951,7 +3955,10 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root)
"private static EveSurfaceComponent Node(",
"ResolveTabSurfaceDocument(",
"new EveUiToolkitSurfaceLowerer()",
"string.Equals(request.Command, AetheriaRuntimeMenuTabsSurfaceBuilder.CommandFor("
"string.Equals(request.Command, AetheriaRuntimeMenuTabsSurfaceBuilder.CommandFor(",
"ProjectTabSurfaceState(",
"new AetheriaRuntimeMenuTabSurfaceEntry(",
"private static string TabKey("
};
var hits = forbiddenSymbols
@ -3965,6 +3972,26 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root)
string.Join(", ", hits));
}
var requiredProjectionBuilderSymbols = new[]
{
"public sealed class AetheriaRuntimeMenuTabProjectionOption",
"public static string NormalizeTabKey(string tabKey)",
"public static AetheriaRuntimeMenuTabsSurfaceState Project(",
"public int Order { get; }",
".OrderBy(tab => tab.Order)",
"new AetheriaRuntimeMenuTabSurfaceEntry(",
"string.Equals(key, normalizedCurrent, StringComparison.Ordinal)"
};
var missingProjectionBuilderSymbols = requiredProjectionBuilderSymbols
.Where(symbol => !menuTabsSurfaceBuilder.Contains(symbol, StringComparison.Ordinal))
.ToArray();
if (missingProjectionBuilderSymbols.Length > 0)
{
throw new InvalidOperationException(
"Shared runtime menu tab surface builder no longer owns tab projection semantics: " +
string.Join(", ", missingProjectionBuilderSymbols));
}
var requiredDockedStoryObserverSymbols = new[]
{
"public bool IsObservedDocked => TryGetObservedDockingBay(out _);",

View file

@ -108,7 +108,7 @@ public class MenuPanel : MonoBehaviour
transform,
_tabSurfaceDocument,
"Aetheria Runtime Menu Tabs Surface",
AetheriaRuntimeMenuTabsSurfaceBuilder.Build(ProjectTabSurfaceState()),
AetheriaRuntimeMenuTabsSurfaceBuilder.Build(ProjectTabSurface()),
HandleTabSurfaceCommand,
_tabSurfaceChrome);
}
@ -124,7 +124,7 @@ public class MenuPanel : MonoBehaviour
foreach (var tab in _tabs.Keys)
{
if (command.Kind == AetheriaRuntimeMenuTabCommandKind.SelectTab &&
string.Equals(command.TabKey, TabKey(tab), StringComparison.Ordinal))
string.Equals(command.TabKey, ToRuntimeTabKey(tab), StringComparison.Ordinal))
{
ShowTab(tab);
return;
@ -142,16 +142,16 @@ public class MenuPanel : MonoBehaviour
AetheriaEveUnitySurfaceHost.Hide(_tabSurfaceDocument);
}
private AetheriaRuntimeMenuTabsSurfaceState ProjectTabSurfaceState()
private AetheriaRuntimeMenuTabsSurfaceState ProjectTabSurface()
{
var visibleTabs = ResolveVisibleTabs();
return new AetheriaRuntimeMenuTabsSurfaceState(
TabKey(CurrentTab),
return AetheriaRuntimeMenuTabsSurfaceBuilder.Project(
ToRuntimeTabKey(CurrentTab),
visibleTabs
.Select(tabBinding => new AetheriaRuntimeMenuTabSurfaceEntry(
TabKey(tabBinding.Tab),
.Select(tabBinding => new AetheriaRuntimeMenuTabProjectionOption(
ToRuntimeTabKey(tabBinding.Tab),
GetTabLabel(tabBinding),
tabBinding.Tab == CurrentTab))
(int)tabBinding.Tab))
.ToArray(),
DateTime.UtcNow.ToString("O"));
}
@ -172,9 +172,9 @@ public class MenuPanel : MonoBehaviour
: tabBinding.Label;
}
private static string TabKey(MenuTab tab)
private static string ToRuntimeTabKey(MenuTab tab)
{
return tab.ToString().ToLowerInvariant();
return AetheriaRuntimeMenuTabsSurfaceBuilder.NormalizeTabKey(tab.ToString());
}
// void Start()

View file

@ -36,16 +36,58 @@ namespace GameCult.Aetheria.State.Verse
public string UpdatedAtUtc { get; }
}
public sealed class AetheriaRuntimeMenuTabProjectionOption
{
public AetheriaRuntimeMenuTabProjectionOption(string key, string label, int order)
{
Key = key ?? "";
Label = label ?? "";
Order = order;
}
public string Key { get; }
public string Label { get; }
public int Order { get; }
}
public static class AetheriaRuntimeMenuTabsSurfaceBuilder
{
public const string SurfaceId = "aetheria.runtime_menu.tabs";
public static string CommandFor(string tabKey)
public static string NormalizeTabKey(string tabKey)
{
var normalized = string.IsNullOrWhiteSpace(tabKey)
return string.IsNullOrWhiteSpace(tabKey)
? "unknown"
: tabKey.Trim().ToLowerInvariant();
return $"aetheria.runtime_menu.tab.{normalized}";
}
public static string CommandFor(string tabKey)
{
return $"aetheria.runtime_menu.tab.{NormalizeTabKey(tabKey)}";
}
public static AetheriaRuntimeMenuTabsSurfaceState Project(
string currentTabKey,
IEnumerable<AetheriaRuntimeMenuTabProjectionOption> visibleTabs,
string updatedAtUtc)
{
var normalizedCurrent = NormalizeTabKey(currentTabKey);
return new AetheriaRuntimeMenuTabsSurfaceState(
normalizedCurrent,
(visibleTabs ?? Array.Empty<AetheriaRuntimeMenuTabProjectionOption>())
.Where(tab => tab != null)
.OrderBy(tab => tab.Order)
.ThenBy(tab => NormalizeTabKey(tab.Key), StringComparer.Ordinal)
.Select(tab =>
{
var key = NormalizeTabKey(tab.Key);
return new AetheriaRuntimeMenuTabSurfaceEntry(
key,
string.IsNullOrWhiteSpace(tab.Label) ? key : tab.Label,
string.Equals(key, normalizedCurrent, StringComparison.Ordinal));
})
.ToArray(),
updatedAtUtc);
}
public static AetheriaRuntimeSurfaceDocument Build(