chore(style): add a widget gallery for visual verification
Every widget in its states, reachable via /hellion widgets. The point is to check them one at a time before they land in real components: the v2.x style engine grew three primitives that were never wired to a call site (DrawGlowBorder, DrawSlipPolygon, DrawHonorificHeader), and this is the cheap way to notice that before a cycle closes. DEBUG-only, like SeStringDebugger. It is a verification aid, not a feature, so it never reaches a release build -- verified against a Release compile. The header line also shows the live GlobalScale and the hover registry size, which makes both the scaling work and the eviction contract observable while dragging the Dalamud scale slider.
This commit is contained in:
@@ -109,6 +109,9 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
internal static InputPreview InputPreview { get; private set; } = null!;
|
||||
internal CommandHelpWindow CommandHelpWindow { get; private set; } = null!;
|
||||
public SeStringDebugger SeStringDebugger { get; private set; } = null!;
|
||||
#if DEBUG
|
||||
internal Ui.Windows.WidgetGalleryWindow WidgetGallery { get; private set; } = null!;
|
||||
#endif
|
||||
public FirstRunWizard FirstRunWizard { get; private set; } = null!;
|
||||
internal DebuggerWindow DebuggerWindow { get; private set; } = null!;
|
||||
|
||||
@@ -365,6 +368,9 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
InputPreview = _host.Services.GetRequiredService<InputPreview>();
|
||||
CommandHelpWindow = _host.Services.GetRequiredService<CommandHelpWindow>();
|
||||
SeStringDebugger = _host.Services.GetRequiredService<SeStringDebugger>();
|
||||
#if DEBUG
|
||||
WidgetGallery = _host.Services.GetRequiredService<Ui.Windows.WidgetGalleryWindow>();
|
||||
#endif
|
||||
DebuggerWindow = _host.Services.GetRequiredService<DebuggerWindow>();
|
||||
FirstRunWizard = _host.Services.GetRequiredService<FirstRunWizard>();
|
||||
ChannelPopoutPool = _host.Services.GetRequiredService<Ui.Windows.ChannelPopoutPool>();
|
||||
@@ -925,6 +931,13 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
SettingsWindow.Toggle();
|
||||
return;
|
||||
}
|
||||
#if DEBUG
|
||||
if (arg.Equals("widgets", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
WidgetGallery.Toggle();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (arg.Equals("reset", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Recovery path documented in the v2.x master spec — drops a
|
||||
|
||||
@@ -340,6 +340,12 @@ internal static class PluginHostFactory
|
||||
sp.GetRequiredService<ILogger<CommandHelpWindow>>()
|
||||
));
|
||||
services.AddSingleton(sp => new SeStringDebugger(sp.GetRequiredService<Plugin>()));
|
||||
#if DEBUG
|
||||
services.AddSingleton(sp => new Ui.Windows.WidgetGalleryWindow(
|
||||
sp.GetRequiredService<Plugin>(),
|
||||
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
|
||||
));
|
||||
#endif
|
||||
services.AddSingleton(sp => new DebuggerWindow(
|
||||
sp.GetRequiredService<Plugin>(),
|
||||
sp.GetRequiredService<PayloadHandler>()
|
||||
|
||||
@@ -64,6 +64,9 @@ internal sealed class PluginLifecycle : IAsyncDisposable
|
||||
plugin.WindowSystem.AddWindow(Plugin.InputPreview);
|
||||
plugin.WindowSystem.AddWindow(plugin.CommandHelpWindow);
|
||||
plugin.WindowSystem.AddWindow(plugin.SeStringDebugger);
|
||||
#if DEBUG
|
||||
plugin.WindowSystem.AddWindow(plugin.WidgetGallery);
|
||||
#endif
|
||||
plugin.WindowSystem.AddWindow(plugin.DebuggerWindow);
|
||||
plugin.WindowSystem.AddWindow(plugin.FirstRunWizard);
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
#if DEBUG
|
||||
using System.Numerics;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using HellionChat.Themes;
|
||||
using HellionChat.Ui.StyleEngine;
|
||||
using HellionChat.Ui.StyleEngine.Widgets;
|
||||
|
||||
namespace HellionChat.Ui.Windows;
|
||||
|
||||
// Every widget in Ui/StyleEngine/Widgets shown in its states, so they can be
|
||||
// checked one at a time before they land in real components. The v2.x style
|
||||
// engine grew three primitives that were never wired to a call site
|
||||
// (DrawGlowBorder, DrawSlipPolygon, DrawHonorificHeader) -- this is the cheap
|
||||
// way to notice that before a cycle closes.
|
||||
//
|
||||
// DEBUG-only, like SeStringDebugger: it is a verification aid, not a feature.
|
||||
internal sealed class WidgetGalleryWindow : Window
|
||||
{
|
||||
private readonly Plugin _plugin;
|
||||
private readonly WidgetPalette _palette;
|
||||
|
||||
private int _badgeCount = 3;
|
||||
private bool _rowActive = true;
|
||||
|
||||
internal WidgetGalleryWindow(Plugin plugin, TokenResolver resolver)
|
||||
: base("Widget Gallery###hellion-widget-gallery")
|
||||
{
|
||||
_plugin = plugin;
|
||||
_palette = new WidgetPalette(resolver);
|
||||
Size = new Vector2(460, 560);
|
||||
SizeCondition = ImGuiCond.FirstUseEver;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var c = _plugin.ThemeRegistry.Active.Colors;
|
||||
|
||||
ImGui.TextDisabled(
|
||||
$"GlobalScale {Metrics.Scale:0.00} · hover entries {HoverState.TrackedCount}"
|
||||
);
|
||||
ImGui.Separator();
|
||||
|
||||
DrawRowSection(c);
|
||||
DrawBadgeSection(c);
|
||||
DrawPillSection(c);
|
||||
DrawIconButtonSection(c);
|
||||
DrawDividerSection(c);
|
||||
}
|
||||
|
||||
private void DrawRowSection(ThemeColors c)
|
||||
{
|
||||
ImGui.TextUnformatted("Row");
|
||||
ImGui.Checkbox("active##row", ref _rowActive);
|
||||
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
var height = Metrics.SidebarRowHeight;
|
||||
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var id = ImGui.GetID($"gallery.row.{i}");
|
||||
ImGui.InvisibleButton($"##gallery-row-{i}", new Vector2(width, height));
|
||||
var hovered = ImGui.IsItemHovered();
|
||||
|
||||
Row.Draw(
|
||||
origin,
|
||||
new Vector2(width, height),
|
||||
new RowVisualState
|
||||
{
|
||||
IsActive = _rowActive && i == 1,
|
||||
HoverAmount = HoverState.Query(id, hovered),
|
||||
SurfaceAbgr = _palette.Abgr(Token.SurfaceBase, c),
|
||||
SurfaceHoverAbgr = _palette.Abgr(Token.SurfaceHover, c),
|
||||
SurfaceActiveAbgr = _palette.Abgr(Token.SurfaceActive, c),
|
||||
AccentAbgr = _palette.Abgr(Token.AccentPrimary, c),
|
||||
BorderAbgr = _palette.Abgr(Token.Border, c),
|
||||
}
|
||||
);
|
||||
|
||||
ImGui
|
||||
.GetWindowDrawList()
|
||||
.AddText(
|
||||
origin + new Vector2(12f * Metrics.Scale, Metrics.CenterY(height)),
|
||||
_palette.Abgr(Token.Text, c),
|
||||
$"Row {i}"
|
||||
);
|
||||
}
|
||||
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
private void DrawBadgeSection(ThemeColors c)
|
||||
{
|
||||
ImGui.TextUnformatted("Badge");
|
||||
ImGui.SliderInt("count##badge", ref _badgeCount, 0, 150);
|
||||
|
||||
int[] samples = [1, 9, _badgeCount, 120];
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var x = origin.X;
|
||||
foreach (var n in samples)
|
||||
{
|
||||
Badge.Draw(
|
||||
new Vector2(x, origin.Y),
|
||||
n,
|
||||
_palette.Abgr(Token.AccentEmber, c),
|
||||
_palette.Abgr(Token.Text, c)
|
||||
);
|
||||
x += Badge.CalcSize(n).X + 8f * Metrics.Scale;
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(0, Badge.CalcSize(1).Y + 8f * Metrics.Scale));
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
private void DrawPillSection(ThemeColors c)
|
||||
{
|
||||
ImGui.TextUnformatted("Pill");
|
||||
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var x = origin.X;
|
||||
|
||||
Pill.Draw(
|
||||
new Vector2(x, origin.Y),
|
||||
"filled",
|
||||
_palette.Abgr(Token.SurfaceRaised, c),
|
||||
_palette.Abgr(Token.Text, c)
|
||||
);
|
||||
x += Pill.CalcSize("filled", withDot: false).X + 8f * Metrics.Scale;
|
||||
|
||||
Pill.Draw(
|
||||
new Vector2(x, origin.Y),
|
||||
"with dot",
|
||||
_palette.Abgr(Token.SurfaceRaised, c),
|
||||
_palette.Abgr(Token.Text, c),
|
||||
_palette.Abgr(Token.AccentPrimary, c)
|
||||
);
|
||||
x += Pill.CalcSize("with dot", withDot: true).X + 8f * Metrics.Scale;
|
||||
|
||||
Pill.Draw(
|
||||
new Vector2(x, origin.Y),
|
||||
"outlined",
|
||||
_palette.Abgr(Token.AccentPrimary, c),
|
||||
_palette.Abgr(Token.TextMuted, c),
|
||||
styleOverride: new PillStyle { Outlined = true }
|
||||
);
|
||||
|
||||
ImGui.Dummy(new Vector2(0, Pill.CalcSize("x", false).Y + 8f * Metrics.Scale));
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
private void DrawIconButtonSection(ThemeColors c)
|
||||
{
|
||||
ImGui.TextUnformatted("IconButton");
|
||||
|
||||
var size = new Vector2(Metrics.SidebarPopOutHitWidth, Metrics.SidebarRowHeight);
|
||||
FontAwesomeIcon[] glyphs =
|
||||
[
|
||||
FontAwesomeIcon.ArrowUpRightFromSquare,
|
||||
FontAwesomeIcon.Check,
|
||||
FontAwesomeIcon.CheckCircle,
|
||||
];
|
||||
|
||||
for (var i = 0; i < glyphs.Length; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
ImGui.SameLine();
|
||||
|
||||
IconButton.Draw(
|
||||
ImGui.GetID($"gallery.icon.{i}"),
|
||||
size,
|
||||
glyphs[i],
|
||||
_palette.Abgr(Token.TextMuted, c),
|
||||
_palette.Abgr(Token.SurfaceHover, c),
|
||||
_plugin.FontManager.FontAwesome
|
||||
);
|
||||
}
|
||||
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
private void DrawDividerSection(ThemeColors c)
|
||||
{
|
||||
ImGui.TextUnformatted("LineDivider");
|
||||
|
||||
LineDivider.Draw(
|
||||
"Section (2)",
|
||||
_palette.Abgr(Token.Border, c),
|
||||
_palette.Abgr(Token.TextMuted, c)
|
||||
);
|
||||
LineDivider.Draw(null, _palette.Abgr(Token.Border, c), _palette.Abgr(Token.TextMuted, c));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user