From 313d388bba42e0634a02b976dcdbae238d25eddc Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 6 May 2016 11:31:32 +0200 Subject: [PATCH] Reorganised windows moving code, documented a lag in FindHoveredWindow(), fixing lag whole moving windows (#635) --- imgui.cpp | 69 ++++++++++++++++++++++++++---------------------- imgui_internal.h | 6 +++-- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 79969ebd8..613e8db6f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2007,8 +2007,35 @@ void ImGui::NewFrame() g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = false; g.ActiveIdIsJustActivated = false; - if (!g.ActiveId) + + // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. + if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId) + { + KeepAliveID(g.MovedWindowMoveId); + IM_ASSERT(g.MovedWindow && g.MovedWindow->RootWindow); + IM_ASSERT(g.MovedWindow->RootWindow->MoveID == g.MovedWindowMoveId); + if (g.IO.MouseDown[0]) + { + if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoMove)) + { + g.MovedWindow->PosFloat += g.IO.MouseDelta; + if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); + } + FocusWindow(g.MovedWindow); + } + else + { + SetActiveID(0); + g.MovedWindow = NULL; + g.MovedWindowMoveId = 0; + } + } + else + { g.MovedWindow = NULL; + g.MovedWindowMoveId = 0; + } // Delay saving settings so we don't spam disk too much if (g.SettingsDirtyTimer > 0.0f) @@ -2019,11 +2046,11 @@ void ImGui::NewFrame() } // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow - g.HoveredWindow = FindHoveredWindow(g.IO.MousePos, false); + g.HoveredWindow = g.MovedWindow ? g.MovedWindow : FindHoveredWindow(g.IO.MousePos, false); if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow)) g.HoveredRootWindow = g.HoveredWindow->RootWindow; else - g.HoveredRootWindow = FindHoveredWindow(g.IO.MousePos, true); + g.HoveredRootWindow = g.MovedWindow ? g.MovedWindow->RootWindow : FindHoveredWindow(g.IO.MousePos, true); if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow()) { @@ -2420,8 +2447,6 @@ void ImGui::EndFrame() ImGui::End(); // Click to focus window and start moving (after we're done with all our widgets) - if (!g.ActiveId) - g.MovedWindow = NULL; if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0]) { if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear @@ -2432,7 +2457,8 @@ void ImGui::EndFrame() if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove)) { g.MovedWindow = g.HoveredWindow; - SetActiveID(g.HoveredRootWindow->MoveID, g.HoveredRootWindow); + g.MovedWindowMoveId = g.HoveredRootWindow->MoveID; + SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow); } } else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL) @@ -2842,6 +2868,7 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items } // Find window given position, search front-to-back +// FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs) { ImGuiState& g = *GImGui; @@ -2856,7 +2883,7 @@ static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs) continue; // Using the clipped AABB so a child window will typically be clipped by its parent. - ImRect bb(window->ClippedWindowRect.Min - g.Style.TouchExtraPadding, window->ClippedWindowRect.Max + g.Style.TouchExtraPadding); + ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding); if (bb.Contains(pos)) return window; } @@ -3884,28 +3911,6 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->PosFloat = g.IO.MousePos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. } - // User moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. - KeepAliveID(window->MoveID); - if (g.ActiveId == window->MoveID) - { - if (g.IO.MouseDown[0]) - { - if (!(flags & ImGuiWindowFlags_NoMove)) - { - window->PosFloat += g.IO.MouseDelta; - if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - MarkSettingsDirty(); - } - IM_ASSERT(g.MovedWindow != NULL); - FocusWindow(g.MovedWindow); - } - else - { - SetActiveID(0); - g.MovedWindow = NULL; // Not strictly necessary but doing it for sanity. - } - } - // Clamp position so it stays visible if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) { @@ -4120,8 +4125,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us } // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() - window->ClippedWindowRect = window->Rect(); - window->ClippedWindowRect.Clip(window->ClipRect); + window->WindowRectClipped = window->Rect(); + window->WindowRectClipped.Clip(window->ClipRect); // Pressing CTRL+C while holding on a window copy its content to the clipboard // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. @@ -4158,7 +4163,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->Collapsed = parent_window && parent_window->Collapsed; if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - window->Collapsed |= (window->ClippedWindowRect.Min.x >= window->ClippedWindowRect.Max.x || window->ClippedWindowRect.Min.y >= window->ClippedWindowRect.Max.y); + window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y); // We also hide the window from rendering because we've already added its border to the command list. // (we could perform the check earlier in the function but it is simpler at this point) diff --git a/imgui_internal.h b/imgui_internal.h index 4027b060f..6c72a0515 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -375,7 +375,8 @@ struct ImGuiState bool ActiveIdIsJustActivated; // Set at the time of activation for one frame bool ActiveIdAllowOverlap; // Set only by active widget ImGuiWindow* ActiveIdWindow; - ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. Pointer is only valid if ActiveID is the "#MOVE" identifier of a window. + ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. + ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId ImVector Settings; // .ini Settings float SettingsDirtyTimer; // Save .ini settinngs on disk when time reaches zero ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() @@ -460,6 +461,7 @@ struct ImGuiState ActiveIdAllowOverlap = false; ActiveIdWindow = NULL; MovedWindow = NULL; + MovedWindowMoveId = 0; SettingsDirtyTimer = 0.0f; SetNextWindowPosVal = ImVec2(0.0f, 0.0f); @@ -627,7 +629,7 @@ struct IMGUI_API ImGuiWindow ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. - ImRect ClippedWindowRect; // = ClipRect just after setup in Begin() + ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. int LastFrameActive; float ItemWidthDefault; ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items