diff --git a/imgui.h b/imgui.h index e9d56b8e..26ba8ecc 100644 --- a/imgui.h +++ b/imgui.h @@ -1081,11 +1081,11 @@ enum ImGuiTabItemFlags_ // Flags for ImGui::BeginTable() // - Important! Sizing policies have complex and subtle side effects, more so than you would expect. // Read comments/demos carefully + experiment with live demos to get acquainted with them. -// - The DEFAULT policy depends on whether the _ScrollX flag is set on the table, and whether _AlwaysAutoResize flag is set on window. -// - ImGuiTableFlags_SizingPolicyStretch is the default if ScrollX if off. -// - ImGuiTableFlags_SizingPolicyFixed is the default if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize. +// - The DEFAULT sizing policies are: +// - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize. +// - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off. // - When ScrollX is off: -// - Table defaults to ImGuiTableFlags_SizingPolicyStretch -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch. +// - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight. // - Columns sizing policy allowed: Stretch (default), Fixed/Auto. // - Fixed Columns will generally obtain their requested width (unless the table cannot fit them all). // - Stretch Columns will share the remaining width. @@ -1093,9 +1093,10 @@ enum ImGuiTabItemFlags_ // The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. // (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing). // - When ScrollX is on: -// - Table defaults to ImGuiTableFlags_SizingPolicyFixed -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed or ImGuiTableColumnFlags_WidthAuto. +// - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed or ImGuiTableColumnFlags_WidthAuto. // - Columns sizing policy allowed: Fixed/Auto mostly. // - Fixed Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed. +// - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop. // - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable(). // If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again. // - Read on documentation at the top of imgui_tables.cpp for details. @@ -1122,11 +1123,12 @@ enum ImGuiTableFlags_ ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders. ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appears in Headers). -> May move to style ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers). -> May move to style - // Sizing Policy - ImGuiTableFlags_SizingPolicyFixed = 1 << 13, // [Default if ScrollX is on] Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width. - ImGuiTableFlags_SizingPolicyStretch = 1 << 14, // [Default if ScrollX is off] Columns default to _WidthStretch with same weights. + // Sizing Policy (read above for defaults) + ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. + ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. + ImGuiTableFlags_SizingStretchProp = 3 << 13, // Columns default to _WidthStretch with default weights proportional to each columns contents widths. + ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overriden by TableSetupColumn(). // Sizing Extra Options - 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. 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. @@ -1141,11 +1143,15 @@ enum ImGuiTableFlags_ ImGuiTableFlags_ScrollY = 1 << 24, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. // Sorting ImGuiTableFlags_SortMulti = 1 << 25, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). - ImGuiTableFlags_SortTristate = 1 << 26 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + ImGuiTableFlags_SortTristate = 1 << 26, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + + // [Internal] Combinations and masks + ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame // Obsolete names (will be removed soon) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiTableFlags_ColumnsWidthFixed = ImGuiTableFlags_SizingPolicyFixed, ImGuiTableFlags_ColumnsWidthStretch = ImGuiTableFlags_SizingPolicyStretch + , ImGuiTableFlags_ColumnsWidthFixed = ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_ColumnsWidthStretch = ImGuiTableFlags_SizingStretchSame // WIP Tables 2020/12 + , ImGuiTableFlags_SizingPolicyFixed = ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingPolicyStretch = ImGuiTableFlags_SizingStretchSame // WIP Tables 2021/01 #endif }; @@ -1156,9 +1162,9 @@ enum ImGuiTableColumnFlags_ ImGuiTableColumnFlags_None = 0, ImGuiTableColumnFlags_DefaultHide = 1 << 0, // Default as a hidden/disabled column. ImGuiTableColumnFlags_DefaultSort = 1 << 1, // Default as a sorting column. - ImGuiTableColumnFlags_WidthStretch = 1 << 2, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingPolicyStretch). - ImGuiTableColumnFlags_WidthFixed = 1 << 3, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingPolicyFixed and table is resizable). - ImGuiTableColumnFlags_WidthAuto = 1 << 4, // Column will not stretch and keep resizing based on submitted contents (default if table sizing policy is _SizingPolicyFixed and table is not resizable). Generally compatible with using right-most fitting widgets (e.g. SetNextItemWidth(-FLT_MIN)) + ImGuiTableColumnFlags_WidthStretch = 1 << 2, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). + ImGuiTableColumnFlags_WidthFixed = 1 << 3, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). + ImGuiTableColumnFlags_WidthAuto = 1 << 4, // Column will not stretch and keep resizing based on submitted contents (default if table sizing policy is _SizingFixedFit and table is not resizable, or policy is _SizingFixedSame). Generally compatible with using right-most fitting widgets (e.g. SetNextItemWidth(-FLT_MIN)) ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0ac873cf..e9691bc6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3403,8 +3403,8 @@ const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL; static void PushStyleCompact() { ImGuiStyle& style = ImGui::GetStyle(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.70f))); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.70f))); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f))); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f))); } static void PopStyleCompact() @@ -3412,6 +3412,49 @@ static void PopStyleCompact() ImGui::PopStyleVar(2); } +// Show a combo box with a choice of sizing policies +static void EditTableSizingFlags(ImGuiTableFlags* p_flags) +{ + struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; }; + static const EnumDesc policies[] = + { + { ImGuiTableFlags_None, "Default", "Use default sizing policy:\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n- ImGuiTableFlags_SizingStretchSame otherwise." }, + { ImGuiTableFlags_SizingFixedFit, "ImGuiTableFlags_SizingFixedFit", "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." }, + { ImGuiTableFlags_SizingFixedSame, "ImGuiTableFlags_SizingFixedSame", "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." }, + { ImGuiTableFlags_SizingStretchProp, "ImGuiTableFlags_SizingStretchProp", "Columns default to _WidthStretch with weights proportional to their widths." }, + { ImGuiTableFlags_SizingStretchSame, "ImGuiTableFlags_SizingStretchSame", "Columns default to _WidthStretch with same weights." } + }; + int idx; + for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++) + if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_)) + break; + const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : ""; + if (ImGui::BeginCombo("Sizing Policy", preview_text)) + { + for (int n = 0; n < IM_ARRAYSIZE(policies); n++) + if (ImGui::Selectable(policies[n].Name, idx == n)) + *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value; + ImGui::EndCombo(); + } + ImGui::SameLine(); + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); + for (int m = 0; m < IM_ARRAYSIZE(policies); m++) + { + ImGui::Separator(); + ImGui::Text("%s:", policies[m].Name); + ImGui::Separator(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f); + ImGui::TextUnformatted(policies[m].Tooltip); + } + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} + static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) { ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide); @@ -3576,12 +3619,12 @@ static void ShowDemoWindowTables() ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter); ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags, ImGuiTableFlags_BordersInner); ImGui::Unindent(); - ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appears in Headers"); ImGui::AlignTextToFramePadding(); ImGui::Text("Cell contents:"); ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text); ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton); ImGui::Checkbox("Display headers", &display_headers); + ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appears in Headers"); PopStyleCompact(); if (ImGui::BeginTable("##table1", 3, flags)) @@ -3621,11 +3664,11 @@ static void ShowDemoWindowTables() { // By default, if we don't enable ScrollX the sizing policy for each columns is "Stretch" // Each columns maintain a sizing weight, and they will occupy all available width. - static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; + static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); - ImGui::SameLine(); HelpMarker("Using the _Resizable flag automatically enables the _BordersV flag as well."); + ImGui::SameLine(); HelpMarker("Using the _Resizable flag automatically enables the _BordersInnerV flag as well, this is why the resize borders are still showing when unchecking this."); PopStyleCompact(); if (ImGui::BeginTable("##table1", 3, flags)) @@ -3648,21 +3691,22 @@ static void ShowDemoWindowTables() ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Resizable, fixed")) { - // Here we use ImGuiTableFlags_SizingPolicyFixed (even though _ScrollX is not set) + // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set) // So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small) // If there is not enough available width to fit all columns, they will however be resized down. // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings HelpMarker( - "Using _Resizable + _SizingPolicyFixed flags.\n" + "Using _Resizable + _SizingFixedFit flags.\n" "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n" "Double-click a column border to auto-fit the column to its contents."); - static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingPolicyFixed | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; - static bool use_all_width = true; - if (ImGui::RadioButton("fit", use_all_width == false)) { use_all_width = false; } - ImGui::SameLine(); - if (ImGui::RadioButton("right-most edge", use_all_width == true)) { use_all_width = true; } + PushStyleCompact(); + static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; + static bool fixed_fill = true; + ImGui::Checkbox("fill", &fixed_fill); + PopStyleCompact(); - if (ImGui::BeginTable("##table1", 3, flags, ImVec2(use_all_width ? -FLT_MIN : 0.0f, 0.0f))) + ImVec2 outer_size(fixed_fill ? -FLT_MIN : 0.0f, 0.0f); + if (ImGui::BeginTable("##table1", 3, flags, outer_size)) { for (int row = 0; row < 5; row++) { @@ -3682,8 +3726,10 @@ static void ShowDemoWindowTables() ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Resizable, mixed")) { - HelpMarker("Using columns flag to alter resizing policy on a per-column basis."); - static ImGuiTableFlags flags = ImGuiTableFlags_SizingPolicyFixed | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; + HelpMarker( + "Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n" + "When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch."); + static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; if (ImGui::BeginTable("##table1", 3, flags)) { @@ -3729,7 +3775,9 @@ static void ShowDemoWindowTables() ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Reorderable, hideable, with headers")) { - HelpMarker("Click and drag column headers to reorder columns.\n\nYou can also right-click on a header to open a context menu."); + HelpMarker( + "Click and drag column headers to reorder columns.\n\n" + "Right-click on a header to open a context menu."); static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); @@ -3760,7 +3808,7 @@ static void ShowDemoWindowTables() } // Use outer_size.x == 0.0f instead of default to make the table as tight as possible (only valid when no scrolling and no stretch column) - if (ImGui::BeginTable("##table2", 3, flags | ImGuiTableFlags_SizingPolicyFixed, ImVec2(0.0f, 0.0f))) + if (ImGui::BeginTable("##table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f))) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); @@ -3792,7 +3840,8 @@ static void ShowDemoWindowTables() "- BorderOuterV\n" "- any form of row selection\n" "Because of this, activating BorderOuterV sets the default to PadOuterX. Using PadOuterX or NoPadOuterX you can override the default.\n\n" - "Actual padding values are using style.CellPadding."); + "Actual padding values are using style.CellPadding.\n\n" + "In this demo we don't show horizontal borders to emphasis how they don't affect default horizontal padding."); static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV; PushStyleCompact(); @@ -3846,7 +3895,7 @@ static void ShowDemoWindowTables() HelpMarker("Setting style.CellPadding to (0,0) or a custom value."); static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; static ImVec2 cell_padding(0.0f, 0.0f); - static bool show_widget_frame_bg = false; + static bool show_widget_frame_bg = true; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders); @@ -3889,32 +3938,108 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); - if (ImGui::TreeNode("Explicit widths")) + if (ImGui::TreeNode("Sizing policies")) { - static ImGuiTableFlags flags = ImGuiTableFlags_None; + static bool fixed_fill = true; + static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); + ImGui::Checkbox("fill", &fixed_fill); + ImGui::SameLine(); HelpMarker( + "Value passed to outer_size only affects tables with _SizingFixedFit or _SizingFixedSame sizing policies.\n\n" + " - outer_size.x == 0: table fit to columns total contents.\n" + " - outer_size.x == -FLT_MIN: table fill until right-most edge."); + ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable); PopStyleCompact(); - if (ImGui::BeginTable("##table1", 4, flags)) + ImVec2 outer_size = ImVec2(fixed_fill ? -FLT_MIN : 0.0f, 0.0f); + static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame }; + for (int table_n = 0; table_n < 4; table_n++) { - // We could also set ImGuiTableFlags_SizingPolicyFixed on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed. - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); - for (int row = 0; row < 5; row++) + ImGui::PushID(table_n); + ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30); + EditTableSizingFlags(&sizing_policy_flags[table_n]); + + // To make it easier to understand the different sizing policy, + // For each policy: we display one table where the columns have equal contents width, and one where the columns have different contents width. + if (ImGui::BeginTable("##table1", 3, sizing_policy_flags[table_n] | flags1, outer_size)) { - ImGui::TableNextRow(); - for (int column = 0; column < 4; column++) + for (int row = 0; row < 3; row++) { - ImGui::TableSetColumnIndex(column); - if (row == 0) - ImGui::Text("(%.2f)", ImGui::GetContentRegionAvail().x); - ImGui::Text("Hello %d,%d", column, row); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::Text("Oh dear"); + ImGui::TableNextColumn(); ImGui::Text("Oh dear"); + ImGui::TableNextColumn(); ImGui::Text("Oh dear"); } + ImGui::EndTable(); + } + if (ImGui::BeginTable("##table2", 3, sizing_policy_flags[table_n] | flags1, outer_size)) + { + for (int row = 0; row < 3; row++) + { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); ImGui::Text("AAAA"); + ImGui::TableNextColumn(); ImGui::Text("BBBBBBBB"); + ImGui::TableNextColumn(); ImGui::Text("CCCCCCCCCCCC"); + } + ImGui::EndTable(); + } + ImGui::PopID(); + } + + ImGui::Spacing(); + ImGui::TextUnformatted("Advanced"); + ImGui::SameLine(); + HelpMarker("This section allows you to interact and see the effect of various sizing policies depending on whether Scroll is enabled and the contents of your columns."); + + enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText }; + static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable; + static int contents_type = CT_ShowWidth; + static int column_count = 3; + + PushStyleCompact(); + ImGui::PushID("Advanced"); + ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); + EditTableSizingFlags(&flags); + ImGui::Combo("Contents", &contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0"); + if (contents_type == CT_FillButton) + { + ImGui::SameLine(); + HelpMarker("Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop where contents width can feed into auto-column width can feed into contents width."); + } + ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp); + ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); + ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); + ImGui::SameLine(); HelpMarker("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."); + ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); + ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); + ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); + ImGui::PopItemWidth(); + ImGui::PopID(); + PopStyleCompact(); + + outer_size = ImVec2(-FLT_MIN, TEXT_BASE_HEIGHT * 7); + if (ImGui::BeginTable("##table2", column_count, flags, outer_size)) + { + for (int cell = 0; cell < 10 * column_count; cell++) + { + ImGui::TableNextColumn(); + int column = ImGui::TableGetColumnIndex(); + int row = ImGui::TableGetRowIndex(); + + ImGui::PushID(cell); + char label[32]; + static char text_buf[32] = ""; + sprintf(label, "Hello %d,%d", column, row); + switch (contents_type) + { + case CT_ShortText: ImGui::TextUnformatted(label); break; + case CT_LongText: ImGui::Text("Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break; + case CT_ShowWidth: ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break; + case CT_Button: ImGui::Button(label); break; + case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; + case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break; + } + ImGui::PopID(); } ImGui::EndTable(); } @@ -3968,7 +4093,7 @@ static void ShowDemoWindowTables() if (ImGui::TreeNode("Horizontal scrolling")) { HelpMarker( - "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingPolicyFixed," + "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, " "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n" "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX," "because the container window won't automatically extend vertically to fix contents (this may be improved in future versions)."); @@ -4021,6 +4146,33 @@ static void ShowDemoWindowTables() } ImGui::EndTable(); } + + ImGui::Spacing(); + ImGui::TextUnformatted("Stretch + ScrollX"); + ImGui::SameLine(); + HelpMarker( + "Showcase using Stretch columns + ScrollX together: " + "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n" + "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns + ScrollX together doesn't make sense."); + static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; + static float inner_width = 1000.0f; + PushStyleCompact(); + ImGui::PushID("flags3"); + ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); + ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX); + ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f"); + ImGui::PopItemWidth(); + ImGui::PopID(); + PopStyleCompact(); + if (ImGui::BeginTable("##table2", 7, flags2, outer_size, inner_width)) + { + for (int cell = 0; cell < 20 * 7; cell++) + { + ImGui::TableNextColumn(); + ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex()); + } + ImGui::EndTable(); + } ImGui::TreePop(); } @@ -4059,7 +4211,7 @@ static void ShowDemoWindowTables() // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above, otherwise in // a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible + resizing the parent window down) const ImGuiTableFlags flags - = ImGuiTableFlags_SizingPolicyFixed | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY + = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable; ImVec2 size = ImVec2(-FLT_MIN, TEXT_BASE_HEIGHT * 9); @@ -4090,7 +4242,43 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); - if (ImGui::TreeNode("Nested")) + if (ImGui::TreeNode("Columns widths")) + { + HelpMarker("Using TableSetupColumn() to setup explicit width."); + + static ImGuiTableFlags flags = ImGuiTableFlags_None; + PushStyleCompact(); + ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); + ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); + ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); + PopStyleCompact(); + + if (ImGui::BeginTable("##table1", 4, flags)) + { + // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed. + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); + for (int row = 0; row < 5; row++) + { + ImGui::TableNextRow(); + for (int column = 0; column < 4; column++) + { + ImGui::TableSetColumnIndex(column); + if (row == 0) + ImGui::Text("(%.2f)", ImGui::GetContentRegionAvail().x); + ImGui::Text("Hello %d,%d", column, row); + } + } + ImGui::EndTable(); + } + ImGui::TreePop(); + } + + if (open_action != -1) + ImGui::SetNextItemOpen(open_action != 0); + if (ImGui::TreeNode("Nested tables")) { HelpMarker("This demonstrate embedding a table into another table cell."); @@ -4132,141 +4320,6 @@ static void ShowDemoWindowTables() ImGui::TreePop(); } - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - if (ImGui::TreeNode("Sizing policies, cell contents")) - { - HelpMarker("This section allows you to interact and see the effect of StretchX vs FixedX sizing policies depending on whether Scroll is enabled and the contents of your columns."); - enum ContentsType { CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText }; - static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg; - static int contents_type = CT_LongText; - static int column_count = 3; - - PushStyleCompact(); - ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); - ImGui::Combo("Contents", &contents_type, "Short Text\0Long Text\0Button\0Fill Button\0InputText\0"); - if (contents_type == CT_FillButton) - { - ImGui::SameLine(); - HelpMarker("Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop where contents width can feed into auto-column width can feed into contents width."); - } - ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp); - ImGui::PopItemWidth(); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); - if (ImGui::CheckboxFlags("ImGuiTableFlags_SizingPolicyStretch", &flags, ImGuiTableFlags_SizingPolicyStretch)) - flags &= ~ImGuiTableFlags_SizingPolicyFixed; // Can't specify both sizing polices so we clear the other - ImGui::SameLine(); HelpMarker("Default if _ScrollX if disabled. Makes columns use _WidthStretch policy by default."); - if (ImGui::CheckboxFlags("ImGuiTableFlags_SizingPolicyFixed", &flags, ImGuiTableFlags_SizingPolicyFixed)) - flags &= ~ImGuiTableFlags_SizingPolicyStretch; // Can't specify both sizing polices so we clear the other - ImGui::SameLine(); HelpMarker("Default if _ScrollX if enabled. Makes columns use _WidthFixed by default, or _WidthFixedResize if _Resizable is not set."); - ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); - ImGui::SameLine(); HelpMarker("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."); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); - ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); - PopStyleCompact(); - - ImVec2 outer_size(-FLT_MIN, TEXT_BASE_HEIGHT * 7); - if (ImGui::BeginTable("##nways", column_count, flags, outer_size)) - { - for (int cell = 0; cell < 10 * column_count; cell++) - { - ImGui::TableNextColumn(); - int column = ImGui::TableGetColumnIndex(); - int row = ImGui::TableGetRowIndex(); - - ImGui::PushID(cell); - char label[32]; - static char text_buf[32] = ""; - sprintf(label, "Hello %d,%d", column, row); - switch (contents_type) - { - case CT_ShortText: ImGui::TextUnformatted(label); break; - case CT_LongText: ImGui::Text("Some longer text %d,%d\nOver two lines..", column, row); break; - case CT_Button: ImGui::Button(label); break; - case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; - case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break; - } - ImGui::PopID(); - } - ImGui::EndTable(); - } - - ImGui::TextUnformatted("Item Widths"); - ImGui::SameLine(); - HelpMarker("Showcase using PushItemWidth() and how it is preserved on a per-column basis"); - if (ImGui::BeginTable("##table2", 3, ImGuiTableFlags_Borders)) - { - ImGui::TableSetupColumn("small"); - ImGui::TableSetupColumn("half"); - ImGui::TableSetupColumn("right-align"); - ImGui::TableHeadersRow(); - - for (int row = 0; row < 3; row++) - { - ImGui::TableNextRow(); - if (row == 0) - { - // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient) - ImGui::TableSetColumnIndex(0); - ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small - ImGui::TableSetColumnIndex(1); - ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); - ImGui::TableSetColumnIndex(2); - ImGui::PushItemWidth(-FLT_MIN); // Right-aligned - } - - // Draw our contents - static float dummy_f = 0.0f; - ImGui::PushID(row); - ImGui::TableSetColumnIndex(0); - ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f); - ImGui::TableSetColumnIndex(1); - ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f); - ImGui::TableSetColumnIndex(2); - ImGui::SliderFloat("float2", &dummy_f, 0.0f, 1.0f); - ImGui::PopID(); - } - ImGui::EndTable(); - } - - ImGui::TextUnformatted("Stretch + ScrollX"); - ImGui::SameLine(); - HelpMarker( - "Showcase using Stretch columns + ScrollX together: " - "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n" - "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns + ScrollX together doesn't make sense."); - static ImGuiTableFlags flags3 = ImGuiTableFlags_SizingPolicyStretch | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg; - static float inner_width = 1000.0f; - PushStyleCompact(); - ImGui::PushID("flags3"); - ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags3, ImGuiTableFlags_ScrollX); - if (ImGui::CheckboxFlags("ImGuiTableFlags_SizingPolicyStretch", &flags3, ImGuiTableFlags_SizingPolicyStretch)) - flags3 &= ~ImGuiTableFlags_SizingPolicyFixed; // Can't specify both sizing polices so we clear the other - if (ImGui::CheckboxFlags("ImGuiTableFlags_SizingPolicyFixed", &flags3, ImGuiTableFlags_SizingPolicyFixed)) - flags3 &= ~ImGuiTableFlags_SizingPolicyStretch; // Can't specify both sizing polices so we clear the other - ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f"); - ImGui::PopItemWidth(); - ImGui::PopID(); - PopStyleCompact(); - if (ImGui::BeginTable("##table3", 7, flags3 | ImGuiTableFlags_SizingPolicyStretch | ImGuiTableFlags_ContextMenuInBody, outer_size, inner_width)) - { - for (int cell = 0; cell < 20 * 7; cell++) - { - ImGui::TableNextColumn(); - ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex()); - } - ImGui::EndTable(); - } - - ImGui::TreePop(); - } - if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Row height")) @@ -4294,11 +4347,15 @@ static void ShowDemoWindowTables() // The default value of outer_size.x is -FLT_MIN which right-align tables. // Using outer_size.x == 0.0f on a table with no scrolling and no stretch column we can make them tighter. ImGui::Text("Using auto/all width, using NoHostExtendY:"); - static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingPolicyFixed; - static bool use_all_width = false; - ImGui::Checkbox("Use all width", &use_all_width); + PushStyleCompact(); + static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit; + static bool fixed_fill = false; + ImGui::Checkbox("fill", &fixed_fill); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); - if (ImGui::BeginTable("##table3", 3, flags, ImVec2(use_all_width ? -FLT_MIN : 0.0f, TEXT_BASE_HEIGHT * 5.5f))) + PopStyleCompact(); + + ImVec2 outer_size = ImVec2(fixed_fill ? -FLT_MIN : 0.0f, TEXT_BASE_HEIGHT * 5.5f); + if (ImGui::BeginTable("##table3", 3, flags, outer_size)) { for (int row = 0; row < 10; row++) { @@ -4476,6 +4533,50 @@ static void ShowDemoWindowTables() ImGui::TreePop(); } + if (open_action != -1) + ImGui::SetNextItemOpen(open_action != 0); + if (ImGui::TreeNode("Item width")) + { + HelpMarker( + "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n" + "Note that on auto-resizing non-resizable fixed columns, querying the content width for e.g. right-alignment doesn't make sense."); + if (ImGui::BeginTable("##table2", 3, ImGuiTableFlags_Borders)) + { + ImGui::TableSetupColumn("small"); + ImGui::TableSetupColumn("half"); + ImGui::TableSetupColumn("right-align"); + ImGui::TableHeadersRow(); + + for (int row = 0; row < 3; row++) + { + ImGui::TableNextRow(); + if (row == 0) + { + // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient) + ImGui::TableSetColumnIndex(0); + ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small + ImGui::TableSetColumnIndex(1); + ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); + ImGui::TableSetColumnIndex(2); + ImGui::PushItemWidth(-FLT_MIN); // Right-aligned + } + + // Draw our contents + static float dummy_f = 0.0f; + ImGui::PushID(row); + ImGui::TableSetColumnIndex(0); + ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f); + ImGui::TableSetColumnIndex(1); + ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f); + ImGui::TableSetColumnIndex(2); + ImGui::SliderFloat("float2", &dummy_f, 0.0f, 1.0f); + ImGui::PopID(); + } + ImGui::EndTable(); + } + ImGui::TreePop(); + } + // Demonstrate using TableHeader() calls instead of TableHeadersRow() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); @@ -4566,7 +4667,7 @@ static void ShowDemoWindowTables() // [2.2] Right-click on the ".." to open a custom popup // [2.3] Right-click in columns to open another custom popup HelpMarker("Demonstrate mixing table context menu (over header), item context button (over button) and custom per-colum context menu (over column body)."); - ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingPolicyFixed | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; + ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; if (ImGui::BeginTable("##table2", COLUMNS_COUNT, flags2)) { ImGui::TableSetupColumn("One"); @@ -4640,7 +4741,7 @@ static void ShowDemoWindowTables() char buf[32]; sprintf(buf, "Synced Table %d", n); bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen); - if (open && ImGui::BeginTable("Table", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingPolicyFixed | ImGuiTableFlags_NoSavedSettings)) + if (open && ImGui::BeginTable("Table", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings)) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); @@ -4689,10 +4790,12 @@ static void ShowDemoWindowTables() ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY; + PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti); ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1)."); ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate); ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0)."); + PopStyleCompact(); if (ImGui::BeginTable("##table", 4, flags, ImVec2(-FLT_MIN, TEXT_BASE_HEIGHT * 15), 0.0f)) { @@ -4756,7 +4859,7 @@ static void ShowDemoWindowTables() | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY - | ImGuiTableFlags_SizingPolicyFixed; + | ImGuiTableFlags_SizingFixedFit; enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; static int contents_type = CT_SelectableSpanRow; @@ -4805,18 +4908,13 @@ static void ShowDemoWindowTables() if (ImGui::TreeNodeEx("Sizing:", ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::CheckboxFlags("ImGuiTableFlags_SizingPolicyStretch", &flags, ImGuiTableFlags_SizingPolicyStretch)) - flags &= ~ImGuiTableFlags_SizingPolicyFixed; // Can't specify both sizing polices so we clear the other - ImGui::SameLine(); HelpMarker("[Default if ScrollX is off]\nFit all columns within available width (or specified inner_width). Fixed and Stretch columns allowed."); - if (ImGui::CheckboxFlags("ImGuiTableFlags_SizingPolicyFixed", &flags, ImGuiTableFlags_SizingPolicyFixed)) - flags &= ~ImGuiTableFlags_SizingPolicyStretch; // Can't specify both sizing polices so we clear the other - ImGui::SameLine(); HelpMarker("[Default if ScrollX is on]\nEnlarge as needed: enable scrollbar if ScrollX is enabled, otherwise extend parent window's contents rectangle. Only Fixed columns allowed. Stretched columns will calculate their width assuming no scrolling."); + EditTableSizingFlags(&flags); + ImGui::SameLine(); HelpMarker("In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical."); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled."); ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths); ImGui::SameLine(); HelpMarker("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."); - ImGui::CheckboxFlags("ImGuiTableFlags_SameWidths", &flags, ImGuiTableFlags_SameWidths); ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); ImGui::SameLine(); HelpMarker("Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options."); ImGui::TreePop(); diff --git a/imgui_internal.h b/imgui_internal.h index 755d95c2..ea7b7899 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2120,19 +2120,19 @@ typedef ImU8 ImGuiTableDrawChannelIdx; // This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped". struct ImGuiTableColumn { - ImRect ClipRect; // Clipping rectangle for the column - ImGuiID UserID; // Optional, value passed to TableSetupColumn() ImGuiTableColumnFlags Flags; // Flags after some patching (not directly same as provided by user). See ImGuiTableColumnFlags_ + float WidthGiven; // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space. float MinX; // Absolute positions float MaxX; - float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_). - float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially. - float WidthAuto; // Automatic width float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout() - float WidthGiven; // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space. - float WorkMinX; // Start position for the frame, currently ~(MinX + CellPaddingX) - float WorkMaxX; - float ItemWidth; + float WidthAuto; // Automatic width + float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially. + float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_). + ImRect ClipRect; // Clipping rectangle for the column + ImGuiID UserID; // Optional, value passed to TableSetupColumn() + float WorkMinX; // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column + float WorkMaxX; // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2) + float ItemWidth; // Current item width for the column, preserved across rows float ContentMaxXFrozen; // Contents maximum position for frozen rows (apart from headers), from which we can infer content width. float ContentMaxXUnfrozen; float ContentMaxXHeadersUsed; // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls @@ -2261,7 +2261,7 @@ struct ImGuiTable ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column! ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing). - ImGuiTableColumnIdx AutoFitSingleStretchColumn; // Index of single stretch column requesting auto-fit. + ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit. ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0. ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. ImGuiTableColumnIdx HeldHeaderColumn; // Index of column header being held. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index e8bb00f7..a574e8c7 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -5,6 +5,8 @@ Index of this file: +// [SECTION] Commentary +// [SECTION] Header mess // [SECTION] Tables: Main code // [SECTION] Tables: Row changes // [SECTION] Tables: Columns changes @@ -24,6 +26,10 @@ Index of this file: // - In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. +//----------------------------------------------------------------------------- +// [SECTION] Commentary +//----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- // Typical tables call flow: (root level is generally public API): //----------------------------------------------------------------------------- @@ -100,20 +106,29 @@ Index of this file: //----------------------------------------------------------------------------- // COLUMNS SIZING POLICIES //----------------------------------------------------------------------------- -// About overriding column width/weight with TableSetupColumn(): +// About overriding column sizing policy and width/weight with TableSetupColumn(): // We use a default parameter of 'init_width_or_weight == -1'. -// - With ImGuiTableColumnFlags_WidthAuto, init_width (ignored) --> width is automatic -// - With ImGuiTableColumnFlags_WidthFixed, init_width <= 0 (default) --> width is automatic -// - With ImGuiTableColumnFlags_WidthFixed, init_width > 0 (explicit) --> width is custom -// - With ImGuiTableColumnFlags_WidthStretch, init_weight <= 0 (default) --> weight is 1.0f -// - With ImGuiTableColumnFlags_WidthStretch, init_weight > 0 (explicit) --> weight is custom +// - with ImGuiTableColumnFlags_WidthAuto, init_width (ignored) --> width is automatic +// - with ImGuiTableColumnFlags_WidthFixed, init_width <= 0 (default) --> width is automatic +// - with ImGuiTableColumnFlags_WidthFixed, init_width > 0 (explicit) --> width is custom +// - with ImGuiTableColumnFlags_WidthStretch, init_weight <= 0 (default) --> weight is 1.0f +// - with ImGuiTableColumnFlags_WidthStretch, init_weight > 0 (explicit) --> weight is custom // Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f) // and you can fit a 100.0f wide item in it without clipping and with full padding. //----------------------------------------------------------------------------- -// About default width policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag) -// - When Table policy ImGuiTableFlags_SizingPolicyStretch --> default Column policy is ImGuiTableColumnFlags_WidthStretch -// - When Table policy ImGuiTableFlags_SizingPolicyFixed and (Table is Resizable or init_width > 0) --> default Column policy is ImGuiTableColumnFlags_WidthFixed -// - When Table policy ImGuiTableFlags_SizingPolicyFixed and (Table is not Resizable and init_width <= 0) --> default Column policy is ImGuiTableColumnFlags_WidthAuto +// About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag) +// - with Table policy ImGuiTableFlags_SizingFixedFit && (Resizable == 1 || init_width > 0) --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width +// - with Table policy ImGuiTableFlags_SizingFixedFit && (Resizable == 0 && init_width <= 0) --> default Column policy is ImGuiTableColumnFlags_WidthAuto, Width always equal to contents width +// - with Table policy ImGuiTableFlags_SizingFixedSame --> default Column policy is ImGuiTableColumnFlags_WidthAuto, default Width is max of all contents width +// - with Table policy ImGuiTableFlags_SizingStretchSame --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f +// - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents +//----------------------------------------------------------------------------- +// About mixing Fixed/Auto and Stretch columns together: +// - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. +// - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place! +// that is, unless 'inner_width' is passed to BeginTable() to explicitely provide a total width to layout columns in. +// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the maximum contents width. +// - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weight/widths. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -145,6 +160,10 @@ Index of this file: // - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false. //----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// [SECTION] Header mess +//----------------------------------------------------------------------------- + #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif @@ -164,10 +183,6 @@ Index of this file: #include // intptr_t #endif -//------------------------------------------------------------------------- -// Warnings -//------------------------------------------------------------------------- - // Visual Studio warnings #ifdef _MSC_VER #pragma warning (disable: 4127) // condition expression is constant @@ -214,12 +229,12 @@ static const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f; // Delay/tim inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_window) { // Adjust flags: set default sizing policy - if ((flags & (ImGuiTableFlags_SizingPolicyStretch | ImGuiTableFlags_SizingPolicyFixed)) == 0) - flags |= ((flags & ImGuiTableFlags_ScrollX) || (outer_window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) ? ImGuiTableFlags_SizingPolicyFixed : ImGuiTableFlags_SizingPolicyStretch; + if ((flags & ImGuiTableFlags_SizingMask_) == 0) + flags |= ((flags & ImGuiTableFlags_ScrollX) || (outer_window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) ? ImGuiTableFlags_SizingFixedFit : ImGuiTableFlags_SizingStretchSame; - // Adjust flags: disable Resizable when using SameWidths (done above enforcing BordersInnerV) - if (flags & ImGuiTableFlags_SameWidths) - flags = (flags & ~ImGuiTableFlags_Resizable) | ImGuiTableFlags_NoKeepColumnsVisible; + // Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame + if ((flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) + flags |= ImGuiTableFlags_NoKeepColumnsVisible; // Adjust flags: enforce borders when resizable if (flags & ImGuiTableFlags_Resizable) @@ -438,7 +453,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->InstanceInteracted = -1; table->ContextPopupColumn = -1; table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1; - table->AutoFitSingleStretchColumn = -1; + table->AutoFitSingleColumn = -1; table->HoveredColumnBody = table->HoveredColumnBorder = -1; for (int n = 0; n < columns_count; n++) { @@ -524,12 +539,12 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) table->ResizedColumnNextWidth = FLT_MAX; table->ResizedColumn = -1; - // Process auto-fit for single stretch column, which is a special case + // Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy. // FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings. - if (table->AutoFitSingleStretchColumn != -1) + if (table->AutoFitSingleColumn != -1) { - TableSetColumnWidth(table->AutoFitSingleStretchColumn, table->Columns[table->AutoFitSingleStretchColumn].WidthAuto); - table->AutoFitSingleStretchColumn = -1; + TableSetColumnWidth(table->AutoFitSingleColumn, table->Columns[table->AutoFitSingleColumn].WidthAuto); + table->AutoFitSingleColumn = -1; } } @@ -586,14 +601,20 @@ static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, I // Sizing Policy if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0) { - // FIXME-TABLE: Inconsistent to promote columns to WidthAuto - if (table->Flags & ImGuiTableFlags_SizingPolicyFixed) + // FIXME-TABLE: clarify promotion to WidthAuto? + const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); + if (table_sizing_policy == ImGuiTableFlags_SizingFixedFit || table_sizing_policy == ImGuiTableFlags_SizingFixedSame) flags |= ((table->Flags & ImGuiTableFlags_Resizable) && !(flags & ImGuiTableColumnFlags_NoResize)) ? ImGuiTableColumnFlags_WidthFixed : ImGuiTableColumnFlags_WidthAuto; else flags |= ImGuiTableColumnFlags_WidthStretch; } - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used. - if (flags & ImGuiTableColumnFlags_WidthAuto) + else + { + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used. + } + + // Resize + if ((flags & ImGuiTableColumnFlags_WidthAuto) != 0 || (table->Flags & ImGuiTableFlags_Resizable) == 0) flags |= ImGuiTableColumnFlags_NoResize; // Sorting @@ -638,16 +659,22 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) ImGuiContext& g = *GImGui; IM_ASSERT(table->IsLayoutLocked == false); + const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); table->IsDefaultDisplayOrder = true; table->ColumnsEnabledCount = 0; table->EnabledMaskByIndex = 0x00; table->EnabledMaskByDisplayOrder = 0x00; table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE - // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. + // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns. // Process columns in their visible orders as we are building the Prev/Next indices. + int count_fixed = 0; // Number of columns that have fixed sizing policies + int count_stretch = 0; // Number of columns that have stretch sizing policies int last_visible_column_idx = -1; - bool want_auto_fit = false; + bool has_auto_fit_request = false; + bool has_resizable = false; + float stretch_sum_width_auto = 0.0f; + float fixed_max_width_auto = 0.0f; for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { const int column_n = table->DisplayOrderToIndex[order_n]; @@ -655,11 +682,10 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->IsDefaultDisplayOrder = false; ImGuiTableColumn* column = &table->Columns[column_n]; - // Clear column settings if not submitted by user. - // Currently we make it mandatory to call TableSetupColumn() every frame. - // It would easily work without but we're ready to guarantee it since e.g. names need resubmission anyway. - // In theory we could be calling TableSetupColumn() here with dummy values it should yield the same effect. - if (column_n >= table->DeclColumnsCount) + // Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame. + // It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway. + // We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect. + if (table->DeclColumnsCount <= column_n) { TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None); column->NameOffset = -1; @@ -667,6 +693,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->InitStretchWeightOrWidth = -1.0f; } + // Update Enabled state, mark settings/sortspecs dirty if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide)) column->IsEnabledNextFrame = true; if (column->IsEnabled != column->IsEnabledNextFrame) @@ -679,16 +706,11 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti)) table->IsSortSpecsDirty = true; - bool start_auto_fit = false; - if (column->Flags & (ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_WidthAuto)) - start_auto_fit = column->WidthRequest < 0.0f; - else - start_auto_fit = column->StretchWeight < 0.0f; + // Auto-fit unsized columns + const bool start_auto_fit = (column->Flags & (ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_WidthAuto)) ? (column->WidthRequest < 0.0f) : (column->StretchWeight < 0.0f); if (start_auto_fit) column->AutoFitQueue = column->CannotSkipItemsQueue = (1 << 3) - 1; // Fit for three frames - ImU64 index_mask = (ImU64)1 << column_n; - ImU64 display_order_mask = (ImU64)1 << column->DisplayOrder; if (!column->IsEnabled) { column->IndexWithinEnabledSet = -1; @@ -700,10 +722,9 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->NextEnabledColumn = -1; if (last_visible_column_idx != -1) table->Columns[last_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n; - column->IndexWithinEnabledSet = table->ColumnsEnabledCount; - table->ColumnsEnabledCount++; - table->EnabledMaskByIndex |= index_mask; - table->EnabledMaskByDisplayOrder |= display_order_mask; + column->IndexWithinEnabledSet = table->ColumnsEnabledCount++; + table->EnabledMaskByIndex |= (ImU64)1 << column_n; + table->EnabledMaskByDisplayOrder |= (ImU64)1 << column->DisplayOrder; last_visible_column_idx = column_n; IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder); @@ -712,8 +733,25 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (!column->IsPreserveWidthAuto) column->WidthAuto = TableGetColumnWidthAuto(table, column); + // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto) + const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0; + if (column_is_resizable) + has_resizable = true; + if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f && !column_is_resizable) + column->WidthAuto = column->InitStretchWeightOrWidth; + if (column->AutoFitQueue != 0x00) - want_auto_fit = true; + has_auto_fit_request = true; + if (column->Flags & ImGuiTableColumnFlags_WidthStretch) + { + stretch_sum_width_auto += column->WidthAuto; + count_stretch++; + } + else + { + fixed_max_width_auto = ImMax(fixed_max_width_auto, column->WidthAuto); + count_fixed++; + } } if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) table->IsSortSpecsDirty = true; @@ -721,19 +759,16 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) IM_ASSERT(table->RightMostEnabledColumn >= 0); // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible - // to avoid the column fitting to wait until the first visible frame of the child container (may or not be a good thing). + // to avoid the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). // FIXME-TABLE: for always auto-resizing columns may not want to do that all the time. - if (want_auto_fit && table->OuterWindow != table->InnerWindow) + if (has_auto_fit_request && table->OuterWindow != table->InnerWindow) table->InnerWindow->SkipItems = false; - if (want_auto_fit) + if (has_auto_fit_request) table->IsSettingsDirty = true; - // [Part 3] Fix column flags. Count how many fixed/stretch columns we have and sum of weights. - 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. - float sum_width_fixed_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns. - float max_width_auto = 0.0f; // Largest auto-width (used for SameWidths feature) + // [Part 3] Fix column flags and record a few extra information. + float sum_width_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding. + float stretch_sum_weights = 0.0f; // Sum of all weights for stretch columns. table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { @@ -741,21 +776,18 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) continue; ImGuiTableColumn* column = &table->Columns[column_n]; - // Count resizable columns - if ((column->Flags & ImGuiTableColumnFlags_NoResize) == 0) - count_resizable++; - + const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0; if (column->Flags & (ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_WidthAuto)) { - // Non-resizable columns keep their requested width - if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f) - if (!(table->Flags & ImGuiTableFlags_Resizable) || (column->Flags & ImGuiTableColumnFlags_NoResize)) - column->WidthRequest = column->WidthAuto = ImMax(column->WidthAuto, column->InitStretchWeightOrWidth); // Use user value regardless of IsPreserveWidthAuto + // Apply same widths policy + float width_auto = column->WidthAuto; + if (table_sizing_policy == ImGuiTableFlags_SizingFixedSame && (column->AutoFitQueue != 0x00 || !column_is_resizable)) + width_auto = fixed_max_width_auto; - // Process auto-fit for non-stretched columns + // Apply automatic width // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!) - if ((column->AutoFitQueue != 0x00) || ((column->Flags & ImGuiTableColumnFlags_WidthAuto) && column->IsVisibleX)) - column->WidthRequest = column->WidthAuto; + if ((column->AutoFitQueue != 0x00) || ((column->Flags & ImGuiTableColumnFlags_WidthAuto) && column->IsVisibleX) || ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable)) + column->WidthRequest = width_auto; // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very @@ -765,60 +797,37 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // 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? - count_fixed += 1; - sum_width_fixed_requests += column->WidthRequest; + sum_width_requests += column->WidthRequest; } else { - IM_ASSERT(column->Flags & ImGuiTableColumnFlags_WidthStretch); + // Initialize stretch weight + if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f || !column_is_resizable) + { + if (column->InitStretchWeightOrWidth > 0.0f) + column->StretchWeight = column->InitStretchWeightOrWidth; + else if (table_sizing_policy == ImGuiTableFlags_SizingStretchProp) + column->StretchWeight = (column->WidthAuto / stretch_sum_width_auto) * count_stretch; + else + column->StretchWeight = 1.0f; + } - // Revert or initialize weight (when column->StretchWeight < 0.0f normally it means there has been no init value so it'll always default to 1.0f) - if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f) - column->StretchWeight = (column->InitStretchWeightOrWidth > 0.0f) ? column->InitStretchWeightOrWidth : 1.0f; - - sum_weights_stretched += column->StretchWeight; + stretch_sum_weights += column->StretchWeight; if (table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder > column->DisplayOrder) table->LeftMostStretchedColumn = (ImGuiTableColumnIdx)column_n; if (table->RightMostStretchedColumn == -1 || table->Columns[table->RightMostStretchedColumn].DisplayOrder < column->DisplayOrder) table->RightMostStretchedColumn = (ImGuiTableColumnIdx)column_n; } column->IsPreserveWidthAuto = false; - max_width_auto = ImMax(max_width_auto, column->WidthAuto); - sum_width_fixed_requests += table->CellPaddingX * 2.0f; + sum_width_requests += table->CellPaddingX * 2.0f; } table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed; - // [Part 4] Apply "same widths" feature. - // - When all columns are fixed or columns are of mixed type: use the maximum auto width. - // - When all columns are stretch: use same weight. - const bool mixed_same_widths = (table->Flags & ImGuiTableFlags_SameWidths) && count_fixed > 0; - if (table->Flags & ImGuiTableFlags_SameWidths) - { - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - if (!(table->EnabledMaskByIndex & ((ImU64)1 << column_n))) - continue; - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->Flags & (ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_WidthAuto)) - { - sum_width_fixed_requests += max_width_auto - column->WidthRequest; // Update old sum - column->WidthRequest = max_width_auto; - } - else - { - sum_weights_stretched += 1.0f - column->StretchWeight; // Update old sum - column->StretchWeight = 1.0f; - if (mixed_same_widths) - column->WidthRequest = max_width_auto; - } - } - } - // [Part 5] 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_avail = ((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth(); - const float width_avail_for_stretched_columns = mixed_same_widths ? 0.0f : width_avail - width_spacings - sum_width_fixed_requests; + const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests; float width_remaining_for_stretched_columns = width_avail_for_stretched_columns; table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) @@ -828,9 +837,9 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) ImGuiTableColumn* column = &table->Columns[column_n]; // Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest) - if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && !mixed_same_widths) + if (column->Flags & ImGuiTableColumnFlags_WidthStretch) { - float weight_ratio = column->StretchWeight / sum_weights_stretched; + float weight_ratio = column->StretchWeight / stretch_sum_weights; column->WidthRequest = IM_FLOOR(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f); width_remaining_for_stretched_columns -= column->WidthRequest; } @@ -848,7 +857,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 6] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column). // Using right-to-left distribution (more likely to match resizing cursor). if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths)) - for (int order_n = table->ColumnsCount - 1; sum_weights_stretched > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) + for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) { if (!(table->EnabledMaskByDisplayOrder & ((ImU64)1 << order_n))) continue; @@ -1000,7 +1009,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (g.IO.MousePos.x >= unused_x1) table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; } - if (count_resizable == 0 && (table->Flags & ImGuiTableFlags_Resizable)) + if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) table->Flags &= ~ImGuiTableFlags_Resizable; // [Part 9] Lock actual OuterRect/WorkRect right-most position. @@ -1302,8 +1311,8 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo // When passing a width automatically enforce WidthFixed policy // (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable) - if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0) - if ((table->Flags & ImGuiTableFlags_SizingPolicyFixed) && (init_width_or_weight > 0.0f)) + 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; TableSetupColumnFlags(table, column, flags); @@ -1314,20 +1323,21 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo if (flags & ImGuiTableColumnFlags_WidthStretch) IM_ASSERT(init_width_or_weight != 0.0f && "Need to provide a valid weight!"); column->InitStretchWeightOrWidth = init_width_or_weight; - if (table->IsInitializing && column->WidthRequest < 0.0f && column->StretchWeight < 0.0f) - { - // Init width or weight - 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; - } if (table->IsInitializing) { + // Init width or weight + 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->IsEnabled = column->IsEnabledNextFrame = false; @@ -1884,7 +1894,7 @@ float ImGui::TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column // Non-resizable fixed columns preserve their requested width if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f) if (!(table->Flags & ImGuiTableFlags_Resizable) || (column->Flags & ImGuiTableColumnFlags_NoResize)) - width_auto = ImMax(width_auto, column->InitStretchWeightOrWidth); + width_auto = column->InitStretchWeightOrWidth; return ImMax(width_auto, table->MinColumnWidth); } @@ -1980,10 +1990,7 @@ void ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n) if (!column->IsEnabled) return; column->CannotSkipItemsQueue = (1 << 0); - if (column->Flags & ImGuiTableColumnFlags_WidthStretch) - table->AutoFitSingleStretchColumn = (ImGuiTableColumnIdx)column_n; - else - column->AutoFitQueue = (1 << 1); + table->AutoFitSingleColumn = (ImGuiTableColumnIdx)column_n; } void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table) @@ -1991,7 +1998,7 @@ void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; - if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Can reset weight of hidden stretch column + if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Cannot reset weight of hidden stretch column continue; column->CannotSkipItemsQueue = (1 << 0); column->AutoFitQueue = (1 << 1); @@ -2343,10 +2350,11 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; const bool is_frozen_separator = (table->FreezeColumnsCount != -1 && table->FreezeColumnsCount == order_n + 1); - if (column->MaxX > table->InnerClipRect.Max.x && !is_resized)// && is_hovered) - continue; - if (column->NextEnabledColumn == -1 && !is_resizable && (table->Flags & ImGuiTableFlags_SameWidths) == 0) + if (column->MaxX > table->InnerClipRect.Max.x && !is_resized) continue; + if (column->NextEnabledColumn == -1 && !is_resizable) + if ((table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame || table->IsOuterRectAutoFitX) + continue; if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size.. continue; @@ -2634,6 +2642,10 @@ void ImGui::TableHeadersRow() ImGuiTable* table = g.CurrentTable; IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); + // 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) + if (!table->IsLayoutLocked) + TableUpdateLayout(table); + // Open row const float row_y1 = GetCursorScreenPos().y; const float row_height = TableGetHeaderRowHeight(); @@ -2856,12 +2868,10 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) } const char* size_all_desc; - if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount) + if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame) size_all_desc = "Size all columns to fit###SizeAll"; // All fixed - else if (table->ColumnsEnabledFixedCount == 0) - size_all_desc = "Size all columns to default###SizeAll"; // All stretch else - size_all_desc = "Size all columns to fit/default###SizeAll";// Mixed + size_all_desc = "Size all columns to default###SizeAll"; // All stretch or mixed if (MenuItem(size_all_desc, NULL)) TableSetColumnWidthAutoAll(table); want_separator = true; @@ -3310,6 +3320,16 @@ void ImGui::TableGcCompactSettings() #ifndef IMGUI_DISABLE_METRICS_WINDOW +static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy) +{ + sizing_policy &= ImGuiTableFlags_SizingMask_; + if (sizing_policy == ImGuiTableFlags_SizingFixedFit) { return "FixedFit"; } + if (sizing_policy == ImGuiTableFlags_SizingFixedSame) { return "FixedSame"; } + if (sizing_policy == ImGuiTableFlags_SizingStretchProp) { return "StretchProp"; } + if (sizing_policy == ImGuiTableFlags_SizingStretchSame) { return "StretchSame"; } + return "N/A"; +} + void ImGui::DebugNodeTable(ImGuiTable* table) { char buf[512]; @@ -3322,10 +3342,12 @@ void ImGui::DebugNodeTable(ImGuiTable* table) if (!is_active) { PopStyleColor(); } if (IsItemHovered()) GetForegroundDrawList()->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)); if (!open) return; bool clear_settings = SmallButton("Clear settings"); - BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f)", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight()); + BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags)); BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : ""); BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX); BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder);