From 51c3c0c9920cec07d98aace659b9bb9831fbde83 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Sep 2026 17:27:28 +0200 Subject: [PATCH 1/3] Sliders: moved code from SliderBehaviorT() to SliderBehavior(). Remove a level of indent too. --- imgui_widgets.cpp | 64 +++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 65d131fb6..19d03d0b1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2665,10 +2665,10 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); + // Things we can do easily outside the DragBehaviorT<> template, saves code generation. ImGuiContext& g = *GImGui; if (g.ActiveId == id) { - // Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation. if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) ClearActiveID(); else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) @@ -3140,37 +3140,24 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ float clicked_t = 0.0f; if (g.ActiveIdSource == ImGuiInputSource_Mouse) { - if (!g.IO.MouseDown[0]) + const float mouse_abs_pos = g.IO.MousePos[axis]; + if (g.ActiveIdIsJustActivated) { - ClearActiveID(); - } - else - { - const float mouse_abs_pos = g.IO.MousePos[axis]; - if (g.ActiveIdIsJustActivated) - { - float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); - if (axis == ImGuiAxis_Y) - grab_t = 1.0f - grab_t; - const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here. - g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f; - } - if (slider_usable_sz > 0.0f) - clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); + float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); if (axis == ImGuiAxis_Y) - clicked_t = 1.0f - clicked_t; - set_new_value = true; + grab_t = 1.0f - grab_t; + const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here. + g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f; } + if (slider_usable_sz > 0.0f) + clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); + if (axis == ImGuiAxis_Y) + clicked_t = 1.0f - clicked_t; + set_new_value = true; } else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) { - if (g.ActiveIdIsJustActivated) - { - g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation - g.SliderCurrentAccumDirty = false; - } - float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis); if (input_delta != 0.0f) { @@ -3198,11 +3185,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ } float delta = g.SliderCurrentAccum; - if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - { - ClearActiveID(); - } - else if (g.SliderCurrentAccumDirty) + if (g.SliderCurrentAccumDirty) { clicked_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); @@ -3228,7 +3211,6 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ else g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta); } - g.SliderCurrentAccumDirty = false; } } @@ -3283,6 +3265,24 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); IM_ASSERT((flags & ImGuiSliderFlags_WrapAround) == 0); // Not supported by SliderXXX(), only by DragXXX() + // Things we can do easily outside the SliderBehaviorT<> template, saves code generation. + ImGuiContext& g = *GImGui; + if (g.ActiveId == id) + { + if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) + ClearActiveID(); + if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) + { + if (g.ActiveIdIsJustActivated) + { + g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation + g.SliderCurrentAccumDirty = false; + } + if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + ClearActiveID(); + } + } + switch (data_type) { case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } From f150bf09f97bc5e4fa663e578fd414b7fdb1e2e8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Sep 2026 17:20:08 +0200 Subject: [PATCH 2/3] Drags, Sliders: ongoing drags may be cancelled using Escape or Gamepad Triangle, reverting to the initial value. (#8564, #9534) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2ce07692c..c0953950e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,8 @@ Other Changes: - Drags, Sliders: - Fixed an overflow using `ImGuiSliderFlags_Logarithmic` with S32/S64 types which leads to clamped interactions. (#9526, #3361, #1823, #1316, #642) [@lailoken] + - Ongoing drags may be cancelled using Escape or Gamepad Triangle, reverting + to the initial value. (#8564, #9534) - TreeNode: - Fixed issues/asserts with 32+ deep trees when using ImGuiTreeNodeFlags_DrawLinesXXX features or other features requiring stack storage. (#9509) [@lasrod] diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 19d03d0b1..5de0dfb06 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2660,6 +2660,17 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const return true; } +static bool ShortcutsForCancel(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + bool is_cancel_with_keyboard = ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_None, id); + bool is_cancel_with_gamepad = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0 && ImGui::Shortcut(ImGuiKey_NavGamepadCancel, ImGuiInputFlags_None, id); + //bool is_cancel_with_mouse = ImGui::IsMouseClicked(ImGuiMouseButton_Right, ImGuiInputFlags_None, id); + //if (is_cancel_with_mouse) + // ImGui::SetKeyOwner(ImGuiKey_MouseRight, id); + return is_cancel_with_keyboard || is_cancel_with_gamepad; //|| is_cancel_with_mouse +} + bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) { // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. @@ -2673,6 +2684,15 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v ClearActiveID(); else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ClearActiveID(); + if (ShortcutsForCancel(id) && g.ActiveId == id) + { + // Canceling action reverts to initial value + size_t data_size = DataTypeGetInfo(data_type)->Size; + bool value_changed = memcmp(p_v, &g.ActiveIdValueOnActivation, data_size); + memcpy(p_v, &g.ActiveIdValueOnActivation, data_size); + ClearActiveID(); + return value_changed; + } } if (g.ActiveId != id) return false; @@ -3281,6 +3301,15 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ClearActiveID(); } + if (ShortcutsForCancel(id) && g.ActiveId == id) + { + // Canceling action reverts to initial value + size_t data_size = DataTypeGetInfo(data_type)->Size; + bool value_changed = memcmp(p_v, &g.ActiveIdValueOnActivation, data_size); + memcpy(p_v, &g.ActiveIdValueOnActivation, data_size); + ClearActiveID(); + return value_changed; + } } switch (data_type) From 96b6eb7728d298decdc939d4c698502220190050 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 4 Sep 2026 12:37:48 +0200 Subject: [PATCH 3/3] Fixed PVS-Studio false positives. Not sure if there's a way to report them. --- imgui_widgets.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5de0dfb06..71da3b4c3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2684,7 +2684,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v ClearActiveID(); else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ClearActiveID(); - if (ShortcutsForCancel(id) && g.ActiveId == id) + if (ShortcutsForCancel(id) && g.ActiveId == id) //-V560 { // Canceling action reverts to initial value size_t data_size = DataTypeGetInfo(data_type)->Size; @@ -3301,7 +3301,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ClearActiveID(); } - if (ShortcutsForCancel(id) && g.ActiveId == id) + if (ShortcutsForCancel(id) && g.ActiveId == id) //-V560 { // Canceling action reverts to initial value size_t data_size = DataTypeGetInfo(data_type)->Size;