Tables: Shared menu item id for "Size all" variations. Avoid allocation on single sort specs. Fix TableGetColumnIsEnabled(). Massage TableHeaderRows().

This commit is contained in:
ocornut 2020-12-02 13:58:55 +01:00
parent e126a64782
commit 155b8bb816
3 changed files with 42 additions and 45 deletions

View File

@ -1900,7 +1900,7 @@ struct ImGuiTableSortSpecsColumn
ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here) ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here)
ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending (you can use this or SortSign, whichever is more convenient for your sort function) ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending (you can use this or SortSign, whichever is more convenient for your sort function)
ImGuiTableSortSpecsColumn() { ColumnUserID = 0; ColumnIndex = 0; SortOrder = 0; SortDirection = ImGuiSortDirection_Ascending; } ImGuiTableSortSpecsColumn() { memset(this, 0, sizeof(*this)); }
}; };
// Sorting specifications for a table (often handling sort specs for a single column, occasionally more) // Sorting specifications for a table (often handling sort specs for a single column, occasionally more)
@ -1913,7 +1913,7 @@ struct ImGuiTableSortSpecs
int SpecsCount; // Sort spec count. Most often 1 unless e.g. ImGuiTableFlags_MultiSortable is enabled. int SpecsCount; // Sort spec count. Most often 1 unless e.g. ImGuiTableFlags_MultiSortable is enabled.
bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag. bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag.
ImGuiTableSortSpecs() { Specs = NULL; SpecsCount = 0; SpecsDirty = false; } ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); }
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -1960,6 +1960,7 @@ struct ImGuiTableCellData
ImGuiTableColumnIdx Column; // Column number ImGuiTableColumnIdx Column; // Column number
}; };
// FIXME-TABLES: transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData
struct ImGuiTable struct ImGuiTable
{ {
ImGuiID ID; ImGuiID ID;
@ -2022,7 +2023,8 @@ struct ImGuiTable
ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window)
ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names
ImDrawListSplitter DrawSplitter; // We carry our own ImDrawList splitter to allow recursion (FIXME: could be stored outside, worst case we need 1 splitter per recursing table) ImDrawListSplitter DrawSplitter; // We carry our own ImDrawList splitter to allow recursion (FIXME: could be stored outside, worst case we need 1 splitter per recursing table)
ImVector<ImGuiTableSortSpecsColumn> SortSpecsData; // FIXME-OPT: Fixed-size array / small-vector pattern, optimize for single sort spec ImGuiTableSortSpecsColumn SortSpecsSingle;
ImVector<ImGuiTableSortSpecsColumn> SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would work be good.
ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs()
ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx SortSpecsCount;
ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount)
@ -2280,6 +2282,7 @@ namespace ImGui
IMGUI_API bool TableGetColumnIsEnabled(int column_n = -1); // Return false when column is disabled (hidden) by user (e.g. via context menu, or _DefaultHide flag) IMGUI_API bool TableGetColumnIsEnabled(int column_n = -1); // Return false when column is disabled (hidden) by user (e.g. via context menu, or _DefaultHide flag)
IMGUI_API void TableSetColumnIsEnabled(int column_n, bool enabled); IMGUI_API void TableSetColumnIsEnabled(int column_n, bool enabled);
IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
IMGUI_API float TableGetHeaderRowHeight();
IMGUI_API void TablePushBackgroundChannel(); IMGUI_API void TablePushBackgroundChannel();
IMGUI_API void TablePopBackgroundChannel(); IMGUI_API void TablePopBackgroundChannel();

View File

@ -1715,7 +1715,7 @@ bool ImGui::TableGetColumnIsEnabled(int column_n)
return false; return false;
if (column_n < 0) if (column_n < 0)
column_n = table->CurrentColumn; column_n = table->CurrentColumn;
return (table->EnabledMaskByIndex & ((ImU64)1 << column_n)) == 0; return (table->EnabledMaskByIndex & ((ImU64)1 << column_n)) != 0;
} }
void ImGui::TableSetColumnIsEnabled(int column_n, bool hidden) void ImGui::TableSetColumnIsEnabled(int column_n, bool hidden)
@ -2134,7 +2134,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
} }
} }
// FIXME-TABLE: This is a mess, need to redesign how we render borders. // FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow)
void ImGui::TableDrawBorders(ImGuiTable* table) void ImGui::TableDrawBorders(ImGuiTable* table)
{ {
ImGuiWindow* inner_window = table->InnerWindow; ImGuiWindow* inner_window = table->InnerWindow;
@ -2391,20 +2391,23 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
TableSortSpecsSanitize(table); TableSortSpecsSanitize(table);
// Write output // Write output
table->SortSpecsData.resize(table->SortSpecsCount); const bool single_sort_specs = (table->SortSpecsCount <= 1);
table->SortSpecsMulti.resize(single_sort_specs ? 0 : table->SortSpecsCount);
ImGuiTableSortSpecsColumn* sort_specs = single_sort_specs ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{ {
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
if (column->SortOrder == -1) if (column->SortOrder == -1)
continue; continue;
ImGuiTableSortSpecsColumn* sort_spec = &table->SortSpecsData[column->SortOrder]; IM_ASSERT(column->SortOrder < table->SortSpecsCount);
ImGuiTableSortSpecsColumn* sort_spec = &sort_specs[column->SortOrder];
sort_spec->ColumnUserID = column->UserID; sort_spec->ColumnUserID = column->UserID;
sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
sort_spec->SortDirection = column->SortDirection; sort_spec->SortDirection = column->SortDirection;
} }
table->SortSpecs.Specs = table->SortSpecsData.Data; table->SortSpecs.Specs = sort_specs;
table->SortSpecs.SpecsCount = table->SortSpecsData.Size; table->SortSpecs.SpecsCount = table->SortSpecsCount;
table->SortSpecs.SpecsDirty = true; // Mark as dirty for user table->SortSpecs.SpecsDirty = true; // Mark as dirty for user
table->IsSortSpecsDirty = false; // Mark as not dirty for us table->IsSortSpecsDirty = false; // Mark as not dirty for us
} }
@ -2412,58 +2415,51 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// [SECTION] Tables: Headers // [SECTION] Tables: Headers
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// - TableGetHeaderRowHeight() [Internal]
// - TableHeadersRow() // - TableHeadersRow()
// - TableHeader() // - TableHeader()
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). float ImGui::TableGetHeaderRowHeight()
{
// Caring for a minor edge case:
// Calculate row height, for the unlikely case that some labels may be taller than others.
// If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height.
// In your custom header row you may omit this all together and just call TableNextRow() without a height...
float row_height = GetTextLineHeight();
int columns_count = TableGetColumnCount();
for (int column_n = 0; column_n < columns_count; column_n++)
if (TableGetColumnIsEnabled(column_n))
row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y);
row_height += GetStyle().CellPadding.y * 2.0f;
return row_height;
}
// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
// The intent is that advanced users willing to create customized headers would not need to use this helper // The intent is that advanced users willing to create customized headers would not need to use this helper
// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets. // and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. // See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
void ImGui::TableHeadersRow() void ImGui::TableHeadersRow()
{ {
ImGuiStyle& style = ImGui::GetStyle();
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
// Calculate row height (for the unlikely case that labels may be are multi-line)
// If we didn't do that, uneven header height would work but their highlight won't cover the full row height.
float row_height = GetTextLineHeight();
const float row_y1 = GetCursorScreenPos().y;
const int columns_count = TableGetColumnCount();
for (int column_n = 0; column_n < columns_count; column_n++)
if (TableGetColumnIsEnabled(column_n))
row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y);
row_height += style.CellPadding.y * 2.0f;
// Open row // Open row
const float row_y1 = GetCursorScreenPos().y;
const float row_height = TableGetHeaderRowHeight();
TableNextRow(ImGuiTableRowFlags_Headers, row_height); TableNextRow(ImGuiTableRowFlags_Headers, row_height);
if (table->HostSkipItems) // Merely an optimization, you may skip in your own code. if (table->HostSkipItems) // Merely an optimization, you may skip in your own code.
return; return;
// This for loop is constructed to not make use of internal functions, const int columns_count = TableGetColumnCount();
// as this is intended to be a base template to copy and build from.
for (int column_n = 0; column_n < columns_count; column_n++) for (int column_n = 0; column_n < columns_count; column_n++)
{ {
if (!TableSetColumnIndex(column_n)) if (!TableSetColumnIndex(column_n))
continue; continue;
// [DEBUG] Test custom user elements
#if 0
if (column_n < 2)
{
static bool b[2] = {};
PushID(column_n);
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
Checkbox("##", &b[column_n]);
PopStyleVar();
PopID();
SameLine(0.0f, style.ItemInnerSpacing.x);
}
#endif
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them) // Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
// - in your own code you may omit the PushID/PopID all-together, provided you know they won't collide // - in your own code you may omit the PushID/PopID all-together, provided you know they won't collide
// - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier. // - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier.
@ -2474,7 +2470,6 @@ void ImGui::TableHeadersRow()
} }
// Allow opening popup from the right-most section after the last column. // Allow opening popup from the right-most section after the last column.
// FIXME-TABLE: TableOpenContextMenu() is not public yet.
ImVec2 mouse_pos = ImGui::GetMousePos(); ImVec2 mouse_pos = ImGui::GetMousePos();
if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count) if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count)
if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height) if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height)
@ -2565,9 +2560,8 @@ void ImGui::TableHeader(const char* label)
w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x); w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x);
if (column->SortOrder != -1) if (column->SortOrder != -1)
{ {
w_sort_text = 0.0f;
char sort_order_suf[8]; char sort_order_suf[8];
w_sort_text = 0.0f;
if (column->SortOrder > 0) if (column->SortOrder > 0)
{ {
ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1); ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
@ -2675,11 +2669,11 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table)
const char* size_all_desc; const char* size_all_desc;
if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount) if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount)
size_all_desc = "Size all columns to fit"; // All fixed size_all_desc = "Size all columns to fit###SizeAll"; // All fixed
else if (table->ColumnsEnabledFixedCount == 0) else if (table->ColumnsEnabledFixedCount == 0)
size_all_desc = "Size all columns to default"; // All stretch size_all_desc = "Size all columns to default###SizeAll"; // All stretch
else else
size_all_desc = "Size all columns to fit/default"; // Mixed size_all_desc = "Size all columns to fit/default###SizeAll";// Mixed
if (MenuItem(size_all_desc, NULL)) if (MenuItem(size_all_desc, NULL))
TableSetColumnWidthAutoAll(table); TableSetColumnWidthAutoAll(table);
want_separator = true; want_separator = true;
@ -3074,7 +3068,7 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(table->MemoryCompacted == false); IM_ASSERT(table->MemoryCompacted == false);
table->DrawSplitter.ClearFreeMemory(); table->DrawSplitter.ClearFreeMemory();
table->SortSpecsData.clear(); table->SortSpecsMulti.clear();
table->SortSpecs.Specs = NULL; table->SortSpecs.Specs = NULL;
table->IsSortSpecsDirty = true; table->IsSortSpecsDirty = true;
table->ColumnsNames.clear(); table->ColumnsNames.clear();