فهرست منبع

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

ocornut 4 سال پیش
والد
کامیت
984c4cb5f8
4فایلهای تغییر یافته به همراه125 افزوده شده و 67 حذف شده
  1. 4 4
      imgui.h
  2. 17 17
      imgui_demo.cpp
  3. 6 3
      imgui_internal.h
  4. 98 43
      imgui_tables.cpp

+ 4 - 4
imgui.h

@@ -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.

+ 17 - 17
imgui_demo.cpp

@@ -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))
-                        continue;
-
+                    ImGui::TableSetColumnIndex(column);
                     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("Two", ImGuiTableColumnFlags_None);
-            ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None);
-            ImGui::TableSetupColumn("Four", ImGuiTableColumnFlags_None);
-            ImGui::TableSetupColumn("Five", ImGuiTableColumnFlags_None);
-            ImGui::TableSetupColumn("Six", ImGuiTableColumnFlags_None);
+            ImGui::TableSetupColumn("One");
+            ImGui::TableSetupColumn("Two");
+            ImGui::TableSetupColumn("Three");
+            ImGui::TableSetupColumn("Four");
+            ImGui::TableSetupColumn("Five");
+            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.
-                    if (!ImGui::TableSetColumnIndex(column))
+                    // Both TableNextColumn() and TableSetColumnIndex() return false when a column is not visible.
+                    // 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,8 +4809,8 @@ static void ShowDemoWindowTables()
                         }
                         }
                     }
                     }
 
 
-                    ImGui::TableNextColumn();
-                    ImGui::TextUnformatted(item->Name);
+                    if (ImGui::TableNextColumn())
+                        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,
                     // and we are currently sorting on the column showing the Quantity.
                     // and we are currently sorting on the column showing the Quantity.
@@ -4825,8 +4825,8 @@ 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();
-                    ImGui::Text("%d", item->Quantity);
+                    if (ImGui::TableNextColumn())
+                        ImGui::Text("%d", item->Quantity);
 
 
                     ImGui::TableNextColumn();
                     ImGui::TableNextColumn();
                     if (show_wrapped_text)
                     if (show_wrapped_text)
@@ -4834,8 +4834,8 @@ static void ShowDemoWindowTables()
                     else
                     else
                         ImGui::Text("Lorem ipsum dolor sit amet");
                         ImGui::Text("Lorem ipsum dolor sit amet");
 
 
-                    ImGui::TableNextColumn();
-                    ImGui::Text("1234");
+                    if (ImGui::TableNextColumn())
+                        ImGui::Text("1234");
 
 
                     ImGui::PopID();
                     ImGui::PopID();
                 }
                 }

+ 6 - 3
imgui_internal.h

@@ -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();

+ 98 - 43
imgui_tables.cpp

@@ -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;
-        if (column->IsClipped)
-            table->EnabledUnclippedMaskByIndex &= ~((ImU64)1 << column_n); // Columns with the _WidthAutoResize sizing policy will never be updated then.
-
+        // Mark column as Clipped (not in sight)
+        // Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables.
+        // 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 (table->EnabledUnclippedMaskByIndex & ((ImU64)1 << column_n)) != 0;
+    // 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.
+    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,