mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Add export / import functionality to macro tab
This enables the easy sharing of single / multiple macros across scene collections. Previously either all settings had to be copied via the export / import functionality of the General tab or none at all.
This commit is contained in:
parent
dcae0e8e8b
commit
4b0a631987
|
|
@ -263,6 +263,8 @@ target_sources(
|
|||
src/utils/filter-combo-box.hpp
|
||||
src/utils/filter-selection.cpp
|
||||
src/utils/filter-selection.hpp
|
||||
src/utils/macro-export-import-dialog.cpp
|
||||
src/utils/macro-export-import-dialog.hpp
|
||||
src/utils/macro-list.cpp
|
||||
src/utils/macro-list.hpp
|
||||
src/utils/macro-segment-selection.cpp
|
||||
|
|
|
|||
|
|
@ -92,6 +92,12 @@ AdvSceneSwitcher.macroTab.group="Group Selected Macros"
|
|||
AdvSceneSwitcher.macroTab.ungroup="Ungroup Selected Groups"
|
||||
AdvSceneSwitcher.macroTab.rename="Rename"
|
||||
AdvSceneSwitcher.macroTab.remove="Remove"
|
||||
AdvSceneSwitcher.macroTab.export="Export"
|
||||
AdvSceneSwitcher.macroTab.export.info="Paste the string below into the import dialog to import the selected macros:"
|
||||
AdvSceneSwitcher.macroTab.import="Import"
|
||||
AdvSceneSwitcher.macroTab.import.info="Paste the export string into the below text box to import macros:"
|
||||
AdvSceneSwitcher.macroTab.import.invalid="Invalid import data provided!"
|
||||
AdvSceneSwitcher.macroTab.import.nameConflict="Cannot continue importing macro \"%1\" as a macro with the same name already exists!\nDo you want to continue with the import of \"%2\" and choose a new name? (Will be skipped otherwise)"
|
||||
AdvSceneSwitcher.macroTab.expandAll="Expand all"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
|
||||
AdvSceneSwitcher.macroTab.maximize="Maximize"
|
||||
|
|
|
|||
|
|
@ -112,8 +112,7 @@ public slots:
|
|||
void on_actionUp_clicked();
|
||||
void on_actionDown_clicked();
|
||||
void on_actionBottom_clicked();
|
||||
void MacroSelectionChanged(const QItemSelection &,
|
||||
const QItemSelection &);
|
||||
void MacroSelectionChanged();
|
||||
void UpMacroSegementHotkey();
|
||||
void DownMacroSegementHotkey();
|
||||
void DeleteMacroSegementHotkey();
|
||||
|
|
@ -122,6 +121,8 @@ public slots:
|
|||
void ShowMacroConditionsContextMenu(const QPoint &);
|
||||
void CopyMacro();
|
||||
void RenameCurrentMacro();
|
||||
void ExportMacros();
|
||||
void ImportMacros();
|
||||
void ExpandAllActions();
|
||||
void ExpandAllConditions();
|
||||
void CollapseAllActions();
|
||||
|
|
@ -165,6 +166,7 @@ signals:
|
|||
void VariableRemoved(const QString &);
|
||||
|
||||
private:
|
||||
bool ResolveMacroImportNameConflict(std::shared_ptr<Macro> &);
|
||||
bool MacroTabIsInFocus();
|
||||
|
||||
MacroSegmentList *conditionsList = nullptr;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
#include "switcher-data.hpp"
|
||||
#include "name-dialog.hpp"
|
||||
#include "macro-properties.hpp"
|
||||
#include "macro-export-import-dialog.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "version.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QMenu>
|
||||
|
|
@ -18,7 +20,7 @@ namespace advss {
|
|||
static QMetaObject::Connection addPulse;
|
||||
static QTimer onChangeHighlightTimer;
|
||||
|
||||
static bool macroNameExists(std::string name)
|
||||
static bool macroNameExists(const std::string &name)
|
||||
{
|
||||
return !!GetMacroByName(name.c_str());
|
||||
}
|
||||
|
|
@ -60,12 +62,8 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
|
|||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
res = std::make_shared<Macro>(
|
||||
name,
|
||||
switcher->macroProperties._newMacroRegisterHotkeys);
|
||||
}
|
||||
res = std::make_shared<Macro>(
|
||||
name, switcher->macroProperties._newMacroRegisterHotkeys);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -77,11 +75,7 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
|||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
ui->macros->Add(newMacro);
|
||||
}
|
||||
|
||||
ui->macros->Add(newMacro);
|
||||
ui->macroAdd->disconnect(addPulse);
|
||||
emit MacroAdded(QString::fromStdString(name));
|
||||
}
|
||||
|
|
@ -101,11 +95,7 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> ¯o)
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
ui->macros->Remove(macro);
|
||||
}
|
||||
|
||||
ui->macros->Remove(macro);
|
||||
emit MacroRemoved(name);
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +142,6 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
|
|||
|
||||
void AdvSceneSwitcher::on_macroUp_clicked()
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
|
|
@ -162,7 +151,6 @@ void AdvSceneSwitcher::on_macroUp_clicked()
|
|||
|
||||
void AdvSceneSwitcher::on_macroDown_clicked()
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
|
|
@ -202,6 +190,179 @@ void AdvSceneSwitcher::RenameCurrentMacro()
|
|||
ui->macroName->setText(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> ¯os,
|
||||
const std::shared_ptr<Macro> &group)
|
||||
{
|
||||
std::vector<std::shared_ptr<Macro>> subitems;
|
||||
subitems.reserve(group->GroupSize());
|
||||
|
||||
// Find all subitems
|
||||
for (auto it = switcher->macros.begin(); it < switcher->macros.end();
|
||||
it++) {
|
||||
if ((*it)->Name() == group->Name()) {
|
||||
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
|
||||
subitems.emplace_back(*std::next(it, i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove subitems which were already selected to avoid duplicates
|
||||
for (const auto &subitem : subitems) {
|
||||
auto it = std::find(macros.begin(), macros.end(), subitem);
|
||||
if (it == macros.end()) {
|
||||
continue;
|
||||
}
|
||||
macros.erase(it);
|
||||
}
|
||||
|
||||
// Add group subitems
|
||||
auto it = std::find(macros.begin(), macros.end(), group);
|
||||
if (it == macros.end()) {
|
||||
return;
|
||||
}
|
||||
it = std::next(it);
|
||||
macros.insert(it, subitems.begin(), subitems.end());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ExportMacros()
|
||||
{
|
||||
auto selectedMacros = GetSelectedMacros();
|
||||
auto macros = selectedMacros;
|
||||
for (const auto ¯o : selectedMacros) {
|
||||
if (macro->IsGroup() && macro->GroupSize() > 0) {
|
||||
addGroupSubitems(macros, macro);
|
||||
}
|
||||
}
|
||||
|
||||
auto data = obs_data_create();
|
||||
auto macroArray = obs_data_array_create();
|
||||
for (const auto ¯o : macros) {
|
||||
obs_data_t *obj = obs_data_create();
|
||||
macro->Save(obj);
|
||||
obs_data_array_push_back(macroArray, obj);
|
||||
obs_data_release(obj);
|
||||
}
|
||||
obs_data_set_array(data, "macros", macroArray);
|
||||
obs_data_array_release(macroArray);
|
||||
obs_data_set_string(data, "version", g_GIT_TAG);
|
||||
auto json = obs_data_get_json(data);
|
||||
QString exportString(json);
|
||||
obs_data_release(data);
|
||||
|
||||
MacroExportImportDialog::ExportMacros(exportString);
|
||||
}
|
||||
|
||||
bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
|
||||
std::shared_ptr<Macro> ¯o)
|
||||
{
|
||||
QString errorMesg = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.import.nameConflict");
|
||||
errorMesg = errorMesg.arg(QString::fromStdString(macro->Name()),
|
||||
QString::fromStdString(macro->Name()));
|
||||
bool continueResolve = DisplayMessage(errorMesg, true);
|
||||
if (!continueResolve) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString format = QString::fromStdString(macro->Name()) + " %1";
|
||||
int i = 2;
|
||||
|
||||
QString placeHolderText = format.arg(i);
|
||||
while ((macroNameExists(placeHolderText.toUtf8().constData()))) {
|
||||
placeHolderText = format.arg(++i);
|
||||
}
|
||||
|
||||
std::string newName;
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.macroTab.add"),
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.name"), newName,
|
||||
placeHolderText);
|
||||
|
||||
if (!accepted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newName.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (macroNameExists(newName)) {
|
||||
DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
|
||||
return ResolveMacroImportNameConflict(macro);
|
||||
}
|
||||
|
||||
macro->SetName(newName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ImportMacros()
|
||||
{
|
||||
QString json;
|
||||
if (!MacroExportImportDialog::ImportMacros(json)) {
|
||||
return;
|
||||
}
|
||||
auto data = obs_data_create_from_json(json.toStdString().c_str());
|
||||
if (!data) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.import.invalid"));
|
||||
ImportMacros();
|
||||
return;
|
||||
}
|
||||
|
||||
auto version = obs_data_get_string(data, "version");
|
||||
if (strcmp(version, g_GIT_TAG) != 0) {
|
||||
blog(LOG_WARNING,
|
||||
"importing macros from non matching plugin version \"%s\"",
|
||||
version);
|
||||
}
|
||||
|
||||
auto array = obs_data_get_array(data, "macros");
|
||||
size_t count = obs_data_array_count(array);
|
||||
|
||||
int groupSize = 0;
|
||||
std::shared_ptr<Macro> group;
|
||||
|
||||
auto lock = LockContext();
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(array, i);
|
||||
auto macro = std::make_shared<Macro>();
|
||||
macro->Load(array_obj);
|
||||
macro->PostLoad();
|
||||
|
||||
if (macroNameExists(macro->Name()) &&
|
||||
!ResolveMacroImportNameConflict(macro)) {
|
||||
obs_data_release(array_obj);
|
||||
groupSize--;
|
||||
continue;
|
||||
}
|
||||
|
||||
switcher->macros.emplace_back(macro);
|
||||
if (groupSize > 0 && !macro->IsGroup()) {
|
||||
Macro::PrepareMoveToGroup(group, macro);
|
||||
groupSize--;
|
||||
}
|
||||
|
||||
if (macro->IsGroup()) {
|
||||
group = macro;
|
||||
groupSize = macro->GroupSize();
|
||||
// We are not sure if all elements will be added so we
|
||||
// have to reset the group size to zero and add elements
|
||||
// to the group as they come up.
|
||||
macro->ResetGroupSize();
|
||||
}
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(array);
|
||||
obs_data_release(data);
|
||||
|
||||
ui->macros->Reset(switcher->macros,
|
||||
switcher->macroProperties._highlightExecuted);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroName_editingFinished()
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
|
|
@ -372,8 +533,7 @@ std::vector<std::shared_ptr<Macro>> AdvSceneSwitcher::GetSelectedMacros()
|
|||
return ui->macros->GetCurrentMacros();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::MacroSelectionChanged(const QItemSelection &,
|
||||
const QItemSelection &)
|
||||
void AdvSceneSwitcher::MacroSelectionChanged()
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
|
|
@ -445,12 +605,8 @@ void AdvSceneSwitcher::SetupMacroTab()
|
|||
}
|
||||
ui->macros->Reset(switcher->macros,
|
||||
switcher->macroProperties._highlightExecuted);
|
||||
connect(ui->macros->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection &,
|
||||
const QItemSelection &)),
|
||||
this,
|
||||
SLOT(MacroSelectionChanged(const QItemSelection &,
|
||||
const QItemSelection &)));
|
||||
connect(ui->macros, SIGNAL(MacroSelectionChanged()), this,
|
||||
SLOT(MacroSelectionChanged()));
|
||||
|
||||
delete conditionsList;
|
||||
conditionsList = new MacroSegmentList(this);
|
||||
|
|
@ -569,6 +725,15 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
|
|||
obs_module_text("AdvSceneSwitcher.macroTab.remove"), this,
|
||||
&AdvSceneSwitcher::on_macroRemove_clicked);
|
||||
remove->setDisabled(ui->macros->SelectionEmpty());
|
||||
menu.addSeparator();
|
||||
|
||||
auto exportAction = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.export"), this,
|
||||
&AdvSceneSwitcher::ExportMacros);
|
||||
exportAction->setDisabled(ui->macros->SelectionEmpty());
|
||||
auto import = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.import"), this,
|
||||
&AdvSceneSwitcher::ImportMacros);
|
||||
|
||||
menu.exec(globalPos);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -395,6 +395,7 @@ CountItemsVisibleInModel(const std::deque<std::shared_ptr<Macro>> ¯os)
|
|||
|
||||
void MacroTreeModel::Add(std::shared_ptr<Macro> item)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto idx = CountItemsVisibleInModel(_macros);
|
||||
beginInsertRows(QModelIndex(), idx, idx);
|
||||
_macros.emplace_back(item);
|
||||
|
|
@ -407,6 +408,7 @@ void MacroTreeModel::Add(std::shared_ptr<Macro> item)
|
|||
|
||||
void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto uiStartIdx = GetItemModelIndex(item);
|
||||
if (uiStartIdx == -1) {
|
||||
return;
|
||||
|
|
@ -759,6 +761,12 @@ void MacroTree::Reset(std::deque<std::shared_ptr<Macro>> ¯os,
|
|||
MacroTreeModel *mtm = new MacroTreeModel(this, macros);
|
||||
setModel(mtm);
|
||||
GetModel()->Reset(macros);
|
||||
connect(selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection &,
|
||||
const QItemSelection &)),
|
||||
this,
|
||||
SLOT(SelectionChangedHelper(const QItemSelection &,
|
||||
const QItemSelection &)));
|
||||
}
|
||||
|
||||
void MacroTree::Add(std::shared_ptr<Macro> item,
|
||||
|
|
@ -1173,6 +1181,7 @@ void MacroTree::Remove(std::shared_ptr<Macro> item) const
|
|||
|
||||
void MacroTree::Up(std::shared_ptr<Macro> item) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto above = GetModel()->Neighbor(item, true);
|
||||
if (!above) {
|
||||
return;
|
||||
|
|
@ -1198,6 +1207,7 @@ void MacroTree::Up(std::shared_ptr<Macro> item) const
|
|||
|
||||
void MacroTree::Down(std::shared_ptr<Macro> item) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto below = GetModel()->Neighbor(item, false);
|
||||
if (!below) {
|
||||
return;
|
||||
|
|
@ -1247,6 +1257,12 @@ void MacroTree::UngroupSelectedGroups()
|
|||
assert(GetModel()->IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTree::SelectionChangedHelper(const QItemSelection &,
|
||||
const QItemSelection &)
|
||||
{
|
||||
emit MacroSelectionChanged();
|
||||
}
|
||||
|
||||
inline MacroTreeItem *MacroTree::GetItemWidget(int idx) const
|
||||
{
|
||||
QWidget *widget = indexWidget(GetModel()->createIndex(idx, 0, nullptr));
|
||||
|
|
|
|||
|
|
@ -138,6 +138,11 @@ public:
|
|||
public slots:
|
||||
void GroupSelectedItems();
|
||||
void UngroupSelectedGroups();
|
||||
void SelectionChangedHelper(const QItemSelection &,
|
||||
const QItemSelection &);
|
||||
|
||||
signals:
|
||||
void MacroSelectionChanged();
|
||||
|
||||
protected:
|
||||
virtual void dropEvent(QDropEvent *event) override;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ public:
|
|||
std::shared_ptr<Macro> item);
|
||||
bool IsGroup() const { return _isGroup; }
|
||||
uint32_t GroupSize() const { return _groupSize; }
|
||||
void ResetGroupSize() { _groupSize = 0; }
|
||||
bool IsSubitem() const { return !_parent.expired(); }
|
||||
void SetCollapsed(bool val) { _isCollapsed = val; }
|
||||
bool IsCollapsed() const { return _isCollapsed; }
|
||||
|
|
|
|||
61
src/utils/macro-export-import-dialog.cpp
Normal file
61
src/utils/macro-export-import-dialog.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#include "macro-export-import-dialog.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroExportImportDialog::MacroExportImportDialog(Type type)
|
||||
: QDialog(nullptr), _importExportString(new QPlainTextEdit(this))
|
||||
{
|
||||
_importExportString->setReadOnly(type == Type::EXPORT_MACRO);
|
||||
auto label = new QLabel(obs_module_text(
|
||||
type == Type::EXPORT_MACRO
|
||||
? "AdvSceneSwitcher.macroTab.export.info"
|
||||
: "AdvSceneSwitcher.macroTab.import.info"));
|
||||
label->setWordWrap(true);
|
||||
|
||||
QDialogButtonBox *buttons = nullptr;
|
||||
if (type == Type::EXPORT_MACRO) {
|
||||
buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
} else {
|
||||
buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel);
|
||||
}
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
buttons->setCenterButtons(true);
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(_importExportString);
|
||||
layout->addWidget(buttons);
|
||||
setLayout(layout);
|
||||
|
||||
setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||
}
|
||||
|
||||
void MacroExportImportDialog::ExportMacros(const QString &json)
|
||||
{
|
||||
MacroExportImportDialog dialog(
|
||||
MacroExportImportDialog::Type::EXPORT_MACRO);
|
||||
dialog._importExportString->setPlainText(json);
|
||||
dialog.adjustSize();
|
||||
dialog.updateGeometry();
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
bool MacroExportImportDialog::ImportMacros(QString &json)
|
||||
{
|
||||
MacroExportImportDialog dialog(
|
||||
MacroExportImportDialog::Type::IMPORT_MACRO);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
json = dialog._importExportString->toPlainText();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
20
src/utils/macro-export-import-dialog.hpp
Normal file
20
src/utils/macro-export-import-dialog.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include <QDialog>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroExportImportDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class Type { EXPORT_MACRO, IMPORT_MACRO };
|
||||
MacroExportImportDialog(Type type);
|
||||
|
||||
static void ExportMacros(const QString &json);
|
||||
static bool ImportMacros(QString &json);
|
||||
|
||||
private:
|
||||
QPlainTextEdit *_importExportString;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
Loading…
Reference in New Issue
Block a user