mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-25 16:22:47 +01:00
Load custom Qt themes from yuzu data directory
- Directory is qt_themes, each theme must be in one folder - It should contain a file "style.qss" - It may contain an "icons" sub-directory, to override included icons (with files like mytheme/icons/colorful/48x48/star.png for example) - Directories ending by "_dark" are reserved for dark variant icons. They are not listed as themes in the UI. - If theme directory contains "dark" or "midnight", theme will be considered dark
This commit is contained in:
parent
70c52a1914
commit
97814d3e59
10 changed files with 102 additions and 62 deletions
|
@ -15,6 +15,7 @@
|
||||||
#define CONFIG_DIR "config"
|
#define CONFIG_DIR "config"
|
||||||
#define CRASH_DUMPS_DIR "crash_dumps"
|
#define CRASH_DUMPS_DIR "crash_dumps"
|
||||||
#define DUMP_DIR "dump"
|
#define DUMP_DIR "dump"
|
||||||
|
#define ICONS_DIR "icons"
|
||||||
#define KEYS_DIR "keys"
|
#define KEYS_DIR "keys"
|
||||||
#define LOAD_DIR "load"
|
#define LOAD_DIR "load"
|
||||||
#define LOG_DIR "log"
|
#define LOG_DIR "log"
|
||||||
|
@ -24,7 +25,7 @@
|
||||||
#define SDMC_DIR "sdmc"
|
#define SDMC_DIR "sdmc"
|
||||||
#define SHADER_DIR "shader"
|
#define SHADER_DIR "shader"
|
||||||
#define TAS_DIR "tas"
|
#define TAS_DIR "tas"
|
||||||
#define ICONS_DIR "icons"
|
#define THEMES_DIR "qt_themes"
|
||||||
|
|
||||||
// suyu-specific files
|
// suyu-specific files
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,7 @@ public:
|
||||||
GenerateSuyuPath(SuyuPath::ConfigDir, suyu_path_config);
|
GenerateSuyuPath(SuyuPath::ConfigDir, suyu_path_config);
|
||||||
GenerateSuyuPath(SuyuPath::CrashDumpsDir, suyu_path / CRASH_DUMPS_DIR);
|
GenerateSuyuPath(SuyuPath::CrashDumpsDir, suyu_path / CRASH_DUMPS_DIR);
|
||||||
GenerateSuyuPath(SuyuPath::DumpDir, suyu_path / DUMP_DIR);
|
GenerateSuyuPath(SuyuPath::DumpDir, suyu_path / DUMP_DIR);
|
||||||
|
GenerateSuyuPath(SuyuPath::IconsDir, suyu_path / ICONS_DIR);
|
||||||
GenerateSuyuPath(SuyuPath::KeysDir, suyu_path / KEYS_DIR);
|
GenerateSuyuPath(SuyuPath::KeysDir, suyu_path / KEYS_DIR);
|
||||||
GenerateSuyuPath(SuyuPath::LoadDir, suyu_path / LOAD_DIR);
|
GenerateSuyuPath(SuyuPath::LoadDir, suyu_path / LOAD_DIR);
|
||||||
GenerateSuyuPath(SuyuPath::LogDir, suyu_path / LOG_DIR);
|
GenerateSuyuPath(SuyuPath::LogDir, suyu_path / LOG_DIR);
|
||||||
|
@ -130,7 +131,7 @@ public:
|
||||||
GenerateSuyuPath(SuyuPath::SDMCDir, suyu_path / SDMC_DIR);
|
GenerateSuyuPath(SuyuPath::SDMCDir, suyu_path / SDMC_DIR);
|
||||||
GenerateSuyuPath(SuyuPath::ShaderDir, suyu_path / SHADER_DIR);
|
GenerateSuyuPath(SuyuPath::ShaderDir, suyu_path / SHADER_DIR);
|
||||||
GenerateSuyuPath(SuyuPath::TASDir, suyu_path / TAS_DIR);
|
GenerateSuyuPath(SuyuPath::TASDir, suyu_path / TAS_DIR);
|
||||||
GenerateSuyuPath(SuyuPath::IconsDir, suyu_path / ICONS_DIR);
|
GenerateSuyuPath(SuyuPath::ThemesDir, suyu_path / THEMES_DIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -17,6 +17,7 @@ enum class SuyuPath {
|
||||||
ConfigDir, // Where config files are stored.
|
ConfigDir, // Where config files are stored.
|
||||||
CrashDumpsDir, // Where crash dumps are stored.
|
CrashDumpsDir, // Where crash dumps are stored.
|
||||||
DumpDir, // Where dumped data is stored.
|
DumpDir, // Where dumped data is stored.
|
||||||
|
IconsDir, // Where Icons for Windows shortcuts are stored.
|
||||||
KeysDir, // Where key files are stored.
|
KeysDir, // Where key files are stored.
|
||||||
LoadDir, // Where cheat/mod files are stored.
|
LoadDir, // Where cheat/mod files are stored.
|
||||||
LogDir, // Where log files are stored.
|
LogDir, // Where log files are stored.
|
||||||
|
@ -26,7 +27,7 @@ enum class SuyuPath {
|
||||||
SDMCDir, // Where the emulated SDMC is stored.
|
SDMCDir, // Where the emulated SDMC is stored.
|
||||||
ShaderDir, // Where shaders are stored.
|
ShaderDir, // Where shaders are stored.
|
||||||
TASDir, // Where TAS scripts are stored.
|
TASDir, // Where TAS scripts are stored.
|
||||||
IconsDir, // Where Icons for Windows shortcuts are stored.
|
ThemesDir, // Where users should put their custom themes
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -106,11 +106,31 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
|
||||||
|
|
||||||
InitializeLanguageComboBox();
|
InitializeLanguageComboBox();
|
||||||
|
|
||||||
for (const auto& theme : UISettings::themes) {
|
for (const auto& theme : UISettings::included_themes) {
|
||||||
ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
|
ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
|
||||||
QString::fromUtf8(theme.second));
|
QString::fromUtf8(theme.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add custom styles stored in yuzu directory
|
||||||
|
const QDir themes_local_dir(
|
||||||
|
QString::fromStdString(Common::FS::GetSuyuPathString(Common::FS::SuyuPath::ThemesDir)));
|
||||||
|
for (const QString& theme_dir :
|
||||||
|
themes_local_dir.entryList(QDir::NoDot | QDir::NoDotDot | QDir::Dirs)) {
|
||||||
|
// folders ending with "_dark" are reserved for dark variant icons of other styles
|
||||||
|
if (theme_dir.endsWith(QStringLiteral("_dark"))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Split at _ and capitalize words in name
|
||||||
|
QStringList cased_name;
|
||||||
|
for (QString word : theme_dir.split(QChar::fromLatin1('_'))) {
|
||||||
|
cased_name.append(word.at(0).toUpper() + word.mid(1));
|
||||||
|
}
|
||||||
|
QString theme_name = cased_name.join(QChar::fromLatin1(' '));
|
||||||
|
theme_name += QStringLiteral(" (%1)").arg(tr("Custom"));
|
||||||
|
|
||||||
|
ui->theme_combobox->addItem(theme_name, themes_local_dir.filePath(theme_dir));
|
||||||
|
}
|
||||||
|
|
||||||
InitializeIconSizeComboBox();
|
InitializeIconSizeComboBox();
|
||||||
InitializeRowComboBoxes();
|
InitializeRowComboBoxes();
|
||||||
|
|
||||||
|
@ -164,7 +184,7 @@ ConfigureUi::~ConfigureUi() = default;
|
||||||
|
|
||||||
void ConfigureUi::ApplyConfiguration() {
|
void ConfigureUi::ApplyConfiguration() {
|
||||||
UISettings::values.theme =
|
UISettings::values.theme =
|
||||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString().toStdString();
|
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||||
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
|
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
|
||||||
UISettings::values.show_compat = ui->show_compat->isChecked();
|
UISettings::values.show_compat = ui->show_compat->isChecked();
|
||||||
UISettings::values.show_size = ui->show_size->isChecked();
|
UISettings::values.show_size = ui->show_size->isChecked();
|
||||||
|
@ -191,8 +211,7 @@ void ConfigureUi::RequestGameListUpdate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureUi::SetConfiguration() {
|
void ConfigureUi::SetConfiguration() {
|
||||||
ui->theme_combobox->setCurrentIndex(
|
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||||
ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
|
|
||||||
ui->language_combobox->setCurrentIndex(ui->language_combobox->findData(
|
ui->language_combobox->setCurrentIndex(ui->language_combobox->findData(
|
||||||
QString::fromStdString(UISettings::values.language.GetValue())));
|
QString::fromStdString(UISettings::values.language.GetValue())));
|
||||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
|
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
|
||||||
|
|
|
@ -260,9 +260,8 @@ void QtConfig::ReadShortcutValues() {
|
||||||
void QtConfig::ReadUIValues() {
|
void QtConfig::ReadUIValues() {
|
||||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
|
||||||
|
|
||||||
UISettings::values.theme = ReadStringSetting(
|
UISettings::values.theme = QString::fromStdString(
|
||||||
std::string("theme"),
|
ReadStringSetting(std::string("theme"), std::string(UISettings::default_theme)));
|
||||||
std::string(UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second));
|
|
||||||
|
|
||||||
ReadUIGamelistValues();
|
ReadUIGamelistValues();
|
||||||
ReadUILayoutValues();
|
ReadUILayoutValues();
|
||||||
|
@ -468,10 +467,8 @@ void QtConfig::SaveUIValues() {
|
||||||
WriteCategory(Settings::Category::Ui);
|
WriteCategory(Settings::Category::Ui);
|
||||||
WriteCategory(Settings::Category::UiGeneral);
|
WriteCategory(Settings::Category::UiGeneral);
|
||||||
|
|
||||||
WriteStringSetting(
|
WriteStringSetting(std::string("theme"), UISettings::values.theme.toStdString(),
|
||||||
std::string("theme"), UISettings::values.theme,
|
std::make_optional(std::string(UISettings::default_theme)));
|
||||||
std::make_optional(std::string(
|
|
||||||
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
|
|
||||||
|
|
||||||
SaveUIGamelistValues();
|
SaveUIGamelistValues();
|
||||||
SaveUILayoutValues();
|
SaveUILayoutValues();
|
||||||
|
|
|
@ -35,9 +35,8 @@ constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{
|
||||||
}};
|
}};
|
||||||
|
|
||||||
bool IsDarkTheme() {
|
bool IsDarkTheme() {
|
||||||
const auto& theme = UISettings::values.theme;
|
return UISettings::values.theme.contains(QStringLiteral("dark")) ||
|
||||||
return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
|
UISettings::values.theme.contains(QStringLiteral("midnight"));
|
||||||
theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -3542,7 +3542,7 @@ void GMainWindow::ResetWindowSize1080() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnConfigure() {
|
void GMainWindow::OnConfigure() {
|
||||||
const auto old_theme = UISettings::values.theme;
|
const QString old_theme = UISettings::values.theme;
|
||||||
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
|
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
|
||||||
const auto old_language_index = Settings::values.language_index.GetValue();
|
const auto old_language_index = Settings::values.language_index.GetValue();
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
|
@ -4812,9 +4812,8 @@ static void AdjustLinkColor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateUITheme() {
|
void GMainWindow::UpdateUITheme() {
|
||||||
const QString default_theme = QString::fromUtf8(
|
QString default_theme = QString::fromStdString(UISettings::default_theme.data());
|
||||||
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second);
|
QString current_theme = UISettings::values.theme;
|
||||||
QString current_theme = QString::fromStdString(UISettings::values.theme);
|
|
||||||
|
|
||||||
if (current_theme.isEmpty()) {
|
if (current_theme.isEmpty()) {
|
||||||
current_theme = default_theme;
|
current_theme = default_theme;
|
||||||
|
@ -4825,6 +4824,7 @@ void GMainWindow::UpdateUITheme() {
|
||||||
AdjustLinkColor();
|
AdjustLinkColor();
|
||||||
#else
|
#else
|
||||||
if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) {
|
if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) {
|
||||||
|
LOG_INFO(Frontend, "Theme is default or colorful: {}", current_theme.toStdString());
|
||||||
QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme
|
QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme
|
||||||
: startup_icon_theme);
|
: startup_icon_theme);
|
||||||
QIcon::setThemeSearchPaths(QStringList(default_theme_paths));
|
QIcon::setThemeSearchPaths(QStringList(default_theme_paths));
|
||||||
|
@ -4832,35 +4832,57 @@ void GMainWindow::UpdateUITheme() {
|
||||||
current_theme = QStringLiteral("default_dark");
|
current_theme = QStringLiteral("default_dark");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
LOG_INFO(Frontend, "Theme is NOT default or colorful: {}", current_theme.toStdString());
|
||||||
QIcon::setThemeName(current_theme);
|
QIcon::setThemeName(current_theme);
|
||||||
QIcon::setThemeSearchPaths(QStringList(QStringLiteral(":/icons")));
|
// Use icon resources from application binary and current theme subdirectory if it exists
|
||||||
|
QStringList theme_paths;
|
||||||
|
theme_paths << QString::fromStdString(":/icons")
|
||||||
|
<< QStringLiteral("%1/%2/icons")
|
||||||
|
.arg(QString::fromStdString(
|
||||||
|
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ThemesDir)),
|
||||||
|
current_theme);
|
||||||
|
QIcon::setThemeSearchPaths(theme_paths);
|
||||||
AdjustLinkColor();
|
AdjustLinkColor();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (current_theme != default_theme) {
|
if (current_theme != default_theme) {
|
||||||
QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
|
QString theme_uri{current_theme + QStringLiteral("style.qss")};
|
||||||
QFile f(theme_uri);
|
if (tryLoadStylesheet(theme_uri)) {
|
||||||
if (!f.open(QFile::ReadOnly | QFile::Text)) {
|
return;
|
||||||
LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
|
|
||||||
UISettings::values.theme);
|
|
||||||
current_theme = default_theme;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
|
// Reading new theme failed, loading default stylesheet
|
||||||
QFile f(theme_uri);
|
LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
|
||||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
current_theme.toStdString());
|
||||||
QTextStream ts(&f);
|
|
||||||
qApp->setStyleSheet(ts.readAll());
|
current_theme = default_theme;
|
||||||
setStyleSheet(ts.readAll());
|
theme_uri = QStringLiteral(":%1/style.qss").arg(default_theme);
|
||||||
} else {
|
if (tryLoadStylesheet(theme_uri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading default failed, loading empty stylesheet
|
||||||
LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
|
LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
|
||||||
UISettings::values.theme);
|
current_theme.toStdString());
|
||||||
|
|
||||||
qApp->setStyleSheet({});
|
qApp->setStyleSheet({});
|
||||||
setStyleSheet({});
|
setStyleSheet({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GMainWindow::tryLoadStylesheet(const QString& theme_path) {
|
||||||
|
QFile theme_file(theme_path);
|
||||||
|
if (theme_file.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
LOG_INFO(Frontend, "Loading style in: {}", theme_path.toStdString());
|
||||||
|
QTextStream ts(&theme_file);
|
||||||
|
qApp->setStyleSheet(ts.readAll());
|
||||||
|
setStyleSheet(ts.readAll());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Opening the file failed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::LoadTranslation() {
|
void GMainWindow::LoadTranslation() {
|
||||||
bool loaded;
|
bool loaded;
|
||||||
|
|
||||||
|
@ -4919,7 +4941,7 @@ void GMainWindow::changeEvent(QEvent* event) {
|
||||||
// UpdateUITheme is a decent work around
|
// UpdateUITheme is a decent work around
|
||||||
if (event->type() == QEvent::PaletteChange) {
|
if (event->type() == QEvent::PaletteChange) {
|
||||||
const QPalette test_palette(qApp->palette());
|
const QPalette test_palette(qApp->palette());
|
||||||
const QString current_theme = QString::fromStdString(UISettings::values.theme);
|
const QString& current_theme = UISettings::values.theme;
|
||||||
// Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
|
// Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
|
||||||
static QColor last_window_color;
|
static QColor last_window_color;
|
||||||
const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
|
const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
|
||||||
|
|
|
@ -165,6 +165,12 @@ class GMainWindow : public QMainWindow {
|
||||||
CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING,
|
CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to load a stylesheet from its path. If the path starts with ":/", its embedded in the app
|
||||||
|
* @returns true if the text file could be opened as read-only
|
||||||
|
*/
|
||||||
|
bool tryLoadStylesheet(const QString& theme_path);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void filterBarSetChecked(bool state);
|
void filterBarSetChecked(bool state);
|
||||||
void UpdateUITheme();
|
void UpdateUITheme();
|
||||||
|
|
|
@ -22,19 +22,18 @@ namespace FS = Common::FS;
|
||||||
|
|
||||||
namespace UISettings {
|
namespace UISettings {
|
||||||
|
|
||||||
const Themes themes{{
|
const Themes included_themes{{
|
||||||
{"Default", "default"},
|
{"Default", ":/default/"},
|
||||||
{"Default Colorful", "colorful"},
|
{"Default Colorful", ":/colorful/"},
|
||||||
{"Dark", "qdarkstyle"},
|
{"Dark", ":/qdarkstyle/"},
|
||||||
{"Dark Colorful", "colorful_dark"},
|
{"Dark Colorful", ":/colorful_dark/"},
|
||||||
{"Midnight Blue", "qdarkstyle_midnight_blue"},
|
{"Midnight Blue", ":/qdarkstyle_midnight_blue/"},
|
||||||
{"Midnight Blue Colorful", "colorful_midnight_blue"},
|
{"Midnight Blue Colorful", ":/colorful_midnight_blue/"},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
bool IsDarkTheme() {
|
bool IsDarkTheme() {
|
||||||
const auto& theme = UISettings::values.theme;
|
return UISettings::values.theme.contains(QStringLiteral("dark")) ||
|
||||||
return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
|
UISettings::values.theme.contains(QStringLiteral("midnight"));
|
||||||
theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Values values = {};
|
Values values = {};
|
||||||
|
|
|
@ -35,6 +35,10 @@ extern template class Setting<unsigned long long>;
|
||||||
|
|
||||||
namespace UISettings {
|
namespace UISettings {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the theme is dark
|
||||||
|
* @returns true if the current theme contains the string "dark" in its name
|
||||||
|
*/
|
||||||
bool IsDarkTheme();
|
bool IsDarkTheme();
|
||||||
|
|
||||||
struct ContextualShortcut {
|
struct ContextualShortcut {
|
||||||
|
@ -50,25 +54,16 @@ struct Shortcut {
|
||||||
ContextualShortcut shortcut;
|
ContextualShortcut shortcut;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Theme {
|
static constexpr std::string_view default_theme{
|
||||||
Default,
|
|
||||||
DefaultColorful,
|
|
||||||
Dark,
|
|
||||||
DarkColorful,
|
|
||||||
MidnightBlue,
|
|
||||||
MidnightBlueColorful,
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr Theme default_theme{
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Theme::DarkColorful
|
"colorful_dark"
|
||||||
#else
|
#else
|
||||||
Theme::DefaultColorful
|
"colorful"
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
using Themes = std::array<std::pair<const char*, const char*>, 6>;
|
using Themes = std::array<std::pair<const char*, const char*>, 6>;
|
||||||
extern const Themes themes;
|
extern const Themes included_themes;
|
||||||
|
|
||||||
struct GameDir {
|
struct GameDir {
|
||||||
std::string path;
|
std::string path;
|
||||||
|
@ -160,7 +155,7 @@ struct Values {
|
||||||
QStringList recent_files;
|
QStringList recent_files;
|
||||||
Setting<std::string> language{linkage, {}, "language", Category::Paths};
|
Setting<std::string> language{linkage, {}, "language", Category::Paths};
|
||||||
|
|
||||||
std::string theme;
|
QString theme;
|
||||||
|
|
||||||
// Shortcut name <Shortcut, context>
|
// Shortcut name <Shortcut, context>
|
||||||
std::vector<Shortcut> shortcuts;
|
std::vector<Shortcut> shortcuts;
|
||||||
|
|
Loading…
Reference in a new issue