feat(settings): add ColorPicker skeleton with lifecycle
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
using System.Numerics;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using HellionChat.Themes;
|
||||
using HellionChat.Util;
|
||||
|
||||
namespace HellionChat.Ui.Components.Settings;
|
||||
|
||||
internal sealed class ColorPicker
|
||||
{
|
||||
private readonly ThemeRegistry _themes;
|
||||
|
||||
public ColorPicker(ThemeRegistry themes)
|
||||
{
|
||||
_themes = themes;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (_themes.EditingThemeBuffer is null)
|
||||
{
|
||||
DrawIdleState();
|
||||
return;
|
||||
}
|
||||
|
||||
DrawEditState(_themes.EditingThemeBuffer);
|
||||
}
|
||||
|
||||
private void DrawIdleState()
|
||||
{
|
||||
var active = _themes.Active;
|
||||
ImGui.TextDisabled($"Active theme: {active.Name}");
|
||||
|
||||
// Fork built-ins before editing: Switch() prefers built-in slugs over custom
|
||||
// files with the same slug, so an in-place edit would silently no-op.
|
||||
if (active.IsBuiltIn)
|
||||
{
|
||||
if (ImGui.Button("Fork & Edit"))
|
||||
{
|
||||
ForkAndBeginEditing(active);
|
||||
}
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.SetTooltip(
|
||||
"Built-in themes cannot be edited in place. Fork creates a custom copy you can edit and save."
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui.Button("Edit theme"))
|
||||
{
|
||||
_themes.BeginEditing(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ForkAndBeginEditing(Theme source)
|
||||
{
|
||||
// 100 attempts is already absurd for one base slug; past that means the
|
||||
// themes folder is broken, not a real user collision.
|
||||
var newSlug = $"{source.Slug}_fork";
|
||||
var attempt = 2;
|
||||
const int MaxAttempts = 100;
|
||||
while (_themes.TryGet(newSlug, out _))
|
||||
{
|
||||
if (attempt > MaxAttempts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
newSlug = $"{source.Slug}_fork_{attempt++}";
|
||||
}
|
||||
|
||||
var forked = source with
|
||||
{
|
||||
Slug = newSlug,
|
||||
Name = $"{source.Name} (fork)",
|
||||
IsBuiltIn = false,
|
||||
};
|
||||
// The `with`-clone leaves AbgrCache empty (private setter outside primary ctor).
|
||||
// OK here because EditingBuffer render path goes through TokenResolver.Resolve(token, buffer.Colors),
|
||||
// not AbgrCache. Save triggers Switch() which recomputes for the active theme.
|
||||
_themes.BeginEditing(forked);
|
||||
}
|
||||
|
||||
private void DrawEditState(Theme buffer)
|
||||
{
|
||||
ImGui.TextUnformatted($"Editing: {buffer.Name}");
|
||||
ImGui.Separator();
|
||||
|
||||
// Sections land in Step 2; placeholder keeps the skeleton compiling.
|
||||
ImGui.TextDisabled("(sections land in next sub-step)");
|
||||
|
||||
ImGui.Separator();
|
||||
DrawActionButtons(buffer);
|
||||
}
|
||||
|
||||
private void DrawActionButtons(Theme buffer)
|
||||
{
|
||||
if (ImGui.Button("Save"))
|
||||
{
|
||||
_themes.SaveEditingBuffer(out _);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Cancel"))
|
||||
{
|
||||
_themes.DiscardEditingBuffer();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
||||
// Reset is disabled during a fork edit: the active theme is still the built-in
|
||||
// source until Save, so BeginEditing(Active) would throw away the fork's slug/name
|
||||
// framing and effectively cancel the fork. Proper Reset-during-fork needs a tracked
|
||||
// _editingSource field in ThemeRegistry (out of v1.7.0 scope).
|
||||
var isForkBuffer = !buffer.IsBuiltIn && _themes.Active.Slug != buffer.Slug;
|
||||
using (ImRaii.Disabled(isForkBuffer))
|
||||
{
|
||||
if (ImGui.Button("Reset to source"))
|
||||
{
|
||||
_themes.BeginEditing(_themes.Active);
|
||||
}
|
||||
}
|
||||
if (isForkBuffer && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||
{
|
||||
ImGui.SetTooltip("Reset is unavailable while editing a fork. Save or Cancel first.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user