feat(ui): Campaign OP slice OP6 — the Config tab
Binds the retail Options panel's Config tab (LayoutDesc 0x21000029, 27 authored rows across 6 sections) through OP2's template mechanism and OP3's per-page OptionPage model, matching the Character/Chat tab controllers' established pattern. The row table is transcribed directly from two decompiled sources — gmConfigUI::InitOptions @0x0049E400 (row order, widget shape, defaults) and gmClient::InitUIPreferences @0x004035b0 (the complete UIPreferences::AttachPreference registration: every label/tooltip key, every slider's real-unit range, every menu's enum choices) — which resolves the research docs' own "U4" unverified slider-caption pairing: retail ships ZERO range captions on this tab (every SetSliderLabel call passes literal string id 0). Consumer disposition: LIVE — Sound/Ambient volume-trio sliders and their toggle halves (AudioSettings.SfxDisabled/AmbientDisabled now gate the already-live engine write; RuntimeSettingsController.SaveAudio newly pushes into OpenAlAudioEngine on every change, not just at startup), Resolution/Full Screen (immediate window resize on save). NEXT-LAUNCH (pre-existing precedent): Sync To Refresh, Field of View. STORE-ONLY (register rows AP-198/199/200, TS-74 extended): Sound Features/Interface trio/Play-Only-When-Active, the nine Graphics/Rendering-Quality rows (Vulkan has no per-feature render knobs), Camera/Input's six rows and Use Mouse Turning (no persistent mouse-turning camera mode), Chat Font Face/Size (distinct new fields from the existing live ChatSettings.FontSize). AudioSettings/DisplaySettings/CameraTurningSettings/ChatSettings each gain new fields for their slice of the 27 rows, backed by SettingsStore round-trips. A real bug caught by testing: the scrollbar scope lookup used the standalone-layout root id (0x100001FF), which does not survive base-merge into the host-mounted tree — fixed to scope from the tab host's own page-slot id (0x10000213), matching Chat's established pattern for the same shared-scrollbar-id hazard (0x10000201, authored by both the Chat and Config ListBoxes). 30 new tests (27 authored rows register as 30 IOptionRow instances — the three toggle+slider trios each register two). Full Release suite: 13,107 passed / 4 skipped / 0 failed (was 13,083/4/0 — net +24, the one existing RuntimeSettingsControllerTests case updated for SaveAudio's new live-apply call, not a regression). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
527a3a4056
commit
f5ac1742ba
16 changed files with 2188 additions and 30 deletions
|
|
@ -202,7 +202,18 @@ public sealed record OptionsRuntimeBindings(
|
|||
// Campaign OP slice OP4 (2026-08-11): reads a live character-option
|
||||
// bit by its linear id (RuntimeCharacterOptionsState.GetOptionBit) —
|
||||
// the Character-tab panel's row-seed source.
|
||||
Func<uint, bool> CurrentCharacterOption);
|
||||
Func<uint, bool> CurrentCharacterOption,
|
||||
// Campaign OP slice OP6 (2026-08-11): the Config tab's Display/Audio-
|
||||
// backed rows. RuntimeSettingsController is the SOLE writer of these
|
||||
// two sections (unlike Chat, which OP5 already routes through a raw
|
||||
// SettingsStore side channel — Config's Chat Font Face/Size rows reuse
|
||||
// THAT same store directly at the composition site instead of these
|
||||
// two delegates, to avoid a stale-cache clobber of CH6's filter/opacity
|
||||
// writes; see RetailUiRuntime.MountOptionsPanel).
|
||||
Func<DisplaySettings> LoadDisplay,
|
||||
Action<DisplaySettings> SaveDisplay,
|
||||
Func<AudioSettings> LoadAudio,
|
||||
Action<AudioSettings> SaveAudio);
|
||||
|
||||
public sealed record InventoryRuntimeBindings(
|
||||
ClientObjectTable Objects,
|
||||
|
|
@ -2131,6 +2142,50 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
Console.WriteLine("[UI] options panel: Chat tab rows did not bind.");
|
||||
}
|
||||
|
||||
// Campaign OP slice OP6 (2026-08-11): the Config tab's 6 headers +
|
||||
// 27 rows. Same "runs before ActivateTabs, own dat-lock scope"
|
||||
// shape as the Character/Chat blocks above.
|
||||
lock (_bindings.Assets.DatLock)
|
||||
{
|
||||
var strings = new DatStringResolver(_bindings.Assets.Dats);
|
||||
|
||||
bool configBound = Layout.ConfigOptionsPageController.Bind(
|
||||
layout,
|
||||
controller.ConfigPage,
|
||||
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.ConfigOptionsPageController.Bindings(
|
||||
LoadDisplay: _bindings.Options.LoadDisplay,
|
||||
SaveDisplay: _bindings.Options.SaveDisplay,
|
||||
LoadAudio: _bindings.Options.LoadAudio,
|
||||
SaveAudio: _bindings.Options.SaveAudio,
|
||||
LoadCameraTurning: _bindings.Options.LoadCameraTurning,
|
||||
SaveCameraTurning: _bindings.Options.SaveCameraTurning,
|
||||
// Chat Font Face/Size route through the SAME raw
|
||||
// SettingsStore side channel SaveChatWindowFilters/
|
||||
// SaveChatOpacity already use — RuntimeSettingsController's
|
||||
// cached ChatSettings is NOT refreshed by those direct
|
||||
// writes, so reading/writing through it here would
|
||||
// silently clobber CH6's filter/opacity edits with a
|
||||
// stale snapshot the next time either surface saves.
|
||||
LoadChat: () => _bindings.Chat.Store?.LoadChat() ?? ChatSettings.Default,
|
||||
SaveChat: chat => _bindings.Chat.Store?.SaveChat(chat)));
|
||||
if (!configBound)
|
||||
Console.WriteLine("[UI] options panel: Config tab rows did not bind.");
|
||||
}
|
||||
|
||||
controller.ActivateTabs();
|
||||
|
||||
RetailWindowHandle handle = RetailWindowFrame.Mount(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue