feat(settings): render six mock elements in live preview
This commit is contained in:
@@ -4,6 +4,7 @@ using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using HellionChat.Themes;
|
||||
using HellionChat.Ui.StyleEngine;
|
||||
using HellionChat.Util;
|
||||
|
||||
namespace HellionChat.Ui.Components.Settings;
|
||||
|
||||
@@ -14,6 +15,20 @@ internal sealed class LivePreviewPanel : IDisposable
|
||||
// higher signals a Dispose skip and a subscriber leak against ThemeRegistry.
|
||||
internal static int InstanceCount;
|
||||
|
||||
// Plan-mandated mock strings — international tester-ready, do not localise.
|
||||
private const string MockSystem = "System: Connection established";
|
||||
private const string MockSay = "Say: Hello, world!";
|
||||
private const string MockTell = "Tell → Player: Hey, want to party?";
|
||||
private const string MockFc = "FC: Welcome aboard.";
|
||||
|
||||
// FontAwesome is intentionally not pulled in — crown/cog render as Unicode
|
||||
// glyphs in the default font so this panel stays DI-light (Step 2 scope).
|
||||
private const string CrownGlyph = "♛";
|
||||
private const string CogGlyph = "⚙";
|
||||
|
||||
private const float MiddleBandHeight = 220f;
|
||||
private const float SidebarWidth = 70f;
|
||||
|
||||
private readonly ThemeRegistry _themes;
|
||||
private readonly TokenResolver _resolver;
|
||||
|
||||
@@ -49,40 +64,256 @@ internal sealed class LivePreviewPanel : IDisposable
|
||||
|
||||
DrawBrandBar(theme);
|
||||
DrawHonorificHeader(theme);
|
||||
// Sidebar paints the left strip without advancing the cursor; the
|
||||
// MessageList paints the right strip and reserves the full band.
|
||||
DrawSidebar(theme);
|
||||
DrawMessageList(theme);
|
||||
DrawInputBar(theme);
|
||||
DrawStatusBar(theme);
|
||||
}
|
||||
|
||||
private void DrawBrandBar(Theme theme)
|
||||
private static void DrawBrandBar(Theme theme)
|
||||
{
|
||||
// Mini gradient: PrimaryDark → Primary → PrimaryLight → PrimaryGlow.
|
||||
// Implementation lands in next sub-step.
|
||||
const float height = 24f;
|
||||
var draw = ImGui.GetWindowDrawList();
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
var max = new Vector2(origin.X + width, origin.Y + height);
|
||||
|
||||
// Horizontal gradient split into three rects so all four primary
|
||||
// slots (PrimaryDark/Primary/PrimaryLight/PrimaryGlow) drive the bar.
|
||||
var pdAbgr = ColourUtil.RgbaToAbgr(theme.Colors.PrimaryDark);
|
||||
var pAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Primary);
|
||||
var plAbgr = ColourUtil.RgbaToAbgr(theme.Colors.PrimaryLight);
|
||||
var pgAbgr = ColourUtil.RgbaToAbgr(theme.Colors.PrimaryGlow);
|
||||
|
||||
var third = width / 3f;
|
||||
draw.AddRectFilledMultiColor(
|
||||
origin,
|
||||
new Vector2(origin.X + third, max.Y),
|
||||
pdAbgr,
|
||||
pAbgr,
|
||||
pAbgr,
|
||||
pdAbgr
|
||||
);
|
||||
draw.AddRectFilledMultiColor(
|
||||
new Vector2(origin.X + third, origin.Y),
|
||||
new Vector2(origin.X + 2 * third, max.Y),
|
||||
pAbgr,
|
||||
plAbgr,
|
||||
plAbgr,
|
||||
pAbgr
|
||||
);
|
||||
draw.AddRectFilledMultiColor(
|
||||
new Vector2(origin.X + 2 * third, origin.Y),
|
||||
max,
|
||||
plAbgr,
|
||||
pgAbgr,
|
||||
pgAbgr,
|
||||
plAbgr
|
||||
);
|
||||
|
||||
var label = "HellionChat";
|
||||
var textSize = ImGui.CalcTextSize(label);
|
||||
var textPos = new Vector2(
|
||||
origin.X + (width - textSize.X) * 0.5f,
|
||||
origin.Y + (height - textSize.Y) * 0.5f
|
||||
);
|
||||
draw.AddText(textPos, ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary), label);
|
||||
|
||||
ImGui.Dummy(new Vector2(width, height));
|
||||
}
|
||||
|
||||
private void DrawHonorificHeader(Theme theme)
|
||||
private static void DrawHonorificHeader(Theme theme)
|
||||
{
|
||||
// Crown + Brackets-Title in Identity. Implementation in next sub-step.
|
||||
const float height = 32f;
|
||||
var draw = ImGui.GetWindowDrawList();
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
|
||||
draw.AddLine(
|
||||
origin,
|
||||
new Vector2(origin.X + width, origin.Y),
|
||||
ColourUtil.RgbaToAbgr(theme.Colors.Border),
|
||||
1f
|
||||
);
|
||||
|
||||
var crownAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Identity);
|
||||
var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
|
||||
var title = "«Champion» Preview";
|
||||
var crownSize = ImGui.CalcTextSize(CrownGlyph);
|
||||
var titleSize = ImGui.CalcTextSize(title);
|
||||
var totalWidth = crownSize.X + 4f + titleSize.X;
|
||||
var startX = origin.X + (width - totalWidth) * 0.5f;
|
||||
var y = origin.Y + (height - titleSize.Y) * 0.5f;
|
||||
draw.AddText(new Vector2(startX, y), crownAbgr, CrownGlyph);
|
||||
draw.AddText(new Vector2(startX + crownSize.X + 4f, y), textAbgr, title);
|
||||
|
||||
ImGui.Dummy(new Vector2(width, height));
|
||||
}
|
||||
|
||||
private void DrawSidebar(Theme theme)
|
||||
private static void DrawSidebar(Theme theme)
|
||||
{
|
||||
// 3 channel rows. Implementation in next sub-step.
|
||||
// Paints the left strip of the horizontal middle band. MessageList
|
||||
// consumes the cursor reservation for the full band height.
|
||||
var draw = ImGui.GetWindowDrawList();
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var rowHeight = MiddleBandHeight / 3f;
|
||||
var surface = ColourUtil.RgbaToAbgr(theme.Colors.Surface);
|
||||
var surfaceHover = ColourUtil.RgbaToAbgr(theme.Colors.SurfaceHover);
|
||||
var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
|
||||
var primaryAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Primary);
|
||||
var accentAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Accent);
|
||||
|
||||
ReadOnlySpan<string> labels = ["Linkshell", "Tell", "FC"];
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var rowMin = new Vector2(origin.X, origin.Y + i * rowHeight);
|
||||
var rowMax = new Vector2(origin.X + SidebarWidth, rowMin.Y + rowHeight);
|
||||
var bg = i == 1 ? surfaceHover : surface;
|
||||
draw.AddRectFilled(rowMin, rowMax, bg);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
draw.AddRectFilled(rowMin, new Vector2(rowMin.X + 2f, rowMax.Y), primaryAbgr);
|
||||
}
|
||||
|
||||
private void DrawMessageList(Theme theme)
|
||||
var labelSize = ImGui.CalcTextSize(labels[i]);
|
||||
var textPos = new Vector2(rowMin.X + 6f, rowMin.Y + (rowHeight - labelSize.Y) * 0.5f);
|
||||
draw.AddText(textPos, textAbgr, labels[i]);
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
// 4 mock messages. Implementation in next sub-step.
|
||||
// Tell row carries an unread-dot in Accent on the right.
|
||||
var dotCenter = new Vector2(rowMax.X - 8f, rowMin.Y + rowHeight * 0.5f);
|
||||
draw.AddRectFilled(
|
||||
new Vector2(dotCenter.X - 2f, dotCenter.Y - 2f),
|
||||
new Vector2(dotCenter.X + 2f, dotCenter.Y + 2f),
|
||||
accentAbgr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawInputBar(Theme theme)
|
||||
private static void DrawMessageList(Theme theme)
|
||||
{
|
||||
// Pill + text + cog. Implementation in next sub-step.
|
||||
var draw = ImGui.GetWindowDrawList();
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var totalWidth = ImGui.GetContentRegionAvail().X;
|
||||
var listOrigin = new Vector2(origin.X + SidebarWidth, origin.Y);
|
||||
var listWidth = totalWidth - SidebarWidth;
|
||||
var max = new Vector2(listOrigin.X + listWidth, listOrigin.Y + MiddleBandHeight);
|
||||
|
||||
draw.AddRectFilled(listOrigin, max, ColourUtil.RgbaToAbgr(theme.Colors.WindowBg));
|
||||
|
||||
var padMin = new Vector2(listOrigin.X + 2f, max.Y - 6f);
|
||||
draw.AddRectFilled(
|
||||
padMin,
|
||||
new Vector2(max.X - 2f, max.Y - 2f),
|
||||
ColourUtil.RgbaToAbgr(theme.Colors.FrameBg)
|
||||
);
|
||||
|
||||
ReadOnlySpan<(string Text, uint Rgba)> rows =
|
||||
[
|
||||
(MockSystem, theme.Colors.TextMuted),
|
||||
(MockSay, theme.Colors.TextPrimary),
|
||||
(MockTell, theme.Colors.StatusInfo),
|
||||
(MockFc, theme.Colors.StatusSuccess),
|
||||
];
|
||||
|
||||
var lineHeight = ImGui.GetTextLineHeightWithSpacing();
|
||||
for (var i = 0; i < rows.Length; i++)
|
||||
{
|
||||
var pos = new Vector2(listOrigin.X + 6f, listOrigin.Y + 6f + i * lineHeight);
|
||||
draw.AddText(pos, ColourUtil.RgbaToAbgr(rows[i].Rgba), rows[i].Text);
|
||||
}
|
||||
|
||||
private void DrawStatusBar(Theme theme)
|
||||
draw.AddLine(
|
||||
new Vector2(origin.X, max.Y - 1f),
|
||||
new Vector2(origin.X + totalWidth, max.Y - 1f),
|
||||
ColourUtil.RgbaToAbgr(theme.Colors.Border),
|
||||
1f
|
||||
);
|
||||
|
||||
// Reserve the full middle-band height (sidebar overlays into the
|
||||
// same vertical span and does not advance the cursor itself).
|
||||
ImGui.Dummy(new Vector2(totalWidth, MiddleBandHeight));
|
||||
}
|
||||
|
||||
private static void DrawInputBar(Theme theme)
|
||||
{
|
||||
// Status icons. Implementation in next sub-step.
|
||||
const float height = 24f;
|
||||
const float pillWidth = 50f;
|
||||
var draw = ImGui.GetWindowDrawList();
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
var max = new Vector2(origin.X + width, origin.Y + height);
|
||||
|
||||
draw.AddRectFilled(origin, max, ColourUtil.RgbaToAbgr(theme.Colors.FrameBg));
|
||||
|
||||
var pillMin = new Vector2(origin.X + 4f, origin.Y + 4f);
|
||||
var pillMax = new Vector2(origin.X + pillWidth, max.Y - 4f);
|
||||
draw.AddRectFilled(pillMin, pillMax, ColourUtil.RgbaToAbgr(theme.Colors.Primary), 6f);
|
||||
|
||||
var pillLabel = "Say";
|
||||
var pillLabelSize = ImGui.CalcTextSize(pillLabel);
|
||||
var pillTextPos = new Vector2(
|
||||
pillMin.X + ((pillMax.X - pillMin.X) - pillLabelSize.X) * 0.5f,
|
||||
pillMin.Y + ((pillMax.Y - pillMin.Y) - pillLabelSize.Y) * 0.5f
|
||||
);
|
||||
draw.AddText(pillTextPos, ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary), pillLabel);
|
||||
|
||||
var placeholder = "Type a message...";
|
||||
var phSize = ImGui.CalcTextSize(placeholder);
|
||||
var phPos = new Vector2(pillMax.X + 6f, origin.Y + (height - phSize.Y) * 0.5f);
|
||||
draw.AddText(phPos, ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary), placeholder);
|
||||
|
||||
var cogSize = ImGui.CalcTextSize(CogGlyph);
|
||||
var cogPos = new Vector2(max.X - cogSize.X - 6f, origin.Y + (height - cogSize.Y) * 0.5f);
|
||||
draw.AddText(cogPos, ColourUtil.RgbaToAbgr(theme.Colors.TextMuted), CogGlyph);
|
||||
|
||||
ImGui.Dummy(new Vector2(width, height));
|
||||
}
|
||||
|
||||
private static void DrawStatusBar(Theme theme)
|
||||
{
|
||||
const float height = 20f;
|
||||
const float iconSize = 8f;
|
||||
const float iconGap = 6f;
|
||||
var draw = ImGui.GetWindowDrawList();
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
var max = new Vector2(origin.X + width, origin.Y + height);
|
||||
|
||||
draw.AddRectFilled(origin, max, ColourUtil.RgbaToAbgr(theme.Colors.ChildBg));
|
||||
|
||||
ReadOnlySpan<uint> statusRgba =
|
||||
[
|
||||
theme.Colors.StatusSuccess,
|
||||
theme.Colors.StatusDanger,
|
||||
theme.Colors.StatusWarning,
|
||||
];
|
||||
|
||||
var iconY = origin.Y + (height - iconSize) * 0.5f;
|
||||
for (var i = 0; i < statusRgba.Length; i++)
|
||||
{
|
||||
var iconX = origin.X + 6f + i * (iconSize + iconGap);
|
||||
draw.AddRectFilled(
|
||||
new Vector2(iconX, iconY),
|
||||
new Vector2(iconX + iconSize, iconY + iconSize),
|
||||
ColourUtil.RgbaToAbgr(statusRgba[i]),
|
||||
2f
|
||||
);
|
||||
}
|
||||
|
||||
var label = "preview";
|
||||
var labelSize = ImGui.CalcTextSize(label);
|
||||
var labelPos = new Vector2(
|
||||
max.X - labelSize.X - 6f,
|
||||
origin.Y + (height - labelSize.Y) * 0.5f
|
||||
);
|
||||
draw.AddText(labelPos, ColourUtil.RgbaToAbgr(theme.Colors.TextDim), label);
|
||||
|
||||
ImGui.Dummy(new Vector2(width, height));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user