From 24bd33ace808ed77fd1840b19f2b8b4789a3ae62 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Feb 2020 16:42:24 +0100 Subject: [PATCH] Menus: Some renaming, comments, add to demo. Amend 0342a3c. (#1207) --- imgui.cpp | 3 ++- imgui.h | 3 ++- imgui_demo.cpp | 36 ++++++++++++++++++++++++++++++++++-- imgui_internal.h | 2 +- imgui_widgets.cpp | 21 +++++++++++---------- 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5a1810f8..974a876c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3623,7 +3623,7 @@ void ImGui::NewFrame() g.FrameCount += 1; g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; - g.RenderedMenusId.resize(0); + g.MenusIdSubmittedThisFrame.resize(0); // Setup current font and draw list shared data g.IO.Fonts->Locked = true; @@ -3915,6 +3915,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.ShrinkWidthBuffer.clear(); g.PrivateClipboard.clear(); + g.MenusIdSubmittedThisFrame.clear(); g.InputTextState.ClearFreeMemory(); g.SettingsWindows.clear(); diff --git a/imgui.h b/imgui.h index 8ddd84a4..1e7b3f33 100644 --- a/imgui.h +++ b/imgui.h @@ -559,7 +559,8 @@ namespace ImGui // Widgets: Menus // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar. - // - Use BeginMainMenuBar() to create a menu bar at the top of the screen. + // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it. + // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it. IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a698a91a..c219f207 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -288,6 +288,27 @@ void ImGui::ShowDemoWindow(bool* p_open) // Menu Bar if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("Foo")) + { + if (ImGui::BeginMenu("AAA")) + { + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("BBB")) + { + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("AAA")) + { + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("BBB")) + { + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Menu")) { ShowExampleMenuFile(); @@ -3580,6 +3601,7 @@ static void ShowExampleMenuFile() } if (ImGui::MenuItem("Save", "Ctrl+S")) {} if (ImGui::MenuItem("Save As..")) {} + ImGui::Separator(); if (ImGui::BeginMenu("Options")) { @@ -3591,13 +3613,12 @@ static void ShowExampleMenuFile() ImGui::EndChild(); static float f = 0.5f; static int n = 0; - static bool b = true; ImGui::SliderFloat("Value", &f, 0.0f, 1.0f); ImGui::InputFloat("Input", &f, 0.1f); ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0"); - ImGui::Checkbox("Check", &b); ImGui::EndMenu(); } + if (ImGui::BeginMenu("Colors")) { float sz = ImGui::GetTextLineHeight(); @@ -3612,6 +3633,17 @@ static void ShowExampleMenuFile() } ImGui::EndMenu(); } + + // Here we demonstrate appending again to the "Options" menu (which we already created above) + // Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice. + // In a real code-base using it would make senses to use this feature from very different code locations. + if (ImGui::BeginMenu("Options")) // <-- Append! + { + static bool b = true; + ImGui::Checkbox("SomeOption", &b); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Disabled", false)) // Disabled { IM_ASSERT(0); diff --git a/imgui_internal.h b/imgui_internal.h index 852211f6..dd92b6a5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1159,7 +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 + ImVector MenusIdSubmittedThisFrame; // 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 1a505a3e..f79fd9c5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6223,25 +6223,26 @@ bool ImGui::BeginMenu(const char* label, bool enabled) 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)) + ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) flags |= ImGuiWindowFlags_ChildWindow; - if (g.RenderedMenusId.contains(id)) + // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). + // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. + // If somehow this is ever becoming a problem we can switch to use e.g. a ImGuiStorager mapping key to last frame used. + if (g.MenusIdSubmittedThisFrame.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 + else + 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. - } + + // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu + g.MenusIdSubmittedThisFrame.push_back(id); ImVec2 label_size = CalcTextSize(label, NULL, true); bool pressed;