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.
This commit is contained in:
2026-08-18 14:11:31 +02:00
parent c5a0c4d0d7
commit 0c3023c8cb
2 changed files with 21 additions and 18 deletions
@@ -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;
+15 -15
View File
@@ -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);