Refactor: moved LockWheelingWindow(), FindBestWheelingWindow(), UpdateMouseWheel() to INPUTS section

This commit is contained in:
ocornut 2022-12-06 12:12:10 +01:00
parent f9ab2a0e9f
commit f83da768b6

304
imgui.cpp
View File

@ -4446,156 +4446,6 @@ static void ImGui::UpdateMouseInputs()
}
}
static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
{
ImGuiContext& g = *GImGui;
if (window)
g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
else
g.WheelingWindowReleaseTimer = 0.0f;
if (g.WheelingWindow == window)
return;
IMGUI_DEBUG_LOG_IO("LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
g.WheelingWindow = window;
g.WheelingWindowRefMousePos = g.IO.MousePos;
if (window == NULL)
{
g.WheelingWindowStartFrame = -1;
g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
}
}
static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
{
// For each axis, find window in the hierarchy that may want to use scrolling
ImGuiContext& g = *GImGui;
ImGuiWindow* windows[2] = { NULL, NULL };
for (int axis = 0; axis < 2; axis++)
if (wheel[axis] != 0.0f)
for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
{
// Bubble up into parent window if:
// - a child window doesn't allow any scrolling.
// - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
//// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
//const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
break; // select this window
}
if (windows[0] == NULL && windows[1] == NULL)
return NULL;
// If there's only one window or only one axis then there's no ambiguity
if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
return windows[1] ? windows[1] : windows[0];
// If candidate are different windows we need to decide which one to prioritize
// - First frame: only find a winner if one axis is zero.
// - Subsequent frames: only find a winner when one is more than the other.
if (g.WheelingWindowStartFrame == -1)
g.WheelingWindowStartFrame = g.FrameCount;
if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
{
g.WheelingWindowWheelRemainder = wheel;
return NULL;
}
return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
}
void ImGui::UpdateMouseWheel()
{
ImGuiContext& g = *GImGui;
// Reset the locked window if we move the mouse or after the timer elapses.
// FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795)
if (g.WheelingWindow != NULL)
{
g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
g.WheelingWindowReleaseTimer = 0.0f;
if (g.WheelingWindowReleaseTimer <= 0.0f)
LockWheelingWindow(NULL, 0.0f);
}
ImVec2 wheel;
wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_None) ? g.IO.MouseWheelH : 0.0f;
wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_None) ? g.IO.MouseWheel : 0.0f;
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
if (!mouse_window || mouse_window->Collapsed)
return;
// Zoom / Scale window
// FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
{
LockWheelingWindow(mouse_window, wheel.y);
ImGuiWindow* window = mouse_window;
const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
const float scale = new_font_scale / window->FontWindowScale;
window->FontWindowScale = new_font_scale;
if (window == window->RootWindow)
{
const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
SetWindowPos(window, window->Pos + offset, 0);
window->Size = ImFloor(window->Size * scale);
window->SizeFull = ImFloor(window->SizeFull * scale);
}
return;
}
if (g.IO.KeyCtrl)
return;
// Mouse wheel scrolling
// As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
// (we avoid doing it on OSX as it the OS input layer handles this already)
const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors;
if (swap_axis)
{
wheel.x = wheel.y;
wheel.y = 0.0f;
}
// Maintain a rough average of moving magnitude on both axises
// FIXME: should by based on wall clock time rather than frame-counter
g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
// In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
wheel += g.WheelingWindowWheelRemainder;
g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
if (wheel.x == 0.0f && wheel.y == 0.0f)
return;
// Mouse wheel scrolling: find target and apply
// - don't renew lock if axis doesn't apply on the window.
// - select a main axis when both axises are being moved.
if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
{
bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
if (do_scroll[ImGuiAxis_X])
{
LockWheelingWindow(window, wheel.x);
float max_step = window->InnerRect.GetWidth() * 0.67f;
float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
}
if (do_scroll[ImGuiAxis_Y])
{
LockWheelingWindow(window, wheel.y);
float max_step = window->InnerRect.GetHeight() * 0.67f;
float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
}
}
}
// 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)
void ImGui::UpdateHoveredWindowAndCaptureFlags()
{
@ -7922,6 +7772,10 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
// - GetMouseCursor()
// - SetMouseCursor()
//-----------------------------------------------------------------------------
// - LockWheelingWindow [Internal]
// - FindBestWheelingWindow [Internal]
// - UpdateMouseWheel() [Internal]
//-----------------------------------------------------------------------------
// - SetNextFrameWantCaptureKeyboard()
// - SetNextFrameWantCaptureMouse()
//-----------------------------------------------------------------------------
@ -8506,6 +8360,156 @@ void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
g.MouseCursor = cursor_type;
}
static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
{
ImGuiContext& g = *GImGui;
if (window)
g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
else
g.WheelingWindowReleaseTimer = 0.0f;
if (g.WheelingWindow == window)
return;
IMGUI_DEBUG_LOG_IO("LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
g.WheelingWindow = window;
g.WheelingWindowRefMousePos = g.IO.MousePos;
if (window == NULL)
{
g.WheelingWindowStartFrame = -1;
g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
}
}
static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
{
// For each axis, find window in the hierarchy that may want to use scrolling
ImGuiContext& g = *GImGui;
ImGuiWindow* windows[2] = { NULL, NULL };
for (int axis = 0; axis < 2; axis++)
if (wheel[axis] != 0.0f)
for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
{
// Bubble up into parent window if:
// - a child window doesn't allow any scrolling.
// - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
//// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
//const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
break; // select this window
}
if (windows[0] == NULL && windows[1] == NULL)
return NULL;
// If there's only one window or only one axis then there's no ambiguity
if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
return windows[1] ? windows[1] : windows[0];
// If candidate are different windows we need to decide which one to prioritize
// - First frame: only find a winner if one axis is zero.
// - Subsequent frames: only find a winner when one is more than the other.
if (g.WheelingWindowStartFrame == -1)
g.WheelingWindowStartFrame = g.FrameCount;
if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
{
g.WheelingWindowWheelRemainder = wheel;
return NULL;
}
return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
}
// Called by NewFrame()
void ImGui::UpdateMouseWheel()
{
// Reset the locked window if we move the mouse or after the timer elapses.
// FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795)
ImGuiContext& g = *GImGui;
if (g.WheelingWindow != NULL)
{
g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
g.WheelingWindowReleaseTimer = 0.0f;
if (g.WheelingWindowReleaseTimer <= 0.0f)
LockWheelingWindow(NULL, 0.0f);
}
ImVec2 wheel;
wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_None) ? g.IO.MouseWheelH : 0.0f;
wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_None) ? g.IO.MouseWheel : 0.0f;
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
if (!mouse_window || mouse_window->Collapsed)
return;
// Zoom / Scale window
// FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
{
LockWheelingWindow(mouse_window, wheel.y);
ImGuiWindow* window = mouse_window;
const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
const float scale = new_font_scale / window->FontWindowScale;
window->FontWindowScale = new_font_scale;
if (window == window->RootWindow)
{
const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
SetWindowPos(window, window->Pos + offset, 0);
window->Size = ImFloor(window->Size * scale);
window->SizeFull = ImFloor(window->SizeFull * scale);
}
return;
}
if (g.IO.KeyCtrl)
return;
// Mouse wheel scrolling
// As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
// (we avoid doing it on OSX as it the OS input layer handles this already)
const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors;
if (swap_axis)
{
wheel.x = wheel.y;
wheel.y = 0.0f;
}
// Maintain a rough average of moving magnitude on both axises
// FIXME: should by based on wall clock time rather than frame-counter
g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
// In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
wheel += g.WheelingWindowWheelRemainder;
g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
if (wheel.x == 0.0f && wheel.y == 0.0f)
return;
// Mouse wheel scrolling: find target and apply
// - don't renew lock if axis doesn't apply on the window.
// - select a main axis when both axises are being moved.
if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
{
bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
if (do_scroll[ImGuiAxis_X])
{
LockWheelingWindow(window, wheel.x);
float max_step = window->InnerRect.GetWidth() * 0.67f;
float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
}
if (do_scroll[ImGuiAxis_Y])
{
LockWheelingWindow(window, wheel.y);
float max_step = window->InnerRect.GetHeight() * 0.67f;
float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
}
}
}
void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
{
ImGuiContext& g = *GImGui;