From 7659a70c15108299ef5084cde81c9326efa1f083 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 8 Apr 2026 00:13:02 +1000 Subject: [PATCH] Common: Get rid of multiple virtuals in ProgressCallback Do all the checking for value changes in the base class, and add a mutator that allows all state to be changed atomically. Reduces flicker when changing state. --- src/common/progress_callback.cpp | 93 +++++++++- src/common/progress_callback.h | 35 ++-- src/core/fullscreenui.h | 19 +- src/core/fullscreenui_settings.cpp | 46 ++++- src/core/fullscreenui_strings.h | 1 + src/core/fullscreenui_widgets.cpp | 154 ++++------------- src/core/game_list.cpp | 8 +- src/duckstation-qt/coverdownloadwindow.cpp | 17 ++ src/duckstation-qt/coverdownloadwindow.ui | 2 +- src/duckstation-qt/gamelistrefreshthread.cpp | 54 +----- src/duckstation-qt/gamelistrefreshthread.h | 14 +- src/duckstation-qt/qtprogresscallback.cpp | 173 ++++++------------- src/duckstation-qt/qtprogresscallback.h | 21 +-- src/updater/cocoa_progress_callback.h | 13 +- src/updater/cocoa_progress_callback.mm | 62 ++----- src/updater/win32_progress_callback.cpp | 39 +---- src/updater/win32_progress_callback.h | 11 +- src/util/cd_image_chd.cpp | 6 +- 18 files changed, 313 insertions(+), 455 deletions(-) diff --git a/src/common/progress_callback.cpp b/src/common/progress_callback.cpp index 864514dee..234c646b0 100644 --- a/src/common/progress_callback.cpp +++ b/src/common/progress_callback.cpp @@ -9,6 +9,8 @@ #include #include +LOG_CHANNEL(Host); + static ProgressCallback s_nullProgressCallbacks; ProgressCallback* ProgressCallback::NullProgressCallback = &s_nullProgressCallbacks; @@ -41,13 +43,69 @@ void ProgressCallback::PopState() static_cast(((float)m_progress_value / (float)m_progress_range) * (float)m_saved_state->progress_range) : m_saved_state->progress_value; + const StateChange state_change = static_cast( + ((m_status_text != m_saved_state->status_text) ? STATE_CHANGE_STATUS_TEXT : STATE_CHANGE_NONE) | + ((m_progress_range != m_saved_state->progress_range || m_progress_value != new_progress_value) ? + STATE_CHANGE_PROGRESS : + STATE_CHANGE_NONE) | + ((m_cancellable != m_saved_state->cancellable) ? STATE_CHANGE_CANCELLABLE : STATE_CHANGE_NONE)); + m_cancellable = m_saved_state->cancellable; m_status_text = std::move(m_saved_state->status_text); + m_base_progress_value = m_saved_state->base_progress_value; m_progress_range = m_saved_state->progress_range; m_progress_value = new_progress_value; - - m_base_progress_value = m_saved_state->base_progress_value; m_saved_state = std::move(m_saved_state->next_saved_state); + + if (state_change != STATE_CHANGE_NONE) + StateChanged(state_change); +} + +void ProgressCallback::SetState(u32 value, u32 range) +{ + SetState(m_status_text, value, range, m_cancellable); +} + +void ProgressCallback::SetState(std::string_view status_text, u32 value, u32 range) +{ + SetState(status_text, value, range, m_cancellable); +} + +void ProgressCallback::SetState(std::string_view status_text, u32 value, u32 range, bool cancellable) +{ + StateChange state_change = STATE_CHANGE_NONE; + if (m_cancellable != cancellable) + { + m_cancellable = cancellable; + state_change = static_cast(state_change | STATE_CHANGE_CANCELLABLE); + } + if (m_status_text != status_text) + { + m_status_text = status_text; + state_change = static_cast(state_change | STATE_CHANGE_STATUS_TEXT); + } + + const u32 prev_range = m_progress_range; + const u32 prev_value = m_progress_value; + + if (m_saved_state) + { + // impose the previous range on this range + m_progress_range = m_saved_state->progress_range * range; + m_base_progress_value = m_progress_value = m_saved_state->progress_value * range; + } + else + { + m_progress_range = range; + m_progress_value = value; + m_base_progress_value = 0; + } + + if (range != prev_range || value != prev_value) + state_change = static_cast(state_change | STATE_CHANGE_PROGRESS); + + if (state_change != STATE_CHANGE_NONE) + StateChanged(state_change); } bool ProgressCallback::IsCancellable() const @@ -66,21 +124,39 @@ void ProgressCallback::SetTitle(const std::string_view title) void ProgressCallback::SetStatusText(const std::string_view text) { + if (m_status_text == text) + return; + + INFO_LOG("Status: {}", text); + m_status_text.assign(text); + StateChanged(STATE_CHANGE_STATUS_TEXT); } void ProgressCallback::SetCancellable(bool cancellable) { + if (m_cancellable == cancellable) + return; + m_cancellable = cancellable; + CancellableChanged(); } void ProgressCallback::SetProgressValue(u32 value) { - m_progress_value = m_base_progress_value + value; + const u32 new_value = m_base_progress_value + value; + if (m_progress_value != new_value) + { + m_progress_value = new_value; + StateChanged(STATE_CHANGE_PROGRESS); + } } void ProgressCallback::SetProgressRange(u32 range) { + const u32 prev_range = m_progress_range; + const u32 prev_value = m_progress_value; + if (m_saved_state) { // impose the previous range on this range @@ -93,6 +169,9 @@ void ProgressCallback::SetProgressRange(u32 range) m_progress_value = 0; m_base_progress_value = 0; } + + if (m_progress_range != prev_range || m_progress_value != prev_value) + StateChanged(STATE_CHANGE_PROGRESS); } void ProgressCallback::IncrementProgressValue() @@ -100,6 +179,14 @@ void ProgressCallback::IncrementProgressValue() SetProgressValue((m_progress_value - m_base_progress_value) + 1); } +void ProgressCallback::StateChanged(StateChange changed) +{ +} + +void ProgressCallback::CancellableChanged() +{ +} + ProgressCallbackWithPrompt::~ProgressCallbackWithPrompt() = default; void ProgressCallbackWithPrompt::AlertPrompt(PromptIcon icon, std::string_view message) diff --git a/src/common/progress_callback.h b/src/common/progress_callback.h index 23d0ca3f6..57159de91 100644 --- a/src/common/progress_callback.h +++ b/src/common/progress_callback.h @@ -25,23 +25,38 @@ class ProgressCallback public: virtual ~ProgressCallback(); - virtual void PushState(); - virtual void PopState(); - + bool IsCancellable() const; + void SetCancellable(bool cancellable); virtual bool IsCancelled() const; - virtual bool IsCancellable() const; - - virtual void SetCancellable(bool cancellable); virtual void SetTitle(const std::string_view title); - virtual void SetStatusText(const std::string_view text); - virtual void SetProgressRange(u32 range); - virtual void SetProgressValue(u32 value); - virtual void IncrementProgressValue(); + + void SetStatusText(const std::string_view text); + + void PushState(); + void PopState(); + + void SetState(u32 value, u32 range); + void SetState(std::string_view status_text, u32 value, u32 range); + void SetState(std::string_view status_text, u32 value, u32 range, bool cancellable); + void SetProgressRange(u32 range); + void SetProgressValue(u32 value); + void IncrementProgressValue(); MAKE_PROGRESS_CALLBACK_FORWARDER(FormatStatusText, SetStatusText); protected: + enum StateChange : u32 + { + STATE_CHANGE_NONE = 0, + STATE_CHANGE_PROGRESS = 1 << 0, + STATE_CHANGE_STATUS_TEXT = 1 << 1, + STATE_CHANGE_CANCELLABLE = 1 << 2, + }; + + virtual void StateChanged(StateChange changed); + virtual void CancellableChanged(); + struct State { std::unique_ptr next_saved_state; diff --git a/src/core/fullscreenui.h b/src/core/fullscreenui.h index d022c6b5d..1b7883243 100644 --- a/src/core/fullscreenui.h +++ b/src/core/fullscreenui.h @@ -55,15 +55,12 @@ public: explicit BackgroundProgressCallback(std::string name); ~BackgroundProgressCallback() override; - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; - void SetCancelled(); -private: - void Redraw(bool force); +protected: + void StateChanged(StateChange changed) override; +private: std::string m_name; int m_last_progress_percent = -1; }; @@ -81,14 +78,10 @@ public: void Close(); - void PushState() override; - void PopState() override; - - void SetCancellable(bool cancellable) override; void SetTitle(const std::string_view title) override; - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; + +protected: + void StateChanged(StateChange changed) override; private: void Redraw(bool force); diff --git a/src/core/fullscreenui_settings.cpp b/src/core/fullscreenui_settings.cpp index 9953ce5a3..8ddf51d1c 100644 --- a/src/core/fullscreenui_settings.cpp +++ b/src/core/fullscreenui_settings.cpp @@ -99,6 +99,7 @@ static void DrawAdvancedSettingsPage(); static void DrawPatchesOrCheatsSettingsPage(bool cheats); static void DrawCoverDownloaderWindow(); +static void SaveCoverDownloaderURLs(); static void DrawAchievementsLoginWindow(); static void StartAchievementsProgressRefresh(); static void StartAchievementsGameIconDownload(); @@ -210,12 +211,15 @@ struct SettingsLocals s8 selected_controller_port = -1; bool settings_changed = false; bool game_settings_changed = false; + bool cover_downloader_urls_loaded = false; + bool cover_downloader_use_serial_names = false; InputBindingDialog input_binding_dialog; }; } // namespace ALIGN_TO_CACHE_LINE static SettingsLocals s_settings_locals; +static char s_cover_downloader_template_urls[512]; } // namespace FullscreenUI @@ -2636,15 +2640,24 @@ void FullscreenUI::DrawGameListSettingsPage() void FullscreenUI::DrawCoverDownloaderWindow() { - static char template_urls[512]; - static bool use_serial_names; - if (!BeginFixedPopupDialog(LayoutScale(LAYOUT_LARGE_POPUP_PADDING), LayoutScale(LAYOUT_LARGE_POPUP_ROUNDING), LayoutScale(1000.0f, 0.0f))) { return; } + if (!s_settings_locals.cover_downloader_urls_loaded) + { + s_settings_locals.cover_downloader_urls_loaded = true; + + const std::vector urls = Core::GetBaseStringListSetting("UI", "CoverDownloaderURL"); + if (!urls.empty()) + { + StringUtil::Strlcpy(s_cover_downloader_template_urls, StringUtil::JoinString(urls, '\n'), + sizeof(s_cover_downloader_template_urls)); + } + } + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f)); ImGui::PushFont(UIStyle.Font, UIStyle.MediumLargeFontSize, UIStyle.NormalFontWeight); @@ -2654,6 +2667,9 @@ void FullscreenUI::DrawCoverDownloaderWindow() FSUI_CSTR("DuckStation can automatically download covers for games which do not currently have a cover set. We " "do not host any cover images, the user must provide their own source for images.")); ImGui::NewLine(); + ImGui::TextWrapped("%s", FSUI_CSTR("Depending on your jurisdiction, game covers may be copyrighted. You are only " + "authorized to use this tool with your own servers and images.")); + ImGui::NewLine(); ImGui::TextWrapped("%s", FSUI_CSTR("In the form below, specify the URLs to download covers from, with one template URL " "per line. The following variables are available:")); @@ -2664,18 +2680,18 @@ void FullscreenUI::DrawCoverDownloaderWindow() ImGui::TextWrapped("%s", FSUI_CSTR("Example: https://www.example-not-a-real-domain.com/covers/${serial}.jpg")); ImGui::NewLine(); - ImGui::InputTextMultiline("##templates", template_urls, sizeof(template_urls), + ImGui::InputTextMultiline("##templates", s_cover_downloader_template_urls, sizeof(s_cover_downloader_template_urls), ImVec2(ImGui::GetCurrentWindow()->WorkRect.GetWidth(), LayoutScale(175.0f))); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(5.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(2.0f, 2.0f)); - ImGui::Checkbox(FSUI_CSTR("Save as Serial File Names"), &use_serial_names); + ImGui::Checkbox(FSUI_CSTR("Save as Serial File Names"), &s_settings_locals.cover_downloader_use_serial_names); ImGui::PopStyleVar(1); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - const bool download_enabled = (std::strlen(template_urls) > 0); + const bool download_enabled = (std::strlen(s_cover_downloader_template_urls) > 0); BeginHorizontalMenuButtons(2, 200.0f); ResetFocusHere(); @@ -2684,8 +2700,9 @@ void FullscreenUI::DrawCoverDownloaderWindow() { // TODO: Remove release once using move_only_function std::unique_ptr progress = OpenModalProgressDialog(FSUI_STR("Cover Downloader"), 1000.0f); - Host::QueueAsyncTask([progress = progress.release(), urls = StringUtil::SplitNewString(template_urls, '\n'), - use_serial_names = use_serial_names]() { + Host::QueueAsyncTask([progress = progress.release(), + urls = StringUtil::SplitNewString(s_cover_downloader_template_urls, '\n'), + use_serial_names = s_settings_locals.cover_downloader_use_serial_names]() { Error error; if (!GameList::DownloadCovers( urls, use_serial_names, progress, &error, [](const GameList::Entry* entry, std::string save_path) { @@ -2706,7 +2723,10 @@ void FullscreenUI::DrawCoverDownloaderWindow() Host::RunOnCoreThread([]() { VideoThread::RunOnThread([]() { if (IsFixedPopupDialogOpen(COVER_DOWNLOADER_DIALOG_NAME)) + { + SaveCoverDownloaderURLs(); CloseFixedPopupDialog(); + } }); }); } @@ -2716,7 +2736,10 @@ void FullscreenUI::DrawCoverDownloaderWindow() } if (HorizontalMenuButton(FSUI_ICONSTR(ICON_FA_XMARK, "Close"))) + { + SaveCoverDownloaderURLs(); CloseFixedPopupDialog(); + } EndHorizontalMenuButtons(); @@ -2726,6 +2749,13 @@ void FullscreenUI::DrawCoverDownloaderWindow() EndFixedPopupDialog(); } +void FullscreenUI::SaveCoverDownloaderURLs() +{ + const std::vector urls = StringUtil::SplitNewString(s_cover_downloader_template_urls, '\n'); + if (urls != Core::GetBaseStringListSetting("UI", "CoverDownloaderURL")) + Core::SetBaseStringListSettingValue("UI", "CoverDownloaderURL", urls); +} + void FullscreenUI::DrawBIOSSettingsPage() { static constexpr const std::array config_keys = {"", "PathNTSCJ", "PathNTSCU", "PathPAL"}; diff --git a/src/core/fullscreenui_strings.h b/src/core/fullscreenui_strings.h index 32403568c..011d4c556 100644 --- a/src/core/fullscreenui_strings.h +++ b/src/core/fullscreenui_strings.h @@ -240,6 +240,7 @@ TRANSLATE_NOOP("FullscreenUI", "Deinterlacing Mode"); TRANSLATE_NOOP("FullscreenUI", "Delete And Boot"); TRANSLATE_NOOP("FullscreenUI", "Delete Save"); TRANSLATE_NOOP("FullscreenUI", "Delete State"); +TRANSLATE_NOOP("FullscreenUI", "Depending on your jurisdiction, game covers may be copyrighted. You are only authorized to use this tool with your own servers and images."); TRANSLATE_NOOP("FullscreenUI", "Depth Clear Threshold"); TRANSLATE_NOOP("FullscreenUI", "Depth Test Transparent Polygons"); TRANSLATE_NOOP("FullscreenUI", "Desktop Mode"); diff --git a/src/core/fullscreenui_widgets.cpp b/src/core/fullscreenui_widgets.cpp index b2d498894..a633868d2 100644 --- a/src/core/fullscreenui_widgets.cpp +++ b/src/core/fullscreenui_widgets.cpp @@ -307,15 +307,14 @@ private: ProgressCallbackImpl(); ~ProgressCallbackImpl() override; - void SetStatusText(std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; - void SetCancellable(bool cancellable) override; bool IsCancelled() const override; void AlertPrompt(PromptIcon icon, std::string_view message) override; bool ConfirmPrompt(PromptIcon icon, std::string_view message, std::string_view yes_text = {}, std::string_view no_text = {}) override; + + protected: + void StateChanged(StateChange changed) override; }; std::string m_status_text; @@ -5192,58 +5191,45 @@ FullscreenUI::ProgressDialog::ProgressCallbackImpl::~ProgressCallbackImpl() Host::RunOnCoreThread([]() { VideoThread::RunOnThread(close_cb); }); } -void FullscreenUI::ProgressDialog::ProgressCallbackImpl::SetStatusText(std::string_view text) -{ - Host::RunOnCoreThread([text = std::string(text)]() mutable { - VideoThread::RunOnThread([text = std::move(text)]() mutable { - if (!s_state.progress_dialog.IsOpen()) - return; - - s_state.progress_dialog.m_status_text = std::move(text); - }); - }); -} - -void FullscreenUI::ProgressDialog::ProgressCallbackImpl::SetProgressRange(u32 range) +void FullscreenUI::ProgressDialog::ProgressCallbackImpl::StateChanged(StateChange changed) { - ProgressCallback::SetProgressRange(range); - - Host::RunOnCoreThread([range]() { - VideoThread::RunOnThread([range]() { - if (!s_state.progress_dialog.IsOpen()) - return; + if (changed & STATE_CHANGE_STATUS_TEXT) + { + Host::RunOnCoreThread([text = m_status_text, range = m_progress_range, value = m_progress_value]() mutable { + VideoThread::RunOnThread([text = std::move(text), range, value]() mutable { + if (!s_state.progress_dialog.IsOpen()) + return; - s_state.progress_dialog.m_progress_range = range; + s_state.progress_dialog.m_progress_range = range; + s_state.progress_dialog.m_progress_value = value; + s_state.progress_dialog.m_status_text = std::move(text); + }); }); - }); -} - -void FullscreenUI::ProgressDialog::ProgressCallbackImpl::SetProgressValue(u32 value) -{ - ProgressCallback::SetProgressValue(value); - - Host::RunOnCoreThread([value]() { - VideoThread::RunOnThread([value]() { - if (!s_state.progress_dialog.IsOpen()) - return; + } + else if (changed & STATE_CHANGE_PROGRESS) + { + Host::RunOnCoreThread([range = m_progress_range, value = m_progress_value]() { + VideoThread::RunOnThread([range, value]() { + if (!s_state.progress_dialog.IsOpen()) + return; - s_state.progress_dialog.m_progress_value = value; + s_state.progress_dialog.m_progress_range = range; + s_state.progress_dialog.m_progress_value = value; + }); }); - }); -} - -void FullscreenUI::ProgressDialog::ProgressCallbackImpl::SetCancellable(bool cancellable) -{ - ProgressCallback::SetCancellable(cancellable); + } - Host::RunOnCoreThread([cancellable]() { - VideoThread::RunOnThread([cancellable]() { - if (!s_state.progress_dialog.IsOpen()) - return; + if (changed & STATE_CHANGE_CANCELLABLE) + { + Host::RunOnCoreThread([cancellable = m_cancellable]() { + VideoThread::RunOnThread([cancellable]() { + if (!s_state.progress_dialog.IsOpen()) + return; - s_state.progress_dialog.m_user_closeable = cancellable; + s_state.progress_dialog.m_user_closeable = cancellable; + }); }); - }); + } } bool FullscreenUI::ProgressDialog::ProgressCallbackImpl::IsCancelled() const @@ -5518,37 +5504,11 @@ FullscreenUI::BackgroundProgressCallback::~BackgroundProgressCallback() CloseBackgroundProgressDialog(m_name.c_str()); } -void FullscreenUI::BackgroundProgressCallback::SetStatusText(const std::string_view text) -{ - ProgressCallback::SetStatusText(text); - Redraw(true); -} - -void FullscreenUI::BackgroundProgressCallback::SetProgressRange(u32 range) -{ - const u32 last_range = m_progress_range; - - ProgressCallback::SetProgressRange(range); - - if (m_progress_range != last_range) - Redraw(false); -} - -void FullscreenUI::BackgroundProgressCallback::SetProgressValue(u32 value) -{ - const u32 last_value = m_progress_value; - - ProgressCallback::SetProgressValue(value); - - if (m_progress_value != last_value) - Redraw(false); -} - -void FullscreenUI::BackgroundProgressCallback::Redraw(bool force) +void FullscreenUI::BackgroundProgressCallback::StateChanged(StateChange changed) { const int percent = static_cast((static_cast(m_progress_value) / static_cast(m_progress_range)) * 100.0f); - if (percent == m_last_progress_percent && !force) + if (percent == m_last_progress_percent && !(changed & STATE_CHANGE_STATUS_TEXT)) return; m_last_progress_percent = percent; @@ -5893,23 +5853,6 @@ void FullscreenUI::LoadingScreenProgressCallback::Close() m_last_progress_percent = -1; } -void FullscreenUI::LoadingScreenProgressCallback::PushState() -{ - ProgressCallback::PushState(); -} - -void FullscreenUI::LoadingScreenProgressCallback::PopState() -{ - ProgressCallback::PopState(); - Redraw(true); -} - -void FullscreenUI::LoadingScreenProgressCallback::SetCancellable(bool cancellable) -{ - ProgressCallback::SetCancellable(cancellable); - Redraw(true); -} - void FullscreenUI::LoadingScreenProgressCallback::SetTitle(const std::string_view title) { ProgressCallback::SetTitle(title); @@ -5917,30 +5860,9 @@ void FullscreenUI::LoadingScreenProgressCallback::SetTitle(const std::string_vie Redraw(true); } -void FullscreenUI::LoadingScreenProgressCallback::SetStatusText(const std::string_view text) +void FullscreenUI::LoadingScreenProgressCallback::StateChanged(StateChange changed) { - ProgressCallback::SetStatusText(text); - Redraw(true); -} - -void FullscreenUI::LoadingScreenProgressCallback::SetProgressRange(u32 range) -{ - u32 last_range = m_progress_range; - - ProgressCallback::SetProgressRange(range); - - if (m_progress_range != last_range) - Redraw(false); -} - -void FullscreenUI::LoadingScreenProgressCallback::SetProgressValue(u32 value) -{ - u32 lastValue = m_progress_value; - - ProgressCallback::SetProgressValue(value); - - if (m_progress_value != lastValue) - Redraw(false); + Redraw((changed & (STATE_CHANGE_STATUS_TEXT | STATE_CHANGE_CANCELLABLE)) != 0); } void FullscreenUI::LoadingScreenProgressCallback::Redraw(bool force) diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index 267331e6c..d5b25ff96 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -579,8 +579,7 @@ void GameList::ScanDirectory(const std::string& path, bool recursive, bool only_ return; progress->PushState(); - progress->SetProgressRange(static_cast(files.size())); - progress->SetProgressValue(0); + progress->SetState(0, static_cast(files.size())); u32 files_scanned = 0; for (FILESYSTEM_FIND_DATA& ffd : files) @@ -1141,8 +1140,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback* if (!dirs.empty() || !recursive_dirs.empty()) { - progress->SetProgressRange(static_cast(dirs.size() + recursive_dirs.size())); - progress->SetProgressValue(0); + progress->SetState(0, static_cast(dirs.size() + recursive_dirs.size())); // we manually count it here, because otherwise pop state updates it itself int directory_counter = 0; @@ -1875,7 +1873,7 @@ bool GameList::DownloadCovers(const std::vector& url_templates, boo } progress->SetCancellable(true); - progress->SetProgressRange(static_cast(download_urls.size())); + progress->SetState(0, static_cast(download_urls.size())); for (auto& [entry_path, url] : download_urls) { diff --git a/src/duckstation-qt/coverdownloadwindow.cpp b/src/duckstation-qt/coverdownloadwindow.cpp index 63ccc7da0..27272b8d8 100644 --- a/src/duckstation-qt/coverdownloadwindow.cpp +++ b/src/duckstation-qt/coverdownloadwindow.cpp @@ -7,9 +7,11 @@ #include "qthost.h" #include "qtprogresscallback.h" +#include "core/core.h" #include "core/game_list.h" #include "common/error.h" +#include "common/string_util.h" #include "moc_coverdownloadwindow.cpp" @@ -24,12 +26,27 @@ CoverDownloadWindow::CoverDownloadWindow() : QWidget() connect(m_ui.start, &QPushButton::clicked, this, &CoverDownloadWindow::onStartClicked); connect(m_ui.close, &QPushButton::clicked, this, &CoverDownloadWindow::close); connect(m_ui.urls, &QTextEdit::textChanged, this, &CoverDownloadWindow::updateEnabled); + + const std::vector urls = Core::GetBaseStringListSetting("UI", "CoverDownloaderURL"); + if (!urls.empty()) + m_ui.urls->setPlainText(QString::fromStdString(StringUtil::JoinString(urls, "\n"))); } CoverDownloadWindow::~CoverDownloadWindow() = default; void CoverDownloadWindow::closeEvent(QCloseEvent* ev) { + std::vector urls; + for (const QString& str : m_ui.urls->toPlainText().split(QChar('\n'))) + { + std::string url = str.toStdString(); + StringUtil::StripWhitespace(&url); + if (!url.empty()) + urls.push_back(std::move(url)); + } + if (urls != Core::GetBaseStringListSetting("UI", "CoverDownloaderURL")) + Core::SetBaseStringListSettingValue("UI", "CoverDownloaderURL", urls); + QtUtils::SaveWindowGeometry(this); QWidget::closeEvent(ev); if (m_task) diff --git a/src/duckstation-qt/coverdownloadwindow.ui b/src/duckstation-qt/coverdownloadwindow.ui index becaa8214..4429889fb 100644 --- a/src/duckstation-qt/coverdownloadwindow.ui +++ b/src/duckstation-qt/coverdownloadwindow.ui @@ -50,7 +50,7 @@ - <html><head/><body><p>In the box below, specify the URLs to download covers from, with one template URL per line. The following variables are available:</p><p><span style=" font-style:italic;">${title}:</span> Title of the game.<br/><span style=" font-style:italic;">${savetitle}:</span> Title of the game including the region.<br/><span style=" font-style:italic;">${localizedtitle}:</span> Localized (native language) title of the game.<br/><span style=" font-style:italic;">${filetitle}:</span> Name component of the game's filename.<br/><span style=" font-style:italic;">${serial}:</span> Serial of the game.</p><p><span style=" font-weight:700;">Example:</span> https://www.example-not-a-real-domain.com/covers/${serial}.jpg</p></body></html> + <html><head/><body><p>Depending on your jurisdiction, <span style=" font-weight:700;">game covers may be copyrighted</span>. You are only authorized to use this tool with <span style=" font-weight:700;">your own servers and images</span>.</p><p>In the box below, specify the URLs to download covers from, with one template URL per line. The following variables are available:</p><p><span style=" font-style:italic;">${title}:</span> Title of the game.<br/><span style=" font-style:italic;">${savetitle}:</span> Title of the game including the region.<br/><span style=" font-style:italic;">${localizedtitle}:</span> Localized (native language) title of the game.<br/><span style=" font-style:italic;">${filetitle}:</span> Name component of the game's filename.<br/><span style=" font-style:italic;">${serial}:</span> Serial of the game.</p><p><span style=" font-weight:700;">Example:</span> https://www.example-not-a-real-domain.com/covers/${serial}.jpg</p></body></html> true diff --git a/src/duckstation-qt/gamelistrefreshthread.cpp b/src/duckstation-qt/gamelistrefreshthread.cpp index 1881d52d5..237c91095 100644 --- a/src/duckstation-qt/gamelistrefreshthread.cpp +++ b/src/duckstation-qt/gamelistrefreshthread.cpp @@ -32,55 +32,13 @@ void GameListRefreshThread::run() emit refreshComplete(); } -void GameListRefreshThread::PushState() +void GameListRefreshThread::StateChanged(StateChange changed) { - ProgressCallback::PushState(); -} - -void GameListRefreshThread::PopState() -{ - ProgressCallback::PopState(); - - if (static_cast(m_progress_range) == m_last_range && static_cast(m_progress_value) == m_last_value) - return; - - m_last_range = static_cast(m_progress_range); - m_last_value = static_cast(m_progress_value); - fireUpdate(); -} - -void GameListRefreshThread::SetStatusText(const std::string_view text) -{ - const QString new_text = QtUtils::StringViewToQString(text); - if (new_text == m_status_text) - return; - - m_status_text = new_text; - fireUpdate(); -} - -void GameListRefreshThread::SetProgressRange(u32 range) -{ - ProgressCallback::SetProgressRange(range); - if (static_cast(m_progress_range) == m_last_range) + if (changed & STATE_CHANGE_STATUS_TEXT) + m_qstatus_text = QtUtils::StringViewToQString(m_status_text); + else if (!(changed & (STATE_CHANGE_PROGRESS | STATE_CHANGE_STATUS_TEXT))) return; - m_last_range = static_cast(m_progress_range); - fireUpdate(); -} - -void GameListRefreshThread::SetProgressValue(u32 value) -{ - ProgressCallback::SetProgressValue(value); - if (static_cast(m_progress_value) == m_last_value) - return; - - m_last_value = static_cast(m_progress_value); - fireUpdate(); -} - -void GameListRefreshThread::fireUpdate() -{ - emit refreshProgress(m_status_text, m_last_value, m_last_range, static_cast(GameList::GetEntryCount()), - static_cast(m_start_time.GetTimeSeconds())); + emit refreshProgress(m_qstatus_text, static_cast(m_progress_value), static_cast(m_progress_range), + static_cast(GameList::GetEntryCount()), static_cast(m_start_time.GetTimeSeconds())); } diff --git a/src/duckstation-qt/gamelistrefreshthread.h b/src/duckstation-qt/gamelistrefreshthread.h index d84575360..1fbc1c652 100644 --- a/src/duckstation-qt/gamelistrefreshthread.h +++ b/src/duckstation-qt/gamelistrefreshthread.h @@ -24,20 +24,10 @@ Q_SIGNALS: protected: void run() final; + void StateChanged(StateChange changed) override; private: - void PushState() override; - void PopState() override; - - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; - - void fireUpdate(); - Timer m_start_time; - QString m_status_text; - int m_last_range = 1; - int m_last_value = 0; + QString m_qstatus_text; bool m_invalidate_cache; }; diff --git a/src/duckstation-qt/qtprogresscallback.cpp b/src/duckstation-qt/qtprogresscallback.cpp index 8f269373d..e51ce0040 100644 --- a/src/duckstation-qt/qtprogresscallback.cpp +++ b/src/duckstation-qt/qtprogresscallback.cpp @@ -6,8 +6,8 @@ #include "qtutils.h" #include "common/assert.h" -#include "common/log.h" #include "common/small_string.h" +#include "common/log.h" #include #include @@ -20,8 +20,6 @@ #include "moc_qtprogresscallback.cpp" -LOG_CHANNEL(Host); - QtProgressCallback::QtProgressCallback(QObject* parent /* = nullptr */) : QObject(parent) { } @@ -38,32 +36,12 @@ void QtProgressCallback::SetTitle(const std::string_view title) emit titleUpdated(QtUtils::StringViewToQString(title)); } -void QtProgressCallback::SetStatusText(const std::string_view text) +void QtProgressCallback::StateChanged(StateChange changed) { - ProgressCallback::SetStatusText(text); - emit statusTextUpdated(QtUtils::StringViewToQString(text)); - if (!text.empty()) - INFO_LOG(text); -} - -void QtProgressCallback::SetProgressRange(u32 range) -{ - const u32 prev_range = m_progress_range; - ProgressCallback::SetProgressRange(range); - if (m_progress_range == prev_range) - return; - - emit progressRangeUpdated(0, static_cast(m_progress_range)); -} - -void QtProgressCallback::SetProgressValue(u32 value) -{ - const u32 prev_value = m_progress_value; - ProgressCallback::SetProgressValue(value); - if (m_progress_value == prev_value) - return; - - emit progressValueUpdated(static_cast(m_progress_value)); + if (changed & STATE_CHANGE_STATUS_TEXT) + emit statusTextUpdated(QtUtils::StringViewToQString(m_status_text)); + if (changed & STATE_CHANGE_PROGRESS) + emit progressUpdated(static_cast(m_progress_value), static_cast(m_progress_range)); } void QtProgressCallback::connectWidgets(QLabel* const status_label, QProgressBar* const progress_bar, @@ -73,8 +51,11 @@ void QtProgressCallback::connectWidgets(QLabel* const status_label, QProgressBar connect(this, &QtProgressCallback::statusTextUpdated, status_label, &QLabel::setText); if (progress_bar) { - connect(this, &QtProgressCallback::progressRangeUpdated, progress_bar, &QProgressBar::setRange); - connect(this, &QtProgressCallback::progressValueUpdated, progress_bar, &QProgressBar::setValue); + connect(this, &QtProgressCallback::progressUpdated, progress_bar, [progress_bar](int value, int range) { + // qt checks if the value has changed + progress_bar->setMaximum(range); + progress_bar->setValue(value); + }); } if (cancel_button) { @@ -101,32 +82,12 @@ void QtAsyncTaskWithProgress::SetTitle(const std::string_view title) emit titleUpdated(QtUtils::StringViewToQString(title)); } -void QtAsyncTaskWithProgress::SetStatusText(const std::string_view text) +void QtAsyncTaskWithProgress::StateChanged(StateChange changed) { - ProgressCallback::SetStatusText(text); - emit statusTextUpdated(QtUtils::StringViewToQString(text)); - if (!text.empty()) - INFO_LOG(text); -} - -void QtAsyncTaskWithProgress::SetProgressRange(u32 range) -{ - const u32 prev_range = m_progress_range; - ProgressCallback::SetProgressRange(range); - if (m_progress_range == prev_range) - return; - - emit progressRangeUpdated(0, static_cast(m_progress_range)); -} - -void QtAsyncTaskWithProgress::SetProgressValue(u32 value) -{ - const u32 prev_value = m_progress_value; - ProgressCallback::SetProgressValue(value); - if (m_progress_value == prev_value) - return; - - emit progressValueUpdated(static_cast(m_progress_value)); + if (changed & STATE_CHANGE_STATUS_TEXT) + emit statusTextUpdated(QtUtils::StringViewToQString(m_status_text)); + if (changed & STATE_CHANGE_PROGRESS) + emit progressUpdated(static_cast(m_progress_value), static_cast(m_progress_range)); } void QtAsyncTaskWithProgress::connectWidgets(QLabel* const status_label, QProgressBar* const progress_bar, @@ -136,8 +97,11 @@ void QtAsyncTaskWithProgress::connectWidgets(QLabel* const status_label, QProgre connect(this, &QtAsyncTaskWithProgress::statusTextUpdated, status_label, &QLabel::setText); if (progress_bar) { - connect(this, &QtAsyncTaskWithProgress::progressRangeUpdated, progress_bar, &QProgressBar::setRange); - connect(this, &QtAsyncTaskWithProgress::progressValueUpdated, progress_bar, &QProgressBar::setValue); + connect(this, &QtAsyncTaskWithProgress::progressUpdated, progress_bar, [progress_bar](int value, int range) { + // qt checks if the value has changed + progress_bar->setMaximum(range); + progress_bar->setValue(value); + }); } if (cancel_button) { @@ -374,19 +338,6 @@ bool QtAsyncTaskWithProgressDialog::IsCancelled() const return m_ts_cancelled.load(std::memory_order_acquire); } -void QtAsyncTaskWithProgressDialog::SetCancellable(bool cancellable) -{ - if (m_cancellable == cancellable) - return; - - ProgressCallback::SetCancellable(cancellable); - - Host::RunOnUIThread([this, cancellable]() { - if (m_dialog) - m_dialog->setCancellable(cancellable); - }); -} - void QtAsyncTaskWithProgressDialog::SetTitle(const std::string_view title) { Host::RunOnUIThread([this, title = QtUtils::StringViewToQString(title)]() { @@ -395,66 +346,42 @@ void QtAsyncTaskWithProgressDialog::SetTitle(const std::string_view title) }); } -void QtAsyncTaskWithProgressDialog::SetStatusText(const std::string_view text) +void QtAsyncTaskWithProgressDialog::StateChanged(StateChange changed) { - if (m_status_text == text) - return; - - ProgressCallback::SetStatusText(text); - if (m_shown) + if (changed & (STATE_CHANGE_STATUS_TEXT | STATE_CHANGE_PROGRESS)) { - Host::RunOnUIThread([this, text = QtUtils::StringViewToQString(text)]() { - if (m_dialog) - m_dialog->m_status_label->setText(text); - }); - } - else - { - CheckForDelayedShow(); - } - - if (!text.empty()) - INFO_LOG(text); -} - -void QtAsyncTaskWithProgressDialog::SetProgressRange(u32 range) -{ - const u32 prev_range = m_progress_range; - ProgressCallback::SetProgressRange(range); - if (m_progress_range == prev_range) - return; - - if (m_shown) - { - Host::RunOnUIThread([this, range = static_cast(m_progress_range)]() { - if (m_dialog) - m_dialog->m_progress_bar->setRange(0, range); - }); - } - else - { - CheckForDelayedShow(); + if (m_shown) + { + if (changed & STATE_CHANGE_STATUS_TEXT) + { + Host::RunOnUIThread([this, text = QtUtils::StringViewToQString(m_status_text)]() { + if (m_dialog) + m_dialog->m_status_label->setText(text); + }); + } + if (changed & STATE_CHANGE_PROGRESS) + { + Host::RunOnUIThread( + [this, value = static_cast(m_progress_value), range = static_cast(m_progress_range)]() { + if (m_dialog) + m_dialog->m_progress_bar->setRange(0, range); + if (m_dialog) + m_dialog->m_progress_bar->setValue(value); + }); + } + } + else + { + CheckForDelayedShow(); + } } -} - -void QtAsyncTaskWithProgressDialog::SetProgressValue(u32 value) -{ - const u32 prev_value = m_progress_value; - ProgressCallback::SetProgressValue(value); - if (m_progress_value == prev_value) - return; - - if (m_shown) + if (changed & STATE_CHANGE_CANCELLABLE) { - Host::RunOnUIThread([this, value = static_cast(m_progress_value)]() { + Host::RunOnUIThread([this, cancellable = m_cancellable]() { if (m_dialog) - m_dialog->m_progress_bar->setValue(value); + m_dialog->setCancellable(cancellable); }); } - else - { - CheckForDelayedShow(); - } } static QMessageBox::Icon ConvertPromptIcon(ProgressCallbackWithPrompt::PromptIcon icon) diff --git a/src/duckstation-qt/qtprogresscallback.h b/src/duckstation-qt/qtprogresscallback.h index f7dbc9a1b..3ebf2a7a1 100644 --- a/src/duckstation-qt/qtprogresscallback.h +++ b/src/duckstation-qt/qtprogresscallback.h @@ -27,18 +27,17 @@ public: bool IsCancelled() const override; void SetTitle(const std::string_view title) override; - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; void connectWidgets(QLabel* const status_label, QProgressBar* const progress_bar, QAbstractButton* const cancel_button); +protected: + void StateChanged(StateChange changed) override; + Q_SIGNALS: void titleUpdated(const QString& title); void statusTextUpdated(const QString& status); - void progressRangeUpdated(int min, int max); - void progressValueUpdated(int value); + void progressUpdated(int value, int range); private: std::atomic_bool m_ts_cancelled{false}; @@ -72,16 +71,13 @@ public: Q_SIGNALS: void titleUpdated(const QString& title); void statusTextUpdated(const QString& status); - void progressRangeUpdated(int min, int max); - void progressValueUpdated(int value); + void progressUpdated(int value, int range); void completed(); protected: bool IsCancelled() const override; void SetTitle(const std::string_view title) override; - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; + void StateChanged(StateChange changed) override; private: QtAsyncTaskWithProgress(); @@ -158,11 +154,8 @@ private: // progress callback overrides bool IsCancelled() const override; - void SetCancellable(bool cancellable) override; void SetTitle(const std::string_view title) override; - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; + void StateChanged(StateChange changed) override; void AlertPrompt(PromptIcon icon, std::string_view message) override; bool ConfirmPrompt(PromptIcon icon, std::string_view message, std::string_view yes_text = {}, diff --git a/src/updater/cocoa_progress_callback.h b/src/updater/cocoa_progress_callback.h index 3b84fbe7a..c5923465b 100644 --- a/src/updater/cocoa_progress_callback.h +++ b/src/updater/cocoa_progress_callback.h @@ -22,14 +22,7 @@ public: CocoaProgressCallback(); ~CocoaProgressCallback(); - void PushState() override; - void PopState() override; - - void SetCancellable(bool cancellable) override; void SetTitle(const std::string_view title) override; - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; void DisplayError(const std::string_view message) override; void DisplayWarning(const std::string_view message) override; @@ -39,7 +32,10 @@ public: void ModalError(const std::string_view message) override; bool ModalConfirmation(const std::string_view message) override; void ModalInformation(const std::string_view message) override; - + +protected: + void StateChanged(StateChange changed) override; + private: enum : int { @@ -52,7 +48,6 @@ private: bool Create(); void Destroy(); - void UpdateProgress(); void AppendMessage(const std::string_view message); NSWindow* m_window = nil; diff --git a/src/updater/cocoa_progress_callback.mm b/src/updater/cocoa_progress_callback.mm index e9a701a70..be32c692e 100644 --- a/src/updater/cocoa_progress_callback.mm +++ b/src/updater/cocoa_progress_callback.mm @@ -18,22 +18,6 @@ CocoaProgressCallback::~CocoaProgressCallback() Destroy(); } -void CocoaProgressCallback::PushState() -{ - UpdaterProgressCallback::PushState(); -} - -void CocoaProgressCallback::PopState() -{ - UpdaterProgressCallback::PopState(); - UpdateProgress(); -} - -void CocoaProgressCallback::SetCancellable(bool cancellable) -{ - UpdaterProgressCallback::SetCancellable(cancellable); -} - void CocoaProgressCallback::SetTitle(const std::string_view title) { @autoreleasepool { @@ -44,29 +28,6 @@ void CocoaProgressCallback::SetTitle(const std::string_view title) } } -void CocoaProgressCallback::SetStatusText(const std::string_view text) -{ - UpdaterProgressCallback::SetStatusText(text); - @autoreleasepool { - dispatch_async(dispatch_get_main_queue(), [this, text = [CocoaTools::StringViewToNSString(text) retain]]() { - [m_status setStringValue:text]; - [text release]; - }); - } -} - -void CocoaProgressCallback::SetProgressRange(u32 range) -{ - UpdaterProgressCallback::SetProgressRange(range); - UpdateProgress(); -} - -void CocoaProgressCallback::SetProgressValue(u32 value) -{ - UpdaterProgressCallback::SetProgressValue(value); - UpdateProgress(); -} - bool CocoaProgressCallback::Create() { @autoreleasepool @@ -145,12 +106,25 @@ void CocoaProgressCallback::Destroy() m_window = nil; } -void CocoaProgressCallback::UpdateProgress() +void CocoaProgressCallback::StateChanged(StateChange changed) { - const float percent = (static_cast(m_progress_value) / static_cast(m_progress_range)) * 100.0f; - dispatch_async(dispatch_get_main_queue(), [this, percent]() { - [m_progress setDoubleValue:percent]; - }); + if (changed & STATE_CHANGE_STATUS_TEXT) + { + @autoreleasepool { + dispatch_async(dispatch_get_main_queue(), [this, text = [CocoaTools::StringViewToNSString(m_status_text) retain]]() { + [m_status setStringValue:text]; + [text release]; + }); + } + } + + if (changed & STATE_CHANGE_PROGRESS) + { + const float percent = (static_cast(m_progress_value) / static_cast(m_progress_range)) * 100.0f; + dispatch_async(dispatch_get_main_queue(), [this, percent]() { + [m_progress setDoubleValue:percent]; + }); + } } void CocoaProgressCallback::DisplayError(const std::string_view message) diff --git a/src/updater/win32_progress_callback.cpp b/src/updater/win32_progress_callback.cpp index 137501bc9..ee5fc2d85 100644 --- a/src/updater/win32_progress_callback.cpp +++ b/src/updater/win32_progress_callback.cpp @@ -23,46 +23,11 @@ Win32ProgressCallback::~Win32ProgressCallback() Destroy(); } -void Win32ProgressCallback::PushState() -{ - UpdaterProgressCallback::PushState(); -} - -void Win32ProgressCallback::PopState() -{ - UpdaterProgressCallback::PopState(); - Redraw(true); -} - -void Win32ProgressCallback::SetCancellable(bool cancellable) -{ - UpdaterProgressCallback::SetCancellable(cancellable); - Redraw(true); -} - void Win32ProgressCallback::SetTitle(const std::string_view title) { SetWindowText(m_window_hwnd, StringUtil::UTF8StringToWideString(title).c_str()); } -void Win32ProgressCallback::SetStatusText(const std::string_view text) -{ - UpdaterProgressCallback::SetStatusText(text); - Redraw(true); -} - -void Win32ProgressCallback::SetProgressRange(u32 range) -{ - UpdaterProgressCallback::SetProgressRange(range); - Redraw(false); -} - -void Win32ProgressCallback::SetProgressValue(u32 value) -{ - UpdaterProgressCallback::SetProgressValue(value); - Redraw(false); -} - bool Win32ProgressCallback::Create() { static constexpr LPCWSTR CLASS_NAME = L"DSWin32ProgressCallbackWindow"; @@ -152,11 +117,11 @@ void Win32ProgressCallback::PumpMessages() } } -void Win32ProgressCallback::Redraw(bool force) +void Win32ProgressCallback::StateChanged(StateChange changed) { const int percent = static_cast((static_cast(m_progress_value) / static_cast(m_progress_range)) * 100.0f); - if (percent == m_last_progress_percent && !force) + if (percent == m_last_progress_percent && !(changed & STATE_CHANGE_STATUS_TEXT)) { PumpMessages(); return; diff --git a/src/updater/win32_progress_callback.h b/src/updater/win32_progress_callback.h index f17b35788..28e84ecb9 100644 --- a/src/updater/win32_progress_callback.h +++ b/src/updater/win32_progress_callback.h @@ -13,14 +13,7 @@ public: Win32ProgressCallback(HWND parent_hwnd = nullptr); ~Win32ProgressCallback() override; - void PushState() override; - void PopState() override; - - void SetCancellable(bool cancellable) override; void SetTitle(const std::string_view title) override; - void SetStatusText(const std::string_view text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; void DisplayError(const std::string_view message) override; void DisplayWarning(const std::string_view message) override; @@ -31,6 +24,9 @@ public: bool ModalConfirmation(const std::string_view message) override; void ModalInformation(const std::string_view message) override; +protected: + void StateChanged(StateChange changed) override; + private: enum : int { @@ -46,7 +42,6 @@ private: bool Create(); void Destroy(); - void Redraw(bool force); void PumpMessages(); static LRESULT CALLBACK WndProcThunk(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); diff --git a/src/util/cd_image_chd.cpp b/src/util/cd_image_chd.cpp index 7b6cd9b11..2a60b8229 100644 --- a/src/util/cd_image_chd.cpp +++ b/src/util/cd_image_chd.cpp @@ -452,15 +452,13 @@ CDImage::PrecacheResult CDImageCHD::Precache(ProgressCallback* progress, Error* return CDImage::PrecacheResult::Success; progress->SetTitle("Precaching CHD..."); - progress->SetProgressRange(100); + progress->SetState({}, 0, 100); auto callback = [](size_t pos, size_t total, void* param) { constexpr size_t one_mb = 1048576; const u32 total_mb = static_cast((total + (one_mb - 1)) / one_mb); const u32 pos_mb = static_cast((pos + (one_mb - 1)) / one_mb); - static_cast(param)->SetProgressRange(total_mb); - static_cast(param)->SetProgressValue(pos_mb); - static_cast(param)->SetStatusText(TinyString::from_format("{}MB of {}MB", pos_mb, total_mb)); + static_cast(param)->SetState(TinyString::from_format("{}MB of {}MB", pos_mb, total_mb), pos_mb, total_mb); }; if (const chd_error err = chd_precache_progress(m_chd, callback, progress); err != CHDERR_NONE)