Skip to content

Commit a1fad4d

Browse files
committed
WIP Scrolling for 3795
1 parent 4529abf commit a1fad4d

File tree

3 files changed

+111
-30
lines changed

3 files changed

+111
-30
lines changed

imgui.cpp

+78-30
Original file line numberDiff line numberDiff line change
@@ -4269,13 +4269,74 @@ static void LockWheelingWindow(ImGuiWindow* window)
42694269
{
42704270
ImGuiContext& g = *GImGui;
42714271
g.WheelingWindowReleaseTimer = window ? WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER : 0.0f;
4272+
if (window == NULL)
4273+
g.WheelingWindowStartFrame = -1;
42724274
if (g.WheelingWindow == window)
42734275
return;
42744276
IMGUI_DEBUG_LOG_IO("LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
42754277
g.WheelingWindow = window;
42764278
g.WheelingWindowRefMousePos = g.IO.MousePos;
42774279
}
42784280

4281+
static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
4282+
{
4283+
ImGuiContext& g = *GImGui;
4284+
if (g.WheelingWindow != NULL)
4285+
return g.WheelingWindow;
4286+
4287+
// For each axis, find window in the hierarchy that may want to use scrolling
4288+
ImGuiWindow* candidates[2] = { NULL, NULL };
4289+
for (int axis = 0; axis < 2; axis++)
4290+
if (wheel[axis] != 0.0f)
4291+
{
4292+
// Bubble up into parent window if:
4293+
// - a child window doesn't allow any scrolling.
4294+
// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in.
4295+
// - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
4296+
ImGuiWindow* window = g.HoveredWindow;
4297+
while (window->Flags & ImGuiWindowFlags_ChildWindow)
4298+
{
4299+
const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
4300+
//const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
4301+
const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
4302+
if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
4303+
break; // select this window
4304+
window = window->ParentWindow; // move up
4305+
}
4306+
candidates[axis] = window;
4307+
}
4308+
if (candidates[ImGuiAxis_X] == NULL && candidates[ImGuiAxis_Y] == NULL)
4309+
return NULL;
4310+
4311+
// If there's only one window or only one axis then there's no ambiguity
4312+
if (candidates[ImGuiAxis_X] == candidates[ImGuiAxis_Y] || candidates[ImGuiAxis_X] == NULL || candidates[ImGuiAxis_Y] == NULL)
4313+
return candidates[ImGuiAxis_Y] ? candidates[ImGuiAxis_Y] : candidates[ImGuiAxis_X];
4314+
4315+
// If candidate are different windows we need to decide which one to prioritize
4316+
IM_ASSERT(candidates[ImGuiAxis_X] != candidates[ImGuiAxis_Y]);
4317+
if (g.WheelingWindowStartFrame == -1)
4318+
{
4319+
g.WheelingWindowStartFrame = g.FrameCount;
4320+
g.WheelingWindowPerAxisData[ImGuiAxis_X] = g.WheelingWindowPerAxisData[ImGuiAxis_Y] = 0.0f;
4321+
}
4322+
4323+
g.WheelingWindowPerAxisData[ImGuiAxis_X] += ImAbs(wheel.x);
4324+
g.WheelingWindowPerAxisData[ImGuiAxis_Y] += ImAbs(wheel.y);
4325+
4326+
// First frame: only find a winner if one axis is zero.
4327+
if (g.WheelingWindowStartFrame == g.FrameCount)
4328+
if (wheel.x != 0.0f && wheel.y != 0.0f)
4329+
return NULL;
4330+
4331+
// Subsequent frames: only find a winner when one is more than the other.
4332+
if (g.WheelingWindowPerAxisData[ImGuiAxis_X] > g.WheelingWindowPerAxisData[ImGuiAxis_Y])
4333+
return candidates[ImGuiAxis_X];
4334+
if (g.WheelingWindowPerAxisData[ImGuiAxis_Y] > g.WheelingWindowPerAxisData[ImGuiAxis_X])
4335+
return candidates[ImGuiAxis_Y];
4336+
4337+
return NULL;
4338+
}
4339+
42794340
void ImGui::UpdateMouseWheel()
42804341
{
42814342
ImGuiContext& g = *GImGui;
@@ -4336,39 +4397,26 @@ void ImGui::UpdateMouseWheel()
43364397
wheel.y = 0.0f;
43374398
}
43384399

4339-
// Vertical Mouse Wheel scrolling
4340-
// Bubble up into parent window if:
4341-
// - a child window doesn't allow any scrolling.
4342-
// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in.
4343-
// - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
4344-
if (wheel.y != 0.0f)
4345-
{
4346-
ImGuiWindow* window = mouse_window;
4347-
while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
4348-
window = window->ParentWindow;
4349-
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
4350-
{
4351-
LockWheelingWindow(mouse_window);
4352-
float max_step = window->InnerRect.GetHeight() * 0.67f;
4353-
float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
4354-
SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
4355-
}
4356-
}
4357-
4358-
// Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
4359-
if (wheel.x != 0.0f)
4360-
{
4361-
ImGuiWindow* window = mouse_window;
4362-
while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
4363-
window = window->ParentWindow;
4400+
// Mouse wheel scrolling: find target and apply
4401+
// - Don't renew lock if axis doesn't apply on the window.
4402+
if (ImGuiWindow* window = FindBestWheelingWindow(wheel))
43644403
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
43654404
{
4366-
LockWheelingWindow(mouse_window);
4367-
float max_step = window->InnerRect.GetWidth() * 0.67f;
4368-
float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
4369-
SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
4405+
if (wheel.x != 0.0f && window->ScrollMax.x != 0.0f)
4406+
{
4407+
LockWheelingWindow(window);
4408+
float max_step = window->InnerRect.GetWidth() * 0.67f;
4409+
float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
4410+
SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
4411+
}
4412+
if (wheel.y != 0.0f && window->ScrollMax.y != 0.0f)
4413+
{
4414+
LockWheelingWindow(window);
4415+
float max_step = window->InnerRect.GetHeight() * 0.67f;
4416+
float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
4417+
SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
4418+
}
43704419
}
4371-
}
43724420
}
43734421

43744422
// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)

imgui_demo.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ void* GImGuiDemoMarkerCallbackUserData = NULL;
241241
// - ShowDemoWindowInputs()
242242
//-----------------------------------------------------------------------------
243243

244+
#include "imgui_internal.h"
245+
244246
// Demonstrate most Dear ImGui features (this is big function!)
245247
// You may execute this function to experiment with the UI and understand what it does.
246248
// You may then search for keywords in the code when you are interested by a specific feature.
@@ -250,6 +252,32 @@ void ImGui::ShowDemoWindow(bool* p_open)
250252
// Most functions would normally just crash if the context is missing.
251253
IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!");
252254

255+
#if 1
256+
ImGui::Begin("#3795 - Window A");
257+
for (int n = 0; n < 10; n++)
258+
ImGui::Text("%*s some contents", n, "");
259+
ImGui::BeginChild("Child B", ImVec2(0.0f, 200.0f), true);
260+
ImGui::Text("this is a long line of text. this is a long line of text. this is a long line of text. this is a long line of text. ");
261+
ImGui::EndChild();
262+
for (int n = 0; n < 10; n++)
263+
ImGui::Text("%*s some contents", n, "");
264+
ImGui::End();
265+
266+
ImGui::Begin("#4559");
267+
ImGui::Text("Parent");
268+
ImGui::BeginChild("Child", ImVec2(0, 0), true);
269+
for (int n = 0; n < 10; n++)
270+
ImGui::Text("%*sthis is a long line of text. this is a long line of text. this is a long line of text. this is a long line of text. ", n * 2, "");
271+
ImGui::EndChild();
272+
ImGui::End();
273+
274+
275+
static float avg_wheel_y = 0.0f;
276+
avg_wheel_y = ImExponentialMovingAverage(avg_wheel_y, ImGui::GetIO().MouseWheel, 30);
277+
ImGui::Text("Avg Wheel %.2f", avg_wheel_y);
278+
279+
#endif
280+
253281
// Examples Apps (accessible from the "Examples" menu)
254282
static bool show_app_main_menu_bar = false;
255283
static bool show_app_documents = false;

imgui_internal.h

+5
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a)
465465
static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }
466466
static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
467467
static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; }
468+
static inline float ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; }
468469
IM_MSVC_RUNTIME_CHECKS_RESTORE
469470

470471
// Helpers: Geometry
@@ -1631,7 +1632,9 @@ struct ImGuiContext
16311632
ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow.
16321633
ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
16331634
ImVec2 WheelingWindowRefMousePos;
1635+
int WheelingWindowStartFrame;
16341636
float WheelingWindowReleaseTimer;
1637+
float WheelingWindowPerAxisData[2];
16351638

16361639
// Item/widgets state and tracking information
16371640
ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
@@ -1885,7 +1888,9 @@ struct ImGuiContext
18851888
HoveredWindowUnderMovingWindow = NULL;
18861889
MovingWindow = NULL;
18871890
WheelingWindow = NULL;
1891+
WheelingWindowStartFrame = -1;
18881892
WheelingWindowReleaseTimer = 0.0f;
1893+
WheelingWindowPerAxisData[0] = WheelingWindowPerAxisData[1] = 0.0f;
18891894

18901895
DebugHookIdInfo = 0;
18911896
HoveredId = HoveredIdPreviousFrame = 0;

0 commit comments

Comments
 (0)