Revision a8009d0e
Added by david.sorber 8 months ago
| external/imgui/imgui_tables.cpp | ||
|---|---|---|
|
// dear imgui, v1.91.5
|
||
|
// dear imgui, v1.92.5
|
||
|
// (tables and columns code)
|
||
|
|
||
|
/*
|
||
| ... | ... | |
|
*/
|
||
|
|
||
|
// Navigating this file:
|
||
|
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
||
|
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
|
||
|
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
|
||
|
// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot.
|
||
|
// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
|
||
|
// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// [SECTION] Commentary
|
||
| ... | ... | |
|
#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" // warning: format specifies type 'int' but the argument has type 'unsigned int'
|
||
|
#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 "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||
|
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||
|
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
|
||
|
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
|
||
|
#elif defined(__GNUC__)
|
||
|
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||
|
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
|
||
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
|
||
|
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
|
||
|
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
|
||
|
#pragma GCC diagnostic ignored "-Wstrict-overflow"
|
||
|
#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
|
||
|
|
||
| ... | ... | |
|
{
|
||
|
ItemSize(outer_rect);
|
||
|
ItemAdd(outer_rect, id);
|
||
|
g.NextWindowData.ClearFlags();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
| ... | ... | |
|
table->ColumnsCount = columns_count;
|
||
|
table->IsLayoutLocked = false;
|
||
|
table->InnerWidth = inner_width;
|
||
|
table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent;
|
||
|
temp_data->UserOuterSize = outer_size;
|
||
|
|
||
|
// Instance data (for instance 0, TableID == TableInstanceID)
|
||
| ... | ... | |
|
|
||
|
// Reset scroll if we are reactivating it
|
||
|
if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
|
||
|
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
|
||
|
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) == 0)
|
||
|
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
|
||
|
|
||
|
// Create scrolling region (without border and zero window padding)
|
||
|
ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||
|
BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags);
|
||
|
ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None;
|
||
|
ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None;
|
||
|
if (flags & ImGuiTableFlags_ScrollX)
|
||
|
child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar;
|
||
|
BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags);
|
||
|
table->InnerWindow = g.CurrentWindow;
|
||
|
table->WorkRect = table->InnerWindow->WorkRect;
|
||
|
table->OuterRect = table->InnerWindow->Rect();
|
||
| ... | ... | |
|
if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)
|
||
|
table->InnerWindow->SkipItems = false;
|
||
|
|
||
|
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned)
|
||
|
// 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;
|
||
| ... | ... | |
|
// 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;
|
||
|
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
|
||
|
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking across a table
|
||
|
}
|
||
|
|
||
|
// Push a standardized ID for both child-using and not-child-using tables
|
||
| ... | ... | |
|
table->HostIndentX = inner_window->DC.Indent.x;
|
||
|
table->HostClipRect = inner_window->ClipRect;
|
||
|
table->HostSkipItems = inner_window->SkipItems;
|
||
|
temp_data->WindowID = inner_window->ID;
|
||
|
temp_data->HostBackupWorkRect = inner_window->WorkRect;
|
||
|
temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect;
|
||
|
temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset;
|
||
| ... | ... | |
|
|
||
|
// Make table current
|
||
|
g.CurrentTable = table;
|
||
|
outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
|
||
|
inner_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;
|
||
| ... | ... | |
|
// Initialize
|
||
|
table->SettingsOffset = -1;
|
||
|
table->IsSortSpecsDirty = true;
|
||
|
table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
|
||
|
table->InstanceInteracted = -1;
|
||
|
table->ContextPopupColumn = -1;
|
||
|
table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1;
|
||
| ... | ... | |
|
// (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: Move this to ->WidthGiven to avoid temporary lossyness?
|
||
|
// 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?
|
||
| ... | ... | |
|
// [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_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synced 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;
|
||
| ... | ... | |
|
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
|
||
|
// Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen
|
||
|
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer);
|
||
|
|
||
|
if (offset_x_frozen && table->FreezeColumnsCount == visible_n)
|
||
|
{
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
// In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible.
|
||
|
// Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar.
|
||
|
// Else if give no chance to a clipper-savvy user to submit rows and therefore total contents height used by scrollbar.
|
||
|
if (has_at_least_one_column_requesting_output == false)
|
||
|
{
|
||
|
table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true;
|
||
| ... | ... | |
|
|
||
|
// [Part 11] Default context menu
|
||
|
// - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
|
||
|
// - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup().
|
||
|
// - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
|
||
|
// - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
|
||
|
// e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
|
||
|
if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
|
||
| ... | ... | |
|
{
|
||
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
IM_ASSERT(table != NULL && "Only call EndTable() if BeginTable() returns true!");
|
||
|
if (table == NULL)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(table != NULL, "EndTable() call should only be done while in BeginTable() scope!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// This assert would be very useful to catch a common error... unfortunately it would probably trigger in some
|
||
|
// cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
|
||
| ... | ... | |
|
ImGuiWindow* inner_window = table->InnerWindow;
|
||
|
ImGuiWindow* outer_window = table->OuterWindow;
|
||
|
ImGuiTableTempData* temp_data = table->TempData;
|
||
|
IM_ASSERT(inner_window == g.CurrentWindow);
|
||
|
IM_ASSERT(inner_window == g.CurrentWindow && inner_window->ID == temp_data->WindowID);
|
||
|
IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow);
|
||
|
|
||
|
if (table->IsInsideRow)
|
||
| ... | ... | |
|
|
||
|
// Setup inner scrolling range
|
||
|
// FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,
|
||
|
// but since the later is likely to be impossible to do we'd rather update both axises together.
|
||
|
// but since the later is likely to be impossible to do we'd rather update both axes together.
|
||
|
if (table->Flags & ImGuiTableFlags_ScrollX)
|
||
|
{
|
||
|
const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
|
||
| ... | ... | |
|
if (inner_window != outer_window)
|
||
|
{
|
||
|
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
|
||
|
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
|
||
|
inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently.
|
||
|
g.CurrentTable = NULL; // To avoid error recovery recursing
|
||
|
EndChild();
|
||
|
g.CurrentTable = table;
|
||
| ... | ... | |
|
}
|
||
|
else
|
||
|
{
|
||
|
table->InnerWindow->DC.TreeDepth--;
|
||
|
ItemSize(table->OuterRect.GetSize());
|
||
|
ItemAdd(table->OuterRect, 0);
|
||
|
}
|
||
| ... | ... | |
|
IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table);
|
||
|
IM_ASSERT(g.TablesTempDataStacked > 0);
|
||
|
temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL;
|
||
|
g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;
|
||
|
g.CurrentTable = temp_data && (temp_data->WindowID == outer_window->ID) ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;
|
||
|
if (g.CurrentTable)
|
||
|
{
|
||
|
g.CurrentTable->TempData = temp_data;
|
||
| ... | ... | |
|
NavUpdateCurrentWindowIsScrollPushableX();
|
||
|
}
|
||
|
|
||
|
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
|
||
|
// 'init_mask' specify which fields to initialize.
|
||
|
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
|
||
|
{
|
||
|
ImGuiTableColumnFlags flags = column->Flags;
|
||
|
if (init_mask & ImGuiTableFlags_Resizable)
|
||
|
{
|
||
|
float init_width_or_weight = column->InitStretchWeightOrWidth;
|
||
|
column->WidthRequest = ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
|
||
|
column->StretchWeight = (init_width_or_weight > 0.0f && (flags & ImGuiTableColumnFlags_WidthStretch)) ? init_width_or_weight : -1.0f;
|
||
|
if (init_width_or_weight > 0.0f) // Disable auto-fit if an explicit width/weight has been specified
|
||
|
column->AutoFitQueue = 0x00;
|
||
|
}
|
||
|
if (init_mask & ImGuiTableFlags_Reorderable)
|
||
|
column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
|
||
|
if (init_mask & ImGuiTableFlags_Hideable)
|
||
|
column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
|
||
|
if (init_mask & ImGuiTableFlags_Sortable)
|
||
|
{
|
||
|
// Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
|
||
|
column->SortOrder = (flags & ImGuiTableColumnFlags_DefaultSort) ? 0 : -1;
|
||
|
column->SortDirection = (flags & ImGuiTableColumnFlags_DefaultSort) ? ((flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending)) : (ImS8)ImGuiSortDirection_None;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// See "COLUMNS SIZING POLICIES" comments at the top of this file
|
||
|
// If (init_width_or_weight <= 0.0f) it is ignored
|
||
|
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
|
||
|
{
|
||
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
|
||
|
IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!");
|
||
|
if (table == NULL)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||
|
return;
|
||
|
}
|
||
|
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
|
||
|
IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
|
||
|
if (table->DeclColumnsCount >= table->ColumnsCount)
|
||
|
{
|
||
| ... | ... | |
|
IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column.");
|
||
|
|
||
|
// When passing a width automatically enforce WidthFixed policy
|
||
|
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable)
|
||
|
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable)
|
||
|
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)
|
||
|
if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
|
||
|
flags |= ImGuiTableColumnFlags_WidthFixed;
|
||
| ... | ... | |
|
column->InitStretchWeightOrWidth = init_width_or_weight;
|
||
|
if (table->IsInitializing)
|
||
|
{
|
||
|
// Init width or weight
|
||
|
ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
|
||
|
if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
|
||
|
{
|
||
|
if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f)
|
||
|
column->WidthRequest = init_width_or_weight;
|
||
|
if (flags & ImGuiTableColumnFlags_WidthStretch)
|
||
|
column->StretchWeight = (init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
|
||
|
|
||
|
// Disable auto-fit if an explicit width/weight has been specified
|
||
|
if (init_width_or_weight > 0.0f)
|
||
|
column->AutoFitQueue = 0x00;
|
||
|
}
|
||
|
|
||
|
// Init default visibility/sort state
|
||
|
if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0)
|
||
|
column->IsUserEnabled = column->IsUserEnabledNextFrame = false;
|
||
|
if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0)
|
||
|
{
|
||
|
column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
|
||
|
column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending);
|
||
|
}
|
||
|
init_flags |= ImGuiTableFlags_Resizable;
|
||
|
TableInitColumnDefaults(table, column, init_flags);
|
||
|
}
|
||
|
|
||
|
// Store name (append with zero-terminator in contiguous buffer)
|
||
| ... | ... | |
|
if (label != NULL && label[0] != 0)
|
||
|
{
|
||
|
column->NameOffset = (ImS16)table->ColumnsNames.size();
|
||
|
table->ColumnsNames.append(label, label + strlen(label) + 1);
|
||
|
table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
| ... | ... | |
|
{
|
||
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
|
||
|
if (table == NULL)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||
|
return;
|
||
|
}
|
||
|
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
|
||
|
IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);
|
||
|
IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit
|
||
| ... | ... | |
|
{
|
||
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
IM_ASSERT(table != NULL);
|
||
|
if (!table)
|
||
|
if (table == NULL)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||
|
return;
|
||
|
}
|
||
|
IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above
|
||
|
if (column_n < 0)
|
||
|
column_n = table->CurrentColumn;
|
||
| ... | ... | |
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
IM_ASSERT(target != ImGuiTableBgTarget_None);
|
||
|
if (table == NULL)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (color == IM_COL32_DISABLE)
|
||
|
color = 0;
|
||
| ... | ... | |
|
IM_ASSERT(table->IsInsideRow);
|
||
|
|
||
|
if (table->CurrentColumn != -1)
|
||
|
{
|
||
|
TableEndCell(table);
|
||
|
table->CurrentColumn = -1;
|
||
|
}
|
||
|
|
||
|
// Logging
|
||
|
if (g.LogEnabled)
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
// End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)
|
||
|
// We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
|
||
|
// get the new cursor position.
|
||
|
// - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark
|
||
|
// end of row and get the new cursor position.
|
||
|
if (unfreeze_rows_request)
|
||
|
{
|
||
|
IM_ASSERT(table->FreezeRowsRequest > 0);
|
||
|
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||
|
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
|
||
|
table->Columns[column_n].NavLayerCurrent = table->NavLayer;
|
||
|
const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
|
||
|
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
|
||
|
|
||
| ... | ... | |
|
{
|
||
|
if (table->CurrentColumn != -1)
|
||
|
TableEndCell(table);
|
||
|
IM_ASSERT(column_n >= 0 && table->ColumnsCount);
|
||
|
if ((column_n >= 0 && column_n < table->ColumnsCount) == false)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(column_n >= 0 && column_n < table->ColumnsCount, "TableSetColumnIndex() invalid column index!");
|
||
|
return false;
|
||
|
}
|
||
|
TableBeginCell(table, column_n);
|
||
|
}
|
||
|
|
||
| ... | ... | |
|
g.LastItemData.StatusFlags = 0;
|
||
|
}
|
||
|
|
||
|
// Also see TablePushColumnChannel()
|
||
|
if (table->Flags & ImGuiTableFlags_NoClip)
|
||
|
{
|
||
|
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
|
||
| ... | ... | |
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiWindow* window = g.CurrentWindow;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
||
|
|
||
|
// Optimization: avoid PopClipRect() + SetCurrentChannel()
|
||
|
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
|
||
|
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
|
||
|
}
|
||
|
|
||
|
// Also see TableBeginCell()
|
||
|
void ImGui::TablePushColumnChannel(int column_n)
|
||
|
{
|
||
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
|
||
|
// Optimization: avoid SetCurrentChannel() + PushClipRect()
|
||
|
if (table->Flags & ImGuiTableFlags_NoClip)
|
||
|
return;
|
||
|
ImGuiWindow* window = g.CurrentWindow;
|
||
|
const ImGuiTableColumn* column = &table->Columns[column_n];
|
||
|
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
|
||
|
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||
|
}
|
||
|
|
||
|
void ImGui::TablePopColumnChannel()
|
||
|
{
|
||
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
|
||
|
// Optimization: avoid PopClipRect() + SetCurrentChannel()
|
||
|
if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
|
||
|
return;
|
||
|
ImGuiWindow* window = g.CurrentWindow;
|
||
|
const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
||
|
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
|
||
|
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||
|
}
|
||
|
|
||
| ... | ... | |
|
// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)
|
||
|
// - Clip --> 2+D+N channels
|
||
|
// - FreezeRows --> 2+D+N*2 (unless scrolling value is zero)
|
||
|
// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero)
|
||
|
// - FreezeRows || FreezeColumns --> 3+D+N*2 (unless scrolling value is zero)
|
||
|
// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.
|
||
|
void ImGui::TableSetupDrawChannels(ImGuiTable* table)
|
||
|
{
|
||
| ... | ... | |
|
continue;
|
||
|
|
||
|
// Draw in outer window so right-most column won't be clipped
|
||
|
// Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling.
|
||
|
float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head;
|
||
|
float draw_y2 = draw_y2_head;
|
||
|
if (is_frozen_separator)
|
||
|
draw_y2 = draw_y2_body;
|
||
|
else if ((table->Flags & ImGuiTableFlags_NoBordersInBodyUntilResize) != 0 && (is_hovered || is_resized))
|
||
|
draw_y2 = draw_y2_body;
|
||
|
else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0)
|
||
|
draw_y2 = draw_y2_body;
|
||
|
if (draw_y2 > draw_y1)
|
||
|
inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);
|
||
|
}
|
||
| ... | ... | |
|
{
|
||
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
IM_ASSERT(table != NULL);
|
||
|
|
||
|
if (!(table->Flags & ImGuiTableFlags_Sortable))
|
||
|
if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable))
|
||
|
return NULL;
|
||
|
|
||
|
// Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths.
|
||
| ... | ... | |
|
{
|
||
|
ImGuiContext& g = *GImGui;
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
|
||
|
if (table == NULL)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make
|
||
|
// it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
|
||
| ... | ... | |
|
return;
|
||
|
|
||
|
ImGuiTable* table = g.CurrentTable;
|
||
|
IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!");
|
||
|
if (table == NULL)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IM_ASSERT(table->CurrentColumn != -1);
|
||
|
const int column_n = table->CurrentColumn;
|
||
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
||
| ... | ... | |
|
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
|
||
|
// be merged into a single draw call.
|
||
|
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
|
||
|
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
|
||
|
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
|
||
|
|
||
|
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
|
||
|
if (text_clipped && hovered && g.ActiveId == 0)
|
||
| ... | ... | |
|
ImGuiTable* table = g.CurrentTable;
|
||
|
ImGuiWindow* window = g.CurrentWindow;
|
||
|
ImDrawList* draw_list = window->DrawList;
|
||
|
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
|
||
|
if (table == NULL)
|
||
|
{
|
||
|
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||
|
return;
|
||
|
}
|
||
|
IM_ASSERT(table->CurrentRow == -1 && "Must be first row");
|
||
|
|
||
|
if (max_label_width == 0.0f)
|
||
| ... | ... | |
|
ButtonBehavior(row_r, row_id, NULL, NULL);
|
||
|
KeepAliveID(row_id);
|
||
|
|
||
|
const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better
|
||
|
const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better
|
||
|
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
|
||
|
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
|
||
|
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
|
||
|
|
||
|
// Draw background and labels in first pass, then all borders.
|
||
|
float max_x = 0.0f;
|
||
|
float max_x = -FLT_MAX;
|
||
|
for (int pass = 0; pass < 2; pass++)
|
||
|
for (int order_n = 0; order_n < data_count; order_n++)
|
||
|
{
|
||
| ... | ... | |
|
|
||
|
// Left<>Right alignment
|
||
|
float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
|
||
|
float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x;
|
||
|
float line_off_for_align_x = ImFloor(ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x);
|
||
|
line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;
|
||
|
|
||
|
// Register header width
|
||
| ... | ... | |
|
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
|
||
|
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
|
||
|
PushStyleColor(ImGuiCol_Text, request->TextColor);
|
||
|
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
|
||
|
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
|
||
|
PopStyleColor();
|
||
|
int vtx_idx_end = draw_list->_VtxCurrentIdx;
|
||
|
|
||
| ... | ... | |
|
table->SettingsLoadedFlags = settings->SaveFlags;
|
||
|
table->RefScale = settings->RefScale;
|
||
|
|
||
|
// Initialize default columns settings
|
||
|
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||
|
{
|
||
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
||
|
TableInitColumnDefaults(table, column, ~0);
|
||
|
column->AutoFitQueue = 0x00;
|
||
|
}
|
||
|
|
||
|
// Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
|
||
|
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
|
||
|
ImU64 display_order_mask = 0;
|
||
| ... | ... | |
|
column->StretchWeight = column_settings->WidthOrWeight;
|
||
|
else
|
||
|
column->WidthRequest = column_settings->WidthOrWeight;
|
||
|
column->AutoFitQueue = 0x00;
|
||
|
}
|
||
|
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
|
||
|
column->DisplayOrder = column_settings->DisplayOrder;
|
||
|
else
|
||
|
column->DisplayOrder = (ImGuiTableColumnIdx)column_n;
|
||
|
display_order_mask |= (ImU64)1 << column->DisplayOrder;
|
||
|
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled;
|
||
|
if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
|
||
|
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;
|
||
|
column->SortOrder = column_settings->SortOrder;
|
||
|
column->SortDirection = column_settings->SortDirection;
|
||
|
}
|
||
| ... | ... | |
|
const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;
|
||
|
const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;
|
||
|
const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;
|
||
|
if (!save_size && !save_visible && !save_order && !save_sort)
|
||
|
continue;
|
||
|
// We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings".
|
||
|
|
||
|
buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve
|
||
|
buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount);
|
||
| ... | ... | |
|
// - TableGcCompactSettings() [Internal]
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
// Remove Table (currently only used by TestEngine)
|
||
|
// Remove Table data (currently only used by TestEngine)
|
||
|
void ImGui::TableRemove(ImGuiTable* table)
|
||
|
{
|
||
|
//IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID);
|
||
| ... | ... | |
|
bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*");
|
||
|
if (!is_active) { PopStyleColor(); }
|
||
|
if (IsItemHovered())
|
||
|
GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
|
||
|
GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
|
||
|
if (IsItemVisible() && table->HoveredColumnBody != -1)
|
||
|
GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));
|
||
|
GetForegroundDrawList(table->OuterWindow)->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));
|
||
|
if (!open)
|
||
|
return;
|
||
|
if (table->InstanceCurrent > 0)
|
||
| ... | ... | |
|
if (IsItemHovered())
|
||
|
{
|
||
|
ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y);
|
||
|
GetForegroundDrawList()->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));
|
||
|
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));
|
||
|
}
|
||
|
}
|
||
|
if (ImGuiTableSettings* settings = TableGetBoundSettings(table))
|
||
Update Dear ImGui to version 1.92.5.