From ea4c46f27c0b87658cf58f595779aa8e9bbbde0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 4 May 2026 14:29:26 +0200 Subject: [PATCH 1/2] Add ability to set keyboard focus to specific screens. Needed for searchbar on Mac. --- Common/UI/PopupScreens.cpp | 9 +++++++++ Common/UI/PopupScreens.h | 3 +-- Common/UI/Screen.cpp | 18 ++++++++++++++++-- Common/UI/Screen.h | 1 + Common/UI/UIScreen.h | 1 + UI/CwCheatScreen.h | 2 ++ UI/MainScreen.h | 1 + UI/MiscViews.cpp | 5 ++++- UI/MiscViews.h | 2 +- 9 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index 3578a3b138..e339f45976 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -949,6 +949,15 @@ ViewGroup *CreateSoftKeyboard(TextEdit *edit, SoftKeyboardState *state) { return scrollView; } +TextEditPopupScreen::TextEditPopupScreen(std::string *value, std::string_view placeholder, std::string_view title, int maxLen) + : PopupScreen(title, T(I18NCat::DIALOG, "OK"), T(I18NCat::DIALOG, "Cancel")), value_(value), placeholder_(placeholder), maxLen_(maxLen) { + if (!value_) { + // Point it to our temporary. + value_ = &textEditValue_; + } +} + + void TextEditPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; UIContext &dc = *screenManager()->getUIContext(); diff --git a/Common/UI/PopupScreens.h b/Common/UI/PopupScreens.h index 0afc183505..ec86de344d 100644 --- a/Common/UI/PopupScreens.h +++ b/Common/UI/PopupScreens.h @@ -233,8 +233,7 @@ ViewGroup *CreateSoftKeyboard(TextEdit *edit, SoftKeyboardState *state); class TextEditPopupScreen : public PopupScreen { public: - TextEditPopupScreen(std::string *value, std::string_view placeholder, std::string_view title, int maxLen) - : PopupScreen(title, T(I18NCat::DIALOG, "OK"), T(I18NCat::DIALOG, "Cancel")), value_(value), placeholder_(placeholder), maxLen_(maxLen) {} + TextEditPopupScreen(std::string *value, std::string_view placeholder, std::string_view title, int maxLen); void CreatePopupContents(ViewGroup *parent) override; const char *tag() const override { return "TextEditPopup"; } diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 4dc7e0aa90..733cdb3293 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -15,8 +15,22 @@ void Screen::focusChanged(ScreenFocusChange focusChange) { const char *eventName = ""; switch (focusChange) { - case ScreenFocusChange::FOCUS_LOST_TOP: eventName = "FOCUS_LOST_TOP"; break; - case ScreenFocusChange::FOCUS_BECAME_TOP: eventName = "FOCUS_BECAME_TOP"; break; + case ScreenFocusChange::FOCUS_LOST_TOP: + #if !defined(MOBILE_DEVICE) + if (WantsTextInput()) { + System_NotifyUIEvent(UIEventNotification::TEXT_LOSTFOCUS); + } + #endif + eventName = "FOCUS_LOST_TOP"; + break; + case ScreenFocusChange::FOCUS_BECAME_TOP: + #if !defined(MOBILE_DEVICE) + if (WantsTextInput()) { + System_NotifyUIEvent(UIEventNotification::TEXT_GOTFOCUS); + } + #endif + eventName = "FOCUS_BECAME_TOP"; + break; } DEBUG_LOG(Log::UI, "Screen %s got %s", this->tag(), eventName); } diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index 20d25ca069..8d4dd3f546 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -113,6 +113,7 @@ public: virtual bool isTopLevel() const { return false; } virtual TouchInput transformTouch(const TouchInput &touch) { return touch; } + virtual bool WantsTextInput() const { return false; } protected: int GetRequesterToken(); diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 0db7454d9b..046c4a9f1d 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -75,6 +75,7 @@ public: virtual UI::Margins RootMargins() const { return UI::Margins(0); } virtual void focusChanged(ScreenFocusChange focusChange) override { + Screen::focusChanged(focusChange); modifiersPressed_ = Modifier::NONE; } diff --git a/UI/CwCheatScreen.h b/UI/CwCheatScreen.h index 55088bb9b0..72849b7823 100644 --- a/UI/CwCheatScreen.h +++ b/UI/CwCheatScreen.h @@ -50,6 +50,8 @@ public: const char *tag() const override { return "CwCheat"; } + bool WantsTextInput() const override { return true; } + protected: void BeforeCreateViews() override; void CreateSettingsViews(UI::ViewGroup *) override; diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 4167cc2830..72c1e3d41e 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -52,6 +52,7 @@ public: bool key(const KeyInput &touch) override; + bool WantsTextInput() const override { return true; } protected: ViewLayoutMode LayoutMode() const override { return ViewLayoutMode::IgnoreBottomInset; } diff --git a/UI/MiscViews.cpp b/UI/MiscViews.cpp index 9a35010780..1907ed4ae2 100644 --- a/UI/MiscViews.cpp +++ b/UI/MiscViews.cpp @@ -325,6 +325,10 @@ void SearchBar::Draw(UIContext &dc) { dc.DrawText(searchFilter_, bounds_.x + leftMargin, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER); } +SearchBar::SearchBar(UI::LayoutParams *params) : UI::InertView(params) { + SetVisibility(UI::Visibility::V_GONE); +} + void SearchBar::GetContentDimensions(const UIContext &dc, float &w, float &h) const { w = 0; h = 0; @@ -439,7 +443,6 @@ bool ViewSearch::Key(UI::ViewGroup *viewGroup, const KeyInput &input) { const int unichar = input.keyCode; if (unichar >= 0x20 && unichar != 127) { // 127 gets produced from Ctrl+Backspace on Windows for some reason. // TODO: Save focus state here. - // Insert it! (todo: do it with a string insert) char buf[8]; buf[u8_wc_toutf8(buf, unichar)] = '\0'; diff --git a/UI/MiscViews.h b/UI/MiscViews.h index 120bfa809a..709a7bf0da 100644 --- a/UI/MiscViews.h +++ b/UI/MiscViews.h @@ -103,7 +103,7 @@ void AddRotationPicker(ScreenManager *screenManager, UI::ViewGroup *parent, bool class SearchBar : public UI::InertView { public: - SearchBar(UI::LayoutParams *params) : UI::InertView(params) { SetVisibility(UI::Visibility::V_GONE); } + SearchBar(UI::LayoutParams *params); void Draw(UIContext &dc) override; bool Touch(const TouchInput &input) override; From cf1bd807c7891face82ea2b3761a73e93ef1858f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 4 May 2026 15:05:30 +0200 Subject: [PATCH 2/2] Fix problem with AskForInput when there's no native text input dialog (iOS, Mac, Linux) --- Common/UI/PopupScreens.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index e339f45976..248169f9fe 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -800,7 +800,7 @@ void AskForInput(ScreenManager *screenManager, RequesterToken token, UI::View *s popupScreen->SetAlignTop(true); } popupScreen->OnChange.Add([callback, sourceView](EventParams &e) { - callback(SanitizeString(StripSpaces(*(std::string *)e.v), StringRestriction::None, 0, 0), true); + callback(SanitizeString(StripSpaces(e.s), StringRestriction::None, 0, 0), true); if (sourceView) { SetFocusedView(sourceView); } @@ -838,6 +838,7 @@ void PopupTextInputChoice::HandleClick(EventParams &e) { *value_ = StripSpaces(SanitizeString(*value_, restriction_, minLen_, maxLen_)); EventParams params{}; params.v = this; + params.s = *value_; OnChange.Trigger(params); if (restoreFocus_) { SetFocusedView(this); @@ -991,6 +992,7 @@ void TextEditPopupScreen::OnCompleted(DialogResult result) { *value_ = StripSpaces(edit_->GetText()); EventParams e{}; e.v = edit_; + e.s = *value_; OnChange.Trigger(e); } }