Skip to content

Commit f95c77e

Browse files
committed
Window rectangles: Changed WorkRect to cover the whole region including scrolling (toward obsolete ContentsRegionRect) + using full WindowPadding*1 padding.
Tweaked InnerClipRect. TreeNode, CollapsingHeader: Fixed highlight frame not covering horizontal area fully when using horizontal scrolling. (ocornut#2211, ocornut#2579) TabBar: Fixed BeginTabBar() within a window with horizontal scrolling from creating a feedback loop with the horizontal contents size. Columns: Fixed Columns() within a window with horizontal scrolling from not covering the full horizontal area (previously only worked with an explicit contents size). (ocornut#125) Demo: Added demo code to test contentsrect/workrect
1 parent a0994d7 commit f95c77e

File tree

5 files changed

+140
-35
lines changed

5 files changed

+140
-35
lines changed

docs/CHANGELOG.txt

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ Other Changes:
6464
frame as clearing the focus. This was in most noticeable in back-ends such as Glfw and SDL which
6565
emits key release events when focusing another viewport, leading to Alt+clicking on void on another
6666
viewport triggering the issue. (#2609)
67+
- TreeNode, CollapsingHeader: Fixed highlight frame not covering horizontal area fully when using
68+
horizontal scrolling. (#2211, #2579)
69+
- TabBar: Fixed BeginTabBar() within a window with horizontal scrolling from creating a feedback
70+
loop with the horizontal contents size.
71+
- Columns: Fixed Columns() within a window with horizontal scrolling from not covering the full
72+
horizontal area (previously only worked with an explicit contents size). (#125)
6773
- Style: Added style.WindowMenuButtonPosition (left/right, defaults to ImGuiDir_Left) to move the
6874
collapsing/docking button to the other side of the title bar.
6975
- Style: Made window close button cross slightly smaller.

imgui.cpp

+25-19
Original file line numberDiff line numberDiff line change
@@ -4999,8 +4999,6 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
49994999
}
50005000
}
50015001

5002-
// Draw background and borders
5003-
// Draw and handle scrollbars
50045002
void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
50055003
{
50065004
ImGuiContext& g = *GImGui;
@@ -5511,7 +5509,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
55115509
// - Begin() initial clipping rect for drawing window background and borders.
55125510
// - Begin() clipping whole child
55135511
ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5514-
window->OuterRectClipped = window->Rect();
5512+
ImRect outer_rect = window->Rect();
5513+
window->OuterRectClipped = outer_rect;
55155514
window->OuterRectClipped.ClipWith(host_rect);
55165515

55175516
// Inner rectangle
@@ -5525,23 +5524,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
55255524
window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
55265525
window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
55275526

5528-
// Work rectangle.
5529-
// Affected by window padding and border size. Used by:
5530-
// - Columns() for right-most edge
5531-
// - BeginTabBar() for right-most edge
5532-
window->WorkRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x * 0.5f - window->WindowBorderSize)));
5533-
window->WorkRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y);
5534-
window->WorkRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x * 0.5f - window->WindowBorderSize)));
5535-
window->WorkRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y);
5536-
55375527
// Inner clipping rectangle.
55385528
// Will extend a little bit outside the normal work region.
55395529
// This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
55405530
// Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
55415531
// Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
55425532
// Affected by window/frame border size. Used by:
55435533
// - Begin() initial clip rect
5544-
window->InnerClipRect = window->WorkRect;
5534+
float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5535+
window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5536+
window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5537+
window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.y * 0.5f), window->WindowBorderSize));
5538+
window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
55455539
window->InnerClipRect.ClipWithFull(host_rect);
55465540

55475541
// DRAWING
@@ -5589,12 +5583,25 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
55895583

55905584
// UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
55915585

5586+
// Work rectangle.
5587+
// Affected by window padding and border size. Used by:
5588+
// - Columns() for right-most edge
5589+
// - TreeNode(), CollapsingHeader() for right-most edge
5590+
// - BeginTabBar() for right-most edge
5591+
const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
5592+
const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
5593+
const float work_rect_size_x = (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : ImMax(allow_scrollbar_x ? window->SizeContents.x : 0.0f, window->InnerRect.GetWidth() - window->WindowPadding.x * 2.0f));
5594+
const float work_rect_size_y = (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : ImMax(allow_scrollbar_y ? window->SizeContents.y : 0.0f, window->InnerRect.GetHeight() - window->WindowPadding.y * 2.0f));
5595+
window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
5596+
window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
5597+
window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
5598+
window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
5599+
55925600
// [LEGACY] Contents Region
5593-
// FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5601+
// FIXME-OBSOLETE: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
55945602
// NB: WindowBorderSize is included in WindowPadding _and_ ScrollbarSizes so we need to cancel one out when we have both.
55955603
// Used by:
5596-
// - Mouse wheel scrolling
5597-
// - ... (many things)
5604+
// - Mouse wheel scrolling + many other things
55985605
window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
55995606
window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
56005607
window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x + ImMin(window->ScrollbarSizes.x, window->WindowBorderSize)));
@@ -8719,9 +8726,8 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlag
87198726
window->DC.CurrentColumns = columns;
87208727

87218728
// Set state for first column
8722-
const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->WorkRect.Max.x - window->Pos.x);
8723-
columns->OffMinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
8724-
columns->OffMaxX = ImMax(content_region_width - window->Scroll.x, columns->OffMinX + 1.0f);
8729+
columns->OffMinX = window->DC.Indent.x - g.Style.ItemSpacing.x;
8730+
columns->OffMaxX = ImMax(window->WorkRect.Max.x - window->Pos.x, columns->OffMinX + 1.0f);
87258731
columns->HostCursorPosY = window->DC.CursorPos.y;
87268732
columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x;
87278733
columns->HostClipRect = window->ClipRect;

imgui_demo.cpp

+89-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,18 @@
1717
// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls, so it is
1818
// essentially like a global variable but declared inside the scope of the function. We do this as a way to gather code and data
1919
// in the same place, to make the demo source code faster to read, faster to write, and smaller in size.
20-
// It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be reentrant
21-
// or used in threads. This might be a pattern you will want to use in your code, but most of the real data you would be editing is
22-
// likely going to be stored outside your functions.
20+
// It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be
21+
// reentrant or used in multiple threads. This might be a pattern you will want to use in your code, but most of the real data
22+
// you would be editing is likely going to be stored outside your functions.
23+
24+
// The Demo code is this file is designed to be easy to copy-and-paste in into your application!
25+
// Because of this:
26+
// - We never omit the ImGui:: namespace when calling functions, even though most of our code is already in the same namespace.
27+
// - We try to declare static variables in the local scope, as close as possible to the code using them.
28+
// - We never use any of the helpers/facilities used internally by dear imgui, unless it has been exposed in the public API (imgui.h).
29+
// - We never use maths operators on ImVec2/ImVec4. For other imgui sources files, they are provided by imgui_internal.h w/ IMGUI_DEFINE_MATH_OPERATORS,
30+
// for your own sources file they are optional and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.
31+
// Because we don't want to assume anything about your support of maths operators, we don't use them in imgui_demo.cpp.
2332

2433
/*
2534
@@ -2135,6 +2144,83 @@ static void ShowDemoWindowLayout()
21352144
ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);
21362145
ImGui::EndChild();
21372146
}
2147+
ImGui::Spacing();
2148+
2149+
static bool show_horizontal_contents_size_demo_window = false;
2150+
ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window);
2151+
2152+
if (show_horizontal_contents_size_demo_window)
2153+
{
2154+
static bool show_h_scrollbar = true;
2155+
static bool show_button = true;
2156+
static bool show_tree_nodes = true;
2157+
static bool show_columns = true;
2158+
static bool show_tab_bar = true;
2159+
static bool explicit_content_size = false;
2160+
static float contents_size_x = 300.0f;
2161+
if (explicit_content_size)
2162+
ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));
2163+
ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);
2164+
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0));
2165+
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0));
2166+
HelpMarker("Test of different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles.");
2167+
ImGui::Checkbox("H-scrollbar", &show_h_scrollbar);
2168+
ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten)
2169+
ImGui::Checkbox("Tree nodes", &show_tree_nodes); // Will grow contents size and display highlight over full width
2170+
ImGui::Checkbox("Columns", &show_columns); // Will use contents size
2171+
ImGui::Checkbox("Tab bar", &show_tab_bar); // Will use contents size
2172+
ImGui::Checkbox("Explicit content size", &explicit_content_size);
2173+
if (explicit_content_size)
2174+
{
2175+
ImGui::SameLine();
2176+
ImGui::SetNextItemWidth(100);
2177+
ImGui::DragFloat("##csx", &contents_size_x);
2178+
ImVec2 p = ImGui::GetCursorScreenPos();
2179+
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);
2180+
ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE);
2181+
ImGui::Dummy(ImVec2(0, 10));
2182+
}
2183+
ImGui::PopStyleVar(2);
2184+
ImGui::Separator();
2185+
if (show_button)
2186+
{
2187+
ImGui::Button("this is a 300-wide button", ImVec2(300, 0));
2188+
}
2189+
if (show_tree_nodes)
2190+
{
2191+
bool open = true;
2192+
if (ImGui::TreeNode("this is a tree node"))
2193+
{
2194+
if (ImGui::TreeNode("another one of those tree node..."))
2195+
{
2196+
ImGui::Text("Some tree contents");
2197+
ImGui::TreePop();
2198+
}
2199+
ImGui::TreePop();
2200+
}
2201+
ImGui::CollapsingHeader("CollapsingHeader", &open);
2202+
}
2203+
if (show_columns)
2204+
{
2205+
ImGui::Columns(4);
2206+
for (int n = 0; n < 4; n++)
2207+
{
2208+
ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
2209+
ImGui::NextColumn();
2210+
}
2211+
ImGui::Columns(1);
2212+
}
2213+
if (show_tab_bar && ImGui::BeginTabBar("Hello"))
2214+
{
2215+
if (ImGui::BeginTabItem("OneOneOne")) { ImGui::EndTabItem(); }
2216+
if (ImGui::BeginTabItem("TwoTwoTwo")) { ImGui::EndTabItem(); }
2217+
if (ImGui::BeginTabItem("ThreeThreeThree")) { ImGui::EndTabItem(); }
2218+
if (ImGui::BeginTabItem("FourFourFour")) { ImGui::EndTabItem(); }
2219+
ImGui::EndTabBar();
2220+
}
2221+
ImGui::End();
2222+
}
2223+
21382224
ImGui::TreePop();
21392225
}
21402226

imgui_internal.h

+10-6
Original file line numberDiff line numberDiff line change
@@ -1294,12 +1294,16 @@ struct IMGUI_API ImGuiWindow
12941294

12951295
ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name.
12961296
ImVector<ImGuiID> IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack
1297-
ImRect ClipRect; // Current clipping rectangle. = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2.
1298-
ImRect OuterRectClipped; // == WindowRect just after setup in Begin(). == window->Rect() for root window.
1299-
ImRect InnerRect; // Inner rectangle
1300-
ImRect InnerClipRect; // == InnerRect minus WindowPadding.x, clipped within viewport or parent clip rect.
1301-
ImRect WorkRect; // == InnerRect minus WindowPadding.x
1302-
ImRect ContentsRegionRect; // FIXME: This is currently confusing/misleading. Maximum visible content position ~~ Pos + (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis
1297+
1298+
// The best way to understand what those rectangles are is to use the 'Metrics -> Tools -> Show windows rectangles' viewer.
1299+
// The main 'OuterRect', omitted as a field, is window->Rect().
1300+
ImRect OuterRectClipped; // == Window->Rect() just after setup in Begin(). == window->Rect() for root window.
1301+
ImRect InnerRect; // Inner rectangle (omit title bar, menu bar)
1302+
ImRect InnerClipRect; // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect.
1303+
ImRect WorkRect; // Cover the whole scrolling region, shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentsRegionRect over time (from 1.71+ onward).
1304+
ImRect ClipRect; // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back().
1305+
ImRect ContentsRegionRect; // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on.
1306+
13031307
int LastFrameActive; // Last frame number the window was Active.
13041308
float ItemWidthDefault;
13051309
ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items

imgui_widgets.cpp

+10-7
Original file line numberDiff line numberDiff line change
@@ -5127,12 +5127,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
51275127
// We vertically grow up to current line height up the typical widget height.
51285128
const float text_base_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it
51295129
const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
5130-
ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(GetContentRegionMaxAbs().x, window->DC.CursorPos.y + frame_height));
5130+
ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->WorkRect.Max.x, window->DC.CursorPos.y + frame_height));
51315131
if (display_frame)
51325132
{
51335133
// Framed header expand a little outside the default padding
5134-
frame_bb.Min.x -= (float)(int)(window->WindowPadding.x * 0.5f) - 1;
5135-
frame_bb.Max.x += (float)(int)(window->WindowPadding.x * 0.5f) - 1;
5134+
frame_bb.Min.x -= (float)(int)(window->WindowPadding.x * 0.5f - 1.0f);
5135+
frame_bb.Max.x += (float)(int)(window->WindowPadding.x * 0.5f - 1.0f);
51365136
}
51375137

51385138
const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing
@@ -6324,15 +6324,15 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
63246324
tab_bar->FramePadding = g.Style.FramePadding;
63256325

63266326
// Layout
6327-
ItemSize(ImVec2(tab_bar->OffsetMax, tab_bar->BarRect.GetHeight()));
6327+
ItemSize(ImVec2(0.0f /*tab_bar->OffsetMax*/, tab_bar->BarRect.GetHeight())); // Don't feed width back
63286328
window->DC.CursorPos.x = tab_bar->BarRect.Min.x;
63296329

63306330
// Draw separator
63316331
const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_Tab);
63326332
const float y = tab_bar->BarRect.Max.y - 1.0f;
63336333
{
6334-
const float separator_min_x = tab_bar->BarRect.Min.x - window->WindowPadding.x;
6335-
const float separator_max_x = tab_bar->BarRect.Max.x + window->WindowPadding.x;
6334+
const float separator_min_x = tab_bar->BarRect.Min.x - ImFloor(window->WindowPadding.x * 0.5f);
6335+
const float separator_max_x = tab_bar->BarRect.Max.x + ImFloor(window->WindowPadding.x * 0.5f);
63366336
window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f);
63376337
}
63386338
return true;
@@ -6874,7 +6874,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
68746874
if (want_clip_rect)
68756875
PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x, bb.Max.y), true);
68766876

6877-
ItemSize(bb, style.FramePadding.y);
6877+
ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
6878+
ItemSize(bb.GetSize(), style.FramePadding.y);
6879+
window->DC.CursorMaxPos = backup_cursor_max_pos;
6880+
68786881
if (!ItemAdd(bb, id))
68796882
{
68806883
if (want_clip_rect)

0 commit comments

Comments
 (0)