Skip to content

Commit 892b48e

Browse files
committed
Tables: Lock contents width while resizing down an horizontal scrolling table. Headers declare ideal width regardless of clipping. Misc comments.
1 parent bd899ef commit 892b48e

File tree

3 files changed

+39
-31
lines changed

3 files changed

+39
-31
lines changed

imgui.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Index of this file:
6060
// Version
6161
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
6262
#define IMGUI_VERSION "1.80 WIP"
63-
#define IMGUI_VERSION_NUM 17906
63+
#define IMGUI_VERSION_NUM 17907
6464
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
6565
#define IMGUI_HAS_TABLE
6666

@@ -1037,8 +1037,8 @@ enum ImGuiTabItemFlags_
10371037
// When ScrollX is off:
10381038
// - Table defaults to ImGuiTableFlags_ColumnsWidthStretch -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch.
10391039
// - Columns sizing policy allowed: Stretch (default) or Fixed/Auto.
1040-
// - Stretch Columns will share the width available in table.
1041-
// - Fixed Columns will generally obtain their requested width unless the Table cannot fit them all.
1040+
// - Fixed Columns will generally obtain their requested width (unless the Table cannot fit them all).
1041+
// - Stretch Columns will share the remaining width.
10421042
// When ScrollX is on:
10431043
// - Table defaults to ImGuiTableFlags_ColumnsWidthFixed -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed.
10441044
// - Columns sizing policy allowed: Fixed/Auto mostly!

imgui_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,7 @@ struct ImGuiTable
20122012
float ColumnsTotalWidth; // Sum of current column width
20132013
float ColumnsAutoFitWidth; // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window
20142014
float ResizedColumnNextWidth;
2015+
float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table.
20152016
float RefScale; // Reference scale to be able to rescale columns on font/dpi changes.
20162017
ImRect OuterRect; // Note: OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().
20172018
ImRect WorkRect;

imgui_tables.cpp

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,7 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table)
492492
{
493493
// Handle resizing request
494494
// (We process this at the first TableBegin of the frame)
495-
// FIXME-TABLE: Preserve contents width _while resizing down_ until releasing.
496-
// FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling.
495+
// FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling?
497496
if (table->InstanceCurrent == 0)
498497
{
499498
if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX)
@@ -691,6 +690,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
691690
if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate))
692691
table->IsSortSpecsDirty = true;
693692
table->RightMostEnabledColumn = (ImGuiTableColumnIdx)last_visible_column_idx;
693+
IM_ASSERT(table->RightMostEnabledColumn >= 0);
694694

695695
// [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible
696696
// to avoid the column fitting to wait until the first visible frame of the child container (may or not be a good thing).
@@ -1014,9 +1014,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
10141014
// because of using _WidthAutoResize/_WidthStretch). This will hide the resizing option from the context menu.
10151015
if (is_hovering_table && table->HoveredColumnBody == -1)
10161016
{
1017-
float unused_x1 = table->WorkRect.Min.x;
1018-
if (table->RightMostEnabledColumn != -1)
1019-
unused_x1 = ImMax(unused_x1, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x);
1017+
float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x);
10201018
if (g.IO.MousePos.x >= unused_x1)
10211019
table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount;
10221020
}
@@ -1109,6 +1107,8 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
11091107
}
11101108
if (held)
11111109
{
1110+
if (table->LastResizedColumn == -1)
1111+
table->ResizeLockMinContentsX2 = table->RightMostEnabledColumn != -1 ? table->Columns[table->RightMostEnabledColumn].MaxX : -FLT_MAX;
11121112
table->ResizedColumn = (ImGuiTableColumnIdx)column_n;
11131113
table->InstanceInteracted = table->InstanceCurrent;
11141114
}
@@ -1182,6 +1182,8 @@ void ImGui::EndTable()
11821182
float max_pos_x = backup_inner_max_pos_x;
11831183
if (table->RightMostEnabledColumn != -1)
11841184
max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].MaxX);
1185+
if (table->ResizedColumn != -1)
1186+
max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2);
11851187

11861188
#if 0
11871189
// Strip out dummy channel draw calls
@@ -1861,7 +1863,12 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
18611863

18621864
ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL;
18631865

1864-
// In this surprisingly not simple because of how we support mixing Fixed and Stretch columns.
1866+
// In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns.
1867+
// - All fixed: easy.
1868+
// - All stretch: easy.
1869+
// - One or more fixed + one stretch: easy.
1870+
// - One or more fixed + more than one stretch: A MESS
1871+
18651872
// When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.
18661873
// FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user.
18671874
// Scenarios:
@@ -2638,6 +2645,26 @@ void ImGui::TableHeader(const char* label)
26382645
ImRect cell_r = TableGetCellBgRect(table, column_n);
26392646
float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f);
26402647

2648+
// Calculate ideal size for sort order arrow
2649+
float w_arrow = 0.0f;
2650+
float w_sort_text = 0.0f;
2651+
char sort_order_suf[4] = "";
2652+
const float ARROW_SCALE = 0.65f;
2653+
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
2654+
{
2655+
w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x);
2656+
if (column->SortOrder > 0)
2657+
{
2658+
ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
2659+
w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;
2660+
}
2661+
}
2662+
2663+
// We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
2664+
float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
2665+
column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX);
2666+
column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
2667+
26412668
// Keep header highlighted when context menu is open.
26422669
const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent);
26432670
ImGuiID id = window->GetID(label);
@@ -2692,36 +2719,21 @@ void ImGui::TableHeader(const char* label)
26922719
}
26932720

26942721
// Sort order arrow
2695-
float w_arrow = 0.0f;
2696-
float w_sort_text = 0.0f;
2697-
float ellipsis_max = cell_r.Max.x;
2722+
const float ellipsis_max = cell_r.Max.x - w_arrow - w_sort_text;
26982723
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
26992724
{
2700-
const float ARROW_SCALE = 0.65f;
2701-
w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x);
27022725
if (column->SortOrder != -1)
27032726
{
2704-
char sort_order_suf[8];
2705-
w_sort_text = 0.0f;
2706-
if (column->SortOrder > 0)
2707-
{
2708-
ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
2709-
w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;
2710-
}
2711-
27122727
float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);
2713-
ellipsis_max -= w_arrow + w_sort_text;
2714-
27152728
float y = label_pos.y;
2716-
ImU32 col = GetColorU32(ImGuiCol_Text);
27172729
if (column->SortOrder > 0)
27182730
{
27192731
PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f));
27202732
RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf);
27212733
PopStyleColor();
27222734
x += w_sort_text;
27232735
}
2724-
RenderArrow(window->DrawList, ImVec2(x, y), col, column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);
2736+
RenderArrow(window->DrawList, ImVec2(x, y), GetColorU32(ImGuiCol_Text), column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);
27252737
}
27262738

27272739
// Handle clicking on column header to adjust Sort Order
@@ -2741,11 +2753,6 @@ void ImGui::TableHeader(const char* label)
27412753
if (text_clipped && hovered && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay)
27422754
SetTooltip("%.*s", (int)(label_end - label), label);
27432755

2744-
// We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
2745-
float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
2746-
column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX);
2747-
column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
2748-
27492756
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
27502757
if (IsMouseReleased(1) && IsItemHovered())
27512758
TableOpenContextMenu(column_n);

0 commit comments

Comments
 (0)