diff --git a/imgui.h b/imgui.h index 9641362cc..8fd785f95 100644 --- a/imgui.h +++ b/imgui.h @@ -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) 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) @@ -1913,7 +1913,7 @@ struct ImGuiTableSortSpecs 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. - ImGuiTableSortSpecs() { Specs = NULL; SpecsCount = 0; SpecsDirty = false; } + ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index 7f103dea3..0ab458208 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1960,6 +1960,7 @@ struct ImGuiTableCellData ImGuiTableColumnIdx Column; // Column number }; +// FIXME-TABLES: transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData struct ImGuiTable { ImGuiID ID; @@ -2022,7 +2023,8 @@ struct ImGuiTable ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) 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) - ImVector SortSpecsData; // FIXME-OPT: Fixed-size array / small-vector pattern, optimize for single sort spec + ImGuiTableSortSpecsColumn SortSpecsSingle; + ImVector 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() ImGuiTableColumnIdx SortSpecsCount; 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 void TableSetColumnIsEnabled(int column_n, bool enabled); 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 TablePopBackgroundChannel(); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index c215270af..8e093fb1c 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1715,7 +1715,7 @@ bool ImGui::TableGetColumnIsEnabled(int column_n) return false; if (column_n < 0) 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) @@ -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) { ImGuiWindow* inner_window = table->InnerWindow; @@ -2391,20 +2391,23 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) TableSortSpecsSanitize(table); // 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++) { ImGuiTableColumn* column = &table->Columns[column_n]; if (column->SortOrder == -1) 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->ColumnIndex = (ImGuiTableColumnIdx)column_n; sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; sort_spec->SortDirection = column->SortDirection; } - table->SortSpecs.Specs = table->SortSpecsData.Data; - table->SortSpecs.SpecsCount = table->SortSpecsData.Size; + table->SortSpecs.Specs = sort_specs; + table->SortSpecs.SpecsCount = table->SortSpecsCount; table->SortSpecs.SpecsDirty = true; // Mark as dirty for user table->IsSortSpecsDirty = false; // Mark as not dirty for us } @@ -2412,58 +2415,51 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) //------------------------------------------------------------------------- // [SECTION] Tables: Headers //------------------------------------------------------------------------- +// - TableGetHeaderRowHeight() [Internal] // - TableHeadersRow() // - 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 // 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. +// 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() { - ImGuiStyle& style = ImGui::GetStyle(); - ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; 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 + const float row_y1 = GetCursorScreenPos().y; + const float row_height = TableGetHeaderRowHeight(); TableNextRow(ImGuiTableRowFlags_Headers, row_height); if (table->HostSkipItems) // Merely an optimization, you may skip in your own code. return; - // This for loop is constructed to not make use of internal functions, - // as this is intended to be a base template to copy and build from. + const int columns_count = TableGetColumnCount(); for (int column_n = 0; column_n < columns_count; column_n++) { if (!TableSetColumnIndex(column_n)) 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) // - 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. @@ -2474,7 +2470,6 @@ void ImGui::TableHeadersRow() } // Allow opening popup from the right-most section after the last column. - // FIXME-TABLE: TableOpenContextMenu() is not public yet. ImVec2 mouse_pos = ImGui::GetMousePos(); if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count) 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); if (column->SortOrder != -1) { - w_sort_text = 0.0f; - char sort_order_suf[8]; + w_sort_text = 0.0f; if (column->SortOrder > 0) { 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; 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) - size_all_desc = "Size all columns to default"; // All stretch + size_all_desc = "Size all columns to default###SizeAll"; // All stretch 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)) TableSetColumnWidthAutoAll(table); want_separator = true; @@ -3074,7 +3068,7 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) ImGuiContext& g = *GImGui; IM_ASSERT(table->MemoryCompacted == false); table->DrawSplitter.ClearFreeMemory(); - table->SortSpecsData.clear(); + table->SortSpecsMulti.clear(); table->SortSpecs.Specs = NULL; table->IsSortSpecsDirty = true; table->ColumnsNames.clear();