Browse Source

Tables: moved TableHeadersRow(), TableHeader() to their own section.

ocornut 4 years ago
parent
commit
d6b1f0d13d
1 changed files with 225 additions and 207 deletions
  1. 225 207
      imgui_tables.cpp

+ 225 - 207
imgui_tables.cpp

@@ -2172,213 +2172,6 @@ void    ImGui::TablePopBackgroundChannel()
     table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
 }
 
-// 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.
-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
-    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.
-    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.
-        const char* name = TableGetColumnName(column_n);
-        PushID(table->InstanceCurrent * table->ColumnsCount + column_n);
-        TableHeader(name);
-        PopID();
-    }
-
-    // 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)
-            TableOpenContextMenu(-1); // Will open a non-column-specific popup.
-}
-
-// Emit a column header (text + optional sort order)
-// We cpu-clip text here so that all columns headers can be merged into a same draw call.
-// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()
-// FIXME-TABLE: Style confusion between CellPadding.y and FramePadding.y
-void    ImGui::TableHeader(const char* label)
-{
-    ImGuiContext& g = *GImGui;
-    ImGuiWindow* window = g.CurrentWindow;
-    if (window->SkipItems)
-        return;
-
-    ImGuiTable* table = g.CurrentTable;
-    IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!");
-    IM_ASSERT(table->CurrentColumn != -1);
-    const int column_n = table->CurrentColumn;
-    ImGuiTableColumn* column = &table->Columns[column_n];
-
-    // Label
-    if (label == NULL)
-        label = "";
-    const char* label_end = FindRenderedTextEnd(label);
-    ImVec2 label_size = CalcTextSize(label, label_end, true);
-    ImVec2 label_pos = window->DC.CursorPos;
-
-    // If we already got a row height, there's use that.
-    // FIXME-TABLE-PADDING: Problem if the correct outer-padding CellBgRect strays off our ClipRect
-    ImRect cell_r = TableGetCellBgRect(table, column_n);
-    float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f);
-
-    // Keep header highlighted when context menu is open.
-    const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent);
-    ImGuiID id = window->GetID(label);
-    ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));
-    ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
-    if (!ItemAdd(bb, id))
-        return;
-
-    //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
-    //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
-
-    bool hovered, held;
-    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
-    if (hovered || selected)
-    {
-        const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
-        //RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
-        TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn);
-        RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
-    }
-    if (held)
-        table->HeldHeaderColumn = (ImS8)column_n;
-    window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
-
-    // Drag and drop to re-order columns.
-    // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone.
-    if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive)
-    {
-        // 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->InstanceCurrent;
-
-        // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder.
-        if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)
-            if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL)
-                if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder))
-                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
-                        table->ReorderColumnDir = -1;
-        if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x)
-            if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL)
-                if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder))
-                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
-                        table->ReorderColumnDir = +1;
-    }
-
-    // Sort order arrow
-    float w_arrow = 0.0f;
-    float w_sort_text = 0.0f;
-    float ellipsis_max = cell_r.Max.x;
-    if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
-    {
-        const float ARROW_SCALE = 0.65f;
-        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];
-            if (column->SortOrder > 0)
-            {
-                ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
-                w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;
-            }
-
-            float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);
-            ellipsis_max -= w_arrow + w_sort_text;
-
-            float y = label_pos.y;
-            ImU32 col = GetColorU32(ImGuiCol_Text);
-            if (column->SortOrder > 0)
-            {
-                PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f));
-                RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf);
-                PopStyleColor();
-                x += w_sort_text;
-            }
-            RenderArrow(window->DrawList, ImVec2(x, y), col, column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);
-        }
-
-        // Handle clicking on column header to adjust Sort Order
-        if (pressed && table->ReorderColumn != column_n)
-        {
-            // Set new sort direction
-            // - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click.
-            // - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op.
-            ImGuiSortDirection sort_direction;
-            if (column->SortOrder == -1)
-                sort_direction = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending;
-            else
-                sort_direction = (column->SortDirection == ImGuiSortDirection_Ascending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending;
-            TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift);
-        }
-    }
-
-    // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
-    // be merged into a single draw call.
-    //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
-    RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
-
-    const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
-    if (text_clipped && hovered && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay)
-        SetTooltip("%.*s", (int)(label_end - label), label);
-
-    // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
-    float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
-    column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX);
-    column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
-
-    // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
-    if (IsMouseReleased(1) && IsItemHovered())
-        TableOpenContextMenu(column_n);
-}
-
 // Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert
 // the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code.
 void ImGui::TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs)
@@ -2497,6 +2290,17 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int colum
     }
 }
 
+//-------------------------------------------------------------------------
+// [SECTION] Tables: Sorting
+//-------------------------------------------------------------------------
+// - TableGetSortSpecs()
+// - TableGetColumnIsSorted()
+// - TableFixColumnSortDirection() [Internal]
+// - TableSetColumnSortDirection() [Internal]
+// - TableSortSpecsSanitize() [Internal]
+// - TableSortSpecsBuild() [Internal]
+//-------------------------------------------------------------------------
+
 void ImGui::TableSortSpecsSanitize(ImGuiTable* table)
 {
     IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);
@@ -2587,6 +2391,220 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
     table->IsSortSpecsDirty = false; // Mark as not dirty for us
 }
 
+//-------------------------------------------------------------------------
+// [SECTION] Tables: Headers
+//-------------------------------------------------------------------------
+// - TableHeadersRow()
+// - TableHeader()
+//-------------------------------------------------------------------------
+
+// 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.
+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
+    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.
+    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.
+        const char* name = TableGetColumnName(column_n);
+        PushID(table->InstanceCurrent * table->ColumnsCount + column_n);
+        TableHeader(name);
+        PopID();
+    }
+
+    // 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)
+            TableOpenContextMenu(-1); // Will open a non-column-specific popup.
+}
+
+// Emit a column header (text + optional sort order)
+// We cpu-clip text here so that all columns headers can be merged into a same draw call.
+// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()
+// FIXME-TABLE: Style confusion between CellPadding.y and FramePadding.y
+void    ImGui::TableHeader(const char* label)
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+    if (window->SkipItems)
+        return;
+
+    ImGuiTable* table = g.CurrentTable;
+    IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!");
+    IM_ASSERT(table->CurrentColumn != -1);
+    const int column_n = table->CurrentColumn;
+    ImGuiTableColumn* column = &table->Columns[column_n];
+
+    // Label
+    if (label == NULL)
+        label = "";
+    const char* label_end = FindRenderedTextEnd(label);
+    ImVec2 label_size = CalcTextSize(label, label_end, true);
+    ImVec2 label_pos = window->DC.CursorPos;
+
+    // If we already got a row height, there's use that.
+    // FIXME-TABLE-PADDING: Problem if the correct outer-padding CellBgRect strays off our ClipRect
+    ImRect cell_r = TableGetCellBgRect(table, column_n);
+    float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f);
+
+    // Keep header highlighted when context menu is open.
+    const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent);
+    ImGuiID id = window->GetID(label);
+    ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));
+    ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
+    if (!ItemAdd(bb, id))
+        return;
+
+    //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
+    //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
+
+    bool hovered, held;
+    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
+    if (hovered || selected)
+    {
+        const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
+        //RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
+        TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn);
+        RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
+    }
+    if (held)
+        table->HeldHeaderColumn = (ImS8)column_n;
+    window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
+
+    // Drag and drop to re-order columns.
+    // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone.
+    if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive)
+    {
+        // 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->InstanceCurrent;
+
+        // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder.
+        if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)
+            if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL)
+                if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder))
+                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
+                        table->ReorderColumnDir = -1;
+        if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x)
+            if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL)
+                if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder))
+                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
+                        table->ReorderColumnDir = +1;
+    }
+
+    // Sort order arrow
+    float w_arrow = 0.0f;
+    float w_sort_text = 0.0f;
+    float ellipsis_max = cell_r.Max.x;
+    if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
+    {
+        const float ARROW_SCALE = 0.65f;
+        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];
+            if (column->SortOrder > 0)
+            {
+                ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
+                w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;
+            }
+
+            float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);
+            ellipsis_max -= w_arrow + w_sort_text;
+
+            float y = label_pos.y;
+            ImU32 col = GetColorU32(ImGuiCol_Text);
+            if (column->SortOrder > 0)
+            {
+                PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f));
+                RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf);
+                PopStyleColor();
+                x += w_sort_text;
+            }
+            RenderArrow(window->DrawList, ImVec2(x, y), col, column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);
+        }
+
+        // Handle clicking on column header to adjust Sort Order
+        if (pressed && table->ReorderColumn != column_n)
+        {
+            // Set new sort direction
+            // - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click.
+            // - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op.
+            ImGuiSortDirection sort_direction;
+            if (column->SortOrder == -1)
+                sort_direction = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending;
+            else
+                sort_direction = (column->SortDirection == ImGuiSortDirection_Ascending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending;
+            TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift);
+        }
+    }
+
+    // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
+    // be merged into a single draw call.
+    //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
+    RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
+
+    const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
+    if (text_clipped && hovered && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay)
+        SetTooltip("%.*s", (int)(label_end - label), label);
+
+    // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
+    float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
+    column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX);
+    column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
+
+    // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
+    if (IsMouseReleased(1) && IsItemHovered())
+        TableOpenContextMenu(column_n);
+}
+
 //-------------------------------------------------------------------------
 // [SECTION] Tables: Context Menu
 //-------------------------------------------------------------------------