From 38f5e5a0b839b19b78dadc0f3d25b7e8ecabbe5b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Mar 2026 14:42:26 +0100 Subject: [PATCH] Tables: rework column reordering code. (#9312) - Move more logic into TableQueueSetColumnDisplayOrder() so that it may be called from different locations. - Use to checking if both columns are on same size of the frozen barrier slightly changed to avoid reordering hidden column (with caveat of ill-defined design for what's "right"). --- imgui_internal.h | 3 ++- imgui_tables.cpp | 56 +++++++++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index b1ae63f27..b6a5d991c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3045,7 +3045,7 @@ struct IMGUI_API ImGuiTable ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. ImGuiTableColumnIdx HeldHeaderColumn; // Index of column header being held. ImGuiTableColumnIdx ReorderColumn; // Index of column being reordered. (not cleared) - ImGuiTableColumnIdx ReorderColumnDir; // -1 or +1 + ImGuiTableColumnIdx ReorderColumnDstOrder; // Requested display order of column being reordered. ImGuiTableColumnIdx LeftMostEnabledColumn; // Index of left-most non-hidden column. ImGuiTableColumnIdx RightMostEnabledColumn; // Index of right-most non-hidden column. ImGuiTableColumnIdx LeftMostStretchedColumn; // Index of left-most stretched column. @@ -3560,6 +3560,7 @@ namespace ImGui IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); + IMGUI_API void TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index a51f2f012..de7380bb0 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -702,21 +702,11 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) // Note: we don't clear ReorderColumn after handling the request (FIXME: clarify why or add a test). if (table->InstanceCurrent == 0) { - if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1) - table->ReorderColumn = -1; table->HeldHeaderColumn = -1; - if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0) + if (table->ReorderColumn != -1 && table->ReorderColumnDstOrder != -1) { - // We need to handle reordering across hidden columns. - // In the configuration below, moving C to the right of E will lead to: - // ... C [D] E ---> ... [D] E C (Column name/index) - // ... 2 3 4 ... 2 3 4 (Display order) - IM_ASSERT(table->ReorderColumnDir == -1 || table->ReorderColumnDir == +1); - IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable); - ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn]; - ImGuiTableColumn* dst_column = &table->Columns[(table->ReorderColumnDir < 0) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn]; - TableSetColumnDisplayOrder(table, table->ReorderColumn, dst_column->DisplayOrder); - table->ReorderColumnDir = 0; + TableSetColumnDisplayOrder(table, table->ReorderColumn, table->ReorderColumnDstOrder); + table->ReorderColumnDstOrder = -1; } } @@ -730,8 +720,7 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) } } -// Note that TableSetupScrollFreeze() enforce a display order range for frozen columns. -// So reordering a column across the frozen column barrier is illegal and will be undone. +// Apply immediately. See TableQueueSetColumnDisplayOrder() for additional checks/constraints. void ImGui::TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order) { IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); @@ -755,6 +744,25 @@ void ImGui::TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_ table->IsSettingsDirty = true; } +// Reorder requested by user indirection needs to verify +// - That we don't reorder columns with the ImGuiTableColumnFlags_NoReorder flag. +// - That we don't cross the frozen column limit. +// (that TableSetupScrollFreeze() enforce a display order range for frozen columns. +// so reordering a column across the frozen column barrier is illegal and will be undone.) +void ImGui::TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order) +{ + ImGuiTableColumn* src_column = &table->Columns[column_n]; + ImGuiTableColumn* dst_column = &table->Columns[table->DisplayOrderToIndex[dst_order]]; + if ((src_column->Flags | dst_column->Flags) & ImGuiTableColumnFlags_NoReorder) // FIXME: Perform a sweep test? + return; + int src_i = (src_column->IndexWithinEnabledSet != -1) ? src_column->IndexWithinEnabledSet : table->Columns.index_from_ptr(src_column); // FIXME: Hidden columns don't count into the FreezeColumns count, so what to do here is ill-defined. For now we use regular index. + int dst_i = (dst_column->IndexWithinEnabledSet != -1) ? dst_column->IndexWithinEnabledSet : table->Columns.index_from_ptr(dst_column); + if ((src_i < table->FreezeColumnsRequest) != (dst_i < table->FreezeColumnsRequest)) + return; + table->ReorderColumn = (ImGuiTableColumnIdx)column_n; + table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)dst_order; +} + // Adjust flags: default width mode + stretch columns are not allowed when auto extending static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in) { @@ -3224,21 +3232,19 @@ void ImGui::TableHeader(const char* label) // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone. if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive) { - // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x - table->ReorderColumn = (ImGuiTableColumnIdx)column_n; + // - While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x + // - We need to handle reordering across hidden columns. + // In the configuration below, moving C to the right of E will lead to: + // ... C [D] E ---> ... [D] E C (Column name/index) + // ... 2 3 4 ... 2 3 4 (Display order) + // - The other constraints are enforced by TableQueueSetColumnDisplayOrder() which might early out. table->InstanceInteracted = table->InstanceCurrent; - - // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder. if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x) if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL) - if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder)) - if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) - table->ReorderColumnDir = -1; + TableQueueSetColumnDisplayOrder(table, column_n, prev_column->DisplayOrder); if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x) if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL) - if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder)) - if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) - table->ReorderColumnDir = +1; + TableQueueSetColumnDisplayOrder(table, column_n, next_column->DisplayOrder); } // Sort order arrow