Tables: distinguishing per-column IsVisible from IsRequestOutput which is returned to user. Clarified clipping rules/requirements. Comments.

This commit is contained in:
ocornut 2020-12-03 16:29:08 +01:00
parent f70bf69e3b
commit 984c4cb5f8
4 changed files with 124 additions and 66 deletions

View File

@ -669,7 +669,7 @@ namespace ImGui
// TableNextColumn() will automatically wrap-around into the next row if needed. // TableNextColumn() will automatically wrap-around into the next row if needed.
// - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column! // - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column!
// - Both TableSetColumnIndex() and TableNextColumn() return false when the column is not visible, so you can // - Both TableSetColumnIndex() and TableNextColumn() return false when the column is not visible, so you can
// skip submitting the contents of a cell BUT ONLY if you know the contents is not going to alter row height. // skip submitting the contents of a cell BUT ONLY if you know it is not going to contribute to row height.
// In many situations, you may skip submitting contents for every columns but one (e.g. the first one). // In many situations, you may skip submitting contents for every columns but one (e.g. the first one).
// - Summary of possible call flow: // - Summary of possible call flow:
// ---------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------
@ -1055,7 +1055,7 @@ enum ImGuiTableFlags_
ImGuiTableFlags_None = 0, ImGuiTableFlags_None = 0,
ImGuiTableFlags_Resizable = 1 << 0, // Allow resizing columns. ImGuiTableFlags_Resizable = 1 << 0, // Allow resizing columns.
ImGuiTableFlags_Reorderable = 1 << 1, // Allow reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) ImGuiTableFlags_Reorderable = 1 << 1, // Allow reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers)
ImGuiTableFlags_Hideable = 1 << 2, // Allow hiding columns in context menu. ImGuiTableFlags_Hideable = 1 << 2, // Allow hiding/disabling columns in context menu.
ImGuiTableFlags_Sortable = 1 << 3, // Allow sorting on one column (sort_specs_count will always be == 1). Call TableGetSortSpecs() to obtain sort specs. ImGuiTableFlags_Sortable = 1 << 3, // Allow sorting on one column (sort_specs_count will always be == 1). Call TableGetSortSpecs() to obtain sort specs.
ImGuiTableFlags_MultiSortable = 1 << 4, // Allow sorting on multiple columns by holding Shift (sort_specs_count may be > 1). Call TableGetSortSpecs() to obtain sort specs. ImGuiTableFlags_MultiSortable = 1 << 4, // Allow sorting on multiple columns by holding Shift (sort_specs_count may be > 1). Call TableGetSortSpecs() to obtain sort specs.
ImGuiTableFlags_NoSavedSettings = 1 << 5, // Disable persisting columns order, width and sort settings in the .ini file. ImGuiTableFlags_NoSavedSettings = 1 << 5, // Disable persisting columns order, width and sort settings in the .ini file.
@ -1095,14 +1095,14 @@ enum ImGuiTableFlags_
enum ImGuiTableColumnFlags_ enum ImGuiTableColumnFlags_
{ {
ImGuiTableColumnFlags_None = 0, ImGuiTableColumnFlags_None = 0,
ImGuiTableColumnFlags_DefaultHide = 1 << 0, // Default as a hidden column. ImGuiTableColumnFlags_DefaultHide = 1 << 0, // Default as a hidden/disabled column.
ImGuiTableColumnFlags_DefaultSort = 1 << 1, // Default as a sorting column. ImGuiTableColumnFlags_DefaultSort = 1 << 1, // Default as a sorting column.
ImGuiTableColumnFlags_WidthStretch = 1 << 2, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _ColumnsWidthStretch). ImGuiTableColumnFlags_WidthStretch = 1 << 2, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _ColumnsWidthStretch).
ImGuiTableColumnFlags_WidthFixed = 1 << 3, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _ColumnsWidthFixed and table is resizable). ImGuiTableColumnFlags_WidthFixed = 1 << 3, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _ColumnsWidthFixed and table is resizable).
ImGuiTableColumnFlags_WidthAutoResize = 1 << 4, // Column will not stretch and keep resizing based on submitted contents (default if table sizing policy is _ColumnsWidthFixed and table is not resizable). ImGuiTableColumnFlags_WidthAutoResize = 1 << 4, // Column will not stretch and keep resizing based on submitted contents (default if table sizing policy is _ColumnsWidthFixed and table is not resizable).
ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing.
ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column.
ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide this column. ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column.
ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command). ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command).
ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).
ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction.

View File

@ -3518,9 +3518,7 @@ static void ShowDemoWindowTables()
ImGui::TableNextRow(); ImGui::TableNextRow();
for (int column = 0; column < 3; column++) for (int column = 0; column < 3; column++)
{ {
if (!ImGui::TableSetColumnIndex(column)) ImGui::TableSetColumnIndex(column);
continue;
char buf[32]; char buf[32];
sprintf(buf, "Hello %d,%d", column, row); sprintf(buf, "Hello %d,%d", column, row);
if (contents_type == CT_Text) if (contents_type == CT_Text)
@ -3861,20 +3859,22 @@ static void ShowDemoWindowTables()
{ {
ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
ImGui::TableSetupColumn("Line #", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze() ImGui::TableSetupColumn("Line #", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze()
ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None); ImGui::TableSetupColumn("One");
ImGui::TableSetupColumn("Two", ImGuiTableColumnFlags_None); ImGui::TableSetupColumn("Two");
ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None); ImGui::TableSetupColumn("Three");
ImGui::TableSetupColumn("Four", ImGuiTableColumnFlags_None); ImGui::TableSetupColumn("Four");
ImGui::TableSetupColumn("Five", ImGuiTableColumnFlags_None); ImGui::TableSetupColumn("Five");
ImGui::TableSetupColumn("Six", ImGuiTableColumnFlags_None); ImGui::TableSetupColumn("Six");
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
for (int row = 0; row < 20; row++) for (int row = 0; row < 20; row++)
{ {
ImGui::TableNextRow(); ImGui::TableNextRow();
for (int column = 0; column < 7; column++) for (int column = 0; column < 7; column++)
{ {
// Both TableNextColumn() and TableSetColumnIndex() return false when a column is not visible, which can be used for clipping. // Both TableNextColumn() and TableSetColumnIndex() return false when a column is not visible.
if (!ImGui::TableSetColumnIndex(column)) // Because here we know that A) all our columns are contributing the same to row height and B) column 0 is always visible,
// we only always submit this one column.
if (!ImGui::TableSetColumnIndex(column) && column > 0)
continue; continue;
if (column == 0) if (column == 0)
ImGui::Text("Line %d", row); ImGui::Text("Line %d", row);
@ -4809,7 +4809,7 @@ static void ShowDemoWindowTables()
} }
} }
ImGui::TableNextColumn(); if (ImGui::TableNextColumn())
ImGui::TextUnformatted(item->Name); ImGui::TextUnformatted(item->Name);
// Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity, // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,
@ -4825,7 +4825,7 @@ static void ShowDemoWindowTables()
if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; } if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
} }
ImGui::TableNextColumn(); if (ImGui::TableNextColumn())
ImGui::Text("%d", item->Quantity); ImGui::Text("%d", item->Quantity);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -4834,7 +4834,7 @@ static void ShowDemoWindowTables()
else else
ImGui::Text("Lorem ipsum dolor sit amet"); ImGui::Text("Lorem ipsum dolor sit amet");
ImGui::TableNextColumn(); if (ImGui::TableNextColumn())
ImGui::Text("1234"); ImGui::Text("1234");
ImGui::PopID(); ImGui::PopID();

View File

@ -1932,7 +1932,9 @@ struct ImGuiTableColumn
ImGuiTableDrawChannelIdx DrawChannelUnfrozen; ImGuiTableDrawChannelIdx DrawChannelUnfrozen;
bool IsEnabled; // Is the column not marked Hidden by the user? (even if off view, e.g. clipped by scrolling). bool IsEnabled; // Is the column not marked Hidden by the user? (even if off view, e.g. clipped by scrolling).
bool IsEnabledNextFrame; bool IsEnabledNextFrame;
bool IsClipped; // Is not actually in view (e.g. not overlapping the host window clipping rectangle). bool IsVisibleX; // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled).
bool IsVisibleY;
bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not.
bool IsSkipItems; // Do we want item submissions to this column to be completely ignored (no layout will happen). bool IsSkipItems; // Do we want item submissions to this column to be completely ignored (no layout will happen).
ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte
ImS8 SortDirection; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending ImS8 SortDirection; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending
@ -1971,8 +1973,9 @@ struct ImGuiTable
ImSpan<ImGuiTableColumnIdx> DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) ImSpan<ImGuiTableColumnIdx> DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)
ImSpan<ImGuiTableCellData> RowCellData; // Point within RawData[]. Store cells background requests for current row. ImSpan<ImGuiTableCellData> RowCellData; // Point within RawData[]. Store cells background requests for current row.
ImU64 EnabledMaskByDisplayOrder; // Column DisplayOrder -> IsEnabled map ImU64 EnabledMaskByDisplayOrder; // Column DisplayOrder -> IsEnabled map
ImU64 EnabledUnclippedMaskByIndex;// Enabled and not Clipped, aka "actually visible" "not hidden by some scrolling"
ImU64 EnabledMaskByIndex; // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data ImU64 EnabledMaskByIndex; // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data
ImU64 VisibleMaskByIndex; // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect)
ImU64 RequestOutputMaskByIndex; // Column Index -> IsVisible || AutoFit (== expect user to submit items)
ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order) ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order)
int SettingsOffset; // Offset in g.SettingsTables int SettingsOffset; // Offset in g.SettingsTables
int LastFrameActive; int LastFrameActive;
@ -2280,7 +2283,7 @@ namespace ImGui
// Tables: Candidates for public api // Tables: Candidates for public api
IMGUI_API void TableOpenContextMenu(int column_n = -1); IMGUI_API void TableOpenContextMenu(int column_n = -1);
IMGUI_API void TableSetColumnWidth(int column_n, float width); IMGUI_API void TableSetColumnWidth(int column_n, float width);
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 bool TableGetColumnIsEnabled(int column_n = -1); // Return false when column is disabled (hidden by user/api, e.g. via context menu, or _DefaultHide flag)
IMGUI_API void TableSetColumnIsEnabled(int column_n, bool enabled); 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 void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
IMGUI_API float TableGetHeaderRowHeight(); IMGUI_API float TableGetHeaderRowHeight();

View File

@ -50,6 +50,63 @@ Index of this file:
// | EndChild() - (if ScrollX/ScrollY is set) // | EndChild() - (if ScrollX/ScrollY is set)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// TABLE SIZING
//-----------------------------------------------------------------------------
// (Read carefully because this is subtle but it does make sense!)
// About 'outer_size', its meaning needs to differ slightly depending of if we are using ScrollX/ScrollY flags:
// X:
// - outer_size.x < 0.0f -> right align from window/work-rect maximum x edge.
// - outer_size.x = 0.0f -> auto enlarge, use all available space.
// - outer_size.x > 0.0f -> fixed width
// Y with ScrollX/ScrollY: using a child window for scrolling:
// - outer_size.y < 0.0f -> bottom align
// - outer_size.y = 0.0f -> bottom align, consistent with BeginChild(). not recommended unless table is last item in parent window.
// - outer_size.y > 0.0f -> fixed child height. recommended when using Scrolling on any axis.
// Y without scrolling, we output table directly in parent window:
// - outer_size.y < 0.0f -> bottom align (will auto extend, unless NoHostExtendV is set)
// - outer_size.y = 0.0f -> zero minimum height (will auto extend, unless NoHostExtendV is set)
// - outer_size.y > 0.0f -> minimum height (will auto extend, unless NoHostExtendV is set)
// About 'inner_width':
// With ScrollX:
// - inner_width < 0.0f -> *illegal* fit in known width (right align from outer_size.x) <-- weird
// - inner_width = 0.0f -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns.
// - inner_width > 0.0f -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space!
// Without ScrollX:
// - inner_width -> *ignored*
// Details:
// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept
// of "available space" doesn't make sense.
// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding
// of what the value does.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// TABLES CULLING
//-----------------------------------------------------------------------------
// About clipping/culling of Rows in Tables:
// - For large numbers of rows, it is recommended you use ImGuiListClipper to only submit visible rows.
// ImGuiListClipper is reliant on the fact that rows are of equal height.
// See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper.
//-----------------------------------------------------------------------------
// About clipping/culling of Columns in Tables:
// - Case A: column is not hidden by user, and at least partially in sight (most common case).
// - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output.
// - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.).
//
// [A] [B] [C]
// TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() return false, user can skip submitting items but only if the column doesn't contribute to row height.
// SkipItems: false false true -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output.
// ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way.
// ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway).
//
// - We need distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row.
// However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer.
//-----------------------------------------------------------------------------
// About clipping/culling of whole Tables:
// - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false.
//-----------------------------------------------------------------------------
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
#endif #endif
@ -183,32 +240,6 @@ ImGuiTable* ImGui::TableFindByID(ImGuiID id)
return g.Tables.GetByKey(id); return g.Tables.GetByKey(id);
} }
// (Read carefully because this is subtle but it does make sense!)
// About 'outer_size', its meaning needs to differ slightly depending of if we are using ScrollX/ScrollY flags:
// X:
// - outer_size.x < 0.0f -> right align from window/work-rect maximum x edge.
// - outer_size.x = 0.0f -> auto enlarge, use all available space.
// - outer_size.x > 0.0f -> fixed width
// Y with ScrollX/ScrollY: using a child window for scrolling:
// - outer_size.y < 0.0f -> bottom align
// - outer_size.y = 0.0f -> bottom align, consistent with BeginChild(). not recommended unless table is last item in parent window.
// - outer_size.y > 0.0f -> fixed child height. recommended when using Scrolling on any axis.
// Y without scrolling, we output table directly in parent window:
// - outer_size.y < 0.0f -> bottom align (will auto extend, unless NoHostExtendV is set)
// - outer_size.y = 0.0f -> zero minimum height (will auto extend, unless NoHostExtendV is set)
// - outer_size.y > 0.0f -> minimum height (will auto extend, unless NoHostExtendV is set)
// About 'inner_width':
// With ScrollX:
// - inner_width < 0.0f -> *illegal* fit in known width (right align from outer_size.x) <-- weird
// - inner_width = 0.0f -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns.
// - inner_width > 0.0f -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space!
// Without ScrollX:
// - inner_width -> *ignored*
// Details:
// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept
// of "available space" doesn't make sense.
// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding
// of what the value does.
bool ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width) bool ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width)
{ {
ImGuiID id = GetID(str_id); ImGuiID id = GetID(str_id);
@ -627,7 +658,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
} }
IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder); IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder);
} }
table->EnabledUnclippedMaskByIndex = table->EnabledMaskByIndex; // Columns will be masked out below when Clipped
table->RightMostEnabledColumn = (ImGuiTableColumnIdx)last_visible_column_idx; table->RightMostEnabledColumn = (ImGuiTableColumnIdx)last_visible_column_idx;
// [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible
@ -685,7 +715,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
{ {
// Process auto-fit for non-stretched columns // Process auto-fit for non-stretched columns
// Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!) // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)
if ((column->AutoFitQueue != 0x00) || ((column->Flags & ImGuiTableColumnFlags_WidthAutoResize) && !column->IsClipped)) if ((column->AutoFitQueue != 0x00) || ((column->Flags & ImGuiTableColumnFlags_WidthAutoResize) && column->IsVisibleX))
column->WidthRequest = width_auto; column->WidthRequest = width_auto;
// FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets
@ -809,6 +839,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1; float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;
ImRect host_clip_rect = table->InnerClipRect; ImRect host_clip_rect = table->InnerClipRect;
//host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2; //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;
table->VisibleMaskByIndex = 0x00;
table->RequestOutputMaskByIndex = 0x00;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++) for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{ {
const int column_n = table->DisplayOrderToIndex[order_n]; const int column_n = table->DisplayOrderToIndex[order_n];
@ -828,7 +860,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->ClipRect.Min.y = work_rect.Min.y; column->ClipRect.Min.y = work_rect.Min.y;
column->ClipRect.Max.y = FLT_MAX; column->ClipRect.Max.y = FLT_MAX;
column->ClipRect.ClipWithFull(host_clip_rect); column->ClipRect.ClipWithFull(host_clip_rect);
column->IsClipped = column->IsSkipItems = true; column->IsVisibleX = column->IsVisibleY = column->IsRequestOutput = false;
column->IsSkipItems = true;
column->ItemWidth = 1.0f; column->ItemWidth = 1.0f;
continue; continue;
} }
@ -877,11 +910,31 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->ClipRect.Max.y = FLT_MAX; column->ClipRect.Max.y = FLT_MAX;
column->ClipRect.ClipWithFull(host_clip_rect); column->ClipRect.ClipWithFull(host_clip_rect);
column->IsClipped = (column->ClipRect.Max.x <= column->ClipRect.Min.x) && (column->AutoFitQueue & 1) == 0 && (column->CannotSkipItemsQueue & 1) == 0; // Mark column as Clipped (not in sight)
if (column->IsClipped) // Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables.
table->EnabledUnclippedMaskByIndex &= ~((ImU64)1 << column_n); // Columns with the _WidthAutoResize sizing policy will never be updated then. // FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window->ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY.
// Taking advantage of LastOuterHeight would yield good results there...
// FIXME-TABLE: IsVisible == false is disabled because it effectively means not submitting will reduces contents width which is fed to outer_window->DC.CursorMaxPos.x,
// and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else).
// Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small.
column->IsVisibleX = (column->ClipRect.Max.x > column->ClipRect.Min.x);
column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y);
const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY;
if (is_visible)
table->VisibleMaskByIndex |= ((ImU64)1 << column_n);
// Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.
column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0;
if (column->IsRequestOutput)
table->RequestOutputMaskByIndex |= ((ImU64)1 << column_n);
// Mark column as SkipItems (ignoring all items/layout)
column->IsSkipItems = !column->IsEnabled || table->HostSkipItems; column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;
//if (!is_visible && (column->Flags & ImGuiTableColumnFlags_AutoCull))
// if ((column->AutoFitQueue & 1) == 0 && (column->CannotSkipItemsQueue & 1) == 0)
// column->IsSkipItems = true;
if (column->IsSkipItems)
IM_ASSERT(!is_visible);
// Detect hovered column // Detect hovered column
if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x) if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x)
@ -1649,9 +1702,10 @@ bool ImGui::TableNextColumn()
TableBeginCell(table, 0); TableBeginCell(table, 0);
} }
// FIXME-TABLE: it is likely to alter layout if user skips a columns contents based on clipping. // Return whether the column is visible. User may choose to skip submitting items based on this return value,
// however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
int column_n = table->CurrentColumn; int column_n = table->CurrentColumn;
return (table->EnabledUnclippedMaskByIndex & ((ImU64)1 << column_n)) != 0; return (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)) != 0;
} }
// [Public] Append into a specific column // [Public] Append into a specific column
@ -1670,8 +1724,9 @@ bool ImGui::TableSetColumnIndex(int column_n)
TableBeginCell(table, column_n); TableBeginCell(table, column_n);
} }
// FIXME-TABLE: it is likely to alter layout if user skips a columns contents based on clipping. // Return whether the column is visible. User may choose to skip submitting items based on this return value,
return (table->EnabledUnclippedMaskByIndex & ((ImU64)1 << column_n)) != 0; // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
return (table->RequestOutputMaskByIndex & ((ImU64)1 << column_n)) != 0;
} }
int ImGui::TableGetColumnCount() int ImGui::TableGetColumnCount()
@ -1820,7 +1875,7 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int colum
return; return;
if (column_n == -1) if (column_n == -1)
column_n = table->CurrentColumn; column_n = table->CurrentColumn;
if ((table->EnabledUnclippedMaskByIndex & ((ImU64)1 << column_n)) == 0) if ((table->VisibleMaskByIndex & ((ImU64)1 << column_n)) == 0)
return; return;
if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n) if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n)
table->RowCellDataCurrent++; table->RowCellDataCurrent++;
@ -1899,7 +1954,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table)
const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1; const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;
const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount; const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount;
const int channels_for_bg = 1 + 1 * freeze_row_multiplier; const int channels_for_bg = 1 + 1 * freeze_row_multiplier;
const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->EnabledUnclippedMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0; const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->VisibleMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0;
const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;
table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total); table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total);
table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1);
@ -1910,7 +1965,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table)
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{ {
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
if (!column->IsClipped) if (column->IsVisibleX && column->IsVisibleY)
{ {
column->DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current); column->DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current);
column->DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0)); column->DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0));
@ -1975,7 +2030,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
// 1. Scan channels and take note of those which can be merged // 1. Scan channels and take note of those which can be merged
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{ {
if (!(table->EnabledUnclippedMaskByIndex & ((ImU64)1 << column_n))) if ((table->VisibleMaskByIndex & ((ImU64)1 << column_n)) == 0)
continue; continue;
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
@ -3116,13 +3171,13 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
const char* name = TableGetColumnName(table, n); const char* name = TableGetColumnName(table, n);
ImFormatString(buf, IM_ARRAYSIZE(buf), ImFormatString(buf, IM_ARRAYSIZE(buf),
"Column %d order %d name '%s': offset %+.2f to %+.2f\n" "Column %d order %d name '%s': offset %+.2f to %+.2f\n"
"Visible: %d, Clipped: %d, SkipItems: %d, DrawChannels: %d,%d\n" "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n"
"WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f\n" "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f\n"
"MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" "MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n"
"ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n" "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n"
"Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s%s..", "Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s%s..",
n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x,
column->IsEnabled, column->IsClipped, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen, column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen,
column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight,
column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x, column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x,
column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX, column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX,