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)); }