From 252f55c8a5f65de729e0b704a90266d2fd0f20e0 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 28 Jan 2024 14:46:26 +0100 Subject: [PATCH] Add variables tab --- CMakeLists.txt | 1 + data/locale/en-US.ini | 12 ++ forms/advanced-scene-switcher.ui | 98 +++++++++++ lib/advanced-scene-switcher.cpp | 1 + lib/advanced-scene-switcher.hpp | 10 ++ lib/general.cpp | 202 +++++++-------------- lib/switcher-data.hpp | 4 - lib/utils/utility.cpp | 7 +- lib/utils/utility.hpp | 2 +- lib/variables/variable-string.cpp | 4 +- lib/variables/variable-tab.cpp | 283 ++++++++++++++++++++++++++++++ lib/variables/variable.cpp | 29 ++- lib/variables/variable.hpp | 7 +- tests/mocks/utility.cpp | 7 +- 14 files changed, 514 insertions(+), 153 deletions(-) create mode 100644 lib/variables/variable-tab.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ed7477a15..5a29ae604 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,6 +223,7 @@ target_sources( lib/variables/variable-spinbox.hpp lib/variables/variable-string.cpp lib/variables/variable-string.hpp + lib/variables/variable-tab.cpp lib/variables/variable-text-edit.cpp lib/variables/variable-text-edit.hpp lib/variables/variable.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 4b7eb76ab..383f165c7 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -65,6 +65,18 @@ AdvSceneSwitcher.generalTab.transitionOverride="Set transition overrides" AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type" AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type" +; Variables Tab +AdvSceneSwitcher.variableTab.title="Variables" +AdvSceneSwitcher.variableTab.help="Variables can be used in many places throughout the plugin.\n\nClick on the highlighted plus symbol to add a new variable." +AdvSceneSwitcher.variableTab.tooltip.variableAddButton="Add new variable" +AdvSceneSwitcher.variableTab.tooltip.variableRemoveButton="Remove selected variables" +AdvSceneSwitcher.variableTab.header.name="Name" +AdvSceneSwitcher.variableTab.header.value="Value" +AdvSceneSwitcher.variableTab.header.saveLoadBehavior="Save/Load behavior" +AdvSceneSwitcher.variableTab.header.lastUse="Last used" +AdvSceneSwitcher.variableTab.neverNused="Never" +AdvSceneSwitcher.variableTab.lastUsed="%1 seconds ago" + ; Macro Tab AdvSceneSwitcher.macroTab.title="Macro" AdvSceneSwitcher.macroTab.macros="Macros" diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index 9b1ef626a..c9e788a22 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -1577,6 +1577,104 @@ + + + AdvSceneSwitcher.variableTab.title + + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + AdvSceneSwitcher.variableTab.help + + + Qt::AlignCenter + + + true + + + + + + + + + + + + 22 + 22 + + + + AdvSceneSwitcher.variableTab.tooltip.variableAddButton + + + true + + + addIconSmall + + + + + + + + 22 + 22 + + + + AdvSceneSwitcher.variableTab.tooltip.variableRemoveButton + + + true + + + removeIconSmall + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + AdvSceneSwitcher.networkTab.title diff --git a/lib/advanced-scene-switcher.cpp b/lib/advanced-scene-switcher.cpp index 7d2588138..c91444f0a 100644 --- a/lib/advanced-scene-switcher.cpp +++ b/lib/advanced-scene-switcher.cpp @@ -111,6 +111,7 @@ void AdvSceneSwitcher::LoadUI() SetupSceneGroupTab(); SetupTriggerTab(); SetupMacroTab(); + SetupVariableTab(); SetDeprecationWarnings(); SetTabOrder(); diff --git a/lib/advanced-scene-switcher.hpp b/lib/advanced-scene-switcher.hpp index f84fc6c22..90556315f 100644 --- a/lib/advanced-scene-switcher.hpp +++ b/lib/advanced-scene-switcher.hpp @@ -208,6 +208,16 @@ public slots: /* --- End of macro tab section --- */ + /* --- Begin of variable tab section --- */ +public: + void SetupVariableTab(); + +public slots: + void on_variableAdd_clicked(); + void on_variableRemove_clicked(); + + /* --- End of variable tab section --- */ + /* --- Begin of legacy tab section --- */ public: void ClearFrames(QListWidget *list); diff --git a/lib/general.cpp b/lib/general.cpp index 7b7e5d21e..d9d56117f 100644 --- a/lib/general.cpp +++ b/lib/general.cpp @@ -18,6 +18,17 @@ namespace advss { +static constexpr std::array tabNames = { + "generalTab", "macroTab", "variableTab", + "windowTitleTab", "executableTab", "screenRegionTab", + "mediaTab", "fileTab", "randomTab", + "timeTab", "idleTab", "sceneSequenceTab", + "audioTab", "videoTab", "networkTab", + "sceneGroupTab", "transitionsTab", "pauseTab", + "sceneTriggerTab"}; + +static std::vector tabOrder = std::vector(tabNames.size()); + void AdvSceneSwitcher::reject() { close(); @@ -355,65 +366,7 @@ void AdvSceneSwitcher::on_importSettings_clicked() static int findTabIndex(QTabWidget *tabWidget, int pos) { int at = -1; - - QString tabName = ""; - switch (pos) { - case 0: - tabName = "generalTab"; - break; - case 1: - tabName = "macroTab"; - break; - case 2: - tabName = "transitionsTab"; - break; - case 3: - tabName = "pauseTab"; - break; - case 4: - tabName = "windowTitleTab"; - break; - case 5: - tabName = "executableTab"; - break; - case 6: - tabName = "screenRegionTab"; - break; - case 7: - tabName = "mediaTab"; - break; - case 8: - tabName = "fileTab"; - break; - case 9: - tabName = "randomTab"; - break; - case 10: - tabName = "timeTab"; - break; - case 11: - tabName = "idleTab"; - break; - case 12: - tabName = "sceneSequenceTab"; - break; - case 13: - tabName = "audioTab"; - break; - case 14: - tabName = "videoTab"; - break; - case 15: - tabName = "networkTab"; - break; - case 16: - tabName = "sceneGroupTab"; - break; - case 17: - tabName = "sceneTriggerTab"; - break; - } - + QString tabName = tabNames.at(pos); QWidget *page = tabWidget->findChild(tabName); if (page) { at = tabWidget->indexOf(page); @@ -426,15 +379,35 @@ static int findTabIndex(QTabWidget *tabWidget, int pos) return at; } +static bool tabWidgetOrderValid() +{ + auto tmp = std::vector(tabNames.size()); + std::iota(tmp.begin(), tmp.end(), 0); + + for (auto &p : tmp) { + auto it = std::find(tabOrder.begin(), tabOrder.end(), p); + if (it == tabOrder.end()) { + return false; + } + } + return true; +} + +static void resetTabWidgetOrder() +{ + tabOrder = std::vector(tabNames.size()); + std::iota(tabOrder.begin(), tabOrder.end(), 0); +} + void AdvSceneSwitcher::SetTabOrder() { - if (!switcher->TabOrderValid()) { - switcher->ResetTabOrder(); + if (!tabWidgetOrderValid()) { + resetTabWidgetOrder(); } QTabBar *bar = ui->tabWidget->tabBar(); for (int i = 0; i < bar->count(); ++i) { - int curPos = findTabIndex(ui->tabWidget, switcher->tabOrder[i]); + int curPos = findTabIndex(ui->tabWidget, tabOrder[i]); if (i != curPos && curPos != -1) { bar->moveTab(curPos, i); @@ -479,7 +452,7 @@ void AdvSceneSwitcher::on_tabMoved(int from, int to) return; } - std::swap(switcher->tabOrder[from], switcher->tabOrder[to]); + std::swap(tabOrder[from], tabOrder[to]); } void AdvSceneSwitcher::on_tabWidget_currentChanged(int) @@ -712,24 +685,13 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj) void SwitcherData::SaveUISettings(obs_data_t *obj) { - obs_data_set_int(obj, "generalTabPos", tabOrder[0]); - obs_data_set_int(obj, "macroTabPos", tabOrder[1]); - obs_data_set_int(obj, "transitionTabPos", tabOrder[2]); - obs_data_set_int(obj, "pauseTabPos", tabOrder[3]); - obs_data_set_int(obj, "titleTabPos", tabOrder[4]); - obs_data_set_int(obj, "exeTabPos", tabOrder[5]); - obs_data_set_int(obj, "regionTabPos", tabOrder[6]); - obs_data_set_int(obj, "mediaTabPos", tabOrder[7]); - obs_data_set_int(obj, "fileTabPos", tabOrder[8]); - obs_data_set_int(obj, "randomTabPos", tabOrder[9]); - obs_data_set_int(obj, "timeTabPos", tabOrder[10]); - obs_data_set_int(obj, "idleTabPos", tabOrder[11]); - obs_data_set_int(obj, "sequenceTabPos", tabOrder[12]); - obs_data_set_int(obj, "audioTabPos", tabOrder[13]); - obs_data_set_int(obj, "videoTabPos", tabOrder[14]); - obs_data_set_int(obj, "networkTabPos", tabOrder[15]); - obs_data_set_int(obj, "sceneGroupTabPos", tabOrder[16]); - obs_data_set_int(obj, "triggerTabPos", tabOrder[17]); + OBSDataArrayAutoRelease tabWidgetOrder = obs_data_array_create(); + for (int i = 0; i < tabNames.size(); i++) { + OBSDataAutoRelease entry = obs_data_create(); + obs_data_set_int(entry, tabNames[i], tabOrder[i]); + obs_data_array_push_back(tabWidgetOrder, entry); + } + obs_data_set_array(obj, "tabWidgetOrder", tabWidgetOrder); obs_data_set_bool(obj, "saveWindowGeo", saveWindowGeo); obs_data_set_int(obj, "windowPosX", windowPos.x()); @@ -743,47 +705,27 @@ void SwitcherData::SaveUISettings(obs_data_t *obj) void SwitcherData::LoadUISettings(obs_data_t *obj) { - obs_data_set_default_int(obj, "generalTabPos", 0); - obs_data_set_default_int(obj, "macroTabPos", 1); - obs_data_set_default_int(obj, "networkTabPos", 13); - obs_data_set_default_int(obj, "sceneGroupTabPos", 14); - obs_data_set_default_int(obj, "transitionTabPos", 15); - obs_data_set_default_int(obj, "pauseTabPos", 16); - obs_data_set_default_int(obj, "titleTabPos", 2); - obs_data_set_default_int(obj, "exeTabPos", 3); - obs_data_set_default_int(obj, "regionTabPos", 4); - obs_data_set_default_int(obj, "mediaTabPos", 5); - obs_data_set_default_int(obj, "fileTabPos", 6); - obs_data_set_default_int(obj, "randomTabPos", 7); - obs_data_set_default_int(obj, "timeTabPos", 8); - obs_data_set_default_int(obj, "idleTabPos", 9); - obs_data_set_default_int(obj, "sequenceTabPos", 10); - obs_data_set_default_int(obj, "audioTabPos", 11); - obs_data_set_default_int(obj, "videoTabPos", 12); - obs_data_set_default_int(obj, "triggerTabPos", 17); + OBSDataArrayAutoRelease defaultTabWidgetOrder = obs_data_array_create(); + for (int i = 0; i < tabNames.size(); i++) { + OBSDataAutoRelease entry = obs_data_create(); + obs_data_set_default_int(entry, tabNames[i], i); + obs_data_array_push_back(defaultTabWidgetOrder, entry); + } + obs_data_set_default_array(obj, "tabWidgetOrder", + defaultTabWidgetOrder); tabOrder.clear(); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "generalTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "macroTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "transitionTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "pauseTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "titleTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "exeTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "regionTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "mediaTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "fileTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "randomTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "timeTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "idleTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "sequenceTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "audioTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "videoTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "networkTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "sceneGroupTabPos"))); - tabOrder.emplace_back((int)(obs_data_get_int(obj, "triggerTabPos"))); - - if (!TabOrderValid()) { - ResetTabOrder(); + OBSDataArrayAutoRelease tabWidgetOrder = + obs_data_get_array(obj, "tabWidgetOrder"); + for (int i = 0; i < tabNames.size(); i++) { + OBSDataAutoRelease entry = + obs_data_array_item(tabWidgetOrder, i); + tabOrder.emplace_back( + (int)(obs_data_get_int(entry, tabNames[i]))); + } + + if (!tabWidgetOrderValid()) { + resetTabWidgetOrder(); } saveWindowGeo = obs_data_get_bool(obj, "saveWindowGeo"); @@ -796,26 +738,6 @@ void SwitcherData::LoadUISettings(obs_data_t *obj) "macroListMacroEditSplitterPosition"); } -bool SwitcherData::TabOrderValid() -{ - auto tmp = std::vector(tab_count); - std::iota(tmp.begin(), tmp.end(), 0); - - for (auto &p : tmp) { - auto it = std::find(tabOrder.begin(), tabOrder.end(), p); - if (it == tabOrder.end()) { - return false; - } - } - return true; -} - -void SwitcherData::ResetTabOrder() -{ - tabOrder = std::vector(tab_count); - std::iota(tabOrder.begin(), tabOrder.end(), 0); -} - void SwitcherData::CheckNoMatchSwitch(bool &match, OBSWeakSource &scene, OBSWeakSource &transition, int &sleep) { diff --git a/lib/switcher-data.hpp b/lib/switcher-data.hpp index d8faafb4d..a83c2bf42 100644 --- a/lib/switcher-data.hpp +++ b/lib/switcher-data.hpp @@ -32,7 +32,6 @@ namespace advss { constexpr auto default_interval = 300; -constexpr auto tab_count = 18; typedef const char *(*translateFunc)(const char *); @@ -89,8 +88,6 @@ class SwitcherData { void LoadUISettings(obs_data_t *obj); bool VersionChanged(obs_data_t *obj, std::string currentVersion); - bool TabOrderValid(); - void ResetTabOrder(); bool PrioFuncsValid(); /* --- End of saving / loading section --- */ @@ -179,7 +176,6 @@ class SwitcherData { bool disableHints = false; bool disableFilterComboboxFilter = false; bool hideLegacyTabs = true; - std::vector tabOrder = std::vector(tab_count); bool saveWindowGeo = false; QPoint windowPos = {}; QSize windowSize = {}; diff --git a/lib/utils/utility.cpp b/lib/utils/utility.cpp index cd890de8d..ce9a2005e 100644 --- a/lib/utils/utility.cpp +++ b/lib/utils/utility.cpp @@ -21,17 +21,20 @@ bool DoubleEquals(double left, double right, double epsilon) return (fabs(left - right) < epsilon); } -void ReplaceAll(std::string &str, const std::string &from, +bool ReplaceAll(std::string &str, const std::string &from, const std::string &to) { if (from.empty()) { - return; + return false; } + bool somethingWasReplaced = false; size_t start_pos = 0; while ((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); + somethingWasReplaced = true; } + return somethingWasReplaced; } std::optional GetJsonField(const std::string &jsonStr, diff --git a/lib/utils/utility.hpp b/lib/utils/utility.hpp index bb28558f7..788886a45 100644 --- a/lib/utils/utility.hpp +++ b/lib/utils/utility.hpp @@ -16,7 +16,7 @@ EXPORT std::pair GetCursorPos(); EXPORT bool DoubleEquals(double left, double right, double epsilon); -void ReplaceAll(std::string &str, const std::string &from, +bool ReplaceAll(std::string &str, const std::string &from, const std::string &to); EXPORT std::optional GetJsonField(const std::string &json, const std::string &id); diff --git a/lib/variables/variable-string.cpp b/lib/variables/variable-string.cpp index ccc3d39c8..3d6c11863 100644 --- a/lib/variables/variable-string.cpp +++ b/lib/variables/variable-string.cpp @@ -73,7 +73,9 @@ std::string SubstitueVariables(std::string str) for (const auto &v : GetVariables()) { const auto &variable = std::dynamic_pointer_cast(v); const std::string pattern = "${" + variable->Name() + "}"; - ReplaceAll(str, pattern, variable->Value()); + if (ReplaceAll(str, pattern, variable->Value(false))) { + variable->UpdateLastUsed(); + } } return str; } diff --git a/lib/variables/variable-tab.cpp b/lib/variables/variable-tab.cpp new file mode 100644 index 000000000..1affe1d2b --- /dev/null +++ b/lib/variables/variable-tab.cpp @@ -0,0 +1,283 @@ +#include "advanced-scene-switcher.hpp" +#include "variable.hpp" + +#include + +namespace advss { + +static void setVariableTabVisible(QTabWidget *tabWidget, bool visible) +{ + for (int idx = 0; idx < tabWidget->count(); idx++) { + if (tabWidget->tabText(idx) != + obs_module_text("AdvSceneSwitcher.variableTab.title")) { + continue; + } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + // TODO: Switch to setTabVisible() once QT 5.15 is more wide spread + tabWidget->setTabEnabled(idx, visible); + tabWidget->setStyleSheet( + "QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} "); +#else + tabWidget->setTabVisible(idx, visible); +#endif + } +} + +static QString getSaveActionString(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.variable.save.default")) + + " \"" + + QString::fromStdString(variable->GetDefaultValue()) + + "\""; + break; + default: + break; + } + return saveAction; +} + +static QString getLastUsedString(Variable *variable) +{ + auto lastUsed = variable->SecondsSinceLastUse(); + if (!lastUsed) { + return obs_module_text( + "AdvSceneSwitcher.variableTab.neverNused"); + } + QString fmt = obs_module_text("AdvSceneSwitcher.variableTab.lastUsed"); + return fmt.arg(QString::number(*lastUsed)); +} + +static void addVariableRow(QTableWidget *table, Variable *variable) +{ + if (!variable) { + blog(LOG_INFO, "%s called with nullptr", __func__); + assert(false); + return; + } + + int row = table->rowCount(); + table->setRowCount(row + 1); + + int col = 0; + auto *item = + new QTableWidgetItem(QString::fromStdString(variable->Name())); + table->setItem(row, col, item); + col++; + item = new QTableWidgetItem( + QString::fromStdString(variable->Value(false))); + table->setItem(row, col, item); + col++; + item = new QTableWidgetItem(getSaveActionString(variable)); + table->setItem(row, col, item); + col++; + item = new QTableWidgetItem(getLastUsedString(variable)); + table->setItem(row, col, item); +} + +static void removeVariableRow(QTableWidget *table, const QString &name) +{ + for (int row = 0; row < table->rowCount(); ++row) { + auto item = table->item(row, 0); + if (item && item->text() == name) { + table->removeRow(row); + return; + } + } +} + +static void updateVaribleStatus(QTableWidget *table) +{ + auto lock = LockContext(); + 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; + } + item = table->item(row, 1); + item->setText(QString::fromStdString(variable->Value(false))); + item = table->item(row, 2); + item->setText(getSaveActionString(variable.get())); + item = table->item(row, 3); + item->setText(getLastUsedString(variable.get())); + } +} + +static void renameVariable(QTableWidget *table, const QString &oldName, + const QString &newName) +{ + for (int row = 0; row < table->rowCount(); row++) { + auto item = table->item(row, 0); + if (!item) { + continue; + } + if (item->text() == oldName) { + item->setText(newName); + return; + } + } +} + +static void openSettingsDialogAtRow(QTableWidget *table, int row) +{ + auto item = table->item(row, 0); + if (!item) { + return; + } + auto weakVariable = GetWeakVariableByQString(item->text()); + auto variable = weakVariable.lock(); + if (!variable) { + return; + } + + VariableSettingsDialog::AskForSettings(table, *variable.get()); +} + +void AdvSceneSwitcher::SetupVariableTab() +{ + if (GetVariables().empty()) { + setVariableTabVisible(ui->tabWidget, false); + } else { + ui->variablesHelp->hide(); + } + + static const QStringList horizontalHeaders = + QStringList() + << obs_module_text("AdvSceneSwitcher.variableTab.header.name") + << obs_module_text("AdvSceneSwitcher.variableTab.header.value") + << obs_module_text( + "AdvSceneSwitcher.variableTab.header.saveLoadBehavior") + << obs_module_text( + "AdvSceneSwitcher.variableTab.header.lastUse"); + + auto &variables = GetVariables(); + + ui->variables->setColumnCount(horizontalHeaders.size()); + ui->variables->horizontalHeader()->setSectionResizeMode( + QHeaderView::ResizeMode::Stretch); + ui->variables->setHorizontalHeaderLabels(horizontalHeaders); + + for (const auto &var : variables) { + auto variable = std::static_pointer_cast(var); + addVariableRow(ui->variables, variable.get()); + } + ui->variables->sortByColumn(0, Qt::AscendingOrder); + + ui->variables->resizeColumnsToContents(); + ui->variables->resizeRowsToContents(); + + // Connect to slots + QWidget::connect( + VariableSignalManager::Instance(), + &VariableSignalManager::Rename, + [this](const QString &oldName, const QString &newName) { + renameVariable(ui->variables, oldName, newName); + }); + QWidget::connect(VariableSignalManager::Instance(), + &VariableSignalManager::Add, this, + [this](const QString &name) { + addVariableRow(ui->variables, + GetVariableByQString(name)); + setVariableTabVisible(ui->tabWidget, true); + }); + QWidget::connect(VariableSignalManager::Instance(), + &VariableSignalManager::Remove, this, + [this](const QString &name) { + removeVariableRow(ui->variables, name); + }); + + QWidget::connect(ui->variables, &QTableWidget::cellDoubleClicked, + [this](int row, int _) { + openSettingsDialogAtRow(ui->variables, row); + }); + + //cellDoubleClicked + auto timer = new QTimer(this); + timer->setInterval(1000); + QWidget::connect(timer, &QTimer::timeout, + [this]() { updateVaribleStatus(ui->variables); }); + timer->start(); +} + +void AdvSceneSwitcher::on_variableAdd_clicked() +{ + auto newVariable = std::make_shared(); + auto accepted = + VariableSettingsDialog::AskForSettings(this, *newVariable); + if (!accepted) { + return; + } + { + auto lock = LockContext(); + auto variables = GetVariables(); + variables.emplace_back(newVariable); + } + + VariableSignalManager::Instance()->Add( + QString::fromStdString(newVariable->Name())); + addVariableRow(ui->variables, newVariable.get()); + ui->variables->sortByColumn(0, Qt::AscendingOrder); +} + +void AdvSceneSwitcher::on_variableRemove_clicked() +{ + auto selectedItems = ui->variables->selectedItems(); + QList selectedRows; + for (auto item : selectedItems) { + int row = item->row(); + if (!selectedRows.contains(row)) { + selectedRows.append(row); + } + } + + auto row = ui->variables->currentRow(); + if (selectedRows.empty()) { + return; + } + + auto lock = LockContext(); + + for (int row : selectedRows) { + auto item = ui->variables->item(row, 0); + if (!item) { + continue; + } + auto name = item->text(); + auto variable = GetVariableByQString(name); + if (!variable) { + continue; + } + + auto variables = GetVariables(); + variables.erase( + std::remove_if( + variables.begin(), variables.end(), + [variable](const std::shared_ptr &item) { + return item.get() == variable; + }), + variables.end()); + + removeVariableRow(ui->variables, name); + VariableSignalManager::Instance()->Remove(name); + ui->variables->sortByColumn(0, Qt::AscendingOrder); + } +} + +} // namespace advss diff --git a/lib/variables/variable.cpp b/lib/variables/variable.cpp index b6c640544..a5ff3c98f 100644 --- a/lib/variables/variable.cpp +++ b/lib/variables/variable.cpp @@ -51,28 +51,53 @@ void Variable::Save(obs_data_t *obj) const obs_data_set_string(obj, "defaultValue", _defaultValue.c_str()); } +std::string Variable::Value(bool updateLastUsed) const +{ + if (updateLastUsed) { + UpdateLastUsed(); + } + return _value; +} + std::optional Variable::DoubleValue() const { - return GetDouble(_value); + return GetDouble(Value()); } std::optional Variable::IntValue() const { - return GetInt(_value); + return GetInt(Value()); } void Variable::SetValue(const std::string &val) { _value = val; + _lastUsed = std::chrono::high_resolution_clock::now(); lastVariableChange = std::chrono::high_resolution_clock::now(); } void Variable::SetValue(double value) { _value = std::to_string(value); + _lastUsed = std::chrono::high_resolution_clock::now(); lastVariableChange = std::chrono::high_resolution_clock::now(); } +std::optional Variable::SecondsSinceLastUse() const +{ + if (_lastUsed.time_since_epoch().count() == 0) { + return {}; + } + const auto now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(now - _lastUsed) + .count(); +} + +void Variable::UpdateLastUsed() const +{ + _lastUsed = std::chrono::high_resolution_clock::now(); +} + std::deque> &GetVariables() { return variables; diff --git a/lib/variables/variable.hpp b/lib/variables/variable.hpp index f35680870..37f3cf5ae 100644 --- a/lib/variables/variable.hpp +++ b/lib/variables/variable.hpp @@ -19,11 +19,12 @@ class Variable : public Item { ~Variable(); void Load(obs_data_t *obj); void Save(obs_data_t *obj) const; - EXPORT std::string Value() const { return _value; } + EXPORT std::string Value(bool updateLastUsed = true) const; EXPORT std::optional DoubleValue() const; EXPORT std::optional IntValue() const; void SetValue(const std::string &val); void SetValue(double); + std::string GetDefaultValue() const { return _defaultValue; } static std::shared_ptr Create() { return std::make_shared(); @@ -34,11 +35,15 @@ class Variable : public Item { SAVE, SET_DEFAULT, }; + SaveAction GetSaveAction() const { return _saveAction; } + std::optional SecondsSinceLastUse() const; + void UpdateLastUsed() const; private: SaveAction _saveAction = SaveAction::DONT_SAVE; std::string _value = ""; std::string _defaultValue = ""; + mutable std::chrono::high_resolution_clock::time_point _lastUsed; friend VariableSelection; friend VariableSettingsDialog; diff --git a/tests/mocks/utility.cpp b/tests/mocks/utility.cpp index ff3659188..e815415eb 100644 --- a/tests/mocks/utility.cpp +++ b/tests/mocks/utility.cpp @@ -14,17 +14,20 @@ bool DoubleEquals(double left, double right, double epsilon) return (fabs(left - right) < epsilon); } -void ReplaceAll(std::string &str, const std::string &from, +bool ReplaceAll(std::string &str, const std::string &from, const std::string &to) { if (from.empty()) { - return; + return false; } size_t start_pos = 0; + bool somethingWasReplaced = false; while ((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); + somethingWasReplaced = true; } + return somethingWasReplaced; } std::optional GetJsonField(const std::string &jsonStr,