diff --git a/HellionChat/AutoTellTabsService.cs b/HellionChat/AutoTellTabsService.cs index 13d8d13..6ea5682 100644 --- a/HellionChat/AutoTellTabsService.cs +++ b/HellionChat/AutoTellTabsService.cs @@ -39,6 +39,10 @@ internal sealed class AutoTellTabsService : IDisposable private bool _initialized; + // Set when Initialize ran before a character was available; cleared once the + // history has actually been loaded. + private bool _rehydratePending; + internal AutoTellTabsService( Plugin plugin, MessageManager messageManager, @@ -76,12 +80,30 @@ internal sealed class AutoTellTabsService : IDisposable RehydratePinnedTabs(); _messageManager.MessageProcessed += HandleTell; + Plugin.ClientState.Login += OnLogin; Plugin.ClientState.Logout += OnLogout; _initialized = true; } + // Deferred when the plugin starts before a character is logged in, which is + // the normal case: the game loads plugins at boot. CurrentContentId is 0 + // until then, so the history query would look up tells for character zero, + // find none, and leave every pinned tab blank for the whole session. + // + // Only visible to someone who actually pins a tell tab AND starts the game + // with the plugin already installed. Reloading the plugin in a running + // session -- what a developer does all day -- hides it completely. private void RehydratePinnedTabs() { + if (_messageManager.CurrentContentId == 0) + { + _logger.LogDebug("[Pin] Rehydrate deferred: no character yet, waiting for login"); + _rehydratePending = true; + return; + } + + _rehydratePending = false; + var pinned = Plugin.Config.Tabs.Count(TabLifecycleHelpers.IsInPinnedPool); _logger.LogDebug($"[Pin] Rehydrate scan: {pinned} pinned tab(s) found"); @@ -122,6 +144,7 @@ internal sealed class AutoTellTabsService : IDisposable return; } + Plugin.ClientState.Login -= OnLogin; Plugin.ClientState.Logout -= OnLogout; _messageManager.MessageProcessed -= HandleTell; _initialized = false; @@ -490,6 +513,17 @@ internal sealed class AutoTellTabsService : IDisposable } } + // Fires on the login that follows a boot-time start, and on every character + // switch after one. Guarded by the pending flag so a switch does not append + // a second copy of the history to tabs that already have it. + private void OnLogin() + { + if (!_rehydratePending) + return; + + RehydratePinnedTabs(); + } + private void OnLogout(int type, int code) { lock (TabsListLock) diff --git a/HellionChat/Ui/Components/Settings/Tabs/ChatTab.cs b/HellionChat/Ui/Components/Settings/Tabs/ChatTab.cs index 31ec8aa..36f1fd6 100644 --- a/HellionChat/Ui/Components/Settings/Tabs/ChatTab.cs +++ b/HellionChat/Ui/Components/Settings/Tabs/ChatTab.cs @@ -6,10 +6,12 @@ namespace HellionChat.Ui.Components.Settings.Tabs; internal sealed class ChatTab { + private readonly Plugin _plugin; private readonly SettingsWidgets _w; public ChatTab(Plugin plugin, TokenResolver resolver) { + _plugin = plugin; _w = new SettingsWidgets(plugin, new SettingsPalette(resolver)); } @@ -53,6 +55,33 @@ internal sealed class ChatTab ); } + if (_w.Section(ImGui.GetID("chat.history"u8), "History")) + { + // The one setting that decides whether the chat log shows anything + // from before this game session. It had no control at all: only the + // first-run wizard ever wrote it, and only if the user actually + // reached step 3 -- skip the wizard and it stays on its default of + // false, leaving the log empty on every launch with no way to fix it. + // + // Named for what it does. The stored name describes filtering, and + // the wizard's own "Load previous session on startup" checkbox + // writes a field nothing reads. + _w.ToggleRow( + ImGui.GetID("chat.history.previoussessions"u8), + "Show history from previous sessions", + "Off means the log starts empty each time the game launches and " + + "only fills with messages received since.", + () => Plugin.Config.FilterIncludePreviousSessions, + v => + { + Plugin.Config.FilterIncludePreviousSessions = v; + // Takes effect at once rather than on the next launch: the + // window is open and the user just asked for the history. + _plugin.MessageManager.FilterAllTabs(); + } + ); + } + if (_w.Section(ImGui.GetID("chat.commandhelp"u8), "Command help", open: false)) { _w.EnumComboRow( diff --git a/HellionChat/Ui/StyleEngine/SurfaceBackdrop.cs b/HellionChat/Ui/StyleEngine/SurfaceBackdrop.cs index ab4c223..f4d246a 100644 --- a/HellionChat/Ui/StyleEngine/SurfaceBackdrop.cs +++ b/HellionChat/Ui/StyleEngine/SurfaceBackdrop.cs @@ -57,8 +57,8 @@ internal sealed class SurfaceBackdrop // the full height is exactly the case that bands, because its handful of // alpha steps each cover twenty-odd pixels. var fade = MathF.Min(190f * Metrics.Scale, (max.Y - min.Y) * 0.45f); - dl.DrawEdgeTint(min, max, 0x00FFFFFFu | ((uint)(0x22 * strength * opacity) << 24), fade); - dl.DrawEdgeTint(min, max, (uint)(0x30 * strength * opacity) << 24, fade, fromBottom: true); + dl.DrawEdgeTint(min, max, 0x00FFFFFFu | ((uint)(0x10 * strength * opacity) << 24), fade); + dl.DrawEdgeTint(min, max, (uint)(0x38 * strength * opacity) << 24, fade, fromBottom: true); if (darken > 0f) dl.AddRectFilled(min, max, (uint)(0xFF * darken * opacity) << 24); @@ -69,8 +69,8 @@ internal sealed class SurfaceBackdrop dl.AddRectFilledMultiColor( min, new Vector2(max.X, min.Y + (max.Y - min.Y) * accentWashHeight), - ColourUtil.ApplyAlpha(accent, 0.07f * opacity), - ColourUtil.ApplyAlpha(accent, 0.035f * opacity), + ColourUtil.ApplyAlpha(accent, 0.045f * opacity), + ColourUtil.ApplyAlpha(accent, 0.02f * opacity), accent & 0x00FFFFFFu, accent & 0x00FFFFFFu );