From abf16e5379595231798606f3dfac820b12af8036 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Oct 2020 19:54:10 +0200 Subject: [PATCH] Tables: Added ImGuiTableFlags_ContextMenuInBody flag. Worked to get TableOpenContextMenu() in public API but kept it internal. --- imgui.h | 29 ++++++++++---------- imgui_demo.cpp | 68 ++++++++++++++++++++++++++++++++++++----------- imgui_internal.h | 2 +- imgui_widgets.cpp | 19 ++++++++++--- 4 files changed, 85 insertions(+), 33 deletions(-) diff --git a/imgui.h b/imgui.h index 2c9184ab2c31..24206fdaa3eb 100644 --- a/imgui.h +++ b/imgui.h @@ -1049,28 +1049,29 @@ enum ImGuiTableFlags_ ImGuiTableFlags_Sortable = 1 << 3, // Allow sorting on one column (sort_specs_count will always be == 1). Call TableGetSortSpecs() to obtain sort specs. ImGuiTableFlags_MultiSortable = 1 << 4, // Allow sorting on multiple columns by holding Shift (sort_specs_count may be > 1). Call TableGetSortSpecs() to obtain sort specs. ImGuiTableFlags_NoSavedSettings = 1 << 5, // Disable persisting columns order, width and sort settings in the .ini file. + ImGuiTableFlags_ContextMenuInBody = 1 << 6, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). // Decoration - ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent to calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) - ImGuiTableFlags_BordersInnerH = 1 << 7, // Draw horizontal borders between rows. - ImGuiTableFlags_BordersOuterH = 1 << 8, // Draw horizontal borders at the top and bottom. - ImGuiTableFlags_BordersInnerV = 1 << 9, // Draw vertical borders between columns. - ImGuiTableFlags_BordersOuterV = 1 << 10, // Draw vertical borders on the left and right sides. + ImGuiTableFlags_RowBg = 1 << 7, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent to calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) + ImGuiTableFlags_BordersInnerH = 1 << 8, // Draw horizontal borders between rows. + ImGuiTableFlags_BordersOuterH = 1 << 9, // Draw horizontal borders at the top and bottom. + ImGuiTableFlags_BordersInnerV = 1 << 10, // Draw vertical borders between columns. + ImGuiTableFlags_BordersOuterV = 1 << 11, // Draw vertical borders on the left and right sides. ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders. ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders. ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders. ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders. ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders. - ImGuiTableFlags_BordersFullHeightV = 1 << 11, // Borders covers all rows even when Headers are being used. Allow resizing from any rows. + ImGuiTableFlags_BordersFullHeightV = 1 << 12, // Borders covers all rows even when Headers are being used. Allow resizing from any rows. // Padding, Sizing - ImGuiTableFlags_SizingPolicyFixedX = 1 << 12, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAlwaysAutoResize policy. Read description above for more details. - ImGuiTableFlags_SizingPolicyStretchX = 1 << 13, // Default if ScrollX is off. Columns will default to use _WidthStretch policy. Read description above for more details. - ImGuiTableFlags_NoHeadersWidth = 1 << 14, // Disable header width contribution to automatic width calculation. - ImGuiTableFlags_NoHostExtendY = 1 << 15, // (FIXME-TABLE: Reword as SizingPolicy?) 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 << 16, // (FIXME-TABLE) Disable code that keeps column always minimally visible when table width gets too small and horizontal scrolling is off. - ImGuiTableFlags_NoClip = 1 << 17, // 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. + ImGuiTableFlags_SizingPolicyFixedX = 1 << 13, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAlwaysAutoResize policy. Read description above for more details. + ImGuiTableFlags_SizingPolicyStretchX = 1 << 14, // Default if ScrollX is off. Columns will default to use _WidthStretch policy. Read description above for more details. + ImGuiTableFlags_NoHeadersWidth = 1 << 15, // Disable header width contribution to automatic width calculation. + ImGuiTableFlags_NoHostExtendY = 1 << 16, // (FIXME-TABLE: Reword as SizingPolicy?) 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 << 17, // (FIXME-TABLE) Disable code that keeps column always minimally visible when table width gets too small and horizontal scrolling is off. + ImGuiTableFlags_NoClip = 1 << 18, // 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. // Scrolling - ImGuiTableFlags_ScrollX = 1 << 20, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. - ImGuiTableFlags_ScrollY = 1 << 21, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. + ImGuiTableFlags_ScrollX = 1 << 19, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. + ImGuiTableFlags_ScrollY = 1 << 20, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. ImGuiTableFlags_Scroll = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY, // [Internal] Combinations and masks diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6e08a8960583..0f27e8cc7133 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4068,54 +4068,91 @@ static void ShowDemoWindowTables() ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Context menus")) { - HelpMarker("By default, TableHeadersRow()/TableHeader() will open a context-menu on right-click."); - ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingPolicyFixedX | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; + HelpMarker("By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\nUsing ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); + static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody; + ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", (unsigned int*)&flags1, ImGuiTableFlags_ContextMenuInBody); + + // Context Menus: first example + // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu. + // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set) const int COLUMNS_COUNT = 3; - if (ImGui::BeginTable("##table1", COLUMNS_COUNT, flags)) + if (ImGui::BeginTable("##table1", COLUMNS_COUNT, flags1)) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); ImGui::TableSetupColumn("Three"); - // Context Menu 1: right-click on header (including empty section after the third column!) should open Default Table Popup + // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu. ImGui::TableHeadersRow(); + + // Submit dummy contents for (int row = 0; row < 4; row++) { ImGui::TableNextRow(); for (int column = 0; column < COLUMNS_COUNT; column++) { ImGui::TableSetColumnIndex(column); - ImGui::PushID(row * COLUMNS_COUNT + column); + ImGui::Text("Cell %d,%d", 0, row); + } + } + ImGui::EndTable(); + } + + // Context Menus: second example + // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. + // [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_SizingPolicyFixedX | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; + if (ImGui::BeginTable("##table2", COLUMNS_COUNT, flags2)) + { + ImGui::TableSetupColumn("One"); + ImGui::TableSetupColumn("Two"); + ImGui::TableSetupColumn("Three"); + + // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. + ImGui::TableHeadersRow(); + for (int row = 0; row < 4; row++) + { + ImGui::TableNextRow(); + for (int column = 0; column < COLUMNS_COUNT; column++) + { + // Submit dummy contents + ImGui::TableSetColumnIndex(column); ImGui::Text("Cell %d,%d", column, row); ImGui::SameLine(); - // Context Menu 2: right-click on buttons open Custom Button Popup + // [2.2] Right-click on the ".." to open a custom popup + ImGui::PushID(row * COLUMNS_COUNT + column); ImGui::SmallButton(".."); if (ImGui::BeginPopupContextItem()) { - ImGui::Text("This is the popup for Button() On Cell %d,%d", column, row); - ImGui::Selectable("Close"); + ImGui::Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } ImGui::PopID(); } } - // Context Menu 3: Right-click anywhere in columns opens a custom popup - // We use the ImGuiPopupFlags_NoOpenOverExistingPopup flag to avoid displaying over either the standard TableHeader context-menu or the Button context-menu. + // [2.3] Right-click anywhere in columns to open another custom popup + // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup + // to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping) const int hovered_column = ImGui::TableGetHoveredColumn(); for (int column = 0; column < COLUMNS_COUNT + 1; column++) { ImGui::PushID(column); - if (hovered_column == column && ImGui::IsMouseReleased(1)) - ImGui::OpenPopup("MyPopup", ImGuiPopupFlags_NoOpenOverExistingPopup); + if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1)) + ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup")) { if (column == COLUMNS_COUNT) - ImGui::Text("This is the popup for unused space after the last column."); + ImGui::Text("This is a custom popup for unused space after the last column."); else - ImGui::Text("This is the popup for Column '%s'", ImGui::TableGetColumnName(column)); - ImGui::Selectable("Close"); + ImGui::Text("This is a custom popup for Column %d", column); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } ImGui::PopID(); @@ -4253,6 +4290,7 @@ static void ShowDemoWindowTables() ImGui::CheckboxFlags("ImGuiTableFlags_Sortable", (unsigned int*)&flags, ImGuiTableFlags_Sortable); ImGui::CheckboxFlags("ImGuiTableFlags_MultiSortable", (unsigned int*)&flags, ImGuiTableFlags_MultiSortable); ImGui::CheckboxFlags("ImGuiTableFlags_NoSavedSettings", (unsigned int*)&flags, ImGuiTableFlags_NoSavedSettings); + ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", (unsigned int*)&flags, ImGuiTableFlags_ContextMenuInBody); ImGui::Unindent(); ImGui::BulletText("Decoration:"); diff --git a/imgui_internal.h b/imgui_internal.h index c35fb38a3ea0..b65b692cfbde 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2285,7 +2285,7 @@ namespace ImGui IMGUI_API void TableSetColumnWidth(ImGuiTable* table, ImGuiTableColumn* column, float width); IMGUI_API void TableDrawBorders(ImGuiTable* table); IMGUI_API void TableDrawContextMenu(ImGuiTable* table); - IMGUI_API void TableOpenContextMenu(ImGuiTable* table, int column_n); + IMGUI_API void TableOpenContextMenu(int column_n = -1); IMGUI_API void TableReorderDrawChannelsForMerge(ImGuiTable* table); IMGUI_API void TableSetColumnSortDirection(ImGuiTable* table, int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6aabe17af68c..6eb797eb0dea 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9232,6 +9232,11 @@ void ImGui::EndTable() if (table->IsInsideRow) TableEndRow(table); + // Context menu in columns body + if (flags & ImGuiTableFlags_ContextMenuInBody) + if (table->HoveredColumnBody != -1 && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Right)) + TableOpenContextMenu((int)table->HoveredColumnBody); + // Finalize table height inner_window->SkipItems = table->HostSkipItems; inner_window->DC.CursorMaxPos = table->HostCursorMaxPos; @@ -10270,6 +10275,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) } // Sorting + // (modify TableOpenContextMenu() to add _Sortable flag if enabling this) #if 0 if ((table->Flags & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0) { @@ -10312,8 +10318,14 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) } // Use -1 to open menu not specific to a given column. -void ImGui::TableOpenContextMenu(ImGuiTable* table, int column_n) +void ImGui::TableOpenContextMenu(int column_n) { + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (column_n == -1 && table->CurrentColumn != -1) // When called within a column automatically use this one (for consistency) + column_n = table->CurrentColumn; + if (column_n == table->ColumnsCount) // To facilitate using with TableGetHoveredColumn() + column_n = -1; IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount); if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { @@ -10387,7 +10399,7 @@ void ImGui::TableHeadersRow() ImVec2 mouse_pos = ImGui::GetMousePos(); if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count) if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height) - TableOpenContextMenu(table, -1); // Will open a non-column-specific popup. + TableOpenContextMenu(-1); // Will open a non-column-specific popup. } // Emit a column header (text + optional sort order) @@ -10527,7 +10539,7 @@ void ImGui::TableHeader(const char* label) // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden if (IsMouseReleased(1) && IsItemHovered()) - TableOpenContextMenu(table, column_n); + TableOpenContextMenu(column_n); } // Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert @@ -10589,6 +10601,7 @@ bool ImGui::TableGetColumnIsSorted(int column_n) return (column->SortOrder != -1); } +// Return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. int ImGui::TableGetHoveredColumn() { ImGuiContext& g = *GImGui;