Nav: fixes keyboard/gamepad nav actions running without the corresponding config flags (#5504). Fixes 8b8a61b. #4921, #4858, #787, #1599, #323)

NavUpdate() can now clears ImGuiInputSource_None.
This commit is contained in:
ocornut 2022-07-25 17:16:01 +02:00
parent 2d38bc99b3
commit e99c4fc668
6 changed files with 39 additions and 27 deletions

View File

@ -582,7 +582,7 @@ static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0
static void ImGui_ImplGlfw_UpdateGamepads() static void ImGui_ImplGlfw_UpdateGamepads()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
return; return;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;

View File

@ -517,7 +517,7 @@ static void ImGui_ImplOSX_UpdateGamepads()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs)); memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
return; return;
#if APPLE_HAS_CONTROLLER #if APPLE_HAS_CONTROLLER

View File

@ -481,7 +481,7 @@ static void ImGui_ImplSDL2_UpdateMouseCursor()
static void ImGui_ImplSDL2_UpdateGamepads() static void ImGui_ImplSDL2_UpdateGamepads()
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
return; return;
// Get gamepad // Get gamepad

View File

@ -277,8 +277,8 @@ static void ImGui_ImplWin32_UpdateGamepads()
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) //if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
return; // return;
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.

View File

@ -10266,6 +10266,10 @@ static void ImGui::NavUpdate()
for (ImGuiKey key : nav_keyboard_keys_to_change_source) for (ImGuiKey key : nav_keyboard_keys_to_change_source)
if (IsKeyDown(key)) if (IsKeyDown(key))
g.NavInputSource = ImGuiInputSource_Keyboard; g.NavInputSource = ImGuiInputSource_Keyboard;
if (!nav_gamepad_active && g.NavInputSource == ImGuiInputSource_Gamepad)
g.NavInputSource = ImGuiInputSource_None;
if (!nav_keyboard_active && g.NavInputSource == ImGuiInputSource_Keyboard)
g.NavInputSource = ImGuiInputSource_None;
// Process navigation init request (select first/default focus) // Process navigation init request (select first/default focus)
if (g.NavInitResultId != 0) if (g.NavInitResultId != 0)
@ -10310,10 +10314,10 @@ static void ImGui::NavUpdate()
g.NavActivateFlags = ImGuiActivateFlags_None; g.NavActivateFlags = ImGuiActivateFlags_None;
if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
{ {
const bool activate_down = IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_NavGamepadActivate); const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate));
const bool activate_pressed = activate_down && (IsKeyPressed(ImGuiKey_Space, false) || IsKeyPressed(ImGuiKey_NavGamepadActivate, false)); const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false)));
const bool input_down = IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadInput); const bool input_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Enter)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput));
const bool input_pressed = input_down && (IsKeyPressed(ImGuiKey_Enter, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false)); const bool input_pressed = input_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Enter, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, false)));
if (g.ActiveId == 0 && activate_pressed) if (g.ActiveId == 0 && activate_pressed)
{ {
g.NavActivateId = g.NavId; g.NavActivateId = g.NavId;
@ -10368,14 +10372,17 @@ static void ImGui::NavUpdate()
SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
} }
// *Normal* Manual scroll with NavScrollXXX keys // *Normal* Manual scroll with LStick
// Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
const ImVec2 scroll_dir = GetKeyVector2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); if (nav_gamepad_active)
const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f; {
if (scroll_dir.x != 0.0f && window->ScrollbarX) const ImVec2 scroll_dir = GetKeyVector2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor)); const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
if (scroll_dir.y != 0.0f) if (scroll_dir.x != 0.0f && window->ScrollbarX)
SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor)); SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
if (scroll_dir.y != 0.0f)
SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
}
} }
// Always prioritize mouse highlight if navigation is disabled // Always prioritize mouse highlight if navigation is disabled
@ -10427,6 +10434,8 @@ void ImGui::NavUpdateCreateMoveRequest()
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO; ImGuiIO& io = g.IO;
ImGuiWindow* window = g.NavWindow; ImGuiWindow* window = g.NavWindow;
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
if (g.NavMoveForwardToNextFrame && window != NULL) if (g.NavMoveForwardToNextFrame && window != NULL)
{ {
@ -10445,10 +10454,10 @@ void ImGui::NavUpdateCreateMoveRequest()
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
{ {
const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateNavMove; const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateNavMove;
if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsKeyPressedEx(ImGuiKey_GamepadDpadLeft, repeat_mode) || IsKeyPressedEx(ImGuiKey_LeftArrow, repeat_mode))) { g.NavMoveDir = ImGuiDir_Left; } if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressedEx(ImGuiKey_GamepadDpadLeft, repeat_mode)) || (nav_keyboard_active && IsKeyPressedEx(ImGuiKey_LeftArrow, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Left; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsKeyPressedEx(ImGuiKey_GamepadDpadRight, repeat_mode) || IsKeyPressedEx(ImGuiKey_RightArrow, repeat_mode))) { g.NavMoveDir = ImGuiDir_Right; } if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressedEx(ImGuiKey_GamepadDpadRight, repeat_mode)) || (nav_keyboard_active && IsKeyPressedEx(ImGuiKey_RightArrow, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Right; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsKeyPressedEx(ImGuiKey_GamepadDpadUp, repeat_mode) || IsKeyPressedEx(ImGuiKey_UpArrow, repeat_mode))) { g.NavMoveDir = ImGuiDir_Up; } if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressedEx(ImGuiKey_GamepadDpadUp, repeat_mode)) || (nav_keyboard_active && IsKeyPressedEx(ImGuiKey_UpArrow, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Up; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsKeyPressedEx(ImGuiKey_GamepadDpadDown, repeat_mode) || IsKeyPressedEx(ImGuiKey_DownArrow, repeat_mode))) { g.NavMoveDir = ImGuiDir_Down; } if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressedEx(ImGuiKey_GamepadDpadDown, repeat_mode)) || (nav_keyboard_active && IsKeyPressedEx(ImGuiKey_DownArrow, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Down; }
} }
g.NavMoveClipDir = g.NavMoveDir; g.NavMoveClipDir = g.NavMoveDir;
g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
@ -10456,7 +10465,6 @@ void ImGui::NavUpdateCreateMoveRequest()
// Update PageUp/PageDown/Home/End scroll // Update PageUp/PageDown/Home/End scroll
// FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
float scoring_rect_offset_y = 0.0f; float scoring_rect_offset_y = 0.0f;
if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
scoring_rect_offset_y = NavUpdatePageUpPageDown(); scoring_rect_offset_y = NavUpdatePageUpPageDown();
@ -10654,7 +10662,9 @@ void ImGui::NavMoveRequestApplyResult()
static void ImGui::NavUpdateCancelRequest() static void ImGui::NavUpdateCancelRequest()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (!IsKeyPressed(ImGuiKey_Escape, false) && !IsKeyPressed(ImGuiKey_NavGamepadCancel, false)) const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, false)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, false)))
return; return;
IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n"); IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
@ -10906,8 +10916,10 @@ static void ImGui::NavUpdateWindowing()
} }
// Start CTRL+Tab or Square+L/R window selection // Start CTRL+Tab or Square+L/R window selection
const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, false); const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab, false); const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, false);
const bool start_windowing_with_keyboard = allow_windowing && nav_keyboard_active && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab, false);
if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (start_windowing_with_gamepad || start_windowing_with_keyboard)
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
{ {
@ -10959,7 +10971,6 @@ static void ImGui::NavUpdateWindowing()
// Keyboard: Press and Release ALT to toggle menu layer // Keyboard: Press and Release ALT to toggle menu layer
// - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer.
// - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
if (nav_keyboard_active && IsKeyPressed(ImGuiKey_ModAlt)) if (nav_keyboard_active && IsKeyPressed(ImGuiKey_ModAlt))
{ {
g.NavWindowingToggleLayer = true; g.NavWindowingToggleLayer = true;

View File

@ -4351,9 +4351,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const bool is_select_all = is_shortcut_key && IsKeyPressed(ImGuiKey_A); const bool is_select_all = is_shortcut_key && IsKeyPressed(ImGuiKey_A);
// We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true); const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true);
const bool is_gamepad_validate = IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false); const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false));
const bool is_cancel = IsKeyPressed(ImGuiKey_Escape, false) || IsKeyPressed(ImGuiKey_NavGamepadCancel, false); const bool is_cancel = IsKeyPressed(ImGuiKey_Escape, false) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, false));
if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }