From f9e8bf6b82ea778a7848bf5c616f4425858d130d Mon Sep 17 00:00:00 2001 From: Maschell Date: Wed, 29 Apr 2026 08:33:18 +0200 Subject: [PATCH] Add "InputRepeating" when holding a navigation button in the config menu --- source/utils/config/CategoryRenderer.cpp | 9 +++- source/utils/config/CategoryRenderer.h | 3 ++ source/utils/config/ConfigRenderer.cpp | 26 +++++++----- source/utils/config/ConfigRenderer.h | 3 ++ source/utils/config/ConfigUtils.cpp | 4 +- source/utils/config/InputRepeater.h | 53 ++++++++++++++++++++++++ 6 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 source/utils/config/InputRepeater.h diff --git a/source/utils/config/CategoryRenderer.cpp b/source/utils/config/CategoryRenderer.cpp index 3d9a41a..606d583 100644 --- a/source/utils/config/CategoryRenderer.cpp +++ b/source/utils/config/CategoryRenderer.cpp @@ -91,9 +91,12 @@ ConfigSubState CategoryRenderer::UpdateStateMain(const Input &input, const WUPSC const int32_t prevSelectedItem = mCursorPos; if (mIsItemMovementAllowed) { - if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) { + uint32_t navMask = Input::eButtons::BUTTON_UP | Input::eButtons::BUTTON_DOWN; + uint32_t actionButton = mNavRepeater.update(input, navMask); + + if (actionButton & Input::eButtons::BUTTON_DOWN) { mCursorPos++; - } else if (input.data.buttons_d & Input::eButtons::BUTTON_UP) { + } else if (actionButton & Input::eButtons::BUTTON_UP) { mCursorPos--; } else if (input.data.buttons_d & Input::eButtons::BUTTON_A) { if (mCursorPos < static_cast(mCat->getCategories().size())) { @@ -107,6 +110,8 @@ ConfigSubState CategoryRenderer::UpdateStateMain(const Input &input, const WUPSC return SUB_STATE_RUNNING; } } + } else { + mNavRepeater.reset(); } if (mCursorPos < 0) { diff --git a/source/utils/config/CategoryRenderer.h b/source/utils/config/CategoryRenderer.h index 5327db1..e4f569e 100644 --- a/source/utils/config/CategoryRenderer.h +++ b/source/utils/config/CategoryRenderer.h @@ -2,6 +2,7 @@ #include "ConfigDefines.h" #include "ConfigDisplayItem.h" +#include "InputRepeater.h" #include @@ -57,4 +58,6 @@ private: bool mFirstFrame = true; bool mIsRoot = false; bool mNeedsRedraw = true; + + InputRepeater mNavRepeater; }; diff --git a/source/utils/config/ConfigRenderer.cpp b/source/utils/config/ConfigRenderer.cpp index 41f2f3c..d949106 100644 --- a/source/utils/config/ConfigRenderer.cpp +++ b/source/utils/config/ConfigRenderer.cpp @@ -103,13 +103,10 @@ void ConfigRenderer::ResetNeedsRedraw() { void ConfigRenderer::RequestRedraw() { mNeedRedraw = true; } - ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) { - if (!mListState) return SUB_STATE_ERROR; - - auto &configs = GetDisplayedConfigList(); - const auto prevSelectedItem = mCursorPos; - auto totalElementSize = (int32_t) configs.size(); + if (!mListState) { + return SUB_STATE_ERROR; + } // Delegate specific inputs to the State bool inputHandled = mListState->HandleInput(*this, input); @@ -122,16 +119,23 @@ ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) { return SUB_STATE_RUNNING; } - // Handle Navigation (Common to all states) - if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) { + const auto prevSelectedItem = mCursorPos; + auto &configs = GetDisplayedConfigList(); + auto totalElementSize = (int32_t) configs.size(); + + uint32_t navMask = Input::eButtons::BUTTON_UP | Input::eButtons::BUTTON_DOWN | + Input::eButtons::BUTTON_LEFT | Input::eButtons::BUTTON_RIGHT; + uint32_t actionButton = mNavRepeater.update(input, navMask); + + if (actionButton & Input::eButtons::BUTTON_DOWN) { mCursorPos++; - } else if (input.data.buttons_d & Input::eButtons::BUTTON_LEFT) { + } else if (actionButton & Input::eButtons::BUTTON_LEFT) { mCursorPos -= MAX_BUTTONS_ON_SCREEN - 1; if (mCursorPos < 0) mCursorPos = 0; - } else if (input.data.buttons_d & Input::eButtons::BUTTON_RIGHT) { + } else if (actionButton & Input::eButtons::BUTTON_RIGHT) { mCursorPos += MAX_BUTTONS_ON_SCREEN - 1; if (mCursorPos >= totalElementSize) mCursorPos = totalElementSize - 1; - } else if (input.data.buttons_d & Input::eButtons::BUTTON_UP) { + } else if (actionButton & Input::eButtons::BUTTON_UP) { mCursorPos--; } diff --git a/source/utils/config/ConfigRenderer.h b/source/utils/config/ConfigRenderer.h index df7f8c7..f1b2b38 100644 --- a/source/utils/config/ConfigRenderer.h +++ b/source/utils/config/ConfigRenderer.h @@ -2,6 +2,7 @@ #include "ConfigDefines.h" #include "ConfigDisplayItem.h" +#include "InputRepeater.h" #include @@ -80,4 +81,6 @@ private: bool mNeedRedraw = true; bool mPluginListDirty = false; bool mLastInputWasOnWiimote = false; + + InputRepeater mNavRepeater; }; \ No newline at end of file diff --git a/source/utils/config/ConfigUtils.cpp b/source/utils/config/ConfigUtils.cpp index 053470e..a437917 100644 --- a/source/utils/config/ConfigUtils.cpp +++ b/source/utils/config/ConfigUtils.cpp @@ -217,8 +217,8 @@ void ConfigUtils::displayMenu() { renderer.ResetNeedsRedraw(); auto diffTime = OSTicksToMicroseconds(OSGetTime() - startTime); - if (diffTime < 16000) { - OSSleepTicks(OSMicrosecondsToTicks(16000 - diffTime)); + if (diffTime < 33000) { + OSSleepTicks(OSMicrosecondsToTicks(33000 - diffTime)); } } diff --git a/source/utils/config/InputRepeater.h b/source/utils/config/InputRepeater.h new file mode 100644 index 0000000..027f3c9 --- /dev/null +++ b/source/utils/config/InputRepeater.h @@ -0,0 +1,53 @@ +#pragma once +#include "utils/input/Input.h" +#include + +class InputRepeater { +public: + // thresholdDelay: frames to wait before starting repeat (e.g., 15) + // repeatRate: frames between repeats (e.g., 6) + explicit InputRepeater(uint32_t thresholdDelay = 15, uint32_t repeatRate = 6) + : mThresholdDelay(thresholdDelay), mRepeatRate(repeatRate) {} + + uint32_t update(const Input &input, uint32_t mask) { + uint32_t currentPressed = input.data.buttons_d & mask; + uint32_t currentHeld = input.data.buttons_h & mask; + + if (currentPressed) { + // New press: reset and return the button immediately + mScrollTimer = 0; + mHeldButton = currentPressed; + mInRepeatMode = false; + return currentPressed; + } + + if (currentHeld && (currentHeld == mHeldButton)) { + mScrollTimer++; + uint32_t threshold = mInRepeatMode ? mRepeatRate : mThresholdDelay; + + if (mScrollTimer >= threshold) { + mScrollTimer = 0; + mInRepeatMode = true; + return currentHeld; + } + } else { + // Button released or changed + reset(); + } + + return 0; // No action this frame + } + + void reset() { + mScrollTimer = 0; + mHeldButton = 0; + mInRepeatMode = false; + } + +private: + uint32_t mThresholdDelay; + uint32_t mRepeatRate; + uint32_t mScrollTimer = 0; + uint32_t mHeldButton = 0; + bool mInRepeatMode = false; +}; \ No newline at end of file