[Qt] Implement settings widgets that use abstract settings items

This commit is contained in:
Satori 2021-10-02 15:38:53 +01:00
parent 566d992f76
commit 4749ee1f03
11 changed files with 502 additions and 50 deletions

View File

@ -33,3 +33,7 @@ QWidget#navigationContainer {
QLabel#subLabel[type="warning"] { QLabel#subLabel[type="warning"] {
color: $warning; color: $warning;
} }
QWidget#SettingsTab QWidget#settingsContainer QLabel#titleLabel {
color: $light2;
}

View File

@ -9,12 +9,13 @@
#include "settings_widget_factory.h" #include "settings_widget_factory.h"
#include <QHBoxLayout> #include <QHBoxLayout>
#include "xenia/ui/qt/widgets/checkbox.h" #include "xenia/ui/qt/settings/widgets/settings_checkbox.h"
#include "xenia/ui/qt/settings/widgets/settings_combobox.h"
#include "xenia/ui/qt/settings/widgets/settings_line_edit.h"
#include "xenia/ui/qt/settings/widgets/settings_slider.h"
#include "xenia/ui/qt/widgets/groupbox.h" #include "xenia/ui/qt/widgets/groupbox.h"
#include "xenia/ui/qt/widgets/line_edit.h"
#include "xenia/ui/qt/widgets/push_button.h" #include "xenia/ui/qt/widgets/push_button.h"
#include "xenia/ui/qt/widgets/scroll_area.h" #include "xenia/ui/qt/widgets/scroll_area.h"
#include "xenia/ui/qt/widgets/slider.h"
namespace xe { namespace xe {
namespace ui { namespace ui {
@ -23,6 +24,12 @@ namespace qt {
const double kSubLabelSize = 6.5; const double kSubLabelSize = 6.5;
const int kLineEditMaxWidth = 420; const int kLineEditMaxWidth = 420;
QLabel* create_title_label(const std::string& title) {
auto label = new QLabel(title.c_str());
label->setObjectName("titleLabel");
return label;
}
QWidget* SettingsWidgetFactory::BuildSettingsWidget( QWidget* SettingsWidgetFactory::BuildSettingsWidget(
const std::string& set_name) { const std::string& set_name) {
const auto& sets = settings_.settings(); const auto& sets = settings_.settings();
@ -53,6 +60,8 @@ QWidget* SettingsWidgetFactory::BuildSettingsWidget(
layout->addWidget(group_box); layout->addWidget(group_box);
} }
base_widget->setObjectName("settingsContainer");
return base_widget; return base_widget;
} }
@ -81,7 +90,8 @@ QWidget* SettingsWidgetFactory::CreateWidgetForSettingsItem(
break; break;
} }
case settings::SettingsType::MultiChoice: { case settings::SettingsType::MultiChoice: {
return nullptr; // TODO: return CreateMultiChoiceWidget(
dynamic_cast<settings::IMultiChoiceSettingsItem&>(item));
break; break;
} }
case settings::SettingsType::Range: { case settings::SettingsType::Range: {
@ -109,20 +119,7 @@ QWidget* SettingsWidgetFactory::CreateWidgetForSettingsItem(
QWidget* SettingsWidgetFactory::CreateCheckBoxWidget( QWidget* SettingsWidgetFactory::CreateCheckBoxWidget(
settings::BooleanSettingsItem& item) { settings::BooleanSettingsItem& item) {
XCheckBox* checkbox = new XCheckBox(); SettingsCheckBox* checkbox = new SettingsCheckBox(item);
checkbox->setText(item.title().c_str());
checkbox->setCheckState(*item.cvar()->current_value() ? Qt::Checked
: Qt::Unchecked);
XCheckBox::connect(checkbox, &XCheckBox::stateChanged, [&](int state) {
if (state == Qt::Checked) {
item.UpdateValue(true);
} else if (state == Qt::Unchecked) {
item.UpdateValue(false);
} else {
XELOGW("PartiallyChecked state not supported for SettingsCheckBox");
}
});
return CreateWidgetContainer(checkbox); return CreateWidgetContainer(checkbox);
} }
@ -133,19 +130,9 @@ QWidget* SettingsWidgetFactory::CreateTextInputWidget(
QVBoxLayout* ctr_layout = new QVBoxLayout(); QVBoxLayout* ctr_layout = new QVBoxLayout();
ctr->setLayout(ctr_layout); ctr->setLayout(ctr_layout);
QLabel* title_label = new QLabel(item.title().c_str()); QLabel* title_label = create_title_label(item.title());
XLineEdit* line_edit = new XLineEdit(); SettingsLineEdit* line_edit = new SettingsLineEdit(item);
line_edit->setPlaceholderText(item.description().c_str());
line_edit->setMaximumWidth(kLineEditMaxWidth);
const auto& current_text = *item.cvar()->current_value();
line_edit->setText(QString(current_text.c_str()));
XLineEdit::connect(line_edit, &XLineEdit::textChanged,
[&](const QString& text) {
item.UpdateValue(std::string(text.toUtf8()));
});
ctr_layout->addWidget(title_label); ctr_layout->addWidget(title_label);
ctr_layout->addWidget(line_edit); ctr_layout->addWidget(line_edit);
@ -161,32 +148,20 @@ QWidget* SettingsWidgetFactory::CreatePathInputWidget(
ctr_layout->setContentsMargins(0, 0, 0, 0); ctr_layout->setContentsMargins(0, 0, 0, 0);
ctr_layout->setSpacing(8); ctr_layout->setSpacing(8);
QLabel* title_label = new QLabel(item.title().c_str()); QLabel* title_label = create_title_label(item.title());
QHBoxLayout* control_layout = new QHBoxLayout(); QHBoxLayout* control_layout = new QHBoxLayout();
control_layout->setSpacing(8); control_layout->setSpacing(8);
XLineEdit* line_edit = new XLineEdit(); SettingsLineEdit* line_edit = new SettingsLineEdit(item);
line_edit->setPlaceholderText(item.description().c_str());
line_edit->setMaximumWidth(kLineEditMaxWidth);
const auto& current_path = *item.cvar()->current_value();
std::string current_path_str = std::string(current_path.u8string());
line_edit->setText(QString(current_path_str.c_str()));
XPushButton* browse_btn = new XPushButton(); XPushButton* browse_btn = new XPushButton();
browse_btn->SetIconFromGlyph(0xE838); browse_btn->SetIconFromGlyph(0xE838);
// TODO: Setup browse button logic
control_layout->addWidget(line_edit); control_layout->addWidget(line_edit);
control_layout->addWidget(browse_btn); control_layout->addWidget(browse_btn);
control_layout->addStretch(); control_layout->addStretch();
XLineEdit::connect(line_edit, &XLineEdit::textChanged,
[&](const QString& text) {
auto path = std::string(text.toUtf8());
item.UpdateValue(std::filesystem::path(path));
});
ctr_layout->addWidget(title_label); ctr_layout->addWidget(title_label);
ctr_layout->addLayout(control_layout); ctr_layout->addLayout(control_layout);
@ -204,17 +179,42 @@ QWidget* SettingsWidgetFactory::CreateRangeInputWidget(
using xe::app::settings::ValueType; using xe::app::settings::ValueType;
QWidget* ctr = new QWidget(); QWidget* ctr = new QWidget();
QVBoxLayout* ctr_layout = new QVBoxLayout(); QHBoxLayout* ctr_layout = new QHBoxLayout();
ctr_layout->setContentsMargins(0, 0, 0, 0);
ctr_layout->setSpacing(20);
ctr->setLayout(ctr_layout); ctr->setLayout(ctr_layout);
QLabel* title_label = new QLabel(item.title().c_str()); QLabel* title_label = create_title_label(item.title());
XSlider* slider = new XSlider(); SettingsSlider* slider = new SettingsSlider(item);
int min = xe::app::settings::number_value_to_int(item.min()); int min = xe::app::settings::number_value_to_int(item.min());
int max = xe::app::settings::number_value_to_int(item.max()); int max = xe::app::settings::number_value_to_int(item.max());
SettingsSlider::connect(slider, &SettingsSlider::valueChanged,
[&](int value) { item.UpdateValue(value); });
ctr_layout->addWidget(title_label); ctr_layout->addWidget(title_label);
ctr_layout->addWidget(slider); ctr_layout->addWidget(slider);
ctr_layout->addStretch();
return CreateWidgetContainer(ctr);
}
QWidget* SettingsWidgetFactory::CreateMultiChoiceWidget(
settings::IMultiChoiceSettingsItem& item) {
QWidget* ctr = new QWidget();
QHBoxLayout* ctr_layout = new QHBoxLayout();
ctr_layout->setContentsMargins(0, 0, 0, 0);
ctr_layout->setSpacing(20);
ctr->setLayout(ctr_layout);
QLabel* title_label = create_title_label(item.title());
SettingsComboBox* combobox = new SettingsComboBox(item);
ctr_layout->addWidget(title_label);
ctr_layout->addWidget(combobox);
ctr_layout->addStretch();
return CreateWidgetContainer(ctr); return CreateWidgetContainer(ctr);
} }

View File

@ -35,7 +35,7 @@ class SettingsWidgetFactory {
QWidget* CreatePathInputWidget(settings::FilePathInputSettingsItem& item); QWidget* CreatePathInputWidget(settings::FilePathInputSettingsItem& item);
QWidget* CreateNumberInputWidget(settings::NumberInputSettingsItem& item); QWidget* CreateNumberInputWidget(settings::NumberInputSettingsItem& item);
QWidget* CreateRangeInputWidget(settings::RangeInputSettingsItem& item); QWidget* CreateRangeInputWidget(settings::RangeInputSettingsItem& item);
//QWidget* CreateMultiChoiceWidget(settings::MultiChoiceSettingsItem<>& item); QWidget* CreateMultiChoiceWidget(settings::IMultiChoiceSettingsItem& item);
QWidget* CreateActionWidget(settings::ActionSettingsItem& item); QWidget* CreateActionWidget(settings::ActionSettingsItem& item);
QWidget* CreateWidgetContainer(QWidget* target); QWidget* CreateWidgetContainer(QWidget* target);

View File

@ -0,0 +1,58 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "settings_checkbox.h"
namespace xe {
namespace ui {
namespace qt {
using namespace xe::app::settings;
using namespace xe::cvar;
SettingsCheckBox::SettingsCheckBox(BooleanSettingsItem& item)
: XCheckBox(item.title().c_str()), item_(item) {
assert_true(Initialize(), "Could not initialize SettingsCheckBox");
}
SettingsCheckBox::~SettingsCheckBox() { item_.cvar()->RemoveListener(this); }
bool SettingsCheckBox::Initialize() {
item_.cvar()->RegisterListener(this);
setCheckState(*item_.cvar()->current_value() ? Qt::Checked : Qt::Unchecked);
// update cvar when checkbox state changes
XCheckBox::connect(this, &XCheckBox::stateChanged, [&](int state) {
is_value_updating_ = true;
if (state == Qt::Checked) {
item_.UpdateValue(true);
} else if (state == Qt::Unchecked) {
item_.UpdateValue(false);
} else {
XELOGW("PartiallyChecked state not supported for SettingsCheckBox");
}
is_value_updating_ = false;
});
return true;
}
void SettingsCheckBox::OnValueUpdated(const ICommandVar& var) {
const auto& new_value = item_.cvar()->current_value();
if (!this->is_value_updating_) {
// emit state change on ui thread
QMetaObject::invokeMethod(this, "setChecked", Qt::QueuedConnection,
Q_ARG(bool, *new_value));
}
}
} // namespace qt
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,44 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_SETTINGS_CHECKBOX_H_
#define XENIA_SETTINGS_CHECKBOX_H_
#include <atomic>
#include "xenia/app/settings/settings.h"
#include "xenia/ui/qt/widgets/checkbox.h"
namespace xe {
namespace ui {
namespace qt {
using namespace xe::app::settings;
using namespace xe::cvar;
class SettingsCheckBox : public XCheckBox, ICommandVarListener {
Q_OBJECT;
public:
explicit SettingsCheckBox(BooleanSettingsItem& item);
~SettingsCheckBox();
bool Initialize();
void OnValueUpdated(const ICommandVar& var) override;
private:
BooleanSettingsItem& item_;
std::atomic_bool is_value_updating_ = false;
};
} // namespace qt
} // namespace ui
} // namespace xe
#endif

View File

@ -0,0 +1,61 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "settings_combobox.h"
namespace xe {
namespace ui {
namespace qt {
using namespace xe::app::settings;
using namespace xe::cvar;
const int kComboboxMaxWidth = 120;
SettingsComboBox::SettingsComboBox(IMultiChoiceSettingsItem& item)
: XComboBox(), item_(item) {
assert_true(Initialize(), "Could not initialize SettingsComboBox");
}
SettingsComboBox::~SettingsComboBox() { item_.cvar()->RemoveListener(this); }
bool SettingsComboBox::Initialize() {
setMaximumWidth(kComboboxMaxWidth);
// load combobox items from setting
for (const std::string& option : item_.option_names()) {
addItem(option.c_str());
}
// set default value to match cvar
setCurrentIndex(item_.current_index());
// update cvar when index changes
XComboBox::connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged),
[&](int index) {
is_value_updating_ = true;
item_.UpdateIndex(index);
is_value_updating_ = false;
});
return true;
}
void SettingsComboBox::OnValueUpdated(const ICommandVar& var) {
if (!is_value_updating_) {
int index = item_.current_index();
// emit state change on ui thread
QMetaObject::invokeMethod(this, "setCurrentIndex", Qt::QueuedConnection,
Q_ARG(int, index));
}
}
} // namespace qt
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,42 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_SETTINGS_COMBOBOX_H_
#define XENIA_SETTINGS_COMBOBOX_H_
#include "xenia/app/settings/settings.h"
#include "xenia/base/cvar.h"
#include "xenia/ui/qt/widgets/combobox.h"
namespace xe {
namespace ui {
namespace qt {
using namespace xe::app::settings;
using namespace xe::cvar;
class SettingsComboBox : public XComboBox, ICommandVarListener {
Q_OBJECT;
public:
explicit SettingsComboBox(IMultiChoiceSettingsItem& item);
~SettingsComboBox();
bool Initialize();
void OnValueUpdated(const ICommandVar& var) override;
private:
IMultiChoiceSettingsItem& item_;
std::atomic_bool is_value_updating_ = false;
};
} // namespace qt
} // namespace ui
} // namespace xe
#endif

View File

@ -0,0 +1,98 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "settings_line_edit.h"
namespace xe {
namespace ui {
namespace qt {
using namespace xe::app::settings;
using namespace xe::cvar;
const int kLineEditMaxWidth = 480;
SettingsLineEdit::SettingsLineEdit(TextInputSettingsItem& item)
: XLineEdit(), item_(item), type_(Type::Text) {
assert_true(Initialize(), "Could not initialize SettingsLineEdit");
}
SettingsLineEdit::SettingsLineEdit(FilePathInputSettingsItem& item)
: XLineEdit(), item_(item), type_(Type::Path) {
assert_true(Initialize(), "Could not initialize SettingsLineEdit");
}
SettingsLineEdit::~SettingsLineEdit() {
if (type_ == Type::Path) {
auto& item = dynamic_cast<FilePathInputSettingsItem&>(item_);
item.cvar()->RemoveListener(this);
} else if (type_ == Type::Text) {
auto& item = dynamic_cast<TextInputSettingsItem&>(item_);
item.cvar()->RemoveListener(this);
}
}
bool SettingsLineEdit::Initialize() {
if (type_ == Type::Path) {
auto item = dynamic_cast<FilePathInputSettingsItem*>(&item_);
if (!item) return false;
this->setPlaceholderText(item->description().c_str());
this->setMaximumWidth(kLineEditMaxWidth);
const auto& current_path = *item->cvar()->current_value();
std::string current_path_str = std::string(current_path.u8string());
this->setText(QString(current_path_str.c_str()));
XLineEdit::connect(this, &XLineEdit::textChanged,
[=](const QString& text) {
auto path = std::string(text.toUtf8());
item->UpdateValue(std::filesystem::path(path));
});
return true;
} else if (type_ == Type::Text) {
auto item = dynamic_cast<TextInputSettingsItem*>(&item_);
if (!item) return false;
this->setPlaceholderText(item->description().c_str());
this->setMaximumWidth(kLineEditMaxWidth);
const auto& current_text = *item->cvar()->current_value();
this->setText(QString(current_text.c_str()));
XLineEdit::connect(this, &XLineEdit::textChanged,
[=](const QString& text) {
item->UpdateValue(std::string(text.toUtf8()));
});
return true;
}
return false;
}
void SettingsLineEdit::OnValueUpdated(const ICommandVar& var) {
if (!is_value_updating_) {
QString text = this->text();
if (type_ == Type::Path) {
auto& item = dynamic_cast<FilePathInputSettingsItem&>(item_);
text = item.cvar()->current_value()->string().c_str();
} else if (type_ == Type::Text) {
auto& item = dynamic_cast<TextInputSettingsItem&>(item_);
text = item.cvar()->current_value()->c_str();
}
QMetaObject::invokeMethod(this, "setText", Qt::QueuedConnection,
Q_ARG(QString, text));
}
}
} // namespace qt
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,47 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_SETTINGS_LINE_EDIT_H_
#define XENIA_SETTINGS_LINE_EDIT_H_
#include "xenia/app/settings/settings.h"
#include "xenia/base/cvar.h"
#include "xenia/ui/qt/widgets/line_edit.h"
namespace xe {
namespace ui {
namespace qt {
using namespace xe::app::settings;
using namespace xe::cvar;
class SettingsLineEdit : public XLineEdit, ICommandVarListener {
Q_OBJECT;
enum class Type { Text, Path };
public:
explicit SettingsLineEdit(TextInputSettingsItem& item);
explicit SettingsLineEdit(FilePathInputSettingsItem& item);
~SettingsLineEdit();
bool Initialize();
void OnValueUpdated(const ICommandVar& var) override;
private:
ISettingsItem& item_;
std::atomic_bool is_value_updating_ = false;
Type type_;
};
} // namespace qt
} // namespace ui
} // namespace xe
#endif

View File

@ -0,0 +1,55 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "settings_slider.h"
namespace xe {
namespace ui {
namespace qt {
using namespace xe::app::settings;
using namespace xe::cvar;
SettingsSlider::SettingsSlider(RangeInputSettingsItem& item)
: XSlider(), item_(item) {
assert_true(Initialize(), "Could not initialize SettingsSlider");
}
SettingsSlider::~SettingsSlider() { item_.cvar()->RemoveListener(this); }
bool SettingsSlider::Initialize() {
int min = number_value_to_int(item_.min());
int max = number_value_to_int(item_.max());
this->setMinimum(min);
this->setMaximum(max);
int current_value = number_value_to_int(item_.current_value());
setValue(current_value);
XSlider::connect(this, &XSlider::valueChanged, [&](int value) {
// TODO: handle UpdateValue returning false for value out of range
item_.UpdateValue(value);
});
return true;
}
void SettingsSlider::OnValueUpdated(const ICommandVar& var) {
if (!is_value_updating_) {
int current_value = number_value_to_int(item_.current_value());
// update value on UI thread
QMetaObject::invokeMethod(this, "setValue", Qt::QueuedConnection,
Q_ARG(int, current_value));
}
}
} // namespace qt
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,43 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_SETTINGS_SLIDER_H_
#define XENIA_SETTINGS_SLIDER_H_
#include "xenia/app/settings/settings.h"
#include "xenia/base/cvar.h"
#include "xenia/ui/qt/widgets/slider.h"
namespace xe {
namespace ui {
namespace qt {
using namespace xe::app::settings;
using namespace xe::cvar;
class SettingsSlider : public XSlider, ICommandVarListener {
Q_OBJECT;
public:
explicit SettingsSlider(RangeInputSettingsItem& item);
~SettingsSlider();
bool Initialize();
void OnValueUpdated(const ICommandVar& var) override;
private:
RangeInputSettingsItem& item_;
std::atomic_bool is_value_updating_ = false;
};
} // namespace qt
} // namespace ui
} // namespace xe
#endif