From 0342a3c54887ebfbcadce8308e866fe60c1dab0b Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 28 Feb 2020 15:50:44 +0200 Subject: [PATCH] Menus: Implement BeginMenu() appending to existing menu when executed with same ID multiple times. (#1207) --- docs/CHANGELOG.txt | 1 + docs/TODO.txt | 1 - imgui.cpp | 1 + imgui_internal.h | 1 + imgui_widgets.cpp | 26 ++++++++++++++++++++------ 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6a25fa65d..7a7c1cd24 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -40,6 +40,7 @@ Other Changes: branch pressing arrow keys while dragging a window from a tab could trigger an assert. (#3025) - ColorButton: Added ImGuiColorEditFlags_NoBorder flag to remove the border normally enforced by default for standalone ColorButton. +- BeginMenu: Using same ID multiple times appends content to a menu. (#1207) - BeginMenu: Fixed a bug where SetNextWindowXXX data before a BeginMenu() would not be cleared when the menu is not open. (#3030) - InputText: Fixed password fields displaying ASCII spaces as blanks instead of using the '*' diff --git a/docs/TODO.txt b/docs/TODO.txt index 24c79e431..ea612c577 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -214,7 +214,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - tooltip: tooltips with delay timers? or general timer policy? (instantaneous vs timed): IsItemHovered() with timer + implicit aabb-id for items with no ID. (#1485) - tooltip: drag tooltip hovering over source widget with IsItemHovered/SetTooltip flickers. - - menus: calling BeginMenu() twice with a same name doesn't append as Begin() does for regular windows (#1207) - menus: menu bars inside modal windows are acting weird. - status-bar: add a per-window status bar helper similar to what menu-bar does. - shortcuts: local-style shortcut api, e.g. parse "&Save" diff --git a/imgui.cpp b/imgui.cpp index 98293e62b..5a1810f84 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3623,6 +3623,7 @@ void ImGui::NewFrame() g.FrameCount += 1; g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; + g.RenderedMenusId.resize(0); // Setup current font and draw list shared data g.IO.Fonts->Locked = true; diff --git a/imgui_internal.h b/imgui_internal.h index 87773e997..852211f6b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1159,6 +1159,7 @@ struct ImGuiContext float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? int TooltipOverrideCount; ImVector PrivateClipboard; // If no custom clipboard handler is defined + ImVector RenderedMenusId; // A list of menu IDs that were rendered at least once // Platform support ImVec2 PlatformImePos; // Cursor position request & last passed to the OS Input Method Editor diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ba35af121..1a505a3e2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6222,11 +6222,29 @@ bool ImGui::BeginMenu(const char* label, bool enabled) ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + bool menu_is_open = IsPopupOpen(id); + ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + + // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + flags |= ImGuiWindowFlags_ChildWindow; + + if (g.RenderedMenusId.contains(id)) + { + // Menu with same ID was already created - append to it. + if (menu_is_open) + menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + if (!menu_is_open) + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + return menu_is_open; + } + else + { + g.RenderedMenusId.push_back(id); // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu. + } ImVec2 label_size = CalcTextSize(label, NULL, true); - bool pressed; - bool menu_is_open = IsPopupOpen(id); bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) @@ -6345,11 +6363,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menu_is_open) { - // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) SetNextWindowPos(popup_pos, ImGuiCond_Always); - ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) - flags |= ImGuiWindowFlags_ChildWindow; menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) } else