1
0
mirror of https://github.com/ocornut/imgui.git synced 2025-01-19 01:34:08 +01:00

Merge branch 'master' into docking

# Conflicts:
#	imgui.cpp
#	imgui_internal.h
This commit is contained in:
ocornut 2024-09-27 19:03:25 +02:00
commit d1ea03b872
7 changed files with 435 additions and 206 deletions

View File

@ -43,6 +43,25 @@ Breaking changes:
Other changes:
- 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)
- Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328)
- Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328)
Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.

476
imgui.cpp
View File

@ -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"))

21
imgui.h
View File

@ -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 19121
#define IMGUI_VERSION_NUM 19123
#define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch
#define IMGUI_HAS_DOCK // Docking WIP branch
@ -2355,6 +2355,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.
@ -2383,7 +2400,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)
//------------------------------------------------------------------

View File

@ -599,6 +599,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.");
@ -4006,6 +4022,9 @@ static void ShowDemoWindowLayout()
ImGui::SeparatorText("Manual-resize");
{
HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.");
//if (ImGui::Button("Set Height to 200"))
// ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
for (int n = 0; n < 10; n++)

View File

@ -28,6 +28,7 @@ Index of this file:
// [SECTION] Viewport support
// [SECTION] Settings support
// [SECTION] Localization support
// [SECTION] Error handling, State recovery support
// [SECTION] Metrics, Debug tools
// [SECTION] Generic context hooks
// [SECTION] ImGuiContext (main imgui context)
@ -135,6 +136,7 @@ struct ImGuiDockContext; // Docking system context
struct ImGuiDockRequest; // Docking system dock/undock queued request
struct ImGuiDockNode; // Docking system node (hold a list of Windows OR two child dock nodes)
struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session)
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
@ -151,7 +153,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)
@ -179,7 +180,7 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E
// Flags
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow();
typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow()
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags
typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns()
typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight()
@ -193,8 +194,6 @@ typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // F
typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest()
typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);
//-----------------------------------------------------------------------------
// [SECTION] Context pointer
// See implementation of this variable in imgui.cpp for comments and details.
@ -227,6 +226,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#else
#define IMGUI_DEBUG_LOG(...) ((void)0)
#endif
#define IMGUI_DEBUG_LOG_ERROR(...) do { ImGuiContext& g2 = *GImGui; if (g2.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g2.DebugLogSkippedErrors++; } while (0)
#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
@ -250,12 +250,6 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IM_ASSERT_PARANOID(_EXPR)
#endif
// 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.
#ifndef IM_ASSERT_USER_ERROR
#define IM_ASSERT_USER_ERROR(_EXP,_MSG) IM_ASSERT((_EXP) && _MSG) // Recoverable User Error
#endif
// Misc Macros
#define IM_PI 3.14159265358979323846f
#ifdef _WIN32
@ -1278,10 +1272,12 @@ 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 SizeOfTreeStack;
short SizeOfColorStack;
short SizeOfStyleVarStack;
short SizeOfFontStack;
@ -1291,18 +1287,16 @@ 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
struct ImGuiWindowStackData
{
ImGuiWindow* Window;
ImGuiLastItemData ParentLastItemDataBackup;
ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting
bool DisabledOverrideReenable; // Non-child window override disabled flag
ImGuiWindow* Window;
ImGuiLastItemData ParentLastItemDataBackup;
ImGuiErrorRecoveryState StackSizesInBegin; // Store size of various stacks for asserting
bool DisabledOverrideReenable; // Non-child window override disabled flag
};
struct ImGuiShrinkWidthItem
@ -2076,6 +2070,22 @@ struct ImGuiLocEntry
const char* Text;
};
//-----------------------------------------------------------------------------
// [SECTION] Error handling, State recovery support
//-----------------------------------------------------------------------------
// Macros used by Recoverable Error handling
// - 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) do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0) // Recoverable User Error
#endif
typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
//-----------------------------------------------------------------------------
// [SECTION] Metrics, Debug Tools
@ -2085,18 +2095,19 @@ enum ImGuiDebugLogFlags_
{
// Event types
ImGuiDebugLogFlags_None = 0,
ImGuiDebugLogFlags_EventActiveId = 1 << 0,
ImGuiDebugLogFlags_EventFocus = 1 << 1,
ImGuiDebugLogFlags_EventPopup = 1 << 2,
ImGuiDebugLogFlags_EventNav = 1 << 3,
ImGuiDebugLogFlags_EventClipper = 1 << 4,
ImGuiDebugLogFlags_EventSelection = 1 << 5,
ImGuiDebugLogFlags_EventIO = 1 << 6,
ImGuiDebugLogFlags_EventInputRouting = 1 << 7,
ImGuiDebugLogFlags_EventDocking = 1 << 8,
ImGuiDebugLogFlags_EventViewport = 1 << 9,
ImGuiDebugLogFlags_EventError = 1 << 0, // Error submitted by IM_ASSERT_USER_ERROR()
ImGuiDebugLogFlags_EventActiveId = 1 << 1,
ImGuiDebugLogFlags_EventFocus = 1 << 2,
ImGuiDebugLogFlags_EventPopup = 1 << 3,
ImGuiDebugLogFlags_EventNav = 1 << 4,
ImGuiDebugLogFlags_EventClipper = 1 << 5,
ImGuiDebugLogFlags_EventSelection = 1 << 6,
ImGuiDebugLogFlags_EventIO = 1 << 7,
ImGuiDebugLogFlags_EventInputRouting = 1 << 8,
ImGuiDebugLogFlags_EventDocking = 1 << 9,
ImGuiDebugLogFlags_EventViewport = 1 << 10,
ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport,
ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport,
ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY
ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine
};
@ -2516,11 +2527,22 @@ struct ImGuiContext
int LogDepthToExpand;
int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
// 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.)
int DebugDrawIdConflictsCount; // Locked count (preserved when holding CTRL)
ImGuiDebugLogFlags DebugLogFlags;
ImGuiTextBuffer DebugLogBuf;
ImGuiTextIndex DebugLogIndex;
int DebugLogSkippedErrors;
ImGuiDebugLogFlags DebugLogAutoDisableFlags;
ImU8 DebugLogAutoDisableFrames;
ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.
@ -3706,11 +3728,15 @@ namespace ImGui
IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window);
IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window);
// Error Checking, State Recovery
IMGUI_API void ErrorLogCallbackToDebugLog(void* user_data, const char* fmt, ...);
IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
// 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 ErrorCheckEndFrameFinalizeErrorTooltip();
IMGUI_API bool BeginErrorTooltip();
IMGUI_API void EndErrorTooltip();
// Debug Tools
IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free

View File

@ -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

View File

@ -7398,6 +7398,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io)
{
ImGuiContext& g = *GImGui;
IM_UNUSED(function);
for (const ImGuiSelectionRequest& req : io->Requests)
{
if (req.Type == ImGuiSelectionRequestType_SetAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear");
@ -7557,7 +7558,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);
@ -8164,6 +8165,13 @@ void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
//-------------------------------------------------------------------------
// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
// This handle some subtleties with capturing info from the label, but for 99% uses it could essentially be rewritten as:
// if (ImGui::BeginChild("...", ImVec2(ImGui::CalcItemWidth(), ImGui::GetTextLineHeight() * 7.5f), ImGuiChildFlags_FrameStyle))
// { .... }
// ImGui::EndChild();
// ImGui::SameLine();
// ImGui::AlignTextToFramePadding();
// ImGui::Text("Label");
// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty"
// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height).
bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)