diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 823892d7..a32ce3ca 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -94,6 +94,21 @@ Other changes: 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 @@ Other changes: - 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 58c21b31..3f32708a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5444,19 +5444,29 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I 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 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I // 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 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) 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 @@ void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond con 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 6c9e63fe..06ce5ae1 100644 --- a/imgui.h +++ b/imgui.h @@ -338,7 +338,12 @@ namespace ImGui // 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 @@ enum ImGuiWindowFlags_ // 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 @@ enum ImGuiChildFlags_ 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 6fffeac2..457c61fb 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2779,6 +2779,23 @@ static void ShowDemoWindowLayout() 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 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) "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++)