Browse Source

Tables: Support for multiple Tables using same id where most settings are synced.

(some minor one-frame lack of sync when e.g. toggling visibility in context menu)
omar 5 years ago
parent
commit
e06a36ab12
2 changed files with 58 additions and 27 deletions
  1. 6 1
      imgui_internal.h
  2. 52 26
      imgui_tables.cpp

+ 6 - 1
imgui_internal.h

@@ -1891,6 +1891,8 @@ struct ImGuiTable
     int                         ColumnsActiveCount;         // Number of non-hidden columns (<= ColumnsCount)
     int                         CurrentColumn;
     int                         CurrentRow;
+    ImS16                       InstanceNo;                 // Count of BeginTable() calls with same ID in the same frame (generally 0)
+    ImS16                       InstanceInteracted;
     float                       RowPosY1;
     float                       RowPosY2;
     float                       RowTextBaseline;
@@ -1910,6 +1912,7 @@ struct ImGuiTable
     float                       LastFirstRowHeight;         // Height of first row from last frame
     float                       ColumnsTotalWidth;
     float                       InnerWidth;
+    float                       ResizedColumnNextWidth;
     ImRect                      OuterRect;                  // Note: OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().
     ImRect                      WorkRect;
     ImRect                      HostClipRect;               // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.
@@ -1925,7 +1928,8 @@ struct ImGuiTable
     ImS8                        DeclColumnsCount;           // Count calls to TableSetupColumn()
     ImS8                        HoveredColumnBody;          // [DEBUG] Unlike HoveredColumnBorder this doesn't fulfill all Hovering rules properly. Used for debugging/tools for now.
     ImS8                        HoveredColumnBorder;        // Index of column whose right-border is being hovered (for resizing).
-    ImS8                        ResizedColumn;              // Index of column being resized.
+    ImS8                        ResizedColumn;              // Index of column being resized. Reset by InstanceNo==0.
+    ImS8                        HeadHeaderColumn;           // Index of column header being held. 
     ImS8                        LastResizedColumn;
     ImS8                        ReorderColumn;              // Index of column being reordered. (not cleared)
     ImS8                        ReorderColumnDir;           // -1 or +1
@@ -1957,6 +1961,7 @@ struct ImGuiTable
     {
         memset(this, 0, sizeof(*this));
         SettingsOffset = -1;
+        InstanceInteracted = -1;
         LastFrameActive = -1;
         LastResizedColumn = -1;
         ContextPopupColumn = -1;

+ 52 - 26
imgui_tables.cpp

@@ -158,9 +158,8 @@ bool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags
     ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
     ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
 
-    // If an outer size is specified ahead we will be able to early out when not visible,
-    // The exact rules here can evolve.
-    if (use_child_window && IsClippedEx(outer_rect, id, false))
+    // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping rules may evolve.
+    if (use_child_window && IsClippedEx(outer_rect, 0, false))
     {
         ItemSize(outer_rect);
         return false;
@@ -173,9 +172,16 @@ bool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags
     // Acquire storage for the table
     ImGuiTable* table = g.Tables.GetOrAddByKey(id);
     const ImGuiTableFlags table_last_flags = table->Flags;
+    const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceNo + 1;
+    const ImGuiID instance_id = id + instance_no;
+    if (instance_no > 0)
+        IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID");
+
+    // Initialize
     table->ID = id;
     table->Flags = flags;
     table->IsFirstFrame = (table->LastFrameActive == -1);
+    table->InstanceNo = (ImS16)instance_no;
     table->LastFrameActive = g.FrameCount;
     table->OuterWindow = table->InnerWindow = outer_window;
     table->ColumnsCount = columns_count;
@@ -203,7 +209,7 @@ bool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags
 
         // Create scrolling region (without border = zero window padding)
         ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
-        BeginChildEx(str_id, id, table->OuterRect.GetSize(), false, child_flags);
+        BeginChildEx(str_id, instance_id, table->OuterRect.GetSize(), false, child_flags);
         table->InnerWindow = g.CurrentWindow;
         table->WorkRect = table->InnerWindow->WorkRect;
         table->OuterRect = table->InnerWindow->Rect();
@@ -211,7 +217,7 @@ bool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags
     else
     {
         // WorkRect.Max will grow as we append contents.
-        PushID(id);
+        PushID(instance_id);
     }
 
     const bool has_cell_padding_x = (flags & (ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV)) != 0;
@@ -244,7 +250,6 @@ bool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags
     table->HoveredColumnBody = -1;
     table->HoveredColumnBorder = -1;
     table->RightMostActiveColumn = -1;
-    table->LeftMostStretchedColumnDisplayOrder = -1;
     table->IsFirstFrame = false;
 
     // FIXME-TABLE FIXME-STYLE: Using opaque colors facilitate overlapping elements of the grid
@@ -291,18 +296,36 @@ bool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags
     if (table->IsFirstFrame || table->IsSettingsRequestLoad)
         TableLoadSettings(table);
 
+    // Handle resizing request
+    // (We process this at the first beginning of the frame)
+    // FIXME-TABLE: Preserve contents width _while resizing down_ until releasing.
+    // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling.
+    if (table->InstanceNo == 0)
+    {
+        if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX)
+            TableSetColumnWidth(table, &table->Columns[table->ResizedColumn], table->ResizedColumnNextWidth);
+        table->ResizedColumnNextWidth = FLT_MAX;
+        table->ResizedColumn = -1;
+    }
+
     // Handle reordering request
     // Note: we don't clear ReorderColumn after handling the request.
-    if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0)
-    {
-        IM_ASSERT(table->ReorderColumnDir == -1 || table->ReorderColumnDir == +1);
-        IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable);
-        ImGuiTableColumn* dragged_column = &table->Columns[table->ReorderColumn];
-        ImGuiTableColumn* target_column = &table->Columns[(table->ReorderColumnDir == -1) ? dragged_column->PrevActiveColumn : dragged_column->NextActiveColumn];
-        ImSwap(table->DisplayOrder[dragged_column->IndexDisplayOrder], table->DisplayOrder[target_column->IndexDisplayOrder]);
-        ImSwap(dragged_column->IndexDisplayOrder, target_column->IndexDisplayOrder);
-        table->ReorderColumnDir = 0;
-        table->IsSettingsDirty = true;
+    if (table->InstanceNo == 0)
+    {
+        if (table->HeadHeaderColumn == -1 && table->ReorderColumn != -1)
+            table->ReorderColumn = -1;
+        table->HeadHeaderColumn = -1;
+        if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0)
+        {
+            IM_ASSERT(table->ReorderColumnDir == -1 || table->ReorderColumnDir == +1);
+            IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable);
+            ImGuiTableColumn* dragged_column = &table->Columns[table->ReorderColumn];
+            ImGuiTableColumn* target_column = &table->Columns[(table->ReorderColumnDir == -1) ? dragged_column->PrevActiveColumn : dragged_column->NextActiveColumn];
+            ImSwap(table->DisplayOrder[dragged_column->IndexDisplayOrder], table->DisplayOrder[target_column->IndexDisplayOrder]);
+            ImSwap(dragged_column->IndexDisplayOrder, target_column->IndexDisplayOrder);
+            table->ReorderColumnDir = 0;
+            table->IsSettingsDirty = true;
+        }
     }
 
     // Handle display order reset request
@@ -713,7 +736,7 @@ void    ImGui::TableUpdateLayout(ImGuiTable* table)
     table->IsUsingHeaders = false;
 
     // Context menu
-    if (table->IsContextPopupOpen)
+    if (table->IsContextPopupOpen && table->InstanceNo == table->InstanceInteracted)
     {
         if (BeginPopup("##TableContextMenu"))
         {
@@ -763,7 +786,7 @@ void    ImGui::TableUpdateBorders(ImGuiTable* table)
         if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_))
             continue;
 
-        ImGuiID column_id = table->ID + (ImGuiID)column_n;
+        ImGuiID column_id = table->ID + (table->InstanceNo * table->ColumnsCount) + column_n;
         ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, hit_y2);
         //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));
         KeepAliveID(column_id);
@@ -771,7 +794,10 @@ void    ImGui::TableUpdateBorders(ImGuiTable* table)
         bool hovered = false, held = false;
         ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
         if (held)
+        {
             table->ResizedColumn = (ImS8)column_n;
+            table->InstanceInteracted = table->InstanceNo;
+        }
         if ((hovered && g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) || held)
         {
             table->HoveredColumnBorder = (ImS8)column_n;
@@ -861,14 +887,12 @@ void    ImGui::EndTable()
     }
 
     // Apply resizing/dragging at the end of the frame
-    // FIXME-TABLE: Preserve contents width _while resizing down_ until releasing.
-    // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling.
     if (table->ResizedColumn != -1)
     {
         ImGuiTableColumn* column = &table->Columns[table->ResizedColumn];
         const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS);
         const float new_width = ImFloor(new_x2 - column->MinX);
-        TableSetColumnWidth(table, column, new_width);
+        table->ResizedColumnNextWidth = new_width;
     }
 
     // Layout in outer window
@@ -940,7 +964,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
             const int column_n = table->DisplayOrder[order_n];
             ImGuiTableColumn* column = &table->Columns[column_n];
             const bool is_hovered = (table->HoveredColumnBorder == column_n);
-            const bool is_resized = (table->ResizedColumn == column_n);
+            const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceNo);
             const bool draw_right_border = (column->MaxX <= table->InnerClipRect.Max.x) || (is_resized || is_hovered);
             if (draw_right_border && column->MaxX > column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size..
             {
@@ -1724,7 +1748,7 @@ void    ImGui::TableAutoHeaders()
         // [DEBUG]
         //if (g.IO.KeyCtrl) { static char buf[32]; name = buf; ImGuiTableColumn* c = &table->Columns[column_n]; if (c->Flags & ImGuiTableColumnFlags_WidthStretch) ImFormatString(buf, 32, "%.3f>%.1f", c->ResizeWeight, c->WidthGiven); else ImFormatString(buf, 32, "%.1f", c->WidthGiven); }
 
-        PushID(column_n); // Allow unnamed labels (generally accidental, but let's behave nicely with them)
+        PushID(table->InstanceNo * table->ColumnsCount + column_n); // Allow unnamed labels (generally accidental, but let's behave nicely with them)
         TableHeader(name);
         PopID();
 
@@ -1767,6 +1791,7 @@ void    ImGui::TableAutoHeaders()
     {
         table->IsContextPopupOpen = true;
         table->ContextPopupColumn = (ImS8)open_context_popup;
+        table->InstanceInteracted = table->InstanceNo;
         OpenPopup("##TableContextMenu");
     }
 }
@@ -1807,9 +1832,11 @@ void    ImGui::TableHeader(const char* label)
     //window->DC.CursorPos.x = column->MinX + table->CellPadding.x;
 
     // Keep header highlighted when context menu is open. (FIXME-TABLE: however we cannot assume the ID of said popup if it has been created by the user...)
-    const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n);
+    const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceNo);
     const bool pressed = Selectable("", selected, ImGuiSelectableFlags_DrawHoveredWhenHeld, ImVec2(0.0f, row_height));
     const bool held = IsItemActive();
+    if (held)
+        table->HeadHeaderColumn = (ImS8)column_n;
     window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
 
     // Drag and drop: re-order columns. Frozen columns are not reorderable.
@@ -1818,6 +1845,7 @@ void    ImGui::TableHeader(const char* label)
     {
         // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x
         table->ReorderColumn = (ImS8)column_n;
+        table->InstanceInteracted = table->InstanceNo;
         if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)
             if (column->PrevActiveColumn != -1 && (column->IndexWithinActiveSet < table->FreezeColumnsRequest) == (table->Columns[column->PrevActiveColumn].IndexWithinActiveSet < table->FreezeColumnsRequest))
                 table->ReorderColumnDir = -1;
@@ -1863,8 +1891,6 @@ void    ImGui::TableHeader(const char* label)
         if (pressed && table->ReorderColumn != column_n)
             TableSortSpecsClickColumn(table, column, g.IO.KeyShift);
     }
-    if (!held && table->ReorderColumn == column_n)
-        table->ReorderColumn = -1;
 
     // Render clipped label
     // Clipping here ensure that in the majority of situations, all our header cells will be merged into a single draw call.