From 0d6877ddc6251a6fbefc37175c82f77d1d1ac831 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 18 Aug 2026 00:04:00 +0200 Subject: [PATCH] fix(settings): align the theme preview with the real chrome The preview had been showing surfaces, an accent bar and an unread marker for months while the real sidebar drew none of them. Now that the sidebar has caught up, the preview is the one that is wrong -- in two specific ways. It put the accent bar on row 0 and the raised surface on row 1, so it showed two half-active rows instead of one active row. Both now sit on row 0, and the rows get the separator the real ones have. The unread marker was a 4x4 square. It is a rounded count badge now. The status bar preview was a separate 20px reimplementation with three coloured squares and a hard-coded label. It mirrors the pill layout instead, with the status colours riding along as slot dots so a theme still shows what it does to them. --- .../Components/Settings/LivePreviewPanel.cs | 112 +++++++++++++----- 1 file changed, 83 insertions(+), 29 deletions(-) diff --git a/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs b/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs index c058133..4621996 100644 --- a/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs +++ b/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs @@ -181,18 +181,29 @@ internal sealed class LivePreviewPanel : IDisposable var primaryAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Primary); var accentAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Accent); + // Row 0 is the active one and carries BOTH the raised surface and the + // accent bar, the way the real sidebar draws it. Until v1.10.0 those two + // sat on different rows here, so the preview promised a layout the + // sidebar never delivered -- and then the sidebar caught up. + var borderAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Border); ReadOnlySpan 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); + var isActive = i == 0; - if (i == 0) - { + draw.AddRectFilled(rowMin, rowMax, isActive ? surfaceHover : surface); + + if (isActive) draw.AddRectFilled(rowMin, new Vector2(rowMin.X + 2f, rowMax.Y), primaryAbgr); - } + + draw.AddLine( + new Vector2(rowMin.X, rowMax.Y - 1f), + new Vector2(rowMax.X, rowMax.Y - 1f), + borderAbgr, + 1f + ); var labelSize = ImGui.CalcTextSize(labels[i]); var textPos = new Vector2(rowMin.X + 6f, rowMin.Y + (rowHeight - labelSize.Y) * 0.5f); @@ -200,13 +211,21 @@ internal sealed class LivePreviewPanel : IDisposable if (i == 1) { - // 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 + // Unread marker: a rounded count badge in Accent, not a square. + var badgeH = MathF.Min(rowHeight - 4f, 14f); + var badgeW = badgeH * 1.4f; + var badgeMin = new Vector2( + rowMax.X - badgeW - 4f, + rowMin.Y + (rowHeight - badgeH) * 0.5f ); + var badgeMax = badgeMin + new Vector2(badgeW, badgeH); + draw.AddRectFilled( + badgeMin, + badgeMax, + (accentAbgr & 0x00FFFFFFu) | 0x38000000u, + badgeH * 0.5f + ); + draw.AddRect(badgeMin, badgeMax, accentAbgr, badgeH * 0.5f); } } } @@ -298,44 +317,79 @@ internal sealed class LivePreviewPanel : IDisposable ImGui.Dummy(new Vector2(width, height)); } + // Mirrors the real status bar: a top rule, then pill-shaped slots, the last + // one right-aligned. The status colours ride along as slot dots so a theme + // still shows what it does to them. private static void DrawStatusBar(Theme theme) { - const float height = 20f; - const float iconSize = 8f; - const float iconGap = 6f; + const float height = 24f; + const float pillH = 18f; + const float padX = 6f; + const float gap = 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)); + draw.AddLine( + origin, + new Vector2(max.X, origin.Y), + ColourUtil.RgbaToAbgr(theme.Colors.Border), + 1f + ); - ReadOnlySpan statusRgba = + var fill = ColourUtil.RgbaToAbgr(theme.Colors.SurfaceHover); + var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary); + var pillY = origin.Y + (height - pillH) * 0.5f; + + ReadOnlySpan slots = ["Say", "open", "3 tabs"]; + ReadOnlySpan dots = [ theme.Colors.StatusSuccess, - theme.Colors.StatusDanger, theme.Colors.StatusWarning, + theme.Colors.StatusDanger, ]; - var iconY = origin.Y + (height - iconSize) * 0.5f; - for (var i = 0; i < statusRgba.Length; i++) + var x = origin.X + padX; + for (var i = 0; i < slots.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 labelSize = ImGui.CalcTextSize(slots[i]); + var slotW = labelSize.X + padX * 2f + 10f; + var slotMin = new Vector2(x, pillY); + var slotMax = new Vector2(x + slotW, pillY + pillH); + + draw.AddRectFilled(slotMin, slotMax, fill, pillH * 0.5f); + draw.AddCircleFilled( + new Vector2(x + padX + 2f, pillY + pillH * 0.5f), + 2.5f, + ColourUtil.RgbaToAbgr(dots[i]), + 10 ); + draw.AddText( + new Vector2(x + padX + 10f, pillY + (pillH - labelSize.Y) * 0.5f), + textAbgr, + slots[i] + ); + + x += slotW + gap; } var label = "preview"; - var labelSize = ImGui.CalcTextSize(label); - var labelPos = new Vector2( - max.X - labelSize.X - 6f, - origin.Y + (height - labelSize.Y) * 0.5f + var versionSize = ImGui.CalcTextSize(label); + var versionW = versionSize.X + padX * 2f; + var versionMin = new Vector2(max.X - versionW - padX, pillY); + draw.AddRectFilled( + versionMin, + versionMin + new Vector2(versionW, pillH), + fill, + pillH * 0.5f + ); + draw.AddText( + new Vector2(versionMin.X + padX, pillY + (pillH - versionSize.Y) * 0.5f), + ColourUtil.RgbaToAbgr(theme.Colors.TextDim), + label ); - draw.AddText(labelPos, ColourUtil.RgbaToAbgr(theme.Colors.TextDim), label); ImGui.Dummy(new Vector2(width, height)); }