Browse Source

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

ocornut 4 years ago
parent
commit
155b8bb816
3 changed files with 42 additions and 45 deletions
  1. 2 2
      imgui.h
  2. 4 1
      imgui_internal.h
  3. 36 42
      imgui_tables.cpp

+ 2 - 2
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)); }
 };
 
 //-----------------------------------------------------------------------------

+ 4 - 1
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<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()
     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();
 

+ 36 - 42
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();