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").
This commit is contained in:
ocornut
2026-03-23 14:42:26 +01:00
parent 697b6886e3
commit 38f5e5a0b8
2 changed files with 33 additions and 26 deletions

View File

@@ -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);

View File

@@ -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