SceneSwitcher/lib/utils/item-selection-helpers.cpp
WarmUpTill a51b7f6b13
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
Fix UI theming issues
2025-03-11 22:12:05 +01:00

370 lines
9.2 KiB
C++

#include "item-selection-helpers.hpp"
#include "name-dialog.hpp"
#include "obs-module-helper.hpp"
#include "ui-helpers.hpp"
#include <algorithm>
#include <QAction>
#include <QMenu>
#include <QLayout>
Q_DECLARE_METATYPE(advss::Item *);
namespace advss {
Item::Item(std::string name) : _name(name) {}
static Item *GetItemByName(const std::string &name,
std::deque<std::shared_ptr<Item>> &items)
{
for (auto &item : items) {
if (item->Name() == name) {
return item.get();
}
}
return nullptr;
}
static Item *GetItemByName(const QString &name,
std::deque<std::shared_ptr<Item>> &items)
{
return GetItemByName(name.toStdString(), items);
}
static bool ItemNameAvailable(const QString &name,
std::deque<std::shared_ptr<Item>> &items)
{
return !GetItemByName(name, items);
}
static bool ItemNameAvailable(const std::string &name,
std::deque<std::shared_ptr<Item>> &items)
{
return ItemNameAvailable(QString::fromStdString(name), items);
}
ItemSelection::ItemSelection(std::deque<std::shared_ptr<Item>> &items,
CreateItemFunc create, SettingsCallback callback,
std::string_view select, std::string_view add,
std::string_view conflict,
std::string_view configureTooltip, QWidget *parent)
: QWidget(parent),
_selection(new FilterComboBox(this, obs_module_text(select.data()))),
_modify(new QPushButton),
_create(create),
_askForSettings(callback),
_items(items),
_selectStr(select),
_addStr(add),
_conflictStr(conflict)
{
_modify->setMaximumWidth(22);
SetButtonIcon(_modify,
GetThemeTypeName() == "Light"
? ":/settings/images/settings/general.svg"
: "theme:Dark/settings/general.svg");
_modify->setFlat(true);
if (!configureTooltip.empty()) {
_modify->setToolTip(obs_module_text(configureTooltip.data()));
}
// Connect to slots
QWidget::connect(_selection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ChangeSelection(const QString &)));
QWidget::connect(_modify, SIGNAL(clicked()), this,
SLOT(ModifyButtonClicked()));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(_selection);
layout->addWidget(_modify);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
for (const auto &i : items) {
_selection->addItem(QString::fromStdString(i->_name));
}
_selection->model()->sort(0);
_selection->insertSeparator(_selection->count());
_selection->addItem(obs_module_text(_addStr.data()));
}
void ItemSelection::SetItem(const std::string &item)
{
if (!!GetItemByName(item, _items)) {
_selection->setCurrentText(QString::fromStdString(item));
if (_selection->lineEdit()) {
_selection->lineEdit()->setText(
QString::fromStdString(item));
}
} else {
_selection->setCurrentIndex(-1);
}
}
void ItemSelection::ShowRenameContextMenu(bool value)
{
_showRenameContextMenu = value;
}
void ItemSelection::ChangeSelection(const QString &sel)
{
if (sel == obs_module_text(_addStr.data())) {
auto item = _create();
bool accepted = _askForSettings(this, *item.get());
if (!accepted) {
_selection->setCurrentIndex(-1);
return;
}
_items.emplace_back(item);
const QString name = QString::fromStdString(item->_name);
AddItem(name);
_selection->setCurrentText(name);
emit ItemAdded(name);
emit SelectionChanged(name);
return;
}
auto item = GetCurrentItem();
if (item) {
emit SelectionChanged(QString::fromStdString(item->_name));
} else {
emit SelectionChanged("");
}
}
void ItemSelection::ModifyButtonClicked()
{
auto item = GetCurrentItem();
if (!item) {
return;
}
auto properties = [&]() {
const auto oldName = item->_name;
bool accepted = _askForSettings(this, *item);
if (!accepted) {
return;
}
if (oldName != item->_name) {
emit ItemRenamed(QString::fromStdString(oldName),
QString::fromStdString(item->_name));
}
};
QMenu menu(this);
QAction *action;
if (_showRenameContextMenu) {
action = new QAction(
obs_module_text("AdvSceneSwitcher.item.rename"), &menu);
connect(action, SIGNAL(triggered()), this, SLOT(RenameItem()));
action->setProperty("item", QVariant::fromValue(item));
menu.addAction(action);
}
action = new QAction(obs_module_text("AdvSceneSwitcher.item.remove"),
&menu);
connect(action, SIGNAL(triggered()), this, SLOT(RemoveItem()));
menu.addAction(action);
action = new QAction(
obs_module_text("AdvSceneSwitcher.item.properties"), &menu);
connect(action, &QAction::triggered, properties);
menu.addAction(action);
menu.exec(QCursor::pos());
}
void ItemSelection::RenameItem()
{
QAction *action = reinterpret_cast<QAction *>(sender());
QVariant variant = action->property("item");
Item *item = variant.value<Item *>();
std::string name;
bool accepted = NameDialog::AskForName(
this, obs_module_text("AdvSceneSwitcher.windowTitle"),
obs_module_text("AdvSceneSwitcher.item.newName"), name,
QString::fromStdString(item->Name()));
if (!accepted) {
return;
}
if (name.empty()) {
DisplayMessage(
obs_module_text("AdvSceneSwitcher.item.emptyName"));
return;
}
if (_selection->currentText().toStdString() != name &&
!ItemNameAvailable(name, _items)) {
DisplayMessage(obs_module_text(_conflictStr.data()));
return;
}
const auto oldName = item->_name;
item->_name = name;
SetItem(name);
emit ItemRenamed(QString::fromStdString(oldName),
QString::fromStdString(name));
}
void ItemSelection::RenameItem(const QString &oldName, const QString &name)
{
int idx = _selection->findText(oldName);
if (idx == -1) {
return;
}
auto currentText = _selection->currentText();
_selection->setItemText(idx, name);
if (oldName == currentText) {
SetItem(name.toStdString());
}
}
void ItemSelection::AddItem(const QString &name)
{
if (_selection->findText(name) == -1) {
_selection->insertItem(1, name);
}
}
void ItemSelection::RemoveItem()
{
auto item = GetCurrentItem();
if (!item) {
return;
}
int idx = _selection->findText(QString::fromStdString(item->_name));
if (idx == -1 || idx == _selection->count()) {
return;
}
auto name = item->_name;
for (auto it = _items.begin(); it != _items.end(); ++it) {
if (it->get()->_name == item->_name) {
_items.erase(it);
break;
}
}
emit ItemRemoved(QString::fromStdString(name));
}
void ItemSelection::RemoveItem(const QString &name)
{
auto currentText = _selection->currentText();
const int idx = _selection->findText(name);
if (currentText == name) {
SetItem("");
}
_selection->removeItem(idx);
}
Item *ItemSelection::GetCurrentItem()
{
return GetItemByName(_selection->currentText(), _items);
}
ItemSettingsDialog::ItemSettingsDialog(const Item &settings,
std::deque<std::shared_ptr<Item>> &items,
std::string_view select,
std::string_view add,
std::string_view nameConflict,
bool showNameEmptyWarning,
QWidget *parent)
: QDialog(parent),
_name(new QLineEdit()),
_nameHint(new QLabel),
_buttonbox(new QDialogButtonBox(QDialogButtonBox::Ok |
QDialogButtonBox::Cancel)),
_items(items),
_selectStr(select),
_addStr(add),
_conflictStr(nameConflict),
_showNameEmptyWarning(showNameEmptyWarning)
{
setModal(true);
setWindowModality(Qt::WindowModality::WindowModal);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setMinimumWidth(555);
setMinimumHeight(100);
_buttonbox->setCenterButtons(true);
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
_originalName = QString::fromStdString(settings._name);
_name->setText(_originalName);
QWidget::connect(_name, SIGNAL(textEdited(const QString &)), this,
SLOT(NameChanged(const QString &)));
QWidget::connect(_buttonbox, &QDialogButtonBox::accepted, this,
&QDialog::accept);
QWidget::connect(_buttonbox, &QDialogButtonBox::rejected, this,
&QDialog::reject);
NameChanged(_name->text());
}
void ItemSettingsDialog::NameChanged(const QString &text)
{
if (text != _originalName && !ItemNameAvailable(text, _items)) {
SetNameWarning(obs_module_text(_conflictStr.data()));
return;
}
if (text.isEmpty()) {
if (_showNameEmptyWarning) {
SetNameWarning(obs_module_text(
"AdvSceneSwitcher.item.emptyName"));
return;
}
_nameHint->setText("");
_nameHint->hide();
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
return;
}
if (text == obs_module_text(_selectStr.data()) ||
text == obs_module_text(_addStr.data())) {
SetNameWarning(
obs_module_text("AdvSceneSwitcher.item.nameReserved"));
return;
}
SetNameWarning("");
}
void ItemSettingsDialog::SetNameWarning(const QString warn)
{
if (warn.isEmpty()) {
_nameHint->hide();
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(false);
return;
}
_nameHint->setText(warn);
_nameHint->show();
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
}
void Item::Load(obs_data_t *obj)
{
_name = obs_data_get_string(obj, "name");
}
void Item::Save(obs_data_t *obj) const
{
obs_data_set_string(obj, "name", _name.c_str());
}
void RemoveItemsByName(std::deque<std::shared_ptr<Item>> &items,
const QStringList &names)
{
for (const auto &name : names) {
items.erase(std::remove_if(
items.begin(), items.end(),
[name](const std::shared_ptr<Item> &item) {
return item->Name() ==
name.toStdString();
}),
items.end());
}
}
} // namespace advss