Tables: Fixed losing stored display order when reducing column count. (#9108, #4046)
Amend f2df804fcc
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index c59efdc..2353fad 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -67,6 +67,8 @@
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 89dcc67..7f8b6f3 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 @@
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 e61f830..254665d 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3514,6 +3514,7 @@
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 4aa8683..0ea1472 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -566,7 +566,7 @@
{
// 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 @@
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 @@
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 @@
// 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 @@
}
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;