BeginChild(): added ImGuiChildFlags_AutoResizeX, ImGuiChildFlags_AutoResizeY, ImGuiChildFlags_AlwaysAutoResize + support for SetNextWindowSizeConstraints(). (#1666, #1395, #1496, #1710) + Demo
Note that child don't report ideal content size to parent so nesting may be difficult.
Note 4e4042b simplified SkipItems logic.
Note e2035a5 standardizing WindowMinSize application on child
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 823892d..a32ce3c 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -94,6 +94,21 @@
child windows from the bottom/right border (toward layout direction). Resized child windows
settings are saved and persistent in .ini file. (#1710)
- BeginChild(): Added ImGuiChildFlags_Border as a replacement for 'bool border = true' parameter.
+ - BeginChild(): Added ImGuiChildFlags_AutoResizeX and ImGuiChildFlags_AutoResizeY to auto-resize
+ on one axis, while generally providing a size on the other axis. (#1666, #1395, #1496, #1710)
+ e.g. BeginChild("name", {-FLT_MIN, 0.0f}, ImGuiChildFlags_AutoResizeY);
+ - Size is only reevaluated if the child window is within visible boundaries or just appearing.
+ This allows coarse clipping to be performed and auto-resizing childs to return false when
+ hidden because of being scrolled out.
+ - Combining this with also specifying ImGuiChildFlags_AlwaysAutoResize disables
+ this optimization, meaning child contents will never be clipped (not recommended).
+ - Please be considerate that child are full windows and carry significiant overhead:
+ combining auto-resizing for both axises to create a non-scrolling child to merely draw
+ a border would be better more optimally using BeginGroup().
+ (until we come up with new helpers for framed groups and work-rect adjustments).
+ - BeginChild(): made it possible to use SetNextWindowSizeConstraints() rectangle, often
+ useful when ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY. (#1666, #1395, #1496)
+ Custom constraint callback are not supported with child window.
- BeginChild(): Added ImGuiChildFlags_FrameStyle as a replacement for BeginChildFrame(),
use it to make child window use FrameBg, FrameRounding, FrameBorderSize, FramePadding
instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.
@@ -192,6 +207,7 @@
- ImVector: Added find_index() helper.
- Demo: Added "Drag and Drop -> Tooltip at target location" demo.
- Demo: Added "Layout -> Child Windows -> Manual-resize" demo. (#1710)
+- Demo: Added "Layout -> Child Windows -> Auto-resize with constraints" demo. (#1666, #1395, #1496, #1710)
- Backends: GLFW: Clear emscripten's MouseWheel callback before shutdown. (#6790, #6096, #4019) [@halx99]
- Backends: GLFW: Added support for F13 to F24 function keys. (#6891)
- Backends: SDL2, SDL3: Added support for F13 to F24 function keys, AppBack, AppForward. (#6891)
diff --git a/imgui.cpp b/imgui.cpp
index 58c21b3..3f32708 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -5444,19 +5444,29 @@
IM_ASSERT(id != 0);
// Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
- const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_FrameStyle;
+ const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle;
IM_UNUSED(ImGuiChildFlags_SupportedMask_);
IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
- if (window_flags & ImGuiWindowFlags_AlwaysAutoResize)
- IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot combine ImGuiChildFlags_ResizeX/ImGuiChildFlags_ResizeY with ImGuiWindowFlags_AlwaysAutoResize.");
+ IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
+ if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
+ {
+ IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
+ IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
+ }
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
#endif
+ if (child_flags & ImGuiChildFlags_AutoResizeX)
+ child_flags &= ~ImGuiChildFlags_ResizeX;
+ if (child_flags & ImGuiChildFlags_AutoResizeY)
+ child_flags &= ~ImGuiChildFlags_ResizeY;
+ // Set window flags
window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar;
window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
-
+ if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
+ window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
@@ -5478,12 +5488,9 @@
// Forward size
// Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
// (the alternative would to store conditional flags per axis, which is possible but more code)
- const ImVec2 content_avail = GetContentRegionAvail();
- ImVec2 size = ImTrunc(size_arg);
- if (size.x <= 0.0f)
- size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too many issues)
- if (size.y <= 0.0f)
- size.y = ImMax(content_avail.y + size.y, 4.0f);
+ const ImVec2 size_avail = GetContentRegionAvail();
+ const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
+ const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
SetNextWindowSize(size);
// Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
@@ -7010,7 +7017,12 @@
const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
if (!g.LogEnabled && !nav_request)
if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
- window->HiddenFramesCanSkipItems = 1;
+ {
+ if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
+ window->HiddenFramesCannotSkipItems = 1;
+ else
+ window->HiddenFramesCanSkipItems = 1;
+ }
// Hide along with parent or if parent is collapsed
if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
@@ -7591,10 +7603,14 @@
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
+ // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
+ if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
+ window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
+ if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
+ window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
+
// Set
ImVec2 old_size = window->SizeFull;
- window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
- window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
if (size.x <= 0.0f)
window->AutoFitOnlyGrows = false;
else
diff --git a/imgui.h b/imgui.h
index 6c9e63f..06ce5ae 100644
--- a/imgui.h
+++ b/imgui.h
@@ -338,7 +338,12 @@
// Child Windows
// - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child.
- // - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400).
+ // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)):
+ // == 0.0f: use remaining parent window size for this axis.
+ // > 0.0f: use specified size for this axis.
+ // < 0.0f: right/bottom-align to specified distance from available content boundaries.
+ // - Specifying ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY makes the sizing automatic based on child contents.
+ // Combining both ImGuiChildFlags_AutoResizeX _and_ ImGuiChildFlags_AutoResizeY defeats purpose of a scrolling region and is NOT recommended.
// - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting
// anything to the window. Always call a matching EndChild() for each BeginChild() call, regardless of its return value.
// [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions
@@ -1015,6 +1020,13 @@
// Flags for ImGui::BeginChild()
// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Border to be backward compatible with old API using 'bool border'.
+// About using AutoResizeX/AutoResizeY flags:
+// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints").
+// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.
+// - This allows BeginChild() to return false when not within boundaries (e.g. when scrolling), which is more optimal. BUT it won't update its auto-size while clipped.
+// While not perfect, it is a better default behavior as the always-on performance gain is more valuable than the occasional "resizing after becoming visible again" glitch.
+// - You may also use ImGuiChildFlags_AlwaysAutoResize to force an update even when child window is not in view.
+// HOWEVER PLEASE UNDERSTAND THAT DOING SO WILL PREVENT BeginChild() FROM EVER RETURNING FALSE, disabling benefits of coarse clipping.
enum ImGuiChildFlags_
{
ImGuiChildFlags_None = 0,
@@ -1022,6 +1034,9 @@
ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1, // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense)
ImGuiChildFlags_ResizeX = 1 << 2, // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags)
ImGuiChildFlags_ResizeY = 1 << 3, // Allow resize from bottom border (layout direction). "
+ ImGuiChildFlags_AutoResizeX = 1 << 4, // Enable auto-resizing width. Read "IMPORTANT: Size measurement" details above.
+ ImGuiChildFlags_AutoResizeY = 1 << 5, // Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above.
+ ImGuiChildFlags_AlwaysAutoResize = 1 << 6, // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED.
ImGuiChildFlags_FrameStyle = 1 << 7, // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.
};
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 6fffeac..457c61f 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -2779,6 +2779,23 @@
ImGui::EndChild();
}
+ // Child 4: auto-resizing height with a limit
+ ImGui::SeparatorText("Auto-resize with constraints");
+ {
+ static int draw_lines = 3;
+ static int max_height_in_lines = 10;
+ ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
+ ImGui::DragInt("Lines Count", &draw_lines, 0.2f);
+ ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
+ ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f);
+
+ ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));
+ if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY))
+ for (int n = 0; n < draw_lines; n++)
+ ImGui::Text("Line %04d", n);
+ ImGui::EndChild();
+ }
+
ImGui::SeparatorText("Misc/Advanced");
// Demonstrate a few extra things
@@ -6605,6 +6622,7 @@
"Left-click on color square to open color picker,\n"
"Right-click to open edit options menu.");
+ ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));
ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened);
ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
for (int i = 0; i < ImGuiCol_COUNT; i++)