From d2c3a7c9781424d9b67ae370402e3b2546e4e777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Jun 2023 21:26:42 +0200 Subject: [PATCH] Add support for displaying progress bars --- Common/System/Request.cpp | 49 +++++++++++++++++++++++++++++++++++++++ Common/System/System.h | 18 +++++++++++++- UI/DevScreens.cpp | 14 +++++++++++ UI/OnScreenDisplay.cpp | 46 +++++++++++++++++++++++++++++++++++- 4 files changed, 125 insertions(+), 2 deletions(-) diff --git a/Common/System/Request.cpp b/Common/System/Request.cpp index 4003318dea..33ec112840 100644 --- a/Common/System/Request.cpp +++ b/Common/System/Request.cpp @@ -20,6 +20,14 @@ void OnScreenDisplay::Update() { iter++; } } + + for (auto iter = bars_.begin(); iter != bars_.end(); ) { + if (now >= iter->endTime) { + iter = bars_.erase(iter); + } else { + iter++; + } + } } std::vector OnScreenDisplay::Entries() { @@ -27,6 +35,11 @@ std::vector OnScreenDisplay::Entries() { return entries_; // makes a copy. } +std::vector OnScreenDisplay::ProgressBars() { + std::lock_guard guard(mutex_); + return bars_; // makes a copy. +} + void OnScreenDisplay::Show(OSDType type, const std::string &text, float duration_s, const char *id) { // Automatic duration based on type. if (duration_s <= 0.0f) { @@ -76,6 +89,42 @@ void OnScreenDisplay::ShowOnOff(const std::string &message, bool on, float durat Show(OSDType::MESSAGE_INFO, message + ": " + (on ? "on" : "off"), duration_s); } +void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress) { + std::lock_guard guard(mutex_); + double now = time_now_d(); + bool found = false; + for (auto &bar : bars_) { + if (bar.id == id) { + _dbg_assert_(minValue == bar.minValue); + _dbg_assert_(maxValue == bar.maxValue); + bar.progress = progress; + bar.message = message; + bar.endTime = now + 60.0; // Nudge the progress bar to keep it shown. + return; + } + } + + ProgressBar bar; + bar.id = id; + bar.message = std::move(message); + bar.minValue = minValue; + bar.maxValue = maxValue; + bar.progress = progress; + bar.endTime = now + 60.0; // Show the progress bar for 60 seconds, then fade it out. + bars_.push_back(bar); +} + +void OnScreenDisplay::RemoveProgressBar(std::string id, float fadeout_s) { + std::lock_guard guard(mutex_); + for (auto iter = bars_.begin(); iter != bars_.end(); iter++) { + if (iter->id == id) { + iter->progress = iter->maxValue; + iter->endTime = time_now_d() + (double)fadeout_s; + break; + } + } +} + const char *RequestTypeAsString(SystemRequestType type) { switch (type) { case SystemRequestType::INPUT_TEXT_MODAL: return "INPUT_TEXT_MODAL"; diff --git a/Common/System/System.h b/Common/System/System.h index 943145e28a..c9908711a7 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -236,18 +236,34 @@ public: // Call this every frame, cleans up old entries. void Update(); + // Progress bar controls + // Set is both create and update. + void SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress); + void RemoveProgressBar(std::string id, float fadeout_s); + struct Entry { OSDType type; std::string text; const char *id; double endTime; double duration; - float progress; }; + + struct ProgressBar { + std::string id; + std::string message; + int minValue; + int maxValue; + int progress; + double endTime; + }; + std::vector Entries(); + std::vector ProgressBars(); private: std::vector entries_; + std::vector bars_; std::mutex mutex_; }; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index df054e4a0d..78a82af7f1 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -815,6 +815,20 @@ void SystemInfoScreen::CreateTabs() { g_OSD.Show(OSDType::MESSAGE_SUCCESS, si->T("Success")); return UI::EVENT_DONE; }); + internals->Add(new ItemHeader(si->T("Progress tests"))); + internals->Add(new Choice(si->T("30%")))->OnClick.Add([&](UI::EventParams &) { + g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30); + return UI::EVENT_DONE; + }); + internals->Add(new Choice(si->T("100%")))->OnClick.Add([&](UI::EventParams &) { + g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100); + return UI::EVENT_DONE; + }); + internals->Add(new Choice(si->T("Clear")))->OnClick.Add([&](UI::EventParams &) { + g_OSD.RemoveProgressBar("testprogress", 0.25f); + return UI::EVENT_DONE; + }); + } void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) { diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 0fe24906ba..c5f5342b85 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -77,19 +77,63 @@ static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, B dc.DrawTextShadowRect(entry.text.c_str(), bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER); } +static void MeasureOSDProgressBar(UIContext &dc, const OnScreenDisplay::ProgressBar &bar, float *width, float *height) { + *height = 36; + *width = 450.0f; +} + +static void RenderOSDProgressBar(UIContext &dc, const OnScreenDisplay::ProgressBar &entry, Bounds bounds, int align, float alpha) { + UI::Drawable background = UI::Drawable(colorAlpha(0x806050, alpha)); + UI::Drawable progressBackground = UI::Drawable(colorAlpha(0xa08070, alpha)); + + uint32_t foreGround = whiteAlpha(alpha); + + Bounds shadowBounds = bounds.Expand(10.0f); + + dc.Draw()->DrawImage4Grid(dc.theme->dropShadow4Grid, shadowBounds.x, shadowBounds.y + 4.0f, shadowBounds.x2(), shadowBounds.y2(), alphaMul(0xFF000000, 0.9f * alpha), 1.0f); + + float ratio = (float)(entry.progress - entry.minValue) / (float)entry.maxValue; + + Bounds boundLeft = bounds; + Bounds boundRight = bounds; + + boundLeft.w *= ratio; + boundRight.x += ratio * boundRight.w; + boundRight.w *= (1.0f - ratio); + + dc.FillRect(progressBackground, boundLeft); + dc.FillRect(background, boundRight); + dc.SetFontStyle(dc.theme->uiFont); + + dc.DrawTextShadowRect(entry.message.c_str(), bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER); +} + void OnScreenMessagesView::Draw(UIContext &dc) { if (!g_Config.bShowOnScreenMessages) { return; } + double now = time_now_d(); + // Get height float w, h; dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, "Wg", &w, &h); float y = 10.0f; // Then draw them all. + const std::vector bars = g_OSD.ProgressBars(); + for (auto &bar : bars) { + float tw, th; + MeasureOSDProgressBar(dc, bar, &tw, &th); + Bounds b(0.0f, y, tw, th); + b.x = (bounds_.w - b.w) * 0.5f; + + float alpha = Clamp((float)(bar.endTime - now) * 4.0f, 0.0f, 1.0f); + RenderOSDProgressBar(dc, bar, b, 0, alpha); + y += (b.h + 4.0f) * alpha; // including alpha here gets us smooth animations. + } + const std::vector entries = g_OSD.Entries(); - double now = time_now_d(); for (auto iter = entries.begin(); iter != entries.end(); ++iter) { dc.SetFontScale(1.0f, 1.0f); // Messages that are wider than the screen are left-aligned instead of centered.