Major update - IMGUI Change
New ImGui version : Branch Doking
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.90.4
|
||||
// dear imgui, v1.91.8 WIP
|
||||
// (tables and columns code)
|
||||
|
||||
/*
|
||||
@@ -24,8 +24,9 @@ Index of this file:
|
||||
*/
|
||||
|
||||
// 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.
|
||||
// - 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
|
||||
@@ -227,9 +228,15 @@ Index of this file:
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
|
||||
@@ -318,14 +325,21 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
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.
|
||||
// FIXME: coarse clipping because access to table data causes two issues:
|
||||
// - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine.
|
||||
// - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers.
|
||||
// The side-effects of accessing table data on coarse clip would be:
|
||||
// - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations.
|
||||
// - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
|
||||
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
|
||||
const ImVec2 avail_size = GetContentRegionAvail();
|
||||
const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
|
||||
const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f));
|
||||
const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
|
||||
const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
|
||||
if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
|
||||
{
|
||||
ItemSize(outer_rect);
|
||||
ItemAdd(outer_rect, id);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -335,7 +349,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
|
||||
// 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);
|
||||
@@ -353,6 +366,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// Initialize
|
||||
const int previous_frame_active = table->LastFrameActive;
|
||||
const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
|
||||
const ImGuiTableFlags previous_flags = table->Flags;
|
||||
table->ID = id;
|
||||
table->Flags = flags;
|
||||
table->LastFrameActive = g.FrameCount;
|
||||
@@ -399,12 +413,13 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
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));
|
||||
if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
|
||||
if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll) == 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);
|
||||
ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags);
|
||||
table->InnerWindow = g.CurrentWindow;
|
||||
table->WorkRect = table->InnerWindow->WorkRect;
|
||||
table->OuterRect = table->InnerWindow->Rect();
|
||||
@@ -428,6 +443,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// 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;
|
||||
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
|
||||
}
|
||||
|
||||
// Push a standardized ID for both child-using and not-child-using tables
|
||||
@@ -450,16 +466,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
|
||||
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
|
||||
|
||||
// Make left and top borders not overlap our contents by offsetting HostClipRect (#6765)
|
||||
// Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)
|
||||
// (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
|
||||
// limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
|
||||
// problem only affect scrolling tables in this case we can get away with doing it without extra cost).
|
||||
if (inner_window != outer_window)
|
||||
{
|
||||
// FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,
|
||||
// it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
|
||||
// different x/y values to BeginChild().
|
||||
if (flags & ImGuiTableFlags_BordersOuterV)
|
||||
{
|
||||
table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
|
||||
if (inner_window->DecoOuterSizeX2 == 0.0f)
|
||||
table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);
|
||||
}
|
||||
if (flags & ImGuiTableFlags_BordersOuterH)
|
||||
{
|
||||
table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
|
||||
if (inner_window->DecoOuterSizeY2 == 0.0f)
|
||||
table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Padding and Spacing
|
||||
@@ -487,7 +514,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
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->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.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()
|
||||
@@ -498,6 +525,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->DeclColumnsCount = table->AngledHeadersCount = 0;
|
||||
if (previous_frame_active + 1 < g.FrameCount)
|
||||
table->IsActiveIdInTable = false;
|
||||
table->AngledHeadersHeight = 0.0f;
|
||||
temp_data->AngledHeadersExtraWidth = 0.0f;
|
||||
|
||||
// Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()
|
||||
@@ -511,7 +539,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
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)
|
||||
if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)
|
||||
table->IsResetDisplayOrderRequest = true;
|
||||
|
||||
// Mark as used to avoid GC
|
||||
@@ -844,7 +872,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
|
||||
// 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)
|
||||
if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0)
|
||||
column->WidthAuto = TableGetColumnWidthAuto(table, column);
|
||||
|
||||
// Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
|
||||
@@ -1048,16 +1076,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect hovered column
|
||||
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_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->WidthMax = TableCalcMaxColumnWidth(table, column_n);
|
||||
column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);
|
||||
column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
|
||||
column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
|
||||
|
||||
@@ -1066,6 +1090,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// - 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.
|
||||
const float previous_instance_work_min_x = column->WorkMinX;
|
||||
column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1;
|
||||
column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max
|
||||
column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f);
|
||||
@@ -1105,8 +1130,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
column->Flags |= ImGuiTableColumnFlags_IsVisible;
|
||||
if (column->SortOrder != -1)
|
||||
column->Flags |= ImGuiTableColumnFlags_IsSorted;
|
||||
if (table->HoveredColumnBody == column_n)
|
||||
|
||||
// Detect hovered column
|
||||
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
|
||||
{
|
||||
column->Flags |= ImGuiTableColumnFlags_IsHovered;
|
||||
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
|
||||
}
|
||||
|
||||
// Alignment
|
||||
// FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
|
||||
@@ -1118,11 +1148,25 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);
|
||||
|
||||
// Reset content width variables
|
||||
column->ContentMaxXFrozen = column->ContentMaxXUnfrozen = column->WorkMinX;
|
||||
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX;
|
||||
if (table->InstanceCurrent == 0)
|
||||
{
|
||||
column->ContentMaxXFrozen = column->WorkMinX;
|
||||
column->ContentMaxXUnfrozen = column->WorkMinX;
|
||||
column->ContentMaxXHeadersUsed = column->WorkMinX;
|
||||
column->ContentMaxXHeadersIdeal = column->WorkMinX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation.
|
||||
const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x;
|
||||
column->ContentMaxXFrozen += offset_from_previous_instance;
|
||||
column->ContentMaxXUnfrozen += offset_from_previous_instance;
|
||||
column->ContentMaxXHeadersUsed += offset_from_previous_instance;
|
||||
column->ContentMaxXHeadersIdeal += offset_from_previous_instance;
|
||||
}
|
||||
|
||||
// Don't decrement auto-fit counters until container window got a chance to submit its items
|
||||
if (table->HostSkipItems == false)
|
||||
if (table->HostSkipItems == false && table->InstanceCurrent == 0)
|
||||
{
|
||||
column->AutoFitQueue >>= 1;
|
||||
column->CannotSkipItemsQueue >>= 1;
|
||||
@@ -1223,7 +1267,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
else
|
||||
inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);
|
||||
inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?
|
||||
}
|
||||
|
||||
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
|
||||
@@ -1238,9 +1282,9 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
|
||||
// really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).
|
||||
// Actual columns highlight/render will be performed in EndTable() and not be affected.
|
||||
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||||
const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS;
|
||||
const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale);
|
||||
const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight;
|
||||
const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight);
|
||||
const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight);
|
||||
const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight;
|
||||
|
||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||
@@ -1293,7 +1337,11 @@ void ImGui::EndTable()
|
||||
{
|
||||
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)
|
||||
@@ -1415,7 +1463,7 @@ void ImGui::EndTable()
|
||||
if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[table->ResizedColumn];
|
||||
const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS);
|
||||
const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale));
|
||||
const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f);
|
||||
table->ResizedColumnNextWidth = new_width;
|
||||
}
|
||||
@@ -1444,7 +1492,12 @@ void ImGui::EndTable()
|
||||
// CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414)
|
||||
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.
|
||||
g.CurrentTable = NULL; // To avoid error recovery recursing
|
||||
EndChild();
|
||||
g.CurrentTable = table;
|
||||
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1462,9 +1515,13 @@ void ImGui::EndTable()
|
||||
}
|
||||
else if (temp_data->UserOuterSize.x <= 0.0f)
|
||||
{
|
||||
const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f);
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth));
|
||||
// Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block
|
||||
// - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags.
|
||||
// - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback.
|
||||
const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y
|
||||
const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f);
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1472,9 +1529,9 @@ void ImGui::EndTable()
|
||||
}
|
||||
if (temp_data->UserOuterSize.y <= 0.0f)
|
||||
{
|
||||
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f;
|
||||
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f;
|
||||
outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y);
|
||||
outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y));
|
||||
outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1507,8 +1564,12 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -1581,7 +1642,11 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
|
||||
{
|
||||
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
|
||||
@@ -1658,9 +1723,11 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled)
|
||||
{
|
||||
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;
|
||||
@@ -1890,7 +1957,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
if (is_visible)
|
||||
{
|
||||
// Update data for TableGetHoveredRow()
|
||||
if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2)
|
||||
if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0)
|
||||
table_instance->HoveredRowNext = table->CurrentRow;
|
||||
|
||||
// Decide of background color for the row
|
||||
@@ -1945,7 +2012,8 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
cell_bg_rect.ClipWith(table->BgClipRect);
|
||||
cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling
|
||||
cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX);
|
||||
window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
|
||||
if (cell_bg_rect.Min.y < cell_bg_rect.Max.y)
|
||||
window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1962,34 +2030,37 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
// 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)
|
||||
{
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
|
||||
if (unfreeze_rows_actual)
|
||||
{
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
|
||||
table->IsUnfrozenRows = true;
|
||||
const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
|
||||
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
|
||||
|
||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
|
||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
|
||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
|
||||
|
||||
float row_height = table->RowPosY2 - table->RowPosY1;
|
||||
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
|
||||
table->RowPosY1 = table->RowPosY2 - row_height;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
if (unfreeze_rows_actual)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
|
||||
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
|
||||
}
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
table->IsUnfrozenRows = true;
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y);
|
||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y;
|
||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
|
||||
|
||||
float row_height = table->RowPosY2 - table->RowPosY1;
|
||||
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
|
||||
table->RowPosY1 = table->RowPosY2 - row_height;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
|
||||
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
|
||||
}
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
|
||||
@@ -2158,8 +2229,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
|
||||
// Note that actual columns widths are computed in TableUpdateLayout().
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis.
|
||||
float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)
|
||||
// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.
|
||||
float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)
|
||||
{
|
||||
const ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
float max_width = FLT_MAX;
|
||||
@@ -2221,7 +2292,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
|
||||
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
|
||||
IM_ASSERT(table->MinColumnWidth > 0.0f);
|
||||
const float min_width = table->MinColumnWidth;
|
||||
const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n));
|
||||
const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
|
||||
column_0_width = ImClamp(column_0_width, min_width, max_width);
|
||||
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
|
||||
return;
|
||||
@@ -2706,7 +2777,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
||||
const ImU32 outer_col = table->BorderColorStrong;
|
||||
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
|
||||
{
|
||||
inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size);
|
||||
inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
|
||||
}
|
||||
else if (table->Flags & ImGuiTableFlags_BordersOuterV)
|
||||
{
|
||||
@@ -2766,7 +2837,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
|
||||
static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n)
|
||||
{
|
||||
IM_ASSERT(n < column->SortDirectionsAvailCount);
|
||||
return (column->SortDirectionsAvailList >> (n << 1)) & 0x03;
|
||||
return (ImGuiSortDirection)((column->SortDirectionsAvailList >> (n << 1)) & 0x03);
|
||||
}
|
||||
|
||||
// Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending)
|
||||
@@ -2907,6 +2978,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
||||
}
|
||||
|
||||
// Write output
|
||||
// May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable()
|
||||
ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;
|
||||
if (dirty && sort_specs != NULL)
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
@@ -2919,7 +2991,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
||||
sort_spec->ColumnUserID = column->UserID;
|
||||
sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
|
||||
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
|
||||
sort_spec->SortDirection = column->SortDirection;
|
||||
sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection;
|
||||
}
|
||||
|
||||
table->SortSpecs.Specs = sort_specs;
|
||||
@@ -2967,17 +3039,23 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
|
||||
|
||||
// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
|
||||
// The intent is that advanced users willing to create customized headers would not need to use this helper
|
||||
// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
|
||||
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
|
||||
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
|
||||
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
|
||||
// This code is intentionally written to not make much use of internal functions, to give you better direction
|
||||
// if you need to write your own.
|
||||
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
|
||||
void ImGui::TableHeadersRow()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout)
|
||||
// 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.
|
||||
if (!table->IsLayoutLocked)
|
||||
TableUpdateLayout(table);
|
||||
|
||||
@@ -2994,8 +3072,7 @@ void ImGui::TableHeadersRow()
|
||||
if (!TableSetColumnIndex(column_n))
|
||||
continue;
|
||||
|
||||
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
|
||||
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
|
||||
// Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)
|
||||
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
|
||||
PushID(column_n);
|
||||
TableHeader(name);
|
||||
@@ -3020,7 +3097,12 @@ void ImGui::TableHeader(const char* label)
|
||||
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];
|
||||
@@ -3086,7 +3168,7 @@ void ImGui::TableHeader(const char* label)
|
||||
if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
|
||||
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
|
||||
}
|
||||
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding);
|
||||
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
|
||||
if (held)
|
||||
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
|
||||
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
|
||||
@@ -3153,21 +3235,53 @@ void ImGui::TableHeader(const char* label)
|
||||
}
|
||||
|
||||
// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.
|
||||
// FIXME: highlight without ImGuiTableFlags_HighlightHoveredColumn
|
||||
// FIXME: No hit-testing/button on the angled header.
|
||||
void ImGui::TableAngledHeadersRow()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
TableAngledHeadersRowEx(g.Style.TableAngledHeadersAngle, 0.0f);
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
ImGuiTableTempData* temp_data = table->TempData;
|
||||
temp_data->AngledHeadersRequests.resize(0);
|
||||
temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount);
|
||||
|
||||
// Which column needs highlight?
|
||||
const ImGuiID row_id = GetID("##AngledHeaders");
|
||||
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||||
int highlight_column_n = table->HighlightColumnHeader;
|
||||
if (highlight_column_n == -1 && table->HoveredColumnBody != -1)
|
||||
if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))
|
||||
highlight_column_n = table->HoveredColumnBody;
|
||||
|
||||
// Build up request
|
||||
ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg);
|
||||
ImU32 col_text = GetColorU32(ImGuiCol_Text);
|
||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||
if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||||
{
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
|
||||
continue;
|
||||
ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 };
|
||||
temp_data->AngledHeadersRequests.push_back(request);
|
||||
}
|
||||
|
||||
// Render row
|
||||
TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size);
|
||||
}
|
||||
|
||||
void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
// Important: data must be fed left to right
|
||||
void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
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)
|
||||
@@ -3185,7 +3299,7 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
// Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()
|
||||
// FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.
|
||||
const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f;
|
||||
const float row_height = ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y);
|
||||
const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y));
|
||||
table->AngledHeadersHeight = row_height;
|
||||
table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f;
|
||||
const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right
|
||||
@@ -3203,28 +3317,22 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color.
|
||||
PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns
|
||||
|
||||
const ImGuiID row_id = GetID("##AngledHeaders");
|
||||
ButtonBehavior(row_r, row_id, NULL, NULL);
|
||||
KeepAliveID(row_id);
|
||||
|
||||
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||||
int highlight_column_n = table->HighlightColumnHeader;
|
||||
if (highlight_column_n == -1 && table->HoveredColumnBody != -1)
|
||||
if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))
|
||||
highlight_column_n = table->HoveredColumnBody;
|
||||
const float ascent_scaled = g.Font->Ascent * g.FontScale; // 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;
|
||||
ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
|
||||
float max_x = -FLT_MAX;
|
||||
for (int pass = 0; pass < 2; pass++)
|
||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||
for (int order_n = 0; order_n < data_count; order_n++)
|
||||
{
|
||||
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||||
continue;
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
const ImGuiTableHeaderData* request = &data[order_n];
|
||||
const int column_n = request->Index;
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
|
||||
continue;
|
||||
|
||||
ImVec2 bg_shape[4];
|
||||
bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y);
|
||||
@@ -3234,9 +3342,8 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
if (pass == 0)
|
||||
{
|
||||
// Draw shape
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableHeaderBg));
|
||||
if (column_n == highlight_column_n)
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_Header)); // Highlight on hover
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0);
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight
|
||||
max_x = ImMax(max_x, bg_shape[3].x);
|
||||
|
||||
// Draw label
|
||||
@@ -3244,8 +3351,17 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
// - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.
|
||||
const char* label_name = TableGetColumnName(table, column_n);
|
||||
const char* label_name_end = FindRenderedTextEnd(label_name);
|
||||
const float line_off_step_x = g.FontSize / -sin_a;
|
||||
float line_off_curr_x = 0.0f;
|
||||
const float line_off_step_x = (g.FontSize / -sin_a);
|
||||
const int label_lines = ImTextCountLines(label_name, label_name_end);
|
||||
|
||||
// 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;
|
||||
line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;
|
||||
|
||||
// Register header width
|
||||
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x);
|
||||
|
||||
while (label_name < label_name_end)
|
||||
{
|
||||
const char* label_name_eol = strchr(label_name, '\n');
|
||||
@@ -3254,26 +3370,30 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
|
||||
// FIXME: Individual line clipping for right-most column is broken for negative angles.
|
||||
ImVec2 label_size = CalcTextSize(label_name, label_name_eol);
|
||||
float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symetrical but hide more text.
|
||||
float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text.
|
||||
float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x);
|
||||
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);
|
||||
PopStyleColor();
|
||||
int vtx_idx_end = draw_list->_VtxCurrentIdx;
|
||||
|
||||
// Up<>Down alignment
|
||||
const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f);
|
||||
const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f);
|
||||
|
||||
// Rotate and offset label
|
||||
ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x, window->ClipRect.Min.y + label_size.y);
|
||||
ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y);
|
||||
ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y);
|
||||
line_off_curr_x += line_off_step_x;
|
||||
line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x;
|
||||
pivot_out += unit_right * padding.y;
|
||||
if (flip_label)
|
||||
pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x));
|
||||
pivot_out.x += flip_label ? line_off_curr_x - line_off_step_x : line_off_curr_x;
|
||||
pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x;
|
||||
ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset
|
||||
//if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 2.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); }
|
||||
//if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); }
|
||||
|
||||
// Register header width
|
||||
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(line_off_curr_x);
|
||||
label_name = label_name_eol + 1;
|
||||
}
|
||||
}
|
||||
@@ -3402,7 +3522,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
|
||||
Separator();
|
||||
want_separator = true;
|
||||
|
||||
PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
|
||||
PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
|
||||
for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
|
||||
{
|
||||
ImGuiTableColumn* other_column = &table->Columns[other_column_n];
|
||||
@@ -3988,7 +4108,7 @@ float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offse
|
||||
return offset / (columns->OffMaxX - columns->OffMinX);
|
||||
}
|
||||
|
||||
static const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f;
|
||||
static const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f;
|
||||
|
||||
static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
|
||||
{
|
||||
@@ -3999,7 +4119,7 @@ static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
|
||||
IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
|
||||
IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
|
||||
|
||||
float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + COLUMNS_HIT_RECT_HALF_WIDTH - window->Pos.x;
|
||||
float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x;
|
||||
x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
|
||||
if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths))
|
||||
x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
|
||||
@@ -4314,7 +4434,7 @@ void ImGui::EndColumns()
|
||||
ImGuiOldColumnData* column = &columns->Columns[n];
|
||||
float x = window->Pos.x + GetColumnOffset(n);
|
||||
const ImGuiID column_id = columns->ID + ImGuiID(n);
|
||||
const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH;
|
||||
const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale);
|
||||
const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
|
||||
if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))
|
||||
continue;
|
||||
@@ -4324,7 +4444,7 @@ void ImGui::EndColumns()
|
||||
{
|
||||
ButtonBehavior(column_hit_rect, column_id, &hovered, &held);
|
||||
if (hovered || held)
|
||||
g.MouseCursor = ImGuiMouseCursor_ResizeEW;
|
||||
SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
|
||||
dragging_column = n;
|
||||
}
|
||||
@@ -4356,12 +4476,12 @@ void ImGui::EndColumns()
|
||||
NavUpdateCurrentWindowIsScrollPushableX();
|
||||
}
|
||||
|
||||
void ImGui::Columns(int columns_count, const char* id, bool border)
|
||||
void ImGui::Columns(int columns_count, const char* id, bool borders)
|
||||
{
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
IM_ASSERT(columns_count >= 1);
|
||||
|
||||
ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder);
|
||||
ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);
|
||||
//flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
|
||||
ImGuiOldColumns* columns = window->DC.CurrentColumns;
|
||||
if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)
|
||||
|
Reference in New Issue
Block a user