mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 20:55:28 -05:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
151f69305b | ||
|
|
71e1e395df | ||
|
|
74e5ca1b57 |
@@ -173,6 +173,8 @@ target_sources(
|
||||
lib/macro/macro-signals.hpp
|
||||
lib/macro/macro-tab.cpp
|
||||
lib/macro/macro-tree.cpp
|
||||
lib/macro/macro-undo-redo.cpp
|
||||
lib/macro/macro-undo-redo.hpp
|
||||
lib/macro/macro-tree.hpp
|
||||
lib/macro/macro-websocket-trigger.cpp
|
||||
lib/macro/macro.cpp
|
||||
|
||||
@@ -198,6 +198,17 @@ AdvSceneSwitcher.macroTab.groupNameExists="The name \"%1\" is already used by a
|
||||
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Are you sure you want to remove \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="Are you sure you want to remove %1 macros/groups?"
|
||||
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Are you sure you want to remove \"%1\" group and all its elements?"
|
||||
AdvSceneSwitcher.undo.renameMacro="Rename Macro '%1'"
|
||||
AdvSceneSwitcher.undo.addMacro="Add Macro '%1'"
|
||||
AdvSceneSwitcher.undo.removeMacro="Remove Macro '%1'"
|
||||
AdvSceneSwitcher.undo.addSegment="Add %1 in '%2'"
|
||||
AdvSceneSwitcher.undo.removeSegment="Remove %1 in '%2'"
|
||||
AdvSceneSwitcher.undo.groupMacros="Group Macros '%1'"
|
||||
AdvSceneSwitcher.undo.ungroupMacros="Ungroup Macros '%1'"
|
||||
AdvSceneSwitcher.undo.removeMacroGroup="Remove Macro Group '%1'"
|
||||
AdvSceneSwitcher.undo.segment.action="Action"
|
||||
AdvSceneSwitcher.undo.segment.elseAction="Else Action"
|
||||
AdvSceneSwitcher.undo.segment.condition="Condition"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="Add"
|
||||
AdvSceneSwitcher.macroTab.copy="Duplicate Macro"
|
||||
AdvSceneSwitcher.macroTab.group="Group Selected Macros"
|
||||
@@ -3107,4 +3118,3 @@ AdvSceneSwitcher.sceneGroupTab.defaultname="Scene Group %1"
|
||||
AdvSceneSwitcher.sceneGroupTab.exists="Scene Group or Scene name exists already"
|
||||
AdvSceneSwitcher.sceneGroupTab.help="Scene Groups can be selected as a target just like a regular scene.\n\nAs the name suggests a scene group is a collection of multiple scenes.\nThe scene group will advance through the list of its assigned scenes depending on the configured settings, which can be found on the right side.\n\nYou can configure the scene group to advance to the next scene in the list:\nAfter a number of times the scene group is selected as a target.\nAfter a certain amount of time has passed.\nOr randomly.\n\nFor example, a scene group containing the scenes ...\nScene 1\nScene 2\nScene 3 \n... will activate \"Scene 1\" the first time it is selected as a target.\nThe second time it will activate \"Scene 2\".\nThe remaining times \"Scene 3\" will be activated.\n\nClick the highlighted plus symbol below to add a new scene group."
|
||||
AdvSceneSwitcher.sceneGroupTab.scenes.help="Select the scene group you want to modify on the left.\n\nSelect a scene to add to this scene group by selecting the scene above and clicking the plus symbol below.\n\nA scene can be added multiple times to the same scene group."
|
||||
AdvSceneSwitcher.kwin.flatpakPermissionWarning="Window detection via KWin is unavailable.\nThe OBS Studio Flatpak requires additional permissions to enable this feature.\nSee the Advanced Scene Switcher wiki for details."
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
#include "platform-funcs.hpp"
|
||||
|
||||
#include "log-helper.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
@@ -632,39 +628,27 @@ int ignoreXerror(Display *d, XErrorEvent *e)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handleKWinInitFailure(bool kwinAvailable)
|
||||
{
|
||||
blog(LOG_INFO, "not using KWin compat");
|
||||
const bool onKDE = qEnvironmentVariable("XDG_CURRENT_DESKTOP")
|
||||
.contains("KDE", Qt::CaseInsensitive);
|
||||
const bool inFlatpak = qEnvironmentVariableIsSet("FLATPAK_ID");
|
||||
if (!kwinAvailable && onKDE && inFlatpak)
|
||||
AddFinishedLoadingStep([]() {
|
||||
DisplayTrayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
obs_module_text("AdvSceneSwitcher.kwin.flatpakPermissionWarning"));
|
||||
});
|
||||
}
|
||||
|
||||
void PlatformInit()
|
||||
{
|
||||
const bool kwinAvailable = isKWinAvailable();
|
||||
KWin = kwinAvailable && registerKWinDBusListener(¬ifier) &&
|
||||
startKWinScript(KWinScriptObjectPath);
|
||||
if (KWin)
|
||||
blog(LOG_INFO, "using KWin compat");
|
||||
else
|
||||
handleKWinInitFailure(kwinAvailable);
|
||||
|
||||
initProcps();
|
||||
initProc2();
|
||||
|
||||
auto display = disp();
|
||||
if (!display) {
|
||||
return;
|
||||
}
|
||||
|
||||
KWin = isKWinAvailable();
|
||||
if (!(KWin && startKWinScript(KWinScriptObjectPath) &&
|
||||
registerKWinDBusListener(¬ifier))) {
|
||||
// something bad happened while trying to initialize
|
||||
// the KWin script/dbus so disable it
|
||||
KWin = false;
|
||||
blog(LOG_INFO, "not using KWin compat");
|
||||
} else {
|
||||
blog(LOG_INFO, "using KWin compat");
|
||||
}
|
||||
|
||||
initXss();
|
||||
initProcps();
|
||||
initProc2();
|
||||
XSetErrorHandler(ignoreXerror);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileDevice>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
#include <QtDBus/QDBusConnectionInterface>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
@@ -98,13 +97,11 @@ bool isKWinAvailable()
|
||||
bool startKWinScript(QString &scriptObjectPath)
|
||||
{
|
||||
const QString scriptPath =
|
||||
QStandardPaths::writableLocation(
|
||||
QStandardPaths::RuntimeLocation) +
|
||||
"/AdvancedSceneSwitcher/KWinFocusNotifier.js";
|
||||
"/tmp/AdvancedSceneSwitcher/KWinFocusNotifier.js";
|
||||
|
||||
const QString script =
|
||||
R"(var adss = "com.obsproject.Studio.Plugin.SceneSwitcher";
|
||||
var adssPath = "/com/obsproject/Studio/Plugin/SceneSwitcher";
|
||||
R"(var adss = "com.github.AdvancedSceneSwitcher";
|
||||
var adssPath = "/com/github/AdvancedSceneSwitcher";
|
||||
|
||||
function trackWindow(window) {
|
||||
var id = window.internalId.toString();
|
||||
@@ -132,8 +129,7 @@ workspace.windowActivated.connect(function(client) {
|
||||
}))";
|
||||
|
||||
if (const QDir dir; !dir.mkpath(QFileInfo(scriptPath).absolutePath())) {
|
||||
blog(LOG_ERROR,
|
||||
"error creating KWinFocusNotifier script directory");
|
||||
blog(LOG_ERROR, "error creating /tmp/AdvancedSceneSwitcher");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -202,8 +198,8 @@ bool stopKWinScript(const QString &scriptObjectPath)
|
||||
|
||||
bool registerKWinDBusListener(FocusNotifier *notifier)
|
||||
{
|
||||
static const QString serviceName = "com.obsproject.Studio.Plugin.SceneSwitcher";
|
||||
static const QString objectPath = "/com/obsproject/Studio/Plugin/SceneSwitcher";
|
||||
static const QString serviceName = "com.github.AdvancedSceneSwitcher";
|
||||
static const QString objectPath = "/com/github/AdvancedSceneSwitcher";
|
||||
auto bus = QDBusConnection::sessionBus();
|
||||
|
||||
if (bus.objectRegisteredAt(objectPath)) {
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace advss {
|
||||
|
||||
class FocusNotifier final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "com.obsproject.Studio.Plugin.SceneSwitcher")
|
||||
Q_CLASSINFO("D-Bus Interface", "com.github.AdvancedSceneSwitcher")
|
||||
|
||||
static std::mutex _mutex;
|
||||
static int activePID;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "macro-edit.hpp"
|
||||
|
||||
#include "condition-logic.hpp"
|
||||
#include "macro-undo-redo.hpp"
|
||||
#include "cursor-shape-changer.hpp"
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-action-macro.hpp"
|
||||
@@ -257,6 +258,92 @@ std::shared_ptr<Macro> MacroEdit::GetMacro() const
|
||||
return _currentMacro;
|
||||
}
|
||||
|
||||
void MacroEdit::InsertSegmentWidget(SegmentType type, int idx)
|
||||
{
|
||||
auto macro = _currentMacro;
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SegmentType::ACTION: {
|
||||
if (idx < 0 || idx > (int)macro->Actions().size()) {
|
||||
break;
|
||||
}
|
||||
ui->actionsList->Insert(
|
||||
idx, new MacroActionEdit(this, ¯o->Actions()[idx]));
|
||||
SetActionData(*macro);
|
||||
ui->actionsList->SetHelpMsgVisible(false);
|
||||
break;
|
||||
}
|
||||
case SegmentType::ELSE_ACTION: {
|
||||
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
|
||||
break;
|
||||
}
|
||||
ui->elseActionsList->Insert(
|
||||
idx,
|
||||
new MacroActionEdit(this, ¯o->ElseActions()[idx]));
|
||||
SetElseActionData(*macro);
|
||||
ui->elseActionsList->SetHelpMsgVisible(false);
|
||||
break;
|
||||
}
|
||||
case SegmentType::CONDITION: {
|
||||
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
||||
break;
|
||||
}
|
||||
ui->conditionsList->Insert(
|
||||
idx,
|
||||
new MacroConditionEdit(this, ¯o->Conditions()[idx],
|
||||
idx == 0));
|
||||
if (idx == 0 && macro->Conditions().size() > 1) {
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(1))
|
||||
->SetRootNode(false);
|
||||
}
|
||||
SetConditionData(*macro);
|
||||
ui->conditionsList->SetHelpMsgVisible(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroEdit::RemoveSegmentWidget(SegmentType type, int idx)
|
||||
{
|
||||
auto macro = _currentMacro;
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SegmentType::ACTION:
|
||||
if (idx >= 0 &&
|
||||
idx < ui->actionsList->ContentLayout()->count()) {
|
||||
ui->actionsList->Remove(idx);
|
||||
SetActionData(*macro);
|
||||
}
|
||||
break;
|
||||
case SegmentType::ELSE_ACTION:
|
||||
if (idx >= 0 &&
|
||||
idx < ui->elseActionsList->ContentLayout()->count()) {
|
||||
ui->elseActionsList->Remove(idx);
|
||||
SetElseActionData(*macro);
|
||||
}
|
||||
break;
|
||||
case SegmentType::CONDITION:
|
||||
if (idx >= 0 &&
|
||||
idx < ui->conditionsList->ContentLayout()->count()) {
|
||||
ui->conditionsList->Remove(idx);
|
||||
if (idx == 0 && !macro->Conditions().empty()) {
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(0))
|
||||
->SetRootNode(true);
|
||||
}
|
||||
SetConditionData(*macro);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MacroEdit::ClearSegmentWidgetCacheFor(Macro *macro) const
|
||||
{
|
||||
ui->conditionsList->ClearWidgetsFromCacheFor(macro);
|
||||
@@ -295,7 +382,7 @@ isValidMacroSegmentIdx(const std::deque<std::shared_ptr<MacroSegment>> &list,
|
||||
return (idx > 0 || (unsigned)idx < list.size());
|
||||
}
|
||||
|
||||
void MacroEdit::SetupMacroSegmentSelection(MacroSection type, int idx)
|
||||
void MacroEdit::SetupMacroSegmentSelection(SegmentType type, int idx)
|
||||
{
|
||||
auto macro = _currentMacro;
|
||||
if (!macro) {
|
||||
@@ -308,7 +395,7 @@ void MacroEdit::SetupMacroSegmentSelection(MacroSection type, int idx)
|
||||
std::deque<std::shared_ptr<MacroSegment>> segments;
|
||||
|
||||
switch (type) {
|
||||
case MacroEdit::MacroSection::CONDITIONS:
|
||||
case MacroEdit::SegmentType::CONDITION:
|
||||
setList = ui->conditionsList;
|
||||
setIdx = ¤tConditionIdx;
|
||||
segments = {macro->Conditions().begin(),
|
||||
@@ -319,7 +406,7 @@ void MacroEdit::SetupMacroSegmentSelection(MacroSection type, int idx)
|
||||
resetIdx1 = ¤tActionIdx;
|
||||
resetIdx2 = ¤tElseActionIdx;
|
||||
break;
|
||||
case MacroEdit::MacroSection::ACTIONS:
|
||||
case MacroEdit::SegmentType::ACTION:
|
||||
setList = ui->actionsList;
|
||||
setIdx = ¤tActionIdx;
|
||||
segments = {macro->Actions().begin(), macro->Actions().end()};
|
||||
@@ -329,7 +416,7 @@ void MacroEdit::SetupMacroSegmentSelection(MacroSection type, int idx)
|
||||
resetIdx1 = ¤tConditionIdx;
|
||||
resetIdx2 = ¤tElseActionIdx;
|
||||
break;
|
||||
case MacroEdit::MacroSection::ELSE_ACTIONS:
|
||||
case MacroEdit::SegmentType::ELSE_ACTION:
|
||||
setList = ui->elseActionsList;
|
||||
setIdx = ¤tElseActionIdx;
|
||||
segments = {macro->ElseActions().begin(),
|
||||
@@ -965,7 +1052,7 @@ void MacroEdit::UpMacroSegmentHotkey()
|
||||
int conditionSize = macro->Conditions().size();
|
||||
|
||||
if (currentActionIdx == -1 && currentConditionIdx == -1) {
|
||||
if (lastInteracted == MacroSection::CONDITIONS) {
|
||||
if (lastInteracted == SegmentType::CONDITION) {
|
||||
if (conditionSize == 0) {
|
||||
MacroActionSelectionChanged(0);
|
||||
} else {
|
||||
@@ -1021,7 +1108,7 @@ void MacroEdit::DownMacroSegmentHotkey()
|
||||
int conditionSize = macro->Conditions().size();
|
||||
|
||||
if (currentActionIdx == -1 && currentConditionIdx == -1) {
|
||||
if (lastInteracted == MacroSection::CONDITIONS) {
|
||||
if (lastInteracted == SegmentType::CONDITION) {
|
||||
if (conditionSize == 0) {
|
||||
MacroActionSelectionChanged(0);
|
||||
} else {
|
||||
@@ -1098,6 +1185,7 @@ void MacroEdit::AddMacroAction(Macro *macro, int idx, const std::string &id,
|
||||
idx, new MacroActionEdit(this, ¯o->Actions()[idx]));
|
||||
SetActionData(*macro);
|
||||
}
|
||||
RegisterSegmentAddUndoRedo(macro, SegmentType::ACTION, idx);
|
||||
HighlightAction(idx);
|
||||
ui->actionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
@@ -1163,6 +1251,19 @@ void MacroEdit::RemoveMacroAction(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
OBSDataAutoRelease segData = obs_data_create();
|
||||
std::string segId;
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->Actions().at(idx)->Save(segData);
|
||||
segId = macro->Actions().at(idx)->GetId();
|
||||
}
|
||||
RegisterSegmentRemoveUndoRedo(macro.get(), SegmentType::ACTION,
|
||||
idx, segId, segData,
|
||||
(int)Logic::Type::ROOT_NONE);
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
ui->actionsList->Remove(idx);
|
||||
@@ -1173,7 +1274,7 @@ void MacroEdit::RemoveMacroAction(int idx)
|
||||
SetActionData(*macro);
|
||||
}
|
||||
MacroActionSelectionChanged(-1);
|
||||
lastInteracted = MacroSection::ACTIONS;
|
||||
lastInteracted = SegmentType::ACTION;
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
@@ -1353,7 +1454,7 @@ void MacroEdit::MoveMacroActionDown(int idx)
|
||||
|
||||
void MacroEdit::MacroElseActionSelectionChanged(int idx)
|
||||
{
|
||||
SetupMacroSegmentSelection(MacroSection::ELSE_ACTIONS, idx);
|
||||
SetupMacroSegmentSelection(SegmentType::ELSE_ACTION, idx);
|
||||
}
|
||||
|
||||
void MacroEdit::MacroElseActionReorder(int to, int from)
|
||||
@@ -1406,6 +1507,7 @@ void MacroEdit::AddMacroElseAction(Macro *macro, int idx, const std::string &id,
|
||||
new MacroActionEdit(this, ¯o->ElseActions()[idx]));
|
||||
SetElseActionData(*macro);
|
||||
}
|
||||
RegisterSegmentAddUndoRedo(macro, SegmentType::ELSE_ACTION, idx);
|
||||
HighlightElseAction(idx);
|
||||
ui->elseActionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
@@ -1455,6 +1557,20 @@ void MacroEdit::RemoveMacroElseAction(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
OBSDataAutoRelease segData = obs_data_create();
|
||||
std::string segId;
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->ElseActions().at(idx)->Save(segData);
|
||||
segId = macro->ElseActions().at(idx)->GetId();
|
||||
}
|
||||
RegisterSegmentRemoveUndoRedo(macro.get(),
|
||||
SegmentType::ELSE_ACTION, idx,
|
||||
segId, segData,
|
||||
(int)Logic::Type::ROOT_NONE);
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
ui->elseActionsList->Remove(idx);
|
||||
@@ -1465,7 +1581,7 @@ void MacroEdit::RemoveMacroElseAction(int idx)
|
||||
SetElseActionData(*macro);
|
||||
}
|
||||
MacroElseActionSelectionChanged(-1);
|
||||
lastInteracted = MacroSection::ELSE_ACTIONS;
|
||||
lastInteracted = SegmentType::ELSE_ACTION;
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
@@ -1526,7 +1642,7 @@ void MacroEdit::MoveMacroElseActionDown(int idx)
|
||||
|
||||
void MacroEdit::MacroActionSelectionChanged(int idx)
|
||||
{
|
||||
SetupMacroSegmentSelection(MacroSection::ACTIONS, idx);
|
||||
SetupMacroSegmentSelection(SegmentType::ACTION, idx);
|
||||
}
|
||||
|
||||
void MacroEdit::MacroActionReorder(int to, int from)
|
||||
@@ -1617,6 +1733,7 @@ void MacroEdit::AddMacroCondition(Macro *macro, int idx, const std::string &id,
|
||||
idx == 0));
|
||||
SetConditionData(*macro);
|
||||
}
|
||||
RegisterSegmentAddUndoRedo(macro, SegmentType::CONDITION, idx);
|
||||
HighlightCondition(idx);
|
||||
ui->conditionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
@@ -1652,6 +1769,21 @@ void MacroEdit::RemoveMacroCondition(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
OBSDataAutoRelease segData = obs_data_create();
|
||||
std::string segId;
|
||||
int logic;
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->Conditions().at(idx)->Save(segData);
|
||||
segId = macro->Conditions().at(idx)->GetId();
|
||||
logic = (int)macro->Conditions().at(idx)->GetLogicType();
|
||||
}
|
||||
RegisterSegmentRemoveUndoRedo(macro.get(),
|
||||
SegmentType::CONDITION, idx,
|
||||
segId, segData, logic);
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
ui->conditionsList->Remove(idx);
|
||||
@@ -1667,7 +1799,7 @@ void MacroEdit::RemoveMacroCondition(int idx)
|
||||
SetConditionData(*macro);
|
||||
}
|
||||
MacroConditionSelectionChanged(-1);
|
||||
lastInteracted = MacroSection::CONDITIONS;
|
||||
lastInteracted = SegmentType::CONDITION;
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
@@ -1792,7 +1924,7 @@ void MacroEdit::MoveMacroConditionDown(int idx)
|
||||
|
||||
void MacroEdit::MacroConditionSelectionChanged(int idx)
|
||||
{
|
||||
SetupMacroSegmentSelection(MacroSection::CONDITIONS, idx);
|
||||
SetupMacroSegmentSelection(SegmentType::CONDITION, idx);
|
||||
}
|
||||
|
||||
void MacroEdit::MacroConditionReorder(int to, int from)
|
||||
|
||||
@@ -12,11 +12,15 @@ class MacroEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class SegmentType { CONDITION, ACTION, ELSE_ACTION };
|
||||
|
||||
MacroEdit(QWidget *parent, QStringList helpMsg = {});
|
||||
void SetMacro(const std::shared_ptr<Macro> &);
|
||||
std::shared_ptr<Macro> GetMacro() const;
|
||||
void ClearSegmentWidgetCacheFor(Macro *) const;
|
||||
void SetControlsDisabled(bool disable) const;
|
||||
void InsertSegmentWidget(SegmentType type, int idx);
|
||||
void RemoveSegmentWidget(SegmentType type, int idx);
|
||||
void HighlightAction(int idx, QColor color = QColor(Qt::green)) const;
|
||||
void HighlightElseAction(int idx,
|
||||
QColor color = QColor(Qt::green)) const;
|
||||
@@ -112,14 +116,12 @@ protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private:
|
||||
enum class MacroSection { CONDITIONS, ACTIONS, ELSE_ACTIONS };
|
||||
|
||||
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
|
||||
void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0);
|
||||
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
|
||||
void ScrollAndFocusNewSegment(MacroSegmentList *list, int newIdx,
|
||||
int *currentIdx);
|
||||
void SetupMacroSegmentSelection(MacroSection type, int idx);
|
||||
void SetupMacroSegmentSelection(SegmentType type, int idx);
|
||||
void
|
||||
SetupContextMenu(const QPoint &pos,
|
||||
const std::function<void(MacroEdit *, int)> &remove,
|
||||
@@ -131,7 +133,7 @@ private:
|
||||
void RunSegmentHighlightChecks();
|
||||
bool ElseSectionIsVisible() const;
|
||||
|
||||
MacroSection lastInteracted = MacroSection::CONDITIONS;
|
||||
SegmentType lastInteracted = SegmentType::CONDITION;
|
||||
int currentConditionIdx = -1;
|
||||
int currentActionIdx = -1;
|
||||
int currentElseActionIdx = -1;
|
||||
|
||||
@@ -201,6 +201,8 @@ bool MacroSegmentList::PopulateWidgetsFromCache(const Macro *macro)
|
||||
_contentLayout->addWidget(widget);
|
||||
widget->show();
|
||||
}
|
||||
// Widgets are now owned by the layout again, so the cache entry is stale.
|
||||
_widgetCache.erase(it);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
@@ -463,7 +465,17 @@ void MacroSegmentList::SetupVisibleMacroSegmentWidgets()
|
||||
|
||||
const auto viewportRect = viewport()->rect();
|
||||
|
||||
for (auto segment : widget()->findChildren<MacroSegmentEdit *>()) {
|
||||
for (int i = 0; i < _contentLayout->count(); ++i) {
|
||||
auto item = _contentLayout->itemAt(i);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto segment = dynamic_cast<MacroSegmentEdit *>(item->widget());
|
||||
if (!segment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto pos = segment->mapTo(viewport(), QPoint(0, 0));
|
||||
const QRect rect(pos, segment->size());
|
||||
if (!viewportRect.intersects(rect)) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "macro-settings.hpp"
|
||||
#include "macro-signals.hpp"
|
||||
#include "macro-tree.hpp"
|
||||
#include "macro-undo-redo.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "math-helpers.hpp"
|
||||
#include "name-dialog.hpp"
|
||||
@@ -122,6 +123,7 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
||||
ui->macros->Add(newMacro);
|
||||
MacroSignalManager::Instance()->Add(
|
||||
QString::fromStdString(name));
|
||||
RegisterMacroAddUndoRedo(name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -131,6 +133,7 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
||||
ui->macros->AddToGroup(newMacro, selectedMacro);
|
||||
MacroSignalManager::Instance()->Add(
|
||||
QString::fromStdString(name));
|
||||
RegisterMacroAddUndoRedo(name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,12 +142,14 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
||||
ui->macros->Add(newMacro, selectedMacro);
|
||||
MacroSignalManager::Instance()->Add(
|
||||
QString::fromStdString(name));
|
||||
RegisterMacroAddUndoRedo(name);
|
||||
return;
|
||||
}
|
||||
|
||||
Macro::PrepareMoveToGroup(selectedMacroGroup, newMacro);
|
||||
ui->macros->Add(newMacro, selectedMacro);
|
||||
MacroSignalManager::Instance()->Add(QString::fromStdString(name));
|
||||
RegisterMacroAddUndoRedo(name);
|
||||
}
|
||||
|
||||
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> ¯os,
|
||||
@@ -185,6 +190,12 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> ¯o)
|
||||
}
|
||||
}
|
||||
|
||||
if (macro->IsGroup()) {
|
||||
RegisterGroupDeleteUndoRedo(macro.get());
|
||||
} else {
|
||||
RegisterMacroRemoveUndoRedo(macro.get());
|
||||
}
|
||||
|
||||
if (macro->IsGroup()) {
|
||||
std::vector<std::shared_ptr<Macro>> macros = {macro};
|
||||
addGroupSubitems(macros, macro);
|
||||
@@ -241,13 +252,16 @@ void AdvSceneSwitcher::RemoveSelectedMacros()
|
||||
void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> ¯o,
|
||||
const QString &name)
|
||||
{
|
||||
auto oldName = QString::fromStdString(macro->Name());
|
||||
const std::string oldName = macro->Name();
|
||||
const std::string newName = name.toStdString();
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->SetName(name.toStdString());
|
||||
macro->SetName(newName);
|
||||
}
|
||||
|
||||
MacroSignalManager::Instance()->Rename(oldName, name);
|
||||
RegisterMacroRenameUndoRedo(oldName, newName);
|
||||
MacroSignalManager::Instance()->Rename(QString::fromStdString(oldName),
|
||||
name);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroRemove_clicked()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "macro-tree.hpp"
|
||||
#include "macro-undo-redo.hpp"
|
||||
|
||||
#include "macro-helpers.hpp"
|
||||
#include "macro-search.hpp"
|
||||
@@ -824,6 +825,8 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
|
||||
|
||||
Reset(_macros);
|
||||
assert(IsInValidState());
|
||||
|
||||
RegisterGroupCreateUndoRedo(name.toStdString());
|
||||
}
|
||||
|
||||
void MacroTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
|
||||
@@ -837,6 +840,7 @@ void MacroTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
|
||||
std::shared_ptr<Macro> item = _macros[ModelIndexToMacroIndex(
|
||||
indices[i].row(), _macros)];
|
||||
if (item->IsGroup()) {
|
||||
RegisterGroupRemoveUndoRedo(item->Name());
|
||||
Macro::RemoveGroup(item);
|
||||
}
|
||||
}
|
||||
|
||||
931
lib/macro/macro-undo-redo.cpp
Normal file
931
lib/macro/macro-undo-redo.cpp
Normal file
@@ -0,0 +1,931 @@
|
||||
#include "macro-undo-redo.hpp"
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "condition-logic.hpp"
|
||||
#include "macro-action-factory.hpp"
|
||||
#include "macro-condition-factory.hpp"
|
||||
#include "macro-edit.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "macro-segment-list.hpp"
|
||||
#include "macro-settings.hpp"
|
||||
#include "macro-signals.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <obs.hpp>
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace advss {
|
||||
|
||||
using SegmentType = MacroEdit::SegmentType;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internal helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
enum class SegmentOp { ADD, REMOVE };
|
||||
|
||||
static const char *segmentTypeName(SegmentType type)
|
||||
{
|
||||
switch (type) {
|
||||
case SegmentType::ACTION:
|
||||
return obs_module_text("AdvSceneSwitcher.undo.segment.action");
|
||||
case SegmentType::ELSE_ACTION:
|
||||
return obs_module_text(
|
||||
"AdvSceneSwitcher.undo.segment.elseAction");
|
||||
case SegmentType::CONDITION:
|
||||
return obs_module_text(
|
||||
"AdvSceneSwitcher.undo.segment.condition");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string fmtUndoName(const char *key, const std::string &arg1)
|
||||
{
|
||||
return QString(obs_module_text(key))
|
||||
.arg(QString::fromStdString(arg1))
|
||||
.toStdString();
|
||||
}
|
||||
|
||||
static std::string fmtUndoName(const char *key, const char *arg1,
|
||||
const std::string &arg2)
|
||||
{
|
||||
return QString(obs_module_text(key))
|
||||
.arg(arg1)
|
||||
.arg(QString::fromStdString(arg2))
|
||||
.toStdString();
|
||||
}
|
||||
|
||||
static void updateMacroEditSegment(AdvSceneSwitcher *window,
|
||||
const std::string ¯oName, SegmentOp op,
|
||||
SegmentType type, int idx)
|
||||
{
|
||||
if (!SettingsWindowIsOpened()) {
|
||||
return;
|
||||
}
|
||||
auto *const macroEdit = window->ui->macroEdit;
|
||||
const auto current = macroEdit->GetMacro();
|
||||
if (!current || current->Name() != macroName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// std::deque invalidates all element addresses on any insert or erase.
|
||||
// Re-point every existing widget before inserting/removing one, so none
|
||||
// hold stale pointers.
|
||||
macroEdit->SetActionData(*current);
|
||||
macroEdit->SetElseActionData(*current);
|
||||
macroEdit->SetConditionData(*current);
|
||||
|
||||
if (op == SegmentOp::ADD) {
|
||||
macroEdit->InsertSegmentWidget(type, idx);
|
||||
} else {
|
||||
macroEdit->RemoveSegmentWidget(type, idx);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Macro-level undo/redo callbacks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Callback data format for deletion: {"name": "MacroName"}
|
||||
static void deleteMacroByName(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string name = obs_data_get_string(data, "name");
|
||||
const auto macro = GetWeakMacroByName(name.c_str()).lock();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto *const window = AdvSceneSwitcher::window;
|
||||
if (SettingsWindowIsOpened() && window) {
|
||||
window->ui->macroEdit->ClearSegmentWidgetCacheFor(macro.get());
|
||||
MacroSegmentList::SetCachingEnabled(false);
|
||||
window->ui->macros->Remove(macro);
|
||||
MacroSegmentList::SetCachingEnabled(true);
|
||||
} else {
|
||||
auto lock = LockContext();
|
||||
auto ¯os = GetTopLevelMacros();
|
||||
const auto it = std::find(macros.begin(), macros.end(), macro);
|
||||
if (it != macros.end()) {
|
||||
macros.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
MacroSignalManager::Instance()->Remove(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
// Callback data format for restoration:
|
||||
// {"index": N, "parent_group": "GroupName", "macro": {...}}
|
||||
static void restoreMacroFromData(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int index = (int)obs_data_get_int(data, "index");
|
||||
const std::string parentGroup =
|
||||
obs_data_get_string(data, "parent_group");
|
||||
OBSDataAutoRelease macroData = obs_data_get_obj(data, "macro");
|
||||
if (!macroData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto macro = std::make_shared<Macro>();
|
||||
std::string macroName;
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->Load(macroData);
|
||||
macro->PostLoad();
|
||||
RunAndClearPostLoadSteps();
|
||||
|
||||
if (!parentGroup.empty()) {
|
||||
const auto parent =
|
||||
GetWeakMacroByName(parentGroup.c_str()).lock();
|
||||
if (parent) {
|
||||
Macro::PrepareMoveToGroup(parent, macro);
|
||||
}
|
||||
}
|
||||
|
||||
auto ¯os = GetTopLevelMacros();
|
||||
const int insertIdx = std::min(index, (int)macros.size());
|
||||
macros.insert(macros.begin() + insertIdx, macro);
|
||||
macroName = macro->Name();
|
||||
}
|
||||
|
||||
auto *const window = AdvSceneSwitcher::window;
|
||||
if (SettingsWindowIsOpened() && window) {
|
||||
window->ui->macros->Reset(
|
||||
GetTopLevelMacros(),
|
||||
GetGlobalMacroSettings()._highlightExecuted);
|
||||
}
|
||||
|
||||
MacroSignalManager::Instance()->Add(QString::fromStdString(macroName));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Group-delete undo/redo callbacks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Callback data format:
|
||||
// {"group_name": "...", "group_index": N, "group_data": {...},
|
||||
// "children": [{"data": {...}}, ...]}
|
||||
static void deleteGroupWithChildrenFromData(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string groupName = obs_data_get_string(data, "group_name");
|
||||
const auto group = GetWeakMacroByName(groupName.c_str()).lock();
|
||||
if (!group || !group->IsGroup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto children = GetGroupMacroEntries(group.get());
|
||||
|
||||
auto *const window = AdvSceneSwitcher::window;
|
||||
if (SettingsWindowIsOpened() && window) {
|
||||
window->ui->macroEdit->ClearSegmentWidgetCacheFor(group.get());
|
||||
for (const auto &child : children) {
|
||||
window->ui->macroEdit->ClearSegmentWidgetCacheFor(
|
||||
child.get());
|
||||
}
|
||||
MacroSegmentList::SetCachingEnabled(false);
|
||||
window->ui->macros->Remove(group);
|
||||
MacroSegmentList::SetCachingEnabled(true);
|
||||
} else {
|
||||
auto lock = LockContext();
|
||||
auto ¯os = GetTopLevelMacros();
|
||||
const auto it = std::find(macros.begin(), macros.end(), group);
|
||||
if (it != macros.end()) {
|
||||
macros.erase(it, std::next(it, 1 + group->GroupSize()));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &child : children) {
|
||||
MacroSignalManager::Instance()->Remove(
|
||||
QString::fromStdString(child->Name()));
|
||||
}
|
||||
MacroSignalManager::Instance()->Remove(
|
||||
QString::fromStdString(groupName));
|
||||
}
|
||||
|
||||
static void restoreGroupWithChildrenFromData(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int groupIndex = (int)obs_data_get_int(data, "group_index");
|
||||
OBSDataAutoRelease groupData = obs_data_get_obj(data, "group_data");
|
||||
OBSDataArrayAutoRelease childArray =
|
||||
obs_data_get_array(data, "children");
|
||||
|
||||
std::string groupName;
|
||||
bool collapsed = false;
|
||||
if (groupData) {
|
||||
groupName = obs_data_get_string(groupData, "name");
|
||||
OBSDataAutoRelease gd =
|
||||
obs_data_get_obj(groupData, "groupData");
|
||||
if (gd) {
|
||||
collapsed = obs_data_get_bool(gd, "collapsed");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Macro>> children;
|
||||
const size_t count = obs_data_array_count(childArray);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
OBSDataAutoRelease entry = obs_data_array_item(childArray, i);
|
||||
OBSDataAutoRelease childData = obs_data_get_obj(entry, "data");
|
||||
if (!childData) {
|
||||
continue;
|
||||
}
|
||||
auto child = std::make_shared<Macro>();
|
||||
{
|
||||
auto lock = LockContext();
|
||||
child->Load(childData);
|
||||
child->PostLoad();
|
||||
RunAndClearPostLoadSteps();
|
||||
}
|
||||
children.push_back(child);
|
||||
}
|
||||
|
||||
std::string restoredGroupName;
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto group = Macro::CreateGroup(groupName, children);
|
||||
group->SetCollapsed(collapsed);
|
||||
restoredGroupName = group->Name();
|
||||
|
||||
auto ¯os = GetTopLevelMacros();
|
||||
const int insertIdx = std::min(groupIndex, (int)macros.size());
|
||||
macros.insert(macros.begin() + insertIdx, group);
|
||||
int offset = 1;
|
||||
for (const auto &child : children) {
|
||||
macros.insert(macros.begin() + insertIdx + offset,
|
||||
child);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
auto *const window = AdvSceneSwitcher::window;
|
||||
if (SettingsWindowIsOpened() && window) {
|
||||
window->ui->macros->Reset(
|
||||
GetTopLevelMacros(),
|
||||
GetGlobalMacroSettings()._highlightExecuted);
|
||||
}
|
||||
|
||||
MacroSignalManager::Instance()->Add(
|
||||
QString::fromStdString(restoredGroupName));
|
||||
for (const auto &child : children) {
|
||||
MacroSignalManager::Instance()->Add(
|
||||
QString::fromStdString(child->Name()));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Group-level undo/redo callbacks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Callback data format for both group and ungroup:
|
||||
// {"group_name": "...", "group_index": N, "children": [{"name": "..."}, ...]}
|
||||
static void removeGroupFromData(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string groupName = obs_data_get_string(data, "group_name");
|
||||
const auto group = GetWeakMacroByName(groupName.c_str()).lock();
|
||||
if (!group || !group->IsGroup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
Macro::RemoveGroup(group);
|
||||
}
|
||||
|
||||
auto *const window = AdvSceneSwitcher::window;
|
||||
if (SettingsWindowIsOpened() && window) {
|
||||
window->ui->macros->Reset(
|
||||
GetTopLevelMacros(),
|
||||
GetGlobalMacroSettings()._highlightExecuted);
|
||||
}
|
||||
|
||||
MacroSignalManager::Instance()->Remove(
|
||||
QString::fromStdString(groupName));
|
||||
}
|
||||
|
||||
static void restoreGroupFromData(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string groupName = obs_data_get_string(data, "group_name");
|
||||
const int groupIndex = (int)obs_data_get_int(data, "group_index");
|
||||
OBSDataArrayAutoRelease childArray =
|
||||
obs_data_get_array(data, "children");
|
||||
|
||||
std::vector<std::shared_ptr<Macro>> children;
|
||||
const size_t count = obs_data_array_count(childArray);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
OBSDataAutoRelease item = obs_data_array_item(childArray, i);
|
||||
const std::string name = obs_data_get_string(item, "name");
|
||||
const auto macro = GetWeakMacroByName(name.c_str()).lock();
|
||||
if (macro) {
|
||||
children.push_back(macro);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto group = Macro::CreateGroup(groupName, children);
|
||||
|
||||
auto ¯os = GetTopLevelMacros();
|
||||
const int insertIdx = std::min(groupIndex, (int)macros.size());
|
||||
macros.insert(macros.begin() + insertIdx, group);
|
||||
|
||||
int offset = 1;
|
||||
for (const auto &child : children) {
|
||||
const auto it =
|
||||
std::find(macros.begin(), macros.end(), child);
|
||||
if (it != macros.end()) {
|
||||
macros.erase(it);
|
||||
}
|
||||
macros.insert(macros.begin() + insertIdx + offset,
|
||||
child);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
auto *const window = AdvSceneSwitcher::window;
|
||||
if (SettingsWindowIsOpened() && window) {
|
||||
window->ui->macros->Reset(
|
||||
GetTopLevelMacros(),
|
||||
GetGlobalMacroSettings()._highlightExecuted);
|
||||
}
|
||||
|
||||
MacroSignalManager::Instance()->Add(QString::fromStdString(groupName));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Segment-level undo/redo callbacks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Callback data format for segment removal:
|
||||
// {"macro": "Name", "type": N, "index": N}
|
||||
static void removeSegmentFromData(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string macroName = obs_data_get_string(data, "macro");
|
||||
const int type = (int)obs_data_get_int(data, "type");
|
||||
const int index = (int)obs_data_get_int(data, "index");
|
||||
|
||||
const auto macro = GetWeakMacroByName(macroName.c_str()).lock();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
switch ((SegmentType)type) {
|
||||
case SegmentType::ACTION:
|
||||
if (index >= 0 &&
|
||||
index < (int)macro->Actions().size()) {
|
||||
macro->Actions().erase(
|
||||
macro->Actions().begin() + index);
|
||||
SetMacroAbortWait(true);
|
||||
GetMacroWaitCV().notify_all();
|
||||
macro->UpdateActionIndices();
|
||||
}
|
||||
break;
|
||||
case SegmentType::ELSE_ACTION:
|
||||
if (index >= 0 &&
|
||||
index < (int)macro->ElseActions().size()) {
|
||||
macro->ElseActions().erase(
|
||||
macro->ElseActions().begin() + index);
|
||||
SetMacroAbortWait(true);
|
||||
GetMacroWaitCV().notify_all();
|
||||
macro->UpdateElseActionIndices();
|
||||
}
|
||||
break;
|
||||
case SegmentType::CONDITION:
|
||||
if (index >= 0 &&
|
||||
index < (int)macro->Conditions().size()) {
|
||||
macro->Conditions().erase(
|
||||
macro->Conditions().begin() + index);
|
||||
macro->UpdateConditionIndices();
|
||||
if (index == 0 &&
|
||||
!macro->Conditions().empty()) {
|
||||
macro->Conditions().at(0)->SetLogicType(
|
||||
Logic::Type::ROOT_NONE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *window = AdvSceneSwitcher::window) {
|
||||
updateMacroEditSegment(window, macroName, SegmentOp::REMOVE,
|
||||
(SegmentType)type, index);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback data format for segment addition:
|
||||
// {"macro": "Name", "type": N, "index": N, "id": "...", "logic": N,
|
||||
// "segment": {...}}
|
||||
static void addSegmentFromData(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string macroName = obs_data_get_string(data, "macro");
|
||||
const int type = (int)obs_data_get_int(data, "type");
|
||||
const int index = (int)obs_data_get_int(data, "index");
|
||||
const std::string id = obs_data_get_string(data, "id");
|
||||
const int logic = (int)obs_data_get_int(data, "logic");
|
||||
OBSDataAutoRelease segData = obs_data_get_obj(data, "segment");
|
||||
|
||||
const auto macro = GetWeakMacroByName(macroName.c_str()).lock();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
switch ((SegmentType)type) {
|
||||
case SegmentType::ACTION: {
|
||||
if (index < 0 || index > (int)macro->Actions().size()) {
|
||||
break;
|
||||
}
|
||||
macro->Actions().emplace(
|
||||
macro->Actions().begin() + index,
|
||||
MacroActionFactory::Create(id, macro.get()));
|
||||
if (segData) {
|
||||
macro->Actions().at(index)->Load(segData);
|
||||
}
|
||||
macro->Actions().at(index)->PostLoad();
|
||||
RunAndClearPostLoadSteps();
|
||||
macro->UpdateActionIndices();
|
||||
break;
|
||||
}
|
||||
case SegmentType::ELSE_ACTION: {
|
||||
if (index < 0 ||
|
||||
index > (int)macro->ElseActions().size()) {
|
||||
break;
|
||||
}
|
||||
macro->ElseActions().emplace(
|
||||
macro->ElseActions().begin() + index,
|
||||
MacroActionFactory::Create(id, macro.get()));
|
||||
if (segData) {
|
||||
macro->ElseActions().at(index)->Load(segData);
|
||||
}
|
||||
macro->ElseActions().at(index)->PostLoad();
|
||||
RunAndClearPostLoadSteps();
|
||||
macro->UpdateElseActionIndices();
|
||||
break;
|
||||
}
|
||||
case SegmentType::CONDITION: {
|
||||
if (index < 0 ||
|
||||
index > (int)macro->Conditions().size()) {
|
||||
break;
|
||||
}
|
||||
macro->Conditions().emplace(
|
||||
macro->Conditions().begin() + index,
|
||||
MacroConditionFactory::Create(id, macro.get()));
|
||||
if (segData) {
|
||||
macro->Conditions().at(index)->Load(segData);
|
||||
}
|
||||
macro->Conditions().at(index)->PostLoad();
|
||||
RunAndClearPostLoadSteps();
|
||||
macro->Conditions().at(index)->SetLogicType(
|
||||
(Logic::Type)logic);
|
||||
macro->UpdateConditionIndices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *window = AdvSceneSwitcher::window) {
|
||||
updateMacroEditSegment(window, macroName, SegmentOp::ADD,
|
||||
(SegmentType)type, index);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Rename undo/redo callback
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Callback data format: {"old_name": "...", "new_name": "..."}
|
||||
// Used for both undo (new->old) and redo (old->new) by swapping order in
|
||||
// obs_frontend_add_undo_redo_action.
|
||||
static void renameMacroFromData(const char *jsonData)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(jsonData);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string oldName = obs_data_get_string(data, "old_name");
|
||||
const std::string newName = obs_data_get_string(data, "new_name");
|
||||
|
||||
const auto macro = GetWeakMacroByName(oldName.c_str()).lock();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->SetName(newName);
|
||||
}
|
||||
|
||||
MacroSignalManager::Instance()->Rename(QString::fromStdString(oldName),
|
||||
QString::fromStdString(newName));
|
||||
|
||||
auto *const window = AdvSceneSwitcher::window;
|
||||
if (SettingsWindowIsOpened() && window) {
|
||||
window->ui->macros->Reset(
|
||||
GetTopLevelMacros(),
|
||||
GetGlobalMacroSettings()._highlightExecuted);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public registration functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void RegisterMacroAddUndoRedo(const std::string ¯oName)
|
||||
{
|
||||
const auto macro = GetWeakMacroByName(macroName.c_str()).lock();
|
||||
if (!macro || macro->IsGroup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ¯os = GetTopLevelMacros();
|
||||
int index = -1;
|
||||
for (int i = 0; i < (int)macros.size(); i++) {
|
||||
if (macros[i]->Name() == macroName) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string parentGroup;
|
||||
if (macro->Parent()) {
|
||||
parentGroup = macro->Parent()->Name();
|
||||
}
|
||||
|
||||
OBSDataAutoRelease macroData = obs_data_create();
|
||||
macro->Save(macroData);
|
||||
|
||||
OBSDataAutoRelease undoData = obs_data_create();
|
||||
obs_data_set_string(undoData, "name", macroName.c_str());
|
||||
|
||||
OBSDataAutoRelease redoData = obs_data_create();
|
||||
obs_data_set_int(redoData, "index", index);
|
||||
obs_data_set_string(redoData, "parent_group", parentGroup.c_str());
|
||||
obs_data_set_obj(redoData, "macro", macroData);
|
||||
|
||||
const std::string actionName =
|
||||
fmtUndoName("AdvSceneSwitcher.undo.addMacro", macroName);
|
||||
obs_frontend_add_undo_redo_action(actionName.c_str(),
|
||||
&deleteMacroByName,
|
||||
&restoreMacroFromData,
|
||||
obs_data_get_json(undoData),
|
||||
obs_data_get_json(redoData), false);
|
||||
}
|
||||
|
||||
bool RegisterMacroRemoveUndoRedo(Macro *macro)
|
||||
{
|
||||
if (!macro || macro->IsGroup()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string macroName = macro->Name();
|
||||
|
||||
const auto ¯os = GetTopLevelMacros();
|
||||
int index = -1;
|
||||
for (int i = 0; i < (int)macros.size(); i++) {
|
||||
if (macros[i].get() == macro) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string parentGroup;
|
||||
if (macro->Parent()) {
|
||||
parentGroup = macro->Parent()->Name();
|
||||
}
|
||||
|
||||
OBSDataAutoRelease macroData = obs_data_create();
|
||||
macro->Save(macroData);
|
||||
|
||||
OBSDataAutoRelease undoData = obs_data_create();
|
||||
obs_data_set_int(undoData, "index", index);
|
||||
obs_data_set_string(undoData, "parent_group", parentGroup.c_str());
|
||||
obs_data_set_obj(undoData, "macro", macroData);
|
||||
|
||||
OBSDataAutoRelease redoData = obs_data_create();
|
||||
obs_data_set_string(redoData, "name", macroName.c_str());
|
||||
|
||||
const std::string actionName =
|
||||
fmtUndoName("AdvSceneSwitcher.undo.removeMacro", macroName);
|
||||
obs_frontend_add_undo_redo_action(actionName.c_str(),
|
||||
&restoreMacroFromData,
|
||||
&deleteMacroByName,
|
||||
obs_data_get_json(undoData),
|
||||
obs_data_get_json(redoData), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RegisterSegmentAddUndoRedo(Macro *macro, SegmentType type, int index)
|
||||
{
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string macroName = macro->Name();
|
||||
std::string id;
|
||||
int logic = (int)Logic::Type::ROOT_NONE;
|
||||
OBSDataAutoRelease segData = obs_data_create();
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
switch (type) {
|
||||
case SegmentType::ACTION:
|
||||
if (index < (int)macro->Actions().size()) {
|
||||
macro->Actions().at(index)->Save(segData);
|
||||
id = macro->Actions().at(index)->GetId();
|
||||
}
|
||||
break;
|
||||
case SegmentType::ELSE_ACTION:
|
||||
if (index < (int)macro->ElseActions().size()) {
|
||||
macro->ElseActions().at(index)->Save(segData);
|
||||
id = macro->ElseActions().at(index)->GetId();
|
||||
}
|
||||
break;
|
||||
case SegmentType::CONDITION:
|
||||
if (index < (int)macro->Conditions().size()) {
|
||||
macro->Conditions().at(index)->Save(segData);
|
||||
id = macro->Conditions().at(index)->GetId();
|
||||
logic = (int)macro->Conditions()
|
||||
.at(index)
|
||||
->GetLogicType();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OBSDataAutoRelease undoData = obs_data_create();
|
||||
obs_data_set_string(undoData, "macro", macroName.c_str());
|
||||
obs_data_set_int(undoData, "type", (int)type);
|
||||
obs_data_set_int(undoData, "index", index);
|
||||
|
||||
OBSDataAutoRelease redoData = obs_data_create();
|
||||
obs_data_set_string(redoData, "macro", macroName.c_str());
|
||||
obs_data_set_int(redoData, "type", (int)type);
|
||||
obs_data_set_int(redoData, "index", index);
|
||||
obs_data_set_string(redoData, "id", id.c_str());
|
||||
obs_data_set_int(redoData, "logic", logic);
|
||||
obs_data_set_obj(redoData, "segment", segData);
|
||||
|
||||
const std::string actionName =
|
||||
fmtUndoName("AdvSceneSwitcher.undo.addSegment",
|
||||
segmentTypeName(type), macroName);
|
||||
obs_frontend_add_undo_redo_action(actionName.c_str(),
|
||||
&removeSegmentFromData,
|
||||
&addSegmentFromData,
|
||||
obs_data_get_json(undoData),
|
||||
obs_data_get_json(redoData), false);
|
||||
}
|
||||
|
||||
void RegisterSegmentRemoveUndoRedo(Macro *macro, SegmentType type, int index,
|
||||
const std::string &segmentId,
|
||||
obs_data_t *segmentData, int logic)
|
||||
{
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string macroName = macro->Name();
|
||||
|
||||
OBSDataAutoRelease undoData = obs_data_create();
|
||||
obs_data_set_string(undoData, "macro", macroName.c_str());
|
||||
obs_data_set_int(undoData, "type", (int)type);
|
||||
obs_data_set_int(undoData, "index", index);
|
||||
obs_data_set_string(undoData, "id", segmentId.c_str());
|
||||
obs_data_set_int(undoData, "logic", logic);
|
||||
obs_data_set_obj(undoData, "segment", segmentData);
|
||||
|
||||
OBSDataAutoRelease redoData = obs_data_create();
|
||||
obs_data_set_string(redoData, "macro", macroName.c_str());
|
||||
obs_data_set_int(redoData, "type", (int)type);
|
||||
obs_data_set_int(redoData, "index", index);
|
||||
|
||||
const std::string actionName =
|
||||
fmtUndoName("AdvSceneSwitcher.undo.removeSegment",
|
||||
segmentTypeName(type), macroName);
|
||||
obs_frontend_add_undo_redo_action(actionName.c_str(),
|
||||
&addSegmentFromData,
|
||||
&removeSegmentFromData,
|
||||
obs_data_get_json(undoData),
|
||||
obs_data_get_json(redoData), false);
|
||||
}
|
||||
|
||||
static OBSDataArrayAutoRelease
|
||||
buildChildArray(const std::vector<std::shared_ptr<Macro>> &children)
|
||||
{
|
||||
OBSDataArrayAutoRelease arr = obs_data_array_create();
|
||||
for (const auto &child : children) {
|
||||
OBSDataAutoRelease item = obs_data_create();
|
||||
obs_data_set_string(item, "name", child->Name().c_str());
|
||||
obs_data_array_push_back(arr, item);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
void RegisterGroupCreateUndoRedo(const std::string &groupName)
|
||||
{
|
||||
const auto group = GetWeakMacroByName(groupName.c_str()).lock();
|
||||
if (!group || !group->IsGroup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ¯os = GetTopLevelMacros();
|
||||
int groupIndex = -1;
|
||||
for (int i = 0; i < (int)macros.size(); i++) {
|
||||
if (macros[i].get() == group.get()) {
|
||||
groupIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (groupIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto children = GetGroupMacroEntries(group.get());
|
||||
OBSDataArrayAutoRelease childArray = buildChildArray(children);
|
||||
|
||||
OBSDataAutoRelease undoData = obs_data_create();
|
||||
obs_data_set_string(undoData, "group_name", groupName.c_str());
|
||||
obs_data_set_int(undoData, "group_index", groupIndex);
|
||||
obs_data_set_array(undoData, "children", childArray);
|
||||
|
||||
OBSDataAutoRelease redoData = obs_data_create();
|
||||
obs_data_set_string(redoData, "group_name", groupName.c_str());
|
||||
obs_data_set_int(redoData, "group_index", groupIndex);
|
||||
obs_data_set_array(redoData, "children", childArray);
|
||||
|
||||
const std::string actionName =
|
||||
fmtUndoName("AdvSceneSwitcher.undo.groupMacros", groupName);
|
||||
obs_frontend_add_undo_redo_action(actionName.c_str(),
|
||||
&removeGroupFromData,
|
||||
&restoreGroupFromData,
|
||||
obs_data_get_json(undoData),
|
||||
obs_data_get_json(redoData), false);
|
||||
}
|
||||
|
||||
void RegisterGroupRemoveUndoRedo(const std::string &groupName)
|
||||
{
|
||||
const auto group = GetWeakMacroByName(groupName.c_str()).lock();
|
||||
if (!group || !group->IsGroup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ¯os = GetTopLevelMacros();
|
||||
int groupIndex = -1;
|
||||
for (int i = 0; i < (int)macros.size(); i++) {
|
||||
if (macros[i].get() == group.get()) {
|
||||
groupIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (groupIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto children = GetGroupMacroEntries(group.get());
|
||||
OBSDataArrayAutoRelease childArray = buildChildArray(children);
|
||||
|
||||
OBSDataAutoRelease undoData = obs_data_create();
|
||||
obs_data_set_string(undoData, "group_name", groupName.c_str());
|
||||
obs_data_set_int(undoData, "group_index", groupIndex);
|
||||
obs_data_set_array(undoData, "children", childArray);
|
||||
|
||||
OBSDataAutoRelease redoData = obs_data_create();
|
||||
obs_data_set_string(redoData, "group_name", groupName.c_str());
|
||||
obs_data_set_int(redoData, "group_index", groupIndex);
|
||||
obs_data_set_array(redoData, "children", childArray);
|
||||
|
||||
const std::string actionName =
|
||||
fmtUndoName("AdvSceneSwitcher.undo.ungroupMacros", groupName);
|
||||
obs_frontend_add_undo_redo_action(actionName.c_str(),
|
||||
&restoreGroupFromData,
|
||||
&removeGroupFromData,
|
||||
obs_data_get_json(undoData),
|
||||
obs_data_get_json(redoData), false);
|
||||
}
|
||||
|
||||
void RegisterGroupDeleteUndoRedo(Macro *macro)
|
||||
{
|
||||
if (!macro || !macro->IsGroup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string groupName = macro->Name();
|
||||
|
||||
const auto ¯os = GetTopLevelMacros();
|
||||
int groupIndex = -1;
|
||||
for (int i = 0; i < (int)macros.size(); i++) {
|
||||
if (macros[i].get() == macro) {
|
||||
groupIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (groupIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
OBSDataAutoRelease groupData = obs_data_create();
|
||||
macro->Save(groupData);
|
||||
|
||||
const auto children = GetGroupMacroEntries(macro);
|
||||
OBSDataArrayAutoRelease childArray = obs_data_array_create();
|
||||
for (const auto &child : children) {
|
||||
OBSDataAutoRelease childData = obs_data_create();
|
||||
child->Save(childData);
|
||||
OBSDataAutoRelease entry = obs_data_create();
|
||||
obs_data_set_obj(entry, "data", childData);
|
||||
obs_data_array_push_back(childArray, entry);
|
||||
}
|
||||
|
||||
OBSDataAutoRelease undoData = obs_data_create();
|
||||
obs_data_set_string(undoData, "group_name", groupName.c_str());
|
||||
obs_data_set_int(undoData, "group_index", groupIndex);
|
||||
obs_data_set_obj(undoData, "group_data", groupData);
|
||||
obs_data_set_array(undoData, "children", childArray);
|
||||
|
||||
OBSDataAutoRelease redoData = obs_data_create();
|
||||
obs_data_set_string(redoData, "group_name", groupName.c_str());
|
||||
obs_data_set_int(redoData, "group_index", groupIndex);
|
||||
obs_data_set_obj(redoData, "group_data", groupData);
|
||||
obs_data_set_array(redoData, "children", childArray);
|
||||
|
||||
const std::string actionName = fmtUndoName(
|
||||
"AdvSceneSwitcher.undo.removeMacroGroup", groupName);
|
||||
obs_frontend_add_undo_redo_action(actionName.c_str(),
|
||||
&restoreGroupWithChildrenFromData,
|
||||
&deleteGroupWithChildrenFromData,
|
||||
obs_data_get_json(undoData),
|
||||
obs_data_get_json(redoData), false);
|
||||
}
|
||||
|
||||
void RegisterMacroRenameUndoRedo(const std::string &oldName,
|
||||
const std::string &newName)
|
||||
{
|
||||
OBSDataAutoRelease undoData = obs_data_create();
|
||||
obs_data_set_string(undoData, "old_name", newName.c_str());
|
||||
obs_data_set_string(undoData, "new_name", oldName.c_str());
|
||||
|
||||
OBSDataAutoRelease redoData = obs_data_create();
|
||||
obs_data_set_string(redoData, "old_name", oldName.c_str());
|
||||
obs_data_set_string(redoData, "new_name", newName.c_str());
|
||||
|
||||
const std::string actionName =
|
||||
fmtUndoName("AdvSceneSwitcher.undo.renameMacro", oldName);
|
||||
obs_frontend_add_undo_redo_action(actionName.c_str(),
|
||||
&renameMacroFromData,
|
||||
&renameMacroFromData,
|
||||
obs_data_get_json(undoData),
|
||||
obs_data_get_json(redoData), false);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
24
lib/macro/macro-undo-redo.hpp
Normal file
24
lib/macro/macro-undo-redo.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "macro-edit.hpp"
|
||||
|
||||
#include <obs-data.h>
|
||||
#include <string>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class Macro;
|
||||
|
||||
void RegisterMacroAddUndoRedo(const std::string ¯oName);
|
||||
bool RegisterMacroRemoveUndoRedo(Macro *macro);
|
||||
void RegisterSegmentAddUndoRedo(Macro *macro, MacroEdit::SegmentType type,
|
||||
int index);
|
||||
void RegisterSegmentRemoveUndoRedo(Macro *macro, MacroEdit::SegmentType type,
|
||||
int index, const std::string &segmentId,
|
||||
obs_data_t *segmentData, int logic);
|
||||
void RegisterMacroRenameUndoRedo(const std::string &oldName,
|
||||
const std::string &newName);
|
||||
void RegisterGroupCreateUndoRedo(const std::string &groupName);
|
||||
void RegisterGroupRemoveUndoRedo(const std::string &groupName);
|
||||
void RegisterGroupDeleteUndoRedo(Macro *macro);
|
||||
|
||||
} // namespace advss
|
||||
@@ -110,7 +110,9 @@ void MacroEdit::HighlightControls() const {}
|
||||
void MacroEdit::PopulateMacroActions(Macro &, uint32_t) {}
|
||||
void MacroEdit::PopulateMacroElseActions(Macro &, uint32_t) {}
|
||||
void MacroEdit::PopulateMacroConditions(Macro &, uint32_t) {}
|
||||
void MacroEdit::SetupMacroSegmentSelection(MacroSection, int) {}
|
||||
void MacroEdit::InsertSegmentWidget(SegmentType, int) {}
|
||||
void MacroEdit::RemoveSegmentWidget(SegmentType, int) {}
|
||||
void MacroEdit::SetupMacroSegmentSelection(SegmentType, int) {}
|
||||
void MacroEdit::SetupContextMenu(const QPoint &,
|
||||
const std::function<void(MacroEdit *, int)> &,
|
||||
const std::function<void(MacroEdit *)> &,
|
||||
|
||||
Reference in New Issue
Block a user