From 30c495849d71804e7394c20576bb01b2497f0ca8 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 18 Aug 2026 09:23:12 +0200 Subject: [PATCH] fix(ui): stop the context menu crashing and give the quick buttons their tooltips back Two things Flo hit on the first run. The context menu threw on EndPopup. The spacing guard added in 929188e was a `using var`, which disposes at the end of the method -- after EndPopup. ImGui asserts when a popup closes with a style var still on its stack. The body moved into a scoped block so the pop happens inside the popup. The quick-button tooltips were empty boxes. SetTooltip ran inside the FontAwesome push, and that atlas has no ASCII glyphs, so the text had nothing to render with. ImRaii.DefaultFont did not save it. The hovered label is collected now and drawn after the font is popped. Same trap as the unread badge in C5, different place. Those three tooltip strings are still hard-coded English while the rest of the UI is localised. Pre-existing, and adding resources means touching 24 language files, so it is noted rather than fixed here. --- HellionChat/Ui/Components/InputBar.cs | 28 +++++++++------------ HellionChat/Ui/Components/TabContextMenu.cs | 23 ++++++++++++----- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/HellionChat/Ui/Components/InputBar.cs b/HellionChat/Ui/Components/InputBar.cs index a281fd1..ab9854d 100644 --- a/HellionChat/Ui/Components/InputBar.cs +++ b/HellionChat/Ui/Components/InputBar.cs @@ -511,15 +511,17 @@ internal sealed class InputBar private void DrawQuickButtons() { + // Tooltip text is collected here and drawn after the icon font is popped. + // Inside the push it rendered against the FontAwesome atlas, which has no + // ASCII glyphs, so every tooltip came out as an empty box. + string? tooltip = null; + using (_fonts.FontAwesome.Push()) { if (ImGui.Button(FontAwesomeIcon.SmileBeam.ToIconString())) _symbolPicker.OpenPopup(); if (ImGui.IsItemHovered()) - { - using (ImRaii.DefaultFont()) - ImGui.SetTooltip("Insert symbol"); - } + tooltip = "Insert symbol"; if (_themeQuickPicker is not null) { @@ -527,10 +529,7 @@ internal sealed class InputBar if (ImGui.Button(FontAwesomeIcon.Palette.ToIconString())) _themeQuickPicker.OpenPopup(); if (ImGui.IsItemHovered()) - { - using (ImRaii.DefaultFont()) - ImGui.SetTooltip(HellionStrings.Settings_QuickPicker_Tooltip); - } + tooltip = HellionStrings.Settings_QuickPicker_Tooltip; } ImGui.SameLine(); @@ -539,10 +538,7 @@ internal sealed class InputBar _onOpenSettings(); } if (ImGui.IsItemHovered()) - { - using (ImRaii.DefaultFont()) - ImGui.SetTooltip("Settings"); - } + tooltip = "Settings"; // Hides the window (1.5.6 UserHide). One-way — Enter brings it back. // Main window only (pop-outs have their own close); last in the row. @@ -552,12 +548,12 @@ internal sealed class InputBar if (ImGui.Button(FontAwesomeIcon.EyeSlash.ToIconString())) _onHideWindow(); if (ImGui.IsItemHovered()) - { - using (ImRaii.DefaultFont()) - ImGui.SetTooltip("Hide chat (Enter to bring back)"); - } + tooltip = "Hide chat (Enter to bring back)"; } } + + if (tooltip is not null) + ImGui.SetTooltip(tooltip); } // Test-only hook; do not call from production code. diff --git a/HellionChat/Ui/Components/TabContextMenu.cs b/HellionChat/Ui/Components/TabContextMenu.cs index 24af76b..f11b639 100644 --- a/HellionChat/Ui/Components/TabContextMenu.cs +++ b/HellionChat/Ui/Components/TabContextMenu.cs @@ -44,11 +44,24 @@ internal static class TabContextMenu // vars are a global stack the popup inherits. Reading GetStyle() here // would read that zero back, so the popup sets its own spacing outright // -- including X, which HelpMarker's SameLine depends on. - using var spacing = ImRaii.PushStyle( - ImGuiStyleVar.ItemSpacing, - new System.Numerics.Vector2(8f, 4f) * Ui.StyleEngine.Metrics.Scale - ); + // + // Scoped block, not a `using var`: that would pop after EndPopup, and + // ImGui asserts when a popup closes with a style var still on the stack. + using ( + ImRaii.PushStyle( + ImGuiStyleVar.ItemSpacing, + new System.Numerics.Vector2(8f, 4f) * Ui.StyleEngine.Metrics.Scale + ) + ) + { + DrawBody(tab, pool); + } + ImGui.EndPopup(); + } + + private static void DrawBody(Tab tab, Windows.ChannelPopoutPool pool) + { // Rename: focus the field the first frame the popup appears. if (ImGui.IsWindowAppearing()) ImGui.SetKeyboardFocusHere(); @@ -84,8 +97,6 @@ internal static class TabContextMenu if (ImGui.MenuItem("Pop Out")) pool.TryOpen(tab); - - ImGui.EndPopup(); } // The flush depends on Draw running once more for this tab. If it never does —