From 0c3023c8cb45954329fc827a97945a85082e38e2 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 18 Aug 2026 14:11:31 +0200 Subject: [PATCH] fix(settings): stop enum combos listing blank rows The shared label buffer was handed to ImGui.Combo whole, with the value count as the fourth argument. That argument is not the item count -- it is popupMaxHeightInItems. The count comes from the span's own length, which was always 8. So every enum combo listed eight rows. A three-value setting showed five blank ones below its real entries, and the popup was capped at three rows high, so it scrolled. Clicking a blank row hit the range guard and silently did nothing. Five combos were affected: world suffix, name form, command help side, preview position and tell auto-open mode. The per-tab code this replaced sized its array to the value count, so the count happened to be right; sharing one buffer is what exposed the misread parameter. Slicing the buffer fixes both halves at once: the span carries the real count and popupMaxHeightInItems falls back to its -1 default. Also moves Lerp below LerpTowardWhite. It was inserted directly under that method's comment block, TEST-MIRROR line included, so the documentation sat on the wrong method. --- .../Ui/Components/Settings/SettingsWidgets.cs | 9 ++++-- HellionChat/Util/ColourUtil.cs | 30 +++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/HellionChat/Ui/Components/Settings/SettingsWidgets.cs b/HellionChat/Ui/Components/Settings/SettingsWidgets.cs index d650bed..6905266 100644 --- a/HellionChat/Ui/Components/Settings/SettingsWidgets.cs +++ b/HellionChat/Ui/Components/Settings/SettingsWidgets.cs @@ -24,6 +24,7 @@ internal sealed class SettingsWidgets // Shared across every combo. ImGui.Combo copies the strings it needs before // returning, so the buffer is free again by the time the next call runs. + // Always sliced to the value count when passed on -- see EnumCombo. private string[] _labelScratch = new string[8]; internal SettingsWidgets(Plugin plugin) @@ -105,12 +106,14 @@ internal sealed class SettingsWidgets selected = i; } + // Sliced, not passed whole with a count. The binding's fourth parameter + // is popupMaxHeightInItems, not the item count -- that comes from the + // span's own length. Handing over the full buffer would list all eight + // slots, so a three-value enum would show five blank rows. ImGui.SetNextItemWidth(width); - if (!ImGui.Combo(label, ref selected, _labelScratch, values.Length)) + if (!ImGui.Combo(label, ref selected, _labelScratch.AsSpan(0, values.Length))) return; - // The scratch buffer can be longer than the value set, so a stale index - // from a previous, larger combo must not reach the setter. if (selected < 0 || selected >= values.Length) return; diff --git a/HellionChat/Util/ColourUtil.cs b/HellionChat/Util/ColourUtil.cs index 8a2a89a..9dee87d 100755 --- a/HellionChat/Util/ColourUtil.cs +++ b/HellionChat/Util/ColourUtil.cs @@ -108,21 +108,6 @@ internal static class ColourUtil // going fully saturated (effect level stays "subtle"). RGB-only on // purpose -- DrawHoverSheen owns the alpha falloff. // TEST-MIRROR: ../../../Hellion Build test/Util/ColourUtilTintTests.cs - // Mixes two ABGR colours channel by channel, alpha included. Used where a - // widget crossfades between two theme slots rather than toward a constant. - internal static uint Lerp(uint fromAbgr, uint toAbgr, float t) - { - t = Math.Clamp(t, 0f, 1f); - uint Mix(int shift) - { - var a = (byte)((fromAbgr >> shift) & 0xFFu); - var b = (byte)((toAbgr >> shift) & 0xFFu); - return (uint)Math.Round(a + (b - a) * t) & 0xFFu; - } - - return (Mix(24) << 24) | (Mix(16) << 16) | (Mix(8) << 8) | Mix(0); - } - internal static uint LerpTowardWhite(uint abgr, float t) { t = Math.Clamp(t, 0f, 1f); @@ -138,6 +123,21 @@ internal static class ColourUtil return ((uint)a << 24) | ((uint)nb << 16) | ((uint)ng << 8) | nr; } + // Mixes two ABGR colours channel by channel, alpha included. Used where a + // widget crossfades between two theme slots rather than toward a constant. + internal static uint Lerp(uint fromAbgr, uint toAbgr, float t) + { + t = Math.Clamp(t, 0f, 1f); + uint Mix(int shift) + { + var a = (byte)((fromAbgr >> shift) & 0xFFu); + var b = (byte)((toAbgr >> shift) & 0xFFu); + return (uint)Math.Round(a + (b - a) * t) & 0xFFu; + } + + return (Mix(24) << 24) | (Mix(16) << 16) | (Mix(8) << 8) | Mix(0); + } + public static uint HexToRgba(string hex) { ArgumentNullException.ThrowIfNull(hex);