From 60f8b0733cec95431891c8344914c3f348103747 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 5 Dec 2025 18:06:34 +0100 Subject: [PATCH 01/12] Rework color marker internals to facilitate arbitrary override using SetNextItemColorMarker(). Amend fa4b47c --- imgui.cpp | 9 +++------ imgui.h | 6 +----- imgui_internal.h | 17 ++++++++++------- imgui_widgets.cpp | 37 ++++++++++++++++++++----------------- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a321d357e..1e8eeae9b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3925,16 +3925,13 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) } } -// FIXME: Might move those to style if there is a real need. -static const ImU32 GColorMarkers[4] = { IM_COL32(240,20,20,255), IM_COL32(20,240,20,255), IM_COL32(20,20,240,255), IM_COL32(140,140,140,255) }; - -void ImGui::RenderColorComponentMarker(int component_idx, const ImRect& bb, float rounding) +void ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding) { - if (!(component_idx >= 0 && component_idx < 4) || (bb.Min.x + 1 >= bb.Max.x)) + if (bb.Min.x + 1 >= bb.Max.x) return; ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(GColorMarkers[component_idx]), bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding); + 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) diff --git a/imgui.h b/imgui.h index 8b359d57f..89dcc6713 100644 --- a/imgui.h +++ b/imgui.h @@ -1927,12 +1927,8 @@ enum ImGuiSliderFlags_ ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds. ImGuiSliderFlags_ClampZeroRange = 1 << 10, // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it. ImGuiSliderFlags_NoSpeedTweaks = 1 << 11, // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. - ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange, - - // Color Markers ImGuiSliderFlags_ColorMarkers = 1 << 12, // DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component. - ImGuiSliderFlags_ColorMarkersIndexShift_ = 13, // [Internal] DragScalar(), SliderScalar(): Pass ([0..3] << ImGuiSliderFlags_ColorMarkersIndexShift_) along with ImGuiSliderFlags_ColorMarkers to select an individual R/G/B/A color. - + ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange, ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from legacy API (obsoleted 2020-08) that has got miscast to this enum, and will trigger an assert if needed. }; diff --git a/imgui_internal.h b/imgui_internal.h index 849e48897..e61f830d5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1334,12 +1334,13 @@ struct ImGuiNextWindowData enum ImGuiNextItemDataFlags_ { - ImGuiNextItemDataFlags_None = 0, - ImGuiNextItemDataFlags_HasWidth = 1 << 0, - ImGuiNextItemDataFlags_HasOpen = 1 << 1, - ImGuiNextItemDataFlags_HasShortcut = 1 << 2, - ImGuiNextItemDataFlags_HasRefVal = 1 << 3, - ImGuiNextItemDataFlags_HasStorageID = 1 << 4, + ImGuiNextItemDataFlags_None = 0, + ImGuiNextItemDataFlags_HasWidth = 1 << 0, + ImGuiNextItemDataFlags_HasOpen = 1 << 1, + ImGuiNextItemDataFlags_HasShortcut = 1 << 2, + ImGuiNextItemDataFlags_HasRefVal = 1 << 3, + ImGuiNextItemDataFlags_HasStorageID = 1 << 4, + ImGuiNextItemDataFlags_HasColorMarker = 1 << 5, }; struct ImGuiNextItemData @@ -1357,6 +1358,7 @@ struct ImGuiNextItemData ImU8 OpenCond; // Set by SetNextItemOpen() ImGuiDataTypeStorage RefVal; // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal ImGuiID StorageId; // Set by SetNextItemStorageID() + ImU32 ColorMarker; // Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags_ColorMarkers. ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; } inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! @@ -3575,7 +3577,7 @@ namespace ImGui IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); - IMGUI_API void RenderColorComponentMarker(int component_idx, const ImRect& bb, float rounding); + 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 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -3664,6 +3666,7 @@ namespace ImGui IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags); + inline void SetNextItemColorMarker(ImU32 col) { ImGuiContext& g = *GImGui; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasColorMarker; g.NextItemData.ColorMarker = col; } // Plot IMGUI_API int PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 086a64c8d..6be8f7691 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2240,6 +2240,11 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void* // - RoundScalarWithFormat<>() //------------------------------------------------------------------------- +static const ImU32 GDefaultRgbaColorMarkers[4] = +{ + IM_COL32(240,20,20,255), IM_COL32(20,240,20,255), IM_COL32(20,20,240,255), IM_COL32(140,140,140,255) +}; + static const ImGuiDataTypeInfo GDataTypeInfo[] = { { sizeof(char), "S8", "%d", "%d" }, // ImGuiDataType_S8 @@ -2688,6 +2693,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); + const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0; const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); @@ -2757,8 +2763,8 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding); - if ((flags & ImGuiSliderFlags_ColorMarkers) && style.ColorMarkerSize > 0.0f) - RenderColorComponentMarker(flags >> ImGuiSliderFlags_ColorMarkersIndexShift_, frame_bb, style.FrameRounding); + if (color_marker != 0 && style.ColorMarkerSize > 0.0f) + RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding); RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding); // Drag behavior @@ -2797,12 +2803,9 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); - - ImGuiSliderFlags flags_for_component = flags; - if ((flags & ImGuiSliderFlags_ColorMarkers) && (flags & (0x03 << ImGuiSliderFlags_ColorMarkersIndexShift_)) == 0 && (i < 4)) - flags_for_component |= (i << ImGuiSliderFlags_ColorMarkersIndexShift_); - - value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags_for_component); + if (flags & ImGuiSliderFlags_ColorMarkers) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]); + value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags); PopID(); PopItemWidth(); p_data = (void*)((char*)p_data + type_size); @@ -3300,6 +3303,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); + const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0; const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); @@ -3351,8 +3355,8 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavCursor(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding); - if ((flags & ImGuiSliderFlags_ColorMarkers) && style.ColorMarkerSize > 0.0f) - RenderColorComponentMarker(flags >> ImGuiSliderFlags_ColorMarkersIndexShift_, frame_bb, style.FrameRounding); + if (color_marker != 0 && style.ColorMarkerSize > 0.0f) + RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding); RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding); // Slider behavior @@ -3397,12 +3401,9 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i PushID(i); if (i > 0) SameLine(0, g.Style.ItemInnerSpacing.x); - - ImGuiSliderFlags flags_for_component = flags; - if ((flags & ImGuiSliderFlags_ColorMarkers) && (flags & (0x03 << ImGuiSliderFlags_ColorMarkersIndexShift_ )) == 0 && (i < 4)) - flags_for_component |= (i << ImGuiSliderFlags_ColorMarkersIndexShift_); - - value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags_for_component); + if (flags & ImGuiSliderFlags_ColorMarkers) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]); + value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags); PopID(); PopItemWidth(); v = (void*)((char*)v + type_size); @@ -5818,6 +5819,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA }; const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; + const ImGuiSliderFlags drag_flags = draw_color_marker ? ImGuiSliderFlags_ColorMarkers : ImGuiSliderFlags_None; float prev_split = 0.0f; for (int n = 0; n < components; n++) @@ -5827,9 +5829,10 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag float next_split = IM_TRUNC(w_items * (n + 1) / components); SetNextItemWidth(ImMax(next_split - prev_split, 1.0f)); prev_split = next_split; + if (draw_color_marker) + SetNextItemColorMarker(GDefaultRgbaColorMarkers[n]); // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0. - ImGuiSliderFlags drag_flags = draw_color_marker ? (ImGuiSliderFlags_ColorMarkers | (n << ImGuiSliderFlags_ColorMarkersIndexShift_)) : ImGuiSliderFlags_None; if (flags & ImGuiColorEditFlags_Float) { value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n], drag_flags); From 7f78f5224318750393ce6235edf0b7b9b6133a21 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 5 Dec 2025 18:44:21 +0100 Subject: [PATCH 02/12] Demo: removed some hardcoded widths. --- imgui.cpp | 2 +- imgui_demo.cpp | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1e8eeae9b..a977c773c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15224,7 +15224,7 @@ void ImGui::LogButtons() const bool log_to_file = Button("Log To File"); SameLine(); const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); PushItemFlag(ImGuiItemFlags_NoTabStop, true); - SetNextItemWidth(80.0f); + SetNextItemWidth(CalcTextSize("999").x); SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); PopItemFlag(); PopID(); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9dd4c5591..8ace9ff6a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4582,16 +4582,14 @@ static void DemoWindowLayout() // Various static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f; - ImGui::PushItemWidth(80); + ImGui::PushItemWidth(ImGui::CalcTextSize("AAAAAAA").x); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; static int item = -1; ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine(); ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine(); ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f); - ImGui::PopItemWidth(); - ImGui::PushItemWidth(80); ImGui::Text("Lists:"); static int selection[4] = { 0, 1, 2, 3 }; for (int i = 0; i < 4; i++) @@ -5026,7 +5024,7 @@ static void DemoWindowLayout() if (explicit_content_size) { ImGui::SameLine(); - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::CalcTextSize("123456").x); ImGui::DragFloat("##csx", &contents_size_x); ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE); @@ -8504,7 +8502,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) } LogFinish(); } - SameLine(); SetNextItemWidth(120); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); + SameLine(); SetNextItemWidth(GetFontSize() * 10); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); SameLine(); Checkbox("Only Modified Colors", &output_only_modified); static ImGuiTextFilter filter; From a726bded11f46fc5ee116089dd2fe6f12735befb Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 5 Dec 2025 19:12:50 +0100 Subject: [PATCH 03/12] Fonts: ClearOutputData() doesn't need to clear FallbackChar, EllipsisChar. Fixed crash changing font loader. --- docs/CHANGELOG.txt | 13 ++++++++----- imgui_draw.cpp | 3 +-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 894baad88..c59efdcb5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,11 +43,14 @@ Breaking Changes: Other Changes: -- Fonts: fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which - did erroneously make a copy of the font data, essentially defeating the purpose - of this flag and wasting memory. - Undetected since July 2015 and now spotted by @TellowKrinkle, this is perhaps - the oldest bug in Dear ImGui history (albeit for a rarely used feature)! (#9086) +- Fonts: + - Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which + did erroneously make a copy of the font data, essentially defeating the purpose + of this flag and wasting memory. + Undetected since July 2015 and now spotted by @TellowKrinkle, this is perhaps + the oldest bug in Dear ImGui history (albeit for a rarely used feature)! (#9086) + - Fixed an issue related to EllipsisChar handling, while changing + font loader or font loader flags dynamically in Style->Fonts menus. - Textures: - Fixed a building issue when ImTextureID is defined as a struct. - Fixed displaying texture # in Metrics/Debugger window. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 88e897ac3..2dddaece7 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3230,7 +3230,6 @@ void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* void ImFontAtlas::RemoveFont(ImFont* font) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); - font->ClearOutputData(); ImFontAtlasFontDestroyOutput(this, font); for (ImFontConfig* src : font->Sources) @@ -5106,7 +5105,7 @@ void ImFont::ClearOutputData() { if (ImFontAtlas* atlas = OwnerAtlas) ImFontAtlasFontDiscardBakes(atlas, this, 0); - FallbackChar = EllipsisChar = 0; + //FallbackChar = EllipsisChar = 0; memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); LastBaked = NULL; } From cf64b7fa72776c434c7e39bc45f51ddf1af755a6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 8 Dec 2025 19:29:41 +0100 Subject: [PATCH 04/12] Tables: Fixed losing stored display order when reducing column count. (#9108, #4046) Amend f2df804fcc --- docs/CHANGELOG.txt | 2 ++ imgui.h | 4 ++-- imgui_internal.h | 1 + imgui_tables.cpp | 50 +++++++++++++++++++++++++++++++++++----------- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c59efdcb5..2353faddb 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -67,6 +67,8 @@ Other Changes: between hard minimum window size and table minimum size). - Fixed an issue where submitting non-integer row heights would eventually advance table parent layout by +0/+1 depending on its visibility. + - Fixed losing stored display order when reducing column count or when .ini + data has missing or duplicate values. (#9108, #4046) - ColorEdit: - Added R/G/B/A color markers next to each component (enabled by default). - Added ImGuiColorEditFlags_NoColorMarkers to disable them. diff --git a/imgui.h b/imgui.h index 89dcc6713..7f8b6f3ac 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.6 WIP" -#define IMGUI_VERSION_NUM 19253 +#define IMGUI_VERSION_NUM 19254 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -2019,7 +2019,7 @@ enum ImGuiTableFlags_ ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu. ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. - ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width and sort settings in the .ini file. + ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width, visibility and sort settings in the .ini file. ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). // Decorations ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) diff --git a/imgui_internal.h b/imgui_internal.h index e61f830d5..254665dee 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3514,6 +3514,7 @@ namespace ImGui IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } inline ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no) { return TableGetInstanceData(table, instance_no)->TableInstanceID; } + IMGUI_API void TableFixDisplayOrder(ImGuiTable* table); IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); IMGUI_API void TableSortSpecsBuild(ImGuiTable* table); IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 4aa868317..0ea14727e 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -566,7 +566,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG { // Attempt to preserve width on column count change (#4046) old_columns_to_preserve = table->Columns.Data; - old_columns_raw_data = table->RawData; + old_columns_raw_data = table->RawData; // Free at end of function table->RawData = NULL; } if (table->RawData == NULL) @@ -592,7 +592,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG ImGuiTableColumn* column = &table->Columns[n]; if (old_columns_to_preserve && n < old_columns_count) { - // FIXME: We don't attempt to preserve column order in this path. *column = old_columns_to_preserve[n]; } else @@ -602,8 +601,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG column->WidthAuto = width_auto; column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; + column->DisplayOrder = (ImGuiTableColumnIdx)n; } - column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; + table->DisplayOrderToIndex[n] = column->DisplayOrder; } } if (old_columns_raw_data) @@ -3792,7 +3792,6 @@ void ImGui::TableLoadSettings(ImGuiTable* table) // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); - ImU64 display_order_mask = 0; for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++) { int column_n = column_settings->Index; @@ -3809,24 +3808,51 @@ void ImGui::TableLoadSettings(ImGuiTable* table) } if (settings->SaveFlags & ImGuiTableFlags_Reorderable) column->DisplayOrder = column_settings->DisplayOrder; - display_order_mask |= (ImU64)1 << column->DisplayOrder; if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1) column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1; column->SortOrder = column_settings->SortOrder; column->SortDirection = column_settings->SortDirection; } - // Validate and fix invalid display order data - const ImU64 expected_display_order_mask = (settings->ColumnsCount == 64) ? ~0 : ((ImU64)1 << settings->ColumnsCount) - 1; - if (display_order_mask != expected_display_order_mask) - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->Columns[column_n].DisplayOrder = (ImGuiTableColumnIdx)column_n; - - // Rebuild index + // Fix display order and build index + if (settings->SaveFlags & ImGuiTableFlags_Reorderable) + TableFixDisplayOrder(table); for (int column_n = 0; column_n < table->ColumnsCount; column_n++) table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; } +struct ImGuiTableFixDisplayOrderColumnData +{ + ImGuiTableColumnIdx Idx; + ImGuiTable* Table; // This is unfortunate but we don't have userdata in qsort api. +}; + +// Sort by DisplayOrder and then Index +static int IMGUI_CDECL TableFixDisplayOrderComparer(const void* lhs, const void* rhs) +{ + const ImGuiTable* table = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Table; + const ImGuiTableColumnIdx lhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Idx; + const ImGuiTableColumnIdx rhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)rhs)->Idx; + const int order_delta = (table->Columns[lhs_idx].DisplayOrder - table->Columns[rhs_idx].DisplayOrder); + return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1; +} + +// Fix invalid display order data: compact values (0,1,3 -> 0,1,2); preserve relative order (0,3,1 -> 0,2,1); deduplicate (0,4,1,1 -> 0,3,1,2) +void ImGui::TableFixDisplayOrder(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + g.TempBuffer.reserve((int)(sizeof(ImGuiTableFixDisplayOrderColumnData) * table->ColumnsCount)); // FIXME: Maybe wrap those two lines as a helper. + ImGuiTableFixDisplayOrderColumnData* fdo_columns = (ImGuiTableFixDisplayOrderColumnData*)g.TempBuffer.Data; + for (int n = 0; n < table->ColumnsCount; n++) + { + fdo_columns[n].Idx = (ImGuiTableColumnIdx)n; + fdo_columns[n].Table = table; + } + ImQsort(fdo_columns, (size_t)table->ColumnsCount, sizeof(ImGuiTableFixDisplayOrderColumnData), TableFixDisplayOrderComparer); + for (int n = 0; n < table->ColumnsCount; n++) + table->Columns[fdo_columns[n].Idx].DisplayOrder = (ImGuiTableColumnIdx)n; +} + static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; From 9a4fd69f6dd6f9cdf89c744bb7a796bc9db0a0b2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Dec 2025 18:35:22 +0100 Subject: [PATCH 05/12] Backends: GLFW: avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. --- backends/imgui_impl_glfw.cpp | 17 ++++++++++++++--- docs/CHANGELOG.txt | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index ac176a080..d088e9693 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -29,6 +29,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-12-10: Avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. Lowers overhead for very high framerates (e.g. 10k+ FPS). // 2025-11-06: Lower minimum requirement to GLFW 3.0. Though a recent version e.g GLFW 3.4 is highly recommended. // 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. // 2025-09-15: Content Scales are always reported as 1.0 on Wayland. FramebufferScale are always reported as 1.0 on X11. (#8920, #8921) @@ -188,6 +189,7 @@ struct ImGui_ImplGlfw_Data GLFWwindow* MouseWindow; #if GLFW_HAS_CREATECURSOR GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + GLFWcursor* LastMouseCursor; #endif ImVec2 LastValidMousePos; bool IsWayland; @@ -849,15 +851,24 @@ static void ImGui_ImplGlfw_UpdateMouseCursor() GLFWwindow* window = bd->Window; if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + if (bd->LastMouseCursor != nullptr) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + bd->LastMouseCursor = nullptr; + } } else { // Show OS mouse cursor // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. #if GLFW_HAS_CREATECURSOR - glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); + GLFWcursor* cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]; + if (bd->LastMouseCursor != cursor) + { + glfwSetCursor(window, cursor); + bd->LastMouseCursor = cursor; + } #endif glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2353faddb..32008203d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -87,6 +87,8 @@ Other Changes: non-ASCII values to io.AddInputCharacter(). (#9099) - Debug Log: can output to debugger on Windows. (#5855) - Backends: + - GLFW: Avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. + Lowers overhead for very high framerates (e.g. 10k+ FPS). [@maxliani] - SDL_GPU3: macOS version can use MSL shaders in order to support macOS 10.14+ (vs Metallib shaders requiring macOS 14+). Requires application calling SDL_CreateGPUDevice() with SDL_GPU_SHADERFORMAT_MSL. (#9076) [@Niminem] From a07b2828ce89c2e10cfcf26450c0b0c3cc4a208f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Dec 2025 18:57:02 +0100 Subject: [PATCH 06/12] Backends: GLFW: add IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND. (#9109, #9116) cc #8884, #8474, #8289 --- backends/imgui_impl_glfw.cpp | 22 ++++++++++++++-------- backends/imgui_impl_glfw.h | 1 + docs/CHANGELOG.txt | 2 ++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index d088e9693..58cb6f1b1 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -29,6 +29,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-12-12: Added IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND to forcefully disable either. // 2025-12-10: Avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. Lowers overhead for very high framerates (e.g. 10k+ FPS). // 2025-11-06: Lower minimum requirement to GLFW 3.0. Though a recent version e.g GLFW 3.4 is highly recommended. // 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. @@ -109,10 +110,15 @@ #endif // GLFW -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) -#define GLFW_HAS_X11_OR_WAYLAND 1 +#if !defined(IMGUI_IMPL_GLFW_DISABLE_X11) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)) +#define GLFW_HAS_X11 1 #else -#define GLFW_HAS_X11_OR_WAYLAND 0 +#define GLFW_HAS_X11 0 +#endif +#if !defined(IMGUI_IMPL_GLFW_DISABLE_WAYLAND) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)) +#define GLFW_HAS_WAYLAND 1 +#else +#define GLFW_HAS_WAYLAND 0 #endif #include #ifdef _WIN32 @@ -126,7 +132,7 @@ #define GLFW_EXPOSE_NATIVE_COCOA #endif #include -#elif GLFW_HAS_X11_OR_WAYLAND +#elif GLFW_HAS_X11 #ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.) #define GLFW_EXPOSE_NATIVE_X11 #endif @@ -239,7 +245,7 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(GLFWwindow* window) // Functions static bool ImGui_ImplGlfw_IsWayland() { -#if !GLFW_HAS_X11_OR_WAYLAND +#if !GLFW_HAS_WAYLAND return false; #elif GLFW_HAS_GETPLATFORM return glfwGetPlatform() == GLFW_PLATFORM_WAYLAND; @@ -933,7 +939,7 @@ static void ImGui_ImplGlfw_UpdateGamepads() // - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle. float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) { -#if GLFW_HAS_X11_OR_WAYLAND +#if GLFW_HAS_WAYLAND if (ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window)) if (bd->IsWayland) return 1.0f; @@ -950,7 +956,7 @@ float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) { -#if GLFW_HAS_X11_OR_WAYLAND +#if GLFW_HAS_WAYLAND if (ImGui_ImplGlfw_IsWayland()) // We can't access our bd->IsWayland cache for a monitor. return 1.0f; #endif @@ -972,7 +978,7 @@ static void ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(GLFWwindow* window, glfwGetFramebufferSize(window, &display_w, &display_h); float fb_scale_x = (w > 0) ? (float)display_w / (float)w : 1.0f; float fb_scale_y = (h > 0) ? (float)display_h / (float)h : 1.0f; -#if GLFW_HAS_X11_OR_WAYLAND +#if GLFW_HAS_WAYLAND ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (!bd->IsWayland) fb_scale_x = fb_scale_y = 1.0f; diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index ce954ed9f..98fae51c7 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -1,6 +1,7 @@ // dear imgui: Platform Backend for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (Requires: GLFW 3.0+. Prefer GLFW 3.3+/3.4+ for full feature support.) // Implemented features: // [X] Platform: Clipboard support. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 32008203d..824e05990 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -89,6 +89,8 @@ Other Changes: - Backends: - GLFW: Avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. Lowers overhead for very high framerates (e.g. 10k+ FPS). [@maxliani] + - GLFW: Added IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND to + forcefully disable either. (#9109, #9116) - SDL_GPU3: macOS version can use MSL shaders in order to support macOS 10.14+ (vs Metallib shaders requiring macOS 14+). Requires application calling SDL_CreateGPUDevice() with SDL_GPU_SHADERFORMAT_MSL. (#9076) [@Niminem] From 99712515740102ad6ea4db96300ed319453fa1fc Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Dec 2025 21:42:07 +0100 Subject: [PATCH 07/12] Fonts: amend/comment on FontDataOwnedByAtlas=false fix being a breaking change. (#9086, #8465) --- docs/CHANGELOG.txt | 17 ++++++++++++----- docs/FONTS.md | 1 + imgui.cpp | 6 ++++++ imgui.h | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 824e05990..faf1ebb2c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,14 +41,21 @@ HOW TO UPDATE? Breaking Changes: +- Fonts: Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which + did erroneously make a copy of the font data, essentially defeating the purpose + of this flag and wasting memory (undetected since July 2015 and now spotted + by @TellowKrinkle, this is perhaps the oldest bug in Dear ImGui history, + albeit for a rarely used feature!) (#9086, #8465) + HOWEVER, fixing this bug is likely to surface bugs in user/app code: + - Prior to 1.92, font data only needs to be available during the atlas->AddFontXXX() call. + - Since 1.92, font data needs to available until atlas->RemoveFont(), or more typically + until a shutdown of the owning context or font atlas. + - The fact that handling of `FontDataOwnedByAtlas = false` was broken + bypassed the issue altogether. + Other Changes: - Fonts: - - Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which - did erroneously make a copy of the font data, essentially defeating the purpose - of this flag and wasting memory. - Undetected since July 2015 and now spotted by @TellowKrinkle, this is perhaps - the oldest bug in Dear ImGui history (albeit for a rarely used feature)! (#9086) - Fixed an issue related to EllipsisChar handling, while changing font loader or font loader flags dynamically in Style->Fonts menus. - Textures: diff --git a/docs/FONTS.md b/docs/FONTS.md index 072ca9762..1a466be8d 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -228,6 +228,7 @@ ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; ImFont* font = io.Fonts->AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg); ``` +IMPORTANT: Since 1.92, when using `FontDataOwnedByAtlas = false`, font data needs to available until `atlas->RemoveFont()`, or more typically until a shutdown of the owning context or font atlas. It was not immediately noticeable in 1.92.0 due to a bug in handling `FontDataOwnedByAtlas = false`, which was fixed in 1.92.6. ##### [Return to Index](#index) diff --git a/imgui.cpp b/imgui.cpp index a977c773c..52b4f0f66 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -394,6 +394,12 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2025/11/24 (1.92.6) - Fonts: Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which did erroneously make a copy of the font data, essentially defeating the purpose of this flag and wasting memory. + (trivia: undetected since July 2015, this is perhaps the oldest bug in Dear ImGui history, albeit for a rarely used feature, see #9086) + HOWEVER, fixing this bug is likely to surface bugs in user code using `FontDataOwnedByAtlas = false`. + - Prior to 1.92, font data only needed to be available during the atlas->AddFontXXX() call. + - Since 1.92, font data needs to available until atlas->RemoveFont(), or more typically until a shutdown of the owning context or font atlas. + - The fact that handling of `FontDataOwnedByAtlas = false` was broken bypassed the issue altogether. - 2025/11/06 (1.92.5) - BeginChild: commented out some legacy names which were obsoleted in 1.90.0 (Nov 2023), 1.90.9 (July 2024), 1.91.1 (August 2024): - ImGuiChildFlags_Border --> ImGuiChildFlags_Borders - ImGuiWindowFlags_NavFlattened --> ImGuiChildFlags_NavFlattened (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0) diff --git a/imgui.h b/imgui.h index 7f8b6f3ac..09fef133f 100644 --- a/imgui.h +++ b/imgui.h @@ -3521,7 +3521,7 @@ struct ImFontConfig char Name[40]; // // Name (strictly to ease debugging, hence limited size buffer) void* FontData; // // TTF/OTF data int FontDataSize; // // TTF/OTF data size - bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself). + bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself). SINCE 1.92, THE DATA NEEDS TO PERSIST FOR WHOLE DURATION OF ATLAS. // Options bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. From 0d2dd30ee9783969cb7fe34deb67b1da92fc2431 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Dec 2025 17:17:57 +0100 Subject: [PATCH 08/12] Tabs: minor tweaks to facilitate mods altering spacing. --- imgui.h | 2 +- imgui_widgets.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/imgui.h b/imgui.h index 09fef133f..6c59e78f8 100644 --- a/imgui.h +++ b/imgui.h @@ -349,7 +349,7 @@ typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or // The identifier is valid even before the texture has been uploaded to the GPU/graphics system. // This is what gets passed to functions such as `ImGui::Image()`, `ImDrawList::AddImage()`. // This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering. -// - When a texture is created by user code (e.g. custom images), we directly stores the low-level ImTextureID. +// - When a texture is created by user code (e.g. custom images), we directly store the low-level ImTextureID. // Because of this, when displaying your own texture you are likely to ever only manage ImTextureID values on your side. // - When a texture is created by the backend, we stores a ImTextureData* which becomes an indirection // to extract the ImTextureID value during rendering, after texture upload has happened. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6be8f7691..84ea505f6 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9788,8 +9788,9 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection); // Calculate spacing between sections - sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; - sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; + const float tab_spacing = g.Style.ItemInnerSpacing.x; + sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? tab_spacing : 0.0f; + sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? tab_spacing : 0.0f; // Setup next selected tab ImGuiID scroll_to_tab_id = 0; @@ -9850,8 +9851,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; - section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); - section->WidthAfterShrinkMinWidth += ImMin(tab->ContentWidth, shrink_min_width) + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); + section->Width += tab->ContentWidth + (section_n == curr_section_n ? tab_spacing : 0.0f); + section->WidthAfterShrinkMinWidth += ImMin(tab->ContentWidth, shrink_min_width) + (section_n == curr_section_n ? tab_spacing : 0.0f); curr_section_n = section_n; // Store data so we can build an array sorted by width if we need to shrink tabs down @@ -10156,6 +10157,7 @@ void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* s if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) return; + const float tab_spacing = g.Style.ItemInnerSpacing.x; const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); @@ -10174,8 +10176,8 @@ void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* s dst_idx = i; // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. - const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; - const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; + const float x1 = bar_offset + dst_tab->Offset - tab_spacing; + const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + tab_spacing; //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) break; From bd6f48fe20ff87757485458d9147a50a445f06fa Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Dec 2025 18:28:23 +0100 Subject: [PATCH 09/12] Backends: OpenGL3: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112) --- backends/imgui_impl_opengl3.cpp | 1 + backends/imgui_impl_opengl3_loader.h | 9 +++++++++ docs/CHANGELOG.txt | 2 ++ 3 files changed, 12 insertions(+) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 3e0e265b1..9b303ac08 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-07-22: OpenGL: Add and call embedded loader shutdown during ImGui_ImplOpenGL3_Shutdown() to facilitate multiple init/shutdown cycles in same process. (#8792) // 2025-07-15: OpenGL: Set GL_UNPACK_ALIGNMENT to 1 before updating textures (#8802) + restore non-WebGL/ES update path that doesn't require a CPU-side copy. diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h index 2c80cc598..2c485841a 100644 --- a/backends/imgui_impl_opengl3_loader.h +++ b/backends/imgui_impl_opengl3_loader.h @@ -836,6 +836,7 @@ static int parse_version(void) } static void load_procs(GL3WGetProcAddressProc proc); +static void clear_procs(); int imgl3wInit(void) { @@ -855,6 +856,7 @@ int imgl3wInit2(GL3WGetProcAddressProc proc) void imgl3wShutdown(void) { close_libgl(); + clear_procs(); } int imgl3wIsSupported(int major, int minor) @@ -943,6 +945,13 @@ static void load_procs(GL3WGetProcAddressProc proc) imgl3wProcs.ptr[i] = proc(proc_names[i]); } +static void clear_procs() +{ + size_t i; + for (i = 0; i < GL3W_ARRAY_SIZE(proc_names); i++) + imgl3wProcs.ptr[i] = nullptr; +} + #ifdef __cplusplus } #endif diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index faf1ebb2c..8c82d6a14 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -98,6 +98,8 @@ Other Changes: Lowers overhead for very high framerates (e.g. 10k+ FPS). [@maxliani] - GLFW: Added IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND to forcefully disable either. (#9109, #9116) + - OpenGL3: Fixed embedded loader multiple init/shutdown cycles broken on some + platforms. (#8792, #9112) - SDL_GPU3: macOS version can use MSL shaders in order to support macOS 10.14+ (vs Metallib shaders requiring macOS 14+). Requires application calling SDL_CreateGPUDevice() with SDL_GPU_SHADERFORMAT_MSL. (#9076) [@Niminem] From 1e7d2adc29f611157f1a50fa87a33ca30a96a24d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Dec 2025 21:36:42 +0100 Subject: [PATCH 10/12] Fixed Clang cast-align warning + Added missing Changelog entry in 1.92.4. (#8893) --- docs/CHANGELOG.txt | 2 ++ imgui_tables.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8c82d6a14..4219987b9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -298,6 +298,8 @@ Other Changes: using ImGuiWindowFlags_UnsavedDocument/ImGuiTabItemFlags_UnsavedDocument. (#8983) - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() helpers to null all handlers. (#8945, #2769) +- Tables: changed ImGuiTableFlags_NoBordersInBody behavior to not draw border in + body even when resizing. (#8893) - Inputs: - Shortcuts: added support for combining ImGuiInputFlags_RouteFocused (which is the default route) with ImGuiInputFlags_RouteOverActive, allowing diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 0ea14727e..20ca7af1b 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -564,7 +564,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const int old_columns_count = table->Columns.size(); if (old_columns_count != 0 && old_columns_count != columns_count) { - // Attempt to preserve width on column count change (#4046) + // Attempt to preserve width and other settings on column count/specs change (#4046) old_columns_to_preserve = table->Columns.Data; old_columns_raw_data = table->RawData; // Free at end of function table->RawData = NULL; @@ -3842,7 +3842,7 @@ void ImGui::TableFixDisplayOrder(ImGuiTable* table) { ImGuiContext& g = *GImGui; g.TempBuffer.reserve((int)(sizeof(ImGuiTableFixDisplayOrderColumnData) * table->ColumnsCount)); // FIXME: Maybe wrap those two lines as a helper. - ImGuiTableFixDisplayOrderColumnData* fdo_columns = (ImGuiTableFixDisplayOrderColumnData*)g.TempBuffer.Data; + ImGuiTableFixDisplayOrderColumnData* fdo_columns = (ImGuiTableFixDisplayOrderColumnData*)(void*)g.TempBuffer.Data; for (int n = 0; n < table->ColumnsCount; n++) { fdo_columns[n].Idx = (ImGuiTableColumnIdx)n; From fc89c61089729f72aa0579f689406edfbddd603a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 26 Dec 2019 21:40:58 +0100 Subject: [PATCH 11/12] Hashing: handling of "###" operator to reset to seed within a string identifier doesn't include the "###" characters in the output hash anymore. This has various simplifying properties. Need a test engine update too. + Demo: removed misleading/unnecessary usage of ###. --- docs/CHANGELOG.txt | 8 ++++++++ imgui.cpp | 20 +++++++++++++------- imgui.h | 2 +- imgui_demo.cpp | 2 +- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4219987b9..90d85aaf2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,14 @@ HOW TO UPDATE? Breaking Changes: +- Hashing: handling of "###" operator to reset to seed within a string identifier + doesn't include the "###" characters in the output hash anymore: + Before: GetID("Hello###World") == GetID("###World") != GetID("World"); + Now: GetID("Hello###World") == GetID("###World") == GetID("World"); + - This has the property of facilitating concatenating and manipulating + identifers using "###", and will allow fixing other dangling issues. + - This will invalidate hashes (stored in .ini data) for Tables and Windows + that are using the "###" operators. (#713, #1698) - Fonts: Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which did erroneously make a copy of the font data, essentially defeating the purpose of this flag and wasting memory (undetected since July 2015 and now spotted diff --git a/imgui.cpp b/imgui.cpp index 52b4f0f66..05be5f3bc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2368,11 +2368,8 @@ ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed) #endif } -// Zero-terminated string hash, with support for ### to reset back to seed value -// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. -// Because this syntax is rarely used we are optimizing for the common case. -// - If we reach ### in the string we discard the hash so far and reset to the seed. -// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) +// Zero-terminated string hash, with support for ### to reset back to seed value. +// e.g. "label###id" outputs the same hash as "id" (and "label" is generally displayed by the UI functions) // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) { @@ -2384,11 +2381,16 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) #endif if (data_size != 0) { - while (data_size-- != 0) + while (data_size-- > 0) { unsigned char c = *data++; if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') + { crc = seed; + data += 2; + data_size -= 2; + continue; + } #ifndef IMGUI_ENABLE_SSE4_2_CRC crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; #else @@ -2401,7 +2403,11 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed) while (unsigned char c = *data++) { if (c == '#' && data[0] == '#' && data[1] == '#') + { crc = seed; + data += 2; + continue; + } #ifndef IMGUI_ENABLE_SSE4_2_CRC crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; #else @@ -2419,7 +2425,7 @@ const char* ImHashSkipUncontributingPrefix(const char* label) const char* result = label; while (unsigned char c = *label++) if (c == '#' && label[0] == '#' && label[1] == '#') - result = label - 1; + result = label + 2; return result; } diff --git a/imgui.h b/imgui.h index 6c59e78f8..40493eb33 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.6 WIP" -#define IMGUI_VERSION_NUM 19254 +#define IMGUI_VERSION_NUM 19255 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8ace9ff6a..beb181986 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3358,7 +3358,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::InputText("###NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("##NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleVar(); } From 4dac00ca0c8138b5db6434a15dc273ee8b26cfbd Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Dec 2025 23:12:52 +0100 Subject: [PATCH 12/12] Windows: fixed always updating internal buffer for ### window titles even when Ctrl+Tab window is hidden. Instead forcing an update on mismatching when appearing. 3997e8b555 was already doing that. (Relates to 8e67fe1: code assume pointer used to be nulled when ctrl+tab window is disabled. not the case). --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 05be5f3bc..6d7b02654 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7506,11 +7506,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. bool window_title_visible_elsewhere = false; - if (g.NavWindowingListWindow != NULL && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using Ctrl+Tab + if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->WasActive && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using Ctrl+Tab window_title_visible_elsewhere = true; if (flags & ImGuiWindowFlags_ChildMenu) window_title_visible_elsewhere = true; - if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) + if ((window_title_visible_elsewhere || window_just_activated_by_user) && !window_just_created && strcmp(name, window->Name) != 0) { size_t buf_len = (size_t)window->NameBufLen; window->Name = ImStrdupcpy(window->Name, &buf_len, name);