From 32401de7f1b69e9f27e0bfd7aa6c867421b7a6b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 12:35:19 +0000 Subject: [PATCH] [patch] Stop ValueSlider reporting a change on held frames that move nothing A drag is held as a double, and a float slider compared that double with the float it was handed, so a value a float cannot hold exactly read as a fresh change on every frame the pointer rested. Values are now compared as they are stored. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_018BGac9LmQV495Bsnrmo1b7 --- ImGui.Widgets/ValueSlider.cs | 5 ++- .../ImGui.Widgets.UITests/ValueSliderTests.cs | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/ImGui.Widgets/ValueSlider.cs b/ImGui.Widgets/ValueSlider.cs index 2b3bd0ba..b0d2aa44 100644 --- a/ImGui.Widgets/ValueSlider.cs +++ b/ImGui.Widgets/ValueSlider.cs @@ -259,7 +259,10 @@ private static bool Drag(ValueSliderState state, ref double value, Spec spec, fl private static bool SetValue(ref double value, double target, double min, double max, bool isInteger) { - double next = Math.Clamp(isInteger ? Math.Round(target, MidpointRounding.AwayFromZero) : target, min, max); + // The value is stored as a float or an int, so it is compared as one: a drag held as a double + // that a float cannot hold exactly would otherwise read as a fresh change on every held frame. + double stored = isInteger ? Math.Round(target, MidpointRounding.AwayFromZero) : (float)target; + double next = Math.Clamp(stored, min, max); if (IsSameValue(next, value)) { return false; diff --git a/tests/ImGui.Widgets.UITests/ValueSliderTests.cs b/tests/ImGui.Widgets.UITests/ValueSliderTests.cs index 52d82062..cc887f00 100644 --- a/tests/ImGui.Widgets.UITests/ValueSliderTests.cs +++ b/tests/ImGui.Widgets.UITests/ValueSliderTests.cs @@ -344,6 +344,42 @@ public void ValueSlider_APrefixIsDrawnButNotTyped() Assert.AreEqual(30f, value, "A typed value with the prefix in front was not read."); } + [TestMethod] + public void ValueSlider_AHeldPointerThatStopsReportsNoChange() + { + // 0..1 over 200 px, so a 7 px drag lands on 0.035, which a float cannot hold exactly. + float fraction = 0.5f; + int changes = 0; + Start(() => + { + ImGui.PushItemWidth(FrameWidth); + if (ImGuiWidgets.ValueSlider(Label, ref fraction, 0f, 1f)) + { + changes++; + } + + ImGui.PopItemWidth(); + }); + + Rectangle rect = RectOf(Label); + float x = rect.MinX + (FrameWidth / 2f); + float y = rect.MinY + (rect.Height / 2f); + Harness.Mouse.MoveTo(x, y); + Step(); + HarnessMouse.Down(0); + Step(); + Harness.Mouse.MoveTo(x + 7f, y); + Step(2); + + Assert.AreNotEqual(0.5f, fraction, "The drag did not move the value."); + changes = 0; + Step(5); + HarnessMouse.Up(0); + Step(); + + Assert.AreEqual(0, changes, "The slider reported a change on frames the held pointer did not move."); + } + [TestMethod] public void ValueSlider_ReportsTheFrameItChangedOn() {