Setup a couple of features to configure them, including ways to display error tooltips instead of assserting.
This commit is contained in:
parent
8776678a46
commit
30c29d291f
@ -43,8 +43,22 @@ Breaking changes:
|
||||
|
||||
Other changes:
|
||||
|
||||
- Error Handling: rewired asserts in PopID(), PopFont(), PopItemFlag(), EndDisabled(),
|
||||
PopTextWrapPos(), PopFocusScope(), PopItemWidth() to use IM_ASSERT_USER_ERROR(). (#1651)
|
||||
- Error Handling: Enabled/improved error recovery systems. (#1651, #5654)
|
||||
- Error recovery is not perfect nor guaranteed! It is a feature to ease development.
|
||||
- Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
|
||||
- You not are not supposed to rely on it in the course of a normal application run.
|
||||
- Possible usage: facilitate recovery from errors triggered from a scripting language or
|
||||
after specific exceptions handlers. Surface errors to programmers in less agressive ways.
|
||||
- Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled
|
||||
when making direct imgui API calls! Otherwise it would severely hinder your ability to
|
||||
catch and correct mistakes!
|
||||
- Added io.ConfigErrorRecovery to enable error recovery support.
|
||||
- Added io.ConfigErrorRecoveryEnableAssert to assert on recoverable errors.
|
||||
- Added io.ConfigErrorRecoveryEnableDebugLog to output to debug log on recoverable errors.
|
||||
- Added io.ConfigErrorRecoveryEnableTooltip to enable displaying an error tooltip on recoverable errors.
|
||||
The tooltip include a way to enable asserts if they were disabled.
|
||||
- All options are enabled by default.
|
||||
- Read https://github.com/ocornut/imgui/wiki/Error-Handling for a bit more details.
|
||||
- Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window
|
||||
using ImGuiChildFlags_ResizeX,ImGuiChildFlags_ResizeY in order to override its current
|
||||
size. (#1710, #8020)
|
||||
|
190
imgui.cpp
190
imgui.cpp
@ -1401,6 +1401,11 @@ ImGuiIO::ImGuiIO()
|
||||
ConfigDebugBeginReturnValueOnce = false;
|
||||
ConfigDebugBeginReturnValueLoop = false;
|
||||
|
||||
ConfigErrorRecovery = true;
|
||||
ConfigErrorRecoveryEnableAssert = true;
|
||||
ConfigErrorRecoveryEnableDebugLog = true;
|
||||
ConfigErrorRecoveryEnableTooltip = true;
|
||||
|
||||
// Inputs Behaviors
|
||||
MouseDoubleClickTime = 0.30f;
|
||||
MouseDoubleClickMaxDist = 6.0f;
|
||||
@ -3978,6 +3983,12 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
|
||||
LogDepthRef = 0;
|
||||
LogDepthToExpand = LogDepthToExpandDefault = 2;
|
||||
|
||||
ErrorCallback = NULL;
|
||||
ErrorCallbackUserData = NULL;
|
||||
ErrorFirst = true;
|
||||
ErrorCountCurrentFrame = 0;
|
||||
StackSizesInBeginForCurrentWindow = NULL;
|
||||
|
||||
DebugDrawIdConflictsCount = 0;
|
||||
DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
|
||||
DebugLocateId = 0;
|
||||
@ -4211,6 +4222,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;
|
||||
g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking
|
||||
if (window)
|
||||
@ -5249,6 +5261,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
|
||||
@ -5467,6 +5483,9 @@ void ImGui::EndFrame()
|
||||
|
||||
CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
|
||||
|
||||
// [EXPERIMENTAL] Recover from errors
|
||||
if (g.IO.ConfigErrorRecovery)
|
||||
ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);
|
||||
ErrorCheckEndFrameSanityChecks();
|
||||
ErrorCheckEndFrameFinalizeErrorTooltip();
|
||||
|
||||
@ -6960,12 +6979,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++;
|
||||
|
||||
@ -7693,7 +7713,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);
|
||||
}
|
||||
@ -8454,7 +8478,7 @@ 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(0, "Calling PopFocusScope() too many times!");
|
||||
return;
|
||||
@ -10303,9 +10327,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.
|
||||
@ -10404,6 +10429,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))
|
||||
@ -10426,43 +10455,35 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
|
||||
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?");
|
||||
}
|
||||
}
|
||||
if (g.CurrentWindowStack.Size >= 1)
|
||||
IM_ASSERT(g.CurrentWindowStack.Size == 1);
|
||||
IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
|
||||
|
||||
IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
|
||||
}
|
||||
|
||||
// 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()
|
||||
// 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)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
|
||||
state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
// 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!
|
||||
@ -10480,12 +10501,17 @@ void ImGui::ErrorCheckEndFrameRecover()
|
||||
End();
|
||||
}
|
||||
}
|
||||
if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
|
||||
ErrorRecoveryTryToRecoverWindowState(state_in);
|
||||
}
|
||||
|
||||
// Must be called before End()/EndChild()
|
||||
void ImGui::ErrorCheckEndWindowRecover()
|
||||
// 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)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
|
||||
@ -10493,8 +10519,6 @@ void ImGui::ErrorCheckEndWindowRecover()
|
||||
}
|
||||
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
IM_ASSERT(window != NULL);
|
||||
ImGuiStackSizes* state_in = &g.CurrentWindowStack.back().StackSizesOnBegin;
|
||||
|
||||
// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
|
||||
while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
|
||||
@ -10560,45 +10584,52 @@ void ImGui::ErrorCheckEndWindowRecover()
|
||||
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;
|
||||
|
||||
// Compare to detect usage errors
|
||||
void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
|
||||
// Output to tooltip
|
||||
if (g.IO.ConfigErrorRecoveryEnableTooltip)
|
||||
{
|
||||
ImGuiContext& g = *ctx;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
IM_UNUSED(window);
|
||||
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
|
||||
|
||||
// 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!");
|
||||
// Output to callback
|
||||
if (g.ErrorCallback != NULL)
|
||||
g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
|
||||
|
||||
// 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!");
|
||||
// Return whether we should assert
|
||||
return g.IO.ConfigErrorRecoveryEnableAssert;
|
||||
}
|
||||
|
||||
void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
|
||||
@ -10625,6 +10656,21 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
|
||||
g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
|
||||
EndErrorTooltip();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
21
imgui.h
21
imgui.h
@ -29,7 +29,7 @@
|
||||
// Library Version
|
||||
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
|
||||
#define IMGUI_VERSION "1.91.3 WIP"
|
||||
#define IMGUI_VERSION_NUM 19122
|
||||
#define IMGUI_VERSION_NUM 19123
|
||||
#define IMGUI_HAS_TABLE
|
||||
|
||||
/*
|
||||
@ -2265,6 +2265,23 @@ struct ImGuiIO
|
||||
// Debug options
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// Options to configure how we handle recoverable errors [EXPERIMENTAL]
|
||||
// - Error recovery is not perfect nor guaranteed! It is a feature to ease development.
|
||||
// - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
|
||||
// - You not are not supposed to rely on it in the course of a normal application run.
|
||||
// - Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.
|
||||
// - Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API calls!
|
||||
// Otherwise it would severely hinder your ability to catch and correct mistakes!
|
||||
// Read https://github.com/ocornut/imgui/wiki/Error-Handling for details about typical usage scenarios:
|
||||
// - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!)
|
||||
// - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (visible log entries, use callback etc.)
|
||||
// - Recovery after error from scripting language: record stack sizes before running script, disable assert, trigger breakpoint from ErrorCallback, recover with ErrorRecoveryTryToRecoverState(), restore settings.
|
||||
// - Recovery after an exception handler: record stack sizes before try {} block, disable assert, set log callback, recover with ErrorRecoveryTryToRecoverState(), restore settings.
|
||||
bool ConfigErrorRecovery; // = true // Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled.
|
||||
bool ConfigErrorRecoveryEnableAssert; // = true // Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR()
|
||||
bool ConfigErrorRecoveryEnableDebugLog; // = true // Enable debug log output on recoverable errors.
|
||||
bool ConfigErrorRecoveryEnableTooltip; // = true // Enable tooltip on recoverable errors. The tooltip include a way to enable asserts if they were disabled.
|
||||
|
||||
// Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
|
||||
// - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
|
||||
// - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
|
||||
@ -2293,7 +2310,7 @@ struct ImGuiIO
|
||||
bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Platform Functions
|
||||
// Platform Identifiers
|
||||
// (the imgui_impl_xxxx backend files are setting those up for you)
|
||||
//------------------------------------------------------------------
|
||||
|
||||
|
@ -545,6 +545,22 @@ void ImGui::ShowDemoWindow(bool* p_open)
|
||||
ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
|
||||
ImGui::Text("Also see Style->Rendering for rendering options.");
|
||||
|
||||
// Read https://github.com/ocornut/imgui/wiki/Error-Handling
|
||||
ImGui::SeparatorText("Error Handling");
|
||||
ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery);
|
||||
ImGui::SameLine(); HelpMarker(
|
||||
"Options to configure how we handle recoverable errors.\n"
|
||||
"- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n"
|
||||
"- You not are not supposed to rely on it in the course of a normal application run.\n"
|
||||
"- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n"
|
||||
"- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call!"
|
||||
"Otherwise it would severely hinder your ability to catch and correct mistakes!");
|
||||
ImGui::Checkbox("io.ConfigErrorRecoveryEnableAssert", &io.ConfigErrorRecoveryEnableAssert);
|
||||
ImGui::Checkbox("io.ConfigErrorRecoveryEnableDebugLog", &io.ConfigErrorRecoveryEnableDebugLog);
|
||||
ImGui::Checkbox("io.ConfigErrorRecoveryEnableTooltip", &io.ConfigErrorRecoveryEnableTooltip);
|
||||
if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
|
||||
io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
|
||||
|
||||
ImGui::SeparatorText("Debug");
|
||||
ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
|
||||
ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
|
||||
|
@ -132,6 +132,7 @@ struct ImGuiContext; // Main Dear ImGui context
|
||||
struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine
|
||||
struct ImGuiDataVarInfo; // Variable information (e.g. to access style variables from an enum)
|
||||
struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum
|
||||
struct ImGuiErrorRecoveryState; // Storage of stack sizes for error handling and recovery
|
||||
struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup()
|
||||
struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box
|
||||
struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id
|
||||
@ -148,7 +149,6 @@ struct ImGuiOldColumnData; // Storage data for a single column for lega
|
||||
struct ImGuiOldColumns; // Storage data for a columns set for legacy Columns() api
|
||||
struct ImGuiPopupData; // Storage for current popup stack
|
||||
struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file
|
||||
struct ImGuiStackSizes; // Storage of stack sizes for debugging/asserting
|
||||
struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it
|
||||
struct ImGuiTabBar; // Storage for a tab bar
|
||||
struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
|
||||
@ -1253,9 +1253,10 @@ struct ImGuiTreeNodeStackData
|
||||
ImRect NavRect; // Used for nav landing
|
||||
};
|
||||
|
||||
// sizeof() = 18
|
||||
struct IMGUI_API ImGuiStackSizes
|
||||
// sizeof() = 20
|
||||
struct IMGUI_API ImGuiErrorRecoveryState
|
||||
{
|
||||
short SizeOfWindowStack;
|
||||
short SizeOfIDStack;
|
||||
short SizeOfColorStack;
|
||||
short SizeOfStyleVarStack;
|
||||
@ -1266,9 +1267,7 @@ struct IMGUI_API ImGuiStackSizes
|
||||
short SizeOfBeginPopupStack;
|
||||
short SizeOfDisabledStack;
|
||||
|
||||
ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }
|
||||
void SetToContextState(ImGuiContext* ctx);
|
||||
void CompareWithContextState(ImGuiContext* ctx);
|
||||
ImGuiErrorRecoveryState() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Data saved for each window pushed into the stack
|
||||
@ -1276,7 +1275,7 @@ struct ImGuiWindowStackData
|
||||
{
|
||||
ImGuiWindow* Window;
|
||||
ImGuiLastItemData ParentLastItemDataBackup;
|
||||
ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting
|
||||
ImGuiErrorRecoveryState StackSizesInBegin; // Store size of various stacks for asserting
|
||||
bool DisabledOverrideReenable; // Non-child window override disabled flag
|
||||
};
|
||||
|
||||
@ -1885,12 +1884,17 @@ struct ImGuiLocEntry
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Macros used by Recoverable Error handling
|
||||
// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults.
|
||||
// - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro).
|
||||
// - The message will always be a string literal, in order to increase likelihood of being display by an assert handler.
|
||||
// - The intent is that you may rewire this macro to dispatch dynamically:
|
||||
// - On programmers machines, when debugger is attached, on direct imgui API usage error: always assert!
|
||||
// - On exception recovery and script language recovery: you may decide to error log.
|
||||
#ifndef IM_ASSERT_USER_ERROR
|
||||
#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) IM_ASSERT((_EXPR) && _MSG)
|
||||
#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0) // Recoverable User Error
|
||||
#endif
|
||||
|
||||
typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);
|
||||
|
||||
typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Metrics, Debug Tools
|
||||
@ -2313,8 +2317,14 @@ struct ImGuiContext
|
||||
int LogDepthToExpand;
|
||||
int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
|
||||
|
||||
// Error handling
|
||||
// Error Handling
|
||||
ImGuiErrorCallback ErrorCallback; // = NULL. May be exposed in public API eventually.
|
||||
void* ErrorCallbackUserData; // = NULL
|
||||
ImVec2 ErrorTooltipLockedPos;
|
||||
bool ErrorFirst;
|
||||
int ErrorCountCurrentFrame; // [Internal] Number of errors submitted this frame.
|
||||
ImGuiErrorRecoveryState StackSizesInNewFrame; // [Internal]
|
||||
ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow; // [Internal]
|
||||
|
||||
// Debug Tools
|
||||
// (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.)
|
||||
@ -3419,9 +3429,11 @@ namespace ImGui
|
||||
IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window);
|
||||
|
||||
// Error handling, State Recovery
|
||||
IMGUI_API bool ErrorLog(const char* msg);
|
||||
IMGUI_API void ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out);
|
||||
IMGUI_API void ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in);
|
||||
IMGUI_API void ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in);
|
||||
IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
|
||||
IMGUI_API void ErrorCheckEndFrameRecover();
|
||||
IMGUI_API void ErrorCheckEndWindowRecover();
|
||||
IMGUI_API void ErrorCheckEndFrameFinalizeErrorTooltip();
|
||||
IMGUI_API bool BeginErrorTooltip();
|
||||
IMGUI_API void EndErrorTooltip();
|
||||
|
@ -1484,7 +1484,9 @@ void ImGui::EndTable()
|
||||
{
|
||||
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
|
||||
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
|
||||
g.CurrentTable = NULL; // To avoid error recovery recursing
|
||||
EndChild();
|
||||
g.CurrentTable = table;
|
||||
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
|
||||
}
|
||||
else
|
||||
|
@ -7551,7 +7551,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
|
||||
ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;
|
||||
ImGuiMultiSelectState* storage = ms->Storage;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId);
|
||||
IM_ASSERT_USER_ERROR(ms->FocusScopeId == g.CurrentFocusScopeId, "EndMultiSelect() FocusScope mismatch!");
|
||||
IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow);
|
||||
IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user