Nav: rework NavEnableSetMousePos application so scrolling is applied more consistently in NavCalcPreferredRefPos() + moved at end of NavUpdate() after the last SetScroll have been done.

This commit is contained in:
ocornut 2021-10-27 12:41:49 +02:00
parent 8361ed1f54
commit 8d361c47fb

View File

@ -9232,7 +9232,8 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
static ImVec2 ImGui::NavCalcPreferredRefPos() static ImVec2 ImGui::NavCalcPreferredRefPos()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) ImGuiWindow* window = g.NavWindow;
if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window)
{ {
// Mouse (we need a fallback in case the mouse becomes invalid after being used) // Mouse (we need a fallback in case the mouse becomes invalid after being used)
if (IsMousePosValid(&g.IO.MousePos)) if (IsMousePosValid(&g.IO.MousePos))
@ -9241,8 +9242,14 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
} }
else else
{ {
// When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
ImRect rect_rel = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[g.NavLayer]); // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
{
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
rect_rel.Translate(window->Scroll - next_scroll);
}
ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
ImGuiViewport* viewport = GetMainViewport(); ImGuiViewport* viewport = GetMainViewport();
return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
@ -9341,19 +9348,12 @@ static void ImGui::NavUpdate()
g.NavTabbingInputableRemaining = 0; g.NavTabbingInputableRemaining = 0;
g.NavMoveSubmitted = g.NavMoveScoringItems = false; g.NavMoveSubmitted = g.NavMoveScoringItems = false;
// Apply application mouse position movement, after we had a chance to process move request result. // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
bool set_mouse_pos = false;
if (g.NavMousePosDirty && g.NavIdIsAlive) if (g.NavMousePosDirty && g.NavIdIsAlive)
{ if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
// Set mouse position given our knowledge of the navigated item position from last frame set_mouse_pos = true;
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) g.NavMousePosDirty = false;
if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
{
io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
io.WantSetMousePos = true;
//IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
}
g.NavMousePosDirty = false;
}
g.NavIdIsAlive = false; g.NavIdIsAlive = false;
g.NavJustTabbedId = 0; g.NavJustTabbedId = 0;
IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
@ -9450,6 +9450,15 @@ static void ImGui::NavUpdate()
g.NavDisableMouseHover = g.NavMousePosDirty = false; g.NavDisableMouseHover = g.NavMousePosDirty = false;
} }
// Update mouse position if requested
// (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)
if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
{
io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
io.WantSetMousePos = true;
//IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
}
// [DEBUG] // [DEBUG]
g.NavScoringDebugCount = 0; g.NavScoringDebugCount = 0;
#if IMGUI_DEBUG_NAV_RECTS #if IMGUI_DEBUG_NAV_RECTS
@ -9614,23 +9623,17 @@ void ImGui::NavMoveRequestApplyResult()
// Scroll to keep newly navigated item fully into view. // Scroll to keep newly navigated item fully into view.
if (g.NavLayer == ImGuiNavLayer_Main) if (g.NavLayer == ImGuiNavLayer_Main)
{ {
ImVec2 delta_scroll;
if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
{ {
// FIXME: Should remove this // FIXME: Should remove this
float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
delta_scroll.y = scroll_target - result->Window->Scroll.y;
SetScrollY(result->Window, scroll_target); SetScrollY(result->Window, scroll_target);
} }
else else
{ {
ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);
delta_scroll = ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
} }
// Offset our result position so mouse position can be applied immediately after in NavUpdate()
result->RectRel.TranslateX(-delta_scroll.x);
result->RectRel.TranslateY(-delta_scroll.y);
} }
ClearActiveID(); ClearActiveID();