feat(themes): restore the chat-channel colour editor in the appearance tab
Brings back the 1.5.6 per-channel colour editor lost in the v1.6.0 rewrite: presets (incl. brand styling), per-ChatType reset/import-from-game/colour-edit over Config.ChatColours, the colour-selected-input-channel toggle, and the theme adopt-banner. The colour-edit drag recolours live but defers SaveConfig to release (IsItemDeactivatedAfterEdit) to avoid a per-frame full-config write.
This commit is contained in:
@@ -177,12 +177,17 @@ internal static class PluginHostFactory
|
||||
sp.GetRequiredService<Plugin>(),
|
||||
sp.GetRequiredService<FontManager>()
|
||||
));
|
||||
services.AddSingleton(sp => new Ui.Components.Settings.ChatColourPicker(
|
||||
sp.GetRequiredService<Plugin>(),
|
||||
sp.GetRequiredService<ThemeRegistry>()
|
||||
));
|
||||
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AppearanceTab(
|
||||
sp.GetRequiredService<Ui.Components.Settings.ThemePicker>(),
|
||||
sp.GetRequiredService<Ui.Components.Settings.ColorPicker>(),
|
||||
sp.GetRequiredService<Ui.Components.Settings.LivePreviewPanel>(),
|
||||
sp.GetRequiredService<Ui.Components.Settings.ThemeImportExportRow>(),
|
||||
sp.GetRequiredService<Ui.Components.Settings.FontsSection>()
|
||||
sp.GetRequiredService<Ui.Components.Settings.FontsSection>(),
|
||||
sp.GetRequiredService<Ui.Components.Settings.ChatColourPicker>()
|
||||
));
|
||||
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.GeneralTab(
|
||||
sp.GetRequiredService<Plugin>()
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user