|
@@ -1440,6 +1440,11 @@ ImGuiIO::ImGuiIO()
|
|
|
ConfigDebugBeginReturnValueOnce = false;
|
|
|
ConfigDebugBeginReturnValueLoop = false;
|
|
|
|
|
|
+ ConfigErrorRecovery = true;
|
|
|
+ ConfigErrorRecoveryEnableAssert = true;
|
|
|
+ ConfigErrorRecoveryEnableDebugLog = true;
|
|
|
+ ConfigErrorRecoveryEnableTooltip = true;
|
|
|
+
|
|
|
// Inputs Behaviors
|
|
|
MouseDoubleClickTime = 0.30f;
|
|
|
MouseDoubleClickMaxDist = 6.0f;
|
|
@@ -3333,7 +3338,7 @@ void ImGui::PopStyleColor(int count)
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
if (g.ColorStack.Size < count)
|
|
|
{
|
|
|
- IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times!");
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!");
|
|
|
count = g.ColorStack.Size;
|
|
|
}
|
|
|
while (count > 0)
|
|
@@ -3456,7 +3461,7 @@ void ImGui::PopStyleVar(int count)
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
if (g.StyleVarStack.Size < count)
|
|
|
{
|
|
|
- IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times!");
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!");
|
|
|
count = g.StyleVarStack.Size;
|
|
|
}
|
|
|
while (count > 0)
|
|
@@ -4059,8 +4064,16 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
|
|
|
LogDepthRef = 0;
|
|
|
LogDepthToExpand = LogDepthToExpandDefault = 2;
|
|
|
|
|
|
- DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;
|
|
|
+ ErrorCallback = NULL;
|
|
|
+ ErrorCallbackUserData = NULL;
|
|
|
+ ErrorFirst = true;
|
|
|
+ ErrorCountCurrentFrame = 0;
|
|
|
+ StackSizesInBeginForCurrentWindow = NULL;
|
|
|
+
|
|
|
+ DebugDrawIdConflictsCount = 0;
|
|
|
+ DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
|
|
|
DebugLocateId = 0;
|
|
|
+ DebugLogSkippedErrors = 0;
|
|
|
DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
|
|
|
DebugLogAutoDisableFrames = 0;
|
|
|
DebugLocateFrames = 0;
|
|
@@ -4311,6 +4324,7 @@ static void SetCurrentWindow(ImGuiWindow* window)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
g.CurrentWindow = window;
|
|
|
+ g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;
|
|
|
g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
|
|
|
if (window)
|
|
|
{
|
|
@@ -5236,7 +5250,8 @@ void ImGui::NewFrame()
|
|
|
KeepAliveID(g.DragDropPayload.SourceId);
|
|
|
|
|
|
// [DEBUG]
|
|
|
- g.DebugDrawIdConflicts = 0;
|
|
|
+ if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL
|
|
|
+ g.DebugDrawIdConflicts = 0;
|
|
|
if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
|
|
|
g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame;
|
|
|
|
|
@@ -5342,8 +5357,25 @@ void ImGui::NewFrame()
|
|
|
// (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
|
|
|
DockContextNewFrameUpdateUndocking(&g);
|
|
|
|
|
|
+ // Mark all windows as not visible and compact unused memory.
|
|
|
+ IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
|
|
|
+ const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
|
|
|
+ for (ImGuiWindow* window : g.Windows)
|
|
|
+ {
|
|
|
+ window->WasActive = window->Active;
|
|
|
+ window->Active = false;
|
|
|
+ window->WriteAccessed = false;
|
|
|
+ window->BeginCountPreviousFrame = window->BeginCount;
|
|
|
+ window->BeginCount = 0;
|
|
|
+
|
|
|
+ // Garbage collect transient buffers of recently unused windows
|
|
|
+ if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
|
|
|
+ GcCompactTransientWindowBuffers(window);
|
|
|
+ }
|
|
|
+
|
|
|
// Find hovered window
|
|
|
// (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
|
|
|
+ // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
|
|
|
UpdateHoveredWindowAndCaptureFlags();
|
|
|
|
|
|
// Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
|
|
@@ -5365,22 +5397,6 @@ void ImGui::NewFrame()
|
|
|
// Mouse wheel scrolling, scale
|
|
|
UpdateMouseWheel();
|
|
|
|
|
|
- // Mark all windows as not visible and compact unused memory.
|
|
|
- IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
|
|
|
- const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
|
|
|
- for (ImGuiWindow* window : g.Windows)
|
|
|
- {
|
|
|
- window->WasActive = window->Active;
|
|
|
- window->Active = false;
|
|
|
- window->WriteAccessed = false;
|
|
|
- window->BeginCountPreviousFrame = window->BeginCount;
|
|
|
- window->BeginCount = 0;
|
|
|
-
|
|
|
- // Garbage collect transient buffers of recently unused windows
|
|
|
- if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
|
|
|
- GcCompactTransientWindowBuffers(window);
|
|
|
- }
|
|
|
-
|
|
|
// Garbage collect transient buffers of recently unused tables
|
|
|
for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
|
|
|
if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
|
|
@@ -5434,6 +5450,10 @@ void ImGui::NewFrame()
|
|
|
Begin("Debug##Default");
|
|
|
IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
|
|
|
|
|
|
+ // Store stack sizes
|
|
|
+ g.ErrorCountCurrentFrame = 0;
|
|
|
+ ErrorRecoveryStoreState(&g.StackSizesInNewFrame);
|
|
|
+
|
|
|
// [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
|
|
|
// allowing to validate correct Begin/End behavior in user code.
|
|
|
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
|
@@ -5700,32 +5720,13 @@ void ImGui::EndFrame()
|
|
|
return;
|
|
|
IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
|
|
|
|
|
|
-#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
|
|
- if (g.DebugDrawIdConflicts != 0)
|
|
|
- {
|
|
|
- PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.10f));
|
|
|
- if (g.DebugItemPickerActive == false && BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
|
|
|
- {
|
|
|
- SeparatorText("MESSAGE FROM DEAR IMGUI");
|
|
|
- Text("Programmer error: %d visible items with conflicting ID!", g.HoveredIdPreviousFrameItemCount);
|
|
|
- BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
|
|
|
- BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
|
|
|
- BulletText("Press F1 to open \"FAQ -> About the ID Stack System\" and read details.");
|
|
|
- BulletText("Press CTRL+P to activate Item Picker and debug-break in item call-stack.");
|
|
|
- BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
|
|
|
- EndTooltip();
|
|
|
- }
|
|
|
- PopStyleColor();
|
|
|
- if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_P, ImGuiInputFlags_RouteGlobal))
|
|
|
- DebugStartItemPicker();
|
|
|
- if (Shortcut(ImGuiKey_F1, ImGuiInputFlags_RouteGlobal) && g.PlatformIO.Platform_OpenInShellFn != NULL)
|
|
|
- g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
|
|
|
|
|
|
+ // [EXPERIMENTAL] Recover from errors
|
|
|
+ if (g.IO.ConfigErrorRecovery)
|
|
|
+ ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);
|
|
|
ErrorCheckEndFrameSanityChecks();
|
|
|
+ ErrorCheckEndFrameFinalizeErrorTooltip();
|
|
|
|
|
|
// Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
|
|
|
ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
|
|
@@ -5936,7 +5937,7 @@ void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_vi
|
|
|
{
|
|
|
ImGuiWindow* window = g.Windows[i];
|
|
|
IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
|
|
|
- if (!window->Active || window->Hidden)
|
|
|
+ if (!window->WasActive || window->Hidden)
|
|
|
continue;
|
|
|
if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
|
|
|
continue;
|
|
@@ -6205,18 +6206,35 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I
|
|
|
window_flags |= ImGuiWindowFlags_NoMove;
|
|
|
}
|
|
|
|
|
|
- // Forward child flags
|
|
|
- g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
|
|
|
- g.NextWindowData.ChildFlags = child_flags;
|
|
|
-
|
|
|
// Forward size
|
|
|
// Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
|
|
|
// (the alternative would to store conditional flags per axis, which is possible but more code)
|
|
|
const ImVec2 size_avail = GetContentRegionAvail();
|
|
|
const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
|
|
|
- const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
|
|
|
+ ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
|
|
|
+
|
|
|
+ // A SetNextWindowSize() call always has priority (#8020)
|
|
|
+ // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
|
|
|
+ // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.
|
|
|
+ if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)
|
|
|
+ {
|
|
|
+ if (g.NextWindowData.SizeVal.x > 0.0f)
|
|
|
+ {
|
|
|
+ size.x = g.NextWindowData.SizeVal.x;
|
|
|
+ child_flags &= ~ImGuiChildFlags_ResizeX;
|
|
|
+ }
|
|
|
+ if (g.NextWindowData.SizeVal.y > 0.0f)
|
|
|
+ {
|
|
|
+ size.y = g.NextWindowData.SizeVal.y;
|
|
|
+ child_flags &= ~ImGuiChildFlags_ResizeY;
|
|
|
+ }
|
|
|
+ }
|
|
|
SetNextWindowSize(size);
|
|
|
|
|
|
+ // Forward child flags
|
|
|
+ g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
|
|
|
+ g.NextWindowData.ChildFlags = child_flags;
|
|
|
+
|
|
|
// Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
|
|
|
// FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
|
|
|
// e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
|
|
@@ -7372,12 +7390,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|
|
|
|
|
// Add to stack
|
|
|
g.CurrentWindow = window;
|
|
|
- ImGuiWindowStackData window_stack_data;
|
|
|
+ g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1);
|
|
|
+ ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
|
|
|
window_stack_data.Window = window;
|
|
|
window_stack_data.ParentLastItemDataBackup = g.LastItemData;
|
|
|
- window_stack_data.StackSizesOnBegin.SetToContextState(&g);
|
|
|
window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
|
|
|
- g.CurrentWindowStack.push_back(window_stack_data);
|
|
|
+ ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin);
|
|
|
+ g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;
|
|
|
if (flags & ImGuiWindowFlags_ChildMenu)
|
|
|
g.BeginMenuDepth++;
|
|
|
|
|
@@ -8251,7 +8270,11 @@ void ImGui::End()
|
|
|
g.BeginMenuDepth--;
|
|
|
if (window->Flags & ImGuiWindowFlags_Popup)
|
|
|
g.BeginPopupStack.pop_back();
|
|
|
- window_stack_data.StackSizesOnBegin.CompareWithContextState(&g);
|
|
|
+
|
|
|
+ // Error handling, state recovery
|
|
|
+ if (g.IO.ConfigErrorRecovery)
|
|
|
+ ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin);
|
|
|
+
|
|
|
g.CurrentWindowStack.pop_back();
|
|
|
SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
|
|
|
if (g.CurrentWindow)
|
|
@@ -8482,7 +8505,11 @@ void ImGui::PushFont(ImFont* font)
|
|
|
void ImGui::PopFont()
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
- IM_ASSERT(g.FontStack.Size > 0);
|
|
|
+ if (g.FontStack.Size <= 0)
|
|
|
+ {
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
g.FontStack.pop_back();
|
|
|
ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back();
|
|
|
SetCurrentFont(font);
|
|
@@ -8505,7 +8532,11 @@ void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
|
|
|
void ImGui::PopItemFlag()
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
- IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
|
|
|
+ if (g.ItemFlagsStack.Size <= 1)
|
|
|
+ {
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
g.ItemFlagsStack.pop_back();
|
|
|
g.CurrentItemFlags = g.ItemFlagsStack.back();
|
|
|
}
|
|
@@ -8535,7 +8566,11 @@ void ImGui::BeginDisabled(bool disabled)
|
|
|
void ImGui::EndDisabled()
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
- IM_ASSERT(g.DisabledStackSize > 0);
|
|
|
+ if (g.DisabledStackSize <= 0)
|
|
|
+ {
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
g.DisabledStackSize--;
|
|
|
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
|
|
|
//PopItemFlag();
|
|
@@ -8570,14 +8605,21 @@ void ImGui::EndDisabledOverrideReenable()
|
|
|
|
|
|
void ImGui::PushTextWrapPos(float wrap_pos_x)
|
|
|
{
|
|
|
- ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiWindow* window = g.CurrentWindow;
|
|
|
window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
|
|
|
window->DC.TextWrapPos = wrap_pos_x;
|
|
|
}
|
|
|
|
|
|
void ImGui::PopTextWrapPos()
|
|
|
{
|
|
|
- ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiWindow* window = g.CurrentWindow;
|
|
|
+ if (window->DC.TextWrapPosStack.Size <= 0)
|
|
|
+ {
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
|
|
|
window->DC.TextWrapPosStack.pop_back();
|
|
|
}
|
|
@@ -9064,9 +9106,9 @@ void ImGui::PushFocusScope(ImGuiID id)
|
|
|
void ImGui::PopFocusScope()
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
- if (g.FocusScopeStack.Size == 0)
|
|
|
+ if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack)
|
|
|
{
|
|
|
- IM_ASSERT_USER_ERROR(g.FocusScopeStack.Size > 0, "Calling PopFocusScope() too many times!");
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
|
|
|
return;
|
|
|
}
|
|
|
g.FocusScopeStack.pop_back();
|
|
@@ -9337,7 +9379,11 @@ ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
|
|
|
void ImGui::PopID()
|
|
|
{
|
|
|
ImGuiWindow* window = GImGui->CurrentWindow;
|
|
|
- IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
|
|
|
+ if (window->IDStack.Size <= 1)
|
|
|
+ {
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
window->IDStack.pop_back();
|
|
|
}
|
|
|
|
|
@@ -10919,9 +10965,10 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID own
|
|
|
// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
|
|
|
// - ErrorCheckNewFrameSanityChecks()
|
|
|
// - ErrorCheckEndFrameSanityChecks()
|
|
|
-// - ErrorCheckEndFrameRecover()
|
|
|
-// - ErrorCheckEndWindowRecover()
|
|
|
-// - ImGuiStackSizes
|
|
|
+// - ErrorRecoveryStoreState()
|
|
|
+// - ErrorRecoveryTryToRecoverState()
|
|
|
+// - ErrorRecoveryTryToRecoverWindowState()
|
|
|
+// - ErrorLog()
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
|
|
@@ -11020,6 +11067,10 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
|
|
|
IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
|
|
|
#endif
|
|
|
|
|
|
+ // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
|
|
|
+ if (g.IO.ConfigErrorRecovery)
|
|
|
+ IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
|
|
|
+
|
|
|
// Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
|
|
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
|
|
if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
|
|
@@ -11070,126 +11121,113 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
|
|
|
|
|
|
static void ImGui::ErrorCheckEndFrameSanityChecks()
|
|
|
{
|
|
|
- ImGuiContext& g = *GImGui;
|
|
|
-
|
|
|
// Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
|
|
|
// One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
|
|
|
// It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
|
|
|
// send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
|
|
|
// We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
|
|
|
// while still correctly asserting on mid-frame key press events.
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
|
|
|
IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
|
|
|
IM_UNUSED(key_mods);
|
|
|
|
|
|
- // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame().
|
|
|
- //ErrorCheckEndFrameRecover();
|
|
|
-
|
|
|
- // 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)
|
|
|
- {
|
|
|
- if (g.CurrentWindowStack.Size > 1)
|
|
|
- {
|
|
|
- ImGuiWindow* window = g.CurrentWindowStack.back().Window; // <-- This window was not Ended!
|
|
|
- IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
|
|
|
- IM_UNUSED(window);
|
|
|
- while (g.CurrentWindowStack.Size > 1)
|
|
|
- End();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
|
|
|
+ IM_ASSERT(g.CurrentWindowStack.Size == 1);
|
|
|
+ IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
|
|
|
}
|
|
|
|
|
|
-// Default implementation of ImGuiErrorLogCallback that pipe errors to DebugLog: appears in tty + Tools->DebugLog
|
|
|
-void ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...)
|
|
|
+// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
|
|
|
+void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
|
|
|
{
|
|
|
-#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
|
|
- va_list args;
|
|
|
- va_start(args, fmt);
|
|
|
- DebugLogV(fmt, args);
|
|
|
- va_end(args);
|
|
|
-#else
|
|
|
- IM_UNUSED(fmt);
|
|
|
-#endif
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
|
|
|
+ state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
|
|
|
+ state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!
|
|
|
+ state_out->SizeOfColorStack = (short)g.ColorStack.Size;
|
|
|
+ state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
|
|
|
+ state_out->SizeOfFontStack = (short)g.FontStack.Size;
|
|
|
+ state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
|
|
|
+ state_out->SizeOfGroupStack = (short)g.GroupStack.Size;
|
|
|
+ state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
|
|
|
+ state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
|
|
|
+ state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;
|
|
|
}
|
|
|
|
|
|
-// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
|
|
|
-// Must be called during or before EndFrame().
|
|
|
-// This is generally flawed as we are not necessarily End/Popping things in the right order.
|
|
|
-// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
|
|
|
-void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
|
|
|
+// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
|
|
|
+// Called by e.g. EndFrame() but may be called for manual recovery.
|
|
|
+// Attempt to recover full window stack.
|
|
|
+void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)
|
|
|
{
|
|
|
// PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
- while (g.CurrentWindowStack.Size > 0) //-V1044
|
|
|
+ while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044
|
|
|
{
|
|
|
- ErrorCheckEndWindowRecover(log_callback, user_data);
|
|
|
+ // Recap:
|
|
|
+ // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
|
|
|
+ // - Always call a matching End() for each Begin() call, regardless of its return value!
|
|
|
+ // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
|
|
|
+ // - We will fix that in a future major update.
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
- if (g.CurrentWindowStack.Size == 1)
|
|
|
- {
|
|
|
- IM_ASSERT(window->IsFallbackWindow);
|
|
|
- break;
|
|
|
- }
|
|
|
if (window->Flags & ImGuiWindowFlags_ChildWindow)
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing EndChild()");
|
|
|
EndChild();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing End()");
|
|
|
End();
|
|
|
}
|
|
|
}
|
|
|
+ if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
|
|
|
+ ErrorRecoveryTryToRecoverWindowState(state_in);
|
|
|
}
|
|
|
|
|
|
-// Must be called before End()/EndChild()
|
|
|
-void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data)
|
|
|
+// Called by e.g. End() but may be called for manual recovery.
|
|
|
+// Read '// Error Handling [BETA]' block in imgui_internal.h for details.
|
|
|
+// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
|
|
|
+void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
+
|
|
|
while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'\n", g.CurrentTable->OuterWindow->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
|
|
|
EndTable();
|
|
|
}
|
|
|
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
- ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin;
|
|
|
- IM_ASSERT(window != NULL);
|
|
|
+
|
|
|
+ // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
|
|
|
while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
|
|
|
EndTabBar();
|
|
|
}
|
|
|
while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window)
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
|
|
|
EndMultiSelect();
|
|
|
}
|
|
|
- while (window->DC.TreeDepth > 0)
|
|
|
+ while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
|
|
|
TreePop();
|
|
|
}
|
|
|
- while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044
|
|
|
+ while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing EndGroup()");
|
|
|
EndGroup();
|
|
|
}
|
|
|
- while (window->IDStack.Size > 1)
|
|
|
+ IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
|
|
|
+ while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing PopID()");
|
|
|
PopID();
|
|
|
}
|
|
|
- while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044
|
|
|
+ while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()");
|
|
|
if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
|
|
|
EndDisabled();
|
|
|
else
|
|
@@ -11198,70 +11236,150 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo
|
|
|
g.CurrentWindowStack.back().DisabledOverrideReenable = false;
|
|
|
}
|
|
|
}
|
|
|
- while (g.ColorStack.Size > stack_sizes->SizeOfColorStack)
|
|
|
+ IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
|
|
|
+ while (g.ColorStack.Size > state_in->SizeOfColorStack)
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s\n", window->Name, GetStyleColorName(g.ColorStack.back().Col));
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
|
|
|
PopStyleColor();
|
|
|
}
|
|
|
- while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044
|
|
|
+ while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()");
|
|
|
PopItemFlag();
|
|
|
}
|
|
|
- while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044
|
|
|
+ while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()");
|
|
|
PopStyleVar();
|
|
|
}
|
|
|
- while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044
|
|
|
+ while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing PopFont()");
|
|
|
PopFont();
|
|
|
}
|
|
|
- while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044
|
|
|
+ while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044
|
|
|
{
|
|
|
- if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'\n", window->Name);
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
|
|
|
PopFocusScope();
|
|
|
}
|
|
|
+ //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);
|
|
|
}
|
|
|
|
|
|
-// Save current stack sizes for later compare
|
|
|
-void ImGuiStackSizes::SetToContextState(ImGuiContext* ctx)
|
|
|
+bool ImGui::ErrorLog(const char* msg)
|
|
|
{
|
|
|
- ImGuiContext& g = *ctx;
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+
|
|
|
+ // Output to debug log
|
|
|
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
- SizeOfIDStack = (short)window->IDStack.Size;
|
|
|
- SizeOfColorStack = (short)g.ColorStack.Size;
|
|
|
- SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
|
|
|
- SizeOfFontStack = (short)g.FontStack.Size;
|
|
|
- SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
|
|
|
- SizeOfGroupStack = (short)g.GroupStack.Size;
|
|
|
- SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
|
|
|
- SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
|
|
|
- SizeOfDisabledStack = (short)g.DisabledStackSize;
|
|
|
+
|
|
|
+ if (g.IO.ConfigErrorRecoveryEnableDebugLog)
|
|
|
+ {
|
|
|
+ if (g.ErrorFirst)
|
|
|
+ IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
|
|
|
+ g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);
|
|
|
+ IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg);
|
|
|
+ }
|
|
|
+ g.ErrorFirst = false;
|
|
|
+
|
|
|
+ // Output to tooltip
|
|
|
+ if (g.IO.ConfigErrorRecoveryEnableTooltip)
|
|
|
+ {
|
|
|
+ if (BeginErrorTooltip())
|
|
|
+ {
|
|
|
+ if (g.ErrorCountCurrentFrame < 20)
|
|
|
+ {
|
|
|
+ Text("In window '%s': %s", window ? window->Name : "NULL", msg);
|
|
|
+ if (window && (!window->IsFallbackWindow || window->WasActive))
|
|
|
+ GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255));
|
|
|
+ }
|
|
|
+ if (g.ErrorCountCurrentFrame == 20)
|
|
|
+ Text("(and more errors)");
|
|
|
+ // EndFrame() will amend debug buttons to this window, after all errors have been submitted.
|
|
|
+ EndErrorTooltip();
|
|
|
+ }
|
|
|
+ g.ErrorCountCurrentFrame++;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ // Output to callback
|
|
|
+ if (g.ErrorCallback != NULL)
|
|
|
+ g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
|
|
|
+
|
|
|
+ // Return whether we should assert
|
|
|
+ return g.IO.ConfigErrorRecoveryEnableAssert;
|
|
|
}
|
|
|
|
|
|
-// Compare to detect usage errors
|
|
|
-void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
|
|
|
+void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
|
|
|
{
|
|
|
- ImGuiContext& g = *ctx;
|
|
|
- ImGuiWindow* window = g.CurrentWindow;
|
|
|
- IM_UNUSED(window);
|
|
|
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ if (g.DebugDrawIdConflicts != 0 && g.IO.KeyCtrl == false)
|
|
|
+ g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;
|
|
|
+ if (g.DebugDrawIdConflicts != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())
|
|
|
+ {
|
|
|
+ Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
|
|
|
+ BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
|
|
|
+ BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
|
|
|
+ BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
|
|
|
+ Separator();
|
|
|
+ Text("(Hold CTRL and: use");
|
|
|
+ SameLine();
|
|
|
+ if (SmallButton("Item Picker"))
|
|
|
+ DebugStartItemPicker();
|
|
|
+ SameLine();
|
|
|
+ Text("to break in item call-stack, or");
|
|
|
+ SameLine();
|
|
|
+ if (SmallButton("Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL)
|
|
|
+ g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
|
|
|
+ EndErrorTooltip();
|
|
|
+ }
|
|
|
|
|
|
- // Window stacks
|
|
|
- // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
|
|
|
- IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!");
|
|
|
+ if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
|
|
|
+ {
|
|
|
+ Separator();
|
|
|
+ Text("(Hold CTRL and:");
|
|
|
+ SameLine();
|
|
|
+ if (SmallButton("Enable Asserts"))
|
|
|
+ g.IO.ConfigErrorRecoveryEnableAssert = true;
|
|
|
+ //SameLine();
|
|
|
+ //if (SmallButton("Hide Error Tooltips"))
|
|
|
+ // g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous
|
|
|
+ SameLine(0, 0);
|
|
|
+ Text(")");
|
|
|
+ EndErrorTooltip();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
|
|
|
- // Global stacks
|
|
|
- // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
|
|
|
- IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!");
|
|
|
- IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
|
|
|
- IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!");
|
|
|
- IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!");
|
|
|
- IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!");
|
|
|
- IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!");
|
|
|
- IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!");
|
|
|
- IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!");
|
|
|
+// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it.
|
|
|
+bool ImGui::BeginErrorTooltip()
|
|
|
+{
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiWindow* window = FindWindowByName("##Tooltip_Error");
|
|
|
+ const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);
|
|
|
+ PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f));
|
|
|
+ if (use_locked_pos)
|
|
|
+ SetNextWindowPos(g.ErrorTooltipLockedPos);
|
|
|
+ bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
|
|
|
+ PopStyleColor();
|
|
|
+ if (is_visible && g.CurrentWindow->BeginCount == 1)
|
|
|
+ {
|
|
|
+ SeparatorText("MESSAGE FROM DEAR IMGUI");
|
|
|
+ BringWindowToDisplayFront(g.CurrentWindow);
|
|
|
+ BringWindowToFocusFront(g.CurrentWindow);
|
|
|
+ g.ErrorTooltipLockedPos = GetWindowPos();
|
|
|
+ }
|
|
|
+ else if (!is_visible)
|
|
|
+ {
|
|
|
+ End();
|
|
|
+ }
|
|
|
+ return is_visible;
|
|
|
+}
|
|
|
+
|
|
|
+void ImGui::EndErrorTooltip()
|
|
|
+{
|
|
|
+ End();
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
@@ -11593,7 +11711,13 @@ void ImGui::PushMultiItemsWidths(int components, float w_full)
|
|
|
|
|
|
void ImGui::PopItemWidth()
|
|
|
{
|
|
|
- ImGuiWindow* window = GetCurrentWindow();
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
+ ImGuiWindow* window = g.CurrentWindow;
|
|
|
+ if (window->DC.ItemWidthStack.Size <= 0)
|
|
|
+ {
|
|
|
+ IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
window->DC.ItemWidth = window->DC.ItemWidthStack.back();
|
|
|
window->DC.ItemWidthStack.pop_back();
|
|
|
}
|
|
@@ -22092,12 +22216,24 @@ static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
|
|
|
SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
|
|
|
+
|
|
|
+ bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0);
|
|
|
+ if (highlight_errors)
|
|
|
+ ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f));
|
|
|
if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
|
|
|
{
|
|
|
g.DebugLogAutoDisableFrames = 2;
|
|
|
g.DebugLogAutoDisableFlags |= flags;
|
|
|
}
|
|
|
- ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
|
|
|
+ if (highlight_errors)
|
|
|
+ {
|
|
|
+ ImGui::PopStyleColor();
|
|
|
+ ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void ImGui::ShowDebugLogWindow(bool* p_open)
|
|
@@ -22115,6 +22251,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
|
|
|
CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
|
|
|
SetItemTooltip("(except InputRouting which is spammy)");
|
|
|
|
|
|
+ ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError);
|
|
|
ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
|
|
|
ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
|
|
|
ShowDebugLogFlag("Docking", ImGuiDebugLogFlags_EventDocking);
|
|
@@ -22130,6 +22267,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
|
|
|
{
|
|
|
g.DebugLogBuf.clear();
|
|
|
g.DebugLogIndex.clear();
|
|
|
+ g.DebugLogSkippedErrors = 0;
|
|
|
}
|
|
|
SameLine();
|
|
|
if (SmallButton("Copy"))
|