From be374fa598aae658ea43f843a6d821b2957d4a83 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jun 2026 17:32:36 +0200 Subject: [PATCH 1/6] TreeNode: fixed nav cursor rendering with rounding even though tree nodes don't have it. (#7589) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 06dd0b1ab..a160931c3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,6 +90,8 @@ Other Changes: - Nav: - Fixed context menu activation with gamepad erroneously testing for _NavEnableKeyboard instead of _NavEnableGamepad. (#9454, #8803, #9270) [@Clownacy] +- TreeNode: + - Fixed nav cursor rendering with rounding even though tree nodes don't have it. (#7589) - DrawList: - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) - Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText() diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7b8c4e9ce..3fb634be5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7116,9 +7116,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (hovered || selected) { const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false, 0.0f); } - RenderNavCursor(frame_bb, id, nav_render_cursor_flags); + RenderNavCursor(frame_bb, id, nav_render_cursor_flags | ImGuiNavRenderCursorFlags_NoRounding); if (span_all_columns && !span_all_columns_label) TablePopBackgroundChannel(); if (flags & ImGuiTreeNodeFlags_Bullet) From 26432cfdc6c144b0ce5cf82a04afd6c359059517 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jun 2026 17:56:54 +0200 Subject: [PATCH 2/6] Style, Nav: scale the NavCursor border thickness when using large values with `ScallAllSizes()`. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a160931c3..ab70c87fd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -92,6 +92,8 @@ Other Changes: instead of _NavEnableGamepad. (#9454, #8803, #9270) [@Clownacy] - TreeNode: - Fixed nav cursor rendering with rounding even though tree nodes don't have it. (#7589) +- Style: + - Scale the NavCursor border thickness when using large values with `ScallAllSizes()`. - DrawList: - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) - Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText() diff --git a/imgui.cpp b/imgui.cpp index c45606863..93d188782 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4045,14 +4045,15 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; ImRect display_rect = bb; display_rect.ClipWith(window->ClipRect); - const float thickness = 2.0f; + const float scale_factor = GetScale(); // FIXME-DPI + const float thickness = (float)(int)ImMax(2.0f, 1.5f * scale_factor); if (flags & ImGuiNavRenderCursorFlags_Compact) { window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, thickness); } else { - const float distance = 3.0f + thickness * 0.5f; + const float distance = (float)(int)(3.0f + thickness * 0.5f); display_rect.Expand(ImVec2(distance, distance)); bool fully_visible = window->ClipRect.Contains(display_rect); if (!fully_visible) From 0e355af09e8dd949c12f9069090c9811d6b3fb2e Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Jun 2026 18:33:43 +0200 Subject: [PATCH 3/6] RenderNavCursor: take rounding as input. Removed ImGuiNavRenderCursorFlags_NoRounding. Toward #7589 --- imgui.cpp | 10 ++++++++-- imgui_internal.h | 4 ++-- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 6 +++--- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 93d188782..c9b23bc4f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4026,7 +4026,7 @@ void ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float roundi RenderRectFilledInRangeH(window->DrawList, bb, col, bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding); } -void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags) +void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags, float rounding) { ImGuiContext& g = *GImGui; if (id != g.NavId) @@ -4042,7 +4042,13 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl if (window->DC.NavHideHighlightOneFrame) return; - float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (rounding < 0.0f && (flags & ImGuiNavRenderCursorFlags_NoRounding)) + rounding = 0.0f; +#endif + if (rounding < 0.0f) + rounding = g.Style.FrameRounding; + ImRect display_rect = bb; display_rect.ClipWith(window->ClipRect); const float scale_factor = GetScale(); // FIXME-DPI diff --git a/imgui_internal.h b/imgui_internal.h index 0bec47037..df7ea651e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1728,8 +1728,8 @@ enum ImGuiNavRenderCursorFlags_ ImGuiNavRenderCursorFlags_None = 0, ImGuiNavRenderCursorFlags_Compact = 1 << 1, // Compact highlight, no padding/distance from focused item ImGuiNavRenderCursorFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) even when g.NavCursorVisible == false, aka even when using the mouse. - ImGuiNavRenderCursorFlags_NoRounding = 1 << 3, #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiNavRenderCursorFlags_NoRounding = 1 << 3, ImGuiNavHighlightFlags_None = ImGuiNavRenderCursorFlags_None, // Renamed in 1.91.4 ImGuiNavHighlightFlags_Compact = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.91.4 ImGuiNavHighlightFlags_AlwaysDraw = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4 @@ -3679,7 +3679,7 @@ namespace ImGui IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); - IMGUI_API void RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None); // Navigation highlight + IMGUI_API void RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None, float rounding = -1.0f); // Navigation highlight #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4 #endif diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 128cd0136..d59658942 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3395,7 +3395,7 @@ void ImGui::TableHeader(const char* label) if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); } - RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding); + RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact, 0.0f); if (held) table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3fb634be5..045e8096d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7118,7 +7118,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false, 0.0f); } - RenderNavCursor(frame_bb, id, nav_render_cursor_flags | ImGuiNavRenderCursorFlags_NoRounding); + RenderNavCursor(frame_bb, id, nav_render_cursor_flags, 0.0f); if (span_all_columns && !span_all_columns_label) TablePopBackgroundChannel(); if (flags & ImGuiTreeNodeFlags_Bullet) @@ -7502,10 +7502,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } if (g.NavId == id) { - ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding; + ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact; if (is_multi_select) nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle - RenderNavCursor(bb, id, nav_render_cursor_flags); + RenderNavCursor(bb, id, nav_render_cursor_flags, 0.0f); } } From 1c1241136adaf6ba1d6903bed9e8b4bd8ce9ae6a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jun 2026 11:22:09 +0200 Subject: [PATCH 4/6] Tables: fixed (harmless) flag clearing issue in TableReconcileColumns(). (#9108) Was actually harmless because IsNeedReconcileSrc never read again for this column. --- imgui_tables.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index d59658942..b71304f25 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1833,7 +1833,7 @@ void ImGui::TableReconcileColumns(ImGuiTable* table) ImGuiTableReconcileColumnData& reconcile_data = reconcile_requests[dst_idx]; ImGuiTableColumn& dst_column = dst_columns[reconcile_data.ColumnNewIdx]; IM_ASSERT(src_column.IsNeedReconcileSrc && dst_column.IsNeedReconcileDst); - dst_column.IsNeedReconcileSrc = dst_column.IsNeedReconcileDst = false; + src_column.IsNeedReconcileSrc = dst_column.IsNeedReconcileDst = false; reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)src_columns.index_from_ptr(&src_column); reconcile_data.ColumnOldData = src_column; } From ac6f9683b5419381bc7f4f02262464c095ee6ece Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jun 2026 11:41:24 +0200 Subject: [PATCH 5/6] Style: added MenuItemRounding, SelectableRounding, ImGuiStyleVar_MenuItemRounding, ImGuiStyleVar_SelectableRounding. (#7589, #9375, #9453) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 6 ++++++ imgui.h | 4 ++++ imgui_demo.cpp | 2 ++ imgui_widgets.cpp | 12 +++++++++--- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ab70c87fd..736a2b67c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -93,6 +93,10 @@ Other Changes: - TreeNode: - Fixed nav cursor rendering with rounding even though tree nodes don't have it. (#7589) - Style: + - Added style.MenuItemRounding, ImGuiStyleVar_MenuItemRounding. (#7589, #9375, #9453) + - Added style.SelectableRounding, ImGuiStyleVar_SelectableRounding. (#7589, #9375, #9453) + The use of this is discouraged because it can easily create problems rendering e.g. + contiguous selection. - Scale the NavCursor border thickness when using large values with `ScallAllSizes()`. - DrawList: - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) diff --git a/imgui.cpp b/imgui.cpp index c9b23bc4f..f6fb61dcd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1533,6 +1533,8 @@ ImGuiStyle::ImGuiStyle() TreeLinesFlags = ImGuiTreeNodeFlags_DrawLinesNone; TreeLinesSize = 1.0f; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines. TreeLinesRounding = 0.0f; // Radius of lines connecting child nodes to the vertical line. + MenuItemRounding = 0.0f; // Radius of MenuItem, BeginMenu rounding. + SelectableRounding = 0.0f; // Radius of selectable rounding. MODIFYING THIS IS DISCOURAGED. CONTIGUOUS SELECTIONS WILL NOT LOOK RIGHT. (#7589) DragDropTargetRounding = 0.0f; // Radius of the drag and drop target frame. DragDropTargetBorderSize = 2.0f; // Thickness of the drag and drop target border. DragDropTargetPadding = 3.0f; // Size to expand the drag and drop target from actual target item size. @@ -1612,6 +1614,8 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor); TreeLinesSize = ImTrunc(TreeLinesSize * scale_factor); TreeLinesRounding = ImTrunc(TreeLinesRounding * scale_factor); + MenuItemRounding = ImTrunc(MenuItemRounding * scale_factor); + SelectableRounding = ImTrunc(SelectableRounding * scale_factor); DragDropTargetRounding = ImTrunc(DragDropTargetRounding * scale_factor); DragDropTargetBorderSize = ImTrunc(DragDropTargetBorderSize * scale_factor); DragDropTargetPadding = ImTrunc(DragDropTargetPadding * scale_factor); @@ -3685,6 +3689,8 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] = { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)}, // ImGuiStyleVar_TreeLinesRounding + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, MenuItemRounding)}, // ImGuiStyleVar_MenuItemRounding + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableRounding)}, // ImGuiStyleVar_SelectableRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DragDropTargetRounding)}, // ImGuiStyleVar_DragDropTargetRounding { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign diff --git a/imgui.h b/imgui.h index 720e3c8cd..14c695b9f 100644 --- a/imgui.h +++ b/imgui.h @@ -1856,6 +1856,8 @@ enum ImGuiStyleVar_ ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign ImGuiStyleVar_TreeLinesSize, // float TreeLinesSize ImGuiStyleVar_TreeLinesRounding, // float TreeLinesRounding + ImGuiStyleVar_MenuItemRounding, // float MenuItemRounding + ImGuiStyleVar_SelectableRounding, // float SelectableRounding ImGuiStyleVar_DragDropTargetRounding, // float DragDropTargetRounding ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign @@ -2333,6 +2335,8 @@ struct ImGuiStyle ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes. float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines. float TreeLinesRounding; // Radius of lines connecting child nodes to the vertical line. + float MenuItemRounding; // Radius of MenuItem, BeginMenu rounding. + float SelectableRounding; // Radius of Selectable rounding. MODIFYING THIS IS DISCOURAGED. CONTIGUOUS SELECTIONS WILL NOT LOOK RIGHT. (#7589) float DragDropTargetRounding; // Radius of the drag and drop target frame. When <0.0f: use FrameRounding. float DragDropTargetBorderSize; // Thickness of the drag and drop target border. float DragDropTargetPadding; // Size to expand the drag and drop target from actual target item size. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e0bbd9af6..1bd300666 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8556,6 +8556,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); + SliderFloat("MenuItemRounding", &style.MenuItemRounding, 0.0f, 12.0f, "%.0f"); + // NB: SelectableRounding is intentionally NOT made visible here. We don't want to encourage people using that. SeparatorText("Scrollbar"); SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 045e8096d..cb9e97441 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7498,14 +7498,14 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { // Between 1.91.0 and 1.91.4 we made selected Selectable use an arbitrary lerp between _Header and _HeaderHovered. Removed that now. (#8106) ImU32 col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + RenderFrame(bb.Min, bb.Max, col, false, style.SelectableRounding); } if (g.NavId == id) { ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact; if (is_multi_select) nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle - RenderNavCursor(bb, id, nav_render_cursor_flags, 0.0f); + RenderNavCursor(bb, id, nav_render_cursor_flags, style.SelectableRounding); } } @@ -9350,7 +9350,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) return false; ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; + ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); @@ -9395,6 +9395,8 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool pressed; + const float backup_rounding = style.SelectableRounding; + style.SelectableRounding = style.MenuItemRounding; const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoAutoClosePopups | (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnClick; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -9430,6 +9432,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) RenderArrow(window->DrawList, ImVec2(text_pos.x + offsets->OffsetMark + extra_w + g.FontSize * 0.30f, text_pos.y), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); popup_pos = ImVec2(pos.x, text_pos.y - style.WindowPadding.y); } + style.SelectableRounding = backup_rounding; if (!enabled) EndDisabled(); @@ -9616,6 +9619,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut BeginDisabled(); // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. + const float backup_rounding = style.SelectableRounding; + style.SelectableRounding = style.MenuItemRounding; const ImGuiSelectableFlags selectable_flags = (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnRelease | (ImGuiSelectableFlags)ImGuiSelectableFlags_SetNavIdOnHover; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -9659,6 +9664,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } } + style.SelectableRounding = backup_rounding; // Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394) // Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here. From 60fbdbb8d8e6e720732ac0c819193f8aa4bf7861 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Jun 2026 14:03:35 +0200 Subject: [PATCH 6/6] Multi-Select: reworked ImGuiMultiSelectFlags_NoAutoSelect as it carried side-effects that were hardcoded/designed to use multi-selection on checkboxes. (#9391) --- docs/CHANGELOG.txt | 8 ++++++++ imgui.h | 1 + imgui_internal.h | 2 +- imgui_widgets.cpp | 10 +++++----- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 736a2b67c..c90713b14 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -77,6 +77,14 @@ Other Changes: - Headers: fixed label being clipped early to reserve space for a sort marker even when no sort marker is displayed. Auto-fitting a column still accounts for the possible marker, so that sorting after an auto-fit doesn't clip the label. +- Multi-Select + - Reworked ImGuiMultiSelectFlags_NoAutoSelect as it carried side-effects that + were hardcoded/designed to use multi-selection on checkboxes. (#9391) + Specifically removed those undocumented behaviors from _NoAutoSelect: + - Clicking a selected/checked item always unselect/uncheck. + - Shift+Click inverts targets value and copy to range. + - Shift+Keyboard copy selection source value to range. + Those behaviors are still happening on checkboxes used within multi-selection. - Fonts: - Added `IMGUI_DISABLE_DEFAULT_FONT_BITMAP`/`IMGUI_DISABLE_DEFAULT_FONT_VECTOR` to disable embedding either fonts separately. (#9407) diff --git a/imgui.h b/imgui.h index 14c695b9f..d498355f4 100644 --- a/imgui.h +++ b/imgui.h @@ -3056,6 +3056,7 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NavWrapX = 1 << 16, // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one. ImGuiMultiSelectFlags_NoSelectOnRightClick = 1 << 17, // Disable default right-click processing, which selects item on mouse down, and is designed for context-menus. ImGuiMultiSelectFlags_SelectOnMask_ = ImGuiMultiSelectFlags_SelectOnAuto | ImGuiMultiSelectFlags_SelectOnClickAlways | ImGuiMultiSelectFlags_SelectOnClickRelease, + ImGuiMultiSelectFlags_CheckboxMode_ = 1 << 20, // [Internal] // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/imgui_internal.h b/imgui_internal.h index df7ea651e..36b205d3c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3637,7 +3637,7 @@ namespace ImGui // Multi-Select API IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); - IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); + IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed, ImGuiMultiSelectFlags extra_flags = 0); IMGUI_API void MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected); IMGUI_API void MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item); inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cb9e97441..c8458eba1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1274,7 +1274,7 @@ bool ImGui::Checkbox(const char* label, bool* v) // Range-Selection/Multi-selection support (footer) if (is_multi_select) - MultiSelectItemFooter(id, &checked, &pressed); + MultiSelectItemFooter(id, &checked, &pressed, ImGuiMultiSelectFlags_CheckboxMode_); else if (pressed) checked = !checked; @@ -8307,7 +8307,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags // - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse. // - Record current selection state for RangeSrc // This is all rather complex, best to run and refer to "widgets_multiselect_xxx" tests in imgui_test_suite. -void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) +void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed, ImGuiMultiSelectFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -8327,7 +8327,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; - ImGuiMultiSelectFlags flags = ms->Flags; + ImGuiMultiSelectFlags flags = ms->Flags | extra_flags; const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0; bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; @@ -8481,7 +8481,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) storage->RangeSrcItem = item_data; - if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + if ((flags & ImGuiMultiSelectFlags_CheckboxMode_) == 0) { // Shift+Arrow always select // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected) @@ -8501,7 +8501,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) else { // Ctrl inverts selection, otherwise always select - if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + if ((flags & ImGuiMultiSelectFlags_CheckboxMode_) == 0) selected = is_ctrl ? !selected : true; else selected = !selected;