From 980b2158ba149e6e1a90fb96ccbdc559931adf50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 2 Sep 2023 15:42:47 +0200 Subject: [PATCH 1/5] Hide the "System Information / Internals" tab in release builds --- UI/DevScreens.cpp | 13 ++++++++++++- UI/DevScreens.h | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 339b27763846..4179b380529c 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -858,7 +858,19 @@ void SystemInfoScreen::CreateTabs() { } } +#ifdef _DEBUG LinearLayout *internals = AddTab("DevSystemInfoInternals", si->T("Internals")); + CreateInternalsTab(internals); +#endif +} + +void SystemInfoScreen::CreateInternalsTab(UI::ViewGroup *internals) { + using namespace UI; + + auto di = GetI18NCategory(I18NCat::DIALOG); + auto si = GetI18NCategory(I18NCat::SYSINFO); + auto sy = GetI18NCategory(I18NCat::SYSTEM); + auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); internals->Add(new ItemHeader(si->T("Icon cache"))); IconCacheStats iconStats = g_iconCache.GetStats(); @@ -929,7 +941,6 @@ void SystemInfoScreen::CreateTabs() { }); static const char *positions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" }; - auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); internals->Add(new ItemHeader(ac->T("Notifications"))); internals->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bAchievementsEnable); diff --git a/UI/DevScreens.h b/UI/DevScreens.h index d000bfb59468..3572061c9f24 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -110,11 +110,11 @@ class SystemInfoScreen : public TabbedUIDialogScreenWithGameBackground { const char *tag() const override { return "SystemInfo"; } void CreateTabs() override; - void update() override; protected: bool ShowSearchControls() const override { return false; } + void CreateInternalsTab(UI::ViewGroup *internals); }; class AddressPromptScreen : public PopupScreen { From cd78097e74a9e2db44ae1f374cad6d3007c8444b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 4 Sep 2023 10:01:07 +0200 Subject: [PATCH 2/5] Allow dismissing notifications by touching/clicking them. Fixes #18040 Might do something more elegant in the future. --- Common/System/OSD.cpp | 7 +++++++ Common/System/OSD.h | 3 +++ Common/UI/Screen.cpp | 7 +++++++ UI/OnScreenDisplay.cpp | 17 +++++++++++++++++ UI/OnScreenDisplay.h | 3 +++ 5 files changed, 37 insertions(+) diff --git a/Common/System/OSD.cpp b/Common/System/OSD.cpp index 7c3bb7c7c101..7a896f20a636 100644 --- a/Common/System/OSD.cpp +++ b/Common/System/OSD.cpp @@ -39,6 +39,13 @@ float OnScreenDisplay::SidebarAlpha() const { return saturatef(1.0f - ((float)timeSinceNudge - 0.1f) * 4.0f); } +void OnScreenDisplay::DismissEntry(size_t index, double now) { + std::lock_guard guard(mutex_); + if (index < entries_.size() && entries_[index].type != OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR) { + entries_[index].endTime = std::min(now + FadeoutTime(), entries_[index].endTime); + } +} + void OnScreenDisplay::Show(OSDType type, const std::string &text, const std::string &text2, const std::string &icon, float duration_s, const char *id) { // Automatic duration based on type. if (duration_s <= 0.0f) { diff --git a/Common/System/OSD.h b/Common/System/OSD.h index dd288c01a2e5..4f81e4ad393b 100644 --- a/Common/System/OSD.h +++ b/Common/System/OSD.h @@ -87,6 +87,9 @@ class OnScreenDisplay { std::vector Entries(); + // TODO: Use something more stable than the index. + void DismissEntry(size_t index, double now); + static float FadeinTime() { return 0.1f; } static float FadeoutTime() { return 0.25f; } diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 08ce0a31ebd9..d42eee36af8d 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -48,6 +48,9 @@ void ScreenManager::update() { switchToNext(); } + if (overlayScreen_) { + overlayScreen_->update(); + } if (stack_.size()) { stack_.back().screen->update(); } @@ -90,6 +93,10 @@ void ScreenManager::touch(const TouchInput &touch) { layer.screen->UnsyncTouch(screen->transformTouch(touch)); } } else if (!stack_.empty()) { + // Let the overlay know about touch-downs, to be able to dismiss popups. + if (overlayScreen_ && (touch.flags & TOUCH_DOWN)) { + overlayScreen_->UnsyncTouch(overlayScreen_->transformTouch(touch)); + } Screen *screen = stack_.back().screen; stack_.back().screen->UnsyncTouch(screen->transformTouch(touch)); } diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 2258ba6cc4d1..954392e09ccb 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -448,9 +448,19 @@ void OnScreenMessagesView::Draw(UIContext &dc) { } } + // Quick hack for dismissing messages by touch. + for (auto &touch : touches_) { + if (b.Contains(touch.x, touch.y)) { + INFO_LOG(G3D, "Dismissing entry %d (%0.1f %0.1f vs %0.1f %0.1f %0.1f %0.f)", j, touch.x, touch.y, bounds_.x, bounds_.y, bounds_.w, bounds_.h); + g_OSD.DismissEntry(j, now); + } + } + y += (measuredEntry.h + 4.0f) * measuredEntry.alpha; } } + + touches_.clear(); } std::string OnScreenMessagesView::DescribeText() const { @@ -465,6 +475,13 @@ std::string OnScreenMessagesView::DescribeText() const { return ss.str(); } +bool OnScreenMessagesView::Touch(const TouchInput &input) { + if (input.flags & TOUCH_DOWN) { + touches_.push_back(input); + } + return true; +} + void OSDOverlayScreen::CreateViews() { root_ = new UI::AnchorLayout(); root_->SetTag("OSDOverlayScreen"); diff --git a/UI/OnScreenDisplay.h b/UI/OnScreenDisplay.h index 5cde5cf585e5..480c34780d35 100644 --- a/UI/OnScreenDisplay.h +++ b/UI/OnScreenDisplay.h @@ -21,7 +21,10 @@ class OnScreenMessagesView : public UI::InertView { public: OnScreenMessagesView(UI::LayoutParams *layoutParams = nullptr) : UI::InertView(layoutParams) {} void Draw(UIContext &dc) override; + bool Touch(const TouchInput &input) override; std::string DescribeText() const override; +private: + std::vector touches_; }; class OSDOverlayScreen : public UIScreen { From 005b072fdc88716b7135d834ca585a9003d95356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 4 Sep 2023 10:54:17 +0200 Subject: [PATCH 3/5] Flip the problem on its head --- UI/OnScreenDisplay.cpp | 28 ++++++++++++++++++---------- UI/OnScreenDisplay.h | 9 ++++++++- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 954392e09ccb..15658fbd3bce 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -367,6 +367,8 @@ void OnScreenMessagesView::Draw(UIContext &dc) { edges[(size_t)pos].maxWidth = std::max(edges[(size_t)pos].maxWidth, measuredEntry.w); } + std::vector dismissZones; + // Now, perform layout for all 8 edges. for (size_t i = 0; i < (size_t)ScreenEdgePosition::VALUE_COUNT; i++) { if (edges[i].height == 0.0f) { @@ -448,19 +450,15 @@ void OnScreenMessagesView::Draw(UIContext &dc) { } } - // Quick hack for dismissing messages by touch. - for (auto &touch : touches_) { - if (b.Contains(touch.x, touch.y)) { - INFO_LOG(G3D, "Dismissing entry %d (%0.1f %0.1f vs %0.1f %0.1f %0.1f %0.f)", j, touch.x, touch.y, bounds_.x, bounds_.y, bounds_.w, bounds_.h); - g_OSD.DismissEntry(j, now); - } - } + // Save the location of the popup, for easy dismissal. + dismissZones.push_back(DismissZone{ (int)j, b }); y += (measuredEntry.h + 4.0f) * measuredEntry.alpha; } } - touches_.clear(); + std::lock_guard lock(dismissMutex_); + dismissZones_ = dismissZones; } std::string OnScreenMessagesView::DescribeText() const { @@ -477,9 +475,19 @@ std::string OnScreenMessagesView::DescribeText() const { bool OnScreenMessagesView::Touch(const TouchInput &input) { if (input.flags & TOUCH_DOWN) { - touches_.push_back(input); + bool dismissed = false; + std::lock_guard lock(dismissMutex_); + double now = time_now_d(); + for (auto &zone : dismissZones_) { + if (zone.bounds.Contains(input.x, input.y)) { + g_OSD.DismissEntry(zone.index, now); + dismissed = true; + } + } + return dismissed; + } else { + return false; } - return true; } void OSDOverlayScreen::CreateViews() { diff --git a/UI/OnScreenDisplay.h b/UI/OnScreenDisplay.h index 480c34780d35..70de6dcdb44a 100644 --- a/UI/OnScreenDisplay.h +++ b/UI/OnScreenDisplay.h @@ -24,7 +24,14 @@ class OnScreenMessagesView : public UI::InertView { bool Touch(const TouchInput &input) override; std::string DescribeText() const override; private: - std::vector touches_; + struct DismissZone { + int index; + Bounds bounds; + }; + + // Argh, would really like to avoid this. + std::mutex dismissMutex_; + std::vector dismissZones_; }; class OSDOverlayScreen : public UIScreen { From 109205a56a365e1834f98b4cb661472f08d0905c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 4 Sep 2023 10:58:11 +0200 Subject: [PATCH 4/5] Add return value to UnsyncTouch --- Common/UI/Screen.cpp | 9 ++++++--- Common/UI/Screen.h | 4 +++- Common/UI/UIScreen.cpp | 3 ++- Common/UI/UIScreen.h | 2 +- UI/EmuScreen.cpp | 5 +++-- UI/EmuScreen.h | 2 +- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index d42eee36af8d..8863a161403f 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -94,11 +94,14 @@ void ScreenManager::touch(const TouchInput &touch) { } } else if (!stack_.empty()) { // Let the overlay know about touch-downs, to be able to dismiss popups. + bool skip = false; if (overlayScreen_ && (touch.flags & TOUCH_DOWN)) { - overlayScreen_->UnsyncTouch(overlayScreen_->transformTouch(touch)); + skip = overlayScreen_->UnsyncTouch(overlayScreen_->transformTouch(touch)); + } + if (!skip) { + Screen *screen = stack_.back().screen; + stack_.back().screen->UnsyncTouch(screen->transformTouch(touch)); } - Screen *screen = stack_.back().screen; - stack_.back().screen->UnsyncTouch(screen->transformTouch(touch)); } } diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index 4f559f9ed7e3..217f6d55dcd5 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -59,7 +59,9 @@ class Screen { virtual void deviceLost() {} virtual void deviceRestored() {} - virtual void UnsyncTouch(const TouchInput &touch) = 0; + // Return value of UnsyncTouch is only used to let the overlay screen block touches. + virtual bool UnsyncTouch(const TouchInput &touch) = 0; + // Return value of UnsyncKey is used to not block certain system keys like volume when unhandled, on Android. virtual bool UnsyncKey(const KeyInput &touch) = 0; virtual void UnsyncAxis(const AxisInput &touch) = 0; diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 86b7a34c302a..6cb3b54d1fa0 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -81,7 +81,7 @@ bool UIScreen::key(const KeyInput &key) { } } -void UIScreen::UnsyncTouch(const TouchInput &touch) { +bool UIScreen::UnsyncTouch(const TouchInput &touch) { if (ClickDebug && root_ && (touch.flags & TOUCH_DOWN)) { INFO_LOG(SYSTEM, "Touch down!"); std::vector views; @@ -96,6 +96,7 @@ void UIScreen::UnsyncTouch(const TouchInput &touch) { ev.type = QueuedEventType::TOUCH; ev.touch = touch; eventQueue_.push_back(ev); + return false; } void UIScreen::UnsyncAxis(const AxisInput &axis) { diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 0a75c06541a3..e4cf3137663f 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -46,7 +46,7 @@ class UIScreen : public Screen { virtual bool key(const KeyInput &key); virtual void axis(const AxisInput &axis); - void UnsyncTouch(const TouchInput &touch) override; + bool UnsyncTouch(const TouchInput &touch) override; bool UnsyncKey(const KeyInput &key) override; void UnsyncAxis(const AxisInput &axis) override; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 231a6951421c..13ef0d39fdd0 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -563,14 +563,14 @@ void EmuScreen::sendMessage(const char *message, const char *value) { } } -void EmuScreen::UnsyncTouch(const TouchInput &touch) { +bool EmuScreen::UnsyncTouch(const TouchInput &touch) { System_Notify(SystemNotification::ACTIVITY); if (chatMenu_ && chatMenu_->GetVisibility() == UI::V_VISIBLE) { // Avoid pressing touch button behind the chat if (chatMenu_->Contains(touch.x, touch.y)) { chatMenu_->Touch(touch); - return; + return true; } else if ((touch.flags & TOUCH_DOWN) != 0) { chatMenu_->Close(); if (chatButton_) @@ -582,6 +582,7 @@ void EmuScreen::UnsyncTouch(const TouchInput &touch) { if (root_) { root_->Touch(touch); } + return true; } void EmuScreen::onVKey(int virtualKeyCode, bool down) { diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 6bfafe62e779..3f07f3d78993 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -51,7 +51,7 @@ class EmuScreen : public UIScreen { // Note: Unlike your average boring UIScreen, here we override the Unsync* functions // to get minimal latency and full control. We forward to UIScreen when needed. - void UnsyncTouch(const TouchInput &touch) override; + bool UnsyncTouch(const TouchInput &touch) override; bool UnsyncKey(const KeyInput &key) override; void UnsyncAxis(const AxisInput &axis) override; From 78c064cc9634f46a96eaa4b7049912221e17502d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 4 Sep 2023 11:04:15 +0200 Subject: [PATCH 5/5] Bypass normal UI messages to avoid slowing down unsynchrononized touch messages. --- UI/OnScreenDisplay.cpp | 52 ++++++++++++++++++++++++++++-------------- UI/OnScreenDisplay.h | 14 ++++++++---- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 15658fbd3bce..96a53eaee759 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -367,7 +367,7 @@ void OnScreenMessagesView::Draw(UIContext &dc) { edges[(size_t)pos].maxWidth = std::max(edges[(size_t)pos].maxWidth, measuredEntry.w); } - std::vector dismissZones; + std::vector dismissZones; // Now, perform layout for all 8 edges. for (size_t i = 0; i < (size_t)ScreenEdgePosition::VALUE_COUNT; i++) { @@ -446,19 +446,30 @@ void OnScreenMessagesView::Draw(UIContext &dc) { float alpha = Clamp((float)(entry.endTime - now) * 4.0f, 0.0f, 1.0f); RenderOSDEntry(dc, entry, b, measuredEntry.h1, measuredEntry.align, alpha); + + switch (entry.type) { + case OSDType::MESSAGE_INFO: + case OSDType::MESSAGE_SUCCESS: + case OSDType::MESSAGE_WARNING: + case OSDType::MESSAGE_ERROR: + case OSDType::MESSAGE_ERROR_DUMP: + case OSDType::MESSAGE_FILE_LINK: + case OSDType::ACHIEVEMENT_UNLOCKED: + // Save the location of the popup, for easy dismissal. + dismissZones.push_back(ClickZone{ (int)j, b }); + break; + } break; } } - // Save the location of the popup, for easy dismissal. - dismissZones.push_back(DismissZone{ (int)j, b }); y += (measuredEntry.h + 4.0f) * measuredEntry.alpha; } } - std::lock_guard lock(dismissMutex_); - dismissZones_ = dismissZones; + std::lock_guard lock(clickMutex_); + clickZones_ = dismissZones; } std::string OnScreenMessagesView::DescribeText() const { @@ -473,18 +484,25 @@ std::string OnScreenMessagesView::DescribeText() const { return ss.str(); } -bool OnScreenMessagesView::Touch(const TouchInput &input) { - if (input.flags & TOUCH_DOWN) { - bool dismissed = false; - std::lock_guard lock(dismissMutex_); - double now = time_now_d(); - for (auto &zone : dismissZones_) { - if (zone.bounds.Contains(input.x, input.y)) { - g_OSD.DismissEntry(zone.index, now); - dismissed = true; - } +// Asynchronous! +bool OnScreenMessagesView::Dismiss(float x, float y) { + bool dismissed = false; + std::lock_guard lock(clickMutex_); + double now = time_now_d(); + for (auto &zone : clickZones_) { + if (zone.bounds.Contains(x, y)) { + g_OSD.DismissEntry(zone.index, now); + dismissed = true; } - return dismissed; + } + return dismissed; +} + +bool OSDOverlayScreen::UnsyncTouch(const TouchInput &touch) { + // Don't really need to forward. + // UIScreen::UnsyncTouch(touch); + if ((touch.flags & TOUCH_DOWN) && osmView_) { + return osmView_->Dismiss(touch.x, touch.y); } else { return false; } @@ -493,7 +511,7 @@ bool OnScreenMessagesView::Touch(const TouchInput &input) { void OSDOverlayScreen::CreateViews() { root_ = new UI::AnchorLayout(); root_->SetTag("OSDOverlayScreen"); - root_->Add(new OnScreenMessagesView(new UI::AnchorLayoutParams(0.0f, 0.0f, 0.0f, 0.0f))); + osmView_ = root_->Add(new OnScreenMessagesView(new UI::AnchorLayoutParams(0.0f, 0.0f, 0.0f, 0.0f))); } void OSDOverlayScreen::render() { diff --git a/UI/OnScreenDisplay.h b/UI/OnScreenDisplay.h index 70de6dcdb44a..597fa6e5b995 100644 --- a/UI/OnScreenDisplay.h +++ b/UI/OnScreenDisplay.h @@ -21,24 +21,30 @@ class OnScreenMessagesView : public UI::InertView { public: OnScreenMessagesView(UI::LayoutParams *layoutParams = nullptr) : UI::InertView(layoutParams) {} void Draw(UIContext &dc) override; - bool Touch(const TouchInput &input) override; + bool Dismiss(float x, float y); // Not reusing Touch since it's asynchronous. std::string DescribeText() const override; private: - struct DismissZone { + struct ClickZone { int index; Bounds bounds; }; // Argh, would really like to avoid this. - std::mutex dismissMutex_; - std::vector dismissZones_; + std::mutex clickMutex_; + std::vector clickZones_; }; class OSDOverlayScreen : public UIScreen { public: const char *tag() const override { return "OSDOverlayScreen"; } + + bool UnsyncTouch(const TouchInput &touch) override; + void CreateViews() override; void render() override; + +private: + OnScreenMessagesView *osmView_ = nullptr; }; enum class NoticeLevel {