From 7ee4dbac898be10398e22e5915eb12af9d7acc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20=C4=90or=C4=91evi=C4=87?= <118117650+MilosLord@users.noreply.github.com> Date: Mon, 2 Nov 2026 14:27:23 +0100 Subject: [PATCH 1/6] imgui_freetype: fixed undefined float to integer conversion when de-multiplying a color glyph with a color above its alpha. (#9599) --- misc/freetype/imgui_freetype.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 975374554..d0d6b8950 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -295,7 +295,7 @@ static void ImGui_ImplFreeType_BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* d case FT_PIXEL_MODE_BGRA: { // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good. - #define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u) + #define DE_MULTIPLY(color, alpha) (ImU32)ImMin(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f, 255.0f) for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch) for (uint32_t x = 0; x < w; x++) { From c6affd114531786ed6615c6ef4036cbbed39808e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 2 Nov 2026 16:13:30 +0100 Subject: [PATCH 2/6] Tables: move code in BeginTableEx() so that RowPosY1 may be assigned before altering WorkRect with padding (subsequent commit). --- imgui_tables.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 483cf3004..f9fe1e487 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -518,21 +518,11 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f; table->OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table->CellPaddingX; - table->CurrentColumn = -1; - table->CurrentRow = -1; - table->RowBgColorCounter = 0; - table->LastRowFlags = ImGuiTableRowFlags_None; - table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; - if (inner_window == outer_window) - table->InnerClipRect.ShrinkClamp(border_size_outer.Min, border_size_outer.Max); - table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width - table->InnerClipRect.ClipWithFull(table->HostClipRect); - if ((flags & ImGuiTableFlags_NoHostExtendY) == 0) - table->InnerClipRect.Max.y = table->HostClipRect.Max.y; - table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() table->RowCellPaddingY = 0.0f; + table->RowBgColorCounter = 0; + table->LastRowFlags = ImGuiTableRowFlags_None; table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; table->IsUnfrozenRows = true; @@ -542,6 +532,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->AngledHeadersHeight = 0.0f; temp_data->AngledHeadersExtraWidth = 0.0f; + table->CurrentColumn = -1; + table->CurrentRow = -1; + table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; + if (inner_window == outer_window) + table->InnerClipRect.ShrinkClamp(border_size_outer.Min, border_size_outer.Max); + table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width + table->InnerClipRect.ClipWithFull(table->HostClipRect); + if ((flags & ImGuiTableFlags_NoHostExtendY) == 0) + table->InnerClipRect.Max.y = table->HostClipRect.Max.y; // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); From 852269e25f2e7b54469f74853405a8d438e14fa8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 2 Nov 2026 16:22:54 +0100 Subject: [PATCH 3/6] Tables: fixed Selectable/TreeNode highlight overlapping left/right-most edges when using a border + no scrolling. (#4678) --- imgui_internal.h | 1 + imgui_tables.cpp | 18 ++++++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 145f3f137..57851e7ee 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3262,6 +3262,7 @@ struct IMGUI_API ImGuiTableTempData int TableIndex; // Index in g.Tables.Buf[] pool float LastTimeActive; // Last timestamp this structure was used float AngledHeadersExtraWidth; // Used in EndTable() + ImRect BorderOuterSizes; ImVector AngledHeadersRequests; // Used in TableAngledHeadersRow() // FIXME: Single instance is enough? // Topology change diff --git a/imgui_tables.cpp b/imgui_tables.cpp index f9fe1e487..72202580e 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -490,15 +490,12 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with // different x/y values to BeginChild().) // - For non-scrolling window, this is applied to table->InnerClipRect below. - ImRect border_size_outer = - { - (flags & ImGuiTableFlags_BordersOuterV) ? border_size : 0.0f, - (flags & ImGuiTableFlags_BordersOuterH) ? border_size : 0.0f, - ((flags & ImGuiTableFlags_BordersOuterV) && ((inner_window == outer_window) || inner_window->DecoOuterSizeX2 == 0.0f)) ? border_size : 0.0f, - ((flags & ImGuiTableFlags_BordersOuterH) && ((inner_window == outer_window) || inner_window->DecoOuterSizeY2 == 0.0f)) ? border_size : 0.0f, - }; + temp_data->BorderOuterSizes.Min.x = (flags & ImGuiTableFlags_BordersOuterV) ? border_size : 0.0f; + temp_data->BorderOuterSizes.Min.y = (flags & ImGuiTableFlags_BordersOuterH) ? border_size : 0.0f; + temp_data->BorderOuterSizes.Max.x = ((flags & ImGuiTableFlags_BordersOuterV) && ((inner_window == outer_window) || inner_window->DecoOuterSizeX2 == 0.0f)) ? border_size : 0.0f; + temp_data->BorderOuterSizes.Max.y = ((flags & ImGuiTableFlags_BordersOuterH) && ((inner_window == outer_window) || inner_window->DecoOuterSizeY2 == 0.0f)) ? border_size : 0.0f; if (inner_window != outer_window) - table->HostClipRect.ShrinkClamp(border_size_outer.Min, border_size_outer.Max); + table->HostClipRect.ShrinkClamp(temp_data->BorderOuterSizes.Min, temp_data->BorderOuterSizes.Max); // Padding and Spacing // - None ........Content..... Pad .....Content........ @@ -536,7 +533,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->CurrentRow = -1; table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; if (inner_window == outer_window) - table->InnerClipRect.ShrinkClamp(border_size_outer.Min, border_size_outer.Max); + table->InnerClipRect.ShrinkClamp(temp_data->BorderOuterSizes.Min, temp_data->BorderOuterSizes.Max); table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width table->InnerClipRect.ClipWithFull(table->HostClipRect); if ((flags & ImGuiTableFlags_NoHostExtendY) == 0) @@ -1313,7 +1310,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->OuterRect.Max.x = table->WorkRect.Max.x = ImMin(table->OuterRect.Max.x, unused_x1 + border_size_outer_w); table->InnerClipRect.Max.x = ImClamp(table->OuterRect.Max.x - border_size_outer_w, table->InnerClipRect.Min.x, table->InnerClipRect.Max.x); } - table->InnerWindow->ParentWorkRect = table->WorkRect; + table->InnerWindow->ParentWorkRect = table->WorkRect; // Used by Selectable/TreeNode highlight + table->InnerWindow->ParentWorkRect.ShrinkClamp(temp_data->BorderOuterSizes.Min, temp_data->BorderOuterSizes.Max); table->BorderX1 = table->InnerClipRect.Min.x; table->BorderX2 = table->InnerClipRect.Max.x; From c5131aff7ea0ee4b932df5a2f08b5237b960a4c3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 2 Nov 2026 16:31:32 +0100 Subject: [PATCH 4/6] Tables: draw outer border before inner borders. --- imgui_tables.cpp | 54 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 72202580e..d56297a56 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2966,6 +2966,33 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const float border_size_inner = table->BorderSize; const float draw_y2_limit = table->OuterWindow->ClipRect.Max.y + (2.0f * ImMax(border_size_outer_h, inner_drawlist->_FringeScale)); + // Draw outer border + if (table->Flags & ImGuiTableFlags_BordersOuter) + { + // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call + // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their + // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part + // of it in inner window, and the part that's over scrollbars in the outer window..) + // Either solution currently won't allow us to use a larger border size: the border would clipped. + ImRect outer_border = table->OuterRect; + outer_border.Max.y = ImMin(outer_border.Max.y, draw_y2_limit); // Clamp but keep bottom border non-visible. + const ImU32 outer_col = table->BorderColorStrong; + if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) + { + inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, border_size_outer_w); + } + else if (table->Flags & ImGuiTableFlags_BordersOuterV) + { + inner_drawlist->AddLineV(outer_border.Min.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size_outer_w); + inner_drawlist->AddLineV(outer_border.Max.x - border_size_outer_w, outer_border.Min.y, outer_border.Max.y, outer_col, border_size_outer_w); + } + else if (table->Flags & ImGuiTableFlags_BordersOuterH) + { + inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size_outer_h); + inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y - border_size_outer_h, outer_col, border_size_outer_h); + } + } + // Draw inner border and resizing feedback ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? border_size_outer_h : 0.0f); @@ -3008,33 +3035,6 @@ void ImGui::TableDrawBorders(ImGuiTable* table) } } - // Draw outer border - // FIXME: could use AddRect or explicit VLine/HLine helper? - if (table->Flags & ImGuiTableFlags_BordersOuter) - { - // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call - // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their - // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part - // of it in inner window, and the part that's over scrollbars in the outer window..) - // Either solution currently won't allow us to use a larger border size: the border would clipped. - ImRect outer_border = table->OuterRect; - outer_border.Max.y = ImMin(outer_border.Max.y, draw_y2_limit); // Clamp but keep bottom border non-visible. - const ImU32 outer_col = table->BorderColorStrong; - if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) - { - inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, border_size_outer_w); - } - else if (table->Flags & ImGuiTableFlags_BordersOuterV) - { - inner_drawlist->AddLineV(outer_border.Min.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size_outer_w); - inner_drawlist->AddLineV(outer_border.Max.x - border_size_outer_w, outer_border.Min.y, outer_border.Max.y, outer_col, border_size_outer_w); - } - else if (table->Flags & ImGuiTableFlags_BordersOuterH) - { - inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size_outer_h); - inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y - border_size_outer_h, outer_col, border_size_outer_h); - } - } if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) { // Draw bottom-most row border between it and above outer border. From 67f442d5de5ace0e8531442e0cb5446137f01147 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 2 Nov 2026 17:41:04 +0100 Subject: [PATCH 5/6] Tables: workaround/hack for max width 1-off issue when right-most column is resizable. (#4678) Requires !Scroll && Resizable && (NoHostExtendX || RightMostColumnnIsFixed) to see a difference. --- imgui_tables.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index d56297a56..f7a39f689 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2415,7 +2415,7 @@ void ImGui::TableEndCell(ImGuiTable* table) //------------------------------------------------------------------------- // [SECTION] Tables: Columns width management //------------------------------------------------------------------------- -// - TableGetMaxColumnWidth() [Internal] +// - TableCalcMaxColumnWidth() [Internal] // - TableGetColumnWidthAuto() [Internal] // - TableSetColumnWidth() // - TableSetColumnWidthAutoSingle() [Internal] @@ -3012,14 +3012,21 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); - if (column->MaxX > table->InnerClipRect.Max.x && !is_resized) + + // FIXME-TABLE: 1-off display hack for right-most column when resizable (!Scroll && Resizable && (NoHostExtendX || RightMostIsFixed)). + // Fixing actual width would involve altering width_avail in layout and TableCalcMaxColumnWidth(), which would drop the property + // ColumnsGivenWidth == Outer Allocated Width. Worth reworking, but would only make sense if we accept that BorderSize affects layout. + float draw_x2 = column->MaxX; + if (draw_x2 >= table->InnerClipRect.Max.x && draw_x2 - 1.0f <= table->InnerClipRect.Max.x) + draw_x2 = table->InnerClipRect.Max.x; + if (draw_x2 >= table->InnerClipRect.Max.x && !is_resized && !is_hovered) continue; // Decide whether right-most column is visible if (column->NextEnabledColumn == -1 && !is_resizable) if ((table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame || (table->Flags & ImGuiTableFlags_NoHostExtendX)) continue; - if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size.. + if (draw_x2 <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size.. continue; // Draw in outer window so right-most column won't be clipped @@ -3031,7 +3038,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0) draw_y2 = draw_y2_body; if (draw_y2 > draw_y1) - inner_drawlist->AddLineV(column->MaxX, draw_y1, draw_y2, TableGetColumnBorderCol(table, order_n, column_n), border_size_inner); + inner_drawlist->AddLineV(draw_x2, draw_y1, draw_y2, TableGetColumnBorderCol(table, order_n, column_n), border_size_inner); } } From 9d0b72573a27d860280811d58ef9ed62f4d46c05 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 Nov 2026 20:03:57 +0100 Subject: [PATCH 6/6] Windows: using SetNextWindowPos() with a pivot while a window is hidden and measuring size will still apply temporary position until size is figured out. (#9568) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4ba908f4f..bbd5e0ba4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -72,6 +72,9 @@ Other Changes: shape and look of arrows. Made default arrow stroked. (#5233, #2431, #2235, #6730, #8243) Legacy arrow: size: (0.60f,0.70f), thickness: 0.0f New arrow: size: (0.72f,0.40f), thickness: 0.30f +- Windows: + - Using `SetNextWindowPos()` with a pivot while a window is hidden and measuring + size will still apply temporary position until size is figured out. (#9568) - TreeNode: - TreeNodeGetOpen(): added flags parameter to allow passing `ImGuiTreeNodeFlags_DefaultOpen` or `ImGuiTreeNodeFlags_Leaf`. (#9569, #9592, #3823, #9251) [@adalisk-emikhaylov, @Yaki-Blake] diff --git a/imgui.cpp b/imgui.cpp index 67edfa37b..4519c96fb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7994,9 +7994,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = parent_window->DC.CursorPos; } - const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); - if (window_pos_with_pivot) + if (window->SetWindowPosVal.x != FLT_MAX) + { + const ImVec2 backup_pos_val = window->SetWindowPosVal; SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) + if (window->HiddenFramesCannotSkipItems != 0) + window->SetWindowPosVal = backup_pos_val; // Restore set request while hidden, so an updated size will be accounted for. (#9568) + } else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) window->Pos = FindBestWindowPosForPopup(window); else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)