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)