From 4f10fe0a27f73fc44d49c8b9d970c30fcd5131f8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 12:12:49 +0200 Subject: [PATCH 01/18] TreePush: removed arbitrary/weird suppot for TreePush((const char*)NULL) --- docs/CHANGELOG.txt | 5 +++++ docs/FAQ.md | 2 +- imgui_widgets.cpp | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2322101f..adea7106 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,11 @@ Other Changes: the arrow section of TreeNode(), the +/- buttons of InputInt()/InputFloat(), Selectable() with ImGuiSelectableFlags_SelectOnRelease. More generally: any direct use of ButtonBehavior() with the PressedOnClick/PressedOnDoubleClick/PressedOnRelease button policy. +- TreePush(): removed unnecessary/inconsistent legacy behavior where passing a NULL value to + the TreePush(const char*) and TreePush(const void*) functions would use an hardcoded replacement. + The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) + (_explicitely_ casting a null pointer to const char*), which is unlikely and will now crash. + You may replace it with anything else. - Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken in 1.84). (#4538) - Menus: Adjust closing logic to accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). diff --git a/docs/FAQ.md b/docs/FAQ.md index f0128890..ec90ed46 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -162,7 +162,7 @@ Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-lik --- ### Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... -Your renderer is not using the font texture correctly or it hasn't be uploaded to GPU. +Your renderer is not using the font texture correctly or it hasn't been uploaded to the GPU. - If this happens using the standard backends: A) have you modified the font atlas after `ImGui_ImplXXX_NewFrame()`? B) maybe the texture failed to upload, which could happens if for some reason your texture is too big. Also see [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md). - If this happens with a custom backend: make sure you have uploaded the font texture to the GPU, that all shaders are rendering states are setup properly (e.g. texture is bound). Compare your code to existing backends and use a graphics debugger such as [RenderDoc](https://renderdoc.org) to debug your rendering states. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b055e1ae..5aad4c8b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5983,7 +5983,7 @@ void ImGui::TreePush(const char* str_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(str_id ? str_id : "#TreePush"); + PushID(str_id); } void ImGui::TreePush(const void* ptr_id) @@ -5991,7 +5991,7 @@ void ImGui::TreePush(const void* ptr_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); + PushID(ptr_id); } void ImGui::TreePushOverrideID(ImGuiID id) From d3666940621fcccb500e2725ddd4ec7371eeb8f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 15:16:58 +0200 Subject: [PATCH 02/18] Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) + Added asserts for missing PopItemFlag() calls. Added both to ErrorCheckEndFrameRecover (#1651) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 17 +++++++++++++++++ imgui.h | 2 +- imgui_internal.h | 10 +++++++--- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index adea7106..09e395da 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,7 @@ Other Changes: an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) +- Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when the NavEnableSetMousePos config flag is set. - Nav: Fixed a few widgets from not setting reference keyboard/gamepad navigation ID when @@ -66,6 +67,7 @@ Other Changes: - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). - Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) - PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] +- Misc: Added asserts for missing PopItemFlag() calls. - IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) This allows apps to receive the click on void when that click is used to close popup (by default, clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). diff --git a/imgui.cpp b/imgui.cpp index bb051e8b..b3256979 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6661,11 +6661,14 @@ void ImGui::BeginDisabled(bool disabled) if (was_disabled || disabled) g.CurrentItemFlags |= ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.DisabledStackSize++; } void ImGui::EndDisabled() { ImGuiContext& g = *GImGui; + IM_ASSERT(g.DisabledStackSize > 0); + g.DisabledStackSize--; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; //PopItemFlag(); g.ItemFlagsStack.pop_back(); @@ -7340,11 +7343,21 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); PopID(); } + while (g.DisabledStackSize > window->DC.StackSizesOnBegin.SizeOfDisabledStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); + EndDisabled(); + } while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); PopStyleColor(); } + while (g.ItemFlagsStack.Size > window->DC.StackSizesOnBegin.SizeOfItemFlagsStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); + PopItemFlag(); + } while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); @@ -7385,7 +7398,9 @@ void ImGuiStackSizes::SetToCurrentState() 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; } // Compare to detect usage errors @@ -7403,6 +7418,8 @@ void ImGuiStackSizes::CompareWithCurrentState() // 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!"); diff --git a/imgui.h b/imgui.h index 6ea22bbb..3f107e49 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18414 +#define IMGUI_VERSION_NUM 18415 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index c5d7d747..aec5f498 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1381,7 +1381,9 @@ struct IMGUI_API ImGuiStackSizes short SizeOfFontStack; short SizeOfFocusScopeStack; short SizeOfGroupStack; + short SizeOfItemFlagsStack; short SizeOfBeginPopupStack; + short SizeOfDisabledStack; ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } void SetToCurrentState(); @@ -1613,9 +1615,10 @@ struct ImGuiContext bool DragCurrentAccumDirty; float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio - float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? - int TooltipOverrideCount; + float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() + short DisabledStackSize; + short TooltipOverrideCount; float TooltipSlowDelay; // Time before slow tooltips appears (FIXME: This is temporary until we merge in tooltip timer+priority work) ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once @@ -1780,6 +1783,7 @@ struct ImGuiContext DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; DisabledAlphaBackup = 0.0f; + DisabledStackSize = 0; ScrollbarClickDeltaToGrabCenter = 0.0f; TooltipOverrideCount = 0; TooltipSlowDelay = 0.50f; @@ -1863,7 +1867,7 @@ struct IMGUI_API ImGuiWindowTempData float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting // FIXME: Can be moved to ImGuiWindowStackData }; // Storage for one window From 66cd21db886be6fe114b9550ff72d65526a34efe Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 15:23:55 +0200 Subject: [PATCH 03/18] Misc: extracted ErrorCheckEndWindowRecover() out of ErrorCheckEndFrameRecover(). (#1651) --- imgui.cpp | 113 +++++++++++++++++++++++++---------------------- imgui_internal.h | 1 + 2 files changed, 62 insertions(+), 52 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b3256979..5cab7224 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7316,61 +7316,11 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi ImGuiContext& g = *GImGui; while (g.CurrentWindowStack.Size > 0) { - while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); - EndTable(); - } + ErrorCheckEndWindowRecover(log_callback, user_data); ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window != NULL); - while (g.CurrentTabBar != NULL) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); - EndTabBar(); - } - while (window->DC.TreeDepth > 0) - { - if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); - TreePop(); - } - while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); - EndGroup(); - } - while (window->IDStack.Size > 1) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); - PopID(); - } - while (g.DisabledStackSize > window->DC.StackSizesOnBegin.SizeOfDisabledStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); - EndDisabled(); - } - while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); - PopStyleColor(); - } - while (g.ItemFlagsStack.Size > window->DC.StackSizesOnBegin.SizeOfItemFlagsStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); - PopItemFlag(); - } - while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); - PopStyleVar(); - } - while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); - PopFocusScope(); - } if (g.CurrentWindowStack.Size == 1) { - IM_ASSERT(g.CurrentWindow->IsFallbackWindow); + IM_ASSERT(window->IsFallbackWindow); break; } IM_ASSERT(window == g.CurrentWindow); @@ -7387,6 +7337,65 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi } } +// Must be called before End()/EndChild() +void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data) +{ + ImGuiContext& g = *GImGui; + while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); + EndTable(); + } + + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(window != NULL); + while (g.CurrentTabBar != NULL) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); + EndTabBar(); + } + while (window->DC.TreeDepth > 0) + { + if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); + TreePop(); + } + while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); + EndGroup(); + } + while (window->IDStack.Size > 1) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); + PopID(); + } + while (g.DisabledStackSize > window->DC.StackSizesOnBegin.SizeOfDisabledStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); + EndDisabled(); + } + while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); + PopStyleColor(); + } + while (g.ItemFlagsStack.Size > window->DC.StackSizesOnBegin.SizeOfItemFlagsStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); + PopItemFlag(); + } + while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); + PopStyleVar(); + } + while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); + PopFocusScope(); + } +} + // Save current stack sizes for later compare void ImGuiStackSizes::SetToCurrentState() { diff --git a/imgui_internal.h b/imgui_internal.h index aec5f498..0aa49df7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2681,6 +2681,7 @@ namespace ImGui // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); + IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } From 2d0a6a4969b4da5eae0704e740e1ae0e11b946d9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 15:36:03 +0200 Subject: [PATCH 04/18] Misc: moved StacSizeOnBegin out of window instance into window stack data. --- imgui.cpp | 21 +++++++++++---------- imgui_internal.h | 36 ++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5cab7224..264493b8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5845,12 +5845,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Add to stack // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() + g.CurrentWindow = window; ImGuiWindowStackData window_stack_data; window_stack_data.Window = window; window_stack_data.ParentLastItemDataBackup = g.LastItemData; + window_stack_data.StackSizesOnBegin.SetToCurrentState(); g.CurrentWindowStack.push_back(window_stack_data); - g.CurrentWindow = window; - window->DC.StackSizesOnBegin.SetToCurrentState(); g.CurrentWindow = NULL; if (flags & ImGuiWindowFlags_Popup) @@ -6465,10 +6465,10 @@ void ImGui::End() // Pop from window stack g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; - g.CurrentWindowStack.pop_back(); if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); - window->DC.StackSizesOnBegin.CompareWithCurrentState(); + g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); + g.CurrentWindowStack.pop_back(); SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); } @@ -7348,6 +7348,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo } ImGuiWindow* window = g.CurrentWindow; + ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin; IM_ASSERT(window != NULL); while (g.CurrentTabBar != NULL) //-V1044 { @@ -7359,7 +7360,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); TreePop(); } - while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) + while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) { if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); EndGroup(); @@ -7369,27 +7370,27 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); PopID(); } - while (g.DisabledStackSize > window->DC.StackSizesOnBegin.SizeOfDisabledStack) + while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) { if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); EndDisabled(); } - while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) + while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); PopStyleColor(); } - while (g.ItemFlagsStack.Size > window->DC.StackSizesOnBegin.SizeOfItemFlagsStack) + while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); PopItemFlag(); } - while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) + while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); PopStyleVar(); } - while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) + while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); PopFocusScope(); diff --git a/imgui_internal.h b/imgui_internal.h index 0aa49df7..2c9f5b96 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1123,11 +1123,29 @@ struct ImGuiLastItemData ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } }; +struct IMGUI_API ImGuiStackSizes +{ + short SizeOfIDStack; + short SizeOfColorStack; + short SizeOfStyleVarStack; + short SizeOfFontStack; + short SizeOfFocusScopeStack; + short SizeOfGroupStack; + short SizeOfItemFlagsStack; + short SizeOfBeginPopupStack; + short SizeOfDisabledStack; + + ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } + void SetToCurrentState(); + void CompareWithCurrentState(); +}; + // Data saved for each window pushed into the stack struct ImGuiWindowStackData { ImGuiWindow* Window; ImGuiLastItemData ParentLastItemDataBackup; + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting }; struct ImGuiShrinkWidthItem @@ -1373,23 +1391,6 @@ struct ImGuiMetricsConfig } }; -struct IMGUI_API ImGuiStackSizes -{ - short SizeOfIDStack; - short SizeOfColorStack; - short SizeOfStyleVarStack; - short SizeOfFontStack; - short SizeOfFocusScopeStack; - short SizeOfGroupStack; - short SizeOfItemFlagsStack; - short SizeOfBeginPopupStack; - short SizeOfDisabledStack; - - ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } - void SetToCurrentState(); - void CompareWithCurrentState(); -}; - //----------------------------------------------------------------------------- // [SECTION] Generic context hooks //----------------------------------------------------------------------------- @@ -1867,7 +1868,6 @@ struct IMGUI_API ImGuiWindowTempData float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting // FIXME: Can be moved to ImGuiWindowStackData }; // Storage for one window From 3973de793316e84bee8719bf29091df1ad61b514 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 16:50:20 +0200 Subject: [PATCH 05/18] Internals: removed last parameter to IsClippedEx() + fix PVS studio warnings. --- imgui.cpp | 18 +++++++++--------- imgui_internal.h | 2 +- imgui_tables.cpp | 4 ++-- imgui_widgets.cpp | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 264493b8..0a72737a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3283,13 +3283,13 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return true; } -bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) +bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!bb.Overlaps(window->ClipRect)) if (id == 0 || (id != g.ActiveId && id != g.NavId)) - if (clip_even_when_logged || !g.LogEnabled) + if (!g.LogEnabled) return true; return false; } @@ -7314,7 +7314,7 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi { // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" ImGuiContext& g = *GImGui; - while (g.CurrentWindowStack.Size > 0) + while (g.CurrentWindowStack.Size > 0) //-V1044 { ErrorCheckEndWindowRecover(log_callback, user_data); ImGuiWindow* window = g.CurrentWindow; @@ -7360,7 +7360,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); TreePop(); } - while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) + while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); EndGroup(); @@ -7370,7 +7370,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); PopID(); } - while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) + while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); EndDisabled(); @@ -7380,17 +7380,17 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); PopStyleColor(); } - while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) + while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); PopItemFlag(); } - while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) + while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); PopStyleVar(); } - while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) + while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); PopFocusScope(); @@ -7561,7 +7561,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu #endif // Clipping test - const bool is_clipped = IsClippedEx(bb, id, false); + const bool is_clipped = IsClippedEx(bb, id); if (is_clipped) return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] diff --git a/imgui_internal.h b/imgui_internal.h index 2c9f5b96..66f31a89 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2413,7 +2413,7 @@ namespace ImGui IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); IMGUI_API void ItemInputable(ImGuiWindow* window, ImGuiID id); - IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); + IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 7b783e3c..945e739a 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -324,7 +324,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const ImVec2 avail_size = GetContentRegionAvail(); ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); - if (use_child_window && IsClippedEx(outer_rect, 0, false)) + if (use_child_window && IsClippedEx(outer_rect, 0)) { ItemSize(outer_rect); return false; @@ -3988,7 +3988,7 @@ void ImGui::EndColumns() const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); KeepAliveID(column_id); - if (IsClippedEx(column_hit_rect, column_id, false)) + if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test continue; bool hovered = false, held = false; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5aad4c8b..3aef1a49 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -205,7 +205,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); while (line < text_end) { - if (IsClippedEx(line_rect, 0, false)) + if (IsClippedEx(line_rect, 0)) break; const char* line_end = (const char*)memchr(line, '\n', text_end - line); From 2cffcbdc64ee9301e473225fff697049697aac2d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 17 Sep 2021 15:44:43 +0200 Subject: [PATCH 06/18] InputText: fix Space key with nav enabled interfering with input text boxes (fix bd6c9e99). made it possible to activate InputText with tweak gamepad button (why not, now that we can cancel) (#4552, #2321) --- docs/CHANGELOG.txt | 3 +-- imgui.cpp | 2 +- imgui.h | 2 +- imgui_widgets.cpp | 13 ++++++++++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 09e395da..557b7154 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,8 +45,7 @@ Other Changes: - Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. -- InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing - an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) +- InputText: Allow activating/cancelling/validating input with gamepad nav events. (#2321, #4552) - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) diff --git a/imgui.cpp b/imgui.cpp index 0a72737a..4f3105f2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5118,7 +5118,7 @@ void ImGui::EndChild() ItemAdd(bb, window->ChildId); RenderNavHighlight(bb, window->ChildId); - // When browsing a window that has no activable items (scroll only) we keep a highlight on the child + // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying) if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow) RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); } diff --git a/imgui.h b/imgui.h index 3f107e49..a1d1f4b0 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18415 +#define IMGUI_VERSION_NUM 18416 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3aef1a49..590cb4bf 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3990,6 +3990,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ window->DC.CursorPos = backup_pos; // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. + // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre. PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); @@ -4027,7 +4028,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool focus_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); + const bool user_nav_input_start = (g.ActiveId != id) && (g.NavActivateInputId == id || g.NavActivateId == id); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); @@ -4255,7 +4256,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. - const bool is_validate = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter) || IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter); + const bool is_validate_nav = (g.NavInputSource == ImGuiInputSource_Gamepad && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } @@ -4278,7 +4280,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (is_validate) + else if (is_validate_enter) { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) @@ -4292,6 +4294,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->OnKeyPressed((int)c); } } + else if (is_validate_nav) + { + IM_ASSERT(!is_validate_enter); + enter_pressed = clear_active_id = true; + } else if (is_cancel) { clear_active_id = cancel_edit = true; From ddddabdccfdafffd8664fb4e29230dc4f848137e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 17 Sep 2021 16:44:14 +0200 Subject: [PATCH 07/18] InputText: amend validation test to allow pushing Activate events remotely. (#4552, #2321) --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 590cb4bf..6e3f6f23 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4257,7 +4257,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter); - const bool is_validate_nav = (g.NavInputSource == ImGuiInputSource_Gamepad && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } From c6ca327fb2c4a0bb35b92a1ca8633991379463df Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Sep 2021 18:43:05 +0200 Subject: [PATCH 08/18] Backends: Added more implicit asserts to detect invalid/redundant calls to Shutdown functions. (#4562) --- backends/imgui_impl_allegro5.cpp | 3 ++- backends/imgui_impl_dx10.cpp | 3 ++- backends/imgui_impl_dx11.cpp | 3 ++- backends/imgui_impl_dx12.cpp | 3 ++- backends/imgui_impl_dx9.cpp | 3 ++- backends/imgui_impl_glfw.cpp | 3 ++- backends/imgui_impl_opengl2.cpp | 3 ++- backends/imgui_impl_opengl3.cpp | 3 ++- backends/imgui_impl_sdl.cpp | 3 ++- backends/imgui_impl_vulkan.cpp | 3 ++- backends/imgui_impl_win32.cpp | 3 ++- docs/CHANGELOG.txt | 1 + 12 files changed, 23 insertions(+), 11 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 93d84f1b..ccb1396d 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -335,8 +335,9 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) void ImGui_ImplAllegro5_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplAllegro5_InvalidateDeviceObjects(); if (bd->VertexDecl) diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 8a3206ff..5be46f67 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -556,8 +556,9 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) void ImGui_ImplDX10_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX10_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index a56b757e..bfe5dca2 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -571,8 +571,9 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co void ImGui_ImplDX11_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX11_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 98e56235..5058885c 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -725,8 +725,9 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO void ImGui_ImplDX12_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX12_InvalidateDeviceObjects(); delete[] bd->pFrameResources; diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 259ce928..9234cb01 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -295,8 +295,9 @@ bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) void ImGui_ImplDX9_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX9_InvalidateDeviceObjects(); if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 8cac7241..bf2a6543 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -308,8 +308,9 @@ bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) void ImGui_ImplGlfw_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); if (bd->InstalledCallbacks) { diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index b4ab2a39..ee52632d 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -86,8 +86,9 @@ bool ImGui_ImplOpenGL2_Init() void ImGui_ImplOpenGL2_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL2_DestroyDeviceObjects(); io.BackendRendererName = NULL; diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index dc07ba50..58664ea1 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -269,8 +269,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) void ImGui_ImplOpenGL3_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL3_DestroyDeviceObjects(); io.BackendRendererName = NULL; diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 669c7b77..e5f99bcb 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -280,8 +280,9 @@ bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window) void ImGui_ImplSDL2_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 40dd0bcd..296304fa 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1048,8 +1048,9 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend void ImGui_ImplVulkan_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_DestroyDeviceObjects(); io.BackendRendererName = NULL; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 740b1765..51fb4702 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -172,8 +172,9 @@ bool ImGui_ImplWin32_Init(void* hwnd) void ImGui_ImplWin32_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 557b7154..d53e5b8f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -73,6 +73,7 @@ Other Changes: - Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). - Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) +- Backends: Added more implicit asserts to detect invalid/redundant calls to Shutdown functions. (#4562) - Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] - Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via From bc3d267c5155fee705e9204c1e0958a73b819459 Mon Sep 17 00:00:00 2001 From: James McCartney Date: Mon, 20 Sep 2021 00:20:04 -0700 Subject: [PATCH 09/18] Backends: OSX: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. (#4557, #4563) --- backends/imgui_impl_osx.mm | 24 +++++++++++++++++++++--- docs/CHANGELOG.txt | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 1eb79d99..97555e6c 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -16,9 +16,11 @@ #include "imgui.h" #include "imgui_impl_osx.h" #import +#include // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. // 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. // 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. // 2021-04-19: Inputs: Added a fix for keys remaining stuck in pressed state when CMD-tabbing into different application. @@ -37,7 +39,8 @@ @class ImFocusObserver; // Data -static CFAbsoluteTime g_Time = 0.0; +static double g_HostClockPeriod = 0.0; +static double g_Time = 0.0; static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; static bool g_MouseCursorHidden = false; static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; @@ -52,6 +55,18 @@ static ImFocusObserver* g_FocusObserver = NULL; + (id)_windowResizeEastWestCursor; @end +static void InitHostClockPeriod() +{ + struct mach_timebase_info info; + mach_timebase_info(&info); + g_HostClockPeriod = 1e-9 * ((double)info.denom / (double)info.numer); // Period is the reciprocal of frequency. +} + +static double GetMachAbsoluteTimeInSeconds() +{ + return (double)mach_absolute_time() * g_HostClockPeriod; +} + static void resetKeys() { ImGuiIO& io = ImGui::GetIO(); @@ -232,8 +247,11 @@ void ImGui_ImplOSX_NewFrame(NSView* view) // Setup time step if (g_Time == 0.0) - g_Time = CFAbsoluteTimeGetCurrent(); - CFAbsoluteTime current_time = CFAbsoluteTimeGetCurrent(); + { + InitHostClockPeriod(); + g_Time = GetMachAbsoluteTimeInSeconds(); + } + double current_time = GetMachAbsoluteTimeInSeconds(); io.DeltaTime = (float)(current_time - g_Time); g_Time = current_time; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d53e5b8f..781677c9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -78,6 +78,7 @@ Other Changes: - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] - Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via a direct unclipped PushClipRect() call. (#4464) +- Backends: OSX: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. (#4557, #4563) [@lfnoise] - Backends: All renderers: Normalize clipping rect handling across backends. (#4464) From 15132217a3767de0ddfe0d8e2b1bf5764e827670 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Sep 2021 13:16:00 +0200 Subject: [PATCH 10/18] Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation is disabled. (#4547, #4439) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 781677c9..87b782bf 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,8 @@ Other Changes: the arrow section of TreeNode(), the +/- buttons of InputInt()/InputFloat(), Selectable() with ImGuiSelectableFlags_SelectOnRelease. More generally: any direct use of ButtonBehavior() with the PressedOnClick/PressedOnDoubleClick/PressedOnRelease button policy. +- Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation + is disabled. (#4547, #4439) [@PathogenDavid] - TreePush(): removed unnecessary/inconsistent legacy behavior where passing a NULL value to the TreePush(const char*) and TreePush(const void*) functions would use an hardcoded replacement. The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) diff --git a/imgui.cpp b/imgui.cpp index 4f3105f2..3a012e3b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9777,8 +9777,9 @@ static void ImGui::NavUpdateWindowing() } // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && nav_keyboard_active && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { @@ -9829,7 +9830,7 @@ static void ImGui::NavUpdateWindowing() // Keyboard: Press and Release ALT to toggle menu layer // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. - if (io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) + if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) { g.NavWindowingToggleLayer = true; g.NavInputSource = ImGuiInputSource_Keyboard; From 30546bc0e7bccbebda0e328780e48bc95af3ae2c Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 8 Apr 2021 14:37:03 +0300 Subject: [PATCH 11/18] ColorEdit: Fix multiple issues. (#4014) * Change g.ColorEditLastColor type to ImU32 and store RGB color value. - Fixes inability to change hue when saturation is 0. (#4014) - Fixes edgecases where lossy color conversion prevent restoration of hue/saturation. - Fixes hue value jitter when modifying color using SV square. * Fix hue resetting to 0 when it is set to 255 by explicitly restoring hue if it is 0 and previous value was 1. * Further reduce hue jitter by restoring hue when color is modified using SV square. --- docs/CHANGELOG.txt | 4 ++++ imgui.h | 2 +- imgui_internal.h | 8 +++---- imgui_widgets.cpp | 56 +++++++++++++++++++++++++++------------------- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 87b782bf..753d7e49 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -63,6 +63,10 @@ Other Changes: The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) (_explicitely_ casting a null pointer to const char*), which is unlikely and will now crash. You may replace it with anything else. +- ColorEdit4: Fixed not being able to change hue when saturation is 0. (#4014) [@rokups] +- ColorEdit4: Fixed hue resetting to 0 when it is set to 255. [@rokups] +- ColorEdit4: Fixed hue value jitter when source color is stored as RGB in 32-bit integer and perform + RGB<>HSV round trips every frames. [@rokups] - Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken in 1.84). (#4538) - Menus: Adjust closing logic to accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). diff --git a/imgui.h b/imgui.h index a1d1f4b0..91041a8c 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18416 +#define IMGUI_VERSION_NUM 18417 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index 66f31a89..01a41953 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1606,9 +1606,9 @@ struct ImGuiContext ImFont InputTextPasswordFont; ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets - float ColorEditLastHue; // Backup of last Hue associated to LastColor[3], so we can restore Hue in lossy RGB<>HSV round trips - float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips - float ColorEditLastColor[3]; + float ColorEditLastHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips + float ColorEditLastSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips + ImU32 ColorEditLastColor; // RGB value with alpha set to 0. ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. ImGuiComboPreviewData ComboPreviewData; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. @@ -1777,7 +1777,7 @@ struct ImGuiContext TempInputId = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditLastHue = ColorEditLastSat = 0.0f; - ColorEditLastColor[0] = ColorEditLastColor[1] = ColorEditLastColor[2] = FLT_MAX; + ColorEditLastColor = 0; SliderCurrentAccum = 0.0f; SliderCurrentAccumDirty = false; DragCurrentAccumDirty = false; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6e3f6f23..633a1f2e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4781,6 +4781,30 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); } +// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. +// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. +static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) +{ + // This check is optional. Suppose we have two color widgets side by side, both widgets display different colors, but both colors have hue and/or saturation undefined. + // With color check: hue/saturation is preserved in one widget. Editing color in one widget would reset hue/saturation in another one. + // Without color check: common hue/saturation would be displayed in all widgets that have hue/saturation undefined. + // g.ColorEditLastColor is stored as ImU32 RGB value: this essentially gives us color equality check with reduced precision. + // Tiny external color changes would not be detected and this check would still pass. This is OK, since we only restore hue/saturation _only_ if they are undefined, + // therefore this change flipping hue/saturation from undefined to a very tiny value would still be represented in color picker. + ImGuiContext& g = *GImGui; + if (g.ColorEditLastColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + return; + + // When S == 0, H is undefined. + // When H == 1 it wraps around to 0. + if (*S == 0.0f || (*H == 0.0f && g.ColorEditLastHue == 1)) + *H = g.ColorEditLastHue; + + // When V == 0, S is undefined. + if (*V == 0.0f) + *S = g.ColorEditLastSat; +} + // Edit colors components (each component in 0.0f..1.0f range). // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. // With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. @@ -4836,13 +4860,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (f[1] == 0) - f[0] = g.ColorEditLastHue; - if (f[2] == 0) - f[1] = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &f[0], &f[1], &f[2]); } int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; @@ -4977,7 +4995,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag g.ColorEditLastHue = f[0]; g.ColorEditLastSat = f[1]; ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - memcpy(g.ColorEditLastColor, f, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); } if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); @@ -5112,13 +5130,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5171,6 +5183,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); + + // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. + if (g.ColorEditLastColor == ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + H = g.ColorEditLastHue; value_changed = value_changed_sv = true; } if (!(flags & ImGuiColorEditFlags_NoOptions)) @@ -5247,7 +5263,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); g.ColorEditLastHue = H; g.ColorEditLastSat = S; - memcpy(g.ColorEditLastColor, col, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5301,13 +5317,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl G = col[1]; B = col[2]; ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) // Fix local Hue as display below will use it immediately. - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. } else if (flags & ImGuiColorEditFlags_InputHSV) { From 15fe7ba31ff3b281c3b4336aa2bdf70ab2058e31 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 12 Apr 2021 12:42:04 +0300 Subject: [PATCH 12/18] ColorPicker: Fix not being able to pick exactly (1.0f, 1.0f, 1.0f) color by dragging toward the edges of the SV square. (#3517) Old code attempted to mitigate hue/saturation resetting for colors where these components are undefined. Since we now explicitly back up and restore these components this workaround is no longer necessary. --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 753d7e49..b12ce98f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -67,6 +67,8 @@ Other Changes: - ColorEdit4: Fixed hue resetting to 0 when it is set to 255. [@rokups] - ColorEdit4: Fixed hue value jitter when source color is stored as RGB in 32-bit integer and perform RGB<>HSV round trips every frames. [@rokups] +- ColorPicker4: Fixed picker being unable to select exact 1.0f color when dragging toward the edges + of the SV square (previously picked 0.999989986f). (#3517) [@rokups] - Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken in 1.84). (#4538) - Menus: Adjust closing logic to accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 633a1f2e..9e045e9c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5260,7 +5260,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { if (flags & ImGuiColorEditFlags_InputRGB) { - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); g.ColorEditLastHue = H; g.ColorEditLastSat = S; g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); From d7260104b145fc8a25e3423575962a082bfd2e33 Mon Sep 17 00:00:00 2001 From: Martin Ejdestig Date: Tue, 21 Sep 2021 19:18:21 +0200 Subject: [PATCH 13/18] Added comments about sliders clamping and ImGuiSliderFlags_AlwaysClamp (#4573) --- imgui.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.h b/imgui.h index 91041a8c..df08ea64 100644 --- a/imgui.h +++ b/imgui.h @@ -522,12 +522,12 @@ namespace ImGui IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); // Widgets: Drag Sliders - // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits. + // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -546,7 +546,7 @@ namespace ImGui IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Regular Sliders - // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. From 62b17f928ee43cf681e5e1dcbe27a463c1ebef4d Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 21 Sep 2021 17:12:30 +0200 Subject: [PATCH 14/18] Backends: SDL_Renderer: Added renderer backend for SDL 2.0.17+ (#3926) (Squashed 20 commits) --- backends/imgui_impl_sdl.cpp | 5 + backends/imgui_impl_sdl.h | 1 + backends/imgui_impl_sdlrenderer.cpp | 223 ++++++++++++++++++ backends/imgui_impl_sdlrenderer.h | 19 ++ docs/BACKENDS.md | 1 + docs/EXAMPLES.md | 5 + docs/README.md | 2 +- examples/example_sdl_sdlrenderer/Makefile | 79 +++++++ examples/example_sdl_sdlrenderer/README.md | 25 ++ .../example_sdl_sdlrenderer/build_win32.bat | 8 + .../example_sdl_sdlrenderer.vcxproj | 181 ++++++++++++++ .../example_sdl_sdlrenderer.vcxproj.filters | 61 +++++ examples/example_sdl_sdlrenderer/main.cpp | 168 +++++++++++++ 13 files changed, 777 insertions(+), 1 deletion(-) create mode 100644 backends/imgui_impl_sdlrenderer.cpp create mode 100644 backends/imgui_impl_sdlrenderer.h create mode 100644 examples/example_sdl_sdlrenderer/Makefile create mode 100644 examples/example_sdl_sdlrenderer/README.md create mode 100644 examples/example_sdl_sdlrenderer/build_win32.bat create mode 100644 examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj create mode 100644 examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj.filters create mode 100644 examples/example_sdl_sdlrenderer/main.cpp diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index e5f99bcb..651fda51 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -251,6 +251,11 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) return true; } +bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window) +{ + return ImGui_ImplSDL2_Init(window); +} + bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { IM_UNUSED(sdl_gl_context); // Viewport branch will need this. diff --git a/backends/imgui_impl_sdl.h b/backends/imgui_impl_sdl.h index 9b40a676..f85233af 100644 --- a/backends/imgui_impl_sdl.h +++ b/backends/imgui_impl_sdl.h @@ -21,6 +21,7 @@ struct SDL_Window; typedef union SDL_Event SDL_Event; +IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window); diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp new file mode 100644 index 00000000..a268deec --- /dev/null +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -0,0 +1,223 @@ +// dear imgui: Renderer Backend for SDL_Renderer, with Platform Backend SDL +// (Requires: SDL 2.0.17+) + +// Implemented features: + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + + +// CHANGELOG +// 2021-16-03: Creation + +#include "imgui.h" +#include "imgui_impl_sdlrenderer.h" +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif + +#include "SDL.h" + +#if SDL_MAJOR_VERSION < 2 || SDL_MINOR_VERSION < 0 || SDL_PATCHLEVEL < 17 +# error Requires: SDL 2.0.17+ because of SDL_RenderGeometry function +#endif + +struct ImGui_ImplSDLRenderer_Data +{ + SDL_Renderer *SDLRenderer; + SDL_Texture *FontTexture; + ImGui_ImplSDLRenderer_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +static ImGui_ImplSDLRenderer_Data* ImGui_ImplSDLRenderer_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer_Data*)ImGui::GetIO().BackendRendererUserData : NULL; +} + +// Functions +bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer) +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + IM_ASSERT(renderer != NULL && "SDL_Renderer not initialized!"); + + // Setup backend capabilities flags + ImGui_ImplSDLRenderer_Data* bd = IM_NEW(ImGui_ImplSDLRenderer_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_SDLRenderer"; + + bd->SDLRenderer = renderer; + return true; +} + +void ImGui_ImplSDLRenderer_Shutdown() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + + ImGui_ImplSDLRenderer_DestroyDeviceObjects(); + + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + IM_DELETE(bd); +} + +static void ImGui_ImplSDLRenderer_SetupRenderState() +{ + ImGui_ImplSDLRenderer_Data *bd = ImGui_ImplSDLRenderer_GetBackendData(); + + // Clear out any viewports and cliprects set by the user + SDL_RenderSetViewport(bd->SDLRenderer, NULL); + SDL_RenderSetClipRect(bd->SDLRenderer, NULL); +} + +void ImGui_ImplSDLRenderer_NewFrame() +{ + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDLRenderer_Init()?"); + + if (!bd->FontTexture) { + ImGui_ImplSDLRenderer_CreateDeviceObjects(); + } +} + +void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) +{ + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + + // If there's a scale factor set by the user, use that instead + float rsX = 1.0f; + float rsY = 1.0f; + SDL_RenderGetScale(bd->SDLRenderer, &rsX, &rsY); + + ImVec2 renderScale; + // If the user has specified a scale factor to SDL_Renderer already (via SDL_RenderSetScale()), SDL will scale whatever we pass + // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. + renderScale.x = (rsX == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; + renderScale.y = (rsY == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; + + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * renderScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * renderScale.y); + if (fb_width == 0 || fb_height == 0) + return; + + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = renderScale; + + ImGui_ImplSDLRenderer_SetupRenderState(); + + for (int n = 0; n < draw_data->CmdListsCount; n++) { + + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) { + ImGui_ImplSDLRenderer_SetupRenderState(); + } else { + pcmd->UserCallback(cmd_list, pcmd); + } + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec4 clip_rect; + clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; + clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; + clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; + clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; + + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + { + SDL_Rect r; + r.x = clip_rect.x; + r.y = clip_rect.y; + r.w = clip_rect.z - clip_rect.x; + r.h = clip_rect.w - clip_rect.y; + + SDL_RenderSetClipRect(bd->SDLRenderer, &r); + + + int xy_stride = sizeof(ImDrawVert); + float *xy = (float *)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)); + + int uv_stride = sizeof(ImDrawVert); + float *uv = (float*)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)); + + int col_stride = sizeof(ImDrawVert); + int *color = (int*)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)); + + SDL_Texture *tex = (SDL_Texture*)pcmd->TextureId; + + SDL_RenderGeometryRaw(bd->SDLRenderer, tex, + xy, xy_stride, color, + col_stride, + uv, uv_stride, + cmd_list->VtxBuffer.Size, + idx_buffer, pcmd->ElemCount, sizeof (ImDrawIdx)); + + } + } + idx_buffer += pcmd->ElemCount; + } + } +} + +// Called by Init/NewFrame/Shutdown +bool ImGui_ImplSDLRenderer_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height); + if (bd->FontTexture == NULL) { + SDL_Log("error creating texture"); + return false; + } + SDL_UpdateTexture(bd->FontTexture, NULL, pixels, 4 * width); + SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND); + // Store our identifier + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); + + return true; +} + +void ImGui_ImplSDLRenderer_DestroyFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + if (bd->FontTexture) { + io.Fonts->SetTexID(0); + SDL_DestroyTexture(bd->FontTexture); + bd->FontTexture = NULL; + } +} + +bool ImGui_ImplSDLRenderer_CreateDeviceObjects() +{ + return ImGui_ImplSDLRenderer_CreateFontsTexture(); +} + +void ImGui_ImplSDLRenderer_DestroyDeviceObjects() +{ + ImGui_ImplSDLRenderer_DestroyFontsTexture(); +} + diff --git a/backends/imgui_impl_sdlrenderer.h b/backends/imgui_impl_sdlrenderer.h new file mode 100644 index 00000000..7b439618 --- /dev/null +++ b/backends/imgui_impl_sdlrenderer.h @@ -0,0 +1,19 @@ +// dear imgui: Renderer Backend for SDL using SDL_Renderer +// (Requires: SDL 2.0.17+) + + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API + +struct SDL_Renderer; + +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_DestroyDeviceObjects(); diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 2ad719a9..bbd66854 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -75,6 +75,7 @@ List of Renderer Backends: imgui_impl_metal.mm ; Metal (with ObjC) imgui_impl_opengl2.cpp ; OpenGL 2 (legacy, fixed pipeline <- don't use with modern OpenGL context) imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2, OpenGL ES 3 (modern programmable pipeline) + imgui_impl_sdlrenderer.cpp; use SDL2 and SDL_Renderer, any SDL backend (opengl, gles, gles2, d3d9, d3d11, metal or software) can be used underneath imgui_impl_vulkan.cpp ; Vulkan imgui_impl_wgpu.cpp ; WebGPU diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 0fd7c250..69ecfa87 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -130,6 +130,11 @@ This uses more modern OpenGL calls and custom shaders.
This may actually also work with OpenGL 2.x contexts!
Prefer using that if you are using modern OpenGL in your application (anything with shaders). +[example_sdl_sdlrenderer/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl_sdlrenderer/)
+Use SDL2 and SDL_Renderer, any SDL backend (opengl, gles, gles2, d3d9, d3d11, metal or software) can be used underneath
+= main.cpp + imgui_impl_sdl.cpp + imgui_impl_sdlrenderer.cpp
+It requires SDL 2.0.17+ + [example_glfw_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_vulkan/)
GLFW (Win32, Mac, Linux) + Vulkan example.
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp
diff --git a/docs/README.md b/docs/README.md index 25850973..9d6dc93f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -116,7 +116,7 @@ On most platforms and when using C++, **you should be able to use a combination Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading one texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that. If you are an experienced programmer at ease with those concepts, it should take you less than two hours to integrate Dear ImGui in your custom engine. **Make sure to spend time reading the [FAQ](https://www.dearimgui.org/faq), comments, and some of the examples/ application!** Officially maintained backends/bindings (in repository): -- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, Vulkan, WebGPU. +- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_Renderer, Vulkan, WebGPU. - Platforms: GLFW, SDL2, Win32, Glut, OSX, Android. - Frameworks: Emscripten, Allegro5, Marmalade. diff --git a/examples/example_sdl_sdlrenderer/Makefile b/examples/example_sdl_sdlrenderer/Makefile new file mode 100644 index 00000000..5667789a --- /dev/null +++ b/examples/example_sdl_sdlrenderer/Makefile @@ -0,0 +1,79 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# +# You will need SDL2 (http://www.libsdl.org): +# Linux: +# apt-get install libsdl2-dev +# Mac OS X: +# brew install sdl2 +# MSYS2: +# pacman -S mingw-w64-i686-SDL2 +# + +#CXX = g++ +#CXX = clang++ + +EXE = example_sdl_sdlrenderer +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl.cpp $(IMGUI_DIR)/backends/imgui_impl_sdlrenderer.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) + +CXXFLAGS = -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +CXXFLAGS += -g -Wall -Wformat +LIBS = + +##--------------------------------------------------------------------- +## BUILD FLAGS PER PLATFORM +##--------------------------------------------------------------------- + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + LIBS += -lGL -ldl `sdl2-config --libs` + + CXXFLAGS += `sdl2-config --cflags` + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs` + LIBS += -L/usr/local/lib -L/opt/local/lib + + CXXFLAGS += `sdl2-config --cflags` + CXXFLAGS += -I/usr/local/include -I/opt/local/include + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(OS), Windows_NT) + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2` + + CXXFLAGS += `pkg-config --cflags sdl2` + CFLAGS = $(CXXFLAGS) +endif + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(ECHO_MESSAGE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/examples/example_sdl_sdlrenderer/README.md b/examples/example_sdl_sdlrenderer/README.md new file mode 100644 index 00000000..5f431240 --- /dev/null +++ b/examples/example_sdl_sdlrenderer/README.md @@ -0,0 +1,25 @@ + +# How to Build + +- On Windows with Visual Studio's CLI + +``` +set SDL2_DIR=path_to_your_sdl2_folder +cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_sdlrenderer.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib /subsystem:console +# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries +# or for 64-bit: +cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_sdlrenderer.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x64 SDL2.lib SDL2main.lib /subsystem:console +``` + +- On Linux and similar Unixes + +``` +c++ `sdl2-config --cflags` -I .. -I ../.. main.cpp ../../backends/imgui_impl_sdl.cpp ../../backends/imgui_impl_sdlrenderer.cpp ../../imgui*.cpp `sdl2-config --libs` -lGL +``` + +- On Mac OS X + +``` +brew install sdl2 +c++ `sdl2-config --cflags` -I .. -I ../.. main.cpp ../../backends/imgui_impl_sdl.cpp ../../backends/imgui_impl_sdlrenderer.cpp ../../imgui*.cpp `sdl2-config --libs` -framework OpenGl +``` diff --git a/examples/example_sdl_sdlrenderer/build_win32.bat b/examples/example_sdl_sdlrenderer/build_win32.bat new file mode 100644 index 00000000..6c1b5fde --- /dev/null +++ b/examples/example_sdl_sdlrenderer/build_win32.bat @@ -0,0 +1,8 @@ +@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. +@set OUT_DIR=Debug +@set OUT_EXE=example_sdl_sdlrenderer_ +@set INCLUDES=/I..\.. /I..\..\backends /I%SDL2_DIR%\include +@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp +@set LIBS=/LIBPATH:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib +mkdir %OUT_DIR% +cl /nologo /Zi /MD %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console diff --git a/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj new file mode 100644 index 00000000..376a2275 --- /dev/null +++ b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj @@ -0,0 +1,181 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741} + example_sdl_sdlrenderer + 8.1 + + + + Application + true + MultiByte + v110 + + + Application + true + MultiByte + v110 + + + Application + false + true + MultiByte + v110 + + + Application + false + true + MultiByte + v110 + + + + + + + + + + + + + + + + + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) + + + true + %SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) + + + true + %SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) + false + + + true + true + true + %SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + + + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) + false + + + true + true + true + %SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj.filters b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj.filters new file mode 100644 index 00000000..01e98fcb --- /dev/null +++ b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj.filters @@ -0,0 +1,61 @@ + + + + + {20b90ce4-7fcb-4731-b9a0-075f875de82d} + + + {f18ab499-84e1-499f-8eff-9754361e0e52} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + imgui + + + imgui + + + imgui + + + sources + + + imgui + + + imgui + + + sources + + + sources + + + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + + + + sources + + + diff --git a/examples/example_sdl_sdlrenderer/main.cpp b/examples/example_sdl_sdlrenderer/main.cpp new file mode 100644 index 00000000..4fd04028 --- /dev/null +++ b/examples/example_sdl_sdlrenderer/main.cpp @@ -0,0 +1,168 @@ +// Dear ImGui: standalone example application for SDL2 + SDL_Renderer +// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +// See imgui_impl_sdl.cpp for details. +// (Requires: SDL 2.0.17+) + +#include "imgui.h" +#include "imgui_impl_sdl.h" +#include "imgui_impl_sdlrenderer.h" +#include +#include + +#if SDL_MAJOR_VERSION < 2 || SDL_MINOR_VERSION < 0 || SDL_PATCHLEVEL < 17 +# error Requires: SDL 2.0.17+ because of SDL_RenderGeometry function +#endif + +// Main code +int main(int, char**) +{ + // Setup SDL + // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, + // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) + { + printf("Error: %s\n", SDL_GetError()); + return -1; + } + + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + + // Setup window + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + // Setup Platform/Renderer backends + ImGui_ImplSDL2_InitForSDLRenderer(window); + + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + if (renderer == NULL) { + SDL_Log("Error creating SDL renderer"); + return false; + } else { + SDL_RendererInfo info; + SDL_GetRendererInfo(renderer, &info); + SDL_Log("Current SDL Renderer: %s", info.name); + } + + ImGui_ImplSDLRenderer_Init(renderer); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Read 'docs/FONTS.md' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != NULL); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL2_ProcessEvent(&event); + if (event.type == SDL_QUIT) + done = true; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + done = true; + } + + // Start the Dear ImGui frame + ImGui_ImplSDLRenderer_NewFrame(); + ImGui_ImplSDL2_NewFrame(window); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData()); + + SDL_RenderPresent(renderer); + } + + // Cleanup + ImGui_ImplSDLRenderer_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} From fba756176d659a8de8f711f68a4a04c675add942 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Sep 2021 19:29:39 +0200 Subject: [PATCH 15/18] Backends: SDL_Renderer: Amend 1d2d246, various tweaks, fixes, sync to latest. (#3926) --- backends/imgui_impl_sdl.cpp | 10 +- backends/imgui_impl_sdl.h | 2 +- backends/imgui_impl_sdlrenderer.cpp | 146 +++++++++--------- backends/imgui_impl_sdlrenderer.h | 12 +- docs/BACKENDS.md | 2 +- docs/CHANGELOG.txt | 2 + docs/EXAMPLES.md | 11 +- examples/example_sdl_sdlrenderer/README.md | 2 +- .../example_sdl_sdlrenderer.vcxproj | 12 +- examples/example_sdl_sdlrenderer/main.cpp | 45 +++--- 10 files changed, 123 insertions(+), 121 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 651fda51..b725e0ac 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -251,11 +251,6 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) return true; } -bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window) -{ - return ImGui_ImplSDL2_Init(window); -} - bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { IM_UNUSED(sdl_gl_context); // Viewport branch will need this. @@ -283,6 +278,11 @@ bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window) return ImGui_ImplSDL2_Init(window); } +bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window) +{ + return ImGui_ImplSDL2_Init(window); +} + void ImGui_ImplSDL2_Shutdown() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); diff --git a/backends/imgui_impl_sdl.h b/backends/imgui_impl_sdl.h index f85233af..d2439eb7 100644 --- a/backends/imgui_impl_sdl.h +++ b/backends/imgui_impl_sdl.h @@ -21,11 +21,11 @@ struct SDL_Window; typedef union SDL_Event SDL_Event; -IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window); IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(); IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index a268deec..4f564f47 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -1,15 +1,21 @@ -// dear imgui: Renderer Backend for SDL_Renderer, with Platform Backend SDL +// dear imgui: Renderer Backend for SDL_Renderer // (Requires: SDL 2.0.17+) +// Important to understand: SDL_Renderer is an _optional_ component of SDL. We do not recommend you use SDL_Renderer +// because it provide a rather limited API to the end-user. We provide this backend for the sake of completeness. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. + // Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! +// Missing features: +// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs - // CHANGELOG -// 2021-16-03: Creation +// 2021-09-21: Initial version. #include "imgui.h" #include "imgui_impl_sdlrenderer.h" @@ -19,16 +25,17 @@ #include // intptr_t #endif -#include "SDL.h" - -#if SDL_MAJOR_VERSION < 2 || SDL_MINOR_VERSION < 0 || SDL_PATCHLEVEL < 17 -# error Requires: SDL 2.0.17+ because of SDL_RenderGeometry function +// SDL +#include +#if !SDL_VERSION_ATLEAST(2,0,17) +#error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function #endif +// SDL_Renderer data struct ImGui_ImplSDLRenderer_Data { - SDL_Renderer *SDLRenderer; - SDL_Texture *FontTexture; + SDL_Renderer* SDLRenderer; + SDL_Texture* FontTexture; ImGui_ImplSDLRenderer_Data() { memset(this, 0, sizeof(*this)); } }; @@ -40,7 +47,7 @@ static ImGui_ImplSDLRenderer_Data* ImGui_ImplSDLRenderer_GetBackendData() } // Functions -bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer) +bool ImGui_ImplSDLRenderer_Init(SDL_Renderer* renderer) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); @@ -49,16 +56,18 @@ bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer) // Setup backend capabilities flags ImGui_ImplSDLRenderer_Data* bd = IM_NEW(ImGui_ImplSDLRenderer_Data)(); io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_SDLRenderer"; + io.BackendRendererName = "imgui_impl_sdlrenderer"; bd->SDLRenderer = renderer; + return true; } void ImGui_ImplSDLRenderer_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer_DestroyDeviceObjects(); @@ -69,9 +78,10 @@ void ImGui_ImplSDLRenderer_Shutdown() static void ImGui_ImplSDLRenderer_SetupRenderState() { - ImGui_ImplSDLRenderer_Data *bd = ImGui_ImplSDLRenderer_GetBackendData(); + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); - // Clear out any viewports and cliprects set by the user + // Clear out any viewports and cliprect set by the user + // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. SDL_RenderSetViewport(bd->SDLRenderer, NULL); SDL_RenderSetClipRect(bd->SDLRenderer, NULL); } @@ -81,9 +91,8 @@ void ImGui_ImplSDLRenderer_NewFrame() ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDLRenderer_Init()?"); - if (!bd->FontTexture) { + if (!bd->FontTexture) ImGui_ImplSDLRenderer_CreateDeviceObjects(); - } } void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) @@ -91,35 +100,33 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); // If there's a scale factor set by the user, use that instead - float rsX = 1.0f; - float rsY = 1.0f; - SDL_RenderGetScale(bd->SDLRenderer, &rsX, &rsY); - - ImVec2 renderScale; - // If the user has specified a scale factor to SDL_Renderer already (via SDL_RenderSetScale()), SDL will scale whatever we pass - // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. - renderScale.x = (rsX == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; - renderScale.y = (rsY == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; + // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass + // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. + float rsx = 1.0f; + float rsy = 1.0f; + SDL_RenderGetScale(bd->SDLRenderer, &rsx, &rsy); + ImVec2 render_scale; + render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; + render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - int fb_width = (int)(draw_data->DisplaySize.x * renderScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * renderScale.y); + int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); + int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); if (fb_width == 0 || fb_height == 0) return; - // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports - ImVec2 clip_scale = renderScale; + ImVec2 clip_scale = render_scale; + // Render command lists ImGui_ImplSDLRenderer_SetupRenderState(); - - for (int n = 0; n < draw_data->CmdListsCount; n++) { - + for (int n = 0; n < draw_data->CmdListsCount; n++) + { const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; @@ -127,53 +134,39 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) { + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) ImGui_ImplSDLRenderer_SetupRenderState(); - } else { + else pcmd->UserCallback(cmd_list, pcmd); - } } else { // Project scissor/clipping rectangles into framebuffer space - ImVec4 clip_rect; - clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; - clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; - clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; - clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - SDL_Rect r; - r.x = clip_rect.x; - r.y = clip_rect.y; - r.w = clip_rect.z - clip_rect.x; - r.h = clip_rect.w - clip_rect.y; + SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) }; + SDL_RenderSetClipRect(bd->SDLRenderer, &r); - SDL_RenderSetClipRect(bd->SDLRenderer, &r); + const float* xy = (const float*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)); + const float* uv = (const float*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)); + const int* color = (const int*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)); - - int xy_stride = sizeof(ImDrawVert); - float *xy = (float *)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)); - - int uv_stride = sizeof(ImDrawVert); - float *uv = (float*)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)); - - int col_stride = sizeof(ImDrawVert); - int *color = (int*)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)); - - SDL_Texture *tex = (SDL_Texture*)pcmd->TextureId; - - SDL_RenderGeometryRaw(bd->SDLRenderer, tex, - xy, xy_stride, color, - col_stride, - uv, uv_stride, - cmd_list->VtxBuffer.Size, - idx_buffer, pcmd->ElemCount, sizeof (ImDrawIdx)); - - } + // Bind texture, Draw + SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + SDL_RenderGeometryRaw(bd->SDLRenderer, tex, + xy, (int)sizeof(ImDrawVert), + color, (int)sizeof(ImDrawVert), + uv, (int)sizeof(ImDrawVert), + cmd_list->VtxBuffer.Size, + idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx)); } - idx_buffer += pcmd->ElemCount; } } } @@ -181,19 +174,24 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) // Called by Init/NewFrame/Shutdown bool ImGui_ImplSDLRenderer_CreateFontsTexture() { - // Build texture atlas ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + + // Build texture atlas unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height); - if (bd->FontTexture == NULL) { + if (bd->FontTexture == NULL) + { SDL_Log("error creating texture"); return false; } SDL_UpdateTexture(bd->FontTexture, NULL, pixels, 4 * width); SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND); + // Store our identifier io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); @@ -204,7 +202,8 @@ void ImGui_ImplSDLRenderer_DestroyFontsTexture() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); - if (bd->FontTexture) { + if (bd->FontTexture) + { io.Fonts->SetTexID(0); SDL_DestroyTexture(bd->FontTexture); bd->FontTexture = NULL; @@ -220,4 +219,3 @@ void ImGui_ImplSDLRenderer_DestroyDeviceObjects() { ImGui_ImplSDLRenderer_DestroyFontsTexture(); } - diff --git a/backends/imgui_impl_sdlrenderer.h b/backends/imgui_impl_sdlrenderer.h index 7b439618..8c67fa3a 100644 --- a/backends/imgui_impl_sdlrenderer.h +++ b/backends/imgui_impl_sdlrenderer.h @@ -1,13 +1,21 @@ -// dear imgui: Renderer Backend for SDL using SDL_Renderer +// dear imgui: Renderer Backend for SDL_Renderer // (Requires: SDL 2.0.17+) +// Important to understand: SDL_Renderer is an _optional_ component of SDL. We do not recommend you use SDL_Renderer +// because it provide a rather limited API to the end-user. We provide this backend for the sake of completeness. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. + +// Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! +// Missing features: +// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. #pragma once #include "imgui.h" // IMGUI_IMPL_API struct SDL_Renderer; -IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer); +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_Init(SDL_Renderer* renderer); IMGUI_IMPL_API void ImGui_ImplSDLRenderer_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer_NewFrame(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data); diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index bbd66854..c878132b 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -75,7 +75,7 @@ List of Renderer Backends: imgui_impl_metal.mm ; Metal (with ObjC) imgui_impl_opengl2.cpp ; OpenGL 2 (legacy, fixed pipeline <- don't use with modern OpenGL context) imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2, OpenGL ES 3 (modern programmable pipeline) - imgui_impl_sdlrenderer.cpp; use SDL2 and SDL_Renderer, any SDL backend (opengl, gles, gles2, d3d9, d3d11, metal or software) can be used underneath + imgui_impl_sdlrenderer.cpp; SDL_Renderer (optional component of SDL2 available from SDL 2.0.18+) imgui_impl_vulkan.cpp ; Vulkan imgui_impl_wgpu.cpp ; WebGPU diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b12ce98f..16334834 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -84,10 +84,12 @@ Other Changes: - Backends: Added more implicit asserts to detect invalid/redundant calls to Shutdown functions. (#4562) - Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] +- Backends: SDL_Renderer: Added SDL_Renderer backend compatible with upcoming SDL 2.0.18. (#3926) [@1bsyl] - Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via a direct unclipped PushClipRect() call. (#4464) - Backends: OSX: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. (#4557, #4563) [@lfnoise] - Backends: All renderers: Normalize clipping rect handling across backends. (#4464) +- Examples: Added SDL + SDL_Renderer example in "examples/example_sdl_sdlrenderer/" folder. (#3926) [@1bsyl] ----------------------------------------------------------------------- diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 69ecfa87..63b64d2e 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -130,11 +130,6 @@ This uses more modern OpenGL calls and custom shaders.
This may actually also work with OpenGL 2.x contexts!
Prefer using that if you are using modern OpenGL in your application (anything with shaders). -[example_sdl_sdlrenderer/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl_sdlrenderer/)
-Use SDL2 and SDL_Renderer, any SDL backend (opengl, gles, gles2, d3d9, d3d11, metal or software) can be used underneath
-= main.cpp + imgui_impl_sdl.cpp + imgui_impl_sdlrenderer.cpp
-It requires SDL 2.0.17+ - [example_glfw_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_vulkan/)
GLFW (Win32, Mac, Linux) + Vulkan example.
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp
@@ -180,6 +175,12 @@ SDL2 (Win32, Mac, Linux, etc.) + OpenGL3+/ES2/ES3 example.
This uses more modern OpenGL calls and custom shaders.
This may actually also work with OpenGL 2.x contexts!
+[example_sdl_sdlrenderer/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl_sdlrenderer/)
+SDL2 (Win32, Mac, Linux, etc.) + SDL_Renderer (most graphics backends are supported underneath)
+= main.cpp + imgui_impl_sdl.cpp + imgui_impl_sdlrenderer.cpp
+This requires SDL 2.0.17+ (expected to release November 2021)
+We do not really recommend using SDL_Renderer as it is a rather primitive API. + [example_sdl_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl_vulkan/)
SDL2 (Win32, Mac, Linux, etc.) + Vulkan example.
= main.cpp + imgui_impl_sdl.cpp + imgui_impl_vulkan.cpp
diff --git a/examples/example_sdl_sdlrenderer/README.md b/examples/example_sdl_sdlrenderer/README.md index 5f431240..209f15ac 100644 --- a/examples/example_sdl_sdlrenderer/README.md +++ b/examples/example_sdl_sdlrenderer/README.md @@ -6,7 +6,7 @@ ``` set SDL2_DIR=path_to_your_sdl2_folder cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_sdlrenderer.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib /subsystem:console -# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries +# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries # or for 64-bit: cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_sdlrenderer.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x64 SDL2.lib SDL2main.lib /subsystem:console ``` diff --git a/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj index 376a2275..4d502ab6 100644 --- a/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj +++ b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -28,27 +28,27 @@ Application true MultiByte - v110 + v140 Application true MultiByte - v110 + v140 Application false true MultiByte - v110 + v140 Application false true MultiByte - v110 + v140 @@ -178,4 +178,4 @@ - + \ No newline at end of file diff --git a/examples/example_sdl_sdlrenderer/main.cpp b/examples/example_sdl_sdlrenderer/main.cpp index 4fd04028..aee4a785 100644 --- a/examples/example_sdl_sdlrenderer/main.cpp +++ b/examples/example_sdl_sdlrenderer/main.cpp @@ -3,8 +3,9 @@ // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs -// See imgui_impl_sdl.cpp for details. -// (Requires: SDL 2.0.17+) +// Important to understand: SDL_Renderer is an _optional_ component of SDL. We do not recommend you use SDL_Renderer +// because it provide a rather limited API to the end-user. We provide this backend for the sake of completeness. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. #include "imgui.h" #include "imgui_impl_sdl.h" @@ -12,8 +13,8 @@ #include #include -#if SDL_MAJOR_VERSION < 2 || SDL_MINOR_VERSION < 0 || SDL_PATCHLEVEL < 17 -# error Requires: SDL 2.0.17+ because of SDL_RenderGeometry function +#if !SDL_VERSION_ATLEAST(2,0,17) +#error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function #endif // Main code @@ -28,18 +29,20 @@ int main(int, char**) return -1; } - // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2"); - // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); - // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); - // Setup window - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+SDL_Renderer example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + + // Setup SDL_Renderer instance + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + if (renderer == NULL) + { + SDL_Log("Error creating SDL_Renderer!"); + return false; + } + //SDL_RendererInfo info; + //SDL_GetRendererInfo(renderer, &info); + //SDL_Log("Current SDL_Renderer: %s", info.name); // Setup Dear ImGui context IMGUI_CHECKVERSION(); @@ -54,17 +57,6 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForSDLRenderer(window); - - SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); - if (renderer == NULL) { - SDL_Log("Error creating SDL renderer"); - return false; - } else { - SDL_RendererInfo info; - SDL_GetRendererInfo(renderer, &info); - SDL_Log("Current SDL Renderer: %s", info.name); - } - ImGui_ImplSDLRenderer_Init(renderer); // Load Fonts @@ -150,8 +142,9 @@ int main(int, char**) // Rendering ImGui::Render(); + SDL_SetRenderDrawColor(renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255)); + SDL_RenderClear(renderer); ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData()); - SDL_RenderPresent(renderer); } From bbb95a5e0640825d97b017e55c72a690c96ddec5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Aug 2021 17:35:27 +0200 Subject: [PATCH 16/18] IO: modify io.AddFocusEvent() to tolerate in/out for multi-viewports. Amend 2f40be6. (merged from docking) (#3532) --- imgui.cpp | 22 ++++++++++++++++------ imgui.h | 4 +++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3a012e3b..a8140862 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1185,13 +1185,8 @@ void ImGuiIO::ClearInputCharacters() InputQueueCharacters.resize(0); } -void ImGuiIO::AddFocusEvent(bool focused) +void ImGuiIO::ClearInputKeys() { - if (focused) - return; - - // Clear buttons state when focus is lost - // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) memset(KeysDown, 0, sizeof(KeysDown)); for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++) KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f; @@ -1201,6 +1196,13 @@ void ImGuiIO::AddFocusEvent(bool focused) NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; } +void ImGuiIO::AddFocusEvent(bool focused) +{ + // We intentionally overwrite this and process in NewFrame(), in order to give a chance + // to multi-viewports backends to queue AddFocusEvent(false),AddFocusEvent(true) in same frame. + AppFocusLost = !focused; +} + //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- @@ -4070,6 +4072,14 @@ void ImGui::NewFrame() g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; + // Clear buttons state when focus is lost + // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) + if (g.IO.AppFocusLost) + { + g.IO.ClearInputKeys(); + g.IO.AppFocusLost = false; + } + // Update keyboard input state // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools g.IO.KeyMods = GetMergedKeyModFlags(); diff --git a/imgui.h b/imgui.h index df08ea64..9ec667d1 100644 --- a/imgui.h +++ b/imgui.h @@ -1892,8 +1892,9 @@ struct ImGuiIO IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string - IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually IMGUI_API void AddFocusEvent(bool focused); // Notifies Dear ImGui when hosting platform windows lose or gain input focus + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually + IMGUI_API void ClearInputKeys(); // [Internal] Release all keys //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -1941,6 +1942,7 @@ struct ImGuiIO float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. + bool AppFocusLost; ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. From 24a77824f267ff61cb76a51241a4b470f8d9a4d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 22 Sep 2021 15:50:40 +0200 Subject: [PATCH 17/18] Added ClosePopupsExceptModals() helper, unused for now (aimed at user being able to close popups on app focus loss, not necessarily a suitable default) --- imgui.cpp | 19 +++++++++++++++++++ imgui_internal.h | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index a8140862..4968187f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4072,6 +4072,10 @@ void ImGui::NewFrame() g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; + // Close popups on focus lost (currently wip/opt-in) + //if (g.IO.AppFocusLost) + // ClosePopupsExceptModals(); + // Clear buttons state when focus is lost // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) if (g.IO.AppFocusLost) @@ -8361,6 +8365,21 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to } } +void ImGui::ClosePopupsExceptModals() +{ + ImGuiContext& g = *GImGui; + + int popup_count_to_keep; + for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--) + { + ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window; + if (!window || window->Flags & ImGuiWindowFlags_Modal) + break; + } + if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below + ClosePopupToLevel(popup_count_to_keep, true); +} + void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; diff --git a/imgui_internal.h b/imgui_internal.h index 01a41953..9e2da8e8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2447,6 +2447,7 @@ namespace ImGui IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); + IMGUI_API void ClosePopupsExceptModals(); IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags); @@ -2454,9 +2455,9 @@ namespace ImGui IMGUI_API ImGuiWindow* GetTopMostPopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); - IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); // Menus + IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); From fc4988ffb048f7d3ebcf9cb96bf7727878ad9d0e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Sep 2021 14:51:14 +0200 Subject: [PATCH 18/18] Added ImGuiFocusedFlags_NoPopupHierarchy and ImGuiHoveredFlags_NoPopupHierarchy (followup #4527) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 32 +++++++++++++++++++++++--------- imgui.h | 16 +++++++++------- imgui_demo.cpp | 35 ++++++++++++++++++++++++++--------- imgui_internal.h | 7 ++++--- 5 files changed, 66 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 16334834..20e6caca 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,10 @@ Breaking Changes: Other Changes: - Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) +- IsWindowFocused: Added ImGuiFocusedFlags_NoPopupHierarchy flag allowing to exclude child popups + from the tested windows when combined with _ChildWindows. +- IsWindowHovered: Added ImGuiHoveredFlags_NoPopupHierarchy flag allowing to exclude child popups + from the tested windows when combined with _ChildWindows. - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. - InputText: Allow activating/cancelling/validating input with gamepad nav events. (#2321, #4552) diff --git a/imgui.cpp b/imgui.cpp index 4968187f..c385a154 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3888,7 +3888,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window, true)) clear_hovered_windows = true; // Disabled mouse? @@ -5778,9 +5778,11 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) window->RootWindow = parent_window->RootWindow; + if (parent_window && (flags & ImGuiWindowFlags_Popup)) + window->RootWindowPopupTree = parent_window->RootWindowPopupTree; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -6726,14 +6728,25 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) { - if (window->RootWindow == potential_parent) + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + return window; +} + +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy) +{ + ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy); + if (window_root == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; + if (window == window_root) // end of chain + return false; window = window->ParentWindow; } return false; @@ -6765,13 +6778,13 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { IM_ASSERT(cur_window); // Not inside a Begin()/End() - + const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = cur_window->RootWindow; + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); bool result; if (flags & ImGuiHoveredFlags_ChildWindows) - result = IsWindowChildOf(ref_window, cur_window); + result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy); else result = (ref_window == cur_window); if (!result) @@ -6798,11 +6811,12 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) return true; IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = cur_window->RootWindow; + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); if (flags & ImGuiHoveredFlags_ChildWindows) - return IsWindowChildOf(ref_window, cur_window); + return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); else return (ref_window == cur_window); } diff --git a/imgui.h b/imgui.h index 9ec667d1..f679762e 100644 --- a/imgui.h +++ b/imgui.h @@ -1267,7 +1267,8 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! - //ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1280,12 +1281,13 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - //ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled + ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window + //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. + ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. + ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3cc50a8f..febca565 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2186,7 +2186,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - if (ImGui::TreeNode("Querying Status (Edited/Active/Hovered etc.)")) + if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) { // Select an item type const char* item_names[] = @@ -2272,43 +2272,63 @@ static void ShowDemoWindowWidgets() if (item_disabled) ImGui::EndDisabled(); + char buf[1] = ""; + ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); + ImGui::SameLine(); + HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) + { static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); + ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true); // Testing IsWindowFocused() function with its various flags. - // Note that the ImGuiFocusedFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" + "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" + "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); // Testing IsWindowHovered() function with its various flags. - // Note that the ImGuiHoveredFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowHovered() = %d\n" "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" + "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" - "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_RootWindow) = %d\n" + "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); ImGui::BeginChild("child", ImVec2(0, 50), true); @@ -2317,9 +2337,6 @@ static void ShowDemoWindowWidgets() if (embed_all_inside_a_child_window) ImGui::EndChild(); - static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above."; - ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly); - // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu associated to the title bar of a window. static bool test_window = false; diff --git a/imgui_internal.h b/imgui_internal.h index 9e2da8e8..2fa71314 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1950,8 +1950,9 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window == Top-level window. + ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. + ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. @@ -2337,7 +2338,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);