From ad83976b35c89ad3cbe9c6d8b7bf0a501457819d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Dec 2020 22:29:11 +0100 Subject: [PATCH] Tables: Added ImGuiTableFlags_NoHostExtendX (#3605) marked as WIP, will probably rename. Moved some code from BeginTable() to TableUpdateLayout() to late latch some of the required data. --- imgui.h | 23 ++++++++++++----------- imgui_demo.cpp | 26 ++++++++++++++++++++++++++ imgui_internal.h | 1 + imgui_tables.cpp | 48 ++++++++++++++++++++++++++++++------------------ 4 files changed, 69 insertions(+), 29 deletions(-) diff --git a/imgui.h b/imgui.h index bf836a751..c0d9dcf3b 100644 --- a/imgui.h +++ b/imgui.h @@ -1075,20 +1075,21 @@ 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_NoHeadersWidth = 1 << 16, // Disable headers' contribution to automatic width calculation. - ImGuiTableFlags_NoHostExtendY = 1 << 17, // Disable extending past the limit set by outer_size.y, only meaningful when neither of ScrollX|ScrollY are set (data below the limit will be clipped and not visible) - ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. - ImGuiTableFlags_PreciseWidths = 1 << 19, // 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. - ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). + ImGuiTableFlags_NoHostExtendX = 1 << 17, // Disable extending table past the limit required by the right-most columns. Only meaningful with Fixed columns and when neither ScrollX nor ScrollY are set. + ImGuiTableFlags_NoHostExtendY = 1 << 18, // 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 << 19, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. + ImGuiTableFlags_PreciseWidths = 1 << 20, // 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. + ImGuiTableFlags_NoClip = 1 << 21, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). // Padding - ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outer-most padding. - ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outer-most padding. - ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). + ImGuiTableFlags_PadOuterX = 1 << 22, // Default if BordersOuterV is on. Enable outer-most padding. + ImGuiTableFlags_NoPadOuterX = 1 << 23, // Default if BordersOuterV is off. Disable outer-most padding. + ImGuiTableFlags_NoPadInnerX = 1 << 24, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). // Scrolling - ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. - ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. + ImGuiTableFlags_ScrollX = 1 << 25, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. + ImGuiTableFlags_ScrollY = 1 << 26, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. // Sorting - ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). - ImGuiTableFlags_SortTristate = 1 << 27 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + ImGuiTableFlags_SortMulti = 1 << 27, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). + ImGuiTableFlags_SortTristate = 1 << 28 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). }; // Flags for ImGui::TableSetupColumn() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index bac215925..3d1773a33 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4221,6 +4221,7 @@ static void ShowDemoWindowTables() ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Outer size")) { + ImGui::Text("Using manual/explicit size:"); if (ImGui::BeginTable("##table1", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) { for (int row = 0; row < 5; row++) @@ -4248,6 +4249,30 @@ static void ShowDemoWindowTables() } ImGui::EndTable(); } + ImGui::Spacing(); + + // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY + // FIXME-TABLE: WIP API: Don't use yet, will probably rework/rename those flags. + ImGui::Text("Using NoHostExtend flags:"); + static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_ColumnsWidthFixed; + ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); + ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); + if (ImGui::BeginTable("##table3", 3, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f))) + { + for (int row = 0; row < 10; row++) + { + ImGui::TableNextRow(); + for (int column = 0; column < 3; column++) + { + ImGui::TableNextColumn(); + ImGui::Text("Cell %d,%d", column, row); + } + } + ImGui::EndTable(); + } + ImGui::SameLine(); + ImGui::Text("Hello!"); + ImGui::TreePop(); } @@ -4715,6 +4740,7 @@ static void ShowDemoWindowTables() flags &= ~ImGuiTableFlags_ColumnsWidthStretch; // 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."); ImGui::CheckboxFlags("ImGuiTableFlags_NoHeadersWidth", &flags, ImGuiTableFlags_NoHeadersWidth); + ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled."); diff --git a/imgui_internal.h b/imgui_internal.h index cd10cfeb7..4ccfbf174 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2072,6 +2072,7 @@ struct ImGuiTable bool IsResetAllRequest; bool IsResetDisplayOrderRequest; bool IsUnfrozen; // Set when we got past the frozen row. + bool IsOuterRectFitX; // Set when outer_size value passed to BeginTable() is (>= -1.0f && <= 0.0f) bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 9c4a66c07..0052c563b 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -38,7 +38,7 @@ Index of this file: // - TableSetupColumn() user submit columns details (optional) // - TableSetupScrollFreeze() user submit scroll freeze information (optional) //----------------------------------------------------------------------------- -// - TableUpdateLayout() [Internal] setup everything, lock all widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). +// - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). // | TableSetupDrawChannels() - setup ImDrawList channels // | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission // | TableDrawContextMenu() - draw right-click context menu @@ -280,6 +280,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->InnerWidth = inner_width; table->OuterRect = outer_rect; table->WorkRect = outer_rect; + table->IsOuterRectFitX = (outer_size.x >= -1.0f && outer_size.x <= 0.0f); // Bit ambiguous // When not using a child window, WorkRect.Max will grow as we append contents. if (use_child_window) @@ -325,7 +326,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; table->HostBackupItemWidth = outer_window->DC.ItemWidth; table->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; - inner_window->ParentWorkRect = table->WorkRect; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // Padding and Spacing @@ -356,13 +356,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->InnerClipRect.ClipWithFull(table->HostClipRect); table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y; - // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default. - // This is because all our cell highlight are manually clipped with BgClipRect - // Larger at first, if/after unfreezing will become same as tight - table->BgClipRect = table->InnerClipRect; - table->BgClipRectForDrawCmd = table->HostClipRect; - IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); - table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any @@ -373,8 +366,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Using opaque colors facilitate overlapping elements of the grid table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); - table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f); - table->BorderX2 = table->InnerClipRect.Max.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : +1.0f); // Make table current const int table_idx = g.Tables.GetIndex(table); @@ -1001,26 +992,39 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 8] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it) // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either // because of using _WidthAutoResize/_WidthStretch). This will hide the resizing option from the context menu. + const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (is_hovering_table && table->HoveredColumnBody == -1) { - float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (g.IO.MousePos.x >= unused_x1) table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; } if (count_resizable == 0 && (table->Flags & ImGuiTableFlags_Resizable)) table->Flags &= ~ImGuiTableFlags_Resizable; - // [Part 9] Allocate draw channels + // [Part 9] Lock actual OuterRect/WorkRect right-most position. + // This is done late to handle the case of fixed-columns tables not claiming more widths that they need. + // Because of this we are careful with uses of WorkRect and InnerClipRect before this point. + if ((table->Flags & ImGuiTableFlags_NoHostExtendX) && table->InnerWindow == table->OuterWindow && table->RightMostStretchedColumn == -1) + { + table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1; + table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1); + table->IsOuterRectFitX = false; + } + table->InnerWindow->ParentWorkRect = table->WorkRect; + table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f); + table->BorderX2 = table->InnerClipRect.Max.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : +1.0f); + + // [Part 10] Allocate draw channels and setup background cliprect TableSetupDrawChannels(table); - // [Part 10] Hit testing on borders + // [Part 11] Hit testing on borders if (table->Flags & ImGuiTableFlags_Resizable) TableUpdateBorders(table); table->LastFirstRowHeight = 0.0f; table->IsLayoutLocked = true; table->IsUsingHeaders = false; - // [Part 11] Context menu + // [Part 12] Context menu if (table->IsContextPopupOpen && table->InstanceCurrent == table->InstanceInteracted) { const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); @@ -1227,14 +1231,14 @@ void ImGui::EndTable() outer_window->DC.ItemWidth = table->HostBackupItemWidth; outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize; outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset; + const float outer_width = table->IsOuterRectFitX ? table->ColumnsAutoFitWidth : table->WorkRect.GetWidth(); if (inner_window != outer_window) { EndChild(); } else { - ImVec2 item_size = table->OuterRect.GetSize(); - item_size.x = table->ColumnsTotalWidth; + ImVec2 item_size(outer_width, table->OuterRect.GetHeight()); ItemSize(item_size); } @@ -1247,7 +1251,8 @@ void ImGui::EndTable() } else { - outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos_x, table->WorkRect.Min.x + table->ColumnsAutoFitWidth); // For auto-fit + outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos_x, table->WorkRect.Min.x + outer_width); // For auto-fit + outer_window->DC.CursorPosPrevLine.x = table->WorkRect.Max.x; // For consistent reaction to SameLine() // FIXME: Should be a feature of layout/ItemAdd } // Save settings @@ -2068,6 +2073,13 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) } column->DrawChannelCurrent = column->DrawChannelFrozen; } + + // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default. + // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect. + // (This technically isn't part of setting up draw channels, but is reasonably related to be done here) + table->BgClipRect = table->InnerClipRect; + table->BgClipRectForDrawCmd = table->HostClipRect; + IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); } // This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable().