From 2c7ba21417dbc82e277e223e556e5a29a0674798 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 7 Nov 2017 11:37:38 +0100 Subject: [PATCH 01/10] Fixed auto-resize allocating too much space for scrollbar when SizeContents is bigger than maximum window size (fixes c0547d358d746699f8d46a4996e49fdac8c55748) (#1417) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 9c9e7b12..d762910a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4039,7 +4039,7 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window) if (size_auto_fit_after_constraint.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) size_auto_fit.y += style.ScrollbarSize; if (size_auto_fit_after_constraint.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar)) - size_auto_fit.x += style.ScrollbarSize * 2.0f; + size_auto_fit.x += style.ScrollbarSize; size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f); } return size_auto_fit; From 8e6adc78afe090fe012ec50625aad82666303f86 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 7 Nov 2017 11:38:14 +0100 Subject: [PATCH 02/10] Examples: Constrained Resize: Added more test cases (for #1417) --- imgui_demo.cpp | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0d6a4423..7f45c50e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2145,31 +2145,40 @@ static void ShowExampleAppConstrainedResize(bool* p_open) static void Step(ImGuiSizeConstraintCallbackData* data) { float step = (float)(int)(intptr_t)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); } }; + static bool auto_resize = false; static int type = 0; + static int display_lines = 10; if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Vertical only if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Horizontal only if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 - if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(300, 0), ImVec2(400, FLT_MAX)); // Width 300-400 - if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square - if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)100);// Fixed Step + if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width 400-500 + if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 500)); // Height 400-500 + if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square + if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)100);// Fixed Step - if (ImGui::Begin("Example: Constrained Resize", p_open)) + ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; + if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) { const char* desc[] = { "Resize vertical only", "Resize horizontal only", "Width > 100, Height > 100", - "Width 300-400", + "Width 400-500", + "Height 400-500", "Custom: Always Square", "Custom: Fixed Steps (100)", }; - ImGui::Combo("Constraint", &type, desc, IM_ARRAYSIZE(desc)); - if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200,200)); } ImGui::SameLine(); - if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500,500)); } ImGui::SameLine(); - if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800,200)); } - for (int i = 0; i < 10; i++) - ImGui::Text("Hello, sailor! Making this line long enough for the example."); + if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); + if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); + if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } + ImGui::PushItemWidth(200); + ImGui::Combo("Constraint", &type, desc, IM_ARRAYSIZE(desc)); + ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); + ImGui::PopItemWidth(); + ImGui::Checkbox("Auto-resize", &auto_resize); + for (int i = 0; i < display_lines; i++) + ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, ""); } ImGui::End(); } From 571b08f31528cf13fe6c8885a8dc13c174aeced3 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 7 Nov 2017 13:59:55 +0100 Subject: [PATCH 03/10] Internal: FindWindowByName() faster and doesn't touch every windows --- imgui.cpp | 10 +++------- imgui_internal.h | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d762910a..ce03aafb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3920,13 +3920,9 @@ static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, ImGuiWindow* ImGui::FindWindowByName(const char* name) { - // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block ImGuiContext& g = *GImGui; ImGuiID id = ImHash(name, 0); - for (int i = 0; i < g.Windows.Size; i++) - if (g.Windows[i]->ID == id) - return g.Windows[i]; - return NULL; + return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); } static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) @@ -3937,6 +3933,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow)); IM_PLACEMENT_NEW(window) ImGuiWindow(name); window->Flags = flags; + g.WindowsById.SetVoidPtr(window->ID, window); if (flags & ImGuiWindowFlags_NoSavedSettings) { @@ -5280,8 +5277,7 @@ bool ImGui::IsWindowAppearing() void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond) { - ImGuiWindow* window = FindWindowByName(name); - if (window) + if (ImGuiWindow* window = FindWindowByName(name)) SetWindowCollapsed(window, collapsed, cond); } diff --git a/imgui_internal.h b/imgui_internal.h index ccfc3bf4..9781f822 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -422,6 +422,7 @@ struct ImGuiContext ImVector Windows; ImVector WindowsSortBuffer; ImVector CurrentWindowStack; + ImGuiStorage WindowsById; ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* NavWindow; // Nav/focused window for navigation ImGuiWindow* HoveredWindow; // Will catch mouse inputs From 1870738880246c18c150d19dbac8a35426688997 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 7 Nov 2017 14:05:48 +0100 Subject: [PATCH 04/10] LowerBound() minor tweaks --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ce03aafb..69ee04d9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1385,10 +1385,10 @@ static ImVector::iterator LowerBound(ImVector::iterator first = data.begin(); ImVector::iterator last = data.end(); - int count = (int)(last - first); + size_t count = (size_t)(last - first); while (count > 0) { - int count2 = count / 2; + size_t count2 = count >> 1; ImVector::iterator mid = first + count2; if (mid->key < key) { From aae52522c3d6ebd35dedb0f62e8e23cdc5768c1d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 7 Nov 2017 13:11:45 +0100 Subject: [PATCH 05/10] Internals: Remove requirement to define IMGUI_DEFINE_PLACEMENT_NEW (#1103) --- imgui_internal.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 9781f822..a5d87722 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2,10 +2,9 @@ // (internals) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! -// Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) +// Set: // #define IMGUI_DEFINE_MATH_OPERATORS -// Define IM_PLACEMENT_NEW() macro helper. -// #define IMGUI_DEFINE_PLACEMENT_NEW +// To implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) #pragma once @@ -159,12 +158,10 @@ static inline float ImLinearSweep(float current, float target, float speed) // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. // Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. -#ifdef IMGUI_DEFINE_PLACEMENT_NEW struct ImPlacementNewDummy {}; inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; } inline void operator delete(void*, ImPlacementNewDummy, void*) {} #define IM_PLACEMENT_NEW(_PTR) new(ImPlacementNewDummy(), _PTR) -#endif //----------------------------------------------------------------------------- // Types From 41862b8c0e9f651a253de45c2b7e982c19c9ea9d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 7 Nov 2017 16:41:58 +0100 Subject: [PATCH 06/10] ButtonBehavior: Fixed ImGuiButtonFlags_NoHoldingActiveID from incorrectly setting ActiveIdClickOffset, which probably have no known effect, but it is more correct this way. (#1418) --- imgui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 69ee04d9..ba212d73 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5835,11 +5835,15 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveID) + { ClearActiveID(); + } else + { SetActiveID(id, window); // Hold on ID + g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; + } FocusWindow(window); - g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; } if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) { From 2ab27be3de2cb794f3be360f5031371460420527 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 7 Nov 2017 22:23:02 +0100 Subject: [PATCH 07/10] Child window with MenuBar use regular WindowPadding.y so layout look consistent in child or in a regular window. --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index ba212d73..d9ccc433 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4251,7 +4251,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects. - window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding; + window->WindowPadding = style.WindowPadding; + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) + window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); // Calculate auto-fit size, handle automatic resize const ImVec2 size_auto_fit = CalcSizeAutoFit(window); From 9ac8820ee2ad3b569b592519a50b6dad4607eb04 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 8 Nov 2017 22:32:22 +0100 Subject: [PATCH 08/10] Fixed non-pixel aligned bounding box of window resize grip, / which triumphally led to any re-arrangement of operations inside the resize grip code outputting non-exact size_target values which led to unstable window position because clamping code uses size in a subtraction, etc etc. Lovely how a whole system can be made to act weird with a single bad input. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index d9ccc433..0ded20c0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4386,7 +4386,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Manual resize // Using the FlattenChilds button flag, we make the resize button accessible even if we are hovering over a child window const ImVec2 br = window->Rect().GetBR(); - const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br); + const ImRect resize_rect(br - ImFloor(ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f)), br); const ImGuiID resize_id = window->GetID("#RESIZE"); bool hovered, held; ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds); From a4cc3d4637f7aa3aac7c730c6d21370c8737a76f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 8 Nov 2017 22:36:31 +0100 Subject: [PATCH 09/10] Minor tweaks/comments. Note that the reordering the one subtraction caused subtle havoc before the patch in 9ac8820ee2ad3b569b592519a50b6dad4607eb04. --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0ded20c0..d64ac6d3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4404,7 +4404,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else if (held) { // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - size_target = (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos; + size_target = (g.IO.MousePos - g.ActiveIdClickOffset - window->Pos) + resize_rect.GetSize(); } if (size_target.x != FLT_MAX && size_target.y != FLT_MAX) @@ -10456,8 +10456,8 @@ void ImGui::EndColumns() { float x = window->Pos.x + GetColumnOffset(i); const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i); - const float column_w = 4.0f; // Width for interaction - const ImRect column_rect(ImVec2(x - column_w, y1), ImVec2(x + column_w, y2)); + const float column_hw = 4.0f; // Half-width for interaction + const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2)); if (IsClippedEx(column_rect, column_id, false)) continue; @@ -10468,7 +10468,7 @@ void ImGui::EndColumns() if (hovered || held) g.MouseCursor = ImGuiMouseCursor_ResizeEW; if (held && g.ActiveIdIsJustActivated) - g.ActiveIdClickOffset.x -= column_w; // Store from center of column line (we used a 8 wide rect for columns clicking). This is used by GetDraggedColumnOffset(). + g.ActiveIdClickOffset.x -= column_hw; // Store from center of column line (we used a 8 wide rect for columns clicking). This is used by GetDraggedColumnOffset(). if (held) dragging_column = i; } From 2df8fa95dfe3463ece78c56b02dc815c3e71331a Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 8 Nov 2017 23:17:08 +0100 Subject: [PATCH 10/10] Fixed vertical scrollbar flickering/appearing, typically when manually resizing and using a pattern of filling available height (e.g. full sized BeginChild). THIS IS A GREAT FIX, this glitch was nasty and annoying (and yet somehow nobody reported it?). Hopefully haven't broken anything else... --- imgui.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d64ac6d3..35e30968 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4232,10 +4232,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // SIZE - // Save contents size from last frame for auto-fitting (unless explicitly specified) + // Update contents size from last frame for auto-fitting (unless explicitly specified) window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x)); window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y)); + // Update scrollbar status based on the Size that was effective during last frame (and not the upcoming Size which we are updating below), so that user code consuming exactly the available size won't trigger scrollbars when e.g. manually resizing. + if (!window->Collapsed) + { + window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar)); + window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); + if (window->ScrollbarX && !window->ScrollbarY) + window->ScrollbarY = (window->SizeContents.y > window->Size.y + style.ItemSpacing.y - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); + window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); + window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f; + } + // Hide popup/tooltip window when first appearing while we measure size (because we recycle them) if (window->HiddenFrames > 0) window->HiddenFrames--; @@ -4416,14 +4427,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) title_bar_rect = window->TitleBarRect(); } - // Scrollbars - window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar)); - window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); - if (window->ScrollbarX && !window->ScrollbarY) - window->ScrollbarY = (window->SizeContents.y > window->Size.y + style.ItemSpacing.y - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); - window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); - window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f; - // Window background, Default Alpha ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BotLeft|ImGuiCorner_BotRight);