From 14a500a4760aaa74bc5913bb9de6d13035e5bd0b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Mar 2026 18:49:57 +0100 Subject: [PATCH] Nav: allow ImGuiKey_Menu or Shift + F10 to open context menus. Rework with polling in NavUpdate(). (#8803, #9270) This might be a little less flexible but removes burden from the higher-frequency calls. --- imgui.cpp | 51 +++++++++++++++++++++++++----------------------- imgui_internal.h | 4 +++- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 422cd22bd..5e4c1606b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1358,6 +1358,7 @@ static void NavUpdateWindowing(); static void NavUpdateWindowingApplyFocus(ImGuiWindow* window); static void NavUpdateWindowingOverlay(); static void NavUpdateCancelRequest(); +static void NavUpdateContextMenuRequest(); static void NavUpdateCreateMoveRequest(); static void NavUpdateCreateTabbingRequest(); static float NavUpdatePageUpPageDown(); @@ -4215,6 +4216,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) NavWindow = NULL; NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0; NavLayer = ImGuiNavLayer_Main; + NavOpenContextMenuItemId = NavOpenContextMenuWindowId = 0; NavNextActivateId = 0; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavHighlightActivatedId = 0; @@ -12505,38 +12507,21 @@ bool ImGui::IsPopupOpenRequestForItem(ImGuiPopupFlags popup_flags, ImGuiID id) ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) return true; - - if (g.NavId == id && g.NavId != 0) // == IsItemFocused() - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - if (Shortcut(ImGuiKey_F10 | ImGuiMod_Shift) || IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner)) - { - g.NavInputSource = ImGuiInputSource_Keyboard; - SetNavCursorVisibleAfterMove(); - return true; - } - + if (g.NavOpenContextMenuItemId == id && IsItemFocused()) + return true; return false; } -bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags, ImGuiID id) +bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) return true; - - // FIXME-NAV: How to meaningful support ImGuiPopupFlags_NoOpenOverItems with keyboard? - if (g.CurrentWindow->ID == id && g.NavWindow != NULL) - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - if (IsWindowChildOf(g.NavWindow, g.CurrentWindow, false)) - if (Shortcut(ImGuiKey_F10 | ImGuiMod_Shift) || IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner)) - { - g.NavInputSource = ImGuiInputSource_Keyboard; - SetNavCursorVisibleAfterMove(); - return true; - } - + if (g.NavOpenContextMenuWindowId && g.CurrentWindow->ID) + if (IsWindowChildOf(g.NavWindow, g.CurrentWindow, false)) // This enable ordering to be used to disambiguate item vs window (#8803) + return true; return false; } @@ -12590,7 +12575,7 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl if (!str_id) str_id = "window_context"; ImGuiID id = window->GetID(str_id); - if (IsPopupOpenRequestForWindow(popup_flags, window->ID)) + if (IsPopupOpenRequestForWindow(popup_flags)) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } @@ -13768,6 +13753,7 @@ static void ImGui::NavUpdate() // Process NavCancel input (to close a popup, get back to parent, clear focus) NavUpdateCancelRequest(); + NavUpdateContextMenuRequest(); // Process manual activation request g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0; @@ -14252,6 +14238,23 @@ static void ImGui::NavUpdateCancelRequest() } } +static void ImGui::NavUpdateContextMenuRequest() +{ + ImGuiContext& g = *GImGui; + g.NavOpenContextMenuItemId = g.NavOpenContextMenuWindowId = 0; + const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + if (!nav_keyboard_active || g.NavWindow == NULL) + return; + + const bool request = IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner) || (IsKeyPressed(ImGuiKey_F10, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner) && g.IO.KeyMods == ImGuiMod_Shift); + if (!request) + return; + g.NavOpenContextMenuItemId = g.NavId; + g.NavOpenContextMenuWindowId = g.NavWindow->ID; + g.NavInputSource = ImGuiInputSource_Keyboard; + SetNavCursorVisibleAfterMove(); +} + // Handle PageUp/PageDown/Home/End keys // Called from NavUpdateCreateMoveRequest() which will use our output to create a move request // FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference diff --git a/imgui_internal.h b/imgui_internal.h index 370f3cc5d..cd1ae9664 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2332,6 +2332,8 @@ struct ImGuiContext ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain. ImGuiID NavHighlightActivatedId; float NavHighlightActivatedTimer; + ImGuiID NavOpenContextMenuItemId; + ImGuiID NavOpenContextMenuWindowId; ImGuiID NavNextActivateId; // Set by ActivateItemByID(), queued until next frame. ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Gamepad @@ -3332,7 +3334,7 @@ namespace ImGui IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); IMGUI_API ImGuiMouseButton GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags); IMGUI_API bool IsPopupOpenRequestForItem(ImGuiPopupFlags flags, ImGuiID id); - IMGUI_API bool IsPopupOpenRequestForWindow(ImGuiPopupFlags flags, ImGuiID id); + IMGUI_API bool IsPopupOpenRequestForWindow(ImGuiPopupFlags flags); // Tooltips IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);