From b0382ed91adfef277f665b32c0cd1ba4bea28f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 19 Oct 2026 11:36:30 +0200 Subject: [PATCH] Game browser: Backspace goes up a directory, Windows raw input flags key repeats With no type-to-search active, Backspace goes to the parent directory. Never on a repeat, so holding it doesn't climb to the root. Not every platform flags repeats, so the browser also tracks whether it's held. Windows raw input now flags auto-repeated key downs as IS_REPEAT, like Android and UWP, and forgets the held keys it releases on losing focus. The UI drops OS repeats of a key it's already repeating itself, so held arrows don't double up (they were dropped before too, a step later). Co-Authored-By: Claude Opus 5.5 (1M context) --- Common/UI/Root.cpp | 12 ++++++++++++ UI/GameBrowser.cpp | 16 ++++++++++++++++ UI/GameBrowser.h | 1 + UI/MiscViews.cpp | 3 +-- Windows/RawInput.cpp | 7 ++++++- 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Common/UI/Root.cpp b/Common/UI/Root.cpp index de7c963749..24eeb93bd4 100644 --- a/Common/UI/Root.cpp +++ b/Common/UI/Root.cpp @@ -257,6 +257,18 @@ static bool KeyToFocusMove(const KeyInput &key, FocusMove *move) { KeyEventResult KeyEventToFocusMoves(const KeyInput &key) { KeyEventResult retval = KeyEventResult::PASS_THROUGH; + // The OS's own repeats of a key we're already repeating (see ProcessHeldKeys) would double + // up with ours, in a text field for example. + if ((key.flags & KeyInputFlags::DOWN) && (key.flags & KeyInputFlags::IS_REPEAT) && !heldKeys.empty()) { + HeldKey hk; + hk.key = key.keyCode; + hk.deviceId = key.deviceId; + hk.triggerTime = 0.0; // irrelevant + hk.move = FocusMove::NEXT; // irrelevant, not part of the comparison + if (heldKeys.find(hk) != heldKeys.end()) { + return KeyEventResult::IGNORE_KEY; + } + } // Ignore repeats for focus moves. if ((key.flags & KeyInputFlags::DOWN) && !(key.flags & KeyInputFlags::IS_REPEAT)) { FocusMove move; diff --git a/UI/GameBrowser.cpp b/UI/GameBrowser.cpp index 7ea5f65cf1..579518a9c1 100644 --- a/UI/GameBrowser.cpp +++ b/UI/GameBrowser.cpp @@ -781,6 +781,22 @@ bool GameBrowser::Key(const KeyInput &input) { } } + // Backspace with no search going also goes to the parent directory, as in file managers. Never on + // a repeat, or holding it would climb to the root (or carry on up after clearing a search). Not + // every platform flags repeats, so this also tracks whether it's held. + if (input.keyCode == NKCODE_DEL && input.deviceId == DEVICE_ID_KEYBOARD) { + if (input.flags & KeyInputFlags::UP) { + backspaceHeld_ = false; + } else if (input.flags & KeyInputFlags::DOWN) { + const bool repeat = backspaceHeld_ || (input.flags & KeyInputFlags::IS_REPEAT); + backspaceHeld_ = true; + if (!repeat && search_.searchFilter.empty() && (browseFlags_ & BrowseFlags::NAVIGATE) && path_.CanNavigateUp()) { + NavigateUp(); + return true; + } + } + } + bool retval = LinearLayout::Key(input); if (retval) { return true; diff --git a/UI/GameBrowser.h b/UI/GameBrowser.h index 02997a8c83..75ad56a646 100644 --- a/UI/GameBrowser.h +++ b/UI/GameBrowser.h @@ -90,6 +90,7 @@ private: Path focusPath_; // Set when entering a directory, for Refresh to focus its first entry. bool focusFirstItem_ = false; + bool backspaceHeld_ = false; bool listingPending_ = false; bool refreshPending_ = false; float lastScale_ = 1.0f; diff --git a/UI/MiscViews.cpp b/UI/MiscViews.cpp index cb44651871..4267749028 100644 --- a/UI/MiscViews.cpp +++ b/UI/MiscViews.cpp @@ -507,9 +507,8 @@ bool ViewSearch::Key(UI::ViewGroup *viewGroup, const KeyInput &input) { // TODO: Restore focus state here. UI::EnableFocusMovement(false); } - } else { - // Empty search filter. Navigate upwards on backspace? } + // With an empty filter, it's up to the owner (the game browser goes up a directory). } else if (!searchFilter.empty() && input.keyCode == NKCODE_ESCAPE) { searchFilter.clear(); ApplySearchFilter(viewGroup, false); diff --git a/Windows/RawInput.cpp b/Windows/RawInput.cpp index e820ca93a1..47a0392633 100644 --- a/Windows/RawInput.cpp +++ b/Windows/RawInput.cpp @@ -264,8 +264,11 @@ namespace WindowsRawInput { key.keyCode = GetTrueVKey(raw->data.keyboard); if (key.keyCode) { + // Windows auto-repeats a held key as more key downs. Flag them, like other platforms do. + if (!keyboardKeysDown.insert(key.keyCode).second) { + key.flags |= KeyInputFlags::IS_REPEAT; + } NativeKey(key); - keyboardKeysDown.insert(key.keyCode); } } else if (raw->data.keyboard.Message == WM_KEYUP || raw->data.keyboard.Message == WM_SYSKEYUP) { // A key released while Alt is held comes as WM_SYSKEYUP. @@ -434,6 +437,8 @@ namespace WindowsRawInput { key.keyCode = *i; NativeKey(key); } + // Or the next press of one would count as a repeat. + keyboardKeysDown.clear(); focused = false; }