FullscreenUI: Various improvements

This commit is contained in:
Connor McLaughlin 2022-09-17 15:51:05 +10:00
parent 91c8681bed
commit ef3ad91ad0
34 changed files with 1350 additions and 514 deletions

View File

@ -69,7 +69,7 @@ public:
void Evict(std::size_t count = 1) void Evict(std::size_t count = 1)
{ {
while (m_items.size() >= count) while (!m_items.empty() && count > 0)
{ {
typename MapType::iterator lowest = m_items.end(); typename MapType::iterator lowest = m_items.end();
for (auto iter = m_items.begin(); iter != m_items.end(); ++iter) for (auto iter = m_items.begin(); iter != m_items.end(); ++iter)
@ -78,6 +78,7 @@ public:
lowest = iter; lowest = iter;
} }
m_items.erase(lowest); m_items.erase(lowest);
count--;
} }
} }
@ -87,23 +88,20 @@ public:
auto iter = m_items.find(key); auto iter = m_items.find(key);
if (iter == m_items.end()) if (iter == m_items.end())
return false; return false;
m_items.erase(iter); m_items.erase(iter);
return true; return true;
} }
void SetManualEvict(bool block) void SetManualEvict(bool block)
{ {
m_manual_evict = block; m_manual_evict = block;
if (!m_manual_evict) if (!m_manual_evict)
ManualEvict(); ManualEvict();
} }
void ManualEvict() void ManualEvict()
{ {
// evict if we went over // evict if we went over
while (m_items.size() > m_max_capacity) while (m_items.size() > m_max_capacity)
Evict(m_items.size() - (m_max_capacity - 1)); Evict(m_items.size() - m_max_capacity);
} }
private: private:

View File

@ -255,6 +255,31 @@ std::vector<std::string_view> StringUtil::SplitString(const std::string_view& st
return res; return res;
} }
std::vector<std::string> StringUtil::SplitNewString(const std::string_view& str, char delimiter,
bool skip_empty /*= true*/)
{
std::vector<std::string> res;
std::string_view::size_type last_pos = 0;
std::string_view::size_type pos;
while (last_pos < str.size() && (pos = str.find(delimiter, last_pos)) != std::string_view::npos)
{
std::string_view part(StripWhitespace(str.substr(last_pos, pos - last_pos)));
if (!skip_empty || !part.empty())
res.emplace_back(part);
last_pos = pos + 1;
}
if (last_pos < str.size())
{
std::string_view part(StripWhitespace(str.substr(last_pos)));
if (!skip_empty || !part.empty())
res.emplace_back(part);
}
return res;
}
std::string StringUtil::ReplaceAll(const std::string_view& subject, const std::string_view& search, std::string StringUtil::ReplaceAll(const std::string_view& subject, const std::string_view& search,
const std::string_view& replacement) const std::string_view& replacement)
{ {

View File

@ -149,6 +149,7 @@ void StripWhitespace(std::string* str);
/// Splits a string based on a single character delimiter. /// Splits a string based on a single character delimiter.
std::vector<std::string_view> SplitString(const std::string_view& str, char delimiter, bool skip_empty = true); std::vector<std::string_view> SplitString(const std::string_view& str, char delimiter, bool skip_empty = true);
std::vector<std::string> SplitNewString(const std::string_view& str, char delimiter, bool skip_empty = true);
/// Joins a string together using the specified delimiter. /// Joins a string together using the specified delimiter.
template<typename T> template<typename T>

View File

@ -376,68 +376,6 @@ void Host::CheckForSettingsChanges(const Settings& old_settings)
CommonHost::CheckForSettingsChanges(old_settings); CommonHost::CheckForSettingsChanges(old_settings);
} }
void Host::SetBaseBoolSettingValue(const char* section, const char* key, bool value)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetBoolValue(section, key, value);
NoGUIHost::SaveSettings();
}
void Host::SetBaseIntSettingValue(const char* section, const char* key, int value)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetIntValue(section, key, value);
NoGUIHost::SaveSettings();
}
void Host::SetBaseFloatSettingValue(const char* section, const char* key, float value)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetFloatValue(section, key, value);
NoGUIHost::SaveSettings();
}
void Host::SetBaseStringSettingValue(const char* section, const char* key, const char* value)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetStringValue(section, key, value);
NoGUIHost::SaveSettings();
}
void Host::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetStringList(section, key, values);
NoGUIHost::SaveSettings();
}
bool Host::AddValueToBaseStringListSetting(const char* section, const char* key, const char* value)
{
auto lock = Host::GetSettingsLock();
if (!s_base_settings_interface->AddToStringList(section, key, value))
return false;
NoGUIHost::SaveSettings();
return true;
}
bool Host::RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value)
{
auto lock = Host::GetSettingsLock();
if (!s_base_settings_interface->RemoveFromStringList(section, key, value))
return false;
NoGUIHost::SaveSettings();
return true;
}
void Host::DeleteBaseSettingValue(const char* section, const char* key)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->DeleteValue(section, key);
NoGUIHost::SaveSettings();
}
void Host::CommitBaseSettingChanges() void Host::CommitBaseSettingChanges()
{ {
NoGUIHost::SaveSettings(); NoGUIHost::SaveSettings();
@ -1008,23 +946,6 @@ void Host::CancelGameListRefresh()
NoGUIHost::CancelAsyncOp(); NoGUIHost::CancelAsyncOp();
} }
void Host::DownloadCoversAsync(std::vector<std::string> url_templates)
{
NoGUIHost::StartAsyncOp([url_templates = std::move(url_templates)](ProgressCallback* progress) {
GameList::DownloadCovers(url_templates, progress);
});
}
void Host::CancelCoversDownload()
{
NoGUIHost::CancelAsyncOp();
}
void Host::CoversChanged()
{
Host::RunOnCPUThread([]() { FullscreenUI::InvalidateCoverCache(); });
}
bool Host::IsFullscreen() bool Host::IsFullscreen()
{ {
return s_is_fullscreen; return s_is_fullscreen;

View File

@ -162,6 +162,7 @@ void AudioSettingsWidget::onOutputVolumeChanged(int new_value)
// only called for base settings // only called for base settings
DebugAssert(!m_dialog->isPerGameSettings()); DebugAssert(!m_dialog->isPerGameSettings());
Host::SetBaseIntSettingValue("Audio", "OutputVolume", new_value); Host::SetBaseIntSettingValue("Audio", "OutputVolume", new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->setAudioOutputVolume(new_value, m_ui.fastForwardVolume->value()); g_emu_thread->setAudioOutputVolume(new_value, m_ui.fastForwardVolume->value());
updateVolumeLabel(); updateVolumeLabel();
@ -172,6 +173,7 @@ void AudioSettingsWidget::onFastForwardVolumeChanged(int new_value)
// only called for base settings // only called for base settings
DebugAssert(!m_dialog->isPerGameSettings()); DebugAssert(!m_dialog->isPerGameSettings());
Host::SetBaseIntSettingValue("Audio", "FastForwardVolume", new_value); Host::SetBaseIntSettingValue("Audio", "FastForwardVolume", new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->setAudioOutputVolume(m_ui.volume->value(), new_value); g_emu_thread->setAudioOutputVolume(m_ui.volume->value(), new_value);
updateVolumeLabel(); updateVolumeLabel();
@ -184,5 +186,6 @@ void AudioSettingsWidget::onOutputMutedChanged(int new_state)
const bool muted = (new_state != 0); const bool muted = (new_state != 0);
Host::SetBaseBoolSettingValue("Audio", "OutputMuted", muted); Host::SetBaseBoolSettingValue("Audio", "OutputMuted", muted);
Host::CommitBaseSettingChanges();
g_emu_thread->setAudioOutputMuted(muted); g_emu_thread->setAudioOutputMuted(muted);
} }

View File

@ -436,6 +436,7 @@ bool AutoUpdaterDialog::updateNeeded() const
void AutoUpdaterDialog::skipThisUpdateClicked() void AutoUpdaterDialog::skipThisUpdateClicked()
{ {
Host::SetBaseStringSettingValue("AutoUpdater", "LastVersion", m_latest_sha.toUtf8().constData()); Host::SetBaseStringSettingValue("AutoUpdater", "LastVersion", m_latest_sha.toUtf8().constData());
Host::CommitBaseSettingChanges();
done(0); done(0);
} }

View File

@ -156,6 +156,7 @@ void ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked(int state)
} }
Host::SetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", true); Host::SetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", true);
Host::CommitBaseSettingChanges();
} }
m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false)); m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false));

View File

@ -177,6 +177,7 @@ void ControllerBindingWidget::onTypeChanged()
{ {
Host::SetBaseStringSettingValue(m_config_section.c_str(), "Type", Host::SetBaseStringSettingValue(m_config_section.c_str(), "Type",
Settings::GetControllerTypeName(m_controller_type)); Settings::GetControllerTypeName(m_controller_type));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
} }

View File

@ -308,6 +308,7 @@ void ControllerSettingsDialog::setStringValue(const char* section, const char* k
else else
{ {
Host::SetBaseStringSettingValue(section, key, value); Host::SetBaseStringSettingValue(section, key, value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
} }
} }

View File

@ -46,6 +46,7 @@ static void BindWidgetToInputProfileBool(SettingsInterface* sif, WidgetType* wid
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const bool new_value = Accessor::getBoolValue(widget); const bool new_value = Accessor::getBoolValue(widget);
Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -78,6 +79,7 @@ static void BindWidgetToInputProfileFloat(SettingsInterface* sif, WidgetType* wi
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget); const float new_value = Accessor::getFloatValue(widget);
Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -110,6 +112,7 @@ static void BindWidgetToInputProfileNormalized(SettingsInterface* sif, WidgetTyp
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() { Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() {
const float new_value = (static_cast<float>(Accessor::getIntValue(widget)) / range); const float new_value = (static_cast<float>(Accessor::getIntValue(widget)) / range);
Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -153,7 +156,7 @@ static void BindWidgetToInputProfileString(SettingsInterface* sif, WidgetType* w
Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData());
else else
Host::DeleteBaseSettingValue(section.c_str(), key.c_str()); Host::DeleteBaseSettingValue(section.c_str(), key.c_str());
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }

View File

@ -5,6 +5,7 @@
CoverDownloadDialog::CoverDownloadDialog(QWidget* parent /*= nullptr*/) : QDialog(parent) CoverDownloadDialog::CoverDownloadDialog(QWidget* parent /*= nullptr*/) : QDialog(parent)
{ {
m_ui.setupUi(this); m_ui.setupUi(this);
m_ui.coverIcon->setPixmap(QIcon::fromTheme("image-fill").pixmap(32));
updateEnabled(); updateEnabled();
connect(m_ui.start, &QPushButton::clicked, this, &CoverDownloadDialog::onStartClicked); connect(m_ui.start, &QPushButton::clicked, this, &CoverDownloadDialog::onStartClicked);
@ -29,7 +30,8 @@ void CoverDownloadDialog::onDownloadStatus(const QString& text)
void CoverDownloadDialog::onDownloadProgress(int value, int range) void CoverDownloadDialog::onDownloadProgress(int value, int range)
{ {
// limit to once every five seconds // Limit to once every five seconds, otherwise it's way too flickery.
// Ideally in the future we'd have some way to invalidate only a single cover.
if (m_last_refresh_time.GetTimeSeconds() >= 5.0f) if (m_last_refresh_time.GetTimeSeconds() >= 5.0f)
{ {
emit coverRefreshRequested(); emit coverRefreshRequested();
@ -83,7 +85,8 @@ void CoverDownloadDialog::updateEnabled()
void CoverDownloadDialog::startThread() void CoverDownloadDialog::startThread()
{ {
m_thread = std::make_unique<CoverDownloadThread>(this, m_ui.urls->toPlainText()); m_thread = std::make_unique<CoverDownloadThread>(this, m_ui.urls->toPlainText(), m_ui.useSerialFileNames->isChecked());
m_last_refresh_time.Reset();
connect(m_thread.get(), &CoverDownloadThread::statusUpdated, this, &CoverDownloadDialog::onDownloadStatus); connect(m_thread.get(), &CoverDownloadThread::statusUpdated, this, &CoverDownloadDialog::onDownloadStatus);
connect(m_thread.get(), &CoverDownloadThread::progressUpdated, this, &CoverDownloadDialog::onDownloadProgress); connect(m_thread.get(), &CoverDownloadThread::progressUpdated, this, &CoverDownloadDialog::onDownloadProgress);
connect(m_thread.get(), &CoverDownloadThread::threadFinished, this, &CoverDownloadDialog::onDownloadComplete); connect(m_thread.get(), &CoverDownloadThread::threadFinished, this, &CoverDownloadDialog::onDownloadComplete);
@ -101,8 +104,8 @@ void CoverDownloadDialog::cancelThread()
m_thread.reset(); m_thread.reset();
} }
CoverDownloadDialog::CoverDownloadThread::CoverDownloadThread(QWidget* parent, const QString& urls) CoverDownloadDialog::CoverDownloadThread::CoverDownloadThread(QWidget* parent, const QString& urls, bool use_serials)
: QtAsyncProgressThread(parent) : QtAsyncProgressThread(parent), m_use_serials(use_serials)
{ {
for (const QString& str : urls.split(QChar('\n'))) for (const QString& str : urls.split(QChar('\n')))
m_urls.push_back(str.toStdString()); m_urls.push_back(str.toStdString());
@ -112,5 +115,5 @@ CoverDownloadDialog::CoverDownloadThread::~CoverDownloadThread() = default;
void CoverDownloadDialog::CoverDownloadThread::runAsync() void CoverDownloadDialog::CoverDownloadThread::runAsync()
{ {
GameList::DownloadCovers(m_urls, this); GameList::DownloadCovers(m_urls, m_use_serials, this);
} }

View File

@ -34,7 +34,7 @@ private:
class CoverDownloadThread : public QtAsyncProgressThread class CoverDownloadThread : public QtAsyncProgressThread
{ {
public: public:
CoverDownloadThread(QWidget* parent, const QString& urls); CoverDownloadThread(QWidget* parent, const QString& urls, bool use_serials);
~CoverDownloadThread(); ~CoverDownloadThread();
protected: protected:
@ -42,6 +42,7 @@ private:
private: private:
std::vector<std::string> m_urls; std::vector<std::string> m_urls;
bool m_use_serials;
}; };
void startThread(); void startThread();

View File

@ -6,18 +6,48 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>656</width> <width>720</width>
<height>343</height> <height>380</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Download Covers</string> <string>Download Covers</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="coverIcon">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="resources/resources.qrc">:/icons/black/svg/image-fill.svg</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;p&gt;In the form 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;${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>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.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_3">
<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;${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>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@ -25,20 +55,24 @@
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="verticalSpacer"> <widget class="QTextEdit" name="urls"/>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item> </item>
<item> <item>
<widget class="QTextEdit" name="urls"/> <widget class="QLabel" name="label_2">
<property name="text">
<string>By default, the downloaded covers will be saved with the game's title. If this is not desired, you can check the &quot;Use Serial File Names&quot; box below. Using serials instead of game titles will prevent conflicts when multiple regions of the same game are used.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useSerialFileNames">
<property name="text">
<string>Use Serial File Names</string>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="status"> <widget class="QLabel" name="status">
@ -76,6 +110,8 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<resources/> <resources>
<include location="resources/resources.qrc"/>
</resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -225,7 +225,10 @@ bool DisplayWidget::event(QEvent* event)
if (ImGuiManager::WantsTextInput() && key_event->type() == QEvent::KeyPress) if (ImGuiManager::WantsTextInput() && key_event->type() == QEvent::KeyPress)
{ {
const QString text(key_event->text()); // Don't forward backspace characters. We send the backspace as a normal key event,
// so if we send the character too, it double-deletes.
QString text(key_event->text());
text.remove(QChar('\b'));
if (!text.isEmpty()) if (!text.isEmpty())
emit windowTextEntered(text); emit windowTextEntered(text);
} }

View File

@ -176,4 +176,6 @@ void GameListSearchDirectoriesModel::saveToSettings()
Host::DeleteBaseSettingValue("GameList", "RecursivePaths"); Host::DeleteBaseSettingValue("GameList", "RecursivePaths");
else else
Host::SetBaseStringListSettingValue("GameList", "RecursivePaths", recursive_paths); Host::SetBaseStringListSettingValue("GameList", "RecursivePaths", recursive_paths);
Host::CommitBaseSettingChanges();
} }

View File

@ -55,6 +55,7 @@ bool GameListSettingsWidget::addExcludedPath(const std::string& path)
if (!Host::AddValueToBaseStringListSetting("GameList", "ExcludedPaths", path.c_str())) if (!Host::AddValueToBaseStringListSetting("GameList", "ExcludedPaths", path.c_str()))
return false; return false;
Host::CommitBaseSettingChanges();
m_ui.excludedPaths->addItem(QString::fromStdString(path)); m_ui.excludedPaths->addItem(QString::fromStdString(path));
g_main_window->refreshGameList(false); g_main_window->refreshGameList(false);
return true; return true;
@ -158,7 +159,8 @@ void GameListSettingsWidget::onRemoveExcludedPathButtonClicked()
if (!item) if (!item)
return; return;
Host::RemoveValueFromBaseStringListSetting("GameList", "ExcludedPaths", item->text().toUtf8().constData()); if (Host::RemoveValueFromBaseStringListSetting("GameList", "ExcludedPaths", item->text().toUtf8().constData()))
Host::CommitBaseSettingChanges();
delete item; delete item;
g_main_window->refreshGameList(false); g_main_window->refreshGameList(false);

View File

@ -322,6 +322,7 @@ void GameListWidget::listZoom(float delta)
{ {
const float new_scale = std::clamp(m_model->getCoverScale() + delta, MIN_SCALE, MAX_SCALE); const float new_scale = std::clamp(m_model->getCoverScale() + delta, MIN_SCALE, MAX_SCALE);
Host::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale); Host::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale);
Host::CommitBaseSettingChanges();
m_model->setCoverScale(new_scale); m_model->setCoverScale(new_scale);
m_model->updateCacheSize(width(), height()); m_model->updateCacheSize(width(), height());
updateListFont(); updateListFont();
@ -345,6 +346,7 @@ void GameListWidget::gridIntScale(int int_scale)
const float new_scale = std::clamp(static_cast<float>(int_scale) / 100.0f, MIN_SCALE, MAX_SCALE); const float new_scale = std::clamp(static_cast<float>(int_scale) / 100.0f, MIN_SCALE, MAX_SCALE);
Host::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale); Host::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale);
Host::CommitBaseSettingChanges();
m_model->setCoverScale(new_scale); m_model->setCoverScale(new_scale);
m_model->updateCacheSize(width(), height()); m_model->updateCacheSize(width(), height());
updateListFont(); updateListFont();
@ -367,6 +369,7 @@ void GameListWidget::showGameList()
} }
Host::SetBaseBoolSettingValue("UI", "GameListGridView", false); Host::SetBaseBoolSettingValue("UI", "GameListGridView", false);
Host::CommitBaseSettingChanges();
m_ui.stack->setCurrentIndex(0); m_ui.stack->setCurrentIndex(0);
resizeTableViewColumnsToFit(); resizeTableViewColumnsToFit();
updateToolbar(); updateToolbar();
@ -382,6 +385,7 @@ void GameListWidget::showGameGrid()
} }
Host::SetBaseBoolSettingValue("UI", "GameListGridView", true); Host::SetBaseBoolSettingValue("UI", "GameListGridView", true);
Host::CommitBaseSettingChanges();
m_ui.stack->setCurrentIndex(1); m_ui.stack->setCurrentIndex(1);
updateToolbar(); updateToolbar();
emit layoutChange(); emit layoutChange();
@ -396,6 +400,7 @@ void GameListWidget::setShowCoverTitles(bool enabled)
} }
Host::SetBaseBoolSettingValue("UI", "GameListShowCoverTitles", enabled); Host::SetBaseBoolSettingValue("UI", "GameListShowCoverTitles", enabled);
Host::CommitBaseSettingChanges();
m_model->setShowCoverTitles(enabled); m_model->setShowCoverTitles(enabled);
if (isShowingGameGrid()) if (isShowingGameGrid())
m_model->refresh(); m_model->refresh();
@ -499,6 +504,7 @@ void GameListWidget::saveTableViewColumnVisibilitySettings()
{ {
const bool visible = !m_table_view->isColumnHidden(column); const bool visible = !m_table_view->isColumnHidden(column);
Host::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), visible); Host::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), visible);
Host::CommitBaseSettingChanges();
} }
} }
@ -506,6 +512,7 @@ void GameListWidget::saveTableViewColumnVisibilitySettings(int column)
{ {
const bool visible = !m_table_view->isColumnHidden(column); const bool visible = !m_table_view->isColumnHidden(column);
Host::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), visible); Host::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), visible);
Host::CommitBaseSettingChanges();
} }
void GameListWidget::loadTableViewColumnSortSettings() void GameListWidget::loadTableViewColumnSortSettings()
@ -533,6 +540,7 @@ void GameListWidget::saveTableViewColumnSortSettings()
} }
Host::SetBaseBoolSettingValue("GameListTableView", "SortDescending", sort_descending); Host::SetBaseBoolSettingValue("GameListTableView", "SortDescending", sort_descending);
Host::CommitBaseSettingChanges();
} }
const GameList::Entry* GameListWidget::getSelectedEntry() const const GameList::Entry* GameListWidget::getSelectedEntry() const

View File

@ -238,6 +238,7 @@ void InputBindingDialog::saveListToSettings()
Host::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings); Host::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings);
else else
Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str());
Host::CommitBaseSettingChanges();
g_emu_thread->reloadInputBindings(); g_emu_thread->reloadInputBindings();
} }
} }

View File

@ -215,6 +215,7 @@ void InputBindingWidget::setNewBinding()
else else
{ {
Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str());
Host::CommitBaseSettingChanges();
g_emu_thread->reloadInputBindings(); g_emu_thread->reloadInputBindings();
} }
} }
@ -235,6 +236,7 @@ void InputBindingWidget::clearBinding()
else else
{ {
Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str());
Host::CommitBaseSettingChanges();
g_emu_thread->reloadInputBindings(); g_emu_thread->reloadInputBindings();
} }
reloadBinding(); reloadBinding();
@ -391,6 +393,7 @@ void InputVibrationBindingWidget::clearBinding()
{ {
m_binding = {}; m_binding = {};
Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str());
Host::CommitBaseSettingChanges();
g_emu_thread->reloadInputBindings(); g_emu_thread->reloadInputBindings();
setText(QString()); setText(QString());
} }
@ -428,6 +431,7 @@ void InputVibrationBindingWidget::onClicked()
const QString new_value(input_dialog.textValue()); const QString new_value(input_dialog.textValue());
m_binding = new_value.toStdString(); m_binding = new_value.toStdString();
Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_binding.c_str()); Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_binding.c_str());
Host::CommitBaseSettingChanges();
setText(new_value); setText(new_value);
} }

View File

@ -1119,18 +1119,21 @@ void MainWindow::onRemoveDiscActionTriggered()
void MainWindow::onViewToolbarActionToggled(bool checked) void MainWindow::onViewToolbarActionToggled(bool checked)
{ {
Host::SetBaseBoolSettingValue("UI", "ShowToolbar", checked); Host::SetBaseBoolSettingValue("UI", "ShowToolbar", checked);
Host::CommitBaseSettingChanges();
m_ui.toolBar->setVisible(checked); m_ui.toolBar->setVisible(checked);
} }
void MainWindow::onViewLockToolbarActionToggled(bool checked) void MainWindow::onViewLockToolbarActionToggled(bool checked)
{ {
Host::SetBaseBoolSettingValue("UI", "LockToolbar", checked); Host::SetBaseBoolSettingValue("UI", "LockToolbar", checked);
Host::CommitBaseSettingChanges();
m_ui.toolBar->setMovable(!checked); m_ui.toolBar->setMovable(!checked);
} }
void MainWindow::onViewStatusBarActionToggled(bool checked) void MainWindow::onViewStatusBarActionToggled(bool checked)
{ {
Host::SetBaseBoolSettingValue("UI", "ShowStatusBar", checked); Host::SetBaseBoolSettingValue("UI", "ShowStatusBar", checked);
Host::CommitBaseSettingChanges();
m_ui.statusBar->setVisible(checked); m_ui.statusBar->setVisible(checked);
} }
@ -1343,7 +1346,7 @@ void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
} }
QString new_filename = QString new_filename =
QString::fromStdString(GameList::GetNewCoverImagePathForEntry(entry, filename.toStdString().c_str())); QString::fromStdString(GameList::GetNewCoverImagePathForEntry(entry, filename.toStdString().c_str(), false));
if (new_filename.isEmpty()) if (new_filename.isEmpty())
return; return;
@ -1427,6 +1430,7 @@ void MainWindow::setupAdditionalUi()
action->setCheckable(true); action->setCheckable(true);
connect(action, &QAction::triggered, [this, mode]() { connect(action, &QAction::triggered, [this, mode]() {
Host::SetBaseStringSettingValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(mode)); Host::SetBaseStringSettingValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(mode));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
updateDebugMenuCPUExecutionMode(); updateDebugMenuCPUExecutionMode();
}); });
@ -1441,6 +1445,7 @@ void MainWindow::setupAdditionalUi()
action->setCheckable(true); action->setCheckable(true);
connect(action, &QAction::triggered, [this, renderer]() { connect(action, &QAction::triggered, [this, renderer]() {
Host::SetBaseStringSettingValue("GPU", "Renderer", Settings::GetRendererName(renderer)); Host::SetBaseStringSettingValue("GPU", "Renderer", Settings::GetRendererName(renderer));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
updateDebugMenuGPURenderer(); updateDebugMenuGPURenderer();
}); });
@ -1455,6 +1460,7 @@ void MainWindow::setupAdditionalUi()
action->setCheckable(true); action->setCheckable(true);
connect(action, &QAction::triggered, [this, crop_mode]() { connect(action, &QAction::triggered, [this, crop_mode]() {
Host::SetBaseStringSettingValue("Display", "CropMode", Settings::GetDisplayCropModeName(crop_mode)); Host::SetBaseStringSettingValue("Display", "CropMode", Settings::GetDisplayCropModeName(crop_mode));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
updateDebugMenuCropMode(); updateDebugMenuCropMode();
}); });
@ -1484,6 +1490,7 @@ void MainWindow::setupAdditionalUi()
connect(action, &QAction::triggered, [this, action]() { connect(action, &QAction::triggered, [this, action]() {
const QString new_language = action->data().toString(); const QString new_language = action->data().toString();
Host::SetBaseStringSettingValue("Main", "Language", new_language.toUtf8().constData()); Host::SetBaseStringSettingValue("Main", "Language", new_language.toUtf8().constData());
Host::CommitBaseSettingChanges();
QtHost::InstallTranslator(); QtHost::InstallTranslator();
recreate(); recreate();
}); });
@ -1950,6 +1957,7 @@ void MainWindow::addThemeToMenu(const QString& name, const QString& key)
void MainWindow::setTheme(const QString& theme) void MainWindow::setTheme(const QString& theme)
{ {
Host::SetBaseStringSettingValue("UI", "Theme", theme.toUtf8().constData()); Host::SetBaseStringSettingValue("UI", "Theme", theme.toUtf8().constData());
Host::CommitBaseSettingChanges();
updateApplicationTheme(); updateApplicationTheme();
updateMenuSelectedTheme(); updateMenuSelectedTheme();
m_game_list_widget->reloadCommonImages(); m_game_list_widget->reloadCommonImages();
@ -2097,7 +2105,10 @@ void MainWindow::saveGeometryToConfig()
const QByteArray geometry_b64 = geometry.toBase64(); const QByteArray geometry_b64 = geometry.toBase64();
const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowGeometry"); const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowGeometry");
if (old_geometry_b64 != geometry_b64.constData()) if (old_geometry_b64 != geometry_b64.constData())
{
Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData()); Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData());
Host::CommitBaseSettingChanges();
}
} }
void MainWindow::restoreGeometryFromConfig() void MainWindow::restoreGeometryFromConfig()
@ -2114,7 +2125,10 @@ void MainWindow::saveDisplayWindowGeometryToConfig()
const QByteArray geometry_b64 = geometry.toBase64(); const QByteArray geometry_b64 = geometry.toBase64();
const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry");
if (old_geometry_b64 != geometry_b64.constData()) if (old_geometry_b64 != geometry_b64.constData())
{
Host::SetBaseStringSettingValue("UI", "DisplayWindowGeometry", geometry_b64.constData()); Host::SetBaseStringSettingValue("UI", "DisplayWindowGeometry", geometry_b64.constData());
Host::CommitBaseSettingChanges();
}
} }
void MainWindow::restoreDisplayWindowGeometryFromConfig() void MainWindow::restoreDisplayWindowGeometryFromConfig()
@ -2443,6 +2457,7 @@ void MainWindow::onCheckForUpdatesActionTriggered()
{ {
// Wipe out the last version, that way it displays the update if we've previously skipped it. // Wipe out the last version, that way it displays the update if we've previously skipped it.
Host::DeleteBaseSettingValue("AutoUpdater", "LastVersion"); Host::DeleteBaseSettingValue("AutoUpdater", "LastVersion");
Host::CommitBaseSettingChanges();
checkForUpdates(true); checkForUpdates(true);
} }
@ -2553,6 +2568,7 @@ void MainWindow::onToolsCheatManagerTriggered()
connect(cb, &QCheckBox::stateChanged, [](int state) { connect(cb, &QCheckBox::stateChanged, [](int state) {
Host::SetBaseBoolSettingValue("UI", "DisplayCheatWarning", (state != Qt::CheckState::Checked)); Host::SetBaseBoolSettingValue("UI", "DisplayCheatWarning", (state != Qt::CheckState::Checked));
Host::CommitBaseSettingChanges();
}); });
if (mb.exec() == QMessageBox::No) if (mb.exec() == QMessageBox::No)

View File

@ -1078,21 +1078,6 @@ void Host::CancelGameListRefresh()
QMetaObject::invokeMethod(g_main_window, "cancelGameListRefresh", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(g_main_window, "cancelGameListRefresh", Qt::BlockingQueuedConnection);
} }
void Host::DownloadCoversAsync(std::vector<std::string> url_templates)
{
//
}
void Host::CancelCoversDownload()
{
//
}
void Host::CoversChanged()
{
//
}
void EmuThread::loadState(const QString& filename) void EmuThread::loadState(const QString& filename)
{ {
if (!isOnThread()) if (!isOnThread())
@ -1579,68 +1564,6 @@ std::optional<std::time_t> Host::GetResourceFileTimestamp(const char* filename)
return sd.ModificationTime; return sd.ModificationTime;
} }
void Host::SetBaseBoolSettingValue(const char* section, const char* key, bool value)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetBoolValue(section, key, value);
QtHost::QueueSettingsSave();
}
void Host::SetBaseIntSettingValue(const char* section, const char* key, int value)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetIntValue(section, key, value);
QtHost::QueueSettingsSave();
}
void Host::SetBaseFloatSettingValue(const char* section, const char* key, float value)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetFloatValue(section, key, value);
QtHost::QueueSettingsSave();
}
void Host::SetBaseStringSettingValue(const char* section, const char* key, const char* value)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetStringValue(section, key, value);
QtHost::QueueSettingsSave();
}
void Host::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->SetStringList(section, key, values);
QtHost::QueueSettingsSave();
}
bool Host::AddValueToBaseStringListSetting(const char* section, const char* key, const char* value)
{
auto lock = Host::GetSettingsLock();
if (!s_base_settings_interface->AddToStringList(section, key, value))
return false;
QtHost::QueueSettingsSave();
return true;
}
bool Host::RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value)
{
auto lock = Host::GetSettingsLock();
if (!s_base_settings_interface->RemoveFromStringList(section, key, value))
return false;
QtHost::QueueSettingsSave();
return true;
}
void Host::DeleteBaseSettingValue(const char* section, const char* key)
{
auto lock = Host::GetSettingsLock();
s_base_settings_interface->DeleteValue(section, key);
QtHost::QueueSettingsSave();
}
void Host::CommitBaseSettingChanges() void Host::CommitBaseSettingChanges()
{ {
if (g_emu_thread->isOnThread()) if (g_emu_thread->isOnThread())

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20 5H4v14l9.292-9.294a1 1 0 0 1 1.414 0L20 15.01V5zM2 3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993zM8 11a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" fill="#000000"/></svg>

After

Width:  |  Height:  |  Size: 359 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20 5H4v14l9.292-9.294a1 1 0 0 1 1.414 0L20 15.01V5zM2 3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993zM8 11a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" fill="#ffffff"/></svg>

After

Width:  |  Height:  |  Size: 359 B

View File

@ -178,6 +178,7 @@
<file>icons/black/svg/gamepad-line.svg</file> <file>icons/black/svg/gamepad-line.svg</file>
<file>icons/black/svg/global-line.svg</file> <file>icons/black/svg/global-line.svg</file>
<file>icons/black/svg/hard-drive-2-line.svg</file> <file>icons/black/svg/hard-drive-2-line.svg</file>
<file>icons/black/svg/image-fill.svg</file>
<file>icons/black/svg/keyboard-line.svg</file> <file>icons/black/svg/keyboard-line.svg</file>
<file>icons/black/svg/layout-grid-line.svg</file> <file>icons/black/svg/layout-grid-line.svg</file>
<file>icons/black/svg/list-check.svg</file> <file>icons/black/svg/list-check.svg</file>
@ -508,6 +509,7 @@
<file>icons/white/svg/gamepad-line.svg</file> <file>icons/white/svg/gamepad-line.svg</file>
<file>icons/white/svg/global-line.svg</file> <file>icons/white/svg/global-line.svg</file>
<file>icons/white/svg/hard-drive-2-line.svg</file> <file>icons/white/svg/hard-drive-2-line.svg</file>
<file>icons/white/svg/image-fill.svg</file>
<file>icons/white/svg/keyboard-line.svg</file> <file>icons/white/svg/keyboard-line.svg</file>
<file>icons/white/svg/layout-grid-line.svg</file> <file>icons/white/svg/layout-grid-line.svg</file>
<file>icons/white/svg/list-check.svg</file> <file>icons/white/svg/list-check.svg</file>

View File

@ -407,6 +407,7 @@ void SettingsDialog::setBoolSettingValue(const char* section, const char* key, s
{ {
value.has_value() ? Host::SetBaseBoolSettingValue(section, key, value.value()) : value.has_value() ? Host::SetBaseBoolSettingValue(section, key, value.value()) :
Host::DeleteBaseSettingValue(section, key); Host::DeleteBaseSettingValue(section, key);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
} }
} }
@ -423,6 +424,7 @@ void SettingsDialog::setIntSettingValue(const char* section, const char* key, st
{ {
value.has_value() ? Host::SetBaseIntSettingValue(section, key, value.value()) : value.has_value() ? Host::SetBaseIntSettingValue(section, key, value.value()) :
Host::DeleteBaseSettingValue(section, key); Host::DeleteBaseSettingValue(section, key);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
} }
} }
@ -439,6 +441,7 @@ void SettingsDialog::setFloatSettingValue(const char* section, const char* key,
{ {
value.has_value() ? Host::SetBaseFloatSettingValue(section, key, value.value()) : value.has_value() ? Host::SetBaseFloatSettingValue(section, key, value.value()) :
Host::DeleteBaseSettingValue(section, key); Host::DeleteBaseSettingValue(section, key);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
} }
} }
@ -455,6 +458,7 @@ void SettingsDialog::setStringSettingValue(const char* section, const char* key,
{ {
value.has_value() ? Host::SetBaseStringSettingValue(section, key, value.value()) : value.has_value() ? Host::SetBaseStringSettingValue(section, key, value.value()) :
Host::DeleteBaseSettingValue(section, key); Host::DeleteBaseSettingValue(section, key);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
} }
} }
@ -470,6 +474,7 @@ void SettingsDialog::removeSettingValue(const char* section, const char* key)
else else
{ {
Host::DeleteBaseSettingValue(section, key); Host::DeleteBaseSettingValue(section, key);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
} }
} }

View File

@ -651,6 +651,7 @@ static void BindWidgetToBoolSetting(SettingsInterface* sif, WidgetType* widget,
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const bool new_value = Accessor::getBoolValue(widget); const bool new_value = Accessor::getBoolValue(widget);
Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -694,6 +695,7 @@ static void BindWidgetToIntSetting(SettingsInterface* sif, WidgetType* widget, s
widget, [widget, section = std::move(section), key = std::move(key), option_offset]() { widget, [widget, section = std::move(section), key = std::move(key), option_offset]() {
const int new_value = Accessor::getIntValue(widget); const int new_value = Accessor::getIntValue(widget);
Host::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset); Host::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -734,6 +736,7 @@ static void BindWidgetToFloatSetting(SettingsInterface* sif, WidgetType* widget,
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget); const float new_value = Accessor::getFloatValue(widget);
Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -774,6 +777,7 @@ static void BindWidgetToNormalizedSetting(SettingsInterface* sif, WidgetType* wi
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() { Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() {
const float new_value = (static_cast<float>(Accessor::getIntValue(widget)) / range); const float new_value = (static_cast<float>(Accessor::getIntValue(widget)) / range);
Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -819,6 +823,7 @@ static void BindWidgetToStringSetting(SettingsInterface* sif, WidgetType* widget
else else
Host::DeleteBaseSettingValue(section.c_str(), key.c_str()); Host::DeleteBaseSettingValue(section.c_str(), key.c_str());
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -884,6 +889,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget,
const DataType value = static_cast<DataType>(static_cast<UnderlyingType>(Accessor::getIntValue(widget))); const DataType value = static_cast<DataType>(static_cast<UnderlyingType>(Accessor::getIntValue(widget)));
const char* string_value = to_string_function(value); const char* string_value = to_string_function(value);
Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value); Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -946,6 +952,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget,
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), enum_names]() { Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), enum_names]() {
const UnderlyingType value = static_cast<UnderlyingType>(Accessor::getIntValue(widget)); const UnderlyingType value = static_cast<UnderlyingType>(Accessor::getIntValue(widget));
Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_names[value]); Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_names[value]);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -1010,6 +1017,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget,
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), enum_values]() { Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), enum_values]() {
const int value = Accessor::getIntValue(widget); const int value = Accessor::getIntValue(widget);
Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]); Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(); g_emu_thread->applySettings();
}); });
} }
@ -1054,6 +1062,7 @@ static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget
Host::DeleteBaseSettingValue(section.c_str(), key.c_str()); Host::DeleteBaseSettingValue(section.c_str(), key.c_str());
} }
Host::CommitBaseSettingChanges();
g_emu_thread->updateEmuFolders(); g_emu_thread->updateEmuFolders();
}); });

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,8 @@ public:
ProgressCallback(std::string name); ProgressCallback(std::string name);
~ProgressCallback() override; ~ProgressCallback() override;
ALWAYS_INLINE const std::string& GetName() const { return m_name; }
void PushState() override; void PushState() override;
void PopState() override; void PopState() override;

View File

@ -647,7 +647,7 @@ std::string GameList::GetCoverImagePath(const std::string& path, const std::stri
return {}; return {};
} }
std::string GameList::GetNewCoverImagePathForEntry(const Entry* entry, const char* new_filename) std::string GameList::GetNewCoverImagePathForEntry(const Entry* entry, const char* new_filename, bool use_serial)
{ {
const char* extension = std::strrchr(new_filename, '.'); const char* extension = std::strrchr(new_filename, '.');
if (!extension) if (!extension)
@ -665,12 +665,12 @@ std::string GameList::GetNewCoverImagePathForEntry(const Entry* entry, const cha
const std::string sanitized_name(Path::SanitizeFileName(entry->title)); const std::string sanitized_name(Path::SanitizeFileName(entry->title));
std::string name; std::string name;
if (sanitized_name != entry->title) if (sanitized_name != entry->title || use_serial)
name = fmt::format("{}{}", entry->serial, extension); name = fmt::format("{}{}", entry->serial, extension);
else else
name = fmt::format("{}{}", entry->title, extension); name = fmt::format("{}{}", entry->title, extension);
return Path::Combine(EmuFolders::Covers, name); return Path::Combine(EmuFolders::Covers, Path::SanitizeFileName(name));
} }
size_t GameList::Entry::GetReleaseDateString(char* buffer, size_t buffer_size) const size_t GameList::Entry::GetReleaseDateString(char* buffer, size_t buffer_size) const
@ -690,7 +690,8 @@ size_t GameList::Entry::GetReleaseDateString(char* buffer, size_t buffer_size) c
return std::strftime(buffer, buffer_size, "%d %B %Y", &date_tm); return std::strftime(buffer, buffer_size, "%d %B %Y", &date_tm);
} }
bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, ProgressCallback* progress /*= nullptr*/) bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial,
ProgressCallback* progress, std::function<void(const Entry*, std::string)> save_callback)
{ {
if (!progress) if (!progress)
progress = ProgressCallback::NullProgressCallback; progress = ProgressCallback::NullProgressCallback;
@ -776,8 +777,9 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, Pro
// we could actually do a few in parallel here... // we could actually do a few in parallel here...
std::string filename(Common::HTTPDownloader::URLDecode(url)); std::string filename(Common::HTTPDownloader::URLDecode(url));
downloader->CreateRequest(std::move(url), [entry_path = std::move(entry_path), filename = std::move(filename)]( downloader->CreateRequest(
s32 status_code, Common::HTTPDownloader::Request::Data data) { std::move(url), [use_serial, &save_callback, entry_path = std::move(entry_path),
filename = std::move(filename)](s32 status_code, Common::HTTPDownloader::Request::Data data) {
if (status_code != Common::HTTPDownloader::HTTP_OK || data.empty()) if (status_code != Common::HTTPDownloader::HTTP_OK || data.empty())
return; return;
@ -786,12 +788,13 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, Pro
if (!entry || !GetCoverImagePathForEntry(entry).empty()) if (!entry || !GetCoverImagePathForEntry(entry).empty())
return; return;
std::string write_path(GetNewCoverImagePathForEntry(entry, filename.c_str())); std::string write_path(GetNewCoverImagePathForEntry(entry, filename.c_str(), use_serial));
if (write_path.empty()) if (write_path.empty())
return; return;
FileSystem::WriteBinaryFile(write_path.c_str(), data.data(), data.size()); FileSystem::WriteBinaryFile(write_path.c_str(), data.data(), data.size());
Host::CoversChanged(); if (save_callback)
save_callback(entry, std::move(write_path));
}); });
downloader->WaitForAllRequests(); downloader->WaitForAllRequests();
progress->IncrementProgressValue(); progress->IncrementProgressValue();

View File

@ -3,6 +3,7 @@
#include "core/types.h" #include "core/types.h"
#include "util/cd_image.h" #include "util/cd_image.h"
#include <ctime> #include <ctime>
#include <functional>
#include <mutex> #include <mutex>
#include <string> #include <string>
@ -74,9 +75,13 @@ void Refresh(bool invalidate_cache, bool only_cache = false, ProgressCallback* p
std::string GetCoverImagePathForEntry(const Entry* entry); std::string GetCoverImagePathForEntry(const Entry* entry);
std::string GetCoverImagePath(const std::string& path, const std::string& serial, const std::string& title); std::string GetCoverImagePath(const std::string& path, const std::string& serial, const std::string& title);
std::string GetNewCoverImagePathForEntry(const Entry* entry, const char* new_filename); std::string GetNewCoverImagePathForEntry(const Entry* entry, const char* new_filename, bool use_serial);
bool DownloadCovers(const std::vector<std::string>& url_templates, ProgressCallback* progress = nullptr); /// Downloads covers using the specified URL templates. By default, covers are saved by title, but this can be changed
/// with the use_serial parameter. save_callback optionall takes the entry and the path the new cover is saved to.
bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial = false,
ProgressCallback* progress = nullptr,
std::function<void(const Entry*, std::string)> save_callback = {});
}; // namespace GameList }; // namespace GameList
namespace Host { namespace Host {
@ -85,8 +90,4 @@ void RefreshGameListAsync(bool invalidate_cache);
/// Cancels game list refresh, if there is one in progress. /// Cancels game list refresh, if there is one in progress.
void CancelGameListRefresh(); void CancelGameListRefresh();
void DownloadCoversAsync(std::vector<std::string> url_templates);
void CancelCoversDownload();
void CoversChanged();
} // namespace Host } // namespace Host

View File

@ -106,6 +106,55 @@ std::vector<std::string> Host::GetStringListSetting(const char* section, const c
return s_layered_settings_interface.GetStringList(section, key); return s_layered_settings_interface.GetStringList(section, key);
} }
void Host::SetBaseBoolSettingValue(const char* section, const char* key, bool value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetBoolValue(section, key, value);
}
void Host::SetBaseIntSettingValue(const char* section, const char* key, int value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetIntValue(section, key, value);
}
void Host::SetBaseFloatSettingValue(const char* section, const char* key, float value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetFloatValue(section, key, value);
}
void Host::SetBaseStringSettingValue(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringValue(section, key, value);
}
void Host::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringList(section, key, values);
}
bool Host::AddValueToBaseStringListSetting(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->AddToStringList(section, key, value);
}
bool Host::RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->RemoveFromStringList(section, key, value);
}
void Host::DeleteBaseSettingValue(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->DeleteValue(section, key);
}
SettingsInterface* Host::Internal::GetBaseSettingsLayer() SettingsInterface* Host::Internal::GetBaseSettingsLayer()
{ {
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE); return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE);

View File

@ -17,15 +17,20 @@
#include "fmt/core.h" #include "fmt/core.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include "imgui_stdlib.h" #include "imgui_stdlib.h"
#include <array>
#include <cmath> #include <cmath>
#include <condition_variable> #include <condition_variable>
#include <deque> #include <deque>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <variant>
Log_SetChannel(ImGuiFullscreen); Log_SetChannel(ImGuiFullscreen);
namespace ImGuiFullscreen { namespace ImGuiFullscreen {
using MessageDialogCallbackVariant = std::variant<InfoMessageDialogCallback, ConfirmMessageDialogCallback>;
static std::optional<Common::RGBA8Image> LoadTextureImage(const char* path); static std::optional<Common::RGBA8Image> LoadTextureImage(const char* path);
static std::shared_ptr<HostDisplayTexture> UploadTexture(const char* path, const Common::RGBA8Image& image); static std::shared_ptr<HostDisplayTexture> UploadTexture(const char* path, const Common::RGBA8Image& image);
static void TextureLoaderThread(); static void TextureLoaderThread();
@ -33,6 +38,7 @@ static void TextureLoaderThread();
static void DrawFileSelector(); static void DrawFileSelector();
static void DrawChoiceDialog(); static void DrawChoiceDialog();
static void DrawInputDialog(); static void DrawInputDialog();
static void DrawMessageDialog();
static void DrawBackgroundProgressDialogs(ImVec2& position, float spacing); static void DrawBackgroundProgressDialogs(ImVec2& position, float spacing);
static void DrawNotifications(ImVec2& position, float spacing); static void DrawNotifications(ImVec2& position, float spacing);
static void DrawToast(); static void DrawToast();
@ -98,6 +104,12 @@ static std::string s_input_dialog_text;
static std::string s_input_dialog_ok_text; static std::string s_input_dialog_ok_text;
static InputStringDialogCallback s_input_dialog_callback; static InputStringDialogCallback s_input_dialog_callback;
static bool s_message_dialog_open = false;
static std::string s_message_dialog_title;
static std::string s_message_dialog_message;
static std::array<std::string, 3> s_message_dialog_buttons;
static MessageDialogCallbackVariant s_message_dialog_callback;
struct FileSelectorItem struct FileSelectorItem
{ {
FileSelectorItem() = default; FileSelectorItem() = default;
@ -201,6 +213,7 @@ void ImGuiFullscreen::Shutdown()
s_notifications.clear(); s_notifications.clear();
s_background_progress_dialogs.clear(); s_background_progress_dialogs.clear();
CloseInputDialog(); CloseInputDialog();
CloseMessageDialog();
s_choice_dialog_open = false; s_choice_dialog_open = false;
s_choice_dialog_checkable = false; s_choice_dialog_checkable = false;
s_choice_dialog_title = {}; s_choice_dialog_title = {};
@ -431,6 +444,16 @@ void ImGuiFullscreen::BeginLayout()
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(8.0f, 8.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(4.0f, 3.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, LayoutScale(8.0f, 4.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, LayoutScale(4.0f, 4.0f));
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, LayoutScale(4.0f, 2.0f));
ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, LayoutScale(21.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, LayoutScale(14.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, LayoutScale(10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_TabRounding, LayoutScale(4.0f));
ImGui::PushStyleColor(ImGuiCol_Text, UISecondaryTextColor); ImGui::PushStyleColor(ImGuiCol_Text, UISecondaryTextColor);
ImGui::PushStyleColor(ImGuiCol_TextDisabled, UIDisabledColor); ImGui::PushStyleColor(ImGuiCol_TextDisabled, UIDisabledColor);
ImGui::PushStyleColor(ImGuiCol_Button, UISecondaryColor); ImGui::PushStyleColor(ImGuiCol_Button, UISecondaryColor);
@ -448,6 +471,7 @@ void ImGuiFullscreen::EndLayout()
DrawFileSelector(); DrawFileSelector();
DrawChoiceDialog(); DrawChoiceDialog();
DrawInputDialog(); DrawInputDialog();
DrawMessageDialog();
const float notification_margin = LayoutScale(10.0f); const float notification_margin = LayoutScale(10.0f);
const float spacing = LayoutScale(10.0f); const float spacing = LayoutScale(10.0f);
@ -460,7 +484,7 @@ void ImGuiFullscreen::EndLayout()
DrawToast(); DrawToast();
ImGui::PopStyleColor(10); ImGui::PopStyleColor(10);
ImGui::PopStyleVar(2); ImGui::PopStyleVar(12);
} }
void ImGuiFullscreen::QueueResetFocus() void ImGuiFullscreen::QueueResetFocus()
@ -536,10 +560,12 @@ void ImGuiFullscreen::PopSecondaryColor()
ImGui::PopStyleColor(5); ImGui::PopStyleColor(5);
} }
bool ImGuiFullscreen::BeginFullscreenColumns(const char* title) bool ImGuiFullscreen::BeginFullscreenColumns(const char* title, float pos_y, bool expand_to_screen_width)
{ {
ImGui::SetNextWindowPos(ImVec2(g_layout_padding_left, 0.0f)); ImGui::SetNextWindowPos(ImVec2(expand_to_screen_width ? 0.0f : g_layout_padding_left, pos_y));
ImGui::SetNextWindowSize(ImVec2(LayoutScale(LAYOUT_SCREEN_WIDTH), ImGui::GetIO().DisplaySize.y)); ImGui::SetNextWindowSize(
ImVec2(expand_to_screen_width ? ImGui::GetIO().DisplaySize.x : LayoutScale(LAYOUT_SCREEN_WIDTH),
ImGui::GetIO().DisplaySize.y - pos_y));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
@ -569,8 +595,16 @@ void ImGuiFullscreen::EndFullscreenColumns()
bool ImGuiFullscreen::BeginFullscreenColumnWindow(float start, float end, const char* name, const ImVec4& background) bool ImGuiFullscreen::BeginFullscreenColumnWindow(float start, float end, const char* name, const ImVec4& background)
{ {
const ImVec2 pos(LayoutScale(start), 0.0f); start = LayoutScale(start);
const ImVec2 size(LayoutScale(end - start), ImGui::GetIO().DisplaySize.y); end = LayoutScale(end);
if (start < 0.0f)
start = ImGui::GetIO().DisplaySize.x + start;
if (end <= 0.0f)
end = ImGui::GetIO().DisplaySize.x + end;
const ImVec2 pos(start, 0.0f);
const ImVec2 size(end - start, ImGui::GetCurrentWindow()->Size.y);
ImGui::PushStyleColor(ImGuiCol_ChildBg, background); ImGui::PushStyleColor(ImGuiCol_ChildBg, background);
@ -891,6 +925,33 @@ bool ImGuiFullscreen::MenuButton(const char* title, const char* summary, bool en
return pressed; return pressed;
} }
bool ImGuiFullscreen::MenuButtonWithoutSummary(const char* title, bool enabled, float height, ImFont* font,
const ImVec2& text_align)
{
ImRect bb;
bool visible, hovered;
bool pressed = MenuButtonFrame(title, enabled, height, &visible, &hovered, &bb);
if (!visible)
return false;
const float midpoint = bb.Min.y + font->FontSize + LayoutScale(4.0f);
const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint));
const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max);
if (!enabled)
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled));
ImGui::PushFont(font);
ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title, nullptr, nullptr, text_align, &title_bb);
ImGui::PopFont();
if (!enabled)
ImGui::PopStyleColor();
s_menu_button_index++;
return pressed;
}
bool ImGuiFullscreen::MenuImageButton(const char* title, const char* summary, ImTextureID user_texture_id, bool ImGuiFullscreen::MenuImageButton(const char* title, const char* summary, ImTextureID user_texture_id,
const ImVec2& image_size, bool enabled, float height, const ImVec2& uv0, const ImVec2& image_size, bool enabled, float height, const ImVec2& uv0,
const ImVec2& uv1, ImFont* title_font, ImFont* summary_font) const ImVec2& uv1, ImFont* title_font, ImFont* summary_font)
@ -1215,6 +1276,7 @@ bool ImGuiFullscreen::RangeButton(const char* title, const char* summary, s32* v
bool changed = false; bool changed = false;
ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f)); ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::PushFont(g_large_font); ImGui::PushFont(g_large_font);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
@ -1286,6 +1348,7 @@ bool ImGuiFullscreen::RangeButton(const char* title, const char* summary, float*
bool changed = false; bool changed = false;
ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f)); ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::PushFont(g_large_font); ImGui::PushFont(g_large_font);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
@ -1652,7 +1715,7 @@ void ImGuiFullscreen::DrawFileSelector()
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIBackgroundColor); ImGui::PushStyleColor(ImGuiCol_PopupBg, MulAlpha(UIBackgroundColor, 0.95f));
bool is_open = !WantsToCloseMenu(); bool is_open = !WantsToCloseMenu();
bool directory_selected = false; bool directory_selected = false;
@ -1755,16 +1818,6 @@ void ImGuiFullscreen::DrawChoiceDialog()
if (!s_choice_dialog_open) if (!s_choice_dialog_open)
return; return;
const float width = 600.0f;
const float title_height =
g_large_font->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f;
const float height =
std::min(400.0f, title_height + (LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY + (LAYOUT_MENU_BUTTON_Y_PADDING * 2.0f)) *
static_cast<float>(s_choice_dialog_options.size()));
ImGui::SetNextWindowSize(LayoutScale(width, height));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup(s_choice_dialog_title.c_str());
ImGui::PushFont(g_large_font); ImGui::PushFont(g_large_font);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
@ -1772,7 +1825,18 @@ void ImGuiFullscreen::DrawChoiceDialog()
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIBackgroundColor); ImGui::PushStyleColor(ImGuiCol_PopupBg, MulAlpha(UIBackgroundColor, 0.95f));
const float width = LayoutScale(600.0f);
const float title_height =
g_large_font->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f;
const float height =
std::min(LayoutScale(400.0f),
title_height + LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY + (LAYOUT_MENU_BUTTON_Y_PADDING * 2.0f)) *
static_cast<float>(s_choice_dialog_options.size()));
ImGui::SetNextWindowSize(ImVec2(width, height));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup(s_choice_dialog_title.c_str());
bool is_open = !WantsToCloseMenu(); bool is_open = !WantsToCloseMenu();
s32 choice = -1; s32 choice = -1;
@ -1871,9 +1935,14 @@ void ImGuiFullscreen::DrawInputDialog()
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup(s_input_dialog_title.c_str()); ImGui::OpenPopup(s_input_dialog_title.c_str());
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f));
ImGui::PushFont(g_large_font); ImGui::PushFont(g_large_font);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING));
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
ImGui::PushStyleColor(ImGuiCol_PopupBg, MulAlpha(UIBackgroundColor, 0.95f));
bool is_open = true; bool is_open = true;
if (ImGui::BeginPopupModal(s_input_dialog_title.c_str(), &is_open, if (ImGui::BeginPopupModal(s_input_dialog_title.c_str(), &is_open,
@ -1881,16 +1950,25 @@ void ImGuiFullscreen::DrawInputDialog()
ImGuiWindowFlags_NoMove)) ImGuiWindowFlags_NoMove))
{ {
ImGui::TextWrapped("%s", s_input_dialog_message.c_str()); ImGui::TextWrapped("%s", s_input_dialog_message.c_str());
ImGui::NewLine();
if (!s_input_dialog_caption.empty())
ImGui::TextUnformatted(s_input_dialog_caption.c_str());
ImGui::InputText("##input", &s_input_dialog_text);
ImGui::NewLine();
BeginMenuButtons(); BeginMenuButtons();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
if (!s_input_dialog_caption.empty())
{
const float prev = ImGui::GetCursorPosX();
ImGui::TextUnformatted(s_input_dialog_caption.c_str());
ImGui::SetNextItemWidth(ImGui::GetCursorPosX() - prev);
}
else
{
ImGui::SetNextItemWidth(ImGui::GetCurrentWindow()->WorkRect.GetWidth());
}
ImGui::InputText("##input", &s_input_dialog_text);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
const bool ok_enabled = !s_input_dialog_text.empty(); const bool ok_enabled = !s_input_dialog_text.empty();
if (ActiveButton(s_input_dialog_ok_text.c_str(), false, ok_enabled) && ok_enabled) if (ActiveButton(s_input_dialog_ok_text.c_str(), false, ok_enabled) && ok_enabled)
@ -1917,8 +1995,9 @@ void ImGuiFullscreen::DrawInputDialog()
if (!is_open) if (!is_open)
CloseInputDialog(); CloseInputDialog();
ImGui::PopFont(); ImGui::PopStyleColor(4);
ImGui::PopStyleVar(2); ImGui::PopStyleVar(2);
ImGui::PopFont();
} }
void ImGuiFullscreen::CloseInputDialog() void ImGuiFullscreen::CloseInputDialog()
@ -1935,6 +2014,137 @@ void ImGuiFullscreen::CloseInputDialog()
s_input_dialog_callback = {}; s_input_dialog_callback = {};
} }
bool ImGuiFullscreen::IsMessageBoxDialogOpen()
{
return s_message_dialog_open;
}
void ImGuiFullscreen::OpenConfirmMessageDialog(std::string title, std::string message,
ConfirmMessageDialogCallback callback, std::string yes_button_text,
std::string no_button_text)
{
CloseMessageDialog();
s_message_dialog_open = true;
s_message_dialog_title = std::move(title);
s_message_dialog_message = std::move(message);
s_message_dialog_callback = std::move(callback);
s_message_dialog_buttons[0] = std::move(yes_button_text);
s_message_dialog_buttons[1] = std::move(no_button_text);
}
void ImGuiFullscreen::OpenInfoMessageDialog(std::string title, std::string message, InfoMessageDialogCallback callback,
std::string button_text)
{
CloseMessageDialog();
s_message_dialog_open = true;
s_message_dialog_title = std::move(title);
s_message_dialog_message = std::move(message);
s_message_dialog_callback = std::move(callback);
s_message_dialog_buttons[0] = std::move(button_text);
}
void ImGuiFullscreen::OpenMessageDialog(std::string title, std::string message, MessageDialogCallback callback,
std::string first_button_text, std::string second_button_text,
std::string third_button_text)
{
CloseMessageDialog();
s_message_dialog_open = true;
s_message_dialog_title = std::move(title);
s_message_dialog_message = std::move(message);
s_message_dialog_callback = std::move(callback);
s_message_dialog_buttons[0] = std::move(first_button_text);
s_message_dialog_buttons[1] = std::move(second_button_text);
s_message_dialog_buttons[2] = std::move(third_button_text);
}
void ImGuiFullscreen::CloseMessageDialog()
{
if (!s_message_dialog_open)
return;
s_message_dialog_open = false;
s_message_dialog_title = {};
s_message_dialog_message = {};
s_message_dialog_buttons = {};
s_message_dialog_callback = {};
}
void ImGuiFullscreen::DrawMessageDialog()
{
if (!s_message_dialog_open)
return;
const char* win_id = s_message_dialog_title.empty() ? "##messagedialog" : s_message_dialog_title.c_str();
ImGui::SetNextWindowSize(LayoutScale(700.0f, 0.0f));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup(win_id);
ImGui::PushFont(g_large_font);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING));
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
ImGui::PushStyleColor(ImGuiCol_PopupBg, MulAlpha(UIBackgroundColor, 0.95f));
bool is_open = true;
const u32 flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
(s_message_dialog_title.empty() ? ImGuiWindowFlags_NoTitleBar : 0);
std::optional<s32> result;
if (ImGui::BeginPopupModal(win_id, &is_open, flags))
{
BeginMenuButtons();
ImGui::TextWrapped("%s", s_message_dialog_message.c_str());
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(20.0f));
for (s32 button_index = 0; button_index < static_cast<s32>(s_message_dialog_buttons.size()); button_index++)
{
if (!s_message_dialog_buttons[button_index].empty() &&
ActiveButton(s_message_dialog_buttons[button_index].c_str(), false))
{
result = button_index;
ImGui::CloseCurrentPopup();
}
}
EndMenuButtons();
ImGui::EndPopup();
}
ImGui::PopStyleColor(4);
ImGui::PopStyleVar(3);
ImGui::PopFont();
if (!is_open || result.has_value())
{
// have to move out in case they open another dialog in the callback
auto cb = (std::move(s_message_dialog_callback));
CloseMessageDialog();
if (std::holds_alternative<InfoMessageDialogCallback>(cb))
{
const InfoMessageDialogCallback& func = std::get<InfoMessageDialogCallback>(cb);
if (func)
func();
}
else if (std::holds_alternative<ConfirmMessageDialogCallback>(cb))
{
const ConfirmMessageDialogCallback& func = std::get<ConfirmMessageDialogCallback>(cb);
if (func)
func(result.value_or(1) == 0);
}
}
}
static float s_notification_vertical_position = 0.3f; static float s_notification_vertical_position = 0.3f;
static float s_notification_vertical_direction = -1.0f; static float s_notification_vertical_direction = -1.0f;
@ -1965,10 +2175,12 @@ void ImGuiFullscreen::OpenBackgroundProgressDialog(const char* str_id, std::stri
std::unique_lock<std::mutex> lock(s_background_progress_lock); std::unique_lock<std::mutex> lock(s_background_progress_lock);
#ifdef _DEBUG
for (const BackgroundProgressDialogData& data : s_background_progress_dialogs) for (const BackgroundProgressDialogData& data : s_background_progress_dialogs)
{ {
DebugAssert(data.id != id); DebugAssert(data.id != id);
} }
#endif
BackgroundProgressDialogData data; BackgroundProgressDialogData data;
data.id = id; data.id = id;
@ -2271,9 +2483,10 @@ void ImGuiFullscreen::DrawToast()
} }
} }
void ImGuiFullscreen::SetTheme() void ImGuiFullscreen::SetTheme(bool light)
{ {
#if 1 if (!light)
{
// dark // dark
UIBackgroundColor = HEX_TO_IMVEC4(0x212121, 0xff); UIBackgroundColor = HEX_TO_IMVEC4(0x212121, 0xff);
UIBackgroundTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); UIBackgroundTextColor = HEX_TO_IMVEC4(0xffffff, 0xff);
@ -2290,7 +2503,9 @@ void ImGuiFullscreen::SetTheme()
UISecondaryLightColor = HEX_TO_IMVEC4(0x63a4ff, 0xff); UISecondaryLightColor = HEX_TO_IMVEC4(0x63a4ff, 0xff);
UISecondaryDarkColor = HEX_TO_IMVEC4(0x002171, 0xff); UISecondaryDarkColor = HEX_TO_IMVEC4(0x002171, 0xff);
UISecondaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); UISecondaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff);
#elif 1 }
else
{
// light // light
UIBackgroundColor = HEX_TO_IMVEC4(0xf5f5f6, 0xff); UIBackgroundColor = HEX_TO_IMVEC4(0xf5f5f6, 0xff);
UIBackgroundTextColor = HEX_TO_IMVEC4(0x000000, 0xff); UIBackgroundTextColor = HEX_TO_IMVEC4(0x000000, 0xff);
@ -2307,5 +2522,5 @@ void ImGuiFullscreen::SetTheme()
UISecondaryLightColor = HEX_TO_IMVEC4(0xc0cfff, 0xff); UISecondaryLightColor = HEX_TO_IMVEC4(0xc0cfff, 0xff);
UISecondaryDarkColor = HEX_TO_IMVEC4(0x0031ca, 0xff); UISecondaryDarkColor = HEX_TO_IMVEC4(0x0031ca, 0xff);
UISecondaryTextColor = HEX_TO_IMVEC4(0x000000, 0xff); UISecondaryTextColor = HEX_TO_IMVEC4(0x000000, 0xff);
#endif }
} }

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "IconsFontAwesome5.h"
#include "common/types.h" #include "common/types.h"
#include "imgui.h" #include "imgui.h"
#include "imgui_internal.h" #include "imgui_internal.h"
@ -100,6 +101,11 @@ static ALWAYS_INLINE ImVec4 ModAlpha(const ImVec4& v, float a)
return ImVec4(v.x, v.y, v.z, a); return ImVec4(v.x, v.y, v.z, a);
} }
static ALWAYS_INLINE ImVec4 MulAlpha(const ImVec4& v, float a)
{
return ImVec4(v.x, v.y, v.z, v.w * a);
}
/// Centers an image within the specified bounds, scaling up or down as needed. /// Centers an image within the specified bounds, scaling up or down as needed.
ImRect CenterImage(const ImVec2& fit_size, const ImVec2& image_size); ImRect CenterImage(const ImVec2& fit_size, const ImVec2& image_size);
ImRect CenterImage(const ImRect& fit_rect, const ImVec2& image_size); ImRect CenterImage(const ImRect& fit_rect, const ImVec2& image_size);
@ -107,7 +113,7 @@ ImRect CenterImage(const ImRect& fit_rect, const ImVec2& image_size);
/// Initializes, setting up any state. /// Initializes, setting up any state.
bool Initialize(const char* placeholder_image_path); bool Initialize(const char* placeholder_image_path);
void SetTheme(); void SetTheme(bool light);
void SetFonts(ImFont* standard_font, ImFont* medium_font, ImFont* large_font); void SetFonts(ImFont* standard_font, ImFont* medium_font, ImFont* large_font);
bool UpdateLayoutScale(); bool UpdateLayoutScale();
@ -137,7 +143,7 @@ void PopSecondaryColor();
void DrawWindowTitle(const char* title); void DrawWindowTitle(const char* title);
bool BeginFullscreenColumns(const char* title = nullptr); bool BeginFullscreenColumns(const char* title = nullptr, float pos_y = 0.0f, bool expand_to_screen_width = false);
void EndFullscreenColumns(); void EndFullscreenColumns();
bool BeginFullscreenColumnWindow(float start, float end, const char* name, bool BeginFullscreenColumnWindow(float start, float end, const char* name,
@ -163,6 +169,9 @@ bool ActiveButton(const char* title, bool is_active, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font); float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font);
bool MenuButton(const char* title, const char* summary, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT, bool MenuButton(const char* title, const char* summary, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); ImFont* font = g_large_font, ImFont* summary_font = g_medium_font);
bool MenuButtonWithoutSummary(const char* title, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font,
const ImVec2& text_align = ImVec2(0.0f, 0.0f));
bool MenuButtonWithValue(const char* title, const char* summary, const char* value, bool enabled = true, bool MenuButtonWithValue(const char* title, const char* summary, const char* value, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font,
ImFont* summary_font = g_medium_font); ImFont* summary_font = g_medium_font);
@ -236,9 +245,23 @@ void CloseChoiceDialog();
using InputStringDialogCallback = std::function<void(std::string text)>; using InputStringDialogCallback = std::function<void(std::string text)>;
bool IsInputDialogOpen(); bool IsInputDialogOpen();
void OpenInputStringDialog(std::string title, std::string message, std::string caption, std::string ok_button_text, InputStringDialogCallback callback); void OpenInputStringDialog(std::string title, std::string message, std::string caption, std::string ok_button_text,
InputStringDialogCallback callback);
void CloseInputDialog(); void CloseInputDialog();
using ConfirmMessageDialogCallback = std::function<void(bool)>;
using InfoMessageDialogCallback = std::function<void()>;
using MessageDialogCallback = std::function<void(s32)>;
bool IsMessageBoxDialogOpen();
void OpenConfirmMessageDialog(std::string title, std::string message, ConfirmMessageDialogCallback callback,
std::string yes_button_text = ICON_FA_CHECK " Yes",
std::string no_button_text = ICON_FA_TIMES " No");
void OpenInfoMessageDialog(std::string title, std::string message, InfoMessageDialogCallback callback = {},
std::string button_text = ICON_FA_WINDOW_CLOSE " Close");
void OpenMessageDialog(std::string title, std::string message, MessageDialogCallback callback,
std::string first_button_text, std::string second_button_text, std::string third_button_text);
void CloseMessageDialog();
float GetNotificationVerticalPosition(); float GetNotificationVerticalPosition();
float GetNotificationVerticalDirection(); float GetNotificationVerticalDirection();
void SetNotificationVerticalPosition(float position, float direction); void SetNotificationVerticalPosition(float position, float direction);

View File

@ -458,18 +458,7 @@ ImFont* ImGuiManager::AddFixedFont(float size)
bool ImGuiManager::AddIconFonts(float size) bool ImGuiManager::AddIconFonts(float size)
{ {
static constexpr ImWchar range_fa[] = { static constexpr ImWchar range_fa[] = { 0xf002,0xf002,0xf005,0xf005,0xf007,0xf007,0xf00c,0xf00e,0xf011,0xf011,0xf013,0xf013,0xf017,0xf017,0xf019,0xf019,0xf021,0xf021,0xf025,0xf025,0xf027,0xf028,0xf02d,0xf02e,0xf030,0xf030,0xf03a,0xf03a,0xf03d,0xf03d,0xf049,0xf04c,0xf050,0xf050,0xf059,0xf059,0xf05e,0xf05e,0xf065,0xf065,0xf067,0xf067,0xf071,0xf071,0xf075,0xf075,0xf077,0xf078,0xf07b,0xf07c,0xf084,0xf085,0xf091,0xf091,0xf0a0,0xf0a0,0xf0ac,0xf0ad,0xf0c5,0xf0c5,0xf0c7,0xf0c8,0xf0cb,0xf0cb,0xf0d0,0xf0d0,0xf0e2,0xf0e2,0xf0eb,0xf0eb,0xf0f1,0xf0f1,0xf0f3,0xf0f3,0xf0fe,0xf0fe,0xf110,0xf110,0xf119,0xf119,0xf11b,0xf11c,0xf140,0xf140,0xf144,0xf144,0xf14a,0xf14a,0xf15b,0xf15b,0xf188,0xf188,0xf191,0xf192,0xf1dd,0xf1de,0xf1e6,0xf1e6,0xf1eb,0xf1eb,0xf1f8,0xf1f8,0xf1fc,0xf1fc,0xf242,0xf242,0xf245,0xf245,0xf26c,0xf26c,0xf279,0xf279,0xf2d0,0xf2d0,0xf2db,0xf2db,0xf2f2,0xf2f2,0xf2f5,0xf2f5,0xf410,0xf410,0xf466,0xf466,0xf500,0xf500,0xf51f,0xf51f,0xf545,0xf545,0xf548,0xf548,0xf552,0xf552,0xf57a,0xf57a,0xf5a2,0xf5a2,0xf5e7,0xf5e7,0xf65d,0xf65e,0xf6a9,0xf6a9,0xf7c2,0xf7c2,0xf807,0xf807,0xf815,0xf815,0xf818,0xf818,0xf84c,0xf84c,0xf8cc,0xf8cc,0x0,0x0 };
0xf002, 0xf002, 0xf005, 0xf005, 0xf007, 0xf007, 0xf00c, 0xf00e, 0xf011, 0xf011, 0xf013, 0xf013, 0xf017, 0xf017,
0xf019, 0xf019, 0xf021, 0xf021, 0xf025, 0xf025, 0xf027, 0xf028, 0xf02d, 0xf02e, 0xf030, 0xf030, 0xf03a, 0xf03a,
0xf03d, 0xf03d, 0xf049, 0xf04c, 0xf050, 0xf050, 0xf059, 0xf059, 0xf05e, 0xf05e, 0xf065, 0xf065, 0xf067, 0xf067,
0xf071, 0xf071, 0xf075, 0xf075, 0xf077, 0xf078, 0xf07b, 0xf07c, 0xf084, 0xf085, 0xf091, 0xf091, 0xf0a0, 0xf0a0,
0xf0ac, 0xf0ad, 0xf0c5, 0xf0c5, 0xf0c7, 0xf0c8, 0xf0cb, 0xf0cb, 0xf0d0, 0xf0d0, 0xf0e2, 0xf0e2, 0xf0eb, 0xf0eb,
0xf0f1, 0xf0f1, 0xf0f3, 0xf0f3, 0xf0fe, 0xf0fe, 0xf110, 0xf110, 0xf119, 0xf119, 0xf11b, 0xf11c, 0xf140, 0xf140,
0xf144, 0xf144, 0xf14a, 0xf14a, 0xf15b, 0xf15b, 0xf188, 0xf188, 0xf191, 0xf192, 0xf1dd, 0xf1de, 0xf1e6, 0xf1e6,
0xf1eb, 0xf1eb, 0xf1f8, 0xf1f8, 0xf242, 0xf242, 0xf245, 0xf245, 0xf26c, 0xf26c, 0xf279, 0xf279, 0xf2d0, 0xf2d0,
0xf2db, 0xf2db, 0xf2f2, 0xf2f2, 0xf2f5, 0xf2f5, 0xf410, 0xf410, 0xf466, 0xf466, 0xf500, 0xf500, 0xf51f, 0xf51f,
0xf545, 0xf545, 0xf548, 0xf548, 0xf552, 0xf552, 0xf57a, 0xf57a, 0xf5a2, 0xf5a2, 0xf5e7, 0xf5e7, 0xf65d, 0xf65e,
0xf6a9, 0xf6a9, 0xf7c2, 0xf7c2, 0xf807, 0xf807, 0xf815, 0xf815, 0xf818, 0xf818, 0xf8cc, 0xf8cc, 0x0, 0x0};
ImFontConfig cfg; ImFontConfig cfg;
cfg.MergeMode = true; cfg.MergeMode = true;