Skip to content

Instantly share code, notes, and snippets.

@Folling
Last active November 16, 2025 12:07
Show Gist options
  • Select an option

  • Save Folling/06c223badea6fb7da76d8039947f2398 to your computer and use it in GitHub Desktop.

Select an option

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
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