[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 {
color: $primary;
}
QWidget#SettingsTab > QScrollArea {
background: transparent;
border: none;
}
QWidget#SettingsTab > QScrollArea > QWidget > QWidget {
background: transparent;
}
QWidget#sidebarContainer {
background: $tertiary;
min-width: 300px;
max-width: 300px;
}
QWidget#sidebarTitleLabel {
color: $primary;
font-size: 32px;
}
QWidget#sidebarTitle #XSeperator {
background: $dark1;
}
QWidget#navigationContainer {
background: $dark2;
}
color: $primary;
}
QWidget#SettingsTab>QScrollArea {
background: transparent;
border: none;
}
QWidget#SettingsTab>QScrollArea>QWidget>QWidget {
background: transparent;
}
QWidget#sidebarContainer {
background: $tertiary;
min-width: 300px;
max-width: 300px;
}
QWidget#sidebarTitleLabel {
color: $primary;
font-size: 32px;
}
QWidget#sidebarTitle #XSeperator {
background: $dark1;
}
QWidget#navigationContainer {
background: $dark2;
}
QLabel#subLabel[type="warning"] {
color: $warning;
}

View File

@ -21,6 +21,7 @@
#include <QApplication>
#include <QFontDatabase>
#include <QScreen>
#include <QtPlugin>
#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 "
"the emulator executable directory if portable.txt is present in it.",
"Storage");
DEFINE_path(
content_root, "",
"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->SetIcon(QIcon(":/resources/graphics/icon.ico"));
main_wnd->Resize(1280, 720);
loop.on_quit.AddListener([](ui::UIEvent*) {
main_wnd->move(QApplication::primaryScreen()->geometry().center() -
main_wnd->rect().center());
loop.on_quit.AddListener([](ui::UIEvent*)
{
if (cvars::discord) {
discord::DiscordPresence::Shutdown();
}

View File

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

View File

@ -7,6 +7,11 @@
#include "xenia/config.h"
#include "xenia/ui/qt/themeable_widget.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 ui {
@ -33,11 +38,6 @@ class SettingsPane : public Themeable<QWidget> {
protected:
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:
QChar glyph_;
@ -45,31 +45,6 @@ class SettingsPane : public Themeable<QWidget> {
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 ui
} // namespace xe

View File

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

View File

@ -18,19 +18,14 @@ namespace xe {
namespace ui {
namespace qt {
using SettingsType = bool;
using SettingsCvar = cvar::ConfigVar<bool>;
class SettingsCheckBox : public SettingsWidget<SettingsType, XCheckBox> {
class SettingsCheckBox : public SettingsWidget<bool, XCheckBox> {
public:
explicit SettingsCheckBox(const std::string& config_name,
explicit SettingsCheckBox(const QString& text,
SettingsCvar* config_var = nullptr, QLabel* label = nullptr,
QWidget* parent = nullptr)
: SettingsWidget(config_name, parent) {
Initialize();
}
explicit SettingsCheckBox(const SettingsType& config_var,
QWidget* parent = nullptr)
: SettingsWidget(config_var, parent) {
: SettingsWidget(config_var, label, text, parent) {
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_
#define XENIA_UI_QT_SETTINGS_SETTINGS_COMBOBOX_H_
@ -16,4 +25,6 @@ class SettingsComboBox : SettingsWidget<T, XComboBox> {
} // namespace qt
} // 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_
#define XENIA_UI_QT_SETTINGS_SETTINGS_RADIOBOX_H_
#include "settings_widget.h"
#include "xenia/ui/qt/widgets/radiobox.h"
#include "xenia/ui/qt/widgets/radio_button.h"
namespace xe {
namespace ui {
namespace qt {
class SettingsRadioBox : SettingsWidget<int, XRadioBox> {};
class SettingsRadioButton : SettingsWidget<int, XRadioButton> {};
} // namespace qt
} // 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_
#define XENIA_UI_QT_SETTINGS_SETTINGS_TEXT_EDIT_H_
@ -10,14 +19,10 @@ namespace xe {
namespace ui {
namespace qt {
template <typename T>
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");
};
class SettingsTextEdit : SettingsWidget<std::string, XTextEdit> {};
} // namespace qt
} // namespace ui
} // namespace xe
} // namespace xe
#endif

View File

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