From bfc61909cfc07423d2ddd23685add259f754ef3f Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 18 Aug 2026 00:05:23 +0200 Subject: [PATCH] refactor(ui): scale the remaining layout constants What blocks A to E did not already touch: the honorific header height and its two offsets, the message list dummy widths, and the quick-button reserve in the input bar. The reserve is the one with visible consequences. At 150% the buttons grow with the font while a fixed 130px column does not, so they stopped fitting. The honorific offsets are centred rather than scaled. The 8f there was (30 - 14) / 2 for the old font, structurally the same case as the sidebar: a scaled constant keeps its mis-centering, a computed one does not. --- HellionChat/Ui/Components/HonorificHeader.cs | 18 ++++++++++++++---- HellionChat/Ui/Components/InputBar.cs | 5 ++++- HellionChat/Ui/Components/MessageList.cs | 16 +++++++++++++--- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/HellionChat/Ui/Components/HonorificHeader.cs b/HellionChat/Ui/Components/HonorificHeader.cs index d1e2ae3..5ab5139 100644 --- a/HellionChat/Ui/Components/HonorificHeader.cs +++ b/HellionChat/Ui/Components/HonorificHeader.cs @@ -13,7 +13,8 @@ namespace HellionChat.Ui.Components; // bracketed title only appears when there is actually a title to show. internal sealed class HonorificHeader { - public const float Height = 30f; + // Scaled: MainWindow reserves body height against this, so it follows. + public static float Height => StyleEngine.Metrics.HonorificHeight; // SelfTest observables — set on the real Draw path so a headless step can // assert the gate/colour/truncation outcome instead of re-implementing it. @@ -68,7 +69,11 @@ internal sealed class HonorificHeader using (_fonts.FontAwesome.Push()) { crownWidth = ImGui.CalcTextSize(crownGlyph).X; - dl.AddText(origin + new Vector2(0f, 8f), crownColor, crownGlyph); + dl.AddText( + origin + new Vector2(0f, StyleEngine.Metrics.CenterY(Height)), + crownColor, + crownGlyph + ); } // Gate the bracketed title through the 1.5.6 contract (toggle, IPC @@ -92,12 +97,17 @@ internal sealed class HonorificHeader // TruncateToFitWidth measures the *Regular* font, so this must run // OUTSIDE the FontAwesome.Push block above (crownWidth was measured // inside it, which is correct). - var maxTitleWidth = maxWidth - crownWidth - 6f - 8f; + var gap = StyleEngine.Metrics.HonorificBracketGap; + var maxTitleWidth = maxWidth - crownWidth - gap - StyleEngine.Metrics.HonorificInset; if (maxTitleWidth > 0f) { var rendered = StringUtil.TruncateToFitWidth($"«{current.Title}»", maxTitleWidth); LastRenderedTitle = rendered; - dl.AddText(origin + new Vector2(crownWidth + 6f, 8f), titleColor, rendered); + dl.AddText( + origin + new Vector2(crownWidth + gap, StyleEngine.Metrics.CenterY(Height)), + titleColor, + rendered + ); LastTitleRendered = true; } } diff --git a/HellionChat/Ui/Components/InputBar.cs b/HellionChat/Ui/Components/InputBar.cs index 47dfe8f..a281fd1 100644 --- a/HellionChat/Ui/Components/InputBar.cs +++ b/HellionChat/Ui/Components/InputBar.cs @@ -30,7 +30,10 @@ internal sealed class InputBar // they follow automatically. The pill's own metrics live in PillStyle now. public static float Height => StyleEngine.Metrics.InputBarHeight; private const int BufferCapacity = 500; - private const float QuickButtonsReserve = 130f; + + // Scaled: the buttons themselves grow with the font, so a fixed reserve + // stops fitting them at 150%. + private static float QuickButtonsReserve => StyleEngine.Metrics.InputQuickButtonsReserve; private readonly SymbolPicker _symbolPicker; private readonly FontManager _fonts; diff --git a/HellionChat/Ui/Components/MessageList.cs b/HellionChat/Ui/Components/MessageList.cs index 6cf0ad6..67982eb 100644 --- a/HellionChat/Ui/Components/MessageList.cs +++ b/HellionChat/Ui/Components/MessageList.cs @@ -308,12 +308,17 @@ internal sealed class MessageList { // Scrolled into a gap: one full-height dummy keeps the scrollbar honest. var gap = CompensatedDummy(plan.LeadDummyHeight + plan.EndDummyHeight); - ImGui.Dummy(new Vector2(10f, gap)); + ImGui.Dummy(new Vector2(StyleEngine.Metrics.MessageDummyWidth, gap)); return; } if (plan.LeadDummyHeight > 0f) - ImGui.Dummy(new Vector2(10f, CompensatedDummy(plan.LeadDummyHeight))); + ImGui.Dummy( + new Vector2( + StyleEngine.Metrics.MessageDummyWidth, + CompensatedDummy(plan.LeadDummyHeight) + ) + ); for (var i = plan.FirstVisible; i <= plan.LastVisible; i++) { @@ -329,7 +334,12 @@ internal sealed class MessageList } if (plan.EndDummyHeight > 0f) - ImGui.Dummy(new Vector2(10f, CompensatedDummy(plan.EndDummyHeight))); + ImGui.Dummy( + new Vector2( + StyleEngine.Metrics.MessageDummyWidth, + CompensatedDummy(plan.EndDummyHeight) + ) + ); } // First-frame / post-invalidation fallback: draw + measure every row into the