Tables: Ditch old columns flags/setting if not called again. Displaying right-most border when using _SameWidths. Moving Width-related functions to their own section.

Effectively removed the TableFixColumnFlags() in first loop of TableUpdateLayout() to replace it with similar one but only when column not declared. I think this should be the right thing.
This commit is contained in:
ocornut 2020-12-10 16:09:11 +01:00
parent 3346544cf5
commit e6b875c09b
2 changed files with 192 additions and 156 deletions

View File

@ -2301,6 +2301,7 @@ namespace ImGui
IMGUI_API void TableSetupDrawChannels(ImGuiTable* table);
IMGUI_API void TableUpdateLayout(ImGuiTable* table);
IMGUI_API void TableUpdateBorders(ImGuiTable* table);
IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table);
IMGUI_API void TableDrawBorders(ImGuiTable* table);
IMGUI_API void TableDrawContextMenu(ImGuiTable* table);
IMGUI_API void TableMergeDrawChannels(ImGuiTable* table);

View File

@ -6,6 +6,7 @@
Index of this file:
// [SECTION] Tables: Main code
// [SECTION] Tables: Columns width management
// [SECTION] Tables: Drawing
// [SECTION] Tables: Sorting
// [SECTION] Tables: Headers
@ -17,17 +18,25 @@ Index of this file:
*/
// Navigating this file:
// - In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
//-----------------------------------------------------------------------------
// Typical tables call flow: (root level is generally public API):
//-----------------------------------------------------------------------------
// - BeginTable() user begin into a table
// | BeginChild() - (if ScrollX/ScrollY is set)
// | TableBeginInitMemory() - first time table is used
// | TableResetSettings() - on settings reset
// | TableLoadSettings() - on settings load
// | TableBeginApplyRequests() - apply queued resizing/reordering/hiding requests
// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame)
// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width
// - TableSetupColumn() user submit columns details (optional)
// - TableSetupScrollFreeze() user submit scroll freeze information (optional)
// - TableUpdateLayout() [Internal] automatically called by the FIRST call to TableNextRow() or TableHeadersRow(): lock all widths, columns positions, clipping rectangles
//-----------------------------------------------------------------------------
// - TableUpdateLayout() [Internal] setup everything, lock all widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().
// | TableSetupDrawChannels() - setup ImDrawList channels
// | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission
// | TableDrawContextMenu() - draw right-click context menu
@ -572,8 +581,11 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table)
}
// Adjust flags: default width mode + stretch columns are not allowed when auto extending
static ImGuiTableColumnFlags TableFixColumnFlags(ImGuiTable* table, ImGuiTableColumnFlags flags)
// Set 'column->FlagsIn' and 'column->Flags'.
static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in)
{
ImGuiTableColumnFlags flags = flags_in;
// Sizing Policy
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0)
{
@ -591,12 +603,18 @@ static ImGuiTableColumnFlags TableFixColumnFlags(ImGuiTable* table, ImGuiTableCo
if ((flags & ImGuiTableColumnFlags_NoSortAscending) && (flags & ImGuiTableColumnFlags_NoSortDescending))
flags |= ImGuiTableColumnFlags_NoSort;
// Indentation
if ((flags & ImGuiTableColumnFlags_IndentMask_) == 0)
flags |= (table->Columns.index_from_ptr(column) == 0) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable;
// Alignment
//if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0)
// flags |= ImGuiTableColumnFlags_AlignCenter;
//IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used.
return flags;
// Preserve status flags
column->FlagsIn = flags_in;
column->Flags = flags | (column->Flags & ImGuiTableColumnFlags_StatusMask_);
}
// Minimum column content width (without padding)
@ -631,6 +649,18 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
if (column_n != order_n)
table->IsDefaultDisplayOrder = false;
ImGuiTableColumn* column = &table->Columns[column_n];
// Clear column settings if not submitted by user.
// Currently we make it mandatory to call TableSetupColumn() every frame.
// It would easily work without but we're ready to guarantee it since e.g. names need resubmission anyway.
// In theory we could be calling TableSetupColumn() here with dummy values it should yield the same effect.
if (column_n >= table->DeclColumnsCount)
{
TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None);
column->UserID = 0;
column->InitStretchWeightOrWidth = -1.0f;
}
if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide))
column->IsEnabledNextFrame = true;
if (column->IsEnabled != column->IsEnabledNextFrame)
@ -699,11 +729,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
continue;
ImGuiTableColumn* column = &table->Columns[column_n];
// Adjust flags: default width mode + weighted columns are not allowed when auto extending
// FIXME-TABLE: Clarify why we need to do this again here and not just in TableSetupColumn()
column->Flags = TableFixColumnFlags(table, column->FlagsIn) | (column->Flags & ImGuiTableColumnFlags_StatusMask_);
if ((column->Flags & ImGuiTableColumnFlags_IndentMask_) == 0)
column->Flags |= (column_n == 0) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable;
// Count resizable columns
if ((column->Flags & ImGuiTableColumnFlags_NoResize) == 0)
count_resizable++;
@ -783,7 +809,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
{
sum_weights_stretched += 1.0f - column->StretchWeight; // Update old sum
column->StretchWeight = 1.0f;
if (count_fixed > 0)
if (mixed_same_widths)
column->WidthRequest = max_width_auto;
}
}
@ -1260,124 +1286,6 @@ void ImGui::EndTable()
outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1;
}
static void TableUpdateColumnsWeightFromWidth(ImGuiTable* table)
{
IM_ASSERT(table->LeftMostStretchedColumnDisplayOrder != -1);
// Measure existing quantity
float visible_weight = 0.0f;
float visible_width = 0.0f;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
continue;
IM_ASSERT(column->StretchWeight > 0.0f);
visible_weight += column->StretchWeight;
visible_width += column->WidthRequest;
}
IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f);
// Apply new weights
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
continue;
column->StretchWeight = ((column->WidthRequest + 0.0f) / visible_width) * visible_weight;
}
}
// 'width' = inner column width, without padding
void ImGui::TableSetColumnWidth(int column_n, float width)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && table->IsLayoutLocked == false);
IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);
ImGuiTableColumn* column_0 = &table->Columns[column_n];
float column_0_width = width;
// Constraints
const float min_width = TableGetMinColumnWidth();
float max_width_0 = FLT_MAX;
if (!(table->Flags & ImGuiTableFlags_ScrollX))
max_width_0 = (table->WorkRect.Max.x - column_0->MinX) - (table->ColumnsEnabledCount - (column_0->IndexWithinEnabledSet + 1)) * min_width;
column_0_width = ImClamp(column_0_width, min_width, max_width_0);
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
return;
ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL;
// In this surprisingly not simple because of how we support mixing Fixed and Stretch columns.
// When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.
// FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user.
// Scenarios:
// - F1 F2 F3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. Subsequent columns will be offset.
// - F1 F2 F3 resize from F3| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered.
// - F1 F2 W3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size.
// - F1 F2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1)
// - W1 W2 W3 resize from W1| or W2| --> FIXME
// - W1 W2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1)
// - W1 F2 F3 resize from F3| --> ok: no-op (disabled by Resize Rule 1)
// - W1 F2 resize from F2| --> ok: no-op (disabled by Resize Rule 1)
// - W1 W2 F3 resize from W1| or W2| --> ok
// - W1 F2 W3 resize from W1| or F2| --> FIXME
// - F1 W2 F3 resize from W2| --> ok
// - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move. (forwarded by Resize Rule 2)
// - W1 F2 F3 resize from F2| --> FIXME should resize F2, F3 and not have effect on W1 (Stretch columns are _before_ the Fixed column).
// Rules:
// - [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout().
// - [Resize Rule 2] Resizing from right-side of a Stretch column before a fixed column forward sizing to left-side of fixed column.
// - [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure that our left border won't move.
table->IsSettingsDirty = true;
if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed)
{
// [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure
// that our left border won't move, which we can do by making sure column_a/column_b resizes cancels each others.
if (column_1 && (column_1->Flags & ImGuiTableColumnFlags_WidthFixed))
if (table->LeftMostStretchedColumnDisplayOrder != -1 && table->LeftMostStretchedColumnDisplayOrder < column_0->DisplayOrder)
{
// (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);
column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;
column_1->WidthRequest = column_1_width;
}
// Apply
//IMGUI_DEBUG_LOG("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthRequested, column_0_width);
column_0->WidthRequest = column_0_width;
}
else if (column_0->Flags & ImGuiTableColumnFlags_WidthStretch)
{
// We can also use previous column if there's no next one
if (column_1 == NULL)
column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL;
if (column_1 == NULL)
return;
if (column_1->Flags & ImGuiTableColumnFlags_WidthFixed)
{
// [Resize Rule 2]
float off = (column_0->WidthGiven - column_0_width);
float column_1_width = column_1->WidthGiven + off;
column_1->WidthRequest = ImMax(min_width, column_1_width);
}
else
{
// (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);
column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;
column_1->WidthRequest = column_1_width;
column_0->WidthRequest = column_0_width;
TableUpdateColumnsWeightFromWidth(table);
}
}
}
// We use a default parameter of 'init_width_or_weight == -1',
// - with ImGuiTableColumnFlags_WidthFixed, width <= 0 --> init width == auto
// - with ImGuiTableColumnFlags_WidthFixed, width > 0 --> init width == manual
@ -1403,9 +1311,8 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
if ((table->Flags & ImGuiTableFlags_ColumnsWidthFixed) && (init_width_or_weight > 0.0f))
flags |= ImGuiTableColumnFlags_WidthFixed;
TableSetupColumnFlags(table, column, flags);
column->UserID = user_id;
column->FlagsIn = flags;
column->Flags = TableFixColumnFlags(table, column->FlagsIn) | (column->Flags & ImGuiTableColumnFlags_StatusMask_);
flags = column->Flags;
// Initialize defaults
@ -1861,32 +1768,6 @@ ImGuiID ImGui::TableGetColumnResizeID(const ImGuiTable* table, int column_n, int
return id;
}
// Disable clipping then auto-fit, will take 2 frames
// (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns)
void ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n)
{
// Single auto width uses auto-fit
ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsEnabled)
return;
column->CannotSkipItemsQueue = (1 << 0);
column->AutoFitQueue = (1 << 1);
if (column->Flags & ImGuiTableColumnFlags_WidthStretch)
table->AutoFitSingleStretchColumn = (ImGuiTableColumnIdx)column_n;
}
void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table)
{
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsEnabled)
continue;
column->CannotSkipItemsQueue = (1 << 0);
column->AutoFitQueue = (1 << 1);
}
}
// Return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
int ImGui::TableGetHoveredColumn()
{
@ -1939,6 +1820,159 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int colum
}
}
//-------------------------------------------------------------------------
// [SECTION] Tables: Columns width management
//-------------------------------------------------------------------------
// - TableSetColumnWidth()
// - TableSetColumnWidthAutoSingle() [Internal]
// - TableSetColumnWidthAutoAll() [Internal]
// - TableUpdateColumnsWeightFromWidth() [Internal]
//-------------------------------------------------------------------------
// 'width' = inner column width, without padding
void ImGui::TableSetColumnWidth(int column_n, float width)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && table->IsLayoutLocked == false);
IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);
ImGuiTableColumn* column_0 = &table->Columns[column_n];
float column_0_width = width;
// Constraints
const float min_width = TableGetMinColumnWidth();
float max_width_0 = FLT_MAX;
if (!(table->Flags & ImGuiTableFlags_ScrollX))
max_width_0 = (table->WorkRect.Max.x - column_0->MinX) - (table->ColumnsEnabledCount - (column_0->IndexWithinEnabledSet + 1)) * min_width;
column_0_width = ImClamp(column_0_width, min_width, max_width_0);
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
return;
ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL;
// In this surprisingly not simple because of how we support mixing Fixed and Stretch columns.
// When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.
// FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user.
// Scenarios:
// - F1 F2 F3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. Subsequent columns will be offset.
// - F1 F2 F3 resize from F3| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered.
// - F1 F2 W3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size.
// - F1 F2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1)
// - W1 W2 W3 resize from W1| or W2| --> FIXME
// - W1 W2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1)
// - W1 F2 F3 resize from F3| --> ok: no-op (disabled by Resize Rule 1)
// - W1 F2 resize from F2| --> ok: no-op (disabled by Resize Rule 1)
// - W1 W2 F3 resize from W1| or W2| --> ok
// - W1 F2 W3 resize from W1| or F2| --> FIXME
// - F1 W2 F3 resize from W2| --> ok
// - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move. (forwarded by Resize Rule 2)
// - W1 F2 F3 resize from F2| --> FIXME should resize F2, F3 and not have effect on W1 (Stretch columns are _before_ the Fixed column).
// Rules:
// - [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout().
// - [Resize Rule 2] Resizing from right-side of a Stretch column before a fixed column forward sizing to left-side of fixed column.
// - [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure that our left border won't move.
table->IsSettingsDirty = true;
if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed)
{
// [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure
// that our left border won't move, which we can do by making sure column_a/column_b resizes cancels each others.
if (column_1 && (column_1->Flags & ImGuiTableColumnFlags_WidthFixed))
if (table->LeftMostStretchedColumnDisplayOrder != -1 && table->LeftMostStretchedColumnDisplayOrder < column_0->DisplayOrder)
{
// (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);
column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;
column_1->WidthRequest = column_1_width;
}
// Apply
//IMGUI_DEBUG_LOG("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthRequested, column_0_width);
column_0->WidthRequest = column_0_width;
}
else if (column_0->Flags & ImGuiTableColumnFlags_WidthStretch)
{
// We can also use previous column if there's no next one
if (column_1 == NULL)
column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL;
if (column_1 == NULL)
return;
if (column_1->Flags & ImGuiTableColumnFlags_WidthFixed)
{
// [Resize Rule 2]
float off = (column_0->WidthGiven - column_0_width);
float column_1_width = column_1->WidthGiven + off;
column_1->WidthRequest = ImMax(min_width, column_1_width);
}
else
{
// (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);
column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;
column_1->WidthRequest = column_1_width;
column_0->WidthRequest = column_0_width;
TableUpdateColumnsWeightFromWidth(table);
}
}
}
// Disable clipping then auto-fit, will take 2 frames
// (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns)
void ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n)
{
// Single auto width uses auto-fit
ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsEnabled)
return;
column->CannotSkipItemsQueue = (1 << 0);
column->AutoFitQueue = (1 << 1);
if (column->Flags & ImGuiTableColumnFlags_WidthStretch)
table->AutoFitSingleStretchColumn = (ImGuiTableColumnIdx)column_n;
}
void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table)
{
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsEnabled)
continue;
column->CannotSkipItemsQueue = (1 << 0);
column->AutoFitQueue = (1 << 1);
}
}
void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table)
{
IM_ASSERT(table->LeftMostStretchedColumnDisplayOrder != -1);
// Measure existing quantity
float visible_weight = 0.0f;
float visible_width = 0.0f;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
continue;
IM_ASSERT(column->StretchWeight > 0.0f);
visible_weight += column->StretchWeight;
visible_width += column->WidthRequest;
}
IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f);
// Apply new weights
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
continue;
column->StretchWeight = ((column->WidthRequest + 0.0f) / visible_width) * visible_weight;
}
}
//-------------------------------------------------------------------------
// [SECTION] Tables: Drawing
//-------------------------------------------------------------------------
@ -2247,7 +2281,8 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
if (column->MaxX > table->InnerClipRect.Max.x && !is_resized)// && is_hovered)
continue;
if (column->NextEnabledColumn == -1 && !is_resizable)
continue;
if ((table->Flags & ImGuiTableFlags_SameWidths) == 0)
continue;
if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size..
continue;