diff --git a/Common/System/OSD.cpp b/Common/System/OSD.cpp index 7c3bb7c7c1..7a896f20a6 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 dd288c01a2..4f81e4ad39 100644 --- a/Common/System/OSD.h +++ b/Common/System/OSD.h @@ -87,6 +87,9 @@ public: 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 08ce0a31eb..8863a16140 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,8 +93,15 @@ void ScreenManager::touch(const TouchInput &touch) { layer.screen->UnsyncTouch(screen->transformTouch(touch)); } } else if (!stack_.empty()) { - Screen *screen = stack_.back().screen; - stack_.back().screen->UnsyncTouch(screen->transformTouch(touch)); + // Let the overlay know about touch-downs, to be able to dismiss popups. + bool skip = false; + if (overlayScreen_ && (touch.flags & TOUCH_DOWN)) { + skip = overlayScreen_->UnsyncTouch(overlayScreen_->transformTouch(touch)); + } + if (!skip) { + 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 4f559f9ed7..217f6d55dc 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -59,7 +59,9 @@ public: 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 86b7a34c30..6cb3b54d1f 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 0a75c06541..e4cf313766 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -46,7 +46,7 @@ public: 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/DevScreens.cpp b/UI/DevScreens.cpp index 339b277638..4179b38052 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 d000bfb594..3572061c9f 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -110,11 +110,11 @@ public: 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 { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 231a695142..13ef0d39fd 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 6bfafe62e7..3f07f3d789 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -51,7 +51,7 @@ public: // 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; diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 2258ba6cc4..96a53eaee7 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) { @@ -444,13 +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; } } + y += (measuredEntry.h + 4.0f) * measuredEntry.alpha; } } + + std::lock_guard lock(clickMutex_); + clickZones_ = dismissZones; } std::string OnScreenMessagesView::DescribeText() const { @@ -465,10 +484,34 @@ std::string OnScreenMessagesView::DescribeText() const { return ss.str(); } +// 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; +} + +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; + } +} + 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 5cde5cf585..597fa6e5b9 100644 --- a/UI/OnScreenDisplay.h +++ b/UI/OnScreenDisplay.h @@ -21,14 +21,30 @@ class OnScreenMessagesView : public UI::InertView { public: OnScreenMessagesView(UI::LayoutParams *layoutParams = nullptr) : UI::InertView(layoutParams) {} void Draw(UIContext &dc) override; + bool Dismiss(float x, float y); // Not reusing Touch since it's asynchronous. std::string DescribeText() const override; +private: + struct ClickZone { + int index; + Bounds bounds; + }; + + // Argh, would really like to avoid this. + 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 {