From b76ab6232d426c646149fd2fa9c8f3aca0a4b5f7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 13 Mar 2026 14:48:10 +0100 Subject: [PATCH 1/3] Nav: changed Gamepad mapping for "Activate with Text Input" action from FaceUp press to FaceDown long press. (#8803, #787) --- docs/CHANGELOG.txt | 5 +++++ imgui.cpp | 18 +++++++++++++----- imgui.h | 4 ++-- imgui_internal.h | 3 ++- imgui_widgets.cpp | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 50734a02..df0f2bf5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -95,6 +95,11 @@ Other Changes: - ColorButton() border thickness. - Separator() thickness, via scaling newly added style.SeparatorSize. (#2657, #9263) - Nav: + - Changed Gamepad mapping for "Activate with Text Input" action: (#8803, #787) + - Previously: press North button (PS4/PS5 triangle, Switch X, Xbox Y). + - Now: long press (hold) Activate button (PS4/PS5 cross, Switch B, Xbox A) for ~0.60 secs. + This is rarely used, somehow easier to discover, and frees a button for other uses. + See updated Gamepad Control Sheets: https://www.dearimgui.com/controls_sheets - Popups: Shift+F10 or Menu key can now open popups menus when using BeginPopupContextItem(), BeginPopupContextWindow() or OpenPopupOnItemClick(). (#8803, #9270) [@exelix11, @ocornut] diff --git a/imgui.cpp b/imgui.cpp index c043989a..61a6456b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1313,6 +1313,7 @@ static const float FONT_DEFAULT_SIZE_BASE = 20.0f; static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut. +static const float NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY = 0.60f; // Time to hold activation button (e.g. FaceDown) to turn the activation into a text input. static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. @@ -4217,6 +4218,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) NavWindow = NULL; NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0; NavLayer = ImGuiNavLayer_Main; + NavIdItemFlags = ImGuiItemFlags_None; NavOpenContextMenuItemId = NavOpenContextMenuWindowId = 0; NavNextActivateId = 0; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; @@ -13101,6 +13103,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); + g.NavIdItemFlags = (g.LastItemData.ID == id) ? g.LastItemData.ItemFlags : ImGuiItemFlags_None; if (id == g.ActiveIdIsAlive) g.NavIdIsAlive = true; @@ -13370,6 +13373,7 @@ static void ImGui::NavProcessItem() SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath g.NavFocusScopeId = g.CurrentFocusScopeId; g.NavIdIsAlive = true; + g.NavIdItemFlags = item_flags; if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData) { IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid); @@ -13763,21 +13767,25 @@ static void ImGui::NavUpdate() { const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner)); const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner))); - const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_NoOwner)); - const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, 0, ImGuiKeyOwner_NoOwner))); + const bool input_pressed_keyboard = nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner)); + bool input_pressed_gamepad = false; + if (activate_down && nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner) && (g.NavIdItemFlags & ImGuiItemFlags_Inputable)) // requires ImGuiItemFlags_Inputable to avoid retriggering regular buttons. + if (GetKeyData(ImGuiKey_NavGamepadActivate)->DownDurationPrev < NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY && GetKeyData(ImGuiKey_NavGamepadActivate)->DownDuration >= NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY) + input_pressed_gamepad = true; + if (g.ActiveId == 0 && activate_pressed) { g.NavActivateId = g.NavId; g.NavActivateFlags = ImGuiActivateFlags_PreferTweak; } - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed) + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (input_pressed_keyboard || input_pressed_gamepad)) { g.NavActivateId = g.NavId; g.NavActivateFlags = ImGuiActivateFlags_PreferInput; } - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down)) + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_pressed_keyboard || input_pressed_gamepad)) // FIXME-NAV: Unsure why input_pressed_xxx (migrated from input_down which was already dubious) g.NavActivateDownId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed)) + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed_keyboard || input_pressed_gamepad)) { g.NavActivatePressedId = g.NavId; NavHighlightActivated(g.NavId); diff --git a/imgui.h b/imgui.h index a41b91b5..349c3104 100644 --- a/imgui.h +++ b/imgui.h @@ -1620,8 +1620,8 @@ enum ImGuiKey : int ImGuiKey_GamepadBack, // View | - | Share | ImGuiKey_GamepadFaceLeft, // X | Y | Square | Tap: Toggle Menu. Hold: Windowing mode (Focus/Move/Resize windows) ImGuiKey_GamepadFaceRight, // B | A | Circle | Cancel / Close / Exit - ImGuiKey_GamepadFaceUp, // Y | X | Triangle | Text Input / On-screen Keyboard - ImGuiKey_GamepadFaceDown, // A | B | Cross | Activate / Open / Toggle / Tweak + ImGuiKey_GamepadFaceUp, // Y | X | Triangle | + ImGuiKey_GamepadFaceDown, // A | B | Cross | Activate / Open / Toggle / Tweak. Hold for 0.60f to Activate in Text Input mode (e.g. wired to an on-screen keyboard). ImGuiKey_GamepadDpadLeft, // D-pad Left | " | " | Move / Tweak / Resize Window (in Windowing mode) ImGuiKey_GamepadDpadRight, // D-pad Right | " | " | Move / Tweak / Resize Window (in Windowing mode) ImGuiKey_GamepadDpadUp, // D-pad Up | " | " | Move / Tweak / Resize Window (in Windowing mode) diff --git a/imgui_internal.h b/imgui_internal.h index cd1ae966..e9eded33 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1509,7 +1509,7 @@ typedef ImBitArray ImBitAr #define ImGuiKey_NavGamepadActivate (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceRight : ImGuiKey_GamepadFaceDown) #define ImGuiKey_NavGamepadCancel (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceDown : ImGuiKey_GamepadFaceRight) #define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft -#define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp +//#define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp enum ImGuiInputEventType { @@ -2325,6 +2325,7 @@ struct ImGuiContext ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope) ImGuiNavLayer NavLayer; // Focused layer (main scrolling layer, or menu/title bar layer) + ImGuiItemFlags NavIdItemFlags; ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItemByID() ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 78fae523..6552dc97 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5068,7 +5068,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_enter = Shortcut(ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiKey_KeypadEnter, f_repeat, id); const bool is_ctrl_enter = Shortcut(ImGuiMod_Ctrl | ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_KeypadEnter, f_repeat, id); const bool is_shift_enter = Shortcut(ImGuiMod_Shift | ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_KeypadEnter, f_repeat, id); - const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false)); + const bool is_gamepad_validate = nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false); const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id)); // FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of. From 49ee151ed6ce9b8043542ae5b1e2472e5bfa4bad Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 13 Mar 2026 15:01:10 +0100 Subject: [PATCH 2/3] Nav: pressing gamepad north button activates context menus. + update ShowUserGuide(). --- docs/CHANGELOG.txt | 1 + imgui.cpp | 7 +++++-- imgui.h | 6 +++--- imgui_demo.cpp | 11 ++++++++++- imgui_internal.h | 4 ++-- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index df0f2bf5..acc3965a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -103,6 +103,7 @@ Other Changes: - Popups: Shift+F10 or Menu key can now open popups menus when using BeginPopupContextItem(), BeginPopupContextWindow() or OpenPopupOnItemClick(). (#8803, #9270) [@exelix11, @ocornut] + - Popups: pressing North button (PS4/PS5 triangle, SwitchX, Xbox Y) also open popups menus. - 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 61a6456b..b7c5d1b2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14252,10 +14252,13 @@ 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) + const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + if ((!nav_keyboard_active && !nav_gamepad_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); + bool request = false; + request |= nav_keyboard_active && (IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner) || (IsKeyPressed(ImGuiKey_F10, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner) && g.IO.KeyMods == ImGuiMod_Shift)); + request |= nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadContextMenu, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner); if (!request) return; g.NavOpenContextMenuItemId = g.NavId; diff --git a/imgui.h b/imgui.h index 349c3104..b69a9b56 100644 --- a/imgui.h +++ b/imgui.h @@ -1618,10 +1618,10 @@ enum ImGuiKey : int // // XBOX | SWITCH | PLAYSTA. | -> ACTION ImGuiKey_GamepadStart, // Menu | + | Options | ImGuiKey_GamepadBack, // View | - | Share | - ImGuiKey_GamepadFaceLeft, // X | Y | Square | Tap: Toggle Menu. Hold: Windowing mode (Focus/Move/Resize windows) + ImGuiKey_GamepadFaceLeft, // X | Y | Square | Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows) ImGuiKey_GamepadFaceRight, // B | A | Circle | Cancel / Close / Exit - ImGuiKey_GamepadFaceUp, // Y | X | Triangle | - ImGuiKey_GamepadFaceDown, // A | B | Cross | Activate / Open / Toggle / Tweak. Hold for 0.60f to Activate in Text Input mode (e.g. wired to an on-screen keyboard). + ImGuiKey_GamepadFaceUp, // Y | X | Triangle | Open Context Menu + ImGuiKey_GamepadFaceDown, // A | B | Cross | Activate / Open / Toggle. Hold for 0.60f to Activate in Text Input mode (e.g. wired to an on-screen keyboard). ImGuiKey_GamepadDpadLeft, // D-pad Left | " | " | Move / Tweak / Resize Window (in Windowing mode) ImGuiKey_GamepadDpadRight, // D-pad Right | " | " | Move / Tweak / Resize Window (in Windowing mode) ImGuiKey_GamepadDpadUp, // D-pad Up | " | " | Move / Tweak / Resize Window (in Windowing mode) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e8a43924..738096c5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8731,7 +8731,7 @@ void ImGui::ShowUserGuide() BulletText("Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo."); BulletText("Escape to revert."); Unindent(); - BulletText("With keyboard navigation enabled:"); + BulletText("With Keyboard controls enabled:"); Indent(); BulletText("Arrow keys or Home/End/PageUp/PageDown to navigate."); BulletText("Space to activate a widget."); @@ -8740,6 +8740,15 @@ void ImGui::ShowUserGuide() BulletText("Alt to jump to the menu layer of a window."); BulletText("Menu or Shift+F10 to open a context menu."); Unindent(); + BulletText("With Gamepad controls enabled:"); + Indent(); + BulletText("D-Pad: Navigate / Tweak / Resize (in Windowing mode)."); + BulletText("%s Face button: Activate / Open / Toggle. Hold: activate with text input.", io.ConfigNavSwapGamepadButtons ? "East" : "South"); + BulletText("%s Face button: Cancel / Close / Exit.", io.ConfigNavSwapGamepadButtons ? "South" : "East"); + BulletText("West Face button: Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows)."); + BulletText("North Face button: Open Context Menu."); + BulletText("L1/R1: Tweak Slower/Faster, Focus Previous/Next (in Windowing Mode)."); + Unindent(); } //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index e9eded33..cb5e4e1c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1508,8 +1508,8 @@ typedef ImBitArray ImBitAr #define ImGuiKey_NavGamepadTweakFast ImGuiKey_GamepadR1 #define ImGuiKey_NavGamepadActivate (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceRight : ImGuiKey_GamepadFaceDown) #define ImGuiKey_NavGamepadCancel (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceDown : ImGuiKey_GamepadFaceRight) -#define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft -//#define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp +#define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft // Toggle menu layer. Hold to enable Windowing. +#define ImGuiKey_NavGamepadContextMenu ImGuiKey_GamepadFaceUp // Open context menu (same as Shift+F10) enum ImGuiInputEventType { From d02c645e3847f3b3d44dfe99ed4dfc526e0e4291 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 13 Mar 2026 15:07:26 +0100 Subject: [PATCH 3/3] Nav: short Gamepad Activation press on InputText() always activate with Text Input mode. --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index acc3965a..0cff4129 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -100,6 +100,7 @@ Other Changes: - Now: long press (hold) Activate button (PS4/PS5 cross, Switch B, Xbox A) for ~0.60 secs. This is rarely used, somehow easier to discover, and frees a button for other uses. See updated Gamepad Control Sheets: https://www.dearimgui.com/controls_sheets + - Short Gamepad Activation press on InputText() always activate with Text Input mode. - Popups: Shift+F10 or Menu key can now open popups menus when using BeginPopupContextItem(), BeginPopupContextWindow() or OpenPopupOnItemClick(). (#8803, #9270) [@exelix11, @ocornut] diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6552dc97..7395fad6 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4760,7 +4760,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_wordwrap) wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize)); - const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard))); + const bool input_requested_by_nav = (g.ActiveId != id) && (g.NavActivateId == id); const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive const bool user_clicked = hovered && io.MouseClicked[0]; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);