From 4e2652bd4b4ab17a8970ec9dffa5359e6d167d2f Mon Sep 17 00:00:00 2001 From: Bashar Astifan Date: Thu, 24 Aug 2023 06:33:36 +0400 Subject: [PATCH] OSK support final update - Renamed few functions to avoid confusion - Now UI will report text (gotfocus, lostfocus), popup_closed to the frontend - Both cases (DPad, Keyboard) covered --- Common/UI/UIScreen.cpp | 4 +- Common/UI/View.cpp | 7 +-- UI/NativeApp.cpp | 2 +- UWP/PPSSPP_UWPMain.cpp | 40 ++++++------- UWP/UWPHelpers/InputHelpers.cpp | 103 +++++++++++++++++++++++--------- UWP/UWPHelpers/InputHelpers.h | 18 ++++-- 6 files changed, 110 insertions(+), 64 deletions(-) diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 9d88db22ad..86b7a34c30 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -391,8 +391,8 @@ void PopupScreen::TriggerFinish(DialogResult result) { OnCompleted(result); } #if PPSSPP_PLATFORM(UWP) - // Inform UI to hide OSK and to disable keyboard mode - System_NotifyUIState("hide_keyboard"); + // Inform UI that popup close to hide OSK (if visible) + System_NotifyUIState("popup_closed"); #endif } diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index f2e5475fb6..d62ab6c246 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -1086,13 +1086,10 @@ TextEdit::TextEdit(const std::string &text, const std::string &title, const std: void TextEdit::FocusChanged(int focusFlags) { #if PPSSPP_PLATFORM(UWP) if (focusFlags == FF_GOTFOCUS) { - System_NotifyUIState("show_keyboard"); + System_NotifyUIState("text_gotfocus"); } else { - // OSK will be hidden by `Back` button and no reason to check focus - if (!isInputPaneVisible()) { - System_NotifyUIState("hide_keyboard"); - } + System_NotifyUIState("text_lostfocus"); } #endif } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 3848b9990b..7675e761c2 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1271,7 +1271,7 @@ bool NativeKey(const KeyInput &key) { } #if PPSSPP_PLATFORM(UWP) - // Ignore if key sent from OnKeyDown/OnKeyUp/XInput while keyboard mode active + // Ignore if key sent from OnKeyDown/OnKeyUp/XInput while text edit active // it's already handled by `OnCharacterReceived` if (IgnoreInput(key.keyCode) && !(key.flags & KEY_CHAR)) { return false; diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 1dcd22d5fe..7bca24ecfa 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -192,6 +192,9 @@ void PPSSPP_UWPMain::OnDeviceRestored() { void PPSSPP_UWPMain::OnKeyDown(int scanCode, Windows::System::VirtualKey virtualKey, int repeatCount) { // TODO: Look like (Ctrl, Alt, Shift) don't trigger this event + bool isDPad = (int)virtualKey >= 195 && (int)virtualKey <= 218; // DPad buttons range + DPadInputState(isDPad); + auto iter = virtualKeyCodeToNKCode.find(virtualKey); if (iter != virtualKeyCodeToNKCode.end()) { KeyInput key{}; @@ -216,7 +219,7 @@ void PPSSPP_UWPMain::OnKeyUp(int scanCode, Windows::System::VirtualKey virtualKe void PPSSPP_UWPMain::OnCharacterReceived(int scanCode, unsigned int keyCode) { // This event triggered only in chars case, (Arrows, Delete..etc don't call it) // TODO: Add ` && !IsCtrlOnHold()` once it's ready and implemented - if (isKeyboardActive()) { + if (isTextEditActive()) { KeyInput key{}; key.deviceId = DEVICE_ID_KEYBOARD; key.keyCode = (InputKeyCode)keyCode; @@ -426,7 +429,7 @@ bool System_GetPropertyBool(SystemProperty prop) { // Do actual check // touch devices has input pane, we need to depend on it // I don't know any possible way to display input dialog in non-xaml apps - return isKeybaordAvailable() || isTouchAvailable(); + return isKeyboardAvailable() || isTouchAvailable(); } default: return false; @@ -536,26 +539,19 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string } case SystemRequestType::NOTIFY_UI_STATE: { - if (!param1.empty() && !strcmp(param1.c_str(), "menu")) { - CloseLaunchItem(); - } - else if (!strcmp(param1.c_str(), "show_keyboard")) { - // Must be performed from UI thread - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - CoreDispatcherPriority::Normal, - ref new Windows::UI::Core::DispatchedHandler([]() - { - ActivateKeyboardInput(); - })); - } - else if (!strcmp(param1.c_str(), "hide_keyboard")) { - // Must be performed from UI thread - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - CoreDispatcherPriority::Normal, - ref new Windows::UI::Core::DispatchedHandler([]() - { - DeactivateKeyboardInput(); - })); + if (!param1.empty()) { + if (!strcmp(param1.c_str(), "menu")) { + CloseLaunchItem(); + } + else if (!strcmp(param1.c_str(), "popup_closed")) { + DeactivateTextEditInput(); + } + else if (!strcmp(param1.c_str(), "text_gotfocus")) { + ActivateTextEditInput(true); + } + else if (!strcmp(param1.c_str(), "text_lostfocus")) { + DeactivateTextEditInput(true); + } } return true; } diff --git a/UWP/UWPHelpers/InputHelpers.cpp b/UWP/UWPHelpers/InputHelpers.cpp index 3ccc42f667..c9d2b24526 100644 --- a/UWP/UWPHelpers/InputHelpers.cpp +++ b/UWP/UWPHelpers/InputHelpers.cpp @@ -41,9 +41,8 @@ bool findInList(std::list& inputList, T& str) { }; #pragma endregion -#pragma region Input Keyboard - -bool isKeybaordAvailable() { +#pragma region Input Devices +bool isKeyboardAvailable() { Windows::Devices::Input::KeyboardCapabilities^ keyboardCapabilities = ref new Windows::Devices::Input::KeyboardCapabilities(); bool hasKeyboard = keyboardCapabilities->KeyboardPresent != 0; return hasKeyboard; @@ -54,8 +53,12 @@ bool isTouchAvailable() { bool hasTouch = touchCapabilities->TouchPresent != 0; return hasTouch; } +#pragma endregion -bool keyboardActive = false; +#pragma region Input Keyboard + +bool dPadInputActive = false; +bool textEditActive = false; bool inputPaneVisible = false; Platform::Agile inputPane = nullptr; @@ -72,45 +75,87 @@ void PrepareInputPane() { inputPane->Hiding += ref new Windows::Foundation::TypedEventHandler(&OnHiding); } +// Show input pane (OSK) +bool ShowInputPane() { + return !isInputPaneVisible() ? inputPane->TryShow() : true; +} +// Hide input pane (OSK) +bool HideInputPane() { + return isInputPaneVisible() ? inputPane->TryHide() : true; +} + +// Check if input pane (OSK) visible bool isInputPaneVisible() { return inputPaneVisible; } -bool isKeyboardActive() { - return keyboardActive; +// Check if text edit active (got focus) +bool isTextEditActive() { + return textEditActive; } -void ActivateKeyboardInput() { - DEBUG_LOG(COMMON, "Activate input keyboard"); - if (inputPane->TryShow()) { - DEBUG_LOG(COMMON, "Input pane: TryShow accepted"); - } - else { - DEBUG_LOG(COMMON, "Input pane: (TryShow is not accepted or pane is not supported)"); - - } - keyboardActive = true; +// Set if the current input is DPad +void DPadInputState(bool inputState) { + dPadInputActive = inputState; } -void DeactivateKeyboardInput() { - DEBUG_LOG(COMMON, "Deactivate input keyboard"); - if (inputPane->TryHide()) { - DEBUG_LOG(COMMON, "Input pane: TryHide accepted"); - } - else { - DEBUG_LOG(COMMON, "Input pane: TryHide is not accepted, or pane is not visible"); - } - keyboardActive = false; +// Check if the current input is DPad +bool isDPadActive() { + return dPadInputActive; +} + +void ActivateTextEditInput(bool byFocus) { + // Must be performed from UI thread + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::Normal, + ref new Windows::UI::Core::DispatchedHandler([=]() + { + if (byFocus) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + if (!isInputPaneVisible() && (isDPadActive() || !IsXBox())) { + if (ShowInputPane()) { + DEBUG_LOG(COMMON, "Input pane: TryShow accepted"); + } + else { + DEBUG_LOG(COMMON, "Input pane: (TryShow is not accepted or not supported)"); + } + } + DEBUG_LOG(COMMON, "Text edit active"); + textEditActive = true; + })); +} + +void DeactivateTextEditInput(bool byFocus) { + // Must be performed from UI thread + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::Normal, + ref new Windows::UI::Core::DispatchedHandler([=]() + { + if (isInputPaneVisible()) { + if (HideInputPane()) { + DEBUG_LOG(COMMON, "Input pane: TryHide accepted"); + } + else { + DEBUG_LOG(COMMON, "Input pane: TryHide is not accepted, or not supported"); + } + } + if (isTextEditActive()) { + DEBUG_LOG(COMMON, "Text edit inactive"); + textEditActive = false; + } + })); } bool IgnoreInput(int keyCode) { - // When keyboard mode active and char is passed this function return 'true' + // When text edit active and char is passed this function return 'true' // it will help to prevent KeyDown from sending the same code again bool ignoreInput = false; // TODO: Add ` && !IsCtrlOnHold()` once it's ready and implemented - if (isKeyboardActive()) { + if (isTextEditActive()) { // To avoid bothering KeyDown to check this case always - // we don't get here unless keyboard mode is active + // we don't get here unless text edit is active std::list nonCharList = { NKCODE_CTRL_LEFT, NKCODE_CTRL_RIGHT, @@ -127,7 +172,6 @@ bool IgnoreInput(int keyCode) { NKCODE_EXT_MOUSEBUTTON_3, NKCODE_EXT_MOUSEBUTTON_4, NKCODE_EXT_MOUSEBUTTON_5, - NKCODE_ESCAPE }; if (!isInputPaneVisible()) { // Keyboard active but no on-screen keyboard @@ -137,6 +181,7 @@ bool IgnoreInput(int keyCode) { nonCharList.push_back(NKCODE_DPAD_LEFT); nonCharList.push_back(NKCODE_DPAD_RIGHT); nonCharList.push_back(NKCODE_BACK); + nonCharList.push_back(NKCODE_ESCAPE); } ignoreInput = !findInList(nonCharList, keyCode); diff --git a/UWP/UWPHelpers/InputHelpers.h b/UWP/UWPHelpers/InputHelpers.h index fdaccc9308..d5656e81fc 100644 --- a/UWP/UWPHelpers/InputHelpers.h +++ b/UWP/UWPHelpers/InputHelpers.h @@ -19,17 +19,25 @@ #include // Input Devices -bool isKeybaordAvailable(); +bool isKeyboardAvailable(); bool isTouchAvailable(); -// Input Keyboard/Pane +// Input Pane void PrepareInputPane(); bool isInputPaneVisible(); -bool isKeyboardActive(); -void ActivateKeyboardInput(); -void DeactivateKeyboardInput(); +bool ShowInputPane(); +bool HideInputPane(); + +// Text Edit +bool isTextEditActive(); +void ActivateTextEditInput(bool byFocus = false); +void DeactivateTextEditInput(bool byFocus = false); bool IgnoreInput(int keyCode); +// Input DPad +void DPadInputState(bool inputState); +bool isDPadActive(); + // Keys Status bool IsCapsLockOn(); bool IsShiftOnHold();