#include "variable-tab.hpp" #include "log-helper.hpp" #include "obs-module-helper.hpp" #include "plugin-state-helpers.hpp" #include "sync-helpers.hpp" #include "tab-helpers.hpp" #include "time-helpers.hpp" #include "ui-helpers.hpp" #include "variable.hpp" #include #include #include namespace advss { static bool registerTab(); static void setupTab(QTabWidget *); static bool registerTabDone = registerTab(); static VariableTable *dockWidget = nullptr; static bool addDock = false; static VariableTable::Settings tabSettings; static VariableTable::Settings dockSettings; static void save(obs_data_t *data) { tabSettings.Save(data, "tabSettings"); dockSettings.Save(data, "dockSettings"); obs_data_set_bool(data, "addVariablesDock", addDock); } static void enableDock(bool enable) { if (OBSIsShuttingDown()) { return; } obs_frontend_remove_dock("advss-variable-dock"); addDock = enable; if (!addDock) { return; } dockWidget = new VariableTable(dockSettings); dockWidget->HideDockOptions(); if (obs_frontend_add_dock_by_id( "advss-variable-dock", obs_module_text("AdvSceneSwitcher.variableTab.title"), dockWidget)) { return; } blog(LOG_INFO, "failed to register variable dock!"); dockWidget->deleteLater(); dockWidget = nullptr; } static void load(obs_data_t *data) { tabSettings.Load(data, "tabSettings"); dockSettings.Load(data, "dockSettings"); enableDock(obs_data_get_bool(data, "addVariablesDock")); } static bool registerTab() { AddPluginInitStep([]() { AddSetupTabCallback("variableTab", VariableTable::CreateTabTable, setupTab); }); AddSaveStep(save); AddLoadStep(load); return true; } static void setTabVisible(QTabWidget *tabWidget, bool visible) { SetTabVisibleByName( tabWidget, visible, obs_module_text("AdvSceneSwitcher.variableTab.title")); } VariableTable *VariableTable::CreateTabTable() { return new VariableTable(tabSettings); } void VariableTable::HideDockOptions() const { _addDock->hide(); } void VariableTable::Add() { auto newVariable = std::make_shared(); auto accepted = VariableSettingsDialog::AskForSettings( GetSettingsWindow(), *newVariable); if (!accepted) { return; } { auto lock = LockContext(); auto &variables = GetVariables(); variables.emplace_back(newVariable); } VariableSignalManager::Instance()->Add( QString::fromStdString(newVariable->Name())); } static QString formatSaveActionText(Variable *variable) { QString saveAction; switch (variable->GetSaveAction()) { case Variable::SaveAction::DONT_SAVE: saveAction = obs_module_text( "AdvSceneSwitcher.variable.save.dontSave"); break; case Variable::SaveAction::SAVE: saveAction = obs_module_text("AdvSceneSwitcher.variable.save.save"); break; case Variable::SaveAction::SET_DEFAULT: saveAction = QString(obs_module_text( "AdvSceneSwitcher.variableTab.saveLoadBehavior.text.default")) .arg(QString::fromStdString( variable->GetDefaultValue())); break; default: break; } return saveAction; } static QString formatLastUsedText(Variable *variable) { auto lastUsed = variable->GetSecondsSinceLastUse(); if (!lastUsed) { return obs_module_text( "AdvSceneSwitcher.variableTab.lastUsed.text.never"); } return FormatRelativeTime(*lastUsed); } static QString formatLastChangedText(Variable *variable) { auto lastChanged = variable->GetSecondsSinceLastChange(); if (!lastChanged) { return obs_module_text( "AdvSceneSwitcher.variableTab.lastChanged.text.none"); } return FormatRelativeTime(*lastChanged); } static QString formatLastChangedTooltip(Variable *variable) { auto lastChanged = variable->GetSecondsSinceLastChange(); if (!lastChanged) { return QString(); } QString tooltip = obs_module_text( "AdvSceneSwitcher.variableTab.lastChanged.tooltip"); return tooltip.arg(QString::number(variable->GetValueChangeCount())) .arg(QString::fromStdString(variable->GetPreviousValue())); } static QStringList getCellLabels(Variable *variable, bool addName = true) { assert(variable); auto result = QStringList(); if (addName) { result << QString::fromStdString(variable->Name()); } result << QString::fromStdString(variable->Value(false)) << formatSaveActionText(variable) << formatLastUsedText(variable) << formatLastChangedText(variable); return result; } static void updateVariableStatus(QTableWidget *table) { for (int row = 0; row < table->rowCount(); row++) { auto item = table->item(row, 0); if (!item) { continue; } auto weakVariable = GetWeakVariableByQString(item->text()); auto variable = weakVariable.lock(); if (!variable) { continue; } UpdateItemTableRow(table, row, getCellLabels(variable.get(), false)); // Special tooltip handling for the "last used" cell const auto lastUsedItem = table->item(row, 4); if (!lastUsedItem) { continue; } const auto lastUsedTooltip = formatLastChangedTooltip(variable.get()); lastUsedItem->setToolTip(lastUsedTooltip); } } static void openSettingsDialog(VariableTable *table) { auto selectedRows = table->Table()->selectionModel()->selectedRows(); if (selectedRows.empty()) { return; } auto cell = table->Table()->item(selectedRows.last().row(), 0); if (!cell) { return; } auto weakVariable = GetWeakVariableByQString(cell->text()); auto variable = weakVariable.lock(); if (!variable) { return; } auto oldName = variable->Name(); bool accepted = VariableSettingsDialog::AskForSettings( GetSettingsWindow(), *variable.get()); if (accepted && oldName != variable->Name()) { VariableSignalManager::Instance()->Rename( QString::fromStdString(oldName), QString::fromStdString(variable->Name())); } } void VariableTable::Remove() { auto selectedRows = Table()->selectionModel()->selectedRows(); if (selectedRows.empty()) { return; } QStringList varNames; for (const auto &row : selectedRows) { auto cell = Table()->item(row.row(), 0); if (!cell) { continue; } varNames << cell->text(); } int varNameCount = varNames.size(); if (varNameCount == 1) { QString deleteWarning = obs_module_text( "AdvSceneSwitcher.variableTab.removeSingleVariablePopup.text"); if (!DisplayMessage(deleteWarning.arg(varNames.at(0)), true)) { return; } } else { QString deleteWarning = obs_module_text( "AdvSceneSwitcher.variableTab.removeMultipleVariablesPopup.text"); if (!DisplayMessage(deleteWarning.arg(varNameCount), true)) { return; } } { auto lock = LockContext(); RemoveItemsByName(GetVariables(), varNames); } for (const auto &name : varNames) { VariableSignalManager::Instance()->Remove(name); } } void VariableTable::Filter() { const auto itemMatches = [&](const QTableWidgetItem *item) { if (!item) { return false; } if (_settings.searchString.empty()) { return true; } const auto text = item->text(); if (_settings.regex.Enabled()) { return _settings.regex.Matches(text.toStdString(), _settings.searchString); } return text.contains( QString::fromStdString(_settings.searchString), Qt::CaseInsensitive); }; for (int row = 0; row < Table()->rowCount(); ++row) { bool match = false; if (_settings.searchType == Settings::SearchType::ALL) { for (int col = 0; col < Table()->columnCount(); ++col) { if (itemMatches(Table()->item(row, col))) { match = true; break; } } } else { if (itemMatches(Table()->item( row, static_cast(_settings.searchType)))) { match = true; } } Table()->setRowHidden(row, !match); } } VariableTable::VariableTable(Settings &settings, QWidget *parent) : ResourceTable( parent, obs_module_text("AdvSceneSwitcher.variableTab.help"), obs_module_text( "AdvSceneSwitcher.variableTab.variableAddButton.tooltip"), obs_module_text( "AdvSceneSwitcher.variableTab.variableRemoveButton.tooltip"), QStringList() << obs_module_text( "AdvSceneSwitcher.variableTab.name.header") << obs_module_text( "AdvSceneSwitcher.variableTab.value.header") << obs_module_text( "AdvSceneSwitcher.variableTab.saveLoadBehavior.header") << obs_module_text( "AdvSceneSwitcher.variableTab.lastUsed.header") << obs_module_text( "AdvSceneSwitcher.variableTab.lastChanged.header"), [this]() { openSettingsDialog(this); }), _searchField(new QLineEdit(this)), _clear(new QPushButton(this)), _searchType(new QComboBox(this)), _regexWidget(new RegexConfigWidget(this)), _addDock(new QCheckBox( obs_module_text("AdvSceneSwitcher.variableTab.addDock"))), _settings(settings) { _clear->setMaximumWidth(22); SetButtonIcon(_clear, GetThemeTypeName() == "Light" ? "theme:Light/close.svg" : "theme:Dark/close.svg"); _clear->setToolTip( obs_module_text("AdvSceneSwitcher.variableTab.clear")); _clear->setDisabled(_settings.searchString.empty()); connect(_clear, &QPushButton::clicked, this, [this]() { _searchField->setText(""); _clear->setDisabled(true); }); for (const auto &variable : GetVariables()) { auto v = std::static_pointer_cast(variable); AddItemTableRow(Table(), getCellLabels(v.get())); } connect(Table(), &QTableWidget::itemChanged, this, [this]() { Filter(); }); _searchField->setPlaceholderText(obs_module_text( ("AdvSceneSwitcher.variableTab.search.placeholder"))); _searchField->setText(QString::fromStdString(_settings.searchString)); connect(_searchField, &QLineEdit::textChanged, this, [this](const QString &text) { _settings.searchString = text.toStdString(); _clear->setDisabled(text.isEmpty()); Filter(); }); _searchType->addItem( obs_module_text("AdvSceneSwitcher.variableTab.search.all"), Settings::SearchType::ALL); _searchType->addItem( obs_module_text("AdvSceneSwitcher.variableTab.search.name"), Settings::SearchType::NAME); _searchType->addItem( obs_module_text("AdvSceneSwitcher.variableTab.search.value"), Settings::SearchType::VALUE); _searchType->setCurrentIndex( _searchType->findData(_settings.searchType)); connect(_searchType, &QComboBox::currentIndexChanged, this, [this]() { _settings.searchType = static_cast( _searchType->currentData().toInt()); }); _regexWidget->SetRegexConfig(_settings.regex); connect(_regexWidget, &RegexConfigWidget::RegexConfigChanged, this, [this](const RegexConfig ®ex) { _settings.regex = regex; Filter(); }); _addDock->setChecked(addDock); connect(_addDock, &QCheckBox::stateChanged, this, [this](int checked) { enableDock(checked); }); QWidget::connect(VariableSignalManager::Instance(), &VariableSignalManager::Rename, this, [this](const QString &oldName, const QString &newName) { RenameItemTableRow(Table(), oldName, newName); }); QWidget::connect( VariableSignalManager::Instance(), &VariableSignalManager::Add, this, [this](const QString &name) { AddItemTableRow( Table(), getCellLabels(GetVariableByQString(name))); SetHelpVisible(false); HighlightAddButton(false); }); QWidget::connect(VariableSignalManager::Instance(), &VariableSignalManager::Remove, this, [this](const QString &name) { RemoveItemTableRow(Table(), name); if (Table()->rowCount() == 0) { SetHelpVisible(true); HighlightAddButton(true); } }); auto timer = new QTimer(this); timer->setInterval(1000); QWidget::connect(timer, &QTimer::timeout, [this]() { updateVariableStatus(Table()); }); timer->start(); auto searchLayout = new QHBoxLayout(); searchLayout->addWidget(_searchField); searchLayout->addWidget(_clear); searchLayout->addWidget(_searchType); searchLayout->addWidget(_regexWidget); searchLayout->addWidget(_addDock); qobject_cast(layout())->insertLayout(0, searchLayout); SetHelpVisible(GetVariables().empty()); } static void setupTab(QTabWidget *tab) { if (GetVariables().empty()) { setTabVisible(tab, false); } QWidget::connect(VariableSignalManager::Instance(), &VariableSignalManager::Add, tab, [tab](const QString &) { setTabVisible(tab, true); }); } void VariableTable::Settings::Save(obs_data_t *data, const char *name) { OBSDataAutoRelease settings = obs_data_create(); obs_data_set_int(settings, "searchType", static_cast(searchType)); obs_data_set_string(settings, "searchString", searchString.c_str()); regex.Save(settings); obs_data_set_obj(data, name, settings); } void VariableTable::Settings::Load(obs_data_t *data, const char *name) { OBSDataAutoRelease settings = obs_data_get_obj(data, name); searchType = static_cast( obs_data_get_int(settings, "searchType")); searchString = obs_data_get_string(settings, "searchString"); regex.Load(settings); } } // namespace advss