diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index 7a65c477ee81..fa5bca187059 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -1329,7 +1329,7 @@ bool TextEdit::Key(const KeyInput &input) { // Process chars. if (input.flags & KEY_CHAR) { - int unichar = input.keyCode; + const int unichar = input.keyCode; if (unichar >= 0x20 && !ctrlDown_) { // Ignore control characters. // Insert it! (todo: do it with a string insert) char buf[8]; diff --git a/GPU/Common/PresentationCommon.h b/GPU/Common/PresentationCommon.h index 7e4877f11d00..2da590dfc588 100644 --- a/GPU/Common/PresentationCommon.h +++ b/GPU/Common/PresentationCommon.h @@ -106,6 +106,7 @@ class PresentationCommon { } void NotifyPresent() { // Something else did the present, skipping PresentationCommon. + // If you haven't called BindFramebufferAsRenderTarget, you must not set this. presentedThisFrame_ = true; } diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp index fb01001474de..f785c6105ab1 100644 --- a/GPU/Common/ReplacedTexture.cpp +++ b/GPU/Common/ReplacedTexture.cpp @@ -199,6 +199,8 @@ inline uint32_t RoundUpTo4(uint32_t value) { } void ReplacedTexture::Prepare(VFSBackend *vfs) { + _assert_(vfs != nullptr); + this->vfs_ = vfs; std::unique_lock lock(lock_); @@ -295,6 +297,11 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference data_.resize(mipLevel + 1); } + if (!vfs_) { + ERROR_LOG(Log::G3D, "Unexpected null vfs_ pointer in LoadLevelData"); + return LoadLevelResult::LOAD_ERROR; + } + ReplacedTextureLevel level; size_t fileSize; VFSOpenFile *openFile = vfs_->OpenFileForRead(fileRef, &fileSize); diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 9087f062b45e..c172f439c1e3 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -100,6 +100,10 @@ static bool g_rebootEmuThread = false; static SDL_AudioSpec g_retFmt; +static bool g_textFocusChanged; +static bool g_textFocus; + + // Window state to be transferred to the main SDL thread. static std::mutex g_mutexWindow; struct WindowState { @@ -347,6 +351,23 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string #endif /* PPSSPP_PLATFORM(WINDOWS) */ return true; } + case SystemRequestType::NOTIFY_UI_EVENT: + { + switch ((UIEventNotification)param3) { + case UIEventNotification::TEXT_GOTFOCUS: + g_textFocus = true; + g_textFocusChanged = true; + break; + case UIEventNotification::POPUP_CLOSED: + case UIEventNotification::TEXT_LOSTFOCUS: + g_textFocus = false; + g_textFocusChanged = true; + break; + default: + break; + } + return true; + } default: return false; } @@ -1087,6 +1108,18 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta } } +void UpdateTextFocus() { + if (g_textFocusChanged) { + INFO_LOG(Log::System, "Updating text focus: %d", g_textFocus); + if (g_textFocus) { + SDL_StartTextInput(); + } else { + SDL_StopTextInput(); + } + g_textFocusChanged = false; + } +} + void UpdateSDLCursor() { #if !defined(MOBILE_DEVICE) if (lastUIState != GetUIState()) { @@ -1428,7 +1461,8 @@ int main(int argc, char *argv[]) { #endif // Avoid the IME popup when holding keys. This doesn't affect all versions of SDL. - // TODO: Enable it in text input fields + // Note: We re-enable it in text input fields! This is necessary otherwise we don't receive + // KEY_CHAR events. SDL_StopTextInput(); InitSDLAudioDevice(); @@ -1465,6 +1499,7 @@ int main(int argc, char *argv[]) { if (g_QuitRequested || g_RestartRequested) break; + UpdateTextFocus(); UpdateSDLCursor(); inputTracker.MouseCaptureControl(); @@ -1491,6 +1526,7 @@ int main(int argc, char *argv[]) { if (g_QuitRequested || g_RestartRequested) break; + UpdateTextFocus(); UpdateSDLCursor(); inputTracker.MouseCaptureControl(); diff --git a/UI/ChatScreen.cpp b/UI/ChatScreen.cpp index 3445767c772c..b71df0177869 100644 --- a/UI/ChatScreen.cpp +++ b/UI/ChatScreen.cpp @@ -15,6 +15,7 @@ #include "Core/System.h" #include "Core/HLE/proAdhoc.h" #include "UI/ChatScreen.h" +#include "UI/PopupScreens.h" void ChatMenu::CreateContents(UI::ViewGroup *parent) { using namespace UI; @@ -22,14 +23,20 @@ void ChatMenu::CreateContents(UI::ViewGroup *parent) { LinearLayout *outer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT,400)); scroll_ = outer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0))); LinearLayout *bottom = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(SDL) - chatEdit_ = bottom->Add(new TextEdit("", n->T("Chat message"), n->T("Chat Here"), new LinearLayoutParams(1.0))); - chatEdit_->OnEnter.Handle(this, &ChatMenu::OnSubmit); -#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) - bottom->Add(new Button(n->T("Chat Here"),new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->OnClick.Handle(this, &ChatMenu::OnSubmit); - bottom->Add(new Button(n->T("Send")))->OnClick.Handle(this, &ChatMenu::OnSubmit); -#endif + chatButton_ = nullptr; + chatEdit_ = nullptr; + chatVert_ = nullptr; + + if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_DESKTOP) { + // We have direct keyboard input. + chatEdit_ = bottom->Add(new TextEdit("", n->T("Chat message"), n->T("Chat Here"), new LinearLayoutParams(1.0))); + chatEdit_->OnEnter.Handle(this, &ChatMenu::OnSubmitMessage); + } else { + // If we have a native input box, like on Android, or at least we can do a popup text input with our UI... + chatButton_ = bottom->Add(new Button(n->T("Chat message"), new LayoutParams(FILL_PARENT, WRAP_CONTENT))); + chatButton_->OnClick.Handle(this, &ChatMenu::OnAskForChatMessage); + } if (g_Config.bEnableQuickChat) { LinearLayout *quickChat = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); @@ -89,18 +96,37 @@ void ChatMenu::CreateSubviews(const Bounds &screenBounds) { UpdateChat(); } -UI::EventReturn ChatMenu::OnSubmit(UI::EventParams &e) { -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || (defined(SDL) && !PPSSPP_PLATFORM(SWITCH)) +UI::EventReturn ChatMenu::OnSubmitMessage(UI::EventParams &e) { std::string chat = chatEdit_->GetText(); chatEdit_->SetText(""); chatEdit_->SetFocus(); sendChat(chat); -#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) || PPSSPP_PLATFORM(IOS) + return UI::EVENT_DONE; +} + +UI::EventReturn ChatMenu::OnAskForChatMessage(UI::EventParams &e) { auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(token_, n->T("Chat"), "", false, [](const std::string &value, int) { - sendChat(value); - }); -#endif + + using namespace UI; + + if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) { + System_InputBoxGetString(token_, n->T("Chat"), "", false, [](const std::string &value, int) { + sendChat(value); + }); + } else { + // We need to pop up a UI inputbox. + messageTemp_.clear(); + TextEditPopupScreen *popupScreen = new TextEditPopupScreen(&messageTemp_, "", n->T("Chat message"), 256); + if (System_GetPropertyBool(SYSPROP_KEYBOARD_IS_SOFT)) { + popupScreen->SetAlignTop(true); + } + popupScreen->OnChange.Add([=](UI::EventParams &e) { + sendChat(messageTemp_); + return UI::EVENT_DONE; + }); + popupScreen->SetPopupOrigin(chatButton_); + screenManager_->push(popupScreen); + } return UI::EVENT_DONE; } diff --git a/UI/ChatScreen.h b/UI/ChatScreen.h index 5c2bee05324e..0c855e7e997b 100644 --- a/UI/ChatScreen.h +++ b/UI/ChatScreen.h @@ -5,8 +5,8 @@ class ChatMenu : public UI::AnchorLayout { public: - ChatMenu(int token, const Bounds &screenBounds, UI::LayoutParams *lp = nullptr) - : UI::AnchorLayout(lp), token_(token) { + ChatMenu(int token, const Bounds &screenBounds, ScreenManager *screenManager, UI::LayoutParams *lp = nullptr) + : UI::AnchorLayout(lp), screenManager_(screenManager), token_(token) { CreateSubviews(screenBounds); } void Update() override; @@ -25,22 +25,25 @@ class ChatMenu : public UI::AnchorLayout { void CreateContents(UI::ViewGroup *parent); void UpdateChat(); - UI::EventReturn OnSubmit(UI::EventParams &e); + UI::EventReturn OnAskForChatMessage(UI::EventParams &e); + + UI::EventReturn OnSubmitMessage(UI::EventParams &e); UI::EventReturn OnQuickChat1(UI::EventParams &e); UI::EventReturn OnQuickChat2(UI::EventParams &e); UI::EventReturn OnQuickChat3(UI::EventParams &e); UI::EventReturn OnQuickChat4(UI::EventParams &e); UI::EventReturn OnQuickChat5(UI::EventParams &e); -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(SDL) UI::TextEdit *chatEdit_ = nullptr; -#endif UI::ScrollView *scroll_ = nullptr; UI::LinearLayout *chatVert_ = nullptr; UI::ViewGroup *box_ = nullptr; + ScreenManager *screenManager_; int chatChangeID_ = 0; bool toBottom_ = true; bool promptInput_ = false; int token_; + std::string messageTemp_; + UI::Button *chatButton_ = nullptr; }; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 361c026dde0f..d079a9d958d1 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -468,6 +468,11 @@ EmuScreen::~EmuScreen() { } void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) { + if (std::string_view(dialog->tag()) == "TextEditPopup") { + // Chat message finished. + return; + } + // TODO: improve the way with which we got commands from PauseMenu. // DR_CANCEL/DR_BACK means clicked on "continue", DR_OK means clicked on "back to menu", // DR_YES means a message sent to PauseMenu by System_PostUIMessage. @@ -584,19 +589,19 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) { if (!chatButton_) RecreateViews(); -#if defined(USING_WIN_UI) - // temporary workaround for hotkey its freeze the ui when open chat screen using hotkey and native keyboard is enable - if (g_Config.bBypassOSKWithKeyboard) { - // TODO: Make translatable. - g_OSD.Show(OSDType::MESSAGE_INFO, "Disable \"Use system native keyboard\" to use ctrl + c hotkey", 2.0f); + if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_DESKTOP) { + // temporary workaround for hotkey its freeze the ui when open chat screen using hotkey and native keyboard is enable + if (g_Config.bBypassOSKWithKeyboard) { + // TODO: Make translatable. + g_OSD.Show(OSDType::MESSAGE_INFO, "Disable \"Use system native keyboard\" to use ctrl + c hotkey", 2.0f); + } else { + UI::EventParams e{}; + OnChatMenu.Trigger(e); + } } else { UI::EventParams e{}; OnChatMenu.Trigger(e); } -#else - UI::EventParams e{}; - OnChatMenu.Trigger(e); -#endif } } else if (message == UIMessage::APP_RESUMED && screenManager()->topScreen() == this) { if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) { @@ -930,7 +935,6 @@ bool EmuScreen::UnsyncKey(const KeyInput &key) { if (UI::IsFocusMovementEnabled()) { return UIScreen::UnsyncKey(key); } - return controlMapper_.Key(key, &pauseTrigger_); } @@ -1055,7 +1059,7 @@ void EmuScreen::CreateViews() { root_->Add(btn)->OnClick.Handle(this, &EmuScreen::OnChat); chatButton_ = btn; } - chatMenu_ = root_->Add(new ChatMenu(GetRequesterToken(), screenManager()->getUIContext()->GetBounds(), new LayoutParams(FILL_PARENT, FILL_PARENT))); + chatMenu_ = root_->Add(new ChatMenu(GetRequesterToken(), screenManager()->getUIContext()->GetBounds(), screenManager(), new LayoutParams(FILL_PARENT, FILL_PARENT))); chatMenu_->SetVisibility(UI::V_GONE); } else { chatButton_ = nullptr; @@ -1464,8 +1468,8 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { if (!framebufferBound && PSP_IsInited()) { // draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, clearColor }, "EmuScreen_Stepping"); gpu->CopyDisplayToOutput(true); + framebufferBound = true; } - framebufferBound = true; } break; } diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 44a3d327a003..5f6d99fed4e9 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -380,9 +380,9 @@ void GamePauseScreen::CreateViews() { leftColumnItems->Add(new NoticeView(NoticeLevel::INFO, notAvailable, "")); } - ViewGroup *middleColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(64, FILL_PARENT, Margins(0, 10, 0, 15))); + LinearLayout *middleColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(64, FILL_PARENT, Margins(0, 10, 0, 15))); root_->Add(middleColumn); - + middleColumn->SetSpacing(0.0f); ViewGroup *rightColumnHolder = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(vertical ? 200 : 300, FILL_PARENT, actionMenuMargins)); ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); @@ -404,7 +404,7 @@ void GamePauseScreen::CreateViews() { root_->SetDefaultFocusView(continueChoice); continueChoice->OnClick.Handle(this, &UIScreen::OnBack); - rightColumnItems->Add(new Spacer(25.0)); + rightColumnItems->Add(new Spacer(20.0)); std::string gameId = g_paramSFO.GetDiscID(); if (g_Config.hasGameConfig(gameId)) { @@ -438,7 +438,7 @@ void GamePauseScreen::CreateViews() { auto rp = GetI18NCategory(I18NCat::REPORTING); rightColumnItems->Add(new Choice(rp->T("ReportButton", "Report Feedback")))->OnClick.Handle(this, &GamePauseScreen::OnReportFeedback); } - rightColumnItems->Add(new Spacer(25.0)); + rightColumnItems->Add(new Spacer(20.0)); if (g_Config.bPauseMenuExitsEmulator) { auto mm = GetI18NCategory(I18NCat::MAINMENU); rightColumnItems->Add(new Choice(mm->T("Exit")))->OnClick.Handle(this, &GamePauseScreen::OnExitToMenu); @@ -453,7 +453,7 @@ void GamePauseScreen::CreateViews() { playButton_->SetImageID(g_Config.bRunBehindPauseMenu ? ImageID("I_PAUSE") : ImageID("I_PLAY")); return UI::EVENT_DONE; }); - + middleColumn->Add(new Spacer(20.0)); Button *infoButton = middleColumn->Add(new Button("", ImageID("I_INFO"), new LinearLayoutParams(64, 64))); infoButton->OnClick.Add([=](UI::EventParams &e) { screenManager()->push(new GameScreen(gamePath_, true));