Tables: Support for multi-line columns name. Renaming of some fields from BackupXXX to HostXXX. Comments.
diff --git a/imgui.cpp b/imgui.cpp
index 90ff9ef..c8023fd 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -2142,7 +2142,7 @@
static bool GetSkipItemForListClipping()
{
ImGuiContext& g = *GImGui;
- return (g.CurrentTable ? g.CurrentTable->BackupSkipItems : g.CurrentWindow->SkipItems);
+ return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
}
// Helper to calculate coarse clipping of large list of evenly sized items.
diff --git a/imgui.h b/imgui.h
index 3577fe2..e0f5127 100644
--- a/imgui.h
+++ b/imgui.h
@@ -693,7 +693,8 @@
IMGUI_API void TableHeader(const char* label); // submit one header cell manually.
// Tables: Sorting
// - Call TableGetSortSpecs() to retrieve latest sort specs for the table. Return value will be NULL if no sorting.
- // - Read ->SpecsChanged to tell if the specs have changed since last call.
+ // - You can sort your data again when 'SpecsChanged == true'. It will be true with sorting specs have changed since last call, or the first time.
+ // - Don't hold on this structure over multiple frames.
IMGUI_API const ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting).
// Tab Bars, Tabs
@@ -1856,7 +1857,7 @@
{
const ImGuiTableSortSpecsColumn* Specs; // Pointer to sort spec array.
int SpecsCount; // Sort spec count. Most often 1 unless e.g. ImGuiTableFlags_MultiSortable is enabled.
- bool SpecsChanged; // Set to true by TableGetSortSpecs() call if the specs have changed since the previous call.
+ bool SpecsChanged; // Set to true by TableGetSortSpecs() call if the specs have changed since the previous call. Use this to sort again!
ImU64 ColumnsMask; // Set to the mask of column indexes included in the Specs array. e.g. (1 << N) when column N is sorted.
ImGuiTableSortSpecs() { Specs = NULL; SpecsCount = 0; SpecsChanged = false; ColumnsMask = 0x00; }
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 25d035b..f1c40e0 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -3916,6 +3916,7 @@
{
MyItem::s_current_sort_specs = sorts_specs; // Store in variable accessible by the sort function.
qsort(&items[0], (size_t)items.Size, sizeof(items[0]), MyItem::CompareWithSortSpecs);
+ MyItem::s_current_sort_specs = NULL;
}
// Display data
@@ -4104,6 +4105,7 @@
{
MyItem::s_current_sort_specs = sorts_specs; // Store in variable accessible by the sort function.
qsort(&items[0], (size_t)items.Size, sizeof(items[0]), MyItem::CompareWithSortSpecs);
+ MyItem::s_current_sort_specs = NULL;
}
items_need_sort = false;
diff --git a/imgui_internal.h b/imgui_internal.h
index f897815..9b980e0 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1918,9 +1918,11 @@
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.
ImRect InnerClipRect;
ImRect BackgroundClipRect; // We use this to cpu-clip cell background color fill
+ 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.
+ ImRect HostWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable()
+ ImVec2 HostCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()
ImGuiWindow* OuterWindow; // Parent window for the table
ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window)
ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names
@@ -1956,9 +1958,7 @@
bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
bool IsResetDisplayOrderRequest;
bool IsFreezeRowsPassed; // Set when we got past the frozen row (the first one).
- bool BackupSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis
- ImRect BackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable()
- ImVec2 BackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()
+ bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis
ImGuiTable()
{
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index b17c136..24519c9 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -232,6 +232,13 @@
PushID(instance_id);
}
+ // Backup a copy of host window members we will modify
+ ImGuiWindow* inner_window = table->InnerWindow;
+ table->HostClipRect = inner_window->ClipRect;
+ table->HostSkipItems = inner_window->SkipItems;
+ table->HostWorkRect = inner_window->WorkRect;
+ table->HostCursorMaxPos = inner_window->DC.CursorMaxPos;
+
// Borders
// - None ........Content..... Pad .....Content........
// - VOuter | Pad ..Content..... Pad .....Content.. Pad | // FIXME-TABLE: Not handled properly
@@ -239,7 +246,6 @@
// - VOuter+VInner | Pad ..Content.. Pad | Pad ..Content.. Pad |
const bool has_cell_padding_x = (flags & ImGuiTableFlags_BordersVOuter) != 0;
- ImGuiWindow* inner_window = table->InnerWindow;
table->CellPaddingX1 = has_cell_padding_x ? g.Style.CellPadding.x + 1.0f : 0.0f;
table->CellPaddingX2 = has_cell_padding_x ? g.Style.CellPadding.x : 0.0f;
table->CellPaddingY = g.Style.CellPadding.y;
@@ -249,7 +255,6 @@
table->CurrentRow = -1;
table->RowBgColorCounter = 0;
table->LastRowFlags = ImGuiTableRowFlags_None;
- table->HostClipRect = inner_window->ClipRect;
table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;
table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width
table->InnerClipRect.ClipWith(table->HostClipRect);
@@ -306,11 +311,6 @@
if (table->IsSettingsRequestLoad)
TableLoadSettings(table);
- // Grab a copy of window fields we will modify
- table->BackupSkipItems = inner_window->SkipItems;
- table->BackupWorkRect = inner_window->WorkRect;
- table->BackupCursorMaxPos = inner_window->DC.CursorMaxPos;
-
// Disable output until user calls TableNextRow() or TableNextCell() leading to the TableUpdateLayout() call..
// This is not strictly necessary but will reduce cases were misleading "out of table" output will be confusing to the user.
// Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.
@@ -756,7 +756,7 @@
}
// Don't decrement auto-fit counters until container window got a chance to submit its items
- if (table->BackupSkipItems == false)
+ if (table->HostSkipItems == false)
{
column->AutoFitQueue >>= 1;
column->CannotSkipItemsQueue >>= 1;
@@ -896,8 +896,8 @@
TableEndRow(table);
// Finalize table height
- inner_window->SkipItems = table->BackupSkipItems;
- inner_window->DC.CursorMaxPos = table->BackupCursorMaxPos;
+ inner_window->SkipItems = table->HostSkipItems;
+ inner_window->DC.CursorMaxPos = table->HostCursorMaxPos;
if (inner_window != outer_window)
{
table->OuterRect.Max.y = ImMax(table->OuterRect.Max.y, inner_window->Pos.y + inner_window->Size.y);
@@ -970,8 +970,8 @@
}
// Layout in outer window
- inner_window->WorkRect = table->BackupWorkRect;
- inner_window->SkipItems = table->BackupSkipItems;
+ inner_window->WorkRect = table->HostWorkRect;
+ inner_window->SkipItems = table->HostSkipItems;
outer_window->DC.CursorPos = table->OuterRect.Min;
outer_window->DC.ColumnsOffset.x = 0.0f;
if (inner_window != outer_window)
@@ -1594,7 +1594,7 @@
// FIXME-COLUMNS: Setup baseline, preserve across columns (how can we obtain first line baseline tho..)
// window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y);
- window->SkipItems = column->IsClipped ? true : table->BackupSkipItems;
+ window->SkipItems = column->IsClipped ? true : table->HostSkipItems;
if (table->Flags & ImGuiTableFlags_NoClipX)
{
table->DrawSplitter.SetCurrentChannel(window->DrawList, 1);
@@ -1828,15 +1828,23 @@
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableAutoHeaders() after BeginTable()!");
+ const int columns_count = table->ColumnsCount;
- TableNextRow(ImGuiTableRowFlags_Headers, GetTextLineHeight() + g.Style.CellPadding.y * 2.0f);
+ // Calculate row height (for the unlikely case that labels may be are multi-line)
+ float row_height = GetTextLineHeight();
+ for (int column_n = 0; column_n < columns_count; column_n++)
+ if (TableGetColumnIsVisible(column_n))
+ row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y);
+ row_height += g.Style.CellPadding.y * 2.0f;
+
+ // Open row
+ TableNextRow(ImGuiTableRowFlags_Headers, row_height);
if (window->SkipItems)
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.
int open_context_popup = INT_MAX;
- const int columns_count = table->ColumnsCount;
for (int column_n = 0; column_n < columns_count; column_n++)
{
if (!TableSetColumnIndex(column_n))
@@ -1873,7 +1881,7 @@
// FIXME-TABLE: This is not user-land code any more...
// FIXME-TABLE: Need to explain why this is here!
- window->SkipItems = table->BackupSkipItems;
+ window->SkipItems = table->HostSkipItems;
// Allow opening popup from the right-most section after the last column
// FIXME-TABLE: This is not user-land code any more... perhaps instead we should expose hovered column.
@@ -1918,6 +1926,7 @@
// 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.
// FIXME-TABLE: Should hold a selection state.
+// FIXME-TABLE: Style confusion between CellPadding.y and FramePadding.y
void ImGui::TableHeader(const char* label)
{
ImGuiContext& g = *GImGui;
@@ -1931,19 +1940,21 @@
const int column_n = table->CurrentColumn;
ImGuiTableColumn* column = &table->Columns[column_n];
- float row_height = GetTextLineHeight();
- ImRect cell_r = TableGetCellRect();
- //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
- ImRect work_r = cell_r;
- work_r.Min.x = window->DC.CursorPos.x;
- work_r.Max.y = work_r.Min.y + row_height;
-
// 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.
+ ImRect cell_r = TableGetCellRect();
+ float label_height = ImMax(label_size.y, cell_r.GetHeight() - g.Style.CellPadding.y * 2.0f);
+
+ //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
+ ImRect work_r = cell_r;
+ work_r.Min.x = window->DC.CursorPos.x;
+ work_r.Max.y = work_r.Min.y + label_height;
float ellipsis_max = work_r.Max.x;
// Selectable
@@ -1954,7 +1965,7 @@
// 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 && table->InstanceInteracted == table->InstanceNo);
- const bool pressed = Selectable("", selected, ImGuiSelectableFlags_DrawHoveredWhenHeld, ImVec2(0.0f, row_height));
+ const bool pressed = Selectable("", selected, ImGuiSelectableFlags_DrawHoveredWhenHeld, ImVec2(0.0f, label_height));
const bool held = IsItemActive();
if (held)
table->HeldHeaderColumn = (ImS8)column_n;
@@ -2016,7 +2027,7 @@
// 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 + row_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
+ 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);
// We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
// FIXME-TABLE: Clarify policies of how label width and potential decorations (arrows) fit into auto-resize of the column
@@ -2066,7 +2077,7 @@
}
// Return NULL if no sort specs.
-// Return ->WantSort == true when the specs have changed since the last query.
+// You can sort your data again when 'SpecsChanged == true'.It will be true with sorting specs have changed since last call, or the first time.
const ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
{
ImGuiContext& g = *GImGui;