From 6449a51daa6027abf37a43c6c86446f142bc2f06 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Wed, 19 Aug 2026 18:08:37 +0200 Subject: [PATCH] fix(chat): the channel header had a plate the settings window gave up weeks ago MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mockup draws this band as a filled surface, and I built it that way. The settings window used to draw its section headings the same way -- and dropped it in v1.11.0, with the reason written into SectionHeader: a fill reads fine against blue themes and vanishes against violet ones, because its only distinction from its surroundings is hue. Tracking is shape, and shape survives every palette. Side by side with yesterday's build the difference was obvious: the settings window sets VERHALTEN and TASTENKĂśRZEL as tracked caps with a rule that fades out, while the chat window right next to it carried a lit bar. One window was three weeks ahead of the other. So the plate is gone and a fading rule runs between the channel name and the world, stopping short of it. Same shape the settings headings have had since ced7ea0. Worth writing down because the mockups are where this came from, and they are from before that lesson. They are the starting point of the style track, not its target -- the target is how far ImGui can be pushed, and the plugin has been past these drawings since v1.11.0. --- .../Components/Settings/LivePreviewPanel.cs | 26 +++++------ .../Ui/StyleEngine/Widgets/ChannelHeader.cs | 45 ++++++++++++------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs b/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs index c11cb1d..32cf0a6 100644 --- a/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs +++ b/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs @@ -269,20 +269,10 @@ internal sealed class LivePreviewPanel : IDisposable draw.AddRectFilled(listOrigin, max, ColourUtil.RgbaToAbgr(theme.Colors.WindowBg)); - // The channel header the real window now carries above its log. Tracked - // caps, same as the widget draws them. + // The channel header the real window carries above its log: tracked caps + // and a rule that fades out, no plate. Same reasoning as the section + // headings in this very window. var headerHeight = ImGui.GetTextLineHeight() + 8f; - draw.AddRectFilled( - listOrigin, - new Vector2(max.X, listOrigin.Y + headerHeight), - ColourUtil.RgbaToAbgr(theme.Colors.Surface) - ); - draw.AddLine( - new Vector2(listOrigin.X, listOrigin.Y + headerHeight), - new Vector2(max.X, listOrigin.Y + headerHeight), - ColourUtil.RgbaToAbgr(theme.Colors.Border), - 1f - ); draw.DrawTrackedText( new Vector2(listOrigin.X + 6f, listOrigin.Y + 4f), MockChannel, @@ -295,6 +285,16 @@ internal sealed class LivePreviewPanel : IDisposable 1.8f ); + draw.DrawFadeRule( + new Vector2( + listOrigin.X + 6f + 68f, + listOrigin.Y + 4f + ImGui.GetTextLineHeight() * 0.5f + ), + listWidth - 6f - 68f - 6f, + ColourUtil.RgbaToAbgr(theme.Colors.Border), + 1f + ); + var padMin = new Vector2(listOrigin.X + 2f, max.Y - 6f); draw.AddRectFilled( padMin, diff --git a/HellionChat/Ui/StyleEngine/Widgets/ChannelHeader.cs b/HellionChat/Ui/StyleEngine/Widgets/ChannelHeader.cs index 8668c7c..6dc720e 100644 --- a/HellionChat/Ui/StyleEngine/Widgets/ChannelHeader.cs +++ b/HellionChat/Ui/StyleEngine/Widgets/ChannelHeader.cs @@ -9,11 +9,15 @@ namespace HellionChat.Ui.StyleEngine.Widgets; // The band above the conversation: which channel you are in on the left, where // you are and what time it is on the right. // -// Set apart by small caps with wide tracking rather than by size. That is a -// deliberate departure from the mockup, which asks for one pixel smaller than -// body text: one pixel would cost a whole additional font handle at full glyph -// range, because tab names are free user input and can be CJK. Tracking carries -// the same weight in every palette and costs nothing. +// Set apart by small caps with wide tracking, and by nothing else. The mockup +// draws this as a filled bar; the settings window used to draw its section +// headings that way too, and it was dropped in v1.11.0 for a reason worth +// repeating -- a fill reads fine against blue themes and vanishes against violet +// ones, because its only distinction from its surroundings is hue. Tracking is +// shape, and shape survives every palette. +// +// So: no plate, and a rule that fades out between the name and the world, the +// same way SectionHeader runs one between its title and the edge. // // Which face draws what is not a style choice here, it is a constraint. The meta // face has a glyph range of ASCII plus a middle dot, so only the world name and @@ -26,6 +30,7 @@ internal static class ChannelHeader private const float TrackRaw = 1.8f; private const float DetailTrackRaw = 0.9f; private const float IconGapRaw = 8f; + private const float RuleGapRaw = 10f; // Measured, never a fixed 32px: the band has to hold a line of text, and the // font comes from Config, which display scaling does not feed into. @@ -68,8 +73,10 @@ internal static class ChannelHeader var origin = ImGui.GetCursorScreenPos(); var theme = Plugin.Instance.ThemeRegistry.Active; - var surface = theme.Colors.Surface; - var surfaceAbgr = ColourUtil.RgbaToAbgr(surface); + // Contrast is still measured against the surface the header sits on, even + // though it no longer paints one -- the chat log's own floor is what the + // text lands on. + var surfaceAbgr = ColourUtil.RgbaToAbgr(theme.Colors.ChildBg); var body = BodyFace(fonts); var meta = MetaFace(fonts); @@ -152,13 +159,6 @@ internal static class ChannelHeader var dl = ImGui.GetWindowDrawList(); var bottomRight = origin + new Vector2(width, height); - dl.AddRectFilled(origin, bottomRight, surfaceAbgr); - dl.AddLine( - new Vector2(origin.X, bottomRight.Y - 1f), - new Vector2(bottomRight.X, bottomRight.Y - 1f), - ColourUtil.RgbaToAbgr(theme.Colors.Border), - MathF.Max(1f, scale) - ); var textY = origin.Y + MathF.Round(PadYRaw * scale); @@ -188,13 +188,28 @@ internal static class ChannelHeader x += iconSize.X + IconGapRaw * scale; + float drawn; using (meta.Push()) - dl.DrawTrackedText( + drawn = dl.DrawTrackedText( new Vector2(x, textY + DropFor(body, meta, scale)), name, accent, track ); + + // Starts where the name ends and fades into nothing, so it reads as a + // continuation of the heading rather than as a box lid. Stops short of + // the trailing detail when there is one. + var ruleX = x + drawn + RuleGapRaw * scale; + var ruleEnd = + bottomRight.X - inset - (plan.ShowDetail ? detailRun + RuleGapRaw * scale : 0f); + if (ruleEnd > ruleX) + dl.DrawFadeRule( + new Vector2(ruleX, textY + ImGui.GetTextLineHeight() * 0.5f), + ruleEnd - ruleX, + ColourUtil.RgbaToAbgr(theme.Colors.Border), + MathF.Max(1f, scale) + ); } if (plan.ShowDetail && detail.Where.Length > 0)