Removed io.NavInputs[] and ImGuiNavInput enum. Kept inline redirection code. (#4921, #4858, #787, #1599, #323)

This commit is contained in:
ocornut
2022-07-08 16:02:07 +02:00
parent a7a25ee19d
commit 8b8a61bdf9
6 changed files with 250 additions and 242 deletions

269
imgui.cpp
View File

@ -132,7 +132,7 @@ CODE
- ESCAPE to revert text to its original value.
- Controls are automatically adjusted for OSX to match standard OSX text editing operations.
- General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
- General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets
- General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. Download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets
PROGRAMMER GUIDE
@ -345,12 +345,10 @@ CODE
USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
------------------------------------------
- The gamepad/keyboard navigation is fairly functional and keeps being improved.
- Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
- You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
- Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
- The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
- Keyboard:
- Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
- Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() calls.
- When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard),
the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from:
- io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
@ -362,8 +360,7 @@ CODE
- Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
- Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() + io.AddKeyAnalogEvent() calls.
- BEFORE 1.87, BACKENDS USED TO WRITE DIRECTLY TO io.NavInputs[]. This is going to be obsoleted in the future. Please call io functions instead!
- BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead!
- You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
- If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
@ -387,6 +384,12 @@ CODE
When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
- 2022/07/08 (1.88) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
- Official backends from 1.87+ -> no issue.
- Official backends from 1.60 to 1.86 -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
- Custom backends not writing to io.NavInputs[] -> no issue.
- Custom backends writing to io.NavInputs[] -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
- TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
- 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
- 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary.
- 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO.
@ -1171,7 +1174,6 @@ ImGuiIO::ImGuiIO()
MouseDragThreshold = 6.0f;
for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
AppAcceptingEvents = true;
BackendUsingLegacyKeyArrays = (ImS8)-1;
BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong
@ -1262,8 +1264,6 @@ void ImGuiIO::ClearInputKeys()
}
KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
KeyMods = ImGuiModFlags_None;
for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++)
NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f;
}
// Queue a new key down/up event.
@ -3425,8 +3425,10 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
// (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
g.ActiveIdUsingMouseWheel = false;
g.ActiveIdUsingNavDirMask = 0x00;
g.ActiveIdUsingNavInputMask = 0x00;
g.ActiveIdUsingKeyInputMask.ClearAllBits();
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
g.ActiveIdUsingNavInputMask = 0x00;
#endif
}
void ImGui::ClearActiveID()
@ -4005,6 +4007,31 @@ static void ImGui::UpdateKeyboardInputs()
io.KeysData[ImGuiKey_ModSuper].Down = io.KeySuper;
}
}
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active)
{
#define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0)
#define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0)
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown);
MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow);
MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp);
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown);
#undef NAV_MAP_KEY
}
#endif
#endif
// Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
@ -4384,7 +4411,6 @@ void ImGui::NewFrame()
if (g.ActiveId == 0)
{
g.ActiveIdUsingNavDirMask = 0x00;
g.ActiveIdUsingNavInputMask = 0x00;
g.ActiveIdUsingKeyInputMask.ClearAllBits();
}
@ -4902,7 +4928,6 @@ void ImGui::EndFrame()
// Clear Input data for next frame
g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
g.IO.InputQueueCharacters.resize(0);
memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
}
@ -5175,7 +5200,6 @@ void ImGui::SetActiveIdUsingNavAndKeys()
ImGuiContext& g = *GImGui;
IM_ASSERT(g.ActiveId != 0);
g.ActiveIdUsingNavDirMask = ~(ImU32)0;
g.ActiveIdUsingNavInputMask = ~(ImU32)0;
g.ActiveIdUsingKeyInputMask.SetAllBits();
NavMoveRequestCancel();
}
@ -5719,9 +5743,9 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
{
ImVec2 nav_resize_dir;
if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
nav_resize_dir = ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow));
nav_resize_dir = GetKeyVector2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
if (g.NavInputSource == ImGuiInputSource_Gamepad)
nav_resize_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_Down);
nav_resize_dir = GetKeyVector2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);
if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
{
const float NAV_RESIZE_SPEED = 600.0f;
@ -6609,7 +6633,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
/*
//if (g.NavWindow == window && g.ActiveId == 0)
if (g.ActiveId == window->MoveId)
if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
if (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_C))
LogToClipboard();
*/
@ -7668,6 +7692,19 @@ int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, flo
return count;
}
void ImGui::GetTypematicRepeatRate(ImGuiInputReadFlags flags, float* repeat_delay, float* repeat_rate)
{
ImGuiContext& g = *GImGui;
switch (flags & ImGuiInputReadFlags_RepeatRateMask_)
{
case ImGuiInputReadFlags_RepeatRateNavMove: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
case ImGuiInputReadFlags_RepeatRateNavTweak: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
case ImGuiInputReadFlags_RepeatRateDefault: default: *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
}
}
// Return value representing the number of presses in the last time period, for the given repeat rate
// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
{
ImGuiContext& g = *GImGui;
@ -7676,6 +7713,14 @@ int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_ra
return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
}
// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
ImVec2 ImGui::GetKeyVector2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
{
return ImVec2(
GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,
GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
}
// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
bool ImGui::IsKeyDown(ImGuiKey key)
@ -7688,12 +7733,26 @@ bool ImGui::IsKeyDown(ImGuiKey key)
bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
{
ImGuiContext& g = *GImGui;
return IsKeyPressedEx(key, repeat ? ImGuiInputReadFlags_Repeat : ImGuiInputReadFlags_None);
}
// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
// [Internal] 2022/07: Do not call this directly! It is a temporary entry point which we will soon replace with an overload for IsKeyPressed() when we introduce key ownership.
bool ImGui::IsKeyPressedEx(ImGuiKey key, ImGuiInputReadFlags flags)
{
const ImGuiKeyData* key_data = GetKeyData(key);
const float t = key_data->DownDuration;
if (t < 0.0f)
return false;
const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && GetKeyPressedAmount(key, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
bool pressed = (t == 0.0f);
if (!pressed && ((flags & ImGuiInputReadFlags_Repeat) != 0))
{
float repeat_delay, repeat_rate;
GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
}
if (!pressed)
return false;
return true;
@ -10119,55 +10178,27 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
}
}
const char* ImGui::GetNavInputName(ImGuiNavInput n)
{
static const char* names[] =
{
"Activate", "Cancel", "Input", "Menu", "DpadLeft", "DpadRight", "DpadUp", "DpadDown", "LStickLeft", "LStickRight", "LStickUp", "LStickDown",
"FocusPrev", "FocusNext", "TweakSlow", "TweakFast", "KeyLeft", "KeyRight", "KeyUp", "KeyDown"
};
IM_ASSERT(IM_ARRAYSIZE(names) == ImGuiNavInput_COUNT);
IM_ASSERT(n >= 0 && n < ImGuiNavInput_COUNT);
return names[n];
}
float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode)
float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
{
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
if (mode == ImGuiNavReadMode_Down) // Instant, read analog input (0.0f..1.0f, as provided by user)
return io.NavInputs[n];
const float t = io.NavInputsDownDuration[n];
if (t < 0.0f)
return 0.0f;
switch (mode)
{
case ImGuiNavReadMode_Repeat:
return (float)CalcTypematicRepeatAmount(t - io.DeltaTime, t, io.KeyRepeatDelay * 0.72f, io.KeyRepeatRate * 0.80f);
case ImGuiNavReadMode_RepeatSlow:
return (float)CalcTypematicRepeatAmount(t - io.DeltaTime, t, io.KeyRepeatDelay * 1.25f, io.KeyRepeatRate * 2.00f);
case ImGuiNavReadMode_RepeatFast:
return (float)CalcTypematicRepeatAmount(t - io.DeltaTime, t, io.KeyRepeatDelay * 0.72f, io.KeyRepeatRate * 0.30f);
default:
IM_ASSERT(0);
return 0.0f;
}
}
float repeat_delay, repeat_rate;
GetTypematicRepeatRate(ImGuiInputReadFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);
ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiNavReadMode mode, float slow_factor, float fast_factor)
{
ImVec2 delta(0.0f, 0.0f);
if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) // Number of presses during the frame, according to repeat rate
delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
delta *= slow_factor;
if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
delta *= fast_factor;
return delta;
ImGuiKey key_less, key_more;
if (g.NavInputSource == ImGuiInputSource_Gamepad)
{
key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
}
else
{
key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
}
float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);
if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase
amount = 0.0f;
return amount;
}
static void ImGui::NavUpdate()
@ -10178,54 +10209,20 @@ static void ImGui::NavUpdate()
io.WantSetMousePos = false;
//if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
// Update Gamepad->Nav inputs mapping
// Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
// Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
// FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
if (nav_gamepad_active && g.IO.BackendUsingLegacyNavInputArray == false)
{
for (int n = 0; n < ImGuiNavInput_COUNT; n++)
IM_ASSERT(io.NavInputs[n] == 0.0f && "Backend needs to either only use io.AddKeyEvent()/io.AddKeyAnalogEvent(), either only fill legacy io.NavInputs[]. Not both!");
#define NAV_MAP_KEY(_KEY, _NAV_INPUT, _ACTIVATE_NAV) do { io.NavInputs[_NAV_INPUT] = io.KeysData[_KEY - ImGuiKey_KeysData_OFFSET].AnalogValue; if (_ACTIVATE_NAV && io.NavInputs[_NAV_INPUT] > 0.0f) { g.NavInputSource = ImGuiInputSource_Gamepad; } } while (0)
NAV_MAP_KEY(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate, true);
NAV_MAP_KEY(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel, true);
NAV_MAP_KEY(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu, true);
NAV_MAP_KEY(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input, true);
NAV_MAP_KEY(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft, true);
NAV_MAP_KEY(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight, true);
NAV_MAP_KEY(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp, true);
NAV_MAP_KEY(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown, true);
NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, false);
NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, false);
NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_TweakSlow, false);
NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_TweakFast, false);
NAV_MAP_KEY(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft, false);
NAV_MAP_KEY(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight, false);
NAV_MAP_KEY(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp, false);
NAV_MAP_KEY(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown, false);
#undef NAV_MAP_KEY
}
// Update Keyboard->Nav inputs mapping
const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
if (nav_gamepad_active)
for (ImGuiKey key : nav_gamepad_keys_to_change_source)
if (IsKeyDown(key))
g.NavInputSource = ImGuiInputSource_Gamepad;
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
if (nav_keyboard_active)
{
#define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(_KEY)) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0)
NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
if (io.KeyCtrl)
io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
if (io.KeyShift)
io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
#undef NAV_MAP_KEY
}
memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
for (ImGuiKey key : nav_keyboard_keys_to_change_source)
if (IsKeyDown(key))
g.NavInputSource = ImGuiInputSource_Keyboard;
// Process navigation init request (select first/default focus)
if (g.NavInitResultId != 0)
@ -10270,10 +10267,10 @@ static void ImGui::NavUpdate()
g.NavActivateFlags = ImGuiActivateFlags_None;
if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
{
bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
bool input_down = IsNavInputDown(ImGuiNavInput_Input);
bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate);
bool input_pressed = input_down && IsNavInputPressed(ImGuiNavInput_Input);
const bool activate_down = IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_NavGamepadActivate);
const bool activate_pressed = activate_down && (IsKeyPressed(ImGuiKey_Space, false) || IsKeyPressed(ImGuiKey_NavGamepadActivate, false));
const bool input_down = IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadInput);
const bool input_pressed = input_down && (IsKeyPressed(ImGuiKey_Enter, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false));
if (g.ActiveId == 0 && activate_pressed)
{
g.NavActivateId = g.NavId;
@ -10330,11 +10327,12 @@ static void ImGui::NavUpdate()
// *Normal* Manual scroll with NavScrollXXX keys
// Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiNavReadMode_Down, 1.0f / 10.0f, 10.0f);
const ImVec2 scroll_dir = GetKeyVector2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
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)
SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
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));
SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
}
// Always prioritize mouse highlight if navigation is disabled
@ -10403,11 +10401,11 @@ void ImGui::NavUpdateCreateMoveRequest()
g.NavMoveScrollFlags = ImGuiScrollFlags_None;
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
{
const ImGuiNavReadMode read_mode = ImGuiNavReadMode_Repeat;
if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
const ImGuiInputReadFlags repeat_mode = ImGuiInputReadFlags_Repeat | ImGuiInputReadFlags_RepeatRateNavMove;
if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsKeyPressedEx(ImGuiKey_GamepadDpadLeft, repeat_mode) || 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_Up) && (IsKeyPressedEx(ImGuiKey_GamepadDpadUp, repeat_mode) || 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; }
}
g.NavMoveClipDir = g.NavMoveDir;
g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
@ -10613,13 +10611,22 @@ void ImGui::NavMoveRequestApplyResult()
static void ImGui::NavUpdateCancelRequest()
{
ImGuiContext& g = *GImGui;
if (!IsNavInputPressed(ImGuiNavInput_Cancel))
if (!IsKeyPressed(ImGuiKey_Escape, false) && !IsKeyPressed(ImGuiKey_NavGamepadCancel, false))
return;
IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
// If your custom widget code used: { g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); }
// Since IMGUI_VERSION_NUM >= 18804 it should be: { SetActiveIdUsingKey(ImGuiKey_Escape); SetActiveIdUsingKey(ImGuiKey_NavGamepadCancel); }
if (g.ActiveIdUsingNavInputMask & (1 << ImGuiNavInput_Cancel))
SetActiveIdUsingKey(ImGuiKey_Escape);
if (g.ActiveIdUsingNavInputMask & ~(1 << ImGuiNavInput_Cancel))
IM_ASSERT(0); // Other values unsupported
#endif
IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
if (g.ActiveId != 0)
{
if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
if (!IsActiveIdUsingKey(ImGuiKey_Escape) && !IsActiveIdUsingKey(ImGuiKey_NavGamepadCancel))
ClearActiveID();
}
else if (g.NavLayer != ImGuiNavLayer_Main)
@ -10865,8 +10872,8 @@ static void ImGui::NavUpdateWindowing()
}
// Start CTRL+Tab or Square+L/R window selection
const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu);
const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab);
const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, false);
const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab, false);
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
{
@ -10885,7 +10892,7 @@ static void ImGui::NavUpdateWindowing()
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
// Select window to focus
const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiNavReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiNavReadMode_RepeatSlow);
const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
if (focus_change_dir != 0)
{
NavUpdateWindowingHighlightWindow(focus_change_dir);
@ -10893,7 +10900,7 @@ static void ImGui::NavUpdateWindowing()
}
// Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
if (!IsNavInputDown(ImGuiNavInput_Menu))
if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
{
g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
if (g.NavWindowingToggleLayer && g.NavWindow)
@ -10946,9 +10953,9 @@ static void ImGui::NavUpdateWindowing()
{
ImVec2 nav_move_dir;
if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
nav_move_dir = ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow));
nav_move_dir = GetKeyVector2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
if (g.NavInputSource == ImGuiInputSource_Gamepad)
nav_move_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiNavReadMode_Down);
nav_move_dir = GetKeyVector2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
{
const float NAV_MOVE_SPEED = 800.0f;
@ -12667,7 +12674,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
int active_id_using_key_input_count = 0;
for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask[n] ? 1 : 0;
Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, active_id_using_key_input_count);
Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, active_id_using_key_input_count);
Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
Unindent();