[Qt] Initial settings subsystem rework

This commit is contained in:
Satori 2020-09-26 23:28:16 +01:00
parent b2d7ea11f5
commit 6e59a5c5ef
12 changed files with 281 additions and 164 deletions

View File

@ -1,31 +1,35 @@
QWidget#SettingsTab QLabel { QWidget#SettingsTab QLabel {
color: $primary; color: $primary;
} }
QWidget#SettingsTab > QScrollArea { QWidget#SettingsTab>QScrollArea {
background: transparent; background: transparent;
border: none; border: none;
} }
QWidget#SettingsTab > QScrollArea > QWidget > QWidget {
background: transparent; QWidget#SettingsTab>QScrollArea>QWidget>QWidget {
} background: transparent;
}
QWidget#sidebarContainer {
background: $tertiary; QWidget#sidebarContainer {
min-width: 300px; background: $tertiary;
max-width: 300px; min-width: 300px;
} max-width: 300px;
}
QWidget#sidebarTitleLabel {
color: $primary; QWidget#sidebarTitleLabel {
font-size: 32px; color: $primary;
} font-size: 32px;
}
QWidget#sidebarTitle #XSeperator {
background: $dark1; QWidget#sidebarTitle #XSeperator {
} background: $dark1;
}
QWidget#navigationContainer {
background: $dark2; QWidget#navigationContainer {
} background: $dark2;
}
QLabel#subLabel[type="warning"] {
color: $warning;
}

View File

@ -21,6 +21,7 @@
#include <QApplication> #include <QApplication>
#include <QFontDatabase> #include <QFontDatabase>
#include <QScreen>
#include <QtPlugin> #include <QtPlugin>
#include "discord/discord_presence.h" #include "discord/discord_presence.h"
@ -47,6 +48,7 @@ DEFINE_path(
"to use the path preferred for the OS, such as the documents folder, or " "to use the path preferred for the OS, such as the documents folder, or "
"the emulator executable directory if portable.txt is present in it.", "the emulator executable directory if portable.txt is present in it.",
"Storage"); "Storage");
DEFINE_path( DEFINE_path(
content_root, "", content_root, "",
"Root path for guest content storage (saves, etc.), or empty to use the " "Root path for guest content storage (saves, etc.), or empty to use the "
@ -126,8 +128,10 @@ int xenia_main(const std::vector<std::string>& args) {
main_wnd->Initialize(); main_wnd->Initialize();
main_wnd->SetIcon(QIcon(":/resources/graphics/icon.ico")); main_wnd->SetIcon(QIcon(":/resources/graphics/icon.ico"));
main_wnd->Resize(1280, 720); main_wnd->Resize(1280, 720);
main_wnd->move(QApplication::primaryScreen()->geometry().center() -
loop.on_quit.AddListener([](ui::UIEvent*) { main_wnd->rect().center());
loop.on_quit.AddListener([](ui::UIEvent*)
{
if (cvars::discord) { if (cvars::discord) {
discord::DiscordPresence::Shutdown(); discord::DiscordPresence::Shutdown();
} }

View File

@ -4,6 +4,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include "xenia/ui/qt/settings/widgets/settings_checkbox.h" #include "xenia/ui/qt/settings/widgets/settings_checkbox.h"
#include "xenia/ui/qt/settings/widgets/settings_groupbox.h"
#include "xenia/ui/qt/widgets/combobox.h" #include "xenia/ui/qt/widgets/combobox.h"
#include "xenia/ui/qt/widgets/groupbox.h" #include "xenia/ui/qt/widgets/groupbox.h"
#include "xenia/ui/qt/widgets/scroll_area.h" #include "xenia/ui/qt/widgets/scroll_area.h"
@ -12,86 +13,70 @@ DECLARE_bool(show_debug_tab);
DECLARE_bool(discord); DECLARE_bool(discord);
DECLARE_bool(use_game_icon); DECLARE_bool(use_game_icon);
namespace xe { namespace xe {
namespace ui { namespace ui {
namespace qt { namespace qt {
const QStringList game_languages = { const QStringList game_languages = {
"English", "Japanese", "German", "French", "Spanish", "English", "Japanese", "German", "French", "Spanish",
"Italian", "Korean", "Chinese", "Portuguese", "Polish", "Italian", "Korean", "Chinese", "Portuguese", "Polish",
"Russian", "Swedish", "Turkish", "Norwegian", "Dutch"}; "Russian", "Swedish", "Turkish", "Norwegian", "Dutch"};
void GeneralPane::Build() { void GeneralPane::Build() {
QWidget* base_widget = new QWidget(); QWidget* base_widget = new QWidget();
base_widget->setSizePolicy(QSizePolicy::MinimumExpanding, base_widget->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding); QSizePolicy::MinimumExpanding);
// Setup scroll area for settings pane // Setup scroll area for settings pane
XScrollArea* scroll_area = new XScrollArea(this); XScrollArea* scroll_area = new XScrollArea(this);
scroll_area->setWidget(base_widget); scroll_area->setWidget(base_widget);
scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll_area->setWidgetResizable(true); scroll_area->setWidgetResizable(true);
QVBoxLayout* layout = new QVBoxLayout(); QVBoxLayout* layout = new QVBoxLayout();
base_widget->setLayout(layout); base_widget->setLayout(layout);
layout->setSpacing(16); layout->setSpacing(16);
layout->setContentsMargins(32, 16, 32, 16); layout->setContentsMargins(32, 16, 32, 16);
// Add settings groupboxes to layout // Add settings groupboxes to layout
layout->addWidget(CreateGeneralGroupBox()); layout->addWidget(CreateGeneralGroupBox());
layout->addWidget(CreateUpdateGroupBox()); layout->addWidget(CreateUpdateGroupBox());
layout->addWidget(CreateWindowGroupBox()); layout->addWidget(CreateWindowGroupBox());
layout->addWidget(CreateLogGroupBox()); layout->addWidget(CreateLogGroupBox());
layout->addStretch(); layout->addStretch();
set_widget(scroll_area); set_widget(scroll_area);
} }
XGroupBox* GeneralPane::CreateGeneralGroupBox() { XGroupBox* GeneralPane::CreateGeneralGroupBox() {
XGroupBox* groupbox = new XGroupBox("General Settings"); auto groupbox = new SettingsGroupBox("General Settings");
QVBoxLayout* groupbox_layout = new QVBoxLayout(); auto& config = Config::Instance();
groupbox_layout->setContentsMargins(16, 16, 16, 16); auto discord_cvar = config.FindConfigVar(cvars::discord);
groupbox->setLayout(groupbox_layout); auto discord_checkbox = groupbox->CreateCheckBox("Discord Rich Presence", discord_cvar);
auto discord_presence_checkbox = new SettingsCheckBox(cvars::discord); discord_checkbox->set_update_config_fn(
discord_presence_checkbox->setText("Discord Rich Presence"); [discord_checkbox](bool value, cvar::ConfigVar<bool>& cvar) {
cvar.set_config_value(value);
groupbox_layout->addWidget(discord_presence_checkbox); discord_checkbox->UpdateLabel(
tr("Please restart xenia for this change to take effect."));
});
XCheckBox* game_icon_checkbox = new XCheckBox(); return groupbox;
game_icon_checkbox->setText("Show Game Icon in Taskbar"); }
groupbox_layout->addWidget(game_icon_checkbox);
QHBoxLayout* game_language_layout = new QHBoxLayout(); XGroupBox* GeneralPane::CreateUpdateGroupBox() {
game_language_layout->setContentsMargins(0, 0, 0, 0); return new XGroupBox("Update Settings");
game_language_layout->setSpacing(16); }
QLabel* game_language_label = new QLabel("Game Language"); XGroupBox* GeneralPane::CreateWindowGroupBox() {
XComboBox* game_language_combobox = new XComboBox(); return new XGroupBox("Window Settings");
game_language_combobox->addItems(game_languages); }
game_language_layout->addWidget(game_language_label); XGroupBox* GeneralPane::CreateLogGroupBox() {
game_language_layout->addWidget(game_language_combobox); return new XGroupBox("Log Settings");
game_language_layout->addStretch(); }
groupbox_layout->addLayout(game_language_layout);
return groupbox;
}
XGroupBox* GeneralPane::CreateUpdateGroupBox() {
return new XGroupBox("Update Settings");
}
XGroupBox* GeneralPane::CreateWindowGroupBox() {
return new XGroupBox("Window Settings");
}
XGroupBox* GeneralPane::CreateLogGroupBox() {
return new XGroupBox("Log Settings");
}
} // namespace qt } // namespace qt
} // namespace ui } // namespace ui

View File

@ -7,6 +7,11 @@
#include "xenia/config.h" #include "xenia/config.h"
#include "xenia/ui/qt/themeable_widget.h" #include "xenia/ui/qt/themeable_widget.h"
#include "xenia/config.h" #include "xenia/config.h"
#include "xenia/ui/qt/settings/widgets/settings_widget.h"
#include "xenia/ui/qt/settings/widgets/settings_radio_button.h"
#include "xenia/ui/qt/settings/widgets/settings_combobox.h"
#include "xenia/ui/qt/settings/widgets/settings_text_edit.h"
#include "xenia/ui/qt/settings/widgets/settings_checkbox.h"
namespace xe { namespace xe {
namespace ui { namespace ui {
@ -33,11 +38,6 @@ class SettingsPane : public Themeable<QWidget> {
protected: protected:
void set_widget(QWidget* widget) { widget_ = widget; } void set_widget(QWidget* widget) { widget_ = widget; }
template <typename T>
bool update_config_var(cvar::ConfigVar<T>* var, const T& value) const;
template <typename T>
bool update_config_var(cvar::ConfigVar<T>* var, QVariant value) const;
private: private:
QChar glyph_; QChar glyph_;
@ -45,31 +45,6 @@ class SettingsPane : public Themeable<QWidget> {
QWidget* widget_ = nullptr; QWidget* widget_ = nullptr;
}; };
template <typename T>
bool SettingsPane::update_config_var(cvar::ConfigVar<T>* var,
const T& value) const {
var->SetConfigValue(value);
Config::Instance().SaveConfig();
return true;
}
template <typename T>
bool SettingsPane::update_config_var(cvar::ConfigVar<T>* var,
QVariant value) const {
// QVariant can't be converted to type T
if (!value.canConvert<T>()) {
return false;
}
var->SetConfigValue(value.value<T>());
Config::Instance().SaveConfig();
return true;
}
} // namespace qt } // namespace qt
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe

View File

@ -20,7 +20,7 @@ void SettingsCheckBox::Initialize() {
setChecked(*cvar_->current_value()); setChecked(*cvar_->current_value());
connect(this, &SettingsCheckBox::stateChanged, [&](int state) { connect(this, &SettingsCheckBox::stateChanged, [this](int state) {
if (state == Qt::Checked) { if (state == Qt::Checked) {
UpdateValue(true); UpdateValue(true);
} else if (state == Qt::Unchecked) { } else if (state == Qt::Unchecked) {

View File

@ -18,19 +18,14 @@ namespace xe {
namespace ui { namespace ui {
namespace qt { namespace qt {
using SettingsType = bool; using SettingsCvar = cvar::ConfigVar<bool>;
class SettingsCheckBox : public SettingsWidget<SettingsType, XCheckBox> { class SettingsCheckBox : public SettingsWidget<bool, XCheckBox> {
public: public:
explicit SettingsCheckBox(const std::string& config_name, explicit SettingsCheckBox(const QString& text,
SettingsCvar* config_var = nullptr, QLabel* label = nullptr,
QWidget* parent = nullptr) QWidget* parent = nullptr)
: SettingsWidget(config_name, parent) { : SettingsWidget(config_var, label, text, parent) {
Initialize();
}
explicit SettingsCheckBox(const SettingsType& config_var,
QWidget* parent = nullptr)
: SettingsWidget(config_var, parent) {
Initialize(); Initialize();
} }

View File

@ -1,3 +1,12 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_QT_SETTINGS_SETTINGS_COMBOBOX_H_ #ifndef XENIA_UI_QT_SETTINGS_SETTINGS_COMBOBOX_H_
#define XENIA_UI_QT_SETTINGS_SETTINGS_COMBOBOX_H_ #define XENIA_UI_QT_SETTINGS_SETTINGS_COMBOBOX_H_
@ -16,4 +25,6 @@ class SettingsComboBox : SettingsWidget<T, XComboBox> {
} // namespace qt } // namespace qt
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe
#endif

View File

@ -0,0 +1,57 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "settings_groupbox.h"
namespace xe {
namespace ui {
namespace qt {
double kSubLabelSize = 6.5;
SettingsCheckBox* SettingsGroupBox::CreateCheckBox(const QString& text,
cvar::ConfigVar<bool>* target) {
auto checkbox_layout = new QVBoxLayout();
checkbox_layout->setContentsMargins(0, 0, 0, 0);
checkbox_layout->setSpacing(0);
auto widget_label = new QLabel();
widget_label->setObjectName("subLabel");
widget_label->setProperty("type", "warning");
auto font = widget_label->font();
font.setPointSizeF(kSubLabelSize);
widget_label->setFont(font);
auto checkbox = new SettingsCheckBox(text, target, widget_label);
checkbox_layout->addWidget(checkbox);
checkbox_layout->addWidget(widget_label);
layout_->addLayout(checkbox_layout);
return checkbox;
}
SettingsComboBox<int>* SettingsGroupBox::CreateComboBox(const QString& text,
cvar::ConfigVar<int>* target) {
return nullptr;
}
SettingsComboBox<std::string>* SettingsGroupBox::CreateComboBox(
const QString& text, cvar::ConfigVar<std::string>* target) {
return nullptr;
}
SettingsRadioButton* SettingsGroupBox::CreateRadioButton(const QString& text,
cvar::ConfigVar<int>* target) {
return nullptr;
}
}
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,52 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_QT_SETTINGS_SETTINGS_GROUPBOX_H_
#define XENIA_UI_QT_SETTINGS_SETTINGS_GROUPBOX_H_
#include "settings_checkbox.h"
#include "settings_combobox.h"
#include "settings_radio_button.h"
#include "xenia/ui/qt/widgets/groupbox.h"
namespace xe {
namespace ui {
namespace qt {
class SettingsGroupBox : public XGroupBox {
public:
SettingsGroupBox(const QString& title, QWidget* parent = nullptr)
: XGroupBox(title, parent), layout_(nullptr) {
layout_ = new QVBoxLayout();
this->setLayout(layout_);
}
SettingsCheckBox* CreateCheckBox(const QString& text,
cvar::ConfigVar<bool>* target = nullptr);
SettingsComboBox<int>* CreateComboBox(const QString& text,
cvar::ConfigVar<int>* target = nullptr);
SettingsComboBox<std::string>* CreateComboBox(
const QString& text, cvar::ConfigVar<std::string>* target = nullptr);
SettingsRadioButton* CreateRadioButton(
const QString& text, cvar::ConfigVar<int>* target = nullptr);
template <typename T>
void AddSettingsWidget(SettingsWidget<T>* widget) {
layout_->addWidget(widget);
}
private:
QVBoxLayout* layout_;
};
} // namespace qt
} // namespace ui
} // namespace xe
#endif

View File

@ -1,15 +1,26 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_QT_SETTINGS_SETTINGS_RADIOBOX_H_ #ifndef XENIA_UI_QT_SETTINGS_SETTINGS_RADIOBOX_H_
#define XENIA_UI_QT_SETTINGS_SETTINGS_RADIOBOX_H_ #define XENIA_UI_QT_SETTINGS_SETTINGS_RADIOBOX_H_
#include "settings_widget.h" #include "settings_widget.h"
#include "xenia/ui/qt/widgets/radiobox.h" #include "xenia/ui/qt/widgets/radio_button.h"
namespace xe { namespace xe {
namespace ui { namespace ui {
namespace qt { namespace qt {
class SettingsRadioBox : SettingsWidget<int, XRadioBox> {}; class SettingsRadioButton : SettingsWidget<int, XRadioButton> {};
} // namespace qt } // namespace qt
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe
#endif

View File

@ -1,3 +1,12 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_QT_SETTINGS_SETTINGS_TEXT_EDIT_H_ #ifndef XENIA_UI_QT_SETTINGS_SETTINGS_TEXT_EDIT_H_
#define XENIA_UI_QT_SETTINGS_SETTINGS_TEXT_EDIT_H_ #define XENIA_UI_QT_SETTINGS_SETTINGS_TEXT_EDIT_H_
@ -10,14 +19,10 @@ namespace xe {
namespace ui { namespace ui {
namespace qt { namespace qt {
template <typename T> class SettingsTextEdit : SettingsWidget<std::string, XTextEdit> {};
class SettingsTextEdit : SettingsWidget<T, XRadioBox> {
static_assert(
std::is_same_v<T, std::string> ||
std::is_same_v<T, std::filesystem::path>,
"Settings TextEdit must use std::string or std::filesystem::path");
};
} // namespace qt } // namespace qt
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe
#endif

View File

@ -10,6 +10,9 @@
#ifndef XENIA_UI_QT_SETTINGS_SETTINGS_WIDGET_H_ #ifndef XENIA_UI_QT_SETTINGS_SETTINGS_WIDGET_H_
#define XENIA_UI_QT_SETTINGS_SETTINGS_WIDGET_H_ #define XENIA_UI_QT_SETTINGS_SETTINGS_WIDGET_H_
#include <QLabel>
#include <QVBoxLayout>
#include <functional>
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
#include "xenia/config.h" #include "xenia/config.h"
#include "xenia/ui/qt/themeable_widget.h" #include "xenia/ui/qt/themeable_widget.h"
@ -18,38 +21,53 @@ namespace xe {
namespace ui { namespace ui {
namespace qt { namespace qt {
template <typename T, typename Widget> template <typename T, typename Widget = QWidget>
class SettingsWidget : public Widget { class SettingsWidget : public Widget {
static_assert(std::is_base_of_v<QWidget, Widget>,
"SettingsWidget base must be a derivative of QWidget");
public: public:
template <typename... Args> template <typename... Args>
SettingsWidget(const std::string& config_name, Args... args) SettingsWidget(cvar::ConfigVar<T>* config_var, QLabel* label = nullptr,
: Widget(args...), cvar_(nullptr) { Args... args)
cvar_ = dynamic_cast<cvar::ConfigVar<T>*>( : Widget(args...), cvar_(config_var), label_(label) {
Config::Instance().FindConfigVarByName(config_name)); // default config update function
update_config_fn_ = [](T val, cvar::ConfigVar<T>& cvar) {
cvar.set_config_value(val);
};
} }
template <typename... Args> void UpdateLabel(const QString& text) {
SettingsWidget(const T& config_ref, Args... args) if (label_) {
: Widget(args...), cvar_(nullptr) { label_->setText(text);
cvar_ = dynamic_cast<cvar::ConfigVar<T>*>( }
Config::Instance().FindConfigVar(config_ref)); }
cvar::ConfigVar<T>* config_var() const { return cvar_; }
void set_config_var(cvar::ConfigVar<T>* cvar) { cvar_ = cvar; }
void set_update_config_fn(
const std::function<void(T, cvar::ConfigVar<T>&)>& fn) {
update_config_fn_ = fn;
} }
void UpdateValue(T val) { void UpdateValue(T val) {
if (cvar_) { if (cvar_) {
cvar_->set_config_value(val); update_config_fn_(val, *cvar_);
SaveToConfig();
} }
Config::Instance().SaveConfig();
} }
void SaveToConfig() { Config::Instance().SaveConfig(); } void SaveToConfig() { Config::Instance().SaveConfig(); }
protected: protected:
cvar::ConfigVar<T>* cvar_; cvar::ConfigVar<T>* cvar_;
std::function<void(T, cvar::ConfigVar<T>&)> update_config_fn_;
QLabel* label_;
}; };
} // namespace qt } // namespace qt
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe
#endif #endif