From 972ca8166faade6c1cb0cd0a3cdcd50bb51c9efa Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Dec 2020 16:40:52 +0100 Subject: [PATCH] Tables: extracted code for TableGetMaxColumnWidth(), fixing "stuck" resize being lossy as it used an older calculation for it and didn't honor e.g. _NoKeepColumnsVisible --- imgui.h | 2 +- imgui_internal.h | 1 + imgui_tables.cpp | 80 ++++++++++++++++++++++++++---------------------- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/imgui.h b/imgui.h index 8f833d0b5..e01bdc5d1 100644 --- a/imgui.h +++ b/imgui.h @@ -1079,7 +1079,7 @@ enum ImGuiTableFlags_ ImGuiTableFlags_ColumnsWidthFixed = 1 << 14, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAutoResize policy (if Resizable is off). Read description above for more details. ImGuiTableFlags_SameWidths = 1 << 15, // Make all columns the same widths which is useful with Fixed columns policy (but granted by default with Stretch policy + no resize). Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible and disable ImGuiTableFlags_Resizable. ImGuiTableFlags_NoHostExtendY = 1 << 16, // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible) - ImGuiTableFlags_NoKeepColumnsVisible = 1 << 17, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. + ImGuiTableFlags_NoKeepColumnsVisible = 1 << 17, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable. ImGuiTableFlags_PreciseWidths = 1 << 18, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. // Clipping ImGuiTableFlags_NoClip = 1 << 19, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). diff --git a/imgui_internal.h b/imgui_internal.h index 5a0c9104a..51556f291 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2325,6 +2325,7 @@ namespace ImGui IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); IMGUI_API ImGuiID TableGetColumnResizeID(const ImGuiTable* table, int column_n, int instance_no = 0); IMGUI_API float TableGetMinColumnWidth(); + IMGUI_API float TableGetMaxColumnWidth(const ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableRemove(ImGuiTable* table); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 609ccb61c..0c20f0526 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -703,7 +703,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 3] Fix column flags. Calculate ideal width for columns. Count how many fixed/stretch columns we have and sum of weights. const float min_column_width = TableGetMinColumnWidth(); - const float min_column_distance = min_column_width + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2; int count_fixed = 0; // Number of columns that have fixed sizing policy (not stretched sizing policy) (this is NOT the opposite of count_resizable!) int count_resizable = 0; // Number of columns the user can resize (this is NOT the opposite of count_fixed!) float sum_weights_stretched = 0.0f; // Sum of all weights for weighted columns. @@ -895,42 +894,20 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x) table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; - // Maximum width - float max_width = FLT_MAX; - if (table->Flags & ImGuiTableFlags_ScrollX) - { - // Frozen columns can't reach beyond visible width else scrolling will naturally break. - if (order_n < table->FreezeColumnsRequest) - { - max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - order_n) * min_column_distance) - offset_x; - max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2; - } - } - else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0) - { - // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make - // sure they are all visible. Because of this we also know that all of the columns will always fit in - // table->WorkRect and therefore in table->InnerRect (because ScrollX is off) - // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match. - // See "table_width_distrib" and "table_width_keep_visible" tests - max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - offset_x; - //max_width -= table->CellSpacingX1; - max_width -= table->CellSpacingX2; - max_width -= table->CellPaddingX * 2.0f; - max_width -= table->OuterPaddingX; - } + // 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); - - // Minimum width column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, min_column_width)); + column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; - // Lock all our positions + // Lock other positions // - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging. // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column. // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow. // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter. - column->MinX = offset_x; - column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1; column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max column->ItemWidth = ImFloor(column->WidthGiven * 0.65f); @@ -1835,6 +1812,7 @@ void ImGui::TableEndCell(ImGuiTable* table) // [SECTION] Tables: Columns width management //------------------------------------------------------------------------- // - TableGetMinColumnWidth() [Internal] +// - TableGetMaxColumnWidth() [Internal] // - TableSetColumnWidth() // - TableSetColumnWidthAutoSingle() [Internal] // - TableSetColumnWidthAutoAll() [Internal] @@ -1849,6 +1827,37 @@ float ImGui::TableGetMinColumnWidth() return g.Style.FramePadding.x * 1.0f; } +// 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) +{ + const ImGuiTableColumn* column = &table->Columns[column_n]; + float max_width = FLT_MAX; + const float min_column_distance = TableGetMinColumnWidth() + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2; + if (table->Flags & ImGuiTableFlags_ScrollX) + { + // Frozen columns can't reach beyond visible width else scrolling will naturally break. + if (column->DisplayOrder < table->FreezeColumnsRequest) + { + max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX; + max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2; + } + } + else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0) + { + // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make + // sure they are all visible. Because of this we also know that all of the columns will always fit in + // table->WorkRect and therefore in table->InnerRect (because ScrollX is off) + // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match. + // See "table_width_distrib" and "table_width_keep_visible" tests + max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - column->MinX; + //max_width -= table->CellSpacingX1; + max_width -= table->CellSpacingX2; + max_width -= table->CellPaddingX * 2.0f; + max_width -= table->OuterPaddingX; + } + return max_width; +} + // 'width' = inner column width, without padding void ImGui::TableSetColumnWidth(int column_n, float width) { @@ -1859,14 +1868,11 @@ void ImGui::TableSetColumnWidth(int column_n, float width) ImGuiTableColumn* column_0 = &table->Columns[column_n]; float column_0_width = width; - // Constraints - const float min_width = TableGetMinColumnWidth(); - float max_width_0 = FLT_MAX; - if (!(table->Flags & ImGuiTableFlags_ScrollX)) - max_width_0 = (table->WorkRect.Max.x - column_0->MinX) - (table->ColumnsEnabledCount - (column_0->IndexWithinEnabledSet + 1)) * min_width; - column_0_width = ImClamp(column_0_width, min_width, max_width_0); - + // Apply constraints early // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded) + const float min_width = TableGetMinColumnWidth(); + const float max_width = TableGetMaxColumnWidth(table, column_n); + 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;