add theme override customizations

This commit is contained in:
azyges
2025-10-11 03:29:44 +09:00
parent 9b04976aa6
commit 2a9b5812ed
6 changed files with 526 additions and 162 deletions

View File

@@ -18,6 +18,7 @@ using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.UI.Style;
using LightlessSync.Utils;
using LightlessSync.UtilsEnum.Enum;
using LightlessSync.WebAPI;
@@ -44,6 +45,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
private readonly ApiController _apiController;
private readonly CacheMonitor _cacheMonitor;
private readonly LightlessConfigService _configService;
private readonly UiThemeConfigService _themeConfigService;
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
private readonly DalamudUtilService _dalamudUtilService;
private readonly HttpClient _httpClient;
@@ -94,7 +96,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
private bool _wasOpen = false;
public SettingsUi(ILogger<SettingsUi> logger,
UiSharedService uiShared, LightlessConfigService configService,
UiSharedService uiShared, LightlessConfigService configService, UiThemeConfigService themeConfigService,
PairManager pairManager,
ServerConfigurationManager serverConfigurationManager,
PlayerPerformanceConfigService playerPerformanceConfigService,
@@ -110,6 +112,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
NameplateHandler nameplateHandler) : base(logger, mediator, "Lightless Sync Settings", performanceCollector)
{
_configService = configService;
_themeConfigService = themeConfigService;
_pairManager = pairManager;
_serverConfigurationManager = serverConfigurationManager;
_playerPerformanceConfigService = playerPerformanceConfigService;
@@ -241,6 +244,253 @@ public class SettingsUi : WindowMediatorSubscriberBase
return ((color & 0xFFu) << 16) | (color & 0xFF00u) | ((color >> 16) & 0xFFu);
}
private static Vector4 PackedThemeColorToVector4(uint packed)
=> new(
(packed & 0xFF) / 255f,
((packed >> 8) & 0xFF) / 255f,
((packed >> 16) & 0xFF) / 255f,
((packed >> 24) & 0xFF) / 255f);
private static uint ThemeVector4ToPackedColor(Vector4 color)
{
static byte ToByte(float channel)
{
var scaled = MathF.Round(Math.Clamp(channel, 0f, 1f) * 255.0f);
return (byte)Math.Clamp((int)scaled, 0, 255);
}
var r = ToByte(color.X);
var g = ToByte(color.Y);
var b = ToByte(color.Z);
var a = ToByte(color.W);
return (uint)(r | (g << 8) | (b << 16) | (a << 24));
}
private void UpdateStyleOverride(string key, Action<UiStyleOverride> updater)
{
var overrides = _themeConfigService.Current.StyleOverrides;
if (!overrides.TryGetValue(key, out var entry))
entry = new UiStyleOverride();
updater(entry);
if (entry.IsEmpty)
overrides.Remove(key);
else
overrides[key] = entry;
_themeConfigService.Save();
}
private void DrawThemeOverridesSection()
{
ImGui.TextUnformatted("Lightless Theme Overrides");
_uiShared.DrawHelpText("Adjust the Lightless redesign theme. Overrides only apply when the redesign is enabled.");
if (!_configService.Current.UseLightlessRedesign)
UiSharedService.ColorTextWrapped("The Lightless redesign is currently disabled. Enable it to see these changes take effect.", UIColors.Get("DimRed"));
const ImGuiTableFlags flags = ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp;
if (!ImGui.BeginTable("##ThemeOverridesTable", 3, flags))
return;
ImGui.TableSetupColumn("Element", ImGuiTableColumnFlags.WidthFixed, 325f);
ImGui.TableSetupColumn("Value", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("Reset", ImGuiTableColumnFlags.WidthFixed, 70f);
ImGui.TableHeadersRow();
DrawThemeCategoryRow("Colors");
foreach (var option in MainStyle.ColorOptions)
DrawThemeColorRow(option);
DrawThemeCategoryRow("Spacing & Padding");
foreach (var option in MainStyle.Vector2Options)
DrawThemeVectorRow(option);
DrawThemeCategoryRow("Rounding & Sizes");
foreach (var option in MainStyle.FloatOptions)
DrawThemeFloatRow(option);
ImGui.EndTable();
}
private static void DrawThemeCategoryRow(string label)
{
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.TextColored(UIColors.Get("LightlessPurple"), label);
ImGui.TableSetColumnIndex(1);
ImGui.TableSetColumnIndex(2);
}
private void DrawThemeColorRow(MainStyle.StyleColorOption option)
{
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.TextUnformatted(option.Label);
bool showTooltip = ImGui.IsItemHovered();
var tooltip = string.Empty;
if (!string.IsNullOrEmpty(option.Description))
tooltip = option.Description;
var overrides = _themeConfigService.Current.StyleOverrides;
overrides.TryGetValue(option.Key, out var existing);
if (!string.IsNullOrEmpty(option.UiColorKey))
{
if (!string.IsNullOrEmpty(tooltip))
tooltip += "\n";
tooltip += $"Default uses UIColors[\"{option.UiColorKey}\"]";
ImGui.SameLine();
ImGui.TextDisabled($"(UIColors.{option.UiColorKey})");
if (ImGui.IsItemHovered())
showTooltip = true;
}
ImGui.TableSetColumnIndex(2);
if (DrawStyleResetButton(option.Key, existing?.Color is not null))
{
UpdateStyleOverride(option.Key, entry =>
{
entry.Color = null;
entry.Float = null;
entry.Vector2 = null;
});
existing = null;
}
if (showTooltip && !string.IsNullOrEmpty(tooltip))
ImGui.SetTooltip(tooltip);
var defaultColor = MainStyle.NormalizeColorVector(option.DefaultValue());
var current = existing?.Color is { } packed ? PackedThemeColorToVector4(packed) : defaultColor;
var edit = current;
ImGui.TableSetColumnIndex(1);
if (ImGui.ColorEdit4($"##theme-color-{option.Key}", ref edit, ImGuiColorEditFlags.AlphaPreviewHalf))
{
UpdateStyleOverride(option.Key, entry =>
{
entry.Color = ThemeVector4ToPackedColor(edit);
entry.Float = null;
entry.Vector2 = null;
});
}
}
private void DrawThemeVectorRow(MainStyle.StyleVector2Option option)
{
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.TextUnformatted(option.Label);
if (!string.IsNullOrEmpty(option.Description) && ImGui.IsItemHovered())
ImGui.SetTooltip(option.Description);
var overrides = _themeConfigService.Current.StyleOverrides;
overrides.TryGetValue(option.Key, out var existing);
ImGui.TableSetColumnIndex(2);
if (DrawStyleResetButton(option.Key, existing?.Vector2 is not null))
{
UpdateStyleOverride(option.Key, entry =>
{
entry.Vector2 = null;
entry.Color = null;
entry.Float = null;
});
existing = null;
}
var defaultValue = option.DefaultValue();
var current = existing?.Vector2 is { } vectorOverride ? (Vector2)vectorOverride : defaultValue;
var edit = current;
ImGui.TableSetColumnIndex(1);
if (ImGui.DragFloat2($"##theme-vector-{option.Key}", ref edit, option.Speed))
{
if (option.Min is { } min)
edit = Vector2.Max(edit, min);
if (option.Max is { } max)
edit = Vector2.Min(edit, max);
UpdateStyleOverride(option.Key, entry =>
{
entry.Vector2 = new Vector2Config(edit.X, edit.Y);
entry.Color = null;
entry.Float = null;
});
}
}
private void DrawThemeFloatRow(MainStyle.StyleFloatOption option)
{
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.TextUnformatted(option.Label);
if (!string.IsNullOrEmpty(option.Description) && ImGui.IsItemHovered())
ImGui.SetTooltip(option.Description);
var overrides = _themeConfigService.Current.StyleOverrides;
overrides.TryGetValue(option.Key, out var existing);
ImGui.TableSetColumnIndex(2);
if (DrawStyleResetButton(option.Key, existing?.Float is not null))
{
UpdateStyleOverride(option.Key, entry =>
{
entry.Float = null;
entry.Color = null;
entry.Vector2 = null;
});
existing = null;
}
var current = existing?.Float ?? option.DefaultValue;
var edit = current;
var min = option.Min ?? float.MinValue;
var max = option.Max ?? float.MaxValue;
ImGui.TableSetColumnIndex(1);
if (ImGui.DragFloat($"##theme-float-{option.Key}", ref edit, option.Speed, min, max, "%.2f"))
{
if (option.Min.HasValue)
edit = MathF.Max(option.Min.Value, edit);
if (option.Max.HasValue)
edit = MathF.Min(option.Max.Value, edit);
UpdateStyleOverride(option.Key, entry =>
{
entry.Float = edit;
entry.Color = null;
entry.Vector2 = null;
});
}
}
private bool DrawStyleResetButton(string key, bool hasOverride, string? tooltipOverride = null)
{
using var id = ImRaii.PushId($"reset-{key}");
using var disabled = ImRaii.Disabled(!hasOverride);
var availableWidth = ImGui.GetContentRegionAvail().X;
bool pressed = false;
using (ImRaii.PushFont(UiBuilder.IconFont))
{
if (ImGui.Button(FontAwesomeIcon.Undo.ToIconString(), new Vector2(availableWidth, 0)))
pressed = true;
}
var tooltip = tooltipOverride ?? (hasOverride
? "Reset this style override to its default value."
: "Value already matches the default.");
UiSharedService.AttachToolTip(tooltip);
return pressed;
}
private void DrawBlockedTransfers()
{
_lastTab = "BlockedTransfers";
@@ -1642,6 +1892,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
}
_uiShared.DrawHelpText("This changes the vanity colored UID's in pair list.");
DrawThemeOverridesSection();
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGui.TreePop();
}