#include "macro-export-prompts-dialog.hpp" #include "json-helpers.hpp" #include "obs-module-helper.hpp" #include "string-list.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace advss { static std::string getArrayElementJson(const std::string &arrayJson, int index) { try { auto j = nlohmann::json::parse(arrayJson); if (!j.is_array() || index < 0 || index >= (int)j.size()) { return "{}"; } return j.at(index).dump(); } catch (const nlohmann::json::exception &) { return "{}"; } } static std::string setArrayElementJson(const std::string &arrayJson, int index, const std::string &elementJson) { try { auto j = nlohmann::json::parse(arrayJson); if (j.is_array() && index >= 0 && index < (int)j.size()) { j[index] = nlohmann::json::parse(elementJson); } return j.dump(); } catch (const nlohmann::json::exception &) { return arrayJson; } } static int arraySize(const std::string &arrayJson) { try { auto j = nlohmann::json::parse(arrayJson); return j.is_array() ? (int)j.size() : 0; } catch (const nlohmann::json::exception &) { return 0; } } void ExportPromptsState::Load(obs_data_t *data) { OBSDataArrayAutoRelease macros = obs_data_get_array(data, "macros"); OBSDataAutoRelease wrapper = obs_data_create(); obs_data_set_array(wrapper, "macros", macros); macrosArrayJson = GetJsonField(obs_data_get_json(wrapper), "macros") .value_or("[]"); entries.clear(); } void ExportPromptsState::Apply(obs_data_t *data) const { if (entries.empty()) { return; } std::string wrapped = "{\"macros\":" + macrosArrayJson + "}"; OBSDataAutoRelease wrapper = obs_data_create_from_json(wrapped.c_str()); if (!wrapper) { return; } OBSDataArrayAutoRelease macros = obs_data_get_array(wrapper, "macros"); obs_data_set_array(data, "macros", macros); std::vector prompts; for (auto &entry : entries) { prompts.push_back(entry.prompt); } SaveImportPrompts(data, prompts); } MacroExportPromptsDialog::MacroExportPromptsDialog( QWidget *parent, const ExportPromptsState &state) : QDialog(parent), _state(state) { setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle")); auto layout = new QVBoxLayout(this); auto info = new QLabel(obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.info")); info->setWordWrap(true); layout->addWidget(info); auto scopeLayout = new QHBoxLayout(); scopeLayout->addWidget(new QLabel(obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.scope"))); _scope = new QComboBox(this); _scope->addItem(obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.scope.all")); int count = arraySize(_state.macrosArrayJson); for (int i = 0; i < count; i++) { auto name = GetJsonField(getArrayElementJson( _state.macrosArrayJson, i), "name") .value_or(""); _scope->addItem(QString::fromStdString(name)); } connect(_scope, qOverload(&QComboBox::currentIndexChanged), this, &MacroExportPromptsDialog::ScopeChanged); scopeLayout->addWidget(_scope, 1); layout->addLayout(scopeLayout); auto contentLayout = new QHBoxLayout(); auto valuesLayout = new QVBoxLayout(); valuesLayout->addWidget(new QLabel(obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.values"))); _values = new QTableWidget(0, 2, this); _values->setHorizontalHeaderLabels( {obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.value"), obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.occurrences")}); _values->horizontalHeader()->setStretchLastSection(false); _values->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); _values->setEditTriggers(QAbstractItemView::NoEditTriggers); _values->setSelectionBehavior(QAbstractItemView::SelectRows); _values->setSelectionMode(QAbstractItemView::SingleSelection); valuesLayout->addWidget(_values); auto addButton = new QPushButton( obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.add"), this); connect(addButton, &QPushButton::clicked, this, &MacroExportPromptsDialog::AddPrompt); valuesLayout->addWidget(addButton); contentLayout->addLayout(valuesLayout, 1); auto promptsLayout = new QVBoxLayout(); promptsLayout->addWidget(new QLabel(obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.defined"))); _promptsList = new QListWidget(this); promptsLayout->addWidget(_promptsList); auto removeButton = new QPushButton( obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.remove"), this); connect(removeButton, &QPushButton::clicked, this, &MacroExportPromptsDialog::RemovePrompt); promptsLayout->addWidget(removeButton); contentLayout->addLayout(promptsLayout, 1); layout->addLayout(contentLayout); auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); buttons->setCenterButtons(true); layout->addWidget(buttons); resize(600, 400); RefreshValuesTable(); RefreshPromptsList(); } std::string MacroExportPromptsDialog::CurrentScopeJson() const { int idx = _scope->currentIndex(); if (idx <= 0) { return _state.macrosArrayJson; } return getArrayElementJson(_state.macrosArrayJson, idx - 1); } void MacroExportPromptsDialog::ApplyCurrentScopeJson(const std::string &json) { int idx = _scope->currentIndex(); if (idx <= 0) { _state.macrosArrayJson = json; return; } _state.macrosArrayJson = setArrayElementJson(_state.macrosArrayJson, idx - 1, json); } void MacroExportPromptsDialog::RefreshValuesTable() { auto values = CollectDistinctJsonStringValues(CurrentScopeJson()); _values->setRowCount(0); for (auto &[value, occurrences] : values) { int row = _values->rowCount(); _values->insertRow(row); _values->setItem( row, 0, new QTableWidgetItem(QString::fromStdString(value))); _values->setItem( row, 1, new QTableWidgetItem(QString::number(occurrences))); } } void MacroExportPromptsDialog::RefreshPromptsList() { _promptsList->clear(); for (auto &entry : _state.entries) { _promptsList->addItem(QString("%1 (%2)").arg( QString::fromStdString(entry.prompt.label), QString::fromStdString(entry.scope))); } } void MacroExportPromptsDialog::ScopeChanged(int) { RefreshValuesTable(); } void MacroExportPromptsDialog::AddPrompt() { int row = _values->currentRow(); if (row < 0) { return; } auto value = _values->item(row, 0)->text(); QDialog dialog(this); dialog.setWindowTitle(obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.add")); auto layout = new QVBoxLayout(&dialog); auto form = new QFormLayout(); auto labelEdit = new QLineEdit(&dialog); form->addRow( obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.label"), labelEdit); auto typeCombo = new QComboBox(&dialog); PopulateValueInputTypeSelection(typeCombo); typeCombo->setCurrentIndex( typeCombo->findData(static_cast(ValueInputType::String))); form->addRow( obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.type"), typeCombo); auto defaultValueEdit = new QLineEdit(value, &dialog); form->addRow( obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.defaultValue"), defaultValueEdit); auto optionsWidget = new QWidget(&dialog); auto optionsLayout = new QVBoxLayout(optionsWidget); optionsLayout->setContentsMargins(0, 0, 0, 0); optionsLayout->addWidget(new QLabel(obs_module_text( "AdvSceneSwitcher.macroTab.export.importPrompts.options"))); auto optionsEdit = new StringListEdit(optionsWidget); optionsEdit->SetStringList({value.toStdString()}); optionsLayout->addWidget(optionsEdit); optionsWidget->hide(); connect(typeCombo, qOverload(&QComboBox::currentIndexChanged), &dialog, [&dialog, typeCombo, optionsWidget](int) { optionsWidget->setVisible( typeCombo->currentData().toInt() == static_cast(ValueInputType::Selection)); dialog.adjustSize(); }); layout->addLayout(form); layout->addWidget(optionsWidget); auto buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog); connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); buttons->setCenterButtons(true); layout->addWidget(buttons); if (dialog.exec() != QDialog::Accepted) { return; } auto label = labelEdit->text().toStdString(); if (label.empty()) { return; } auto type = static_cast(typeCombo->currentData().toInt()); ExportPromptsState::Entry entry; entry.prompt.label = label; entry.prompt.type = type; entry.prompt.defaultValue = defaultValueEdit->text().toStdString(); if (type == ValueInputType::Selection) { for (const auto &option : optionsEdit->GetStringList()) { entry.prompt.options.push_back( option.UnresolvedValue()); } } entry.scope = _scope->currentText().toStdString(); auto placeholder = GenerateImportPlaceholder(); ApplyCurrentScopeJson(ReplaceJsonStringValue( CurrentScopeJson(), value.toStdString(), placeholder)); entry.prompt.placeholders.push_back(placeholder); entry.originalValues.push_back(value.toStdString()); _state.entries.emplace_back(std::move(entry)); RefreshValuesTable(); RefreshPromptsList(); } void MacroExportPromptsDialog::RemovePrompt() { int idx = _promptsList->currentRow(); if (idx < 0 || idx >= (int)_state.entries.size()) { return; } auto &entry = _state.entries[idx]; for (size_t i = 0; i < entry.prompt.placeholders.size(); i++) { _state.macrosArrayJson = ReplaceJsonStringValue( _state.macrosArrayJson, entry.prompt.placeholders[i], entry.originalValues[i]); } _state.entries.erase(_state.entries.begin() + idx); RefreshValuesTable(); RefreshPromptsList(); } bool MacroExportPromptsDialog::Edit(QWidget *parent, ExportPromptsState &state) { MacroExportPromptsDialog dialog(parent, state); if (dialog.exec() != QDialog::Accepted) { return false; } state = dialog._state; return true; } } // namespace advss