From 1b0e38df47e7b29932717e695e4a76e0bcbec55b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 22:14:28 +0100 Subject: [PATCH 1/4] Fixes crash/assert bug introduced in d845135 (#1651): would assert when showing the CTRL+Tab list and or fallback "...." tooltip. --- imgui.cpp | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 82fcb9af..4d9adb2b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3580,9 +3580,6 @@ void ImGui::EndFrame() return; IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?"); - g.FrameScopeActive = false; - g.FrameCountEnded = g.FrameCount; - // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f) { @@ -3590,6 +3587,31 @@ void ImGui::EndFrame() g.PlatformImeLastPos = g.PlatformImePos; } + // Show CTRL+TAB list window + if (g.NavWindowingTarget) + NavUpdateWindowingList(); + + // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) + if (g.DragDropActive) + { + bool is_delivered = g.DragDropPayload.Delivery; + bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); + if (is_delivered || is_elapsed) + ClearDragDrop(); + } + + // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. + if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount) + { + g.DragDropWithinSourceOrTarget = true; + SetTooltip("..."); + g.DragDropWithinSourceOrTarget = false; + } + + // End the frame scope. From this point we are not allowed to call Begin() any more. + g.FrameScopeActive = false; + g.FrameCountEnded = g.FrameCount; + // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). if (g.CurrentWindowStack.Size != 1) @@ -3611,27 +3633,6 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); - // Show CTRL+TAB list - if (g.NavWindowingTarget) - NavUpdateWindowingList(); - - // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) - if (g.DragDropActive) - { - bool is_delivered = g.DragDropPayload.Delivery; - bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); - if (is_delivered || is_elapsed) - ClearDragDrop(); - } - - // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. - if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount) - { - g.DragDropWithinSourceOrTarget = true; - SetTooltip("..."); - g.DragDropWithinSourceOrTarget = false; - } - // Initiate moving window if (g.ActiveId == 0 && g.HoveredId == 0) { From 3e30bfd6c9750588dbd0a0f904c3ed02c14c4c12 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 22:56:17 +0100 Subject: [PATCH 2/4] Revert "Fixes crash/assert bug introduced in d845135 (#1651): would assert when showing the CTRL+Tab list and or fallback "...." tooltip." This reverts commit 1b0e38df47e7b29932717e695e4a76e0bcbec55b. --- imgui.cpp | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4d9adb2b..82fcb9af 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3580,6 +3580,9 @@ void ImGui::EndFrame() return; IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?"); + g.FrameScopeActive = false; + g.FrameCountEnded = g.FrameCount; + // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f) { @@ -3587,31 +3590,6 @@ void ImGui::EndFrame() g.PlatformImeLastPos = g.PlatformImePos; } - // Show CTRL+TAB list window - if (g.NavWindowingTarget) - NavUpdateWindowingList(); - - // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) - if (g.DragDropActive) - { - bool is_delivered = g.DragDropPayload.Delivery; - bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); - if (is_delivered || is_elapsed) - ClearDragDrop(); - } - - // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. - if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount) - { - g.DragDropWithinSourceOrTarget = true; - SetTooltip("..."); - g.DragDropWithinSourceOrTarget = false; - } - - // End the frame scope. From this point we are not allowed to call Begin() any more. - g.FrameScopeActive = false; - g.FrameCountEnded = g.FrameCount; - // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). if (g.CurrentWindowStack.Size != 1) @@ -3633,6 +3611,27 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); + // Show CTRL+TAB list + if (g.NavWindowingTarget) + NavUpdateWindowingList(); + + // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) + if (g.DragDropActive) + { + bool is_delivered = g.DragDropPayload.Delivery; + bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); + if (is_delivered || is_elapsed) + ClearDragDrop(); + } + + // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. + if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount) + { + g.DragDropWithinSourceOrTarget = true; + SetTooltip("..."); + g.DragDropWithinSourceOrTarget = false; + } + // Initiate moving window if (g.ActiveId == 0 && g.HoveredId == 0) { From b3469fa94b79ad56e978130cd28f863f1cbe7217 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 22:52:09 +0100 Subject: [PATCH 3/4] Alternative fix for bug introduced in d845135 (#1651), fix CTRL+Tab and fallback tooltip. --- imgui.cpp | 14 +++++++++----- imgui_internal.h | 5 +++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 82fcb9af..25955ab3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3349,6 +3349,7 @@ void ImGui::NewFrame() // This fallback is particularly important as it avoid ImGui:: calls from crashing. SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); Begin("Debug##Default"); + g.FrameScopePushedImplicitWindow = true; #ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiTestEngineHook_PostNewFrame(); @@ -3580,9 +3581,6 @@ void ImGui::EndFrame() return; IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?"); - g.FrameScopeActive = false; - g.FrameCountEnded = g.FrameCount; - // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f) { @@ -3607,11 +3605,12 @@ void ImGui::EndFrame() } // Hide implicit/fallback "Debug" window if it hasn't been used + g.FrameScopePushedImplicitWindow = false; if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) g.CurrentWindow->Active = false; End(); - // Show CTRL+TAB list + // Show CTRL+TAB list window if (g.NavWindowingTarget) NavUpdateWindowingList(); @@ -3632,6 +3631,10 @@ void ImGui::EndFrame() g.DragDropWithinSourceOrTarget = false; } + // End frame + g.FrameScopeActive = false; + g.FrameCountEnded = g.FrameCount; + // Initiate moving window if (g.ActiveId == 0 && g.HoveredId == 0) { @@ -5283,11 +5286,12 @@ void ImGui::End() { ImGuiContext& g = *GImGui; - if (g.CurrentWindowStack.Size <= 1 && g.FrameScopeActive) + if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow) { IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!"); return; // FIXME-ERRORHANDLING } + IM_ASSERT(g.CurrentWindowStack.Size > 0); ImGuiWindow* window = g.CurrentWindow; diff --git a/imgui_internal.h b/imgui_internal.h index 60704564..d4d6f940 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -694,7 +694,8 @@ struct ImGuiTabBarSortItem struct ImGuiContext { bool Initialized; - bool FrameScopeActive; // Set by NewFrame(), cleared by EndFrame()/Render() + bool FrameScopeActive; // Set by NewFrame(), cleared by EndFrame() + bool FrameScopePushedImplicitWindow; // Set by NewFrame(), cleared by EndFrame() bool FontAtlasOwnedByContext; // Io.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; ImGuiStyle Style; @@ -859,7 +860,7 @@ struct ImGuiContext ImGuiContext(ImFontAtlas* shared_font_atlas) : OverlayDrawList(NULL) { Initialized = false; - FrameScopeActive = false; + FrameScopeActive = FrameScopePushedImplicitWindow = false; Font = NULL; FontSize = FontBaseSize = 0.0f; FontAtlasOwnedByContext = shared_font_atlas ? false : true; From 237109caa57bd1e34e339f07c0c72f07c0034129 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 23:05:59 +0100 Subject: [PATCH 4/4] Internals: Extracted code out of EndFrame() into UpdateMouseMovingWindowEndFrame() --- imgui.cpp | 99 ++++++++++++++++++++++++++---------------------- imgui_internal.h | 3 +- 2 files changed, 56 insertions(+), 46 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 25955ab3..ec4788a1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2969,7 +2969,7 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) // Handle mouse moving window // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() -void ImGui::UpdateMouseMovingWindow() +void ImGui::UpdateMouseMovingWindowNewFrame() { ImGuiContext& g = *GImGui; if (g.MovingWindow != NULL) @@ -3007,6 +3007,56 @@ void ImGui::UpdateMouseMovingWindow() } } +// Initiate moving window, handle left-click and right-click focus +void ImGui::UpdateMouseMovingWindowEndFrame() +{ + // Initiate moving window + ImGuiContext& g = *GImGui; + if (g.ActiveId != 0 || g.HoveredId != 0) + return; + + // Unless we just made a window/popup appear + if (g.NavWindow && g.NavWindow->Appearing) + return; + + // Click to focus window and start moving (after we're done with all our widgets) + if (g.IO.MouseClicked[0]) + { + if (g.HoveredRootWindow != NULL) + { + StartMouseMovingWindow(g.HoveredWindow); + if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) + g.MovingWindow = NULL; + } + else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL) + { + FocusWindow(NULL); // Clicking on void disable focus + } + } + + // With right mouse button we close popups without changing focus + // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) + if (g.IO.MouseClicked[1]) + { + // Find the top-most window between HoveredWindow and the front most Modal Window. + // This is where we can trim the popup stack. + ImGuiWindow* modal = GetFrontMostPopupModal(); + bool hovered_window_above_modal = false; + if (modal == NULL) + hovered_window_above_modal = true; + for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (window == modal) + break; + if (window == g.HoveredWindow) + hovered_window_above_modal = true; + } + ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); + } +} + static bool IsWindowActiveAndVisible(ImGuiWindow* window) { return (window->Active) && (!window->Hidden); @@ -3298,7 +3348,7 @@ void ImGui::NewFrame() g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) - UpdateMouseMovingWindow(); + UpdateMouseMovingWindowNewFrame(); UpdateHoveredWindowAndCaptureFlags(); // Background darkening/whitening @@ -3635,49 +3685,8 @@ void ImGui::EndFrame() g.FrameScopeActive = false; g.FrameCountEnded = g.FrameCount; - // Initiate moving window - if (g.ActiveId == 0 && g.HoveredId == 0) - { - if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear - { - // Click to focus window and start moving (after we're done with all our widgets) - if (g.IO.MouseClicked[0]) - { - if (g.HoveredRootWindow != NULL) - { - StartMouseMovingWindow(g.HoveredWindow); - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar)) - if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) - g.MovingWindow = NULL; - } - else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL) - { - FocusWindow(NULL); // Clicking on void disable focus - } - } - - // With right mouse button we close popups without changing focus - // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) - if (g.IO.MouseClicked[1]) - { - // Find the top-most window between HoveredWindow and the front most Modal Window. - // This is where we can trim the popup stack. - ImGuiWindow* modal = GetFrontMostPopupModal(); - bool hovered_window_above_modal = false; - if (modal == NULL) - hovered_window_above_modal = true; - for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) - { - ImGuiWindow* window = g.Windows[i]; - if (window == modal) - break; - if (window == g.HoveredWindow) - hovered_window_above_modal = true; - } - ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); - } - } - } + // Initiate moving window + handle left-click and right-click focus + UpdateMouseMovingWindowEndFrame(); // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because childs may not exist yet diff --git a/imgui_internal.h b/imgui_internal.h index d4d6f940..3a25c3c9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1261,7 +1261,8 @@ namespace ImGui // NewFrame IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); - IMGUI_API void UpdateMouseMovingWindow(); + IMGUI_API void UpdateMouseMovingWindowNewFrame(); + IMGUI_API void UpdateMouseMovingWindowEndFrame(); // Settings IMGUI_API void MarkIniSettingsDirty();