diff --git a/HellionChat/PluginHostFactory.cs b/HellionChat/PluginHostFactory.cs index 09186f2..e26d9aa 100644 --- a/HellionChat/PluginHostFactory.cs +++ b/HellionChat/PluginHostFactory.cs @@ -177,12 +177,17 @@ internal static class PluginHostFactory sp.GetRequiredService(), sp.GetRequiredService() )); + services.AddSingleton(sp => new Ui.Components.Settings.ChatColourPicker( + sp.GetRequiredService(), + sp.GetRequiredService() + )); services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AppearanceTab( sp.GetRequiredService(), sp.GetRequiredService(), sp.GetRequiredService(), sp.GetRequiredService(), - sp.GetRequiredService() + sp.GetRequiredService(), + sp.GetRequiredService() )); services.AddSingleton(sp => new Ui.Components.Settings.Tabs.GeneralTab( sp.GetRequiredService() diff --git a/HellionChat/Ui/Components/Settings/ChatColourPicker.cs b/HellionChat/Ui/Components/Settings/ChatColourPicker.cs new file mode 100644 index 0000000..62f1e24 --- /dev/null +++ b/HellionChat/Ui/Components/Settings/ChatColourPicker.cs @@ -0,0 +1,242 @@ +using System.Numerics; +using Dalamud.Bindings.ImGui; +using Dalamud.Interface; +using Dalamud.Interface.Utility.Raii; +using HellionChat.Code; +using HellionChat.Resources; +using HellionChat.Themes; +using HellionChat.Util; + +namespace HellionChat.Ui.Components.Settings; + +// Restores the 1.5.6 chat-channel colour editor (1d3b429:Appearance.cs:189-243, +// 409-534): presets, the per-ChatType ColorEdit3 over Config.ChatColours (which +// ChunkRenderer consumes), reset/import-game-colour buttons, and the apply-banner +// that adopts the active theme's chatChannels. v1.6.0 saves live + refreshes cache. +internal sealed class ChatColourPicker +{ + private readonly Plugin _plugin; + private readonly ThemeRegistry _themes; + private string? _applyDismissedFor; + private string? _lastSeenSlug; + + public ChatColourPicker(Plugin plugin, ThemeRegistry themes) + { + _plugin = plugin; + _themes = themes; + } + + // Drawn directly under the theme picker (1.5.6 placement) so the adopt prompt + // sits next to the theme that triggered it, not at the bottom of the tab. + public void DrawThemeAdoptBanner() => DrawApplyBanner(_themes.Active); + + public void Draw() + { + if (!ImGui.CollapsingHeader(HellionStrings.Settings_Section_Colours)) + return; + + DrawPresetButtons(); + ImGui.TextDisabled(HellionStrings.Settings_Appearance_Colours_PresetsHint); + ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + + if ( + ImGui.Checkbox( + Language.Options_ColorSelectedInputChannelButton_Name, + ref Plugin.Config.ColorSelectedInputChannelButton + ) + ) + { + _plugin.SaveConfig(); + } + ImGuiUtil.HelpMarker(Language.Options_ColorSelectedInputChannelButton_Description); + ImGui.Spacing(); + + // Discrete clicks (reset/import) persist at once. The ColorEdit3 drag only + // recolours live (Refresh, no disk write) and defers SaveConfig to release + // via IsItemDeactivatedAfterEdit, so dragging the colour wheel doesn't fire + // a full-config disk write every frame. + var commit = false; + var liveOnly = false; + foreach (var (_, types) in ChatTypeExt.SortOrder) + { + foreach (var type in types) + { + if ( + ImGuiUtil.IconButton( + FontAwesomeIcon.UndoAlt, + $"{type}", + Language.Options_ChatColours_Reset + ) + ) + { + Plugin.Config.ChatColours.Remove(type); + commit = true; + } + + ImGui.SameLine(); + + if ( + ImGuiUtil.IconButton( + FontAwesomeIcon.LongArrowAltDown, + $"{type}", + Language.Options_ChatColours_Import + ) + ) + { + var gameColour = _plugin.Functions.Chat.GetChannelColor(type); + Plugin.Config.ChatColours[type] = gameColour ?? type.DefaultColor() ?? 0; + commit = true; + } + + ImGui.SameLine(); + + var vec = Plugin.Config.ChatColours.TryGetValue(type, out var colour) + ? ColourUtil.RgbaToVector3(colour) + : ColourUtil.RgbaToVector3(type.DefaultColor() ?? 0); + if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs)) + { + Plugin.Config.ChatColours[type] = ColourUtil.Vector3ToRgba(vec); + liveOnly = true; + } + if (ImGui.IsItemDeactivatedAfterEdit()) + commit = true; + } + } + + if (commit) + ApplyChatColourChange(); + else if (liveOnly) + GlobalParametersCache.Refresh(); + + ImGui.Spacing(); + } + + private void DrawPresetButtons() + { + var first = true; + foreach (var (_, preset) in ChatColourPresets.All) + { + if (!first) + ImGui.SameLine(); + first = false; + + var brand = preset.IsBrandPreset; + if (brand) + { + var border = ColourUtil.RgbaToVector3(ColourUtil.ComponentsToRgba(255, 128, 200)); + var btn = ColourUtil.RgbaToVector3(ColourUtil.ComponentsToRgba(74, 42, 106)); + ImGui.PushStyleColor(ImGuiCol.Border, new Vector4(border, 1f)); + ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(btn, 1f)); + ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1.5f); + } + + if (ImGui.Button(GetPresetLabel(preset))) + ApplyPreset(preset); + + if (brand) + { + ImGui.PopStyleVar(); + ImGui.PopStyleColor(2); + } + } + } + + private static string GetPresetLabel(ChatColourPreset preset) + { + var localized = HellionStrings.ResourceManager.GetString( + preset.LocalizationKey, + HellionStrings.Culture + ); + return string.IsNullOrEmpty(localized) ? preset.DisplayName : localized; + } + + private void ApplyPreset(ChatColourPreset preset) + { + foreach (var (channel, colour) in preset.Colours) + Plugin.Config.ChatColours[channel] = colour; + ApplyChatColourChange(); + } + + private void ApplyChatColourChange() + { + _plugin.SaveConfig(); + GlobalParametersCache.Refresh(); + } + + // Offers to adopt the active theme's chatChannels into Config.ChatColours when + // they differ; dismissable per theme slug (matches 1.5.6 banner behaviour). + private void DrawApplyBanner(Theme active) + { + // Clear the per-theme dismiss whenever the active theme changes, so leaving + // a theme and returning re-offers the prompt (1.5.6 reset this on every switch). + if (active.Slug != _lastSeenSlug) + { + _applyDismissedFor = null; + _lastSeenSlug = active.Slug; + } + + if (active.ChatColors is not { Channels.Count: > 0 } themeChatColors) + return; + if (_applyDismissedFor == active.Slug) + return; + + var alreadyMatching = themeChatColors.Channels.All(kvp => + Plugin.Config.ChatColours.TryGetValue(kvp.Key, out var current) && current == kvp.Value + ); + if (alreadyMatching) + return; + + ImGui.Spacing(); + var border = ColourUtil.RgbaToAbgr(active.Colors.Primary); + var bgFill = ColourUtil.RgbaToAbgr((active.Colors.Surface & 0xFFFFFF00u) | 0xCCu); + var origin = ImGui.GetCursorScreenPos(); + var width = ImGui.GetContentRegionAvail().X; + const float height = 64f; + var draw = ImGui.GetWindowDrawList(); + draw.AddRectFilled(origin, origin + new Vector2(width, height), bgFill, 4f); + draw.AddRect(origin, origin + new Vector2(width, height), border, 4f, ImDrawFlags.None, 1f); + draw.AddText( + origin + new Vector2(12f, 10f), + ColourUtil.RgbaToAbgr(active.Colors.TextPrimary), + HellionStrings.Settings_Themes_ApplyChatColors_Hint + ); + + using ( + ImRaii.PushColor( + ImGuiCol.Button, + new Vector4(ColourUtil.RgbaToVector3(active.Colors.Primary), 1f) + ) + ) + using ( + ImRaii.PushColor( + ImGuiCol.ButtonHovered, + new Vector4(ColourUtil.RgbaToVector3(active.Colors.PrimaryLight), 1f) + ) + ) + using ( + ImRaii.PushColor( + ImGuiCol.ButtonActive, + new Vector4(ColourUtil.RgbaToVector3(active.Colors.PrimaryDark), 1f) + ) + ) + { + ImGui.SetCursorScreenPos(origin + new Vector2(12f, 32f)); + if (ImGui.Button(HellionStrings.Settings_Themes_ApplyChatColors_Apply)) + { + foreach (var kvp in themeChatColors.Channels) + Plugin.Config.ChatColours[kvp.Key] = kvp.Value; + _applyDismissedFor = active.Slug; + ApplyChatColourChange(); + } + } + + ImGui.SameLine(); + if (ImGui.Button(HellionStrings.Settings_Themes_ApplyChatColors_Keep)) + _applyDismissedFor = active.Slug; + + ImGui.SetCursorScreenPos(origin + new Vector2(0f, height + 8f)); + ImGui.Spacing(); + } +} diff --git a/HellionChat/Ui/Components/Settings/Tabs/AppearanceTab.cs b/HellionChat/Ui/Components/Settings/Tabs/AppearanceTab.cs index 5797e79..b0035ed 100644 --- a/HellionChat/Ui/Components/Settings/Tabs/AppearanceTab.cs +++ b/HellionChat/Ui/Components/Settings/Tabs/AppearanceTab.cs @@ -12,13 +12,15 @@ internal sealed class AppearanceTab private readonly LivePreviewPanel _preview; private readonly ThemeImportExportRow _importExport; private readonly FontsSection _fonts; + private readonly ChatColourPicker _chatColours; public AppearanceTab( ThemePicker picker, ColorPicker color, LivePreviewPanel preview, ThemeImportExportRow importExport, - FontsSection fonts + FontsSection fonts, + ChatColourPicker chatColours ) { _picker = picker; @@ -26,6 +28,7 @@ internal sealed class AppearanceTab _preview = preview; _importExport = importExport; _fonts = fonts; + _chatColours = chatColours; } public void Draw() @@ -38,12 +41,15 @@ internal sealed class AppearanceTab if (left.Success) { _picker.Draw(); + _chatColours.DrawThemeAdoptBanner(); ImGui.Spacing(); _importExport.Draw(); ImGui.Separator(); _fonts.Draw(); ImGui.Separator(); _color.Draw(); + ImGui.Separator(); + _chatColours.Draw(); } } ImGui.SameLine();