Skip to content

Commit 26f14e0

Browse files
committed
Scrolling: Made mouse-wheel scrolling lock the underlying window until the mouse is moved again or until a short delay expires (2 seconds). This allow uninterrupted scroll even if child windows are passing under the mouse cursor. (ocornut#2604)
1 parent dcd03f6 commit 26f14e0

File tree

3 files changed

+41
-7
lines changed

3 files changed

+41
-7
lines changed

docs/CHANGELOG.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ Other Changes:
5353
any more. Forwarding can still be disabled by setting ImGuiWindowFlags_NoInputs. (amend #1502, #1380).
5454
- Window: Fixed old SetWindowFontScale() api value from not being inherited by child window. Added
5555
comments about the right way to scale your UI (load a font at the right side, rebuild atlas, scale style).
56-
- Scrollbar: Avoid overlapping the opposite side when window (often a child window) is forcibly too small.
5756
- Combo: Hide arrow when there's not enough space even for the square button.
5857
- TabBar: Fixed unfocused tab bar separator color (was using ImGuiCol_Tab, should use ImGuiCol_TabUnfocusedActive).
5958
- Columns: Fixed a regression from 1.71 where the right-side of the contents rectangle within each column
@@ -67,12 +66,16 @@ Other Changes:
6766
- InputTextMultiline: Fixed vertical scrolling tracking glitch.
6867
- Word-wrapping: Fixed overzealous word-wrapping when glyph edge lands exactly on the limit. Because
6968
of this, auto-fitting exactly unwrapped text would make it wrap. (fixes initial 1.15 commit, 78645a7d).
69+
- Scrolling: Made mouse-wheel scrolling lock the underlying window until the mouse is moved again or
70+
until a short delay expires (2 seconds). This allow uninterrupted scroll even if child windows are
71+
passing under the mouse cursor. (#2604)
7072
- Scrolling: Made it possible for mouse wheel and navigation-triggered scrolling to override a call to
7173
SetScrollX()/SetScrollY(), making it possible to use a simpler stateless pattern for auto-scrolling:
7274
// (Submit items..)
7375
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) // Keep scrolling at the bottom if already
7476
ImGui::SetScrollHereY(1.0f);
7577
- Scrolling: Added SetScrollHereX(), SetScrollFromPosX() for completeness. (#1580) [@kevreco]
78+
- Scrollbar: Avoid overlapping the opposite side when window (often a child window) is forcibly too small.
7679
- Style: Attenuated default opacity of ImGuiCol_Separator in Classic and Light styles.
7780
- Style: Added style.ColorButtonPosition (left/right, defaults to ImGuiDir_Right) to move the color button
7881
of ColorEdit3/ColorEdit4 functions to either side of the inputs.

imgui.cpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,7 @@ static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time
10421042
// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
10431043
static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
10441044
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
1045+
static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certaint time, unless mouse moved.
10451046

10461047
//-------------------------------------------------------------------------
10471048
// [SECTION] FORWARD DECLARATIONS
@@ -3465,19 +3466,45 @@ static void ImGui::UpdateMouseInputs()
34653466
}
34663467
}
34673468

3468-
void ImGui::UpdateMouseWheel()
3469+
static void StartLockWheelingWindow(ImGuiWindow* window)
34693470
{
34703471
ImGuiContext& g = *GImGui;
3471-
if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
3472+
if (g.WheelingWindow == window)
34723473
return;
3474+
g.WheelingWindow = window;
3475+
g.WheelingWindowRefMousePos = g.IO.MousePos;
3476+
g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3477+
}
3478+
3479+
void ImGui::UpdateMouseWheel()
3480+
{
3481+
ImGuiContext& g = *GImGui;
3482+
3483+
// Reset the locked window if we move the mouse or after the timer elapses
3484+
if (g.WheelingWindow != NULL)
3485+
{
3486+
g.WheelingWindowTimer -= g.IO.DeltaTime;
3487+
if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3488+
g.WheelingWindowTimer = 0.0f;
3489+
if (g.WheelingWindowTimer <= 0.0f)
3490+
{
3491+
g.WheelingWindow = NULL;
3492+
g.WheelingWindowTimer = 0.0f;
3493+
}
3494+
}
3495+
34733496
if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
34743497
return;
34753498

3499+
ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3500+
if (!window || window->Collapsed)
3501+
return;
3502+
34763503
// Zoom / Scale window
34773504
// FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
34783505
if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
34793506
{
3480-
ImGuiWindow* window = g.HoveredWindow;
3507+
StartLockWheelingWindow(window);
34813508
const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
34823509
const float scale = new_font_scale / window->FontWindowScale;
34833510
window->FontWindowScale = new_font_scale;
@@ -3493,13 +3520,12 @@ void ImGui::UpdateMouseWheel()
34933520

34943521
// Mouse wheel scrolling
34953522
// If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3496-
// FIXME: Lock scrolling window while not moving (see #2604)
34973523

34983524
// Vertical Mouse Wheel scrolling
34993525
const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
35003526
if (wheel_y != 0.0f && !g.IO.KeyCtrl)
35013527
{
3502-
ImGuiWindow* window = g.HoveredWindow;
3528+
StartLockWheelingWindow(window);
35033529
while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
35043530
window = window->ParentWindow;
35053531
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
@@ -3514,7 +3540,7 @@ void ImGui::UpdateMouseWheel()
35143540
const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
35153541
if (wheel_x != 0.0f && !g.IO.KeyCtrl)
35163542
{
3517-
ImGuiWindow* window = g.HoveredWindow;
3543+
StartLockWheelingWindow(window);
35183544
while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
35193545
window = window->ParentWindow;
35203546
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))

imgui_internal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,9 @@ struct ImGuiContext
868868
ImGuiWindow* HoveredWindow; // Will catch mouse inputs
869869
ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only)
870870
ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow.
871+
ImGuiWindow* WheelingWindow;
872+
ImVec2 WheelingWindowRefMousePos;
873+
float WheelingWindowTimer;
871874

872875
// Item/widgets state and tracking information
873876
ImGuiID HoveredId; // Hovered widget
@@ -1058,6 +1061,8 @@ struct ImGuiContext
10581061
HoveredWindow = NULL;
10591062
HoveredRootWindow = NULL;
10601063
MovingWindow = NULL;
1064+
WheelingWindow = NULL;
1065+
WheelingWindowTimer = 0.0f;
10611066

10621067
HoveredId = 0;
10631068
HoveredIdAllowOverlap = false;

0 commit comments

Comments
 (0)