namespace AcDream.UI.Abstractions.Panels.Settings; /// /// Retail's linked default/active window-opacity invariant: active >= default, /// ALWAYS — enforced by dragging the OTHER value, never by clamping the one being /// set. Verbatim port of ChatInterface::SetDefaultOpacity @0x004F3BC0 / /// SetActiveOpacity @0x004F3C40 (docs/research/2026-08-09-chat-retail-window-shell.md /// §3): raising the default ABOVE the current active value drags active UP to match; /// lowering the active value BELOW the current default drags default DOWN to match. /// Retail's options-page sliders share a DualHash link /// (gmChatOptionsUI::InitOptions @0x0049FC60) — this is why they visibly track /// each other while dragging. /// /// /// Consumed directly by RetailWindowOpacityController (AcDream.App — the /// live per-window mechanism, mutating its own two float fields). The retail /// Options panel's Chat tab (ChatOptionsPageController, Campaign OP slice /// OP5) drives that SAME controller rather than calling these methods a second /// time, so the panel's two sliders and every retained window's live fade always /// apply the identical link math through one path. (The OP9-retired /// SettingsPanel's own Chat tab used to call these methods directly as a /// second consumer, mutating a draft record — that /// draft mechanism is gone, not the link math.) Pure functions — no /// window/render/state dependency — so any future caller can use them without /// violating the App→Abstractions dependency direction. /// /// public static class ChatOpacityLink { /// /// Port of ChatInterface::SetDefaultOpacity @0x004F3BC0: set the default /// (unfocused) opacity to , dragging /// UP if it would otherwise fall below the new /// default. Never returns an (default, active) pair with active < default. /// public static (float DefaultOpacity, float ActiveOpacity) SetDefault( float currentActive, float newDefault) { newDefault = System.Math.Clamp(newDefault, 0f, 1f); float active = currentActive < newDefault ? newDefault : currentActive; return (newDefault, active); } /// /// Port of ChatInterface::SetActiveOpacity @0x004F3C40: set the active /// (focused) opacity to , dragging /// DOWN if it would otherwise exceed the new /// active value. Never returns an (default, active) pair with active < default. /// public static (float DefaultOpacity, float ActiveOpacity) SetActive( float currentDefault, float newActive) { newActive = System.Math.Clamp(newActive, 0f, 1f); float def = currentDefault > newActive ? newActive : currentDefault; return (def, newActive); } }