Nav: Fixed moving window with gamepad or keyboard when running at very high framerate + removed ImGuiNavDirSourceFlags_RawKeyboard.

This commit is contained in:
ocornut 2022-07-06 16:06:31 +02:00
parent 82e10f1b61
commit 93f02ee0c6
3 changed files with 54 additions and 32 deletions

View File

@ -41,6 +41,7 @@ Other Changes:
- InputText: added experimental io.ConfigInputTextEnterKeepActive feature to make pressing - InputText: added experimental io.ConfigInputTextEnterKeepActive feature to make pressing
Enter keep the input active and select all text. Enter keep the input active and select all text.
- Nav: Fixed moving/resizing window with gamepad or keyboard when running at very high framerate.
- Misc: io.Framerate moving average now converge in 60 frames instead of 120. (#5236, #4138) - Misc: io.Framerate moving average now converge in 60 frames instead of 120. (#5236, #4138)
- Backends: Metal: Use __bridge for ARC based systems. (#5403) [@stack] - Backends: Metal: Use __bridge for ARC based systems. (#5403) [@stack]
- Backends: Metal: Add dispatch synchronization. (#5447) [@luigifcruz] - Backends: Metal: Add dispatch synchronization. (#5447) [@luigifcruz]

View File

@ -5713,23 +5713,31 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
// Navigation resize (keyboard/gamepad) // Navigation resize (keyboard/gamepad)
// FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
// Not even sure the callback works here.
if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
{ {
ImVec2 nav_resize_delta; ImVec2 nav_resize_dir;
if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiNavReadMode_Down); nav_resize_dir = ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow));
if (g.NavInputSource == ImGuiInputSource_Gamepad) if (g.NavInputSource == ImGuiInputSource_Gamepad)
nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_Down); nav_resize_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_Down);
if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
{ {
const float NAV_RESIZE_SPEED = 600.0f; const float NAV_RESIZE_SPEED = 600.0f;
nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);
nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size); g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, visibility_rect.Min - window->Pos - window->Size); // We need Pos+Size >= visibility_rect.Min, so Size >= visibility_rect.Min - Pos, so size_delta >= visibility_rect.Min - window->Pos - window->Size
g.NavWindowingToggleLayer = false; g.NavWindowingToggleLayer = false;
g.NavDisableMouseHover = true; g.NavDisableMouseHover = true;
resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
// FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. ImVec2 accum_floored = ImFloor(g.NavWindowingAccumDeltaSize);
size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
{
// FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);
g.NavWindowingAccumDeltaSize -= accum_floored;
}
} }
} }
@ -10125,31 +10133,30 @@ const char* ImGui::GetNavInputName(ImGuiNavInput n)
float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (mode == ImGuiNavReadMode_Down) ImGuiIO& io = g.IO;
return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) if (mode == ImGuiNavReadMode_Down) // Instant, read analog input (0.0f..1.0f, as provided by user)
return io.NavInputs[n];
const float t = g.IO.NavInputsDownDuration[n]; const float t = io.NavInputsDownDuration[n];
if (t < 0.0f && mode == ImGuiNavReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. if (t < 0.0f && mode == ImGuiNavReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); return (io.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
if (t < 0.0f) if (t < 0.0f)
return 0.0f; return 0.0f;
if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
return (t == 0.0f) ? 1.0f : 0.0f; return (t == 0.0f) ? 1.0f : 0.0f;
if (mode == ImGuiNavReadMode_Repeat) if (mode == ImGuiNavReadMode_Repeat)
return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f); return (float)CalcTypematicRepeatAmount(t - io.DeltaTime, t, io.KeyRepeatDelay * 0.72f, io.KeyRepeatRate * 0.80f);
if (mode == ImGuiNavReadMode_RepeatSlow) if (mode == ImGuiNavReadMode_RepeatSlow)
return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f); return (float)CalcTypematicRepeatAmount(t - io.DeltaTime, t, io.KeyRepeatDelay * 1.25f, io.KeyRepeatRate * 2.00f);
if (mode == ImGuiNavReadMode_RepeatFast) if (mode == ImGuiNavReadMode_RepeatFast)
return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f); return (float)CalcTypematicRepeatAmount(t - io.DeltaTime, t, io.KeyRepeatDelay * 0.72f, io.KeyRepeatRate * 0.30f);
return 0.0f; return 0.0f;
} }
ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiNavReadMode mode, float slow_factor, float fast_factor) ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiNavReadMode mode, float slow_factor, float fast_factor)
{ {
ImVec2 delta(0.0f, 0.0f); ImVec2 delta(0.0f, 0.0f);
if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard) if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) // Number of presses during the frame, according to repeat rate
delta += ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow));
if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
@ -10825,7 +10832,10 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
if (!window_target) if (!window_target)
window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
if (window_target) // Don't reset windowing target if there's a single window in the list if (window_target) // Don't reset windowing target if there's a single window in the list
{
g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
}
g.NavWindowingToggleLayer = false; g.NavWindowingToggleLayer = false;
} }
@ -10859,8 +10869,9 @@ static void ImGui::NavUpdateWindowing()
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))
{ {
g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
} }
@ -10932,18 +10943,24 @@ static void ImGui::NavUpdateWindowing()
// Move window // Move window
if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
{ {
ImVec2 move_delta; ImVec2 nav_move_dir;
if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiNavReadMode_Down); nav_move_dir = ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow));
if (g.NavInputSource == ImGuiInputSource_Gamepad) if (g.NavInputSource == ImGuiInputSource_Gamepad)
move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiNavReadMode_Down); nav_move_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiNavReadMode_Down);
if (move_delta.x != 0.0f || move_delta.y != 0.0f) if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
{ {
const float NAV_MOVE_SPEED = 800.0f; const float NAV_MOVE_SPEED = 800.0f;
const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
g.NavDisableMouseHover = true; g.NavDisableMouseHover = true;
ImVec2 accum_floored = ImFloor(g.NavWindowingAccumDeltaPos);
if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
{
ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);
g.NavWindowingAccumDeltaPos -= accum_floored;
}
} }
} }

View File

@ -1171,6 +1171,7 @@ struct ImGuiPtrOrIndex
typedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN> ImBitArrayForNamedKeys; typedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN> ImBitArrayForNamedKeys;
// Extend ImGuiKey_
enum ImGuiKeyPrivate_ enum ImGuiKeyPrivate_
{ {
ImGuiKey_LegacyNativeKey_BEGIN = 0, ImGuiKey_LegacyNativeKey_BEGIN = 0,
@ -1308,10 +1309,9 @@ enum ImGuiNavHighlightFlags_
enum ImGuiNavDirSourceFlags_ enum ImGuiNavDirSourceFlags_
{ {
ImGuiNavDirSourceFlags_None = 0, ImGuiNavDirSourceFlags_None = 0,
ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), facilitate use of some functions before we can unify nav and keys ImGuiNavDirSourceFlags_Keyboard = 1 << 0,
ImGuiNavDirSourceFlags_Keyboard = 1 << 1, ImGuiNavDirSourceFlags_PadDPad = 1 << 1,
ImGuiNavDirSourceFlags_PadDPad = 1 << 2, ImGuiNavDirSourceFlags_PadLStick = 1 << 2
ImGuiNavDirSourceFlags_PadLStick = 1 << 3
}; };
enum ImGuiNavMoveFlags_ enum ImGuiNavMoveFlags_
@ -1724,6 +1724,8 @@ struct ImGuiContext
float NavWindowingTimer; float NavWindowingTimer;
float NavWindowingHighlightAlpha; float NavWindowingHighlightAlpha;
bool NavWindowingToggleLayer; bool NavWindowingToggleLayer;
ImVec2 NavWindowingAccumDeltaPos;
ImVec2 NavWindowingAccumDeltaSize;
// Render // Render
float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)
@ -2699,13 +2701,15 @@ namespace ImGui
inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask[key]; } inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask[key]; }
inline void SetActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key); } inline void SetActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key); }
IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; }
inline bool IsNavInputTest(ImGuiNavInput n, ImGuiNavReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); }
IMGUI_API ImGuiModFlags GetMergedModFlags(); IMGUI_API ImGuiModFlags GetMergedModFlags();
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // [removed in 1.87] inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // [removed in 1.87]
#endif #endif
// Inputs: Navigation
inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; }
inline bool IsNavInputTest(ImGuiNavInput n, ImGuiNavReadMode read_mode) { return (GetNavInputAmount(n, read_mode) > 0.0f); }
// Drag and Drop // Drag and Drop
IMGUI_API bool IsDragDropActive(); IMGUI_API bool IsDragDropActive();
IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);