Nav: allow ImGuiKey_Menu or Shift + F10 to open context menus. Amends. (#8803, #9270)

This doesn't attempt to move the shortcut polling in NavUpdate() yet.
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index a88d8d0..161a4e6 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -90,6 +90,10 @@
     - TextLink() underline thickness.
     - ColorButton() border thickness.
     - Separator() thickness, via scaling newly added style.SeparatorSize. (#2657, #9263)
+- Nav:
+  - Popups: Shift+F10 or Menu key can now open popups menus when using
+    BeginPopupContextItem(), BeginPopupContextWindow() or OpenPopupOnItemClick().
+    (#8803, #9270) [@exelix11, @ocornut]
 - Clipper:
   - Clear `DisplayStart`/`DisplayEnd` fields when `Step()` returns false.
   - Added `UserIndex` helper storage. This is solely a convenience for cases where
diff --git a/imgui.cpp b/imgui.cpp
index 8455298..422cd22 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -167,6 +167,7 @@
      - Home, End                    Scroll to top, scroll to bottom.
      - Alt                          Toggle between scrolling layer and menu layer.
      - Ctrl+Tab then Ctrl+Arrows    Move window. Hold Shift to resize instead of moving.
+     - Menu or Shift+F10            Open context menu.
    - Output when ImGuiConfigFlags_NavEnableKeyboard set,
      - io.WantCaptureKeyboard flag is set when keyboard is claimed.
      - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
@@ -12498,21 +12499,57 @@
     return ImGuiMouseButton_Right; // Default == 1
 }
 
+bool ImGui::IsPopupOpenRequestForItem(ImGuiPopupFlags popup_flags, ImGuiID id)
+{
+    ImGuiContext& g = *GImGui;
+    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;
+           }
+
+    return false;
+}
+
+bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags, ImGuiID id)
+{
+    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;
+                }
+
+    return false;
+}
+
 // Helper to open a popup if mouse button is released over the item
 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
-    ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags);
-    bool isMouseInvocation = IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
-    bool isKeyboardInvocation = false;
-    if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsItemFocused())
-        isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift);
-    if (isKeyboardInvocation || isMouseInvocation)
+    if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID))
     {
-        ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
-        IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
+        ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
+        IM_ASSERT(id != 0);                                              // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
         OpenPopupEx(id, popup_flags);
     }
 }
@@ -12539,14 +12576,9 @@
     ImGuiWindow* window = g.CurrentWindow;
     if (window->SkipItems)
         return false;
-    ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
-    IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
-    ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags);
-    bool isMouseInvocation = IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
-    bool isKeyboardInvocation = false;
-    if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsItemFocused())
-        isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift);
-    if (isKeyboardInvocation || isMouseInvocation)
+    ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItem ID. Using LastItem ID as a Popup ID won't conflict!
+    IM_ASSERT(id != 0);                                              // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
+    if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID))
         OpenPopupEx(id, popup_flags);
     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
 }
@@ -12558,14 +12590,8 @@
     if (!str_id)
         str_id = "window_context";
     ImGuiID id = window->GetID(str_id);
-    ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags);
-    bool isMouseInvocation = IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
-    bool isKeyboardInvocation = false;
-    if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsWindowFocused())
-        isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift);
-    if (isMouseInvocation || isKeyboardInvocation)
-        if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered() || isKeyboardInvocation)
-            OpenPopupEx(id, popup_flags);
+    if (IsPopupOpenRequestForWindow(popup_flags, window->ID))
+        OpenPopupEx(id, popup_flags);
     return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
 }
 
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index d7dc0b0..e8a4392 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -8738,6 +8738,7 @@
     BulletText("Return to input text into a widget.");
     BulletText("Escape to deactivate a widget, close popup,\nexit a child window or the menu layer, clear focus.");
     BulletText("Alt to jump to the menu layer of a window.");
+    BulletText("Menu or Shift+F10 to open a context menu.");
     Unindent();
 }
 
diff --git a/imgui_internal.h b/imgui_internal.h
index c540407..370f3cc 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -3331,6 +3331,8 @@
     IMGUI_API ImVec2        FindBestWindowPosForPopup(ImGuiWindow* window);
     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);
 
     // Tooltips
     IMGUI_API bool          BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);