@@ -4274,6 +4274,50 @@ static void LockWheelingWindow(ImGuiWindow* window)
4274
4274
IMGUI_DEBUG_LOG_IO("LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
4275
4275
g.WheelingWindow = window;
4276
4276
g.WheelingWindowRefMousePos = g.IO.MousePos;
4277
+ if (window == NULL)
4278
+ {
4279
+ g.WheelingWindowStartFrame = -1;
4280
+ g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
4281
+ }
4282
+ }
4283
+
4284
+ static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
4285
+ {
4286
+ // For each axis, find window in the hierarchy that may want to use scrolling
4287
+ ImGuiContext& g = *GImGui;
4288
+ ImGuiWindow* windows[2] = { NULL, NULL };
4289
+ for (int axis = 0; axis < 2; axis++)
4290
+ if (wheel[axis] != 0.0f)
4291
+ for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
4292
+ {
4293
+ // Bubble up into parent window if:
4294
+ // - a child window doesn't allow any scrolling.
4295
+ // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
4296
+ //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
4297
+ const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
4298
+ const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
4299
+ //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
4300
+ if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
4301
+ break; // select this window
4302
+ }
4303
+ if (windows[0] == NULL && windows[1] == NULL)
4304
+ return NULL;
4305
+
4306
+ // If there's only one window or only one axis then there's no ambiguity
4307
+ if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
4308
+ return windows[1] ? windows[1] : windows[0];
4309
+
4310
+ // If candidate are different windows we need to decide which one to prioritize
4311
+ // - First frame: only find a winner if one axis is zero.
4312
+ // - Subsequent frames: only find a winner when one is more than the other.
4313
+ if (g.WheelingWindowStartFrame == -1)
4314
+ g.WheelingWindowStartFrame = g.FrameCount;
4315
+ if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
4316
+ {
4317
+ g.WheelingWindowWheelRemainder = wheel;
4318
+ return NULL;
4319
+ }
4320
+ return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
4277
4321
}
4278
4322
4279
4323
void ImGui::UpdateMouseWheel()
@@ -4297,8 +4341,6 @@ void ImGui::UpdateMouseWheel()
4297
4341
ImVec2 wheel;
4298
4342
wheel.x = (!hovered_id_using_mouse_wheel && !active_id_using_mouse_wheel_x) ? g.IO.MouseWheelH : 0.0f;
4299
4343
wheel.y = (!hovered_id_using_mouse_wheel && !active_id_using_mouse_wheel_y) ? g.IO.MouseWheel : 0;
4300
- if (wheel.x == 0.0f && wheel.y == 0.0f)
4301
- return;
4302
4344
4303
4345
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
4304
4346
ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
@@ -4336,39 +4378,41 @@ void ImGui::UpdateMouseWheel()
4336
4378
wheel.y = 0.0f;
4337
4379
}
4338
4380
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
- }
4381
+ // Maintain a rough average of moving magnitude on both axises
4382
+ // FIXME: should by based on wall clock time rather than frame-counter
4383
+ g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
4384
+ g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
4357
4385
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;
4386
+ // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
4387
+ wheel += g.WheelingWindowWheelRemainder;
4388
+ g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
4389
+ if (wheel.x == 0.0f && wheel.y == 0.0f)
4390
+ return;
4391
+
4392
+ // Mouse wheel scrolling: find target and apply
4393
+ // - don't renew lock if axis doesn't apply on the window.
4394
+ // - select a main axis when both axises are being moved.
4395
+ if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
4364
4396
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
4365
4397
{
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);
4398
+ bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
4399
+ if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
4400
+ do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
4401
+ if (do_scroll[ImGuiAxis_X])
4402
+ {
4403
+ LockWheelingWindow(window);
4404
+ float max_step = window->InnerRect.GetWidth() * 0.67f;
4405
+ float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
4406
+ SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
4407
+ }
4408
+ if (do_scroll[ImGuiAxis_Y])
4409
+ {
4410
+ LockWheelingWindow(window);
4411
+ float max_step = window->InnerRect.GetHeight() * 0.67f;
4412
+ float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
4413
+ SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
4414
+ }
4370
4415
}
4371
- }
4372
4416
}
4373
4417
4374
4418
// 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)
@@ -8132,7 +8176,7 @@ static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
8132
8176
ImGuiContext& g = *GImGui;
8133
8177
if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("%s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f, %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; }
8134
8178
if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; }
8135
- if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.1f , %.1f )\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; }
8179
+ if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.3f , %.3f )\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; }
8136
8180
if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; }
8137
8181
if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
8138
8182
if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
@@ -12934,6 +12978,13 @@ void ImGui::ShowMetricsWindow(bool* p_open)
12934
12978
Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
12935
12979
Unindent();
12936
12980
12981
+ Text("MOUSE WHEELING");
12982
+ Indent();
12983
+ Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
12984
+ Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
12985
+ Text("WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? "X" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? "Y" : "<none>");
12986
+ Unindent();
12987
+
12937
12988
TreePop();
12938
12989
}
12939
12990
0 commit comments