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.
pull/3710/head
Stenzek 6 months ago
parent 3004aa35e3
commit 7659a70c15
No known key found for this signature in database

@ -9,6 +9,8 @@
#include <cstdio>
#include <limits>
LOG_CHANNEL(Host);
static ProgressCallback s_nullProgressCallbacks;
ProgressCallback* ProgressCallback::NullProgressCallback = &s_nullProgressCallbacks;
@ -41,13 +43,69 @@ void ProgressCallback::PopState()
static_cast<u32>(((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<StateChange>(
((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<StateChange>(state_change | STATE_CHANGE_CANCELLABLE);
}
if (m_status_text != status_text)
{
m_status_text = status_text;
state_change = static_cast<StateChange>(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<StateChange>(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)

@ -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<State> next_saved_state;

@ -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);

@ -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<std::string> 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<ProgressCallback> 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<std::string> 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"};

@ -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");

@ -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<int>((static_cast<float>(m_progress_value) / static_cast<float>(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)

@ -579,8 +579,7 @@ void GameList::ScanDirectory(const std::string& path, bool recursive, bool only_
return;
progress->PushState();
progress->SetProgressRange(static_cast<u32>(files.size()));
progress->SetProgressValue(0);
progress->SetState(0, static_cast<u32>(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<u32>(dirs.size() + recursive_dirs.size()));
progress->SetProgressValue(0);
progress->SetState(0, static_cast<u32>(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<std::string>& url_templates, boo
}
progress->SetCancellable(true);
progress->SetProgressRange(static_cast<u32>(download_urls.size()));
progress->SetState(0, static_cast<u32>(download_urls.size()));
for (auto& [entry_path, url] : download_urls)
{

@ -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<std::string> 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<std::string> 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)

@ -50,7 +50,7 @@
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;In the box below, specify the URLs to download covers from, with one template URL per line. The following variables are available:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${title}:&lt;/span&gt; Title of the game.&lt;br/&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${savetitle}:&lt;/span&gt; Title of the game including the region.&lt;br/&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${localizedtitle}:&lt;/span&gt; Localized (native language) title of the game.&lt;br/&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${filetitle}:&lt;/span&gt; Name component of the game's filename.&lt;br/&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${serial}:&lt;/span&gt; Serial of the game.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Example:&lt;/span&gt; https://www.example-not-a-real-domain.com/covers/${serial}.jpg&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Depending on your jurisdiction, &lt;span style=&quot; font-weight:700;&quot;&gt;game covers may be copyrighted&lt;/span&gt;. You are only authorized to use this tool with &lt;span style=&quot; font-weight:700;&quot;&gt;your own servers and images&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;In the box below, specify the URLs to download covers from, with one template URL per line. The following variables are available:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${title}:&lt;/span&gt; Title of the game.&lt;br/&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${savetitle}:&lt;/span&gt; Title of the game including the region.&lt;br/&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${localizedtitle}:&lt;/span&gt; Localized (native language) title of the game.&lt;br/&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${filetitle}:&lt;/span&gt; Name component of the game's filename.&lt;br/&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;${serial}:&lt;/span&gt; Serial of the game.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Example:&lt;/span&gt; https://www.example-not-a-real-domain.com/covers/${serial}.jpg&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>

@ -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<int>(m_progress_range) == m_last_range && static_cast<int>(m_progress_value) == m_last_value)
return;
m_last_range = static_cast<int>(m_progress_range);
m_last_value = static_cast<int>(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<int>(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<int>(m_progress_range);
fireUpdate();
}
void GameListRefreshThread::SetProgressValue(u32 value)
{
ProgressCallback::SetProgressValue(value);
if (static_cast<int>(m_progress_value) == m_last_value)
return;
m_last_value = static_cast<int>(m_progress_value);
fireUpdate();
}
void GameListRefreshThread::fireUpdate()
{
emit refreshProgress(m_status_text, m_last_value, m_last_range, static_cast<int>(GameList::GetEntryCount()),
static_cast<float>(m_start_time.GetTimeSeconds()));
emit refreshProgress(m_qstatus_text, static_cast<int>(m_progress_value), static_cast<int>(m_progress_range),
static_cast<int>(GameList::GetEntryCount()), static_cast<float>(m_start_time.GetTimeSeconds()));
}

@ -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;
};

@ -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 <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QLabel>
@ -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<int>(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<int>(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<int>(m_progress_value), static_cast<int>(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<int>(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<int>(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<int>(m_progress_value), static_cast<int>(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<int>(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<int>(m_progress_value), range = static_cast<int>(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<int>(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)

@ -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 = {},

@ -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;

@ -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<float>(m_progress_value) / static_cast<float>(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<float>(m_progress_value) / static_cast<float>(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)

@ -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<int>((static_cast<float>(m_progress_value) / static_cast<float>(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;

@ -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);

@ -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<u32>((total + (one_mb - 1)) / one_mb);
const u32 pos_mb = static_cast<u32>((pos + (one_mb - 1)) / one_mb);
static_cast<ProgressCallback*>(param)->SetProgressRange(total_mb);
static_cast<ProgressCallback*>(param)->SetProgressValue(pos_mb);
static_cast<ProgressCallback*>(param)->SetStatusText(TinyString::from_format("{}MB of {}MB", pos_mb, total_mb));
static_cast<ProgressCallback*>(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)

Loading…
Cancel
Save