Reorganised windows moving code, documented a lag in FindHoveredWindow(), fixing lag whole moving windows (#635)

This commit is contained in:
ocornut 2016-05-06 11:31:32 +02:00
parent 67df0ba185
commit 313d388bba
2 changed files with 41 additions and 34 deletions

View File

@ -2007,8 +2007,35 @@ void ImGui::NewFrame()
g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdPreviousFrame = g.ActiveId;
g.ActiveIdIsAlive = false; g.ActiveIdIsAlive = false;
g.ActiveIdIsJustActivated = 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.MovedWindow = NULL;
g.MovedWindowMoveId = 0;
}
// Delay saving settings so we don't spam disk too much // Delay saving settings so we don't spam disk too much
if (g.SettingsDirtyTimer > 0.0f) 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 // 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)) if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow))
g.HoveredRootWindow = g.HoveredWindow->RootWindow; g.HoveredRootWindow = g.HoveredWindow->RootWindow;
else 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()) if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
{ {
@ -2420,8 +2447,6 @@ void ImGui::EndFrame()
ImGui::End(); ImGui::End();
// Click to focus window and start moving (after we're done with all our widgets) // 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.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 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)) if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove))
{ {
g.MovedWindow = g.HoveredWindow; 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) 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 // 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) static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
{ {
ImGuiState& g = *GImGui; ImGuiState& g = *GImGui;
@ -2856,7 +2883,7 @@ static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
continue; continue;
// Using the clipped AABB so a child window will typically be clipped by its parent. // 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)) if (bb.Contains(pos))
return window; 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. 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 // Clamp position so it stays visible
if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) 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() // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
window->ClippedWindowRect = window->Rect(); window->WindowRectClipped = window->Rect();
window->ClippedWindowRect.Clip(window->ClipRect); window->WindowRectClipped.Clip(window->ClipRect);
// Pressing CTRL+C while holding on a window copy its content to the clipboard // 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. // 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; window->Collapsed = parent_window && parent_window->Collapsed;
if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 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 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) // (we could perform the check earlier in the function but it is simpler at this point)

View File

@ -375,7 +375,8 @@ struct ImGuiState
bool ActiveIdIsJustActivated; // Set at the time of activation for one frame bool ActiveIdIsJustActivated; // Set at the time of activation for one frame
bool ActiveIdAllowOverlap; // Set only by active widget bool ActiveIdAllowOverlap; // Set only by active widget
ImGuiWindow* ActiveIdWindow; 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<ImGuiIniData> Settings; // .ini Settings ImVector<ImGuiIniData> Settings; // .ini Settings
float SettingsDirtyTimer; // Save .ini settinngs on disk when time reaches zero float SettingsDirtyTimer; // Save .ini settinngs on disk when time reaches zero
ImVector<ImGuiColMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector<ImGuiColMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
@ -460,6 +461,7 @@ struct ImGuiState
ActiveIdAllowOverlap = false; ActiveIdAllowOverlap = false;
ActiveIdWindow = NULL; ActiveIdWindow = NULL;
MovedWindow = NULL; MovedWindow = NULL;
MovedWindowMoveId = 0;
SettingsDirtyTimer = 0.0f; SettingsDirtyTimer = 0.0f;
SetNextWindowPosVal = ImVec2(0.0f, 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 ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame
ImVector<ImGuiID> IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack ImVector<ImGuiID> 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 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; int LastFrameActive;
float ItemWidthDefault; float ItemWidthDefault;
ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items