feat(ui): #409 — client-wide retail tooltip system
Full re-derivation from named-retail decomp: UIElement::StartTooltipAtMouse @0x00460D70 -> UIElementManager::StartTooltip @0x0045DE90/@0x00459700, UIElement::MouseHover @0x00462520 (P0x4B TooltipOn gate + global m_tooltipEnable), UIElementManager::CheckTooltip @0x0045B6E0 (dwell/ auto-hide timer, default 0.25s/10s), SwitchMouseOver/DeletingElement (dismissal). Corrects the earlier GF-16 investigation: P0x47 is the element-desc id WITHIN the popup LayoutDesc (P0x48), not a "behavior enum"; P0x4A is read off the popup's own instantiated root, not the trigger element. - ElementInfo/UiElement gain six tooltip data fields (P0x47/48/49/4A/4B/50), read generically by ElementReader and copied through LayoutImporter, mirroring the existing AuthoredInvisible passthrough pattern. - UiRoot's existing CheckTooltip-derived hover timer gains TooltipShow/ TooltipHide events, a per-element P0x50 delay override, and dismissal wiring at every retail-confirmed teardown site. - RetailTooltipPresenter (owned by RetailUiRuntime, mounted alongside RetailDialogFactory) builds the popup via the existing LayoutImporter dat-lock seam, auto-resizes by the measured-vs-authored text delta (word-wrapped via the existing UiText.WrapWords primitive), positions at the mouse clamped to the display, and stays topmost over dialogs via its own later per-tick BringToFront (register AD-106). - Misc.TooltipEnable/Misc.TooltipDelay are client-local UserPreferences (retail's own 2013 Config tab authors no visible row for either) — SettingsStore gains a MiscSettings section, no new options-panel row. - Live-DAT sweep: 434 elements author >=1 trigger property (243 with literal text this port shows; 191 rely on retail's dynamic InqProperty(0x49) override, deferred as register TS-85 alongside the unmodeled P0x3D wrap-width override). Gates: Release build 0 errors; App suite (live-DAT env) 5410/5407 passed/ 3 skipped (was 5379/3); Runtime 1735/0 unchanged; UI.Abstractions 926/0; full solution 14,617/14,548 passed/69 skipped/0 failed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
d3755eb231
commit
a377b9bff7
16 changed files with 1386 additions and 9 deletions
33
src/AcDream.UI.Abstractions/Panels/Settings/MiscSettings.cs
Normal file
33
src/AcDream.UI.Abstractions/Panels/Settings/MiscSettings.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
namespace AcDream.UI.Abstractions.Panels.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// #409 (client-wide retail tooltip system): the two <c>Misc.*</c>
|
||||
/// <c>UserPreferences.ini</c> keys retail's <c>UIElementManager::Init
|
||||
/// @0x0045EE10</c> registers (<c>UIPreferences::AttachPreference</c>,
|
||||
/// <c>Misc_TooltipDelay</c>/<c>Misc_TooltipEnable</c>) and binds onto
|
||||
/// <c>UIElementManager::m_tooltipDelay</c>/<c>m_tooltipEnable</c>. These are
|
||||
/// CLIENT-LOCAL preferences, not part of the server-synced
|
||||
/// <c>CharacterOptionTable</c> — the #409 investigation confirmed retail's
|
||||
/// own 2013 Config tab does not expose a visible row for either (they exist
|
||||
/// in <c>UserPreferences.ini</c> and are registered engine-side, but
|
||||
/// <c>gmConfigUI::InitOptions</c>'s 27 authored rows never author one), so
|
||||
/// this record follows the <see cref="CameraTurningSettings"/>/
|
||||
/// <see cref="AudioSettings"/> client-local persistence precedent WITHOUT a
|
||||
/// matching options-panel row — persisted, but not (yet) user-editable
|
||||
/// in-client, exactly matching what retail itself ships.
|
||||
/// </summary>
|
||||
public sealed record MiscSettings(
|
||||
bool TooltipEnable,
|
||||
float TooltipDelaySeconds)
|
||||
{
|
||||
/// <summary>
|
||||
/// Retail's registered defaults: <c>m_tooltipEnable=1</c>,
|
||||
/// <c>m_tooltipDelay=0.25f</c>
|
||||
/// (<c>UIElementManager::UIElementManager @0x0045F5D0</c>). The
|
||||
/// preference's registered clamp range is <c>[0,10]</c> seconds
|
||||
/// (<c>UIPreferences::SetPreferenceRange(&Misc_TooltipDelay, 0f, 10f)</c>).
|
||||
/// </summary>
|
||||
public static MiscSettings Default { get; } = new(
|
||||
TooltipEnable: true,
|
||||
TooltipDelaySeconds: 0.25f);
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ public readonly record struct UiWindowPosition(float X, float Y);
|
|||
|
||||
/// <summary>
|
||||
/// JSON-backed persistence for non-keybind settings (Display / Audio / Chat /
|
||||
/// Character; the OP9-retired <c>Gameplay</c> section is no longer read or
|
||||
/// CameraTurning / Character / Misc; the OP9-retired <c>Gameplay</c> section is no longer read or
|
||||
/// written — see the class's Schema note below for how a leftover
|
||||
/// <c>"gameplay"</c> block from an older settings.json is tolerated). The
|
||||
/// graphical host supplies a canonical portable configuration path. Coexists
|
||||
|
|
@ -280,6 +280,38 @@ public sealed class SettingsStore
|
|||
public void SaveCameraTurning(CameraTurningSettings cameraTurning)
|
||||
=> SaveSection("cameraTurning", BuildCameraTurningObject(cameraTurning));
|
||||
|
||||
/// <summary>
|
||||
/// #409: load the two <c>Misc.*</c> tooltip preferences. Same fall-back
|
||||
/// behaviour as <see cref="LoadDisplay"/>.
|
||||
/// </summary>
|
||||
public MiscSettings LoadMisc()
|
||||
{
|
||||
if (!File.Exists(_path)) return MiscSettings.Default;
|
||||
try
|
||||
{
|
||||
using var stream = File.OpenRead(_path);
|
||||
var doc = JsonDocument.Parse(stream);
|
||||
var root = doc.RootElement;
|
||||
if (!root.TryGetProperty("misc", out var misc)
|
||||
|| misc.ValueKind != JsonValueKind.Object)
|
||||
return MiscSettings.Default;
|
||||
|
||||
var d = MiscSettings.Default;
|
||||
return new MiscSettings(
|
||||
TooltipEnable: ReadBool (misc, "tooltipEnable", d.TooltipEnable),
|
||||
TooltipDelaySeconds: ReadFloat(misc, "tooltipDelaySeconds", d.TooltipDelaySeconds));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"settings: failed to load {_path}: {ex.Message} — using defaults");
|
||||
return MiscSettings.Default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Save the tooltip preferences, preserving all other top-level keys.</summary>
|
||||
public void SaveMisc(MiscSettings misc)
|
||||
=> SaveSection("misc", BuildMiscObject(misc));
|
||||
|
||||
/// <summary>
|
||||
/// Load per-character settings keyed by <paramref name="toonKey"/>.
|
||||
/// Missing file or missing toon entry → <see cref="CharacterSettings.Default"/>.
|
||||
|
|
@ -652,6 +684,13 @@ public sealed class SettingsStore
|
|||
["vsync"] = d.VSync,
|
||||
};
|
||||
|
||||
private static SortedDictionary<string, object> BuildMiscObject(MiscSettings m)
|
||||
=> new(StringComparer.Ordinal)
|
||||
{
|
||||
["tooltipDelaySeconds"] = m.TooltipDelaySeconds,
|
||||
["tooltipEnable"] = m.TooltipEnable,
|
||||
};
|
||||
|
||||
private static SortedDictionary<string, object> BuildCameraTurningObject(CameraTurningSettings c)
|
||||
=> new(StringComparer.Ordinal)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue