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<Plugin>(),
|
||||||
sp.GetRequiredService<FontManager>()
|
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(
|
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AppearanceTab(
|
||||||
sp.GetRequiredService<Ui.Components.Settings.ThemePicker>(),
|
sp.GetRequiredService<Ui.Components.Settings.ThemePicker>(),
|
||||||
sp.GetRequiredService<Ui.Components.Settings.ColorPicker>(),
|
sp.GetRequiredService<Ui.Components.Settings.ColorPicker>(),
|
||||||
sp.GetRequiredService<Ui.Components.Settings.LivePreviewPanel>(),
|
sp.GetRequiredService<Ui.Components.Settings.LivePreviewPanel>(),
|
||||||
sp.GetRequiredService<Ui.Components.Settings.ThemeImportExportRow>(),
|
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(
|
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.GeneralTab(
|
||||||
sp.GetRequiredService<Plugin>()
|
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 LivePreviewPanel _preview;
|
||||||
private readonly ThemeImportExportRow _importExport;
|
private readonly ThemeImportExportRow _importExport;
|
||||||
private readonly FontsSection _fonts;
|
private readonly FontsSection _fonts;
|
||||||
|
private readonly ChatColourPicker _chatColours;
|
||||||
|
|
||||||
public AppearanceTab(
|
public AppearanceTab(
|
||||||
ThemePicker picker,
|
ThemePicker picker,
|
||||||
ColorPicker color,
|
ColorPicker color,
|
||||||
LivePreviewPanel preview,
|
LivePreviewPanel preview,
|
||||||
ThemeImportExportRow importExport,
|
ThemeImportExportRow importExport,
|
||||||
FontsSection fonts
|
FontsSection fonts,
|
||||||
|
ChatColourPicker chatColours
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_picker = picker;
|
_picker = picker;
|
||||||
@@ -26,6 +28,7 @@ internal sealed class AppearanceTab
|
|||||||
_preview = preview;
|
_preview = preview;
|
||||||
_importExport = importExport;
|
_importExport = importExport;
|
||||||
_fonts = fonts;
|
_fonts = fonts;
|
||||||
|
_chatColours = chatColours;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
@@ -38,12 +41,15 @@ internal sealed class AppearanceTab
|
|||||||
if (left.Success)
|
if (left.Success)
|
||||||
{
|
{
|
||||||
_picker.Draw();
|
_picker.Draw();
|
||||||
|
_chatColours.DrawThemeAdoptBanner();
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
_importExport.Draw();
|
_importExport.Draw();
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
_fonts.Draw();
|
_fonts.Draw();
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
_color.Draw();
|
_color.Draw();
|
||||||
|
ImGui.Separator();
|
||||||
|
_chatColours.Draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|||||||
Reference in New Issue
Block a user