4115 lines
215 KiB
C++
4115 lines
215 KiB
C++
|
// dear imgui, v1.89.6
|
||
|
// (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:
|
||
|
// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
||
|
// - 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 if 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 if 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 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 useful and 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
|
||
|
// (Reference: ImGuiTableFlags_SizingXXX flags and ImGuiTableColumnFlags_WidthXXX flags)
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// About overriding column sizing policy and width/weight with TableSetupColumn():
|
||
|
// We use a default parameter of -1 for 'init_width'/'init_weight'.
|
||
|
// - 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 padding honored.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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 weights/widths.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// About using column width:
|
||
|
// If a column is manually 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 submit only 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() returns 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
|
||
|
|
||
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||
|
#endif
|
||
|
|
||
|
#include "imgui.h"
|
||
|
#ifndef IMGUI_DISABLE
|
||
|
#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);
|
||
|
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 criteria 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);
|
||
|
if (use_child_window && IsClippedEx(outer_rect, 0))
|
||
|
{
|
||
|
ItemSize(outer_rect);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Acquire storage for the table
|
||
|
ImGuiTable* table = g.Tables.GetOrAddByKey(id);
|
||
|
const ImGuiTableFlags table_last_flags = table->Flags;
|
||
|
|
||
|
// Acquire temporary buffers
|
||
|
const int table_idx = g.Tables.GetIndex(table);
|
||
|
if (++g.TablesTempDataStacked > g.TablesTempData.Size)
|
||
|
g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData());
|
||
|
ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1];
|
||
|
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
|
||
|
const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
|
||
|
table->ID = id;
|
||
|
table->Flags = flags;
|
||
|
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;
|
||
|
|
||
|
// Instance data (for instance 0, TableID == TableInstanceID)
|
||
|
ImGuiID instance_id;
|
||
|
table->InstanceCurrent = (ImS16)instance_no;
|
||
|
if (instance_no > 0)
|
||
|
{
|
||
|
IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID");
|
||
|
if (table->InstanceDataExtra.Size < instance_no)
|
||
|
table->InstanceDataExtra.push_back(ImGuiTableInstanceData());
|
||
|
instance_id = GetIDWithSeed(instance_no, GetIDWithSeed("##Instances", NULL, id)); // Push "##Instances" followed by (int)instance_no in ID stack.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
instance_id = id;
|
||
|
}
|
||
|
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||
|
table_instance->TableInstanceID = instance_id;
|
||
|
|
||
|
// 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);
|
||
|
|
||
|
// 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);
|
||
|
}
|
||
|
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(id);
|
||
|
if (instance_no > 0)
|
||
|
PushOverrideID(instance_id); // FIXME: Somehow this is not resolved by stack-tool, even tho GetIDWithSeed() submitted the symbol.
|
||
|
|
||
|
// 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 lines of the grid, otherwise we'd need to improve TableDrawBorders()
|
||
|
table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong);
|
||
|
table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight);
|
||
|
|
||
|
// Make table current
|
||
|
g.CurrentTable = table;
|
||
|
outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
|
||
|
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 to avoid GC
|
||
|
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;
|
||
|
//IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);
|
||
|
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)
|
||
|
// Shared allocations for the maximum number of simultaneously nested tables (generally a very small number)
|
||
|
// + 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
|
||
|
const int columns_bit_array_size = (int)ImBitArrayGetStorageSizeInBytes(columns_count);
|
||
|
ImSpanAllocator<6> 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);
|
||
|
for (int n = 3; n < 6; n++)
|
||
|
span_allocator.Reserve(n, columns_bit_array_size);
|
||
|
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);
|
||
|
table->EnabledMaskByDisplayOrder = (ImU32*)span_allocator.GetSpanPtrBegin(3);
|
||
|
table->EnabledMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(4);
|
||
|
table->VisibleMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(5);
|
||
|
}
|
||
|
|
||
|
// Apply queued resizing/reordering/hiding requests
|
||
|
void ImGui::TableBeginApplyRequests(ImGuiTable* table)
|
||
|
{
|
||
|
// Handle resizing request
|
||
|
// (We process this in the TableBegin() of the first instance of each table)
|
||
|
// 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 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() and this is our largest function.
|
||
|
// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() and other TableSetupXXXXX() functions 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;
|
||
|
ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount);
|
||
|
ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount);
|
||
|
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++;
|
||
|
ImBitArraySetBit(table->EnabledMaskByIndex, column_n);
|
||
|
ImBitArraySetBit(table->EnabledMaskByDisplayOrder, 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 (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, 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 && column->IsRequestOutput)
|
||
|
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;
|
||
|
table->ColumnsStretchSumWeights = stretch_sum_weights;
|
||
|
|
||
|
// [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);
|
||
|
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);
|
||
|
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 (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, 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 (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, 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;
|
||
|
}
|
||
|
|
||
|
// 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);
|
||
|
table->HoveredColumnBody = -1;
|
||
|
table->HoveredColumnBorder = -1;
|
||
|
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;
|
||
|
const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0);
|
||
|
g.ActiveId = backup_active_id;
|
||
|
|
||
|
// [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;
|
||
|
ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount);
|
||
|
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];
|
||
|
|
||
|
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen
|
||
|
|
||
|
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 (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||
|
{
|
||
|
// 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)
|
||
|
ImBitArraySetBit(table->VisibleMaskByIndex, 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;
|
||
|
|
||
|
// Mark column as SkipItems (ignoring all items/layout)
|
||
|
column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;
|
||
|
if (column->IsSkipItems)
|
||
|
IM_ASSERT(!is_visible);
|
||
|
|
||
|