Tables: column order scrambled on topology change in non-reorderable tables #9570

Closed
opened 2026-10-01 19:59:35 +00:00 by lailoken · 0 comments
lailoken commented 2026-10-01 19:59:35 +00:00 (Migrated from github.com)

Version/Branch of Dear ImGui:

1.93.0 WIP, Branch: master ed73ef4e8 and docking 64944b452

Back-ends:

imgui_impl_sdl2.cpp + imgui_impl_metal.mm (backend independent)

Compiler, OS:

macOS, Clang

Full config/build information:

No response

Details:

My Issue/Question:

Follow-up to #9108 (and distinct from #9519): in a table without ImGuiTableFlags_Reorderable, the column order gets scrambled when columns are added. This happens on a live topology change and also on restart with .ini settings.

Results for columns Month, C, D -> Month, A, B, C, D (insert) and Month, A, B, C -> Month, A, B, C, AB, AD (append), on master ed73ef4e8 and docking 64944b452:

Scenario Displayed Expected
Live insert Month A C B D Month A B C D
Live append Month A B C AB AD (correct)
Restart, never resized, insert C D Month A B Month A B C D
Restart, resized, insert Month A C B D Month A B C D
Restart, never resized, append AB AD Month A B C Month A B C AB AD
Restart, resized, append Month A B C AB AD (correct)

"Never resized" means the table is saved as a bare [Table][0x...,N] entry with no Column lines (it records itself in default state, #7934). That is the common case for us.

Cause, as far as I can tell:

  • TableReconcileColumns() copies the old column data, including the old DisplayOrder (= old index), to the matched columns. TableLoadSettingsForColumn() sets column->DisplayOrder = column_settings->Index (the saved index) when the settings have no Reorderable data. New columns get DisplayOrder = their new index from TableInitColumnDefaults(), so old and new values clash.
  • For a bare .ini entry, the N default-constructed column settings (ID = 0, Index = -1) are matched sequentially onto the first N columns. Those columns then get DisplayOrder = -1 and sort to the end, so every column beyond the old count displays first.

TableFixDisplayOrder() then sorts by these values.

Since a non-reorderable table always shows columns in index order, the fix I'm using locally is to apply that directly in TableFixDisplayOrder(), which both the load and the reconcile paths go through:

 void ImGui::TableFixDisplayOrder(ImGuiTable* table)
 {
+    if ((table->Flags & ImGuiTableFlags_Reorderable) == 0)
+    {
+        for (int n = 0; n < table->ColumnsCount; n++)
+            table->DisplayOrderToIndex[n] = table->Columns[n].DisplayOrder = (ImGuiTableColumnIdx)n;
+        return;
+    }
     ImGuiContext& g = *GImGui;

Widths and other per-column data are still matched by ID. With the patch, all six scenarios display the expected order (tested headless against master and docking).

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

ImGui::Begin("Example Bug");
static bool extra = false;
ImGui::Checkbox("Add columns A, B", &extra); // toggle on: displays "Month A C B D"
const char* cols_small[] = { "Month", "C", "D" };
const char* cols_large[] = { "Month", "A", "B", "C", "D" };
const char** cols = extra ? cols_large : cols_small;
const int cols_count = extra ? 5 : 3;
if (ImGui::BeginTable("table", cols_count, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit))
{
    for (int n = 0; n < cols_count; n++)
        ImGui::TableSetupColumn(cols[n]);
    ImGui::TableHeadersRow();
    ImGui::EndTable();
}
ImGui::End();
### Version/Branch of Dear ImGui: 1.93.0 WIP, Branch: master ed73ef4e8 and docking 64944b452 ### Back-ends: imgui_impl_sdl2.cpp + imgui_impl_metal.mm (backend independent) ### Compiler, OS: macOS, Clang ### Full config/build information: _No response_ ### Details: **My Issue/Question:** Follow-up to #9108 (and distinct from #9519): in a table **without** `ImGuiTableFlags_Reorderable`, the column order gets scrambled when columns are added. This happens on a live topology change and also on restart with .ini settings. Results for columns `Month, C, D` -> `Month, A, B, C, D` (insert) and `Month, A, B, C` -> `Month, A, B, C, AB, AD` (append), on master `ed73ef4e8` and docking `64944b452`: | Scenario | Displayed | Expected | |---|---|---| | Live insert | `Month A C B D` | `Month A B C D` | | Live append | `Month A B C AB AD` | (correct) | | Restart, never resized, insert | `C D Month A B` | `Month A B C D` | | Restart, resized, insert | `Month A C B D` | `Month A B C D` | | Restart, never resized, append | `AB AD Month A B C` | `Month A B C AB AD` | | Restart, resized, append | `Month A B C AB AD` | (correct) | "Never resized" means the table is saved as a bare `[Table][0x...,N]` entry with no `Column` lines (it records itself in default state, #7934). That is the common case for us. Cause, as far as I can tell: - `TableReconcileColumns()` copies the old column data, including the old `DisplayOrder` (= old index), to the matched columns. `TableLoadSettingsForColumn()` sets `column->DisplayOrder = column_settings->Index` (the saved index) when the settings have no Reorderable data. New columns get `DisplayOrder` = their new index from `TableInitColumnDefaults()`, so old and new values clash. - For a bare .ini entry, the N default-constructed column settings (`ID = 0`, `Index = -1`) are matched sequentially onto the first N columns. Those columns then get `DisplayOrder = -1` and sort to the end, so every column beyond the old count displays first. `TableFixDisplayOrder()` then sorts by these values. Since a non-reorderable table always shows columns in index order, the fix I'm using locally is to apply that directly in `TableFixDisplayOrder()`, which both the load and the reconcile paths go through: ```patch void ImGui::TableFixDisplayOrder(ImGuiTable* table) { + if ((table->Flags & ImGuiTableFlags_Reorderable) == 0) + { + for (int n = 0; n < table->ColumnsCount; n++) + table->DisplayOrderToIndex[n] = table->Columns[n].DisplayOrder = (ImGuiTableColumnIdx)n; + return; + } ImGuiContext& g = *GImGui; ``` Widths and other per-column data are still matched by ID. With the patch, all six scenarios display the expected order (tested headless against master and docking). ### Screenshots/Video: _No response_ ### Minimal, Complete and Verifiable Example code: ```cpp ImGui::Begin("Example Bug"); static bool extra = false; ImGui::Checkbox("Add columns A, B", &extra); // toggle on: displays "Month A C B D" const char* cols_small[] = { "Month", "C", "D" }; const char* cols_large[] = { "Month", "A", "B", "C", "D" }; const char** cols = extra ? cols_large : cols_small; const int cols_count = extra ? 5 : 3; if (ImGui::BeginTable("table", cols_count, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit)) { for (int n = 0; n < cols_count; n++) ImGui::TableSetupColumn(cols[n]); ImGui::TableHeadersRow(); ImGui::EndTable(); } ImGui::End(); ```
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ocornut/imgui#9570
No description provided.