Basic Smooth Scrolling Example
Version/Branch of Dear ImGui:
Version 1.91.8, Branch: master
Back-ends:
imgui_impl_sdl3.cpp + imgui_impl_opengl3.cpp
Compiler, OS:
Linux + Clang 19.1.7
Details:
I found a workaround for smooth scrolling without modifying ImGui code, so I thought I'd share it here for anyone looking for a way to do this. It's a very simple and dumb way to do it, I am not proposing this be added to ImGui itself of course, but it works well enough for my needs so maybe others will find it useful too.
Screenshots/Video:
https://github.com/user-attachments/assets/a79a888f-c79a-4223-a561-31f657b288cc
Minimal, Complete and Verifiable Example code:
Parts marked as // ... indicate sections of code that aren't relevant to the logic of this workaround, you can refer to the complete ImGui backends examples and slot in this code where relevant.
// ... Your main loop
ImGuiIO& io = ImGui::GetIO();
const float scroll_multiplier = 2.0f;
const float scroll_smoothing = 8.0f;
static ImVec2 scroll_energy = ImVec2(0.0f, 0.0f);
SDL_Event event;
while (SDL_PollEvent(&event)) {
if(event.type == SDL_EVENT_MOUSE_WHEEL && event.window.windowID == SDL_GetWindowID(window)) {
// Handle wheel events locally to apply smooth scrolling
event.wheel.x *= scroll_multiplier;
event.wheel.y *= scroll_multiplier;
// Immediately stop if direction changes
if(scroll_energy.x * event.wheel.x < 0.0f) {
scroll_energy.x = 0.0f;
}
if(scroll_energy.y * event.wheel.y < 0.0f) {
scroll_energy.y = 0.0f;
}
scroll_energy.x += event.wheel.x;
scroll_energy.y += event.wheel.y;
} else {
ImGui_ImplSDL3_ProcessEvent(&event);
}
// ... Handle close events if relevant
}
// ...
// Apply smooth scrolling (MUST be before ImGui::NewFrame())
ImVec2 scroll_now = ImVec2(0.0f, 0.0f);
if(std::abs(scroll_energy.x) > 0.01f) {
scroll_now.x = scroll_energy.x * io.DeltaTime * scroll_smoothing;
scroll_energy.x -= scroll_now.x;
} else {
// Cutoff smoothing when it's basically stopped
scroll_energy.x = 0.0f;
}
if(std::abs(scroll_energy.y) > 0.01f) {
scroll_now.y = scroll_energy.y * io.DeltaTime * scroll_smoothing;
scroll_energy.y -= scroll_now.y;
} else {
// Cutoff smoothing when it's basically stopped
scroll_energy.y = 0.0f;
}
io.MouseWheel = scroll_now.y;
io.MouseWheelH = -scroll_now.x;
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
// ... Your draw code
Note that:
- I am coding my project in C, not C++, so I tried my best to translate the relevant code but haven't tested it. The concept should be fairly clear anyway.
- I believe it should work with other backends, as long as you can handle their scroll events yourself without ImGui seeing them.
You calling UpdateInputEvents() before NewFrame() means it's going to be called 2 times every frame, which is incorrect and it going to cause problems.
The other problem is to test whether this would work with mouse/drivers that are already submitting smooth scrolling value as inputs.
Linking to #7348, #2675
With a quick test with my trackpad: scrolling works as expected but double tap (IsMouseDoubleClicked) stopped working
That makes a lot of sense actually, and aligns with what Omar mentioned. Then yeah what I proposed is definitely flawed in that state, I just never noticed as I don't have anything using double clicks in my apps using imgui.
I suppose the other approach of not passing the events to the imgui SDL3 backend and processing downstream before NewFrame() would work fine then, I will update the post.
Thank you both for the feedback!
Updated the snippet, no longer uses the internal UpdateInputEvents(), and also made it smooth horizontal scrolling. From what I can see double clicking should work fine now, I can double click the titlebar to collapse a ImGui window for example
Working quite well!