feat(ui): Campaign OP slice OP5 — the Chat tab
Binds LayoutDesc 0x2100005C through OP2's template-list mechanism and OP3's per-page OptionPage model: the General Options header + two DualHash-linked opacity sliders (Option_DefaultOpacity_Property 0x10000080 / Option_ActiveOpacity_Property 0x10000081, live-apply on drag through RetailWindowOpacityController, defaults read from the installed DAT's DBProperties collection at DID 0x78000001 via ChatOptionsDatDefaults), and the five per-window text-filter blocks (main window 12 rows minus Gameplay, four floaties 13 rows each — the byte-verified authored order cross-checked against the raw gmChatOptionsUI::InitOptions/AddCheckboxBitfield64Option pseudo-C, not just the research doc's own table) writing AcDream.Core.Chat. ChatWindowState directly, the same state CH6's chat windows already read. AP-195 retired: ported both halves left open at the OP2 re-review — the ALL-set LED media swap (new UiButton.FaceFileOverride, driven by the block-level P0x10000082/P0x10000083 sprites now threaded through ElementInfo/DatWidgetFactory) and the CreateChildren self-sizing tail (UiCheckboxBitfield64.Height grows with its stacked row content; the enclosing ListBox reflows around the block's FINAL height via the new UiTemplateListBox.AddPrebuiltRow, reusing the ListBox's own stacking rather than a third stacking path). AP-187 broadened to cover the main window's own filter (previously only the four floaties) and the new live-editing write path. The main chat window's filter (retail window id 8, ChatWindowState id 0) gains its own settings.json persistence (ChatSettings. ChatWindowMainFilter) alongside the pre-existing floaty 1-4 fields; opacity persistence is now wired on every live slider change, not only through the old dev-scaffold Settings panel. Fixture regeneration (ACDREAM_REGENERATE_UI_FIXTURES=1) picked up the new ElementInfo.LedCheckedSprite/LedUncheckedSprite fields across all 19 committed layout fixtures — purely additive, confirmed against the live installed DAT (0x10000520's own 0x82/0x83 properties resolve to 0x06004D17/0x06004D19 exactly as AP-195 documented). Conformance: FilterRows/FilterBlocks pinned against the byte-verified authored order and ChatWindowState's own default constants; the AP-195 LED swap and self-sizing behavior; the DAT opacity-default extraction against the live installed DAT; live filter/opacity writes reaching ChatWindowState/RetailWindowOpacityController; OnShown re-seed and Reset/Defaults ghosting per the OP4 binding-pattern discipline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4798302226
commit
e71e5a9614
52 changed files with 4540 additions and 40 deletions
|
|
@ -657,19 +657,18 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
};
|
||||
|
||||
/// <summary>
|
||||
/// Capture the four floating windows' current text-type filters into
|
||||
/// the local settings store (research doc §4.4/§6.1 — local-only until
|
||||
/// CH6f's wire format). No-op when no store was wired.
|
||||
/// Capture the main window's and four floating windows' current text-type
|
||||
/// filters into the local settings store (research doc §4.4/§6.1 — local-only
|
||||
/// until a future retail <c>0x1000008C</c> wire format). No-op when no store was
|
||||
/// wired.
|
||||
///
|
||||
/// <para>
|
||||
/// CH6a/b REJECT-review NIT 1: unlike window geometry/visibility (which
|
||||
/// <see cref="_persistence"/> auto-saves on every change), a filter
|
||||
/// change is only captured here — i.e. only when <see cref="SaveLayout"/>
|
||||
/// runs, which today is exclusively the explicit <c>/saveautoui</c>
|
||||
/// command. Harmless in practice (nothing currently mutates a window's
|
||||
/// filter live — no options UI exists yet), but worth tightening to
|
||||
/// auto-save-on-change once CH6e/CH6f gives filters a live settings
|
||||
/// surface a user can actually edit mid-session.
|
||||
/// CH6a/b REJECT-review NIT 1 — CLOSED by Campaign OP slice OP5: filters are now
|
||||
/// ALSO captured on every live edit through the Chat tab's five filter blocks
|
||||
/// (each block's <c>BitfieldOptionRow.apply</c> closure calls this directly), not
|
||||
/// only when <see cref="SaveLayout"/> runs via the explicit <c>/saveautoui</c>
|
||||
/// command — OP5 is the "live settings surface a user can actually edit
|
||||
/// mid-session" this NIT was waiting on.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private void SaveChatWindowFilters()
|
||||
|
|
@ -679,6 +678,7 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
ChatSettings current = store.LoadChat();
|
||||
store.SaveChat(current with
|
||||
{
|
||||
ChatWindowMainFilter = windows.GetFilter(ChatWindowState.MainWindowId),
|
||||
ChatWindow1Filter = windows.GetFilter(1),
|
||||
ChatWindow2Filter = windows.GetFilter(2),
|
||||
ChatWindow3Filter = windows.GetFilter(3),
|
||||
|
|
@ -686,6 +686,25 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Campaign OP slice OP5: persists the Chat tab's two opacity-slider values —
|
||||
/// the "worth tightening to auto-save-on-change" gap
|
||||
/// <see cref="SaveChatWindowFilters"/>'s own doc flagged before a live options UI
|
||||
/// existed to edit them. Called from the Chat tab's slider apply closures, so
|
||||
/// every drag tick is captured (matching the filter blocks' own auto-save-on-
|
||||
/// change wiring below, not the old "only on /saveautoui" schedule).
|
||||
/// </summary>
|
||||
private void SaveChatOpacity()
|
||||
{
|
||||
if (_bindings.Chat.Store is not { } store) return;
|
||||
ChatSettings current = store.LoadChat();
|
||||
store.SaveChat(current with
|
||||
{
|
||||
DefaultOpacity = WindowOpacity.DefaultOpacity,
|
||||
ActiveOpacity = WindowOpacity.ActiveOpacity,
|
||||
});
|
||||
}
|
||||
|
||||
public void CloseWindow(string name)
|
||||
{
|
||||
if (RetailPanelCatalog.TryGetPanelId(name, out uint panelId))
|
||||
|
|
@ -898,6 +917,16 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
return;
|
||||
}
|
||||
|
||||
// Campaign OP slice OP5: the main window's own filter (id 0) is now
|
||||
// user-settable through the Chat tab's main-window filter block
|
||||
// (research doc §5.2) — seed it from the local store here, the same
|
||||
// "local-only persistence" leg MountFloatingChatWindows already runs
|
||||
// for windows 1-4 (research doc §4.4/§6.1 — the retail 0x1000008C
|
||||
// gameplay-options wire remains explicitly deferred).
|
||||
if (_bindings.Chat.Store is { } chatStore)
|
||||
_bindings.Chat.Windows.SetFilter(
|
||||
ChatWindowState.MainWindowId, chatStore.LoadChat().ChatWindowMainFilter);
|
||||
|
||||
ChatWindowController? controller = ChatWindowController.Bind(
|
||||
info,
|
||||
layout,
|
||||
|
|
@ -2040,6 +2069,68 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
Console.WriteLine("[UI] options panel: Character tab rows did not bind.");
|
||||
}
|
||||
|
||||
// Campaign OP slice OP5 (2026-08-11): the Chat tab's 6 headers, 2 linked
|
||||
// opacity sliders, and 5 per-window filter blocks. Same "runs before
|
||||
// ActivateTabs, own dat-lock scope" shape as the Character-tab block above.
|
||||
lock (_bindings.Assets.DatLock)
|
||||
{
|
||||
var strings = new DatStringResolver(_bindings.Assets.Dats);
|
||||
|
||||
// U1's Chat-tab-specific consumer (this class's own doc trace): the
|
||||
// two sliders' Defaults values come from the DAT DBProperties
|
||||
// collection, resolved ONCE — the installed DAT does not change
|
||||
// within a session.
|
||||
if (!Layout.ChatOptionsDatDefaults.TryRead(
|
||||
_bindings.Assets.Dats, out float datDefaultOpacity, out float datActiveOpacity))
|
||||
{
|
||||
Console.WriteLine(
|
||||
"[UI] options panel: Chat tab opacity DAT defaults did not resolve "
|
||||
+ "(DID 0x78000001) — falling back to retail's ChatInterface base-"
|
||||
+ "constructor values (0.5/1.0).");
|
||||
}
|
||||
|
||||
bool chatBound = Layout.ChatOptionsPageController.Bind(
|
||||
layout,
|
||||
controller.ChatPage,
|
||||
templateResolver: (templateLayoutId, templateElementId) =>
|
||||
{
|
||||
ElementInfo? info = LayoutImporter.ImportInfos(
|
||||
_bindings.Assets.Dats, templateLayoutId, templateElementId);
|
||||
return info is null
|
||||
? null
|
||||
: LayoutImporter.Build(
|
||||
info,
|
||||
_bindings.Assets.ResolveSprite,
|
||||
_bindings.Assets.DefaultFont,
|
||||
_bindings.Assets.ResolveFont,
|
||||
strings.Resolve).Root;
|
||||
},
|
||||
resolveString: (tableId, stringId) => strings.Resolve(tableId, stringId),
|
||||
new Layout.ChatOptionsPageController.Bindings(
|
||||
CurrentDefaultOpacity: () => WindowOpacity.DefaultOpacity,
|
||||
CurrentActiveOpacity: () => WindowOpacity.ActiveOpacity,
|
||||
SetDefaultOpacity: value =>
|
||||
{
|
||||
WindowOpacity.SetDefaultOpacity(value);
|
||||
SaveChatOpacity();
|
||||
},
|
||||
SetActiveOpacity: value =>
|
||||
{
|
||||
WindowOpacity.SetActiveOpacity(value);
|
||||
SaveChatOpacity();
|
||||
},
|
||||
DefaultOpacityDatDefault: datDefaultOpacity,
|
||||
ActiveOpacityDatDefault: datActiveOpacity,
|
||||
CurrentFilter: windowId => _bindings.Chat.Windows.GetFilter(windowId),
|
||||
SetFilter: (windowId, value) =>
|
||||
{
|
||||
_bindings.Chat.Windows.SetFilter(windowId, value);
|
||||
SaveChatWindowFilters();
|
||||
}));
|
||||
if (!chatBound)
|
||||
Console.WriteLine("[UI] options panel: Chat tab rows did not bind.");
|
||||
}
|
||||
|
||||
controller.ActivateTabs();
|
||||
|
||||
RetailWindowHandle handle = RetailWindowFrame.Mount(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue