Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Tables API (alpha available for testing) #2957

Closed
ocornut opened this issue Dec 30, 2019 · 71 comments
Closed

New Tables API (alpha available for testing) #2957

ocornut opened this issue Dec 30, 2019 · 71 comments
Milestone

Comments

@ocornut
Copy link
Owner

ocornut commented Dec 30, 2019

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

@ocornut
Copy link
Owner Author

ocornut commented Dec 30, 2019

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)
};

@ocornut
Copy link
Owner Author

ocornut commented Dec 30, 2019

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? (Virtual scrolling and columns #543)
  • B. Demo: add Tree view demo (see if Indent works as desired)
  • B. Demo: replace more uses of Columns() with table api.

@ocornut ocornut changed the title New Tables API (available for testing) New Tables API (alpha available for testing) Dec 30, 2019
@soulthreads
Copy link

soulthreads commented Dec 30, 2019

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?

@Folling
Copy link

Folling commented Dec 30, 2019

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

@hofstee
Copy link

hofstee commented Dec 30, 2019

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?

ocornut added a commit that referenced this issue Jan 3, 2020
…ellRect() include expected paddings. Add demo code. Comments. (#2957)

Remove misleading commented-out flags for now.
@ocornut
Copy link
Owner Author

ocornut commented Jan 3, 2020

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

ocornut added a commit that referenced this issue Jan 3, 2020
…to never be larger than scrolling visible rect width. (#2957)
ocornut added a commit that referenced this issue Jan 6, 2020
ocornut added a commit that referenced this issue Jan 6, 2020
…xtBaseOffset in EndCell of inactive column). (#2957)
@joeslay
Copy link
Contributor

joeslay commented Jan 9, 2020

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.

@ocornut
Copy link
Owner Author

ocornut commented Jan 9, 2020

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.

@joeslay
Copy link
Contributor

joeslay commented Jan 9, 2020

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.

ocornut added a commit that referenced this issue Jan 9, 2020
…api) and exposed flags. Added simple Tree demo. (#2957)
ocornut added a commit that referenced this issue Jan 9, 2020
…api) and exposed flags. Added simple Tree demo. (#2957)
@ocornut
Copy link
Owner Author

ocornut commented Jan 9, 2020

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

@randallrvr
Copy link

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?

@ocornut
Copy link
Owner Author

ocornut commented Jan 10, 2020

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

@randallrvr
Copy link

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.

@ocornut
Copy link
Owner Author

ocornut commented Jan 10, 2020

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!

@j-zeppenfeld
Copy link

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.

@xpenatan
Copy link

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

@eRabbit0
Copy link

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.

@eRabbit0
Copy link

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.

@eRabbit0
Copy link

eRabbit0 commented Jan 16, 2020

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?

@ocornut
Copy link
Owner Author

ocornut commented Jan 16, 2020

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.

@eRabbit0
Copy link

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

@ocornut
Copy link
Owner Author

ocornut commented Nov 9, 2020

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

@ocornut
Copy link
Owner Author

ocornut commented Nov 16, 2020

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.

@ocornut
Copy link
Owner Author

ocornut commented Nov 19, 2020

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
Copy link
Contributor

frink commented Nov 19, 2020

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

@ocornut
Copy link
Owner Author

ocornut commented Nov 19, 2020

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

@frink
Copy link
Contributor

frink commented Nov 20, 2020

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

@ocornut
Copy link
Owner Author

ocornut commented Nov 20, 2020

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.

@ocornut
Copy link
Owner Author

ocornut commented Nov 20, 2020

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.

@ocornut
Copy link
Owner Author

ocornut commented Dec 4, 2020

BREAKING CHANGES (DEC 2020)

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.

@ice1000
Copy link
Contributor

ice1000 commented Dec 4, 2020

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)?

@ocornut
Copy link
Owner Author

ocornut commented Dec 11, 2020

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.

@ocornut
Copy link
Owner Author

ocornut commented Dec 18, 2020

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

@iocafe
Copy link

iocafe commented Jan 5, 2021

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:)

@ocornut
Copy link
Owner Author

ocornut commented Jan 8, 2021

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.

@iocafe
Copy link

iocafe commented Jan 9, 2021

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

@ocornut
Copy link
Owner Author

ocornut commented Jan 11, 2021

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

@iocafe
Copy link

iocafe commented Jan 13, 2021 via email

@ocornut ocornut unpinned this issue Jan 21, 2021
@ocornut ocornut closed this as completed Jan 21, 2021
ocornut added a commit that referenced this issue Oct 13, 2022
… prevent columns output flags from having ImGuiTableColumnFlags_IsHovered set. (#2957)
BramblePie pushed a commit to BramblePie/imgui that referenced this issue Oct 26, 2022
… prevent columns output flags from having ImGuiTableColumnFlags_IsHovered set. (ocornut#2957)
BramblePie pushed a commit to BramblePie/imgui that referenced this issue Oct 26, 2022
… prevent columns output flags from having ImGuiTableColumnFlags_IsHovered set. (ocornut#2957)
ocornut added a commit that referenced this issue Oct 12, 2023
…ledHeader, ImGui::TableHeadersAngledRow(), style.TableAngledHeadersAngle. (#2957)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests