ocornut/imgui

New Tables API (alpha available for testing)

ocornut opened this issue Β· 71 comments

TABLES ARE NOW MERGED, MOVING THIS TO #3740

I have pushed an experimental tables branch:
https://github.com/ocornut/imgui/tree/tables
Providing a long awaited full-featured replacement to the old "Columns" API (#125).

I have been working on this for an embarassingly looong time.. What seemingly started as "let's refactor columns" became a multi-month thing with many rewrites/iterations. A large portion of this work been sponsored by Blizzard Entertainment. Several internal changes pushed to 1.71-1.74 were in preparation for this as I often try to have changes trickle down to master whenever possible to reduce complication of branching.

Please read <3
(this post will be occasionally updated)

Basic Usage:
#2957 (comment)

TODO List:
#2957 (comment)

WIP API breaking changes Sept 2020
#2957 (comment)

Question? Feedback? Bug report? Feature request? Please create a NEW ISSUE!

Status

  • Dec 2019: made branch public.
  • (EDIT) Oct 2020: planned for merging in 1.80 (next release). feedback welcome!
  • It is fairly functional but I am sure you will find issues. It can be used in many scenarios down to simple N-way columning without borders.
  • Next post include a TODO list.
  • I'm hoping that this ideally can be in master in 2-3 months (edit: HAHAHA). But that will depends on feedback, how many issues we find and how many we can fix etc.
  • The Columns() api will be marked "obsolete" when this gets merged, it will probably be kept as-is for a few years but we will encourage everyone to use Tables (not harder to use!).

Looking for early testers

  • Looking for early adopters to experiment with this and provide feedback. When I am confident enough that the API can become stable we will merge the feature in Master.
  • Please create New Issues instead of answering in this thread.
  • Some of the API will evolve in the upcoming few months. I would advise using this if you are confortable with following on some API changes (they will be posted here).
  • There are lots of known issues (see post below), but your feedback will help me prioritize them and will probably expand the feature set.
  • When you provide feedback please make it detailed, specify which flags you are using, provide shots, repros, etc. As with many other features, lots of things here are surprisingly more subtle and complex than you'd expect, magic under the hood, and many flags have subtle side-effects, etc. please don't make me guess.
  • If you use this branch I would appreciate if you tried to update regularly or semi-regularly so you can provide feedback and help detect regression.

Git Flow

  • Branch is tables (https://github.com/ocornut/imgui/tree/tables).
  • The branch will merge into docking/viewports without conflict.
  • The branch is based off master. I am expecting to merge this into master before the docking/viewports features.
  • I will rebase/push-force this branch over master in the course of the next few weeks/months.

Features

  • Scrolling on both axises
  • Possibility to freeze/lock rows or columns so they are always visible with scrolling
  • Headers (which can be customized)
  • Cells can contains anything (you can output multiple widgets, etc.) it's a regular canvas for your contents.
  • Stretching (weighted) or static size columns
  • Columns can be reordered
  • Columns can be hidden
  • Columns can be resized
  • Columns can be sorted (actual data sorting is done by user, api gives you the sort specs/infos you need)
  • Various bordering and padding options
  • Per-Columns flags (e.g. honor indent)
  • Borders and odd/even row background colors options
  • Clipper can be used on vertical axis (per column clipping possible as visibility is provided to user)
  • Saved settings (storage is font/dpi change friendly)
  • Context menu (should be customizable later)

Some screenshots

image

image

image

image

image

Basic Usage (Edited Jan 8, 2021)

  • There's a large amount of demo contents in Demo > Tables & Columns, please check it out.
  • This has lots of features! Check demo and imgui.h for details.
  • One big difference with the Columns API is that you need to call TableNextRow() to begin a new row (you can also call TableNextColumn() there and benefit of wrapping). Refer to Demo>Tables&Columns->Basic for a rundown of ways to use TableNextRow() / TableNextColumn() /
    TableSetColumnIndex().

Main API

// Tables
// [BETA API] API may evolve!
// - Full-featured replacement for old Columns API.
// - See Demo->Tables for details.
// - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags.
// The typical call flow is:
// - 1. Call BeginTable()
// - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults
// - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows
// - 4. Optionally call TableHeadersRow() to submit a header row (names will be pulled from data submitted to TableSetupColumns)
// - 5. Populate contents
//    - In most situations you can use TableNextRow() + TableSetColumnIndex(N) to start appending into a column.
//    - If you are using tables as a sort of grid, where every columns is holding the same type of contents,
//      you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex().
//      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!
//    - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing
//      width measurements. Otherwise, you may skip submitting the contents of a cell/column, 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).
//    - Summary of possible call flow:
//      ----------------------------------------------------------------------------------------------------------
//       TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1")  // OK
//       TableNextRow() -> TableNextColumn()      -> Text("Hello 0") -> TableNextColumn()      -> Text("Hello 1")  // OK
//                         TableNextColumn()      -> Text("Hello 0") -> TableNextColumn()      -> Text("Hello 1")  // OK: TableNextColumn() automatically gets to next row!
//       TableNextRow()                           -> Text("Hello 0")                                               // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear!
//      ----------------------------------------------------------------------------------------------------------
// - 5. Call EndTable()

bool  BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
void  EndTable();                                 // only call EndTable() if BeginTable() returns true!
void  TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row.
bool  TableNextColumn();                          // append into the next column (or first column of next row if currently in last column). Return true when column is visible.
bool  TableSetColumnIndex(int column_n);          // append into the specified column. Return true when column is visible.
int   TableGetColumnIndex();                      // return current column index.
int   TableGetRowIndex();                         // return current row index.

// Tables: Headers & Columns declaration
// - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc.
//   Important: this will not display anything! The name passed to TableSetupColumn() is used by TableHeadersRow() and context-menus.
// - Use TableHeadersRow() to create a row and automatically submit a TableHeader() for each column.
//   Headers are required to perform: reordering, sorting, and opening the context menu (but context menu can also be available in columns body using ImGuiTableFlags_ContextMenuInBody).
// - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in some advanced cases (e.g. adding custom widgets in header row).
// - Use TableSetupScrollFreeze() to lock columns (from the right) or rows (from the top) so they stay visible when scrolled.
void  TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = -1.0f, ImU32 user_id = 0);
void  TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled.
void  TableHeadersRow();                          // submit all headers cells based on data provided to TableSetupColumn() + submit context menu
void  TableHeader(const char* label);             // submit one header cell manually (rarely used)

// Tables: Miscellaneous functions
// - Most functions taking 'int column_n' treat the default value of -1 as the same as passing the current column index
// - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. Return value will be NULL if no sorting.
//   When 'SpecsDirty == true' you should sort your data. It will be true when sorting specs have changed since last call, or the first time.
//   Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!
//   Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable().
int                   TableGetColumnCount();                      // return number of columns (value passed to BeginTable)
const char*           TableGetColumnName(int column_n = -1);      // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.
ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1);     // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags.
ImGuiTableSortSpecs*  TableGetSortSpecs();                        // get latest sort specs for the table (NULL if not sorting).
void                  TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int column_n = -1);  // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details.
// Basic use of tables using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
// In many situations, this is the most flexible and easy to use pattern.
HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.");
if (ImGui::BeginTable("##table1", 3))
{
    for (int row = 0; row < 4; row++)
    {
        ImGui::TableNextRow();
        for (int column = 0; column < 3; column++)
        {
            ImGui::TableSetColumnIndex(column);
            ImGui::Text("Row %d Column %d", row, column);
        }
    }
    ImGui::EndTable();
}

// This essentially the same as above, except instead of using a for loop we call TableSetColumnIndex() manually.
// Sometimes this makes more sense.
HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.");
if (ImGui::BeginTable("##table2", 3))
{
    for (int row = 0; row < 4; row++)
    {
        ImGui::TableNextRow();
        ImGui::TableNextColumn();
        ImGui::Text("Row %d", row);
        ImGui::TableNextColumn();
        ImGui::Text("Some contents");
        ImGui::TableNextColumn();
        ImGui::Text("123.456");
    }
    ImGui::EndTable();
}

// Another subtle variant, we call TableNextColumn() _before_ each cell. At the end of a row, TableNextColumn() will create a new row.
// Note that we never TableNextRow() here!
HelpMarker(
    "Only using TableNextColumn(), which tends to be convenient for tables where every cells contains the same type of contents.\n"
    "This is also more similar to the old NextColumn() function of the Columns API, and provided to facilitate the Columns->Tables API transition.");
if (ImGui::BeginTable("##table4", 3))
{
    for (int item = 0; item < 14; item++)
    {
        ImGui::TableNextColumn();
        ImGui::Text("Item %d", item);
    }
    ImGui::EndTable();
}
  • You will likely be confused by default/per-column sizing policies and the effect that enabling ScrollX has on them. It's a little complex, but there are good reasons behind it. The short version here is:

"Columns can either varying resizing policy: "Fixed", "Stretch" or "AlwaysAutoResize". Toggling ScrollX needs to alter default sizing policy. Sizing policy have many subtle side effects which may be hard to fully comprehend at first.. They'll eventually make sense.

  • with SizingPolicyFixedX (default is ScrollX is on): Columns can be enlarged as needed. Enable scrollbar if ScrollX is enabled, otherwise extend parent window's contents rect. Only Fixed columns allowed. Weighted columns will calculate their width assuming no scrolling.
  • with SizingPolicyStretchX (default is ScrollX is off): Fit all columns within available table width (so it doesn't make sense to use ScrollX with Stretch columns!). Fixed and Weighted columns allowed.

TODO: add details here

In particular, Demo > Tables & Columns > Advanced is exposing a lots of options for you to play with:

image

API as of October 2020

Provided a rough references here,

// Flags for ImGui::BeginTable()
// - Important! Sizing policies have particularly complex and subtle side effects, more so than you would expect.
//   Read comments/demos carefully + experiment with live demos to get acquainted with them.
// - The default sizing policy for columns depends on whether the ScrollX flag is set on the table:
//   When ScrollX is off:
//    - Table defaults to ImGuiTableFlags_ColumnsWidthStretch -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch.
//    - Columns sizing policy allowed: Stretch (default) or Fixed/Auto.
//    - Stretch Columns will share the width available in table.
//    - Fixed Columns will generally obtain their requested width unless the Table cannot fit them all.
//   When ScrollX is on:
//    - Table defaults to ImGuiTableFlags_ColumnsWidthFixed -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed.
//    - Columns sizing policy allowed: Fixed/Auto mostly! 
//    - Fixed Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed.
//    - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable().
// - Mixing up columns with different sizing policy is possible BUT can be tricky and has some side-effects and restrictions.
//   (their visible order and the scrolling state have subtle but necessary effects on how they can be manually resized).
//   The typical use of mixing sizing policies is to have ScrollX disabled, one or two Stretch Column and many Fixed Columns.
enum ImGuiTableFlags_
{
    // Features
    ImGuiTableFlags_None                            = 0,
    ImGuiTableFlags_Resizable                       = 1 << 0,   // Enable resizing columns.
    ImGuiTableFlags_Reorderable                     = 1 << 1,   // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers)
    ImGuiTableFlags_Hideable                        = 1 << 2,   // Enable hiding/disabling columns in context menu.
    ImGuiTableFlags_Sortable                        = 1 << 3,   // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate.
    ImGuiTableFlags_NoSavedSettings                 = 1 << 4,   // Disable persisting columns order, width and sort settings in the .ini file.
    ImGuiTableFlags_ContextMenuInBody               = 1 << 5,   // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow().
    // Decorations
    ImGuiTableFlags_RowBg                           = 1 << 6,   // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually)
    ImGuiTableFlags_BordersInnerH                   = 1 << 7,   // Draw horizontal borders between rows.
    ImGuiTableFlags_BordersOuterH                   = 1 << 8,   // Draw horizontal borders at the top and bottom.
    ImGuiTableFlags_BordersInnerV                   = 1 << 9,   // Draw vertical borders between columns.
    ImGuiTableFlags_BordersOuterV                   = 1 << 10,  // Draw vertical borders on the left and right sides.
    ImGuiTableFlags_BordersH                        = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders.
    ImGuiTableFlags_BordersV                        = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders.
    ImGuiTableFlags_BordersInner                    = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders.
    ImGuiTableFlags_BordersOuter                    = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders.
    ImGuiTableFlags_Borders                         = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter,   // Draw all borders.
    ImGuiTableFlags_NoBordersInBody                 = 1 << 11,  // [ALPHA] Disable vertical borders in columns Body (borders will always appears in Headers). -> May move to style
    ImGuiTableFlags_NoBordersInBodyUntilResize      = 1 << 12,  // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers). -> May move to style
    // Sizing Policy (read above for defaults)
    ImGuiTableFlags_SizingFixedFit                  = 1 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width.
    ImGuiTableFlags_SizingFixedSame                 = 2 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible.
    ImGuiTableFlags_SizingStretchProp               = 3 << 13,  // Columns default to _WidthStretch with default weights proportional to each columns contents widths.
    ImGuiTableFlags_SizingStretchSame               = 4 << 13,  // Columns default to _WidthStretch with default weights all equal, unless overriden by TableSetupColumn().
    // Sizing Extra Options
    ImGuiTableFlags_NoHostExtendY                   = 1 << 16,  // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible)
    ImGuiTableFlags_NoKeepColumnsVisible            = 1 << 17,  // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable.
    ImGuiTableFlags_PreciseWidths                   = 1 << 18,  // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.
    // Clipping
    ImGuiTableFlags_NoClip                          = 1 << 19,  // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze().
    // Padding
    ImGuiTableFlags_PadOuterX                       = 1 << 20,  // Default if BordersOuterV is on. Enable outer-most padding.
    ImGuiTableFlags_NoPadOuterX                     = 1 << 21,  // Default if BordersOuterV is off. Disable outer-most padding.
    ImGuiTableFlags_NoPadInnerX                     = 1 << 22,  // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off).
    // Scrolling
    ImGuiTableFlags_ScrollX                         = 1 << 23,  // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX.
    ImGuiTableFlags_ScrollY                         = 1 << 24,  // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size.
    // Sorting
    ImGuiTableFlags_SortMulti                       = 1 << 25,  // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).
    ImGuiTableFlags_SortTristate                    = 1 << 26,  // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).
};

// Flags for ImGui::TableSetupColumn()
enum ImGuiTableColumnFlags_
{
    // Input configuration flags
    ImGuiTableColumnFlags_None                      = 0,
    ImGuiTableColumnFlags_DefaultHide               = 1 << 0,   // Default as a hidden/disabled 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 _SizingStretchSame or _SizingStretchProp).
    ImGuiTableColumnFlags_WidthFixed                = 1 << 3,   // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable).
    ImGuiTableColumnFlags_WidthAuto                 = 1 << 4,   // Column will not stretch and keep resizing based on submitted contents (default if table sizing policy is _SizingFixedFit and table is not resizable, or policy is _SizingFixedSame). Generally compatible with using right-most fitting widgets (e.g. SetNextItemWidth(-FLT_MIN))
    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_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_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_NoSortDescending          = 1 << 11,  // Disable ability to sort in the descending direction.
    ImGuiTableColumnFlags_NoHeaderWidth             = 1 << 12,  // Disable header text width contribution to automatic column width.
    ImGuiTableColumnFlags_PreferSortAscending       = 1 << 13,  // Make the initial sort direction Ascending when first sorting on this column (default).
    ImGuiTableColumnFlags_PreferSortDescending      = 1 << 14,  // Make the initial sort direction Descending when first sorting on this column.
    ImGuiTableColumnFlags_IndentEnable              = 1 << 15,  // Use current Indent value when entering cell (default for column 0).
    ImGuiTableColumnFlags_IndentDisable             = 1 << 16,  // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored.

    // Output status flags, read-only via TableGetColumnFlags()
    ImGuiTableColumnFlags_IsEnabled                 = 1 << 20,  // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags.
    ImGuiTableColumnFlags_IsVisible                 = 1 << 21,  // Status: is visible == is enabled AND not clipped by scrolling.
    ImGuiTableColumnFlags_IsSorted                  = 1 << 22,  // Status: is currently part of the sort specs
    ImGuiTableColumnFlags_IsHovered                 = 1 << 23,  // Status: is hovered by mouse
};

// Flags for ImGui::TableNextRow()
enum ImGuiTableRowFlags_
{
    ImGuiTableRowFlags_None                         = 0,
    ImGuiTableRowFlags_Headers                      = 1 << 0    // Identify header row (set default background color + width of its contents accounted different for auto column width)
};

// Enum for ImGui::TableSetBgColor()
// Background colors are rendering in 3 layers:
//  - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
//  - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
//  - Layer 2: draw with CellBg color if set.
// The purpose of the two row/columns layers is to let you decide if a background color changes should override or blend with the existing color.
// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.
// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
enum ImGuiTableBgTarget_
{
    ImGuiTableBgTarget_None                         = 0,
    ImGuiTableBgTarget_RowBg0                       = 1,        // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)
    ImGuiTableBgTarget_RowBg1                       = 2,        // Set row background color 1 (generally used for selection marking)
    ImGuiTableBgTarget_CellBg                       = 3         // Set cell background color (top-most color)
};

Issues / Todo list

Some of the stuff remaining in my current todo list

  • B. Fast path for _NoClip + minimum column size based on contents size
  • B. Detect that a cell is hovered/clicked/dragged from? How can we expose GetRectCellRect() without explicit row height?
  • B. InnerWidth parameter is confusing, with ScrollX and < OuterWidth also mess up with clipping.
  • B. Spanning multiple columns and/or multiple rows. (issue 3565)
  • B. Using Separator the same way as in Columns?
  • B. Experiment with drag and drop patterns (e.g. drag row from left-most header cell)
  • B. Visible one frame lag with Fixed columns appearing while window is still showing.
  • C. Glitch when clipped while host ScrollbarY status change (repro by toggling "Advanced->Options" and seeing the table scroll down)
  • C. Programmatic control: should be able to reorder, hide, sort, resize with public API.
  • C. Provide feedback to code on reorder (visually contiguous selection patterns may need to clear selection or rely on display order).
  • C. Support ScrollX without ScrollY while properly extending the height of the child window.
  • C. Multi/synced instances: Same frame sync issue with: Toggling visibility in context menu, Size all columns in context menu
  • C. Multi/synced instances: Validate and document possible edge cases (what happens when tables have heterogeneous widths? variable count?)
  • C. GC: scrolling windows tables may destroy window?
  • [Alpha] B. Rework policy of returning false in NextColumns()/SetColumnIndex() (benefit from common optimization while avoiding side-effect on vertical layout).
  • B. ItemWidth isn't set nor manipulated by tables api. see issue 3156)
  • [BUG] B. BorderOuterV or BorderInnerV without the other don't have correct padding.
  • [BUG] B. Header selectable padding != cell padding
  • [BUG] B. Hiding first column in e.g. "Resizable, Reorderable" demo messes up with heights.
  • [BUG] B. Making a frozen column larger than scrolling area breaks scrolling.
  • B. Clipper: bug with FrozenRows > 1: probably need to just rewrite clipper nicely.
  • B. Selectable with SpanAllColumns: PushTableBackground() with no ScrollX/Y (no child window) covers too much -> work toward applying table->WorkRect to window->WorkRect?
  • [BUG] B. Reordering across hidden column doesn't give the expected result after unhiding the column.
  • C. Settings: Expose functions in imgui_internal.h to clear stored settings.
  • C. Freezing rows count should: disable reordering across the freezing line, and decrease frozen count with hidden columns
  • C. GC: garbage collection of large buffers in unused tables.
  • B. How to submit a Selectable spanning the whole line if first row has been reordered/hidden? -> submit in any/first visible column!
  • C. Might make TableSetupColumn() optional after the first time?
  • C. Freezing bottom row (e.g. to have a total/count display)
  • [Dropped] C. Horizontal per-column alignment: store per-visible-row width? For now easier to let user do it.
  • [Dropped] B. Wrap Columns api to use Table api > Too many edge cases, saner to avoid wrapping and treat Columns as legacy API.

Sizing behaviors

  • B. If user outputs to clipped column (ignoring the return value) the content width should feed in size instead of being ignored.
  • B. Resize height of outer container from lower border (could also be a BeginChild feature!)
  • B. Rework/clarify usage of AutoFitQueue and CannotSkipItemsQueue which is a mess.
  • B. Resizing columns moving everything to the right is only possible with ONLY fixed columns! Clarify..
  • B. Auto-resize of a single stretch column could redistribute width to multiple siblings.
  • B. Simple non-border n-ways should aim to use matching outer cliprect for natural merge.
  • C. Sizing: api to set per-column minimum column width?
  • C. While resizing down a right-most column in scrolling table, preserve table/contents width while resizing down until releasing would reduce the feedback loop.
  • B. Clarify/test ImGuiTableColumnFlags_WidthAuto.
  • C. Flag to enforce same-width on all fixed columns?
  • [Alpha] B. Resizing in a set of stretched columns, clarify/fix. Sizing policies in general are confusing. Auto-fit doesn't work on Stretched columns.
  • B. Resize context menu options don't make sense on stretched columns
  • [Alpha] B. Settings: Store weight/sizes (currently disabled because undecided how to store in a multi-dpi/multi-font friendly way).
  • B. DPI Change support (store all widths unscaled?)
  • C. Allow auto-resize with double-click on right-most column of a stretch set.
  • B. "Fixed" sizing policy is perhaps not a good name? "Regular' ?

Render, clipping, borders, background color

  • B. Borders: Rework borders drawing code, currently a bit too complex and brittle.
  • B. Borders: Compact table demo still have issue with vertical borders
  • B. Borders: Ideally per cell borders would allow e.g. joining columns/cells. Bit of a high-bar but could be a good reference points when redesigning borders code.
  • B. Figure out Selection patterns/features and provide a demo.
  • B. Per-column background color overrides.
  • B. NoBordersInBody and NoBordersInBodyUntilResize could be regular style variables/colors.
  • B. Style: Decide how we express border colors in the style (or if we can't infer it from Separator+WindowBg color)
  • C. Borders: configure the presence of borders over the freezing line? (e.g. _NoBordersOnFreezingLine)
  • B. Per-cell, per-row background color overrides.
  • C. Borders: split ImGuiTableFlags_BordersVFullHeight into "visibility" vs "resizability"

Keyboard/Gamepad Navigation

  • B. Nav: Fixes for using navigation with scroll freeze (either axis). Alter InnerRect, standardize decoration size in windows?
  • B. Nav: Should be able to programmatically make a given column or row visible.
  • B. Nav: Navigation support for resizing, context menu? (can't right-click)
  • B. ImGui::ScrollToBringRectIntoView seems to ignore frozen columns/rows. A ImGui::ScrollToBringTableCellIntoView function would be even better!

Headers, context menu

  • B. Reorder: when going past mid-point of next columns + improve reorder past the scrolling view.
  • C. Headers: Using TableHeader() in any cell (generally first column)
  • B. Context menu: Per-column context menu without header?
  • B. Headers: need to reduce the gap between TableHeadersRow() and custom impl.
  • C. Headers: Multi-line labels in header are broken.
  • C. Headers: Tooltip when hovering clipped header name?

Sorting

  • C. Sort: disable per-column sorting (with a third click: e.g. ascending->descending->no sort) in some instances can be useful (needs to be opt-in). Effectively allow "zero sorting column" which is implicitly sorting on another criteria (e.g. storage order) which for some reason user would rather not display.
  • C. Bug: Settings: Loading settings without Hidden/Sort info with _DefaultHide/_DefaultSort is broken

Documentation

  • Alpha: B. Columns to Table conversion guide.
  • C. Clarify usage of TableNextRow + TableSetColumnIndex vs TableNextCell

Demo

  • B. Demo: demonstrate filtering patterns on large tables (w/ clipping)
  • C. Demo: Virtual scrolling demo? (#543)
  • B. Demo: add Tree view demo (see if Indent works as desired)
  • B. Demo: replace more uses of Columns() with table api.

What do you think would be the best way to implement a table tree view with this API? Something like, say, QTreeView.
Or is this out of scope for now?

It's entirely possible to use TreeNode with the table API, which should be enough to recreate a widget akin to QTreeView

General Questions

  • If you're in the first/last row of a table, and you scroll upwards/downwards, should that scroll the parent window?
  • Are cells selectable? Copy/Paste/Context Menus on individual cells?
  • Should the column list in the context menu for hiding be in the same order as the columns visibly appear?

Notes

  • Echoing the todo about per-column horizontal alignment. I have a column with names that are hundreds of characters long but the most important part of the name is on the right side (arguably the least important part is the middle, but ellipses in the middle of the text seems out of scope here).

Feedback

  • I like the way that resizing is handled. At first, it bugged me that extending a column in the middle would shrink the columns to its right, but when you shrink the middle column the other columns expand in the ratio they had prior so I'm ok with it.
  • Reordering columns feels a bit weird. I think I might personally prefer if it didn't reorder until you pass more than halfway through the next column. For an example of what I mean, you can reorder the demo window, then move your mouse horizontally back and forth a few pixels, and watch the column jump from side to side of the column your mouse is on.
  • Hidden columns behave strangely with reordering. Original order: ID, Name , Action, Quantity Long Label, Description. Hide Quantity Long Label. Move Action to the right of Description. Unhide Quantity Long Label. Order is now ID, Name, Description, Quantity Long Label, Action. Without hiding, the order is ID, Name, Quantity Long Label, Description, Action.
  • With many columns, I think the context menu has the potential to get very long and crowded. Maybe a Hide option when right-clicking a column would be helpful? Should this be left to the user to implement?

@soulthreads I am working on a Tree view demo right now. I think there may be currently some problems with how indentation is handled accross multiple columns. Will post when I have a demo.

@hofstee

If you're in the first/last row of a table, and you scroll upwards/downwards, should that scroll the parent window?

That's a good idea, thought not inherently a table feature. I'll add something in my todo list about the possibility for child windows to forward scroll requests to their parent when already at a limit. (EDIT: there's a ImGuiWindowFlags_NavFlattened flag that attempts to do that but it's not fully functional)

Are cells selectable? Copy/Paste/Context Menus on individual cells?

Will be working on selection patterns in the upcoming months. You can already use Selectable() and the ImGuiSelectableFlags_SpanAllColumns flag but there's lots to do to provide variety of solid selection scheme (expose ways to alter per-cell bg/borders, expose index>display map, interface with range-selection branch). I don't have very specific answers or ETA.

Should the column list in the context menu for hiding be in the same order as the columns visibly appear?

I personally don't think it does as I modelled it after Windows Explorer which shows this list in default order. I think it has the advantage of highlighting important columns. I'd be curious to know if other OS/toolkits uses a different scheme.

Echoing the todo about per-column horizontal alignment. I have a column with names that are hundreds of characters long but the most important part of the name is on the right side

I don't it as a table-specific feature but we should generally aim to provide ellipsis/clipping settings to e.g. Text functions. When we have this features we could imagine that table columns should store and forward this setting as a convenience. I think when we start refactoring text functions we will work on low-level features to enable this.

I like the way that resizing is handled. At first, it bugged me that extending a column in the middle would shrink the columns to its right, but when you shrink the middle column the other columns expand in the ratio they had prior so I'm ok with it.

It's a lot more complicated than you'd imagine as they are lots of rules depending on the order and number of columns of each sizing policy. So you'll probably stumble on cases where it behave differently than this too. There are generally good reasons for it but they are very unobvious to the user :/

Reordering columns feels a bit weird. I think I might personally prefer if it didn't reorder until you pass more than halfway through the next column.

I will experiment with this. I also had code to smoothly move the columns but it was incredibly complicated and eventually decided to ditch/stash it (was requiring z-ordering of elements, with side-effects on inputs, alpha blending, draw channels etc.). Surprisingly, alpha blending was the trickiest issue to solve. Comment in my stashed code says:

// To handle smooth reordering of columns, initially we tried to offset the entire column (headers + row).
// This is possible but we ran into a problem where it is difficult to draw the correct background color for the front-most column,
// because 1) we don't know what the actual background color was, 2) we don't want to be overlaying the same transparent color twice.
// Instead we decided to only offset the header row, for which we control what the background is, and can make it non-transparent.
// The complications involved with offsetting only the header rows are simpler to handle.

Hidden columns behave strangely with reordering.

Thanks, will look at this! EDIT: Fixed

With many columns, I think the context menu has the potential to get very long and crowded. Maybe a Hide option when right-clicking a column would be helpful? Should this be left to the user to implement?

Will see when it happens, we might want to add dedicated context menu hints.

I haven't used the new tables API yet, but hopefully I have some useful feedback as I implemented tables with a bunch of similar features to this a while back using the old columns API.

While a lot of table features look great with a trivial/nice/small examples data and you can do a lot of cool stuff, once I connected it to some real data into it and tried to interact / understand the table/data a lot of features became useless almost immediately when the number of table rows was 50+, nevermind 500+.

Put simply, features like sorting were only useful in tables where the sorting by a column happens to put the thing your looking for at the top of the list in the first ~10 item.

Anyway. long story short, I'm not sure if it's in your plans to build in directly, but the feature that made the tables usable for me was putting basic per column filtering based on data type directly in the table.

So in your example, my IDs columns (number data type) could filter for all items > 12 (comparison operator could changable)
The Name column (string data type) could filter for strings containing "Apr"
This would give me just the Apricot item.

There's a few more filter types you can implement like for enums, but you get the idea. Only in a trivial example does it seems a bit pointless, but the goal when I was implementing this pretty much went from implementing the table features you've made to be able to find / filter for as many subsets of data in a dataset into a trivial/nice/small sizes so that "last mile" features like sorting by column were much more usable.

the feature that made the tables usable for me was putting basic per column filtering based on data type directly in the table.

That makes sense of course, if you have tables with 100+ you want some sort of search going on. That's perfectly implementable already with what you have available. I'm not sure what you expect Dear ImGui to provide here, there are an infinite ways of filtering things (math ops, all kind of regexps). There's no concept of "data type in the table", tables are layout helper where you output your own data and that can be anything.

I agree we could provide more elaborate demo/examples code of doing things like that, adding that to my list.

Yeah it was pretty easy to implement simple/generic filters, the issues (besides columns api constraints/bugs) were pretty minor but mostly being space constrained to fit a filter in the column space and getting the look/interaction mechanics somewhat acceptable.

I wouldn't expect imgui to do much, if you've implemented native support for regexes things have probably gone very wrong. It would be as if Valve had implemented regexes to help find the games you own in your steam library for those 0.01% of users.

However I think there is compromise, I've not had much time to think about what the interface/implementation inside imgui would look like, but maybe you could optionally define some filtering flag for a given column for basic operations, trying to get you 95% there with little effort and not trying to hunt the last 5%. But like you say, it might also be better done in just a demo/example.

@joeslay You are right and will definitively work on this at some point. The optimal way to handle coarse clipping along with filtering requires sorting e.g. a filtered list of index anyway, so it would be good to provide a demo. This is very similar to the data structure ideally required for large range-multi-selection patterns (see features/rangeselect branch #1861). I will likely work on both filtering demos and range-select demos hand in hands.

Speaking of which I have fixed the indentation issue and added a simple Tree View demo now (cc: @soulthreads @Folling)

image

The ultimate target is to get large tree view with clipping, multi-select, range-select. This should currently be possible if merging both tables and rangeselect features but requires some work on a demo. I have a feeling those branches may converge soon.

Could a ImGuiTableFlags_ScrollFreezeBottomRow be added? Sometimes it is nice to have a row of totals at the bottom of a table. Also, is there any intended functionality for cells to span columns or rows?

@raegnar

Sometimes it is nice to have a row of totals at the bottom of a table.

Ouch, I didn't think of that. Adding to todo list but it may not be easy right now (though you should be able to create second table with same id right below the first one)

Also, is there any intended functionality for cells to span columns or rows?

Spanning multiple columns I will consider looking into but not too easy (will look after reworking all the border drawing code). Spanning multiple rows is much weirder/trickier but also less useful maybe? Because we don't know row height ahead it'd be difficult to implement.

Is there a way to query the column widths from the first table? I've tried the second table with the same id, but it seems to override the column width sizing.

Is there a way to query the column widths from the first table? I've tried the second table with the same id, but it seems to override the column width sizing.

The sizes should be synchronized so you shouldn't not have query any width. If you do two successives BeginTable()/EndTable() with same id they are normally synchronized. If you can't get it to work please open a separate issue for it, thanks!

This is looking very nice - so much more flexible than the columns API!

I'm still missing the ability to select table rows, specifically when the table is paired with interactive widgets such as buttons or tree nodes. Adding the following to the demo:

                 static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)
                 {
                     ImGui::TableNextRow();
+                    ImGui::Selectable("##select", false,
+                                      ImGuiSelectableFlags_SpanAllColumns |
+                                      ImGuiSelectableFlags_AllowItemOverlap);
+                    ImGui::SameLine();
                     const bool is_folder = (node->ChildCount > 0);
                     if (is_folder)
                     {

basically works, but is not at all pretty, since the Selectable "disappears" when the TreeNode is hovered. This is generally a problem with Selectables over interactive widgets, and it would be very nice to see this addressed by the table selection mechanics you are working on.

Hi, I deleted my posts earlier to attempt a new way to find the row height. In my earlier post I tried to edit internal table code but now I added the code to my ImGuiEx Library. If you can add the code or use the idea to find the row height it would be great.

What I would like to have is a method that will tell the height of a row. If any cell content change its height it will update the row height value.

This example shows it in action along with my Layout lib solution that align the "A0 Cell 1" text.

gif2

To try it out you can download the c++ files from

The tableEx methods used are
GetTableContentHeight()
CalculateTableRowHeight()
GetTableRowHeight()

Thanks

Fantastic job with new tables!

On the subject of alignment - why not add header alignment?
As a solution it could be implemented with flags, something like:
imgui.h

` 
enum ImGuiTableColumnFlags_ 
{
    ImGuiTableColumnFlags_AlignLeft		= 1 << 29,    // left align cell content
    ImGuiTableColumnFlags_AlignCenter 		= 1 << 30,    // center align cell content
    ImGuiTableColumnFlags_AlignRight		= 1 << 31     // right align cell content
};
` 

imgui_widgets.cpp

`
void    ImGui::TableHeader(const char* label) 
{
     if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) {
        if (column->SortOrder != -1) {
              w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);  // w_arrow is calculated only for sorting column
        }
    }
...
    if 	    (column->Flags & ImGuiTableColumnFlags_AlignCenter)		label_pos.x += (column->WidthGiven - label_size.x - g.Style.CellPadding.x) / 2;
    else if (column->Flags & ImGuiTableColumnFlags_AlignRight)		label_pos.x +=  column->WidthGiven - label_size.x - g.Style.CellPadding.x - w_arrow;

    RenderTextEllipsis(...)
}
`

Screenshot from 2020-01-16 07-18-10
Screenshot from 2020-01-16 07-18-20

It does seem to behave nicely and is very easy to implement. Please correct me if i am missing something.

I also noticed some misalignment of highlighted part of the header (on the right side):
Screenshot from 2020-01-16 07-24-28
Tried different flags, but behaviour is persistent and happens for every column. Could be a bug stemming from behaviour of ImGui::Selectable() in this line of imgui_widgets.cpp:

const bool pressed = Selectable("", selected, ImGuiSelectableFlags_DrawHoveredWhenHeld, ImVec2(0.0f, label_height));

But i'm not sure.

What would be the best way to implement tooltips for the header?
For now i can use TableSetupColumn (NULL, flags, width); and then in a helper so something like:

TableHeader (actual_label);
If (IsItemHovered ()) {
   show_something();
}
TableNextCell ();
 

Am i doing it right or is there better solution to tackle the problem?

About row selection, TreeNode, Selectable

@j-zeppenfeld Please open a dedicated issue for it. This is at the cross-over of several features. On one hand, I'm going to add internal api to make it easy to colorize row/lines/cells so that will be used for selection visuals. The wider issue is that the reach and features scope of Selectable vs TreeNode have been overlapping and confusing because none of them provide all the features we want. I think there's more general table-agnostic rework to do on them, maybe a new, better widget that covers the desirable feature set of both. Note in the meanwhile that TreeNode has a ImGuiTreeNodeFlags_Selected.

Since the canonical use case of Tables will be to provide e.g. large selectable trees we will aim to provide such demo and implement whatever is needed. I think the features/rangeselect branch will probably also converge into Tables.

About row height

@xpenatan: You can't calculcate a guaranted row height for the current frame before the full row (all cells) have been submitted. It's just not possible since any cell can extend the row height.

Possible workaround are:

  • Row height are known by the user and submitted in TableNextRow() then no cell is going higher than the row height.
  • Relying on temporal coherency by reusing row height from last frame. Assuming we decide to store a log of row heights indexed by row: this would be difficult to make work with clipping. Say you have a large table using clipping and you press Home or PageUp leading us to show previously non-visible items, we would have an old height for them. We also cannot guarantee clipping below y2 line (it would mean 1 cliprect/drawcmd per cell) so there's going to be overlap glitchs.

-> If you want to discuss the topic of Tables Row Height please open a dedicated issue for it.

About horizontal alignment of headers

On the subject of alignment - why not add header alignment?

@eRabbit0 I had those exact flags during earlier work but removed them. We can only align 1 item over 1 line, anything else won't work and proper multi-item multi-line alignment wouldn't be a table feature per se. If we offer the option only for Headers, we make it harder for people to use the option with custom headers (multi-item or multi-lines, say the user wants to have a checkbox before the TableHeader call). That said, if we restrict it to single-line header and rely on temporal coherency I could re-enable the feature.

Selectable() are a bit tricky here they were initially designed to cover half of the spacing between each others to avoid leaving any visual and mouse-hover gaps, but we can solve this.

TableHeader()+IsItemHovered() should work I am not sure what you are asking.
-> If you to discuss the topic of Tables Column Horizontal Alignment please open a dedicated issue for it.

I played a bit more with Tables and would like to expand on my issue with Selectable in a header.

Overflow is happening with ImGuiTableColumnFlags_BordersVInner.
But, as you can see below, behaviour is predictable with both ImGuiTableColumnFlags_BordersV and ImGuiTableColumnFlags_Borders.
Screenshot from 2020-01-16 07-24-28
Screenshot from 2020-01-21 11-36-20

Additionally, when FreezeRow is set, clipping of Selectable could be used for cases like below, when top visible row is selected and/or hovered):
Screenshot from 2020-01-21 11-27-11

Thanks for the new tables!

I just want to report that clicking on the table headers will close the whole modal dialog.

In case anyone is facing the same issue, the work-around for now is to add these internal function calls

ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
ImGui::TableAutoHeaders();
ImGui::PopItemFlag();

as suggested in a similar issue here.

@lunarflint Thank you for the report. This now has been fixed by 250f961.

parbo commented

Is it possible to set the font for each column header? I want some headers to use my icon font and some to use a regular font.

Is it possible to set the font for each column header? I want some headers to use my icon font and some to use a regular font.

Just push your desired font before submitting the headers?

hi
seems like a bug
GIF 05-02-2020 13-41-20

parbo commented

Is it possible to set the font for each column header? I want some headers to use my icon font and some to use a regular font.

Just push your desired font before submitting the headers?

Ah, I had to use ImGui::TableHeader manually, not ImGui::TableAutoHeaders

Now it works fine!

parbo commented

Is it possible to make the sorted column default to descending? I set the table as sortable, and one column to ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending, but it will still sort it ascending.

parbo commented

This seems to make it do what I want:

--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -9152,8 +9152,12 @@ void    ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags,
         // Init default visibility/sort state
         if (flags & ImGuiTableColumnFlags_DefaultHide)
             column->IsActive = column->NextIsActive = false;
-        if (flags & ImGuiTableColumnFlags_DefaultSort)
+        if (flags & ImGuiTableColumnFlags_DefaultSort) {
             column->SortOrder = 0; // Multiple columns using _DefaultSort will be reordered when building the sort specs.
+            column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending)
+                                        ? (ImS8)ImGuiSortDirection_Descending
+                                        : (ImU8)(ImGuiSortDirection_Ascending);
+        }
     }

@codz01 Fixed this bug in 978a3e7
It's a little tricky because Table-wide ImGuiTableFlags_SizingPolicyFixedX without ImGuiTableFlags_Resizable promote columns to ImGuiTableColumnFlags_WidthAlwaysAutoResize which can be misleading and confusing especially with that particular mode of the demo outputting a right-aligned Button(). I think I will rework some of the sizing policy wording and naming at some point.

@parbo:

Just push your desired font before submitting the headers?
Ah, I had to use ImGui::TableHeader manually, not ImGui::TableAutoHeaders
Now it works fine!

It should work in both cases and I just tested and confirmed it here. TableAutoHeaders() mostly just submit TableHeaders() so there should not be any difference between both, as long as they are surrounded with the PushFont/PopFon calls.

Is it possible to make the sorted column default to descending? I set the table as sortable,

Applied your fix now, thank you!

Note, I have fixed a bug with the arrow direction displayed for sort direction, and also removed SortSign from ImGuiTableSortSpecsColumn.

See d456e19 and #3023

I removed SortSign from ImGuiTableSortSpecsColumn because it was both duplicate from SortDirection (therefore misleading/confusing) and because different sort functions work differently this integer value/sign was also more misleading. Better removed!

sab24 commented

Scrolling in a table with horizontal scrollbar seems to get stuck on MacOs. I tried both Glfw with OpenGL3 and Vulkan. When moving the scrollbar while click dragging the mouse is fine, by using the touchpad it does not move left and right.

Code is like this:

ImGui::BeginChild("##AthletesTable");
    
    int ncolumns = 9 + raceobj.readpoints.size() * 4;
    ImVec2 size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 7);

    static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY; 
    std::string tstring = "cell content";
    if (ImGui::BeginTable("##AthletesTable", ncolumns, flags, size)){
        ImGuiListClipper clipper(10);
        while (clipper.Step()){
            for (int i = 0; i < 10; i++){
                ImGui::TableNextRow();
                for (int j = 0; j < 5; j++){ 
                    if (i >= clipper.DisplayStart && i < clipper.DisplayEnd){
                        ImGui::TableSetColumnIndex(j);
                        char label[10];
                        std::sprintf(label, "##%d%d", i, j);
                        ImGui::InputText(label, tstring.data(), (size_t)tstring.size() + 1, ImGuiInputTextFlags_CallbackResize, MainWindow::inputTextCallback, (void*)&tstring);
                    }
                }
            }
        }
        ImGui::EndTable();
    }
    ImGui::EndChild()

1

sab24 commented

The problem was with the clipper.

ImGuiListClipper clipper;
clipper.Begin(100);

solved the problem.

Great work on this API so far - intuitive to use and works great, for the most part. :)

Some issues that might be related to stuff you've already mentioned:

ImGui::ScrollToBringRectIntoView seems to ignore frozen columns/rows

(the rect is "in another cell" below the frozen column and thus not visible - not sure how to handle this - maybe have frozen cells/rows as "not visible" regions?), possibly related to this:

B. Clipper: should keep going without advancing step while table row is within frozen count > probably need to just rewrite clipper nicely.

Edit: A ImGui::ScrollToBringTableCellIntoView function would be even better!

I've locally needed to display which row was "selected" or "current".
I did this by adding:

ImGuiTableRowFlags_Selected
ImGuiCol_TableRowBgSelected

// and at the bottom of ImGui::TableBeginRow
if (table->RowFlags & ImGuiTableRowFlags_Selected)
{
        table->RowBgColor = GetColorU32(ImGuiCol_TableRowBgSelected);
}

Could possibly be solved much more nicely with this related item:

B. Per-cell background color override. Also easy to do per-row, per-column?

All in all, very good job on this!

As an expansion for the tables API, would be nice to be able to set row colour directly.
There are many use cases, e.g. when you want to group entries as in the image below.
Currently it is doable with ImGuiTableFlags_RowBg, but clunky.

Screenshot from 2020-06-18 23-49-34

Would it be possible to initialize the table with multi column sorting?
(Note would also like to have that multi sort available without enabling user sorting).

I was testing the tables API with the latest docking branch (so not sure if you will care about "bugs" out of the tables branch).

I have noticed something that I am not sure if it is intended bhv so I wanted to mention it just in case.
if you have two tables in the same window (with different IDs) and you are using the ImGuiTableFlags_ScrollY flag in both. The second table gets collapsed and disappears.

The flag mentions "Require 'outer_size' parameter of BeginTable() to specify the container size."

And yes, adding a vertical fixed size to the outer_size, fixes the problem from the images.
It wasn't obvious to me at first that if I didn't have an outer size, the table would disappear, I can't think of any other imgui "widget" that does that.
Would it be possible to maintain the default size of the table and autoscroll the header if the outer size is 0?

Without the flag:
image

With the flag:
image

  const ImGuiTableFlags flags = ImGuiTableFlags_ScrollY;

  ImGui::Begin("CC");
  if (ImGui::BeginTable("TestTable", 2, flags)) {
    ImGui::TableSetupColumn("A", ImGuiTableColumnFlags_None);
    ImGui::TableSetupColumn("B", ImGuiTableColumnFlags_None);
    ImGui::TableAutoHeaders();

    ImGui::TableNextRow();
    ImGui::TableSetColumnIndex(0);
    ImGui::Text("%d", 0);
    ImGui::TableSetColumnIndex(1);
    ImGui::Text("%d", 1);

    ImGui::EndTable();
  }

  if (ImGui::BeginTable("TestTable1", 2, flags)) {
    ImGui::TableSetupColumn("A", ImGuiTableColumnFlags_None);
    ImGui::TableSetupColumn("B", ImGuiTableColumnFlags_None);
    ImGui::TableAutoHeaders();

    ImGui::TableNextRow();
    ImGui::TableSetColumnIndex(0);
    ImGui::Text("%d", 0);
    ImGui::TableSetColumnIndex(1);
    ImGui::Text("%d", 1);

    ImGui::EndTable();
  }

  ImGui::End();

I could attach the debug report if you want more info to repro.
Thanks

parbo commented

In my project, I want to support doubleclick on rows. To do this I've been using ImGui::Selectable with ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick in a zero-width column. This works fine. But when I updated to the latest ImGui today it seems like it messes up the header height calculations.

This example:

    const int ROWS_COUNT = 3;
    const int COLUMNS_COUNT = 3;
    static int double_clicked[ROWS_COUNT] = {0};
    if (ImGui::BeginTable("##table1", COLUMNS_COUNT)) {
      ImGui::TableSetupColumn("Apricot");
      ImGui::TableSetupColumn("Banana");
      ImGui::TableSetupColumn("Cherry");

      ImGui::TableNextRow(ImGuiTableRowFlags_Headers /*, ImGui::GetTextLineHeightWithSpacing()*/);
      for (int column = 0; column < COLUMNS_COUNT; column++) {
        ImGui::TableSetColumnIndex(column);
        ImGui::PushID(column);
        ImGui::TableHeader(ImGui::TableGetColumnName(column));
        ImGui::PopID();
      }

      for (int row = 0; row < ROWS_COUNT; row++) {
        for (int column = 0; column < COLUMNS_COUNT; column++) {
          char buf[32];
          sprintf(buf, "Cell %d,%d", row, column);
          ImGui::TableNextCell();
          if (column == 0) {
            if (ImGui::Selectable(
                    buf,
                    false,
                    ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {
              if (ImGui::IsMouseDoubleClicked(0)) {
                double_clicked[row]++;
              }
            }
          } else {
            ImGui::Text(buf);
          }
        }
      }
      ImGui::EndTable();
    }
    for (int row = 0; row < ROWS_COUNT; row++) {
      ImGui::Text("Double-clicked %d times on row %d.", double_clicked[row], row);
    }

Turns out like:

image

The fix for now is the commented out part to set min row height for the header row.

@parbo

This works fine. But when I updated to the latest ImGui today it seems like it messes up the header height calculations.

This is a bug introduced in 2c67677, it's unrelated to the contents of your rows, it's just that TableHeader() doesn't properly extend the content height so the header row ends up being minimum size (~cellpadding.y * 2).
Now fixed by 56b0485.

There's some smelly code left-over in there caused by the fact I want to support customs widgets in the header lines.

@johanwendin

ImGui::ScrollToBringRectIntoView seems to ignore frozen columns/rows
A ImGui::ScrollToBringTableCellIntoView function would be even better!

Right, will look into that.

@eRabbit0

As an expansion for the tables API, would be nice to be able to set row colour directly.

Yes this planned and in the todo list above.

@MunWolf

Would it be possible to initialize the table with multi column sorting?
(Note would also like to have that multi sort available without enabling user sorting).

Everything is in the demo. Since sorting the data to submit is on your end, if you don't want to expose the sorting button to the user there's nothing for dear imgui to provide, feel free to sort your data however you want.

@Aborres

I have noticed something that I am not sure if it is intended bhv so I wanted to mention it just in case.
if you have two tables in the same window (with different IDs) and you are using the ImGuiTableFlags_ScrollY flag in both. The second table gets collapsed and disappears. [...]
And yes, adding a vertical fixed size to the outer_size, fixes the problem from the images.
It wasn't obvious to me at first that if I didn't have an outer size, the table would disappear, I can't think of any other imgui "widget" that does that.
Would it be possible to maintain the default size of the table and autoscroll the header if the outer size is 0?

It's not that it disappears, it is that the first table fills the height available in the window.
We can't by definition decide of sensible default height if the container is meant to scroll, so we default to "fill available space".
The only change I can think of would be to require the user to specify a size, aka assert if size is (0,0).
The same thing would happen if you create two child windows without specifying their height.

Added a WIP api for altering Row or Cell background color.
I iterated quite a lot on this to find a flexible and suitable solution that would also be fast.
It's currently missing support for changing the color of a whole Column (hopefully coming soon).

In my earlier draft I wanted to make it possible for CellBg color to completely overlap (no blending) ColumnBg or RowBg, which required storing a mask of drawn cell bg colors for each visible row and render a masked out selection. I have some unfinished code for that but I don't think it is worth the complexity, you can always set CellBg to be opaque anyway so the only remaining bonus would be to save on fill-rate and I think that's absurdly negligible at this point. However I'm going to use some of that uncommitted code for tracking visible rows so I can simplify and improve the Borders rendering code down the line.

Here's the current API:

void TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int column_n = -1);
// Enum for ImGui::TableSetBgColor()
// Background colors are rendering in 3 layers:
//  - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
//  - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
//  - Layer 2: draw with CellBg color if set.
// The purpose of the two row/columns layers is to let you decide if a background color changes should override or blend with the existing color.
// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.
// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
enum ImGuiTableBgTarget_
{
    ImGuiTableBgTarget_None          = 0,
    ImGuiTableBgTarget_ColumnBg0     = 1,   // FIXME-TABLE: Todo. Set column background color 0 (generally used for background
    ImGuiTableBgTarget_ColumnBg1     = 2,   // FIXME-TABLE: Todo. Set column background color 1 (generally used for selection marking)
    ImGuiTableBgTarget_RowBg0        = 3,   // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)
    ImGuiTableBgTarget_RowBg1        = 4,   // Set row background color 1 (generally used for selection marking)
    ImGuiTableBgTarget_CellBg        = 5    // Set cell background color (top-most color)
};

image

image

Really like the blending!

One thing which i stumbled upon while testing new TableSetBgColor is when i apply ImGuiTableBgTarget_RowBg1 to any row but the header, i cannot select those rows, hover doesn't work either. Though it is perfectly fine when used on the header row.
No problems at all with RowBg0 and CellBg.

Some additional thoughs:

  1. Priority of the colors. If RowBg1 is envisioned to be used for selection, then it should override CellBg, and probably future ColumnBg0.
  2. Selectable on hover and for selected rows should behave the same as above, otherwise it could look like this (applied extreme color to make the point):
    Screenshot from 2020-07-29 23-26-48

Here we have (for the first 3 rows):

  • RowBg0 = ImGuiColTableRowBg
  • RowBg1 = ImVec4 (0, 0, 0.1, 1) - rows became non-selectable due to this
  • CellBg = ImVec4 (0, 1, 0, 1)

This may already be covered with the clipper items on the todo, but I'm not sure. Popups that use a clipper within a table throw an error when creating the clipper within the popup.

Assertion failed: window == table->InnerWindow, file imgui_widgets.cpp, line 9480

void Sample()
{
	ImGui::Begin("Window");
	ImGui::BeginTable("Table", 2);

	ImGui::TableNextRow();
	ImGui::TableSetColumnIndex(0);
	ImGui::Text("Column 0");

	ImGui::TableSetColumnIndex(1);
	if (ImGui::Button("Open"))
		ImGui::OpenPopup("Popup");

	if (ImGui::BeginPopupModal("Popup"))
	{
		ImGuiListClipper clipper = ImGuiListClipper(100);
		while (clipper.Step())
		{
			for (int x = clipper.DisplayStart; x < clipper.DisplayEnd; x++)
				ImGui::Text("%d", x);
		}

		ImGui::EndPopup();
	}

	ImGui::EndTable();
	ImGui::End();
}

@sdhawk Fixed this issue now, thanks for reporting!

@ocornut, just wanted to drop in and say that Tables play really nicely with ImPlot. Still learning the API but I love it!

tables

@ocornut I apologize in that I have not yet played with the new table API. As always, great work on everything you do. For DearPyGui, I made a simple table widget using the columns API and selectables. It was really just a Band-aid while waiting for this branch to merge with master. However with the recent explosion of popularity, we receive requests everyday for more features.

You mention that the API is evolving (understandably), however are there any parts of the API that we can expect to remain? Initially we would just wrap the minimum number of features and gradually add more. I imagine this would help find issues with the API as we have several thousand users who would be using it immediately once we added it in.

Are certain parts of the API not expected to change?

I'm genuinely confused as to why are apologizing here (?), nor why you find the need to unnecessarily state "we have several thousand users who would be using it immediately" which is one lie followed by one idealistic assumption. Let's move forward by being accurate and genuine else we're not building on solid ground.

Unfortunately I can't predict what is expected to be stable or evolve but I'm hoping we'll know more by 1.80. There are such so many daily queries here and there which are eating time out from working on Tables so I am not able to provide much guarantees or ETA there.

Wow. I don't understand the reason to be rude to me.

  1. I apologized because I haven't had time to test or follow the progress of the tables API and assumed I may be asking a question that's been asked before.

  2. We do have a few thousand users using DearPyGui, 2300 downloads in that last month, and ~1000 the previous month.

  3. I claimed they would be using it "immediately" because we've been pushing out updates almost daily, which most users seem to be upgrading as soon after doing so.

Again sorry for asking...

We did decide to go ahead start bringing in the new table API (along with docking and multiple viewports), but will require users to explicitly import these features from an "experimental" module. Hopefully, we can provide some good feedback.

It seems that too many nested popups that use tables will crash with the error:

Assertion failed: _Current == 0 && _Count <= 1 && "Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter.", file imgui_draw.cpp, line 1444

I should note that in my actual project this same error occurs with just one popup after being open/closed roughly 20 or so times, but I've been unable to isolate that version of it (I suspect they have the same root cause).

In this sample, the error happens after opening 8 popups:

void Table(int count)
{
	ImGui::SetNextWindowSize(ImVec2(300, 300));

	char tableId[32]; sprintf(tableId, "table %d", count);
	char popupId[32]; sprintf(popupId, "popup %d", count);

	if (ImGui::BeginTable(tableId, 2))
	{
		ImGui::TableNextRow();
		ImGui::TableSetColumnIndex(0);
		ImGui::Text("Column 0");

		ImGui::TableSetColumnIndex(1);
		if (ImGui::Button("open"))
		{
			ImGui::OpenPopup(popupId);
		}

		if (ImGui::BeginPopupModal(popupId))
		{
			Table(count + 1);
			ImGui::EndPopup();
		}

		ImGui::EndTable();
	}
}

void Sample()
{
	ImGui::SetNextWindowSize(ImVec2(300, 300));

	ImGui::Begin("Sample");
	Table(0);
	ImGui::End();
}

Everyone:

BREAKING CHANGES (SEPT 2020)

I haved broken theTableGetSortSpecs() API to make user explicitely clear the dirty flag:

const ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs();
if (sorts_specs && sorts_specs->SpecsChanged)
{
    // [...] Perform sort
}

To:

ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs();
if (sorts_specs && sorts_specs->SpecsDirty)
{
    // [...] Perform sort
    sorts_specs->SpecsDirty = false; // Clear dirty flag
}

The reason being that TableGetSortSpecs() had user-visible side-effect of effectively clearing that flag so multiple calls would lead to confusing bugs. Current version is less bug prone. See change 68ef531

Everyone:
Prefer opening a new issue to submit a bug.

@sdhawk

It seems that too many nested popups that use tables will crash with the error:

Thank you for reporting with a nice repro! it was a bug holding on a table pointer accross resizing the pool used to store them. Our ImVector implementation by default would typically allocate 8 items ahead of them so bug wasn't noticeable. Fixed with 4cb0431.

BREAKING CHANGES (SEPT 2020)

I'll be making some more breaking changes toward trying to stabilizing the api for 1.80.

Recently

  • Renamed TableAutoHeaders() to TableHeadersRow().
  • Replaced ImGuiTableFlags_ScrollFreezeXXX flags with TableSetupScrollFreeze() api.
  • Renamed ImGuiTableFlags_NoClipX to ImGuiTableFlags_NoClip`.
  • In ImGuiTableSortSpecs renamed bool SpecsChanged to bool SpecsDirty and made it the user responsability to clear that flag after sorting.

BREAKING CHANGES (OCT 2020)

Doing some more breaking as I'm hoping to promote this branch a lots more after releasing 1.79, toward merging the branch into master in 1.80.

  • Renamed TableNextCell() to TableNextColumn(). This makes lots more sense as there is a neat symmetry with the use of rows, the TableSetColumnIndex() function and the old Columns API.
  • Changed TableNextRow() to not automatically get into the first column. This makes the API generally more consistent.

Those changes reflected recurrent feedback from first-time users, so I'm biting the bullet and changing things now.
Note that most the demo has been suggesting using TableSetColumnIndex() so code written using that won't be affected.

- In most situations you can use TableNextRow() + TableSetColumnIndex(xx) to start appending into a column.
- If you are using tables as a sort of grid, where every columns is holding the same type of contents,
  you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex().
  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!
- Here's a summary of possible call flow:
  ----------------------------------------------------------------------------------------------------------------
    TableNextRow() -> TableSetColumnIndex(0) -> Button("Hello 0") -> TableSetColumnIndex(1) -> Button("Hello 1")   // OK
    TableNextRow() -> TableNextColumn()         Button("Hello 0") -> TableNextColumn()      -> Button("Hello 1")   // OK
                      TableNextColumn()         Button("Hello 0") -> TableNextColumn()      -> Button("Hello 1")   // OK: TableNextColumn() automatically gets to next row!
    TableNextRow()                              Button("Hello 0") ................................................ // Not OK! Missing TableSetColumnIndex() or TableNextColumn()!
  ----------------------------------------------------------------------------------------------------------------

@marinjurjevic Please post this in a new issue as instructed above and delete message here, thank you!

Recent changes to Tables:

  • Fixing row-spanning Selectable() overlapping frozen header (by separating background draw channels, will also be of use to rework borders rendering)
  • Added some support for auto-resizing of stretch columns. It's trickier than it sounds because essentially it amounts to four cases:
    • [1] visible columns are all stretch : "size all to default" reset to default weight
    • [2] visible columns are all stretch "size one to fit" set weight, reapply weight (FIXME: improve weight redistribution in case of >1 siblings)
    • [3] visible columns are mixed : "size all to fit/default" reset stretch columns to default weight, set fixed to auto width
    • [4] visible columns are mixed : "size one to fit", redistribute weight the same way as a manual resize.
  • Added ImGuiTableFlags_PreciseStretchWidths for situations where we want stretch columns with same weight to be exactly the same width down to the pixels, otherwise by default the reminder width is spread across columns so some may be +1 pixel.
  • Reworked code to allocate width and shrink them down when tables is resized too small. They are various desirable properties for this behavior, one being that the contents of shrunk columns appears the same width. That part is currently not perfect, namely in the situation when there's no outer x padding (most often when there's no outer vertical border), the cliprect width of right-most columns may be a few pixels off (it's really complicated to get right, have have battery of tests and spent a few days trying to solve this).
  • Added tooltip on clipped headers.
  • Fixed crash when changing columns count while settings are bound.
  • Added garbage collection of the large buffers (vertices) for unused tables (no-op is only touching 4 bytes per tables over a single consecutive soa buffer).

Before shipping 1.80 the things I would like to solve are:

  • [Alpha] B. Rewrite borders drawing code as a post-process past (will reduce complexity, fix off-by-1 outer border in scrolling tables, allow thick borders)
  • [Alpha] B. Rework standard idiom of using NextColumns()/SetColumnIndex() return value for common optimization while-avoiding side-effects on vertical layout. Current idiom is essentially broken or misleading in a way which I'm afraid will direct people to not use it, have some ideas for how to fix it.

BREAKING CHANGES (NOV 2020)

Everyone, I have pushed a few "major" breaking changes yesterday. I think they may be the final batch of breaking changes going in before going to master.

  • Renamed ImGuiTableFlags_SizingPolicyStretchX to ImGuiTableFlags_ColumnsWidthStretch.
  • Renamed ImGuiTableFlags_SizingPolicyFixedX to ImGuiTableFlags_ColumnsWidthFixed.
  • Renamed ImGuiTableColumnFlags_XXX to ImGuiColumnFlags_XXX, ImGuiTableRowFlags_XXX to ImGuiRowFlags_XXX.

Sorry for the disturbance, hopefully this is for the good!

frink commented

Is the plan to obsolete the old Column API completely or are both approaches sharing the same set of flags...

@frink the public columns API have no flags, only internal api has, see:
72de6f3
#125 (comment)

frink commented

I stand corrected. I misread your post on #125 this morning.

Hello,

After some internal monologue, I have undid this specific change mentioned two days ago:

Renamed ImGuiTableColumnFlags_XXX to ImGuiColumnFlags_XXX, ImGuiTableRowFlags_XXX to ImGuiRowFlags_XXX.

Effectively nuked top-most commit 694f6cb from tables/ history.
It's not worth creating the small inconsistency to simplify/shorten those symbols and they are not so often used, so backtracked on that decision.

I realized that one essential feature of tables was not demonstrated anywhere in the demo, and poorly documented.
Added a bit of demo code now.

Tables with the same identifier share all settings (width, visibility, order, etc)
tables_synced

This is also de-facto a sensible workaround for creating contents that span an entire row (aka #3565) + ref #2136.

BREAKING CHANGES (DEC 2020)

  • Added imgui_tables.cpp file (#3513)
  • Renamed ImGuiTableSortSpecsColumn to ImGuiTableColumnSortSpecs.
  • Removed TableGetHoveredColumn() in favor of using TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered.

INCOMING RELEASE

Tables have been merged into master today.
I'll make a few extra changes, gather extra feedback, open a new thread soon (and close this one), and tag as 1.80 hopefully next week.

Hi, is there going to be a summary of the tables branch (and I guess it might be the new thread you mentioned in the above comment)?

Hi, is there going to be a summary of the tables branch (and I guess it might be the new thread you mentioned in the above comment)?

Yes I'm going to open a new threads when ready, but the top posts here + demo are giving a good summary already.

I found one niggle with sizing policy which I want to settle before tagging 1.80 hence the delay.

Related to table, since last week:

  • Tweaked some inner padding inconsistency with no borders.
  • Added ImGuiTableFlags_SortTristate to allow a third disabled state.
  • Fixed various edge cases/minor issues with toggling some of the sorting parameters while a table is up.
  • Added some more regression tests (running tests currently get us a coverage of 92% of lines in imgui_tables.cpp).
  • Minor border display fix when using SameWidths on fixed columns.
  • Shuffled some functions in the file and improved the navigation index.

BREAKING CHANGES (DEC 2020)

  • Renamed ImGuiTableFlags_MultiSortable to ImGuiTableFlags_SortMulti.

BREAKING CHANGES (DEC 2020)

To support #3605 I made a little change to the default value and handling of the ImVec2 outer_size parameter.

  • ImVec2 outer_size previous default value to ImVec2(0.0f, 0.0f)
  • ImVec2 outer_size new default value is ImVec2(-FLT-MIN, 0.0f) (right-align)

The new meaning of outer_size.x == 0.0f is "automatic width".
If Scrolling is enabled or if any columns is set to Stretch, outer_size.x == 0.0f is the same as outer_size == -FLT_MIN (will right-align). If Scrolling is disabled and all columns are set to Non-Stretch, then outer_size.x == 0.0f allows to create tables which don't use the full window width while not having to specify a width ahead:

image

Effectively this is unlikely to break much code as outer_size is only required for scrolling tables.
(As a data point: The only things affected in the demo were demo tables which had a toggle to switch between scrolling and not-scrolling, and therefore had specific height provided and yet could have their scrolling disabled, which is not likely to appear in real code.)

Just a positive note. Tables work well and are very functional, I especially like fixed row and column headers and fast display of even large tables. The architecture is clean and understandable. During the last months, judging by GitHub updates, the development in this area has been fast, now I am looking forward to the next stable version to test. Thank you:)

BREAKING CHANGES (JAN 2021)

I know the joke is getting old now :) but last month while looking at a totally unrelated thing (#1149) I came up with a few realization and situations where the sizing policies were inadequate, which eventually got me into a larger/deeper exploration of various sizing-related ideas and a mild refactor of the exposed policilies. As a result I've again broken the API (but only in a minor way and only if you specified a table sizing policy explicitly).

TL;DR:
ImGuiTableFlags_ColumnsWidthFixed > ImGuiTableFlags_SizingFixedFit
ImGuiTableFlags_ColumnsWidthStretch > ImGuiTableFlags_SizingStretchSame.

If you are using Tables I would appreciate if you could update to latest to make sure everything's alright. And then hopefully we can tag 1.80....

Here's a run down of table sizing policies:

// Sizing Policy (read above for defaults)
ImGuiTableFlags_SizingFixedFit    = 1 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width.
ImGuiTableFlags_SizingFixedSame   = 2 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible.
ImGuiTableFlags_SizingStretchProp = 3 << 13,  // Columns default to _WidthStretch with default weights proportional to each columns contents widths.
ImGuiTableFlags_SizingStretchSame = 4 << 13,  // Columns default to _WidthStretch with default weights all equal, unless overriden by TableSetupColumn().

New section in the demo:

tables sizing policies

e.g. a use of ImGuiTableFlags_SizingStretchProp:

SizingStretchProp

There are many details under the hood which I don't have the energy to explain right now (most you don't really need to know until you hit very specific use cases) but added variety of comments and added myself more notes.

Also, previously default table policy in an auto-resizing window (e.g. a tooltip) would lead to stretching columns with same weight which was wholly inadequate, we're now using ImGuiTableFlags_SizingFixedFit by default in an auto-resizing window.

One last thing I'd like to do is clarify per-column Auto vs Fixed policies and the effect of some of the internals unexposed api to override current width (not default width).

Bonus fact: Our regression tests are now covering 97% lines of imgui_tables.cpp, aka 61 uncovered and many are asserts/error handlers.

Quick test Dear ImGui, 9.1.2021 docking branch, commit tag 70703da

I switched to use this version, all started working quickly. Notes:

  • This branch has both tables and docking ready.
  • ImGuiTableFlags_SizingPolicyStretchX -> ImGuiTableFlags_SizingStretchProp
  • TableGetHoveredColumn() now internal, included "imgui_internal.h". (I need to look for better way to do this some point).
  • I hit to 64 columns limit but decided to live with this for now. Support to more columns maybe in future, the original code has preparation for more columns (typedef ImGuiTableColumnIdx and draw channels). I hope for a real tested version sometime in the future (much preferred over my own quick and dirty patches).

Based on the quick test, all good with the newest table code.
Best regards, Pekka

@iocafe

ImGuiTableFlags_SizingPolicyStretchX -> ImGuiTableFlags_SizingStretchProp

Note that the old ImGuiTableFlags_SizingPolicyStretchX is == ImGuiTableFlags_SizingStretchSame. ImGuiTableFlags_SizingStretchProp is a slightly different behavior.

TableGetHoveredColumn() now internal, included "imgui_internal.h". (I need to look for better way to do this some point).

Post above says:
"Removed TableGetHoveredColumn() in favor of using TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered."

I hit to 64 columns limit but decided to live with this for now. Support to more columns maybe in future, the original code has preparation for more columns (typedef ImGuiTableColumnIdx and draw channels). I hope for a real tested version sometime in the future (much preferred over my own quick and dirty patches).

Yes I think we'll support it eventually.

  • The ImU64 mask will be changed to something like ImBitArray or ImBitVector. Neither are currently satisfactory as one would require a worst-case limit and the other would heap-allocate. Instead we will allocate everything we need for the 4 masks inside the RawData single-alloc and call the lower-level ImBitArrayXXX functions directly. The MAIN purpose of all those packed masks is to avoid touching unnecessary cache-lines for non-visible columns (otherwise we could naively move all those flags as bool stored into each columns, but that would defeat their purpose).
  • I would like to move ImDrawSplitter and some other transient data out of ImGuiTable and into a structure shared by all tables for a given recursion level, that will alleviate the recurrent cost of high-columns tables.