2022-12-11 05:57:49 +01:00
// dear imgui, v1.89.2 WIP
2021-09-05 09:47:05 +02:00
// (tables and columns code)
/*
Index of this file :
// [SECTION] Commentary
// [SECTION] Header mess
// [SECTION] Tables: Main code
// [SECTION] Tables: Simple accessors
// [SECTION] Tables: Row changes
// [SECTION] Tables: Columns changes
// [SECTION] Tables: Columns width management
// [SECTION] Tables: Drawing
// [SECTION] Tables: Sorting
// [SECTION] Tables: Headers
// [SECTION] Tables: Context Menu
// [SECTION] Tables: Settings (.ini data)
// [SECTION] Tables: Garbage Collection
// [SECTION] Tables: Debugging
// [SECTION] Columns, BeginColumns, EndColumns, etc.
*/
// Navigating this file:
2022-12-11 05:57:49 +01:00
// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
2021-09-05 09:47:05 +02:00
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
//-----------------------------------------------------------------------------
// [SECTION] Commentary
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Typical tables call flow: (root level is generally public API):
//-----------------------------------------------------------------------------
// - BeginTable() user begin into a table
// | BeginChild() - (if ScrollX/ScrollY is set)
// | TableBeginInitMemory() - first time table is used
// | TableResetSettings() - on settings reset
// | TableLoadSettings() - on settings load
// | TableBeginApplyRequests() - apply queued resizing/reordering/hiding requests
// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame)
// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width
// - TableSetupColumn() user submit columns details (optional)
// - TableSetupScrollFreeze() user submit scroll freeze information (optional)
//-----------------------------------------------------------------------------
// - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().
// | TableSetupDrawChannels() - setup ImDrawList channels
// | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission
// | TableDrawContextMenu() - draw right-click context menu
//-----------------------------------------------------------------------------
// - TableHeadersRow() or TableHeader() user submit a headers row (optional)
// | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction
// | TableOpenContextMenu() - when right-clicked: trigger opening of the default context menu
// - TableGetSortSpecs() user queries updated sort specs (optional, generally after submitting headers)
// - TableNextRow() user begin into a new row (also automatically called by TableHeadersRow())
// | TableEndRow() - finish existing row
// | TableBeginRow() - add a new row
// - TableSetColumnIndex() / TableNextColumn() user begin into a cell
// | TableEndCell() - close existing column/cell
// | TableBeginCell() - enter into current column/cell
// - [...] user emit contents
//-----------------------------------------------------------------------------
// - EndTable() user ends the table
// | TableDrawBorders() - draw outer borders, inner vertical borders
// | TableMergeDrawChannels() - merge draw channels if clipping isn't required
// | EndChild() - (if ScrollX/ScrollY is set)
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// TABLE SIZING
//-----------------------------------------------------------------------------
// (Read carefully because this is subtle but it does make sense!)
//-----------------------------------------------------------------------------
// About 'outer_size':
// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags.
// Default value is ImVec2(0.0f, 0.0f).
// X
// - outer_size.x <= 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge.
// - outer_size.x > 0.0f -> Set Fixed width.
// Y with ScrollX/ScrollY disabled: we output table directly in current window
// - outer_size.y < 0.0f -> Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful is parent window can vertically scroll.
// - outer_size.y = 0.0f -> No minimum height (but will auto extend, unless _NoHostExtendY is set)
// - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless _NoHostExtenY is set)
// Y with ScrollX/ScrollY enabled: using a child window for scrolling
// - outer_size.y < 0.0f -> Bottom-align. Not meaningful is parent window can vertically scroll.
// - outer_size.y = 0.0f -> Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window.
// - outer_size.y > 0.0f -> Set Exact height. Recommended when using Scrolling on any axis.
//-----------------------------------------------------------------------------
// Outer size is also affected by the NoHostExtendX/NoHostExtendY flags.
// Important to that note how the two flags have slightly different behaviors!
// - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.
// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible.
// In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height.
// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not easily noticeable)
//-----------------------------------------------------------------------------
// About 'inner_width':
// With ScrollX disabled:
// - inner_width -> *ignored*
// With ScrollX enabled:
// - inner_width < 0.0f -> *illegal* fit in known width (right align from outer_size.x) <-- weird
// - inner_width = 0.0f -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns.
// - inner_width > 0.0f -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space!
//-----------------------------------------------------------------------------
// Details:
// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept
// of "available space" doesn't make sense.
// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding
// of what the value does.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// COLUMNS SIZING POLICIES
//-----------------------------------------------------------------------------
// About overriding column sizing policy and width/weight with TableSetupColumn():
// We use a default parameter of 'init_width_or_weight == -1'.
// - with ImGuiTableColumnFlags_WidthFixed, init_width <= 0 (default) --> width is automatic
// - with ImGuiTableColumnFlags_WidthFixed, init_width > 0 (explicit) --> width is custom
// - with ImGuiTableColumnFlags_WidthStretch, init_weight <= 0 (default) --> weight is 1.0f
// - with ImGuiTableColumnFlags_WidthStretch, init_weight > 0 (explicit) --> weight is custom
// Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f)
// and you can fit a 100.0f wide item in it without clipping and with full padding.
//-----------------------------------------------------------------------------
// About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag)
// - with Table policy ImGuiTableFlags_SizingFixedFit --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width
// - with Table policy ImGuiTableFlags_SizingFixedSame --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is max of all contents width
// - with Table policy ImGuiTableFlags_SizingStretchSame --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f
// - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents
// Default Width and default Weight can be overridden when calling TableSetupColumn().
//-----------------------------------------------------------------------------
// About mixing Fixed/Auto and Stretch columns together:
// - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
// - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place!
// that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in.
// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents.
// - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weight/widths.
//-----------------------------------------------------------------------------
// About using column width:
// If a column is manual resizable or has a width specified with TableSetupColumn():
// - you may use GetContentRegionAvail().x to query the width available in a given column.
// - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width.
// If the column is not resizable and has no width specified with TableSetupColumn():
// - its width will be automatic and be set to the max of items submitted.
// - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN).
// - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN).
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// TABLES CLIPPING/CULLING
//-----------------------------------------------------------------------------
// About clipping/culling of Rows in Tables:
// - For large numbers of rows, it is recommended you use ImGuiListClipper to only submit visible rows.
// ImGuiListClipper is reliant on the fact that rows are of equal height.
// See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper.
// - Note that auto-resizing columns don't play well with using the clipper.
// By default a table with _ScrollX but without _Resizable will have column auto-resize.
// So, if you want to use the clipper, make sure to either enable _Resizable, either setup columns width explicitly with _WidthFixed.
//-----------------------------------------------------------------------------
// About clipping/culling of Columns in Tables:
// - 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 column but one (e.g. the first one).
// - Case A: column is not hidden by user, and at least partially in sight (most common case).
// - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output.
// - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.).
//
// [A] [B] [C]
// TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() return false, user can skip submitting items but only if the column doesn't contribute to row height.
// SkipItems: false false true -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output.
// ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way.
// ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway).
//
// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row.
// However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer.
//-----------------------------------------------------------------------------
// About clipping/culling of whole Tables:
// - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// [SECTION] Header mess
//-----------------------------------------------------------------------------
# if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
# define _CRT_SECURE_NO_WARNINGS
# endif
# include "imgui.h"
# ifndef IMGUI_DISABLE
# ifndef IMGUI_DEFINE_MATH_OPERATORS
# define IMGUI_DEFINE_MATH_OPERATORS
# endif
# include "imgui_internal.h"
// System includes
# if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
# include <stddef.h> // intptr_t
# else
# include <stdint.h> // intptr_t
# endif
// Visual Studio warnings
# ifdef _MSC_VER
# pragma warning (disable: 4127) // condition expression is constant
# pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
# if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
# pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
# endif
# pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
# pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
# endif
// Clang/GCC warnings with -Weverything
# if defined(__clang__)
# if __has_warning("-Wunknown-warning-option")
# pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
# endif
# pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
# pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
# pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
# pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
# pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
# pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
# pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
# pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
# pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
# elif defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
# pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
# pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
# endif
//-----------------------------------------------------------------------------
// [SECTION] Tables: Main code
//-----------------------------------------------------------------------------
// - TableFixFlags() [Internal]
// - TableFindByID() [Internal]
// - BeginTable()
// - BeginTableEx() [Internal]
// - TableBeginInitMemory() [Internal]
// - TableBeginApplyRequests() [Internal]
// - TableSetupColumnFlags() [Internal]
// - TableUpdateLayout() [Internal]
// - TableUpdateBorders() [Internal]
// - EndTable()
// - TableSetupColumn()
// - TableSetupScrollFreeze()
//-----------------------------------------------------------------------------
// Configuration
static const int TABLE_DRAW_CHANNEL_BG0 = 0 ;
static const int TABLE_DRAW_CHANNEL_BG2_FROZEN = 1 ;
static const int TABLE_DRAW_CHANNEL_NOCLIP = 2 ; // When using ImGuiTableFlags_NoClip (this becomes the last visible channel)
static const float TABLE_BORDER_SIZE = 1.0f ; // FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering.
static const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f ; // Extend outside inner borders.
static const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f ; // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped.
// Helper
inline ImGuiTableFlags TableFixFlags ( ImGuiTableFlags flags , ImGuiWindow * outer_window )
{
// Adjust flags: set default sizing policy
if ( ( flags & ImGuiTableFlags_SizingMask_ ) = = 0 )
flags | = ( ( flags & ImGuiTableFlags_ScrollX ) | | ( outer_window - > Flags & ImGuiWindowFlags_AlwaysAutoResize ) ) ? ImGuiTableFlags_SizingFixedFit : ImGuiTableFlags_SizingStretchSame ;
// Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame
if ( ( flags & ImGuiTableFlags_SizingMask_ ) = = ImGuiTableFlags_SizingFixedSame )
flags | = ImGuiTableFlags_NoKeepColumnsVisible ;
// Adjust flags: enforce borders when resizable
if ( flags & ImGuiTableFlags_Resizable )
flags | = ImGuiTableFlags_BordersInnerV ;
// Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on
if ( flags & ( ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY ) )
flags & = ~ ( ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoHostExtendY ) ;
// Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody
if ( flags & ImGuiTableFlags_NoBordersInBodyUntilResize )
flags & = ~ ImGuiTableFlags_NoBordersInBody ;
// Adjust flags: disable saved settings if there's nothing to save
if ( ( flags & ( ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable ) ) = = 0 )
flags | = ImGuiTableFlags_NoSavedSettings ;
// Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set)
if ( outer_window - > RootWindow - > Flags & ImGuiWindowFlags_NoSavedSettings )
flags | = ImGuiTableFlags_NoSavedSettings ;
return flags ;
}
ImGuiTable * ImGui : : TableFindByID ( ImGuiID id )
{
ImGuiContext & g = * GImGui ;
return g . Tables . GetByKey ( id ) ;
}
// Read about "TABLE SIZING" at the top of this file.
bool ImGui : : BeginTable ( const char * str_id , int columns_count , ImGuiTableFlags flags , const ImVec2 & outer_size , float inner_width )
{
ImGuiID id = GetID ( str_id ) ;
return BeginTableEx ( str_id , id , columns_count , flags , outer_size , inner_width ) ;
}
bool ImGui : : BeginTableEx ( const char * name , ImGuiID id , int columns_count , ImGuiTableFlags flags , const ImVec2 & outer_size , float inner_width )
{
ImGuiContext & g = * GImGui ;
ImGuiWindow * outer_window = GetCurrentWindow ( ) ;
if ( outer_window - > SkipItems ) // Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible.
return false ;
// Sanity checks
IM_ASSERT ( columns_count > 0 & & columns_count < = IMGUI_TABLE_MAX_COLUMNS & & " Only 1..64 columns allowed! " ) ;
if ( flags & ImGuiTableFlags_ScrollX )
IM_ASSERT ( inner_width > = 0.0f ) ;
// If an outer size is specified ahead we will be able to early out when not visible. Exact clipping rules may evolve.
const bool use_child_window = ( flags & ( ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY ) ) ! = 0 ;
const ImVec2 avail_size = GetContentRegionAvail ( ) ;
ImVec2 actual_outer_size = CalcItemSize ( outer_size , ImMax ( avail_size . x , 1.0f ) , use_child_window ? ImMax ( avail_size . y , 1.0f ) : 0.0f ) ;
ImRect outer_rect ( outer_window - > DC . CursorPos , outer_window - > DC . CursorPos + actual_outer_size ) ;
2022-12-11 05:57:49 +01:00
if ( use_child_window & & IsClippedEx ( outer_rect , 0 ) )
2021-09-05 09:47:05 +02:00
{
ItemSize ( outer_rect ) ;
return false ;
}
// Acquire storage for the table
ImGuiTable * table = g . Tables . GetOrAddByKey ( id ) ;
const int instance_no = ( table - > LastFrameActive ! = g . FrameCount ) ? 0 : table - > InstanceCurrent + 1 ;
const ImGuiID instance_id = id + instance_no ;
const ImGuiTableFlags table_last_flags = table - > Flags ;
if ( instance_no > 0 )
IM_ASSERT ( table - > ColumnsCount = = columns_count & & " BeginTable(): Cannot change columns count mid-frame while preserving same ID " ) ;
// Acquire temporary buffers
const int table_idx = g . Tables . GetIndex ( table ) ;
2022-12-11 05:57:49 +01:00
if ( + + g . TablesTempDataStacked > g . TablesTempData . Size )
g . TablesTempData . resize ( g . TablesTempDataStacked , ImGuiTableTempData ( ) ) ;
ImGuiTableTempData * temp_data = table - > TempData = & g . TablesTempData [ g . TablesTempDataStacked - 1 ] ;
2021-09-05 09:47:05 +02:00
temp_data - > TableIndex = table_idx ;
table - > DrawSplitter = & table - > TempData - > DrawSplitter ;
table - > DrawSplitter - > Clear ( ) ;
// Fix flags
table - > IsDefaultSizingPolicy = ( flags & ImGuiTableFlags_SizingMask_ ) = = 0 ;
flags = TableFixFlags ( flags , outer_window ) ;
// Initialize
table - > ID = id ;
table - > Flags = flags ;
table - > InstanceCurrent = ( ImS16 ) instance_no ;
table - > LastFrameActive = g . FrameCount ;
table - > OuterWindow = table - > InnerWindow = outer_window ;
table - > ColumnsCount = columns_count ;
table - > IsLayoutLocked = false ;
table - > InnerWidth = inner_width ;
temp_data - > UserOuterSize = outer_size ;
2022-12-11 05:57:49 +01:00
if ( instance_no > 0 & & table - > InstanceDataExtra . Size < instance_no )
table - > InstanceDataExtra . push_back ( ImGuiTableInstanceData ( ) ) ;
2021-09-05 09:47:05 +02:00
// When not using a child window, WorkRect.Max will grow as we append contents.
if ( use_child_window )
{
// Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent
// (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing)
ImVec2 override_content_size ( FLT_MAX , FLT_MAX ) ;
if ( ( flags & ImGuiTableFlags_ScrollX ) & & ! ( flags & ImGuiTableFlags_ScrollY ) )
override_content_size . y = FLT_MIN ;
// Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and
// never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align
// based on the right side of the child window work rect, which would require knowing ahead if we are going to
// have decoration taking horizontal spaces (typically a vertical scrollbar).
if ( ( flags & ImGuiTableFlags_ScrollX ) & & inner_width > 0.0f )
override_content_size . x = inner_width ;
if ( override_content_size . x ! = FLT_MAX | | override_content_size . y ! = FLT_MAX )
SetNextWindowContentSize ( ImVec2 ( override_content_size . x ! = FLT_MAX ? override_content_size . x : 0.0f , override_content_size . y ! = FLT_MAX ? override_content_size . y : 0.0f ) ) ;
// Reset scroll if we are reactivating it
if ( ( table_last_flags & ( ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY ) ) = = 0 )
SetNextWindowScroll ( ImVec2 ( 0.0f , 0.0f ) ) ;
// Create scrolling region (without border and zero window padding)
ImGuiWindowFlags child_flags = ( flags & ImGuiTableFlags_ScrollX ) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None ;
BeginChildEx ( name , instance_id , outer_rect . GetSize ( ) , false , child_flags ) ;
table - > InnerWindow = g . CurrentWindow ;
table - > WorkRect = table - > InnerWindow - > WorkRect ;
table - > OuterRect = table - > InnerWindow - > Rect ( ) ;
table - > InnerRect = table - > InnerWindow - > InnerRect ;
IM_ASSERT ( table - > InnerWindow - > WindowPadding . x = = 0.0f & & table - > InnerWindow - > WindowPadding . y = = 0.0f & & table - > InnerWindow - > WindowBorderSize = = 0.0f ) ;
2022-12-11 05:57:49 +01:00
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned)
if ( instance_no = = 0 )
{
table - > HasScrollbarYPrev = table - > HasScrollbarYCurr ;
table - > HasScrollbarYCurr = false ;
}
table - > HasScrollbarYCurr | = ( table - > InnerWindow - > ScrollMax . y > 0.0f ) ;
2021-09-05 09:47:05 +02:00
}
else
{
// For non-scrolling tables, WorkRect == OuterRect == InnerRect.
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
table - > WorkRect = table - > OuterRect = table - > InnerRect = outer_rect ;
}
// Push a standardized ID for both child-using and not-child-using tables
PushOverrideID ( instance_id ) ;
// Backup a copy of host window members we will modify
ImGuiWindow * inner_window = table - > InnerWindow ;
table - > HostIndentX = inner_window - > DC . Indent . x ;
table - > HostClipRect = inner_window - > ClipRect ;
table - > HostSkipItems = inner_window - > SkipItems ;
temp_data - > HostBackupWorkRect = inner_window - > WorkRect ;
temp_data - > HostBackupParentWorkRect = inner_window - > ParentWorkRect ;
temp_data - > HostBackupColumnsOffset = outer_window - > DC . ColumnsOffset ;
temp_data - > HostBackupPrevLineSize = inner_window - > DC . PrevLineSize ;
temp_data - > HostBackupCurrLineSize = inner_window - > DC . CurrLineSize ;
temp_data - > HostBackupCursorMaxPos = inner_window - > DC . CursorMaxPos ;
temp_data - > HostBackupItemWidth = outer_window - > DC . ItemWidth ;
temp_data - > HostBackupItemWidthStackSize = outer_window - > DC . ItemWidthStack . Size ;
inner_window - > DC . PrevLineSize = inner_window - > DC . CurrLineSize = ImVec2 ( 0.0f , 0.0f ) ;
// Padding and Spacing
// - None ........Content..... Pad .....Content........
// - PadOuter | Pad ..Content..... Pad .....Content.. Pad |
// - PadInner ........Content.. Pad | Pad ..Content........
// - PadOuter+PadInner | Pad ..Content.. Pad | Pad ..Content.. Pad |
const bool pad_outer_x = ( flags & ImGuiTableFlags_NoPadOuterX ) ? false : ( flags & ImGuiTableFlags_PadOuterX ) ? true : ( flags & ImGuiTableFlags_BordersOuterV ) ! = 0 ;
const bool pad_inner_x = ( flags & ImGuiTableFlags_NoPadInnerX ) ? false : true ;
const float inner_spacing_for_border = ( flags & ImGuiTableFlags_BordersInnerV ) ? TABLE_BORDER_SIZE : 0.0f ;
const float inner_spacing_explicit = ( pad_inner_x & & ( flags & ImGuiTableFlags_BordersInnerV ) = = 0 ) ? g . Style . CellPadding . x : 0.0f ;
const float inner_padding_explicit = ( pad_inner_x & & ( flags & ImGuiTableFlags_BordersInnerV ) ! = 0 ) ? g . Style . CellPadding . x : 0.0f ;
table - > CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border ;
table - > CellSpacingX2 = inner_spacing_explicit ;
table - > CellPaddingX = inner_padding_explicit ;
table - > CellPaddingY = g . Style . CellPadding . y ;
const float outer_padding_for_border = ( flags & ImGuiTableFlags_BordersOuterV ) ? TABLE_BORDER_SIZE : 0.0f ;
const float outer_padding_explicit = pad_outer_x ? g . Style . CellPadding . x : 0.0f ;
table - > OuterPaddingX = ( outer_padding_for_border + outer_padding_explicit ) - table - > CellPaddingX ;
table - > CurrentColumn = - 1 ;
table - > CurrentRow = - 1 ;
table - > RowBgColorCounter = 0 ;
table - > LastRowFlags = ImGuiTableRowFlags_None ;
table - > InnerClipRect = ( inner_window = = outer_window ) ? table - > WorkRect : inner_window - > ClipRect ;
table - > InnerClipRect . ClipWith ( table - > WorkRect ) ; // We need this to honor inner_width
table - > InnerClipRect . ClipWithFull ( table - > HostClipRect ) ;
table - > InnerClipRect . Max . y = ( flags & ImGuiTableFlags_NoHostExtendY ) ? ImMin ( table - > InnerClipRect . Max . y , inner_window - > WorkRect . Max . y ) : inner_window - > ClipRect . Max . y ;
table - > RowPosY1 = table - > RowPosY2 = table - > WorkRect . Min . y ; // This is needed somehow
table - > RowTextBaseline = 0.0f ; // This will be cleared again by TableBeginRow()
table - > FreezeRowsRequest = table - > FreezeRowsCount = 0 ; // This will be setup by TableSetupScrollFreeze(), if any
table - > FreezeColumnsRequest = table - > FreezeColumnsCount = 0 ;
table - > IsUnfrozenRows = true ;
table - > DeclColumnsCount = 0 ;
// Using opaque colors facilitate overlapping elements of the grid
table - > BorderColorStrong = GetColorU32 ( ImGuiCol_TableBorderStrong ) ;
table - > BorderColorLight = GetColorU32 ( ImGuiCol_TableBorderLight ) ;
// Make table current
g . CurrentTable = table ;
outer_window - > DC . CurrentTableIdx = table_idx ;
if ( inner_window ! = outer_window ) // So EndChild() within the inner window can restore the table properly.
inner_window - > DC . CurrentTableIdx = table_idx ;
if ( ( table_last_flags & ImGuiTableFlags_Reorderable ) & & ( flags & ImGuiTableFlags_Reorderable ) = = 0 )
table - > IsResetDisplayOrderRequest = true ;
// Mark as used
if ( table_idx > = g . TablesLastTimeActive . Size )
g . TablesLastTimeActive . resize ( table_idx + 1 , - 1.0f ) ;
g . TablesLastTimeActive [ table_idx ] = ( float ) g . Time ;
temp_data - > LastTimeActive = ( float ) g . Time ;
table - > MemoryCompacted = false ;
// Setup memory buffer (clear data if columns count changed)
ImGuiTableColumn * old_columns_to_preserve = NULL ;
void * old_columns_raw_data = NULL ;
const int old_columns_count = table - > Columns . size ( ) ;
if ( old_columns_count ! = 0 & & old_columns_count ! = columns_count )
{
// Attempt to preserve width on column count change (#4046)
old_columns_to_preserve = table - > Columns . Data ;
old_columns_raw_data = table - > RawData ;
table - > RawData = NULL ;
}
if ( table - > RawData = = NULL )
{
TableBeginInitMemory ( table , columns_count ) ;
table - > IsInitializing = table - > IsSettingsRequestLoad = true ;
}
if ( table - > IsResetAllRequest )
TableResetSettings ( table ) ;
if ( table - > IsInitializing )
{
// Initialize
table - > SettingsOffset = - 1 ;
table - > IsSortSpecsDirty = true ;
table - > InstanceInteracted = - 1 ;
table - > ContextPopupColumn = - 1 ;
table - > ReorderColumn = table - > ResizedColumn = table - > LastResizedColumn = - 1 ;
table - > AutoFitSingleColumn = - 1 ;
table - > HoveredColumnBody = table - > HoveredColumnBorder = - 1 ;
for ( int n = 0 ; n < columns_count ; n + + )
{
ImGuiTableColumn * column = & table - > Columns [ n ] ;
if ( old_columns_to_preserve & & n < old_columns_count )
{
// FIXME: We don't attempt to preserve column order in this path.
* column = old_columns_to_preserve [ n ] ;
}
else
{
float width_auto = column - > WidthAuto ;
* column = ImGuiTableColumn ( ) ;
column - > WidthAuto = width_auto ;
column - > IsPreserveWidthAuto = true ; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker
column - > IsEnabled = column - > IsUserEnabled = column - > IsUserEnabledNextFrame = true ;
}
column - > DisplayOrder = table - > DisplayOrderToIndex [ n ] = ( ImGuiTableColumnIdx ) n ;
}
}
if ( old_columns_raw_data )
IM_FREE ( old_columns_raw_data ) ;
// Load settings
if ( table - > IsSettingsRequestLoad )
TableLoadSettings ( table ) ;
// Handle DPI/font resize
// This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.
// It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.
// FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.
// This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.
const float new_ref_scale_unit = g . FontSize ; // g.Font->GetCharAdvance('A') ?
if ( table - > RefScale ! = 0.0f & & table - > RefScale ! = new_ref_scale_unit )
{
const float scale_factor = new_ref_scale_unit / table - > RefScale ;
2022-12-11 05:57:49 +01:00
//IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);
2021-09-05 09:47:05 +02:00
for ( int n = 0 ; n < columns_count ; n + + )
table - > Columns [ n ] . WidthRequest = table - > Columns [ n ] . WidthRequest * scale_factor ;
}
table - > RefScale = new_ref_scale_unit ;
// Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call..
// This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user.
// Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.
inner_window - > SkipItems = true ;
// Clear names
// At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn()
if ( table - > ColumnsNames . Buf . Size > 0 )
table - > ColumnsNames . Buf . resize ( 0 ) ;
// Apply queued resizing/reordering/hiding requests
TableBeginApplyRequests ( table ) ;
return true ;
}
// For reference, the average total _allocation count_ for a table is:
// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables)
// + 1 (for table->RawData allocated below)
// + 1 (for table->ColumnsNames, if names are used)
2022-12-11 05:57:49 +01:00
// Shared allocations per number of nested tables
2021-09-05 09:47:05 +02:00
// + 1 (for table->Splitter._Channels)
// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels)
// Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details.
// Unused channels don't perform their +2 allocations.
void ImGui : : TableBeginInitMemory ( ImGuiTable * table , int columns_count )
{
// Allocate single buffer for our arrays
ImSpanAllocator < 3 > span_allocator ;
span_allocator . Reserve ( 0 , columns_count * sizeof ( ImGuiTableColumn ) ) ;
span_allocator . Reserve ( 1 , columns_count * sizeof ( ImGuiTableColumnIdx ) ) ;
span_allocator . Reserve ( 2 , columns_count * sizeof ( ImGuiTableCellData ) , 4 ) ;
table - > RawData = IM_ALLOC ( span_allocator . GetArenaSizeInBytes ( ) ) ;
memset ( table - > RawData , 0 , span_allocator . GetArenaSizeInBytes ( ) ) ;
span_allocator . SetArenaBasePtr ( table - > RawData ) ;
span_allocator . GetSpan ( 0 , & table - > Columns ) ;
span_allocator . GetSpan ( 1 , & table - > DisplayOrderToIndex ) ;
span_allocator . GetSpan ( 2 , & table - > RowCellData ) ;
}
// Apply queued resizing/reordering/hiding requests
void ImGui : : TableBeginApplyRequests ( ImGuiTable * table )
{
// Handle resizing request
// (We process this at the first TableBegin of the frame)
// FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling?
if ( table - > InstanceCurrent = = 0 )
{
if ( table - > ResizedColumn ! = - 1 & & table - > ResizedColumnNextWidth ! = FLT_MAX )
TableSetColumnWidth ( table - > ResizedColumn , table - > ResizedColumnNextWidth ) ;
table - > LastResizedColumn = table - > ResizedColumn ;
table - > ResizedColumnNextWidth = FLT_MAX ;
table - > ResizedColumn = - 1 ;
// Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy.
// FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings.
if ( table - > AutoFitSingleColumn ! = - 1 )
{
TableSetColumnWidth ( table - > AutoFitSingleColumn , table - > Columns [ table - > AutoFitSingleColumn ] . WidthAuto ) ;
table - > AutoFitSingleColumn = - 1 ;
}
}
// Handle reordering request
// Note: we don't clear ReorderColumn after handling the request.
if ( table - > InstanceCurrent = = 0 )
{
if ( table - > HeldHeaderColumn = = - 1 & & table - > ReorderColumn ! = - 1 )
table - > ReorderColumn = - 1 ;
table - > HeldHeaderColumn = - 1 ;
if ( table - > ReorderColumn ! = - 1 & & table - > ReorderColumnDir ! = 0 )
{
// We need to handle reordering across hidden columns.
// In the configuration below, moving C to the right of E will lead to:
// ... C [D] E ---> ... [D] E C (Column name/index)
// ... 2 3 4 ... 2 3 4 (Display order)
const int reorder_dir = table - > ReorderColumnDir ;
IM_ASSERT ( reorder_dir = = - 1 | | reorder_dir = = + 1 ) ;
IM_ASSERT ( table - > Flags & ImGuiTableFlags_Reorderable ) ;
ImGuiTableColumn * src_column = & table - > Columns [ table - > ReorderColumn ] ;
ImGuiTableColumn * dst_column = & table - > Columns [ ( reorder_dir = = - 1 ) ? src_column - > PrevEnabledColumn : src_column - > NextEnabledColumn ] ;
IM_UNUSED ( dst_column ) ;
const int src_order = src_column - > DisplayOrder ;
const int dst_order = dst_column - > DisplayOrder ;
src_column - > DisplayOrder = ( ImGuiTableColumnIdx ) dst_order ;
for ( int order_n = src_order + reorder_dir ; order_n ! = dst_order + reorder_dir ; order_n + = reorder_dir )
table - > Columns [ table - > DisplayOrderToIndex [ order_n ] ] . DisplayOrder - = ( ImGuiTableColumnIdx ) reorder_dir ;
IM_ASSERT ( dst_column - > DisplayOrder = = dst_order - reorder_dir ) ;
// Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[],
// rebuild the later from the former.
for ( int column_n = 0 ; column_n < table - > ColumnsCount ; column_n + + )
table - > DisplayOrderToIndex [ table - > Columns [ column_n ] . DisplayOrder ] = ( ImGuiTableColumnIdx ) column_n ;
table - > ReorderColumnDir = 0 ;
table - > IsSettingsDirty = true ;
}
}
// Handle display order reset request
if ( table - > IsResetDisplayOrderRequest )
{
for ( int n = 0 ; n < table - > ColumnsCount ; n + + )
table - > DisplayOrderToIndex [ n ] = table - > Columns [ n ] . DisplayOrder = ( ImGuiTableColumnIdx ) n ;
table - > IsResetDisplayOrderRequest = false ;
table - > IsSettingsDirty = true ;
}
}
// Adjust flags: default width mode + stretch columns are not allowed when auto extending
static void TableSetupColumnFlags ( ImGuiTable * table , ImGuiTableColumn * column , ImGuiTableColumnFlags flags_in )
{
ImGuiTableColumnFlags flags = flags_in ;
// Sizing Policy
if ( ( flags & ImGuiTableColumnFlags_WidthMask_ ) = = 0 )
{
const ImGuiTableFlags table_sizing_policy = ( table - > Flags & ImGuiTableFlags_SizingMask_ ) ;
if ( table_sizing_policy = = ImGuiTableFlags_SizingFixedFit | | table_sizing_policy = = ImGuiTableFlags_SizingFixedSame )
flags | = ImGuiTableColumnFlags_WidthFixed ;
else
flags | = ImGuiTableColumnFlags_WidthStretch ;
}
else
{
IM_ASSERT ( ImIsPowerOfTwo ( flags & ImGuiTableColumnFlags_WidthMask_ ) ) ; // Check that only 1 of each set is used.
}
// Resize
if ( ( table - > Flags & ImGuiTableFlags_Resizable ) = = 0 )
flags | = ImGuiTableColumnFlags_NoResize ;
// Sorting
if ( ( flags & ImGuiTableColumnFlags_NoSortAscending ) & & ( flags & ImGuiTableColumnFlags_NoSortDescending ) )
flags | = ImGuiTableColumnFlags_NoSort ;
// Indentation
if ( ( flags & ImGuiTableColumnFlags_IndentMask_ ) = = 0 )
flags | = ( table - > Columns . index_from_ptr ( column ) = = 0 ) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable ;
// Alignment
//if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0)
// flags |= ImGuiTableColumnFlags_AlignCenter;
//IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used.
// Preserve status flags
column - > Flags = flags | ( column - > Flags & ImGuiTableColumnFlags_StatusMask_ ) ;
// Build an ordered list of available sort directions
column - > SortDirectionsAvailCount = column - > SortDirectionsAvailMask = column - > SortDirectionsAvailList = 0 ;
if ( table - > Flags & ImGuiTableFlags_Sortable )
{
int count = 0 , mask = 0 , list = 0 ;
if ( ( flags & ImGuiTableColumnFlags_PreferSortAscending ) ! = 0 & & ( flags & ImGuiTableColumnFlags_NoSortAscending ) = = 0 ) { mask | = 1 < < ImGuiSortDirection_Ascending ; list | = ImGuiSortDirection_Ascending < < ( count < < 1 ) ; count + + ; }
if ( ( flags & ImGuiTableColumnFlags_PreferSortDescending ) ! = 0 & & ( flags & ImGuiTableColumnFlags_NoSortDescending ) = = 0 ) { mask | = 1 < < ImGuiSortDirection_Descending ; list | = ImGuiSortDirection_Descending < < ( count < < 1 ) ; count + + ; }
if ( ( flags & ImGuiTableColumnFlags_PreferSortAscending ) = = 0 & & ( flags & ImGuiTableColumnFlags_NoSortAscending ) = = 0 ) { mask | = 1 < < ImGuiSortDirection_Ascending ; list | = ImGuiSortDirection_Ascending < < ( count < < 1 ) ; count + + ; }
if ( ( flags & ImGuiTableColumnFlags_PreferSortDescending ) = = 0 & & ( flags & ImGuiTableColumnFlags_NoSortDescending ) = = 0 ) { mask | = 1 < < ImGuiSortDirection_Descending ; list | = ImGuiSortDirection_Descending < < ( count < < 1 ) ; count + + ; }
if ( ( table - > Flags & ImGuiTableFlags_SortTristate ) | | count = = 0 ) { mask | = 1 < < ImGuiSortDirection_None ; count + + ; }
column - > SortDirectionsAvailList = ( ImU8 ) list ;
column - > SortDirectionsAvailMask = ( ImU8 ) mask ;
column - > SortDirectionsAvailCount = ( ImU8 ) count ;
ImGui : : TableFixColumnSortDirection ( table , column ) ;
}
}
// Layout columns for the frame. This is in essence the followup to BeginTable().
// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() to be called first.
// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns.
// Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns?
void ImGui : : TableUpdateLayout ( ImGuiTable * table )
{
ImGuiContext & g = * GImGui ;
IM_ASSERT ( table - > IsLayoutLocked = = false ) ;
const ImGuiTableFlags table_sizing_policy = ( table - > Flags & ImGuiTableFlags_SizingMask_ ) ;
table - > IsDefaultDisplayOrder = true ;
table - > ColumnsEnabledCount = 0 ;
table - > EnabledMaskByIndex = 0x00 ;
table - > EnabledMaskByDisplayOrder = 0x00 ;
table - > LeftMostEnabledColumn = - 1 ;
table - > MinColumnWidth = ImMax ( 1.0f , g . Style . FramePadding . x * 1.0f ) ; // g.Style.ColumnsMinSpacing; // FIXME-TABLE
// [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns.
// Process columns in their visible orders as we are building the Prev/Next indices.
int count_fixed = 0 ; // Number of columns that have fixed sizing policies
int count_stretch = 0 ; // Number of columns that have stretch sizing policies
int prev_visible_column_idx = - 1 ;
bool has_auto_fit_request = false ;
bool has_resizable = false ;
float stretch_sum_width_auto = 0.0f ;
float fixed_max_width_auto = 0.0f ;
for ( int order_n = 0 ; order_n < table - > ColumnsCount ; order_n + + )
{
const int column_n = table - > DisplayOrderToIndex [ order_n ] ;
if ( column_n ! = order_n )
table - > IsDefaultDisplayOrder = false ;
ImGuiTableColumn * column = & table - > Columns [ column_n ] ;
// Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame.
// It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway.
// We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect.
if ( table - > DeclColumnsCount < = column_n )
{
TableSetupColumnFlags ( table , column , ImGuiTableColumnFlags_None ) ;
column - > NameOffset = - 1 ;
column - > UserID = 0 ;
column - > InitStretchWeightOrWidth = - 1.0f ;
}
// Update Enabled state, mark settings and sort specs dirty
if ( ! ( table - > Flags & ImGuiTableFlags_Hideable ) | | ( column - > Flags & ImGuiTableColumnFlags_NoHide ) )
column - > IsUserEnabledNextFrame = true ;
if ( column - > IsUserEnabled ! = column - > IsUserEnabledNextFrame )
{
column - > IsUserEnabled = column - > IsUserEnabledNextFrame ;
table - > IsSettingsDirty = true ;
}
column - > IsEnabled = column - > IsUserEnabled & & ( column - > Flags & ImGuiTableColumnFlags_Disabled ) = = 0 ;
if ( column - > SortOrder ! = - 1 & & ! column - > IsEnabled )
table - > IsSortSpecsDirty = true ;
if ( column - > SortOrder > 0 & & ! ( table - > Flags & ImGuiTableFlags_SortMulti ) )
table - > IsSortSpecsDirty = true ;
// Auto-fit unsized columns
const bool start_auto_fit = ( column - > Flags & ImGuiTableColumnFlags_WidthFixed ) ? ( column - > WidthRequest < 0.0f ) : ( column - > StretchWeight < 0.0f ) ;
if ( start_auto_fit )
column - > AutoFitQueue = column - > CannotSkipItemsQueue = ( 1 < < 3 ) - 1 ; // Fit for three frames
if ( ! column - > IsEnabled )
{
column - > IndexWithinEnabledSet = - 1 ;
continue ;
}
// Mark as enabled and link to previous/next enabled column
column - > PrevEnabledColumn = ( ImGuiTableColumnIdx ) prev_visible_column_idx ;
column - > NextEnabledColumn = - 1 ;
if ( prev_visible_column_idx ! = - 1 )
table - > Columns [ prev_visible_column_idx ] . NextEnabledColumn = ( ImGuiTableColumnIdx ) column_n ;
else
table - > LeftMostEnabledColumn = ( ImGuiTableColumnIdx ) column_n ;
column - > IndexWithinEnabledSet = table - > ColumnsEnabledCount + + ;
table - > EnabledMaskByIndex | = ( ImU64 ) 1 < < column_n ;
table - > EnabledMaskByDisplayOrder | = ( ImU64 ) 1 < < column - > DisplayOrder ;
prev_visible_column_idx = column_n ;
IM_ASSERT ( column - > IndexWithinEnabledSet < = column - > DisplayOrder ) ;
// Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
// Combine width from regular rows + width from headers unless requested not to.
if ( ! column - > IsPreserveWidthAuto )
column - > WidthAuto = TableGetColumnWidthAuto ( table , column ) ;
// Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
const bool column_is_resizable = ( column - > Flags & ImGuiTableColumnFlags_NoResize ) = = 0 ;
if ( column_is_resizable )
has_resizable = true ;
if ( ( column - > Flags & ImGuiTableColumnFlags_WidthFixed ) & & column - > InitStretchWeightOrWidth > 0.0f & & ! column_is_resizable )
column - > WidthAuto = column - > InitStretchWeightOrWidth ;
if ( column - > AutoFitQueue ! = 0x00 )
has_auto_fit_request = true ;
if ( column - > Flags & ImGuiTableColumnFlags_WidthStretch )
{
stretch_sum_width_auto + = column - > WidthAuto ;
count_stretch + + ;
}
else
{
fixed_max_width_auto = ImMax ( fixed_max_width_auto , column - > WidthAuto ) ;
count_fixed + + ;
}
}
if ( ( table - > Flags & ImGuiTableFlags_Sortable ) & & table - > SortSpecsCount = = 0 & & ! ( table - > Flags & ImGuiTableFlags_SortTristate ) )
table - > IsSortSpecsDirty = true ;
table - > RightMostEnabledColumn = ( ImGuiTableColumnIdx ) prev_visible_column_idx ;
IM_ASSERT ( table - > LeftMostEnabledColumn > = 0 & & table - > RightMostEnabledColumn > = 0 ) ;
// [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible
// to avoid the column fitting having to wait until the first visible frame of the child container (may or not be a good thing).
// FIXME-TABLE: for always auto-resizing columns may not want to do that all the time.
if ( has_auto_fit_request & & table - > OuterWindow ! = table - > InnerWindow )
table - > InnerWindow - > SkipItems = false ;
if ( has_auto_fit_request )
table - > IsSettingsDirty = true ;
// [Part 3] Fix column flags and record a few extra information.
float sum_width_requests = 0.0f ; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding.
float stretch_sum_weights = 0.0f ; // Sum of all weights for stretch columns.
table - > LeftMostStretchedColumn = table - > RightMostStretchedColumn = - 1 ;
for ( int column_n = 0 ; column_n < table - > ColumnsCount ; column_n + + )
{
if ( ! ( table - > EnabledMaskByIndex & ( ( ImU64 ) 1 < < column_n ) ) )
continue ;
ImGuiTableColumn * column = & table - > Columns [ column_n ] ;
const bool column_is_resizable = ( column - > Flags & ImGuiTableColumnFlags_NoResize ) = = 0 ;
if ( column - > Flags & ImGuiTableColumnFlags_WidthFixed )
{
// Apply same widths policy
float width_auto = column - > WidthAuto ;
if ( table_sizing_policy = = ImGuiTableFlags_SizingFixedSame & & ( column - > AutoFitQueue ! = 0x00 | | ! column_is_resizable ) )
width_auto = fixed_max_width_auto ;
// Apply automatic width
// Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)
if ( column - > AutoFitQueue ! = 0x00 )
column - > WidthRequest = width_auto ;
else if ( ( column - > Flags & ImGuiTableColumnFlags_WidthFixed ) & & ! column_is_resizable & & ( table - > RequestOutputMaskByIndex & ( ( ImU64 ) 1 < < column_n ) ) )
column - > WidthRequest = width_auto ;
// FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets
// (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very
// large height (= first frame scrollbar display very off + clipper would skip lots of items).
// This is merely making the side-effect less extreme, but doesn't properly fixes it.
// FIXME: Move this to ->WidthGiven to avoid temporary lossyless?
// FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.
if ( column - > AutoFitQueue > 0x01 & & table - > IsInitializing & & ! column - > IsPreserveWidthAuto )
column - > WidthRequest = ImMax ( column - > WidthRequest , table - > MinColumnWidth * 4.0f ) ; // FIXME-TABLE: Another constant/scale?
sum_width_requests + = column - > WidthRequest ;
}
else
{
// Initialize stretch weight
if ( column - > AutoFitQueue ! = 0x00 | | column - > StretchWeight < 0.0f | | ! column_is_resizable )
{
if ( column - > InitStretchWeightOrWidth > 0.0f )
column - > StretchWeight = column - > InitStretchWeightOrWidth ;
else if ( table_sizing_policy = = ImGuiTableFlags_SizingStretchProp )
column - > StretchWeight = ( column - > WidthAuto / stretch_sum_width_auto ) * count_stretch ;
else
column - > StretchWeight = 1.0f ;
}
stretch_sum_weights + = column - > StretchWeight ;
if ( table - > LeftMostStretchedColumn = = - 1 | | table - > Columns [ table - > LeftMostStretchedColumn ] . DisplayOrder > column - > DisplayOrder )
table - > LeftMostStretchedColumn = ( ImGuiTableColumnIdx ) column_n ;
if ( table - > RightMostStretchedColumn = = - 1 | | table - > Columns [ table - > RightMostStretchedColumn ] . DisplayOrder < column - > DisplayOrder )
table - > RightMostStretchedColumn = ( ImGuiTableColumnIdx ) column_n ;
}
column - > IsPreserveWidthAuto = false ;
sum_width_requests + = table - > CellPaddingX * 2.0f ;
}
table - > ColumnsEnabledFixedCount = ( ImGuiTableColumnIdx ) count_fixed ;
2022-12-11 05:57:49 +01:00
table - > ColumnsStretchSumWeights = stretch_sum_weights ;
2021-09-05 09:47:05 +02:00
// [Part 4] Apply final widths based on requested widths
const ImRect work_rect = table - > WorkRect ;
const float width_spacings = ( table - > OuterPaddingX * 2.0f ) + ( table - > CellSpacingX1 + table - > CellSpacingX2 ) * ( table - > ColumnsEnabledCount - 1 ) ;
2022-12-11 05:57:49 +01:00
const float width_removed = ( table - > HasScrollbarYPrev & & ! table - > InnerWindow - > ScrollbarY ) ? g . Style . ScrollbarSize : 0.0f ; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920)
const float width_avail = ImMax ( 1.0f , ( ( ( table - > Flags & ImGuiTableFlags_ScrollX ) & & table - > InnerWidth = = 0.0f ) ? table - > InnerClipRect . GetWidth ( ) : work_rect . GetWidth ( ) ) - width_removed ) ;
2021-09-05 09:47:05 +02:00
const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests ;
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns ;
table - > ColumnsGivenWidth = width_spacings + ( table - > CellPaddingX * 2.0f ) * table - > ColumnsEnabledCount ;
for ( int column_n = 0 ; column_n < table - > ColumnsCount ; column_n + + )
{
if ( ! ( table - > EnabledMaskByIndex & ( ( ImU64 ) 1 < < column_n ) ) )
continue ;
ImGuiTableColumn * column = & table - > Columns [ column_n ] ;
// Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest)
if ( column - > Flags & ImGuiTableColumnFlags_WidthStretch )
{
float weight_ratio = column - > StretchWeight / stretch_sum_weights ;
column - > WidthRequest = IM_FLOOR ( ImMax ( width_avail_for_stretched_columns * weight_ratio , table - > MinColumnWidth ) + 0.01f ) ;
width_remaining_for_stretched_columns - = column - > WidthRequest ;
}
// [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column
// See additional comments in TableSetColumnWidth().
if ( column - > NextEnabledColumn = = - 1 & & table - > LeftMostStretchedColumn ! = - 1 )
column - > Flags | = ImGuiTableColumnFlags_NoDirectResize_ ;
// Assign final width, record width in case we will need to shrink
column - > WidthGiven = ImFloor ( ImMax ( column - > WidthRequest , table - > MinColumnWidth ) ) ;
table - > ColumnsGivenWidth + = column - > WidthGiven ;
}
// [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column).
// Using right-to-left distribution (more likely to match resizing cursor).
if ( width_remaining_for_stretched_columns > = 1.0f & & ! ( table - > Flags & ImGuiTableFlags_PreciseWidths ) )
for ( int order_n = table - > ColumnsCount - 1 ; stretch_sum_weights > 0.0f & & width_remaining_for_stretched_columns > = 1.0f & & order_n > = 0 ; order_n - - )
{
if ( ! ( table - > EnabledMaskByDisplayOrder & ( ( ImU64 ) 1 < < order_n ) ) )
continue ;
ImGuiTableColumn * column = & table - > Columns [ table - > DisplayOrderToIndex [ order_n ] ] ;
if ( ! ( column - > Flags & ImGuiTableColumnFlags_WidthStretch ) )
continue ;
column - > WidthRequest + = 1.0f ;
column - > WidthGiven + = 1.0f ;
width_remaining_for_stretched_columns - = 1.0f ;
}
2022-12-11 05:57:49 +01:00
// Determine if table is hovered which will be used to flag columns as hovered.
// - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
// but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily
// clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem).
// - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop.
ImGuiTableInstanceData * table_instance = TableGetInstanceData ( table , table - > InstanceCurrent ) ;
2021-09-05 09:47:05 +02:00
table - > HoveredColumnBody = - 1 ;
table - > HoveredColumnBorder = - 1 ;
2022-12-11 05:57:49 +01:00
const ImRect mouse_hit_rect ( table - > OuterRect . Min . x , table - > OuterRect . Min . y , table - > OuterRect . Max . x , ImMax ( table - > OuterRect . Max . y , table - > OuterRect . Min . y + table_instance - > LastOuterHeight ) ) ;
const ImGuiID backup_active_id = g . ActiveId ;
g . ActiveId = 0 ;
2021-09-05 09:47:05 +02:00
const bool is_hovering_table = ItemHoverable ( mouse_hit_rect , 0 ) ;
2022-12-11 05:57:49 +01:00
g . ActiveId = backup_active_id ;
2021-09-05 09:47:05 +02:00
// [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
// Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.
int visible_n = 0 ;
bool offset_x_frozen = ( table - > FreezeColumnsCount > 0 ) ;
float offset_x = ( ( table - > FreezeColumnsCount > 0 ) ? table - > OuterRect . Min . x : work_rect . Min . x ) + table - > OuterPaddingX - table - > CellSpacingX1 ;
ImRect host_clip_rect = table - > InnerClipRect ;
//host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;
table - > VisibleMaskByIndex = 0x00 ;
table - > RequestOutputMaskByIndex = 0x00 ;
for ( int order_n = 0 ; order_n < table - > ColumnsCount ; order_n + + )
{
const int column_n = table - > DisplayOrderToIndex [ order_n ] ;
ImGuiTableColumn * column = & table - > Columns [ column_n ] ;
2022-12-11 05:57:49 +01:00
column - > NavLayerCurrent = ( ImS8 ) ( table - > FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main ) ; // Use Count NOT request so Header line changes layer when frozen
2021-09-05 09:47:05 +02:00
if ( offset_x_frozen & & table - > FreezeColumnsCount = = visible_n )
{
offset_x + = work_rect . Min . x - table - > OuterRect . Min . x ;
offset_x_frozen = false ;
}
// Clear status flags
column - > Flags & = ~ ImGuiTableColumnFlags_StatusMask_ ;
if ( ( table - > EnabledMaskByDisplayOrder & ( ( ImU64 ) 1 < < order_n ) ) = = 0 )
{
// Hidden column: clear a few fields and we are done with it for the remainder of the function.
// We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.
column - > MinX = column - > MaxX = column - > WorkMinX = column - > ClipRect . Min . x = column - > ClipRect . Max . x = offset_x ;
column - > WidthGiven = 0.0f ;
column - > ClipRect . Min . y = work_rect . Min . y ;
column - > ClipRect . Max . y = FLT_MAX ;
column - > ClipRect . ClipWithFull ( host_clip_rect ) ;
column - > IsVisibleX = column - > IsVisibleY = column - > IsRequestOutput = false ;
column - > IsSkipItems = true ;
column - > ItemWidth = 1.0f ;
continue ;
}
// Detect hovered column
if ( is_hovering_table & & g . IO . MousePos . x > = column - > ClipRect . Min . x & & g . IO . MousePos . x < column - > ClipRect . Max . x )
table - > HoveredColumnBody = ( ImGuiTableColumnIdx ) column_n ;
// Lock start position
column - > MinX = offset_x ;
// Lock width based on start position and minimum/maximum width for this position
float max_width = TableGetMaxColumnWidth ( table , column_n ) ;
column - > WidthGiven = ImMin ( column - > WidthGiven , max_width ) ;
column - > WidthGiven = ImMax ( column - > WidthGiven , ImMin ( column - > WidthRequest , table - > MinColumnWidth ) ) ;
column - > MaxX = offset_x + column - > WidthGiven + table - > CellSpacingX1 + table - > CellSpacingX2 + table - > CellPaddingX * 2.0f ;
// Lock other positions
// - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging.
// - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column.
// - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow.
// - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter.
column - > WorkMinX = column - > MinX + table - > CellPaddingX + table - > CellSpacingX1 ;
column - > WorkMaxX = column - > MaxX - table - > CellPaddingX - table - > CellSpacingX2 ; // Expected max
column - > ItemWidth = ImFloor ( column - > WidthGiven * 0.65f ) ;
column - > ClipRect . Min . x = column - > MinX ;
column - > ClipRect . Min . y = work_rect . Min . y ;
column - > ClipRect . Max . x = column - > MaxX ; //column->WorkMaxX;
column - > ClipRect . Max . y = FLT_MAX ;
column - > ClipRect . ClipWithFull ( host_clip_rect ) ;
// Mark column as Clipped (not in sight)
// Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables.
// FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window->ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY.
// Taking advantage of LastOuterHeight would yield good results there...
// FIXME-TABLE: Y clipping is disabled because it effectively means not submitting will reduce contents width which is fed to outer_window->DC.CursorMaxPos.x,
// and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else).
// Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small.
column - > IsVisibleX = ( column - > ClipRect . Max . x > column - > ClipRect . Min . x ) ;
column - > IsVisibleY = true ; // (column->ClipRect.Max.y > column->ClipRect.Min.y);
const bool is_visible = column - > IsVisibleX ; //&& column->IsVisibleY;
if ( is_visible )
table - > VisibleMaskByIndex | = ( ( ImU64 ) 1 < < column_n ) ;
// Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.
column - > IsRequestOutput = is_visible | | column - > AutoFitQueue ! = 0 | | column - > CannotSkipItemsQueue ! = 0 ;
if ( column - > IsRequestOutput )
table - > RequestOutputMaskByIndex | = ( ( ImU64 ) 1 < < column_n ) ;
// Mark column as SkipItems (ignoring all items/layout)
column - > IsSkipItems = ! column - > IsEnabled | | table - > HostSkipItems ;
if ( column - > IsSkipItems )
IM_ASSERT ( ! is_visible ) ;
// Update status flags
column - > Flags | = ImGuiTableColumnFlags_IsEnabled ;
if ( is_visible )
column - > Flags | = ImGuiTableColumnFlags_IsVisible ;
if ( column - > SortOrder ! = - 1 )
column - > Flags | = ImGuiTableColumnFlags_IsSorted ;
if ( table - > HoveredColumnBody = = column_n )
column - > Flags | = ImGuiTableColumnFlags_IsHovered ;
// Alignment
// FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
// many cases (to be able to honor this we might be able to store a log of cells width, per row, for
// visible rows, but nav/programmatic scroll would have visible artifacts.)
//if (column->Flags & ImGuiTableColumnFlags_AlignRight)
// column->WorkMinX = ImMax(column->WorkMinX, column->MaxX - column->ContentWidthRowsUnfrozen);
//else if (column->Flags & ImGuiTableColumnFlags_AlignCenter)
// column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);
// Reset content width variables
column - > ContentMaxXFrozen = column - > ContentMaxXUnfrozen = column - > WorkMinX ;
column - > ContentMaxXHeadersUsed = column - > ContentMaxXHeadersIdeal = column - > WorkMinX ;
// Don't decrement auto-fit counters until container window got a chance to submit its items
if ( table - > HostSkipItems = = false )
{
column - > AutoFitQueue > > = 1 ;
column - > CannotSkipItemsQueue > > = 1 ;
}
if ( visible_n < table - > FreezeColumnsCount )
host_clip_rect . Min . x = ImClamp ( column - > MaxX + TABLE_BORDER_SIZE , host_clip_rect . Min . x , host_clip_rect . Max . x ) ;
offset_x + = column - > WidthGiven + table - > CellSpacingX1 + table - > CellSpacingX2 + table - > CellPaddingX * 2.0f ;
visible_n + + ;
}
// [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)
// Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either
// because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.
const float unused_x1 = ImMax ( table - > WorkRect . Min . x , table - > Columns [ table - > RightMostEnabledColumn ] . ClipRect .