diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a88d8d0a7..161a4e6e7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,6 +90,10 @@ Other Changes: - 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 845529829..422cd22bd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -167,6 +167,7 @@ CODE - 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 @@ ImGuiMouseButton ImGui::GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags) 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 @@ bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flag 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 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl 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 d7dc0b028..e8a43924c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8738,6 +8738,7 @@ void ImGui::ShowUserGuide() 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 c54040747..370f3cc5d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3331,6 +3331,8 @@ namespace ImGui 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);