Last active
November 16, 2025 12:07
-
-
Save Folling/06c223badea6fb7da76d8039947f2398 to your computer and use it in GitHub Desktop.
an incomplete ImGui::Slider wrapper with support for scrolling to change the underlying value, includes support for glm::vec's and can be easily extended
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| template<typename F, typename T> | |
| concept ImGuiSliderFunc = requires( | |
| F f, std::string_view label, T * v, value_or_ref_t<T> v_min, value_or_ref_t<T> v_max, char const * format, ImGuiSliderFlags flags | |
| ) { | |
| { f(label.data(), v, v_min, v_max, format, flags) } -> std::convertible_to<bool>; | |
| }; | |
| template<typename T> | |
| struct delayed_false : std::false_type {}; | |
| template<typename T> | |
| constexpr auto delayed_false_v = delayed_false<T>::value; | |
| template<typename T> | |
| using value_or_ref = std::conditional<std::is_trivially_copyable_v<T>, T, T const &>; | |
| template<typename T> | |
| using value_or_ref_t = value_or_ref<T>::type; | |
| template<typename F, typename T> | |
| concept ImGuiSliderFunc = requires( | |
| F f, std::string_view label, T * v, value_or_ref_t<T> v_min, value_or_ref_t<T> v_max, char const * format, ImGuiSliderFlags flags | |
| ) { | |
| { f(label.data(), v, v_min, v_max, format, flags) } -> std::convertible_to<bool>; | |
| }; | |
| template<typename T> | |
| auto Slider(std::string_view label, T * v, value_or_ref_t<T> v_min, value_or_ref_t<T> v_max) -> bool { | |
| auto slider = []<typename T2>( | |
| ImGuiSliderFunc<T2> auto slider_func, | |
| std::string_view label, | |
| T2 * v, | |
| value_or_ref_t<T2> v_min, | |
| value_or_ref_t<T2> v_max, | |
| value_or_ref_t<T2> small_increment, | |
| value_or_ref_t<T2> large_increment | |
| ) -> bool { | |
| auto changed = slider_func(label.data(), v, v_min, v_max, nullptr, 0); | |
| if (ImGui::IsItemHovered()) { | |
| value_or_ref_t<T2> increment = ImGui::GetIO().KeyCtrl ? large_increment : small_increment; | |
| if (ImGui::GetIO().MouseWheel > 1e-6f) { | |
| changed = *v + increment <= v_max; | |
| *v += increment; | |
| } else if (ImGui::GetIO().MouseWheel < -1e-6f) { | |
| changed = *v - increment >= v_min; | |
| *v -= increment; | |
| } | |
| *v = glm::clamp(*v, v_min, v_max); | |
| } | |
| return changed; | |
| }; | |
| auto multi_slider = [&slider]<std::size_t N, typename T2>( | |
| ImGuiSliderFunc<T2> auto slider_func, | |
| std::string_view label, | |
| glm::vec<N, T2> * v, | |
| glm::vec<N, T2> const & v_min, | |
| glm::vec<N, T2> const & v_max, | |
| value_or_ref_t<T2> small_increment, | |
| value_or_ref_t<T2> large_increment | |
| ) -> bool { | |
| ImGui::BeginGroup(); | |
| ImGui::PushID(label.data()); | |
| ImGui::PushItemWidth( | |
| ImGui::CalcItemWidth() / static_cast<float>(N) - | |
| ImGui::GetStyle().ItemInnerSpacing.x * (static_cast<float>(N) - 1.0f) / static_cast<float>(N) | |
| ); | |
| bool changed = false; | |
| for (std::size_t i = 0; i < N; ++i) { | |
| ImGui::PushID(static_cast<int>(i)); | |
| changed |= slider(slider_func, "", &(*v)[i], v_min[i], v_max[i], small_increment, large_increment); | |
| if (i != N - 1) { | |
| ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x); | |
| } | |
| ImGui::PopID(); | |
| } | |
| ImGui::PopItemWidth(); | |
| // mimic the calculation of ImGui::SliderScalarN for the label position | |
| ImGui::SameLine(0, ImGui::GetStyle().FramePadding.x - ImGui::GetStyle().ItemInnerSpacing.x); | |
| ImGui::LabelText("", "%s", label.data()); | |
| ImGui::PopID(); | |
| ImGui::EndGroup(); | |
| return changed; | |
| }; | |
| if constexpr (std::same_as<T, float>) { | |
| return slider(&ImGui::SliderFloat, label, v, v_min, v_max, 0.01f, 0.1f); | |
| } else if constexpr (std::same_as<T, int32_t>) { | |
| return slider(&ImGui::SliderInt, label, v, v_min, v_max, 1, 10); | |
| } else if constexpr (std::same_as<T, uint32_t>) { | |
| int temp = static_cast<int>(*v); | |
| int temp_min = static_cast<int>(v_min); | |
| int temp_max = static_cast<int>(v_max); | |
| if (slider(&ImGui::SliderInt, label, &temp, temp_min, temp_max, 1, 10)) { | |
| *v = static_cast<uint32_t>(temp); | |
| return true; | |
| } | |
| return false; | |
| } else if constexpr (std::same_as<T, glm::vec2>) { | |
| return multi_slider.template operator()<2, float>(&ImGui::SliderFloat, label, v, v_min, v_max, 0.01f, 0.1f); | |
| } else if constexpr (std::same_as<T, glm::vec3>) { | |
| return multi_slider.template operator()<3, float>(&ImGui::SliderFloat, label, v, v_min, v_max, 0.01f, 0.1f); | |
| } else if constexpr (std::same_as<T, glm::vec4>) { | |
| return multi_slider.template operator()<4, float>(&ImGui::SliderFloat, label, v, v_min, v_max, 0.01f, 0.1f); | |
| } else { | |
| static_assert(delayed_false_v<T>, "unsupported slider type"); | |
| } | |
| return false; | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment