mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 09:05:57 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cc44b4470 | ||
|
|
54356d9410 | ||
|
|
defbdf8b7a | ||
|
|
a01af6cfc3 | ||
|
|
4d9c7f1054 | ||
|
|
ced36e2b5b | ||
|
|
d2749f29b0 | ||
|
|
b78c8bc1de | ||
|
|
cc4a3560d8 | ||
|
|
3ab2ea9e66 |
@@ -683,6 +683,17 @@ AdvSceneSwitcher.condition.clipboard.condition.isImage="Clipboard contains an im
|
|||||||
AdvSceneSwitcher.condition.clipboard.condition.isURL="Clipboard contains an URL"
|
AdvSceneSwitcher.condition.clipboard.condition.isURL="Clipboard contains an URL"
|
||||||
AdvSceneSwitcher.condition.clipboard.condition.matches="Clipboard content matches"
|
AdvSceneSwitcher.condition.clipboard.condition.matches="Clipboard content matches"
|
||||||
AdvSceneSwitcher.condition.clipboard.condition.entry="{{conditions}}{{regex}}{{urlInfo}}"
|
AdvSceneSwitcher.condition.clipboard.condition.entry="{{conditions}}{{regex}}{{urlInfo}}"
|
||||||
|
AdvSceneSwitcher.condition.folder="Folder watch"
|
||||||
|
AdvSceneSwitcher.condition.folder.tooltip="This condition type will allow you to monitor the contents of a folder.\nNote that the monitoring will *not* recursively scan for changes in sub directories within directories of the selected folder!\nNote that if there are several changes during a short period of time, some of the changes might not emit this signal.\nHowever, the last change in the sequence of changes always will."
|
||||||
|
AdvSceneSwitcher.condition.folder.condition.any="Any change happened"
|
||||||
|
AdvSceneSwitcher.condition.folder.condition.fileAdd="A file was added"
|
||||||
|
AdvSceneSwitcher.condition.folder.condition.fileChange="A file was modified"
|
||||||
|
AdvSceneSwitcher.condition.folder.condition.fileRemove="A file was removed"
|
||||||
|
AdvSceneSwitcher.condition.folder.condition.folderAdd="A directory was added"
|
||||||
|
AdvSceneSwitcher.condition.folder.condition.folderRemove="A directory was removed"
|
||||||
|
AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}"
|
||||||
|
AdvSceneSwitcher.condition.folder.enableFilter="Only evaluate to true, if the changed path matches a patern"
|
||||||
|
AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
|
||||||
|
|
||||||
; Macro Actions
|
; Macro Actions
|
||||||
AdvSceneSwitcher.action.scene="Switch scene"
|
AdvSceneSwitcher.action.scene="Switch scene"
|
||||||
@@ -972,6 +983,7 @@ AdvSceneSwitcher.action.variable.type.sceneItemCount="Set to scene item count of
|
|||||||
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
|
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
|
||||||
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
|
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
|
||||||
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
|
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
|
||||||
|
AdvSceneSwitcher.action.variable.type.setToTempvar.help="This action type will allow you to extract values out of segments of the current macro and assign those values to the selected variable.\nFor example, you can get the current scene name from a scene condition and assign this name to a variable."
|
||||||
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
|
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
|
||||||
AdvSceneSwitcher.action.variable.type.padValue="Pad current value"
|
AdvSceneSwitcher.action.variable.type.padValue="Pad current value"
|
||||||
AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
|
AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
|
||||||
@@ -988,7 +1000,7 @@ AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
|
|||||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
|
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
|
||||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
|
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
|
||||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
|
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
|
||||||
AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
|
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
|
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
|
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
|
||||||
@@ -1054,6 +1066,8 @@ AdvSceneSwitcher.action.queue.entry.add="{{actions}}actions of macro{{macros}}to
|
|||||||
AdvSceneSwitcher.action.queue.entry.other="{{actions}}queue{{queues}}"
|
AdvSceneSwitcher.action.queue.entry.other="{{actions}}queue{{queues}}"
|
||||||
AdvSceneSwitcher.action.window="Window"
|
AdvSceneSwitcher.action.window="Window"
|
||||||
AdvSceneSwitcher.action.window.type.setFocusWindow="Focus window"
|
AdvSceneSwitcher.action.window.type.setFocusWindow="Focus window"
|
||||||
|
AdvSceneSwitcher.action.window.type.setFocusWindow.limitation="The focus action can only be performed under certain circumstances"
|
||||||
|
AdvSceneSwitcher.action.window.type.setFocusWindow.limitation.details="At least one of the following conditions must be true:\n OBS is the foreground process.\n There is currently no foreground window, and thus no foreground process.\n OBS received the last input event."
|
||||||
AdvSceneSwitcher.action.window.type.maximizeWindow="Maximize window"
|
AdvSceneSwitcher.action.window.type.maximizeWindow="Maximize window"
|
||||||
AdvSceneSwitcher.action.window.type.minimizeWindow="Minimize window"
|
AdvSceneSwitcher.action.window.type.minimizeWindow="Minimize window"
|
||||||
AdvSceneSwitcher.action.window.type.closeWindow="Close window"
|
AdvSceneSwitcher.action.window.type.closeWindow="Close window"
|
||||||
@@ -1732,6 +1746,12 @@ AdvSceneSwitcher.tempVar.clipboard.text.description="The text contained in the c
|
|||||||
AdvSceneSwitcher.tempVar.file.content="File content"
|
AdvSceneSwitcher.tempVar.file.content="File content"
|
||||||
AdvSceneSwitcher.tempVar.file.date="File modification date"
|
AdvSceneSwitcher.tempVar.file.date="File modification date"
|
||||||
|
|
||||||
|
AdvSceneSwitcher.tempVar.folder.newFiles="New files"
|
||||||
|
AdvSceneSwitcher.tempVar.folder.changedFiles="Changed files"
|
||||||
|
AdvSceneSwitcher.tempVar.folder.removedFiles="Removed files"
|
||||||
|
AdvSceneSwitcher.tempVar.folder.newDirs="New directories"
|
||||||
|
AdvSceneSwitcher.tempVar.folder.removedDirs="Removed directories"
|
||||||
|
|
||||||
AdvSceneSwitcher.selectScene="--select scene--"
|
AdvSceneSwitcher.selectScene="--select scene--"
|
||||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
#include "non-modal-dialog.hpp"
|
#include "non-modal-dialog.hpp"
|
||||||
#include "source-helpers.hpp"
|
#include "source-helpers.hpp"
|
||||||
|
#include "ui-helpers.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
@@ -630,6 +631,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
_scenes(new SceneSelectionWidget(this, true, false, true, true,
|
_scenes(new SceneSelectionWidget(this, true, false, true, true,
|
||||||
true)),
|
true)),
|
||||||
_tempVars(new TempVariableSelection(this)),
|
_tempVars(new TempVariableSelection(this)),
|
||||||
|
_tempVarsHelp(new QLabel()),
|
||||||
_sceneItemIndex(new VariableSpinBox()),
|
_sceneItemIndex(new VariableSpinBox()),
|
||||||
_direction(new QComboBox()),
|
_direction(new QComboBox()),
|
||||||
_stringLength(new VariableSpinBox()),
|
_stringLength(new VariableSpinBox()),
|
||||||
@@ -657,6 +659,15 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
populateTypeSelection(_actions);
|
populateTypeSelection(_actions);
|
||||||
populateDirectionSelection(_direction);
|
populateDirectionSelection(_direction);
|
||||||
|
|
||||||
|
QString path = GetThemeTypeName() == "Light"
|
||||||
|
? ":/res/images/help.svg"
|
||||||
|
: ":/res/images/help_light.svg";
|
||||||
|
QIcon icon(path);
|
||||||
|
QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||||
|
_tempVarsHelp->setPixmap(pixmap);
|
||||||
|
_tempVarsHelp->setToolTip(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.variable.type.setToTempvar.help"));
|
||||||
|
|
||||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||||
this, SLOT(VariableChanged(const QString &)));
|
this, SLOT(VariableChanged(const QString &)));
|
||||||
QWidget::connect(_variables2, SIGNAL(SelectionChanged(const QString &)),
|
QWidget::connect(_variables2, SIGNAL(SelectionChanged(const QString &)),
|
||||||
@@ -742,6 +753,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
{"{{envVariableName}}", _envVariable},
|
{"{{envVariableName}}", _envVariable},
|
||||||
{"{{scenes}}", _scenes},
|
{"{{scenes}}", _scenes},
|
||||||
{"{{tempVars}}", _tempVars},
|
{"{{tempVars}}", _tempVars},
|
||||||
|
{"{{tempVarsHelp}}", _tempVarsHelp},
|
||||||
{"{{sceneItemIndex}}", _sceneItemIndex},
|
{"{{sceneItemIndex}}", _sceneItemIndex},
|
||||||
{"{{direction}}", _direction},
|
{"{{direction}}", _direction},
|
||||||
{"{{stringLength}}", _stringLength},
|
{"{{stringLength}}", _stringLength},
|
||||||
@@ -1202,6 +1214,7 @@ void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
|
|||||||
|
|
||||||
auto lock = LockContext();
|
auto lock = LockContext();
|
||||||
_entryData->_tempVar = var;
|
_entryData->_tempVar = var;
|
||||||
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::SceneItemIndexChanged(
|
void MacroActionVariableEdit::SceneItemIndexChanged(
|
||||||
@@ -1268,6 +1281,7 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
{"{{envVariableName}}", _envVariable},
|
{"{{envVariableName}}", _envVariable},
|
||||||
{"{{scenes}}", _scenes},
|
{"{{scenes}}", _scenes},
|
||||||
{"{{tempVars}}", _tempVars},
|
{"{{tempVars}}", _tempVars},
|
||||||
|
{"{{tempVarsHelp}}", _tempVarsHelp},
|
||||||
{"{{sceneItemIndex}}", _sceneItemIndex},
|
{"{{sceneItemIndex}}", _sceneItemIndex},
|
||||||
{"{{direction}}", _direction},
|
{"{{direction}}", _direction},
|
||||||
{"{{stringLength}}", _stringLength},
|
{"{{stringLength}}", _stringLength},
|
||||||
@@ -1381,6 +1395,10 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||||
_tempVars->setVisible(_entryData->_type ==
|
_tempVars->setVisible(_entryData->_type ==
|
||||||
MacroActionVariable::Type::SET_TO_TEMPVAR);
|
MacroActionVariable::Type::SET_TO_TEMPVAR);
|
||||||
|
_tempVarsHelp->setVisible(
|
||||||
|
_entryData->_type ==
|
||||||
|
MacroActionVariable::Type::SET_TO_TEMPVAR &&
|
||||||
|
!_entryData->_tempVar.HasValidID());
|
||||||
_sceneItemIndex->setVisible(_entryData->_type ==
|
_sceneItemIndex->setVisible(_entryData->_type ==
|
||||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||||
_direction->setVisible(
|
_direction->setVisible(
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ private:
|
|||||||
VariableLineEdit *_envVariable;
|
VariableLineEdit *_envVariable;
|
||||||
SceneSelectionWidget *_scenes;
|
SceneSelectionWidget *_scenes;
|
||||||
TempVariableSelection *_tempVars;
|
TempVariableSelection *_tempVars;
|
||||||
|
QLabel *_tempVarsHelp;
|
||||||
VariableSpinBox *_sceneItemIndex;
|
VariableSpinBox *_sceneItemIndex;
|
||||||
QComboBox *_direction;
|
QComboBox *_direction;
|
||||||
VariableSpinBox *_stringLength;
|
VariableSpinBox *_stringLength;
|
||||||
|
|||||||
@@ -11,19 +11,16 @@
|
|||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
static bool registerTab();
|
|
||||||
static void setupTab(QTabWidget *);
|
static void setupTab(QTabWidget *);
|
||||||
static bool registerTabDone = registerTab();
|
|
||||||
|
|
||||||
static ActionQueueTable *tabWidget = nullptr;
|
static ActionQueueTable *tabWidget = nullptr;
|
||||||
|
|
||||||
static bool registerTab()
|
void RegisterActionQueueTab()
|
||||||
{
|
{
|
||||||
AddPluginInitStep([]() {
|
AddPluginInitStep([]() {
|
||||||
AddSetupTabCallback("actionQueueTab", ActionQueueTable::Create,
|
AddSetupTabCallback("actionQueueTab", ActionQueueTable::Create,
|
||||||
setupTab);
|
setupTab);
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
||||||
@@ -70,11 +67,11 @@ static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
|
|||||||
<< QString::fromStdString(obs_module_text(
|
<< QString::fromStdString(obs_module_text(
|
||||||
queue->IsRunning()
|
queue->IsRunning()
|
||||||
? "AdvSceneSwitcher.actionQueueTab.yes"
|
? "AdvSceneSwitcher.actionQueueTab.yes"
|
||||||
: "AdvSceneSwitcher.actionQueueTab.yes"))
|
: "AdvSceneSwitcher.actionQueueTab.no"))
|
||||||
<< QString::fromStdString(obs_module_text(
|
<< QString::fromStdString(obs_module_text(
|
||||||
queue->RunsOnStartup()
|
queue->RunsOnStartup()
|
||||||
? "AdvSceneSwitcher.actionQueueTab.yes"
|
? "AdvSceneSwitcher.actionQueueTab.yes"
|
||||||
: "AdvSceneSwitcher.actionQueueTab.yes"));
|
: "AdvSceneSwitcher.actionQueueTab.no"));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ std::deque<std::shared_ptr<Item>> &GetActionQueues()
|
|||||||
return queues;
|
return queues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RegisterActionQueueTab();
|
||||||
|
|
||||||
void SetupActionQueues()
|
void SetupActionQueues()
|
||||||
{
|
{
|
||||||
static bool done = false;
|
static bool done = false;
|
||||||
@@ -19,6 +21,7 @@ void SetupActionQueues()
|
|||||||
}
|
}
|
||||||
AddSaveStep(SaveActionQueues);
|
AddSaveStep(SaveActionQueues);
|
||||||
AddLoadStep(LoadActionQueues);
|
AddLoadStep(LoadActionQueues);
|
||||||
|
RegisterActionQueueTab();
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
EXPORT std::mutex *GetMutex();
|
[[nodiscard]] EXPORT std::mutex *GetMutex();
|
||||||
EXPORT std::lock_guard<std::mutex> LockContext();
|
[[nodiscard]] EXPORT std::lock_guard<std::mutex> LockContext();
|
||||||
EXPORT std::unique_lock<std::mutex> *GetLoopLock();
|
[[nodiscard]] EXPORT std::unique_lock<std::mutex> *GetLoopLock();
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public:
|
|||||||
EXPORT void Load(obs_data_t *, Macro *, const char *name = "tempVar");
|
EXPORT void Load(obs_data_t *, Macro *, const char *name = "tempVar");
|
||||||
EXPORT std::optional<const TempVariable> GetTempVariable(Macro *) const;
|
EXPORT std::optional<const TempVariable> GetTempVariable(Macro *) const;
|
||||||
EXPORT bool operator==(const TempVariableRef &other) const;
|
EXPORT bool operator==(const TempVariableRef &other) const;
|
||||||
|
bool HasValidID() const { return !_id.empty(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class SegmentType { NONE, CONDITION, ACTION, ELSEACTION };
|
enum class SegmentType { NONE, CONDITION, ACTION, ELSEACTION };
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ target_sources(
|
|||||||
macro-condition-file.hpp
|
macro-condition-file.hpp
|
||||||
macro-condition-filter.cpp
|
macro-condition-filter.cpp
|
||||||
macro-condition-filter.hpp
|
macro-condition-filter.hpp
|
||||||
|
macro-condition-folder.cpp
|
||||||
|
macro-condition-folder.hpp
|
||||||
macro-condition-hotkey.cpp
|
macro-condition-hotkey.cpp
|
||||||
macro-condition-hotkey.hpp
|
macro-condition-hotkey.hpp
|
||||||
macro-condition-idle.cpp
|
macro-condition-idle.cpp
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "layout-helpers.hpp"
|
#include "layout-helpers.hpp"
|
||||||
#include "platform-funcs.hpp"
|
#include "platform-funcs.hpp"
|
||||||
#include "selection-helpers.hpp"
|
#include "selection-helpers.hpp"
|
||||||
|
#include "ui-helpers.hpp"
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
@@ -163,7 +164,8 @@ MacroActionWindowEdit::MacroActionWindowEdit(
|
|||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
_actions(new QComboBox()),
|
_actions(new QComboBox()),
|
||||||
_windows(new QComboBox()),
|
_windows(new QComboBox()),
|
||||||
_regex(new RegexConfigWidget(this))
|
_regex(new RegexConfigWidget(this)),
|
||||||
|
_infoLayout(new QHBoxLayout())
|
||||||
{
|
{
|
||||||
populateActionSelection(_actions);
|
populateActionSelection(_actions);
|
||||||
|
|
||||||
@@ -171,6 +173,22 @@ MacroActionWindowEdit::MacroActionWindowEdit(
|
|||||||
_windows->setMaxVisibleItems(20);
|
_windows->setMaxVisibleItems(20);
|
||||||
PopulateWindowSelection(_windows);
|
PopulateWindowSelection(_windows);
|
||||||
|
|
||||||
|
auto focusLimitation = new QLabel(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.window.type.setFocusWindow.limitation"));
|
||||||
|
_infoLayout->addWidget(focusLimitation);
|
||||||
|
|
||||||
|
const QString path = GetThemeTypeName() == "Light"
|
||||||
|
? ":/res/images/help.svg"
|
||||||
|
: ":/res/images/help_light.svg";
|
||||||
|
const QIcon icon(path);
|
||||||
|
const QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||||
|
auto focusLimitationDetails = new QLabel();
|
||||||
|
focusLimitationDetails->setPixmap(pixmap);
|
||||||
|
focusLimitationDetails->setToolTip(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.window.type.setFocusWindow.limitation.details"));
|
||||||
|
_infoLayout->addWidget(focusLimitationDetails);
|
||||||
|
_infoLayout->addStretch();
|
||||||
|
|
||||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(ActionChanged(int)));
|
SLOT(ActionChanged(int)));
|
||||||
QWidget::connect(_windows, SIGNAL(currentTextChanged(const QString &)),
|
QWidget::connect(_windows, SIGNAL(currentTextChanged(const QString &)),
|
||||||
@@ -179,12 +197,15 @@ MacroActionWindowEdit::MacroActionWindowEdit(
|
|||||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||||
SLOT(RegexChanged(const RegexConfig &)));
|
SLOT(RegexChanged(const RegexConfig &)));
|
||||||
|
|
||||||
auto layout = new QHBoxLayout;
|
auto entryLayout = new QHBoxLayout;
|
||||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.window.entry"),
|
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.window.entry"),
|
||||||
layout,
|
entryLayout,
|
||||||
{{"{{actions}}", _actions},
|
{{"{{actions}}", _actions},
|
||||||
{"{{windows}}", _windows},
|
{"{{windows}}", _windows},
|
||||||
{"{{regex}}", _regex}});
|
{"{{regex}}", _regex}});
|
||||||
|
auto layout = new QVBoxLayout();
|
||||||
|
layout->addLayout(entryLayout);
|
||||||
|
layout->addLayout(_infoLayout);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
@@ -200,8 +221,7 @@ void MacroActionWindowEdit::UpdateEntryData()
|
|||||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||||
_windows->setCurrentText(QString::fromStdString(_entryData->_window));
|
_windows->setCurrentText(QString::fromStdString(_entryData->_window));
|
||||||
_regex->SetRegexConfig(_entryData->_regex);
|
_regex->SetRegexConfig(_entryData->_regex);
|
||||||
adjustSize();
|
SetWidgetVisibility();
|
||||||
updateGeometry();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionWindowEdit::WindowChanged(const QString &text)
|
void MacroActionWindowEdit::WindowChanged(const QString &text)
|
||||||
@@ -228,6 +248,15 @@ void MacroActionWindowEdit::RegexChanged(const RegexConfig ®ex)
|
|||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionWindowEdit::SetWidgetVisibility()
|
||||||
|
{
|
||||||
|
SetLayoutVisible(_infoLayout,
|
||||||
|
_entryData->_action ==
|
||||||
|
MacroActionWindow::Action::SET_FOCUS_WINDOW);
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
void MacroActionWindowEdit::ActionChanged(int value)
|
void MacroActionWindowEdit::ActionChanged(int value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -236,6 +265,7 @@ void MacroActionWindowEdit::ActionChanged(int value)
|
|||||||
|
|
||||||
auto lock = LockContext();
|
auto lock = LockContext();
|
||||||
_entryData->_action = static_cast<MacroActionWindow::Action>(value);
|
_entryData->_action = static_cast<MacroActionWindow::Action>(value);
|
||||||
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -59,9 +59,12 @@ signals:
|
|||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
QComboBox *_actions;
|
QComboBox *_actions;
|
||||||
QComboBox *_windows;
|
QComboBox *_windows;
|
||||||
RegexConfigWidget *_regex;
|
RegexConfigWidget *_regex;
|
||||||
|
QHBoxLayout *_infoLayout;
|
||||||
|
|
||||||
std::shared_ptr<MacroActionWindow> _entryData;
|
std::shared_ptr<MacroActionWindow> _entryData;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
|
|||||||
@@ -61,26 +61,48 @@ MacroConditionAudio::~MacroConditionAudio()
|
|||||||
obs_volmeter_destroy(_volmeter);
|
obs_volmeter_destroy(_volmeter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float MacroConditionAudio::GetVolumePeak()
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
using namespace std::chrono;
|
||||||
|
static constexpr std::chrono::milliseconds timeout = 250ms;
|
||||||
|
|
||||||
|
// OBS might rarely not update _peak quickly enough when very low
|
||||||
|
// intervals are configured on the General tab.
|
||||||
|
// In that case _peak might be set to negative infinity still, which
|
||||||
|
// will result in unexpected behavior, so we use the previously valid
|
||||||
|
// peak value instead.
|
||||||
|
//
|
||||||
|
// If no volume update was received within a timeout window, however, it
|
||||||
|
// is assumed, that the source no longer produces any audio output and
|
||||||
|
// thus a peak volume value of negative infinity is used.
|
||||||
|
|
||||||
|
float peak;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(_peakMutex);
|
||||||
|
auto msPassedSinceLastUpdate = duration_cast<milliseconds>(
|
||||||
|
high_resolution_clock::now() - _lastPeakUpdate);
|
||||||
|
if (_lastPeakUpdate.time_since_epoch().count() != 0 &&
|
||||||
|
msPassedSinceLastUpdate > timeout) {
|
||||||
|
peak = -std::numeric_limits<float>::infinity();
|
||||||
|
} else {
|
||||||
|
peak = _peakUpdated ? _peak : _previousPeak;
|
||||||
|
}
|
||||||
|
|
||||||
|
_previousPeak = peak;
|
||||||
|
_peak = -std::numeric_limits<float>::infinity();
|
||||||
|
_peakUpdated = false;
|
||||||
|
|
||||||
|
return peak;
|
||||||
|
}
|
||||||
|
|
||||||
bool MacroConditionAudio::CheckOutputCondition()
|
bool MacroConditionAudio::CheckOutputCondition()
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
OBSSourceAutoRelease source =
|
OBSSourceAutoRelease source =
|
||||||
obs_weak_source_get_source(_audioSource.GetSource());
|
obs_weak_source_get_source(_audioSource.GetSource());
|
||||||
|
|
||||||
// OBS might very rarely not update _peak quickly enough when very low
|
float peak = GetVolumePeak();
|
||||||
// intervals are configured on the General tab.
|
|
||||||
// In that case _peak might be set to negative infinity still, which
|
|
||||||
// will result in unexpected behavior, so we use the previously valid
|
|
||||||
// peak value instead.
|
|
||||||
float peak;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(_peakMutex);
|
|
||||||
peak = _peakUpdated ? _peak : _previousPeak;
|
|
||||||
_previousPeak = peak;
|
|
||||||
_peak = -std::numeric_limits<float>::infinity();
|
|
||||||
_peakUpdated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
double curVolume = _useDb ? peak : DecibelToPercent(peak) * 100;
|
double curVolume = _useDb ? peak : DecibelToPercent(peak) * 100;
|
||||||
|
|
||||||
switch (_outputCondition) {
|
switch (_outputCondition) {
|
||||||
@@ -349,6 +371,7 @@ void MacroConditionAudio::SetVolumeLevel(void *data, const float *,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c->_peakUpdated = true;
|
c->_peakUpdated = true;
|
||||||
|
c->_lastPeakUpdate = std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionAudio::ResetVolmeter()
|
void MacroConditionAudio::ResetVolmeter()
|
||||||
|
|||||||
@@ -71,12 +71,14 @@ private:
|
|||||||
bool CheckMonitor();
|
bool CheckMonitor();
|
||||||
bool CheckBalance();
|
bool CheckBalance();
|
||||||
void SetupTempVars();
|
void SetupTempVars();
|
||||||
|
float GetVolumePeak();
|
||||||
|
|
||||||
Type _checkType = Type::OUTPUT_VOLUME;
|
Type _checkType = Type::OUTPUT_VOLUME;
|
||||||
std::mutex _peakMutex;
|
std::mutex _peakMutex;
|
||||||
float _peak = -std::numeric_limits<float>::infinity();
|
float _peak = -std::numeric_limits<float>::infinity();
|
||||||
float _previousPeak = -std::numeric_limits<float>::infinity();
|
float _previousPeak = -std::numeric_limits<float>::infinity();
|
||||||
bool _peakUpdated = false;
|
bool _peakUpdated = false;
|
||||||
|
std::chrono::high_resolution_clock::time_point _lastPeakUpdate = {};
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
static const std::string id;
|
static const std::string id;
|
||||||
};
|
};
|
||||||
|
|||||||
414
plugins/base/macro-condition-folder.cpp
Normal file
414
plugins/base/macro-condition-folder.cpp
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
#include "macro-condition-folder.hpp"
|
||||||
|
#include "macro-helpers.hpp"
|
||||||
|
#include "layout-helpers.hpp"
|
||||||
|
#include "ui-helpers.hpp"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
const std::string MacroConditionFolder::id = "folder";
|
||||||
|
|
||||||
|
bool MacroConditionFolder::_registered = MacroConditionFactory::Register(
|
||||||
|
MacroConditionFolder::id,
|
||||||
|
{MacroConditionFolder::Create, MacroConditionFolderEdit::Create,
|
||||||
|
"AdvSceneSwitcher.condition.folder"});
|
||||||
|
|
||||||
|
static const std::map<MacroConditionFolder::Condition, std::string> conditions =
|
||||||
|
{
|
||||||
|
{MacroConditionFolder::Condition::ANY,
|
||||||
|
"AdvSceneSwitcher.condition.folder.condition.any"},
|
||||||
|
{MacroConditionFolder::Condition::FILE_ADD,
|
||||||
|
"AdvSceneSwitcher.condition.folder.condition.fileAdd"},
|
||||||
|
{MacroConditionFolder::Condition::FILE_CHANGE,
|
||||||
|
"AdvSceneSwitcher.condition.folder.condition.fileChange"},
|
||||||
|
{MacroConditionFolder::Condition::FILE_REMOVE,
|
||||||
|
"AdvSceneSwitcher.condition.folder.condition.fileRemove"},
|
||||||
|
{MacroConditionFolder::Condition::FOLDER_ADD,
|
||||||
|
"AdvSceneSwitcher.condition.folder.condition.folderAdd"},
|
||||||
|
{MacroConditionFolder::Condition::FOLDER_REMOVE,
|
||||||
|
"AdvSceneSwitcher.condition.folder.condition.folderRemove"},
|
||||||
|
};
|
||||||
|
|
||||||
|
MacroConditionFolder::MacroConditionFolder(Macro *m) : MacroCondition(m, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionFolder::CheckCondition()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
bool ret = _matched;
|
||||||
|
if (_lastWatchedValue != _folder.UnresolvedValue()) {
|
||||||
|
SetupWatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTempVarValues();
|
||||||
|
|
||||||
|
_newFiles.clear();
|
||||||
|
_changedFiles.clear();
|
||||||
|
_removedFiles.clear();
|
||||||
|
_newDirs.clear();
|
||||||
|
_removedDirs.clear();
|
||||||
|
_matched = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionFolder::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
MacroCondition::Save(obj);
|
||||||
|
_folder.Save(obj, "file");
|
||||||
|
obs_data_set_bool(obj, "enableFilter", _enableFilter);
|
||||||
|
_regex.Save(obj);
|
||||||
|
_filter.Save(obj, "filter");
|
||||||
|
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionFolder::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
MacroCondition::Load(obj);
|
||||||
|
_folder.Load(obj, "file");
|
||||||
|
_enableFilter = obs_data_get_bool(obj, "enableFilter");
|
||||||
|
_regex.Load(obj);
|
||||||
|
_regex.SetEnabled(true); // Already controlled via _enableFilter
|
||||||
|
_filter.Load(obj, "filter");
|
||||||
|
_condition = static_cast<Condition>(obs_data_get_int(obj, "condition"));
|
||||||
|
SetupWatcher();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MacroConditionFolder::GetShortDesc() const
|
||||||
|
{
|
||||||
|
return _folder.UnresolvedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolder::SetFolder(const std::string &folder)
|
||||||
|
{
|
||||||
|
_folder = folder;
|
||||||
|
SetupWatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QSet<QString> getFilesInDir(const QString &path)
|
||||||
|
{
|
||||||
|
QSet<QString> result;
|
||||||
|
for (const auto &file : QDir(path).entryList(QDir::Files)) {
|
||||||
|
result << file;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QSet<QString> getDirsInDir(const QString &path)
|
||||||
|
{
|
||||||
|
QSet<QString> result;
|
||||||
|
for (const auto &dir : QDir(path).entryList(QDir::AllDirs)) {
|
||||||
|
result << dir;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reduceSetToPatternMatch(QSet<QString> &set,
|
||||||
|
const RegexConfig ®ex,
|
||||||
|
const std::string &pattern)
|
||||||
|
{
|
||||||
|
QSet<QString> copy = set;
|
||||||
|
for (const auto &value : copy) {
|
||||||
|
if (!regex.Matches(value.toStdString(), pattern)) {
|
||||||
|
set.remove(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolder::DirectoryChanged(const QString &path)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (MacroIsPaused(GetMacro())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentFiles = getFilesInDir(path);
|
||||||
|
auto currentDirs = getDirsInDir(path);
|
||||||
|
|
||||||
|
if (currentFiles.count() > _currentFiles.count()) {
|
||||||
|
_newFiles += currentFiles - _currentFiles;
|
||||||
|
} else {
|
||||||
|
_removedFiles += _currentFiles - currentFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentDirs.count() > _currentDirs.count()) {
|
||||||
|
_newDirs += currentDirs - _currentDirs;
|
||||||
|
} else {
|
||||||
|
_removedDirs += _currentDirs - currentDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_enableFilter) {
|
||||||
|
reduceSetToPatternMatch(_newFiles, _regex, _filter);
|
||||||
|
reduceSetToPatternMatch(_removedFiles, _regex, _filter);
|
||||||
|
reduceSetToPatternMatch(_newDirs, _regex, _filter);
|
||||||
|
reduceSetToPatternMatch(_removedDirs, _regex, _filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_condition) {
|
||||||
|
case Condition::ANY:
|
||||||
|
_matched = _matched || _newFiles.count() > 0 ||
|
||||||
|
_removedFiles.count() > 0 || _newDirs.count() > 0 ||
|
||||||
|
_removedDirs.count() > 0;
|
||||||
|
break;
|
||||||
|
case Condition::FILE_ADD:
|
||||||
|
_matched = _newFiles.count() > 0;
|
||||||
|
break;
|
||||||
|
case Condition::FILE_REMOVE:
|
||||||
|
_matched = _removedFiles.count() > 0;
|
||||||
|
break;
|
||||||
|
case Condition::FOLDER_ADD:
|
||||||
|
_matched = _newDirs.count() > 0;
|
||||||
|
break;
|
||||||
|
case Condition::FOLDER_REMOVE:
|
||||||
|
_matched = _removedDirs.count() > 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &newFile : _newFiles) {
|
||||||
|
_watcher->addPath(path + "/" + newFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentFiles = currentFiles;
|
||||||
|
_currentDirs = currentDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolder::FileChanged(const QString &path)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
QFileInfo fileInfo(path);
|
||||||
|
if (!fileInfo.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fileName = fileInfo.fileName();
|
||||||
|
if (_enableFilter && !_regex.Matches(fileName.toStdString(), _filter)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_changedFiles << fileName;
|
||||||
|
if (_condition == Condition::FILE_CHANGE ||
|
||||||
|
_condition == Condition::ANY) {
|
||||||
|
_matched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList getFileWatcherList(const QSet<QString> &files,
|
||||||
|
const QString &dirName)
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
for (const auto &value : files) {
|
||||||
|
list << (dirName + "/" + value);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolder::SetupWatcher()
|
||||||
|
{
|
||||||
|
_watcher = std::make_unique<QFileSystemWatcher>();
|
||||||
|
const auto path = QString::fromStdString(_folder);
|
||||||
|
_currentFiles = getFilesInDir(path);
|
||||||
|
_currentDirs = getDirsInDir(path);
|
||||||
|
_lastWatchedValue = _folder.UnresolvedValue();
|
||||||
|
connect(_watcher.get(), SIGNAL(directoryChanged(const QString &)), this,
|
||||||
|
SLOT(DirectoryChanged(const QString &)));
|
||||||
|
connect(_watcher.get(), SIGNAL(fileChanged(const QString &)), this,
|
||||||
|
SLOT(FileChanged(const QString &)));
|
||||||
|
_watcher.get()->addPaths(getFileWatcherList(_currentFiles, path));
|
||||||
|
_watcher->addPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolder::SetTempVarValues()
|
||||||
|
{
|
||||||
|
auto setVarHelper = [this](const QSet<QString> &set,
|
||||||
|
const std::string &id) {
|
||||||
|
std::string result;
|
||||||
|
for (const auto &value : set) {
|
||||||
|
result += value.toStdString() + "\n";
|
||||||
|
}
|
||||||
|
if (result.size() > 0) {
|
||||||
|
result.pop_back();
|
||||||
|
}
|
||||||
|
SetTempVarValue(id, result);
|
||||||
|
};
|
||||||
|
|
||||||
|
setVarHelper(_newFiles, "newFiles");
|
||||||
|
setVarHelper(_changedFiles, "changedFiles");
|
||||||
|
setVarHelper(_removedFiles, "removedFiles");
|
||||||
|
setVarHelper(_newDirs, "newDirs");
|
||||||
|
setVarHelper(_removedDirs, "removedDirs");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolder::SetupTempVars()
|
||||||
|
{
|
||||||
|
MacroCondition::SetupTempVars();
|
||||||
|
AddTempvar("newFiles",
|
||||||
|
obs_module_text("AdvSceneSwitcher.tempVar.folder.newFiles"));
|
||||||
|
AddTempvar("changedFiles",
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.folder.changedFiles"));
|
||||||
|
AddTempvar("removedFiles",
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.folder.removedFiles"));
|
||||||
|
AddTempvar("newDirs",
|
||||||
|
obs_module_text("AdvSceneSwitcher.tempVar.folder.newDirs"));
|
||||||
|
AddTempvar(
|
||||||
|
"removedDirs",
|
||||||
|
obs_module_text("AdvSceneSwitcher.tempVar.folder.removedDirs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void populateConditions(QComboBox *list)
|
||||||
|
{
|
||||||
|
for (const auto &[value, name] : conditions) {
|
||||||
|
list->addItem(obs_module_text(name.c_str()),
|
||||||
|
static_cast<int>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroConditionFolderEdit::MacroConditionFolderEdit(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroConditionFolder> entryData)
|
||||||
|
: QWidget(parent),
|
||||||
|
_conditions(new QComboBox()),
|
||||||
|
_folder(new FileSelection(FileSelection::Type::FOLDER)),
|
||||||
|
_enableFilter(new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.folder.enableFilter"))),
|
||||||
|
_filterLayout(new QHBoxLayout()),
|
||||||
|
_regex(new RegexConfigWidget(this, false)),
|
||||||
|
_filter(new VariableLineEdit(this))
|
||||||
|
{
|
||||||
|
QString path = GetThemeTypeName() == "Light"
|
||||||
|
? ":/res/images/help.svg"
|
||||||
|
: ":/res/images/help_light.svg";
|
||||||
|
QIcon icon(path);
|
||||||
|
QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||||
|
auto tooltipLabel = new QLabel(this);
|
||||||
|
tooltipLabel->setPixmap(pixmap);
|
||||||
|
tooltipLabel->setToolTip(
|
||||||
|
obs_module_text("AdvSceneSwitcher.condition.folder.tooltip"));
|
||||||
|
|
||||||
|
populateConditions(_conditions);
|
||||||
|
|
||||||
|
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(ConditionChanged(int)));
|
||||||
|
QWidget::connect(_folder, SIGNAL(PathChanged(const QString &)), this,
|
||||||
|
SLOT(PathChanged(const QString &)));
|
||||||
|
QWidget::connect(_enableFilter, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(EnableFilterChanged(int)));
|
||||||
|
QWidget::connect(_regex,
|
||||||
|
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||||
|
SLOT(RegexChanged(const RegexConfig &)));
|
||||||
|
QWidget::connect(_filter, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(FilterChanged()));
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{conditions}}", _conditions}, {"{{folder}}", _folder},
|
||||||
|
{"{{tooltip}}", tooltipLabel}, {"{{regex}}", _regex},
|
||||||
|
{"{{filter}}", _filter},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto entryLayout = new QHBoxLayout();
|
||||||
|
entryLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.folder.entry"),
|
||||||
|
entryLayout, widgetPlaceholders, false);
|
||||||
|
_filterLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
PlaceWidgets(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.folder.entry.filter"),
|
||||||
|
_filterLayout, widgetPlaceholders, false);
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout();
|
||||||
|
layout->addLayout(entryLayout);
|
||||||
|
layout->addWidget(_enableFilter);
|
||||||
|
layout->addLayout(_filterLayout);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
_entryData = entryData;
|
||||||
|
UpdateEntryData();
|
||||||
|
_loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolderEdit::UpdateEntryData()
|
||||||
|
{
|
||||||
|
if (!_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_conditions->setCurrentIndex(_conditions->findData(
|
||||||
|
static_cast<int>(_entryData->_condition)));
|
||||||
|
_folder->SetPath(_entryData->GetFolder());
|
||||||
|
_enableFilter->setChecked(_entryData->_enableFilter);
|
||||||
|
_regex->SetRegexConfig(_entryData->_regex);
|
||||||
|
_filter->setText(_entryData->_filter);
|
||||||
|
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolderEdit::ConditionChanged(int index)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lock = LockContext();
|
||||||
|
_entryData->_condition = static_cast<MacroConditionFolder::Condition>(
|
||||||
|
_conditions->itemData(index).toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolderEdit::PathChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lock = LockContext();
|
||||||
|
_entryData->SetFolder(text.toStdString());
|
||||||
|
emit HeaderInfoChanged(
|
||||||
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolderEdit::RegexChanged(const RegexConfig ®ex)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lock = LockContext();
|
||||||
|
_entryData->_regex = regex;
|
||||||
|
_entryData->_regex.SetEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolderEdit::FilterChanged()
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lock = LockContext();
|
||||||
|
_entryData->_filter = _filter->text().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolderEdit::EnableFilterChanged(int value)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lock = LockContext();
|
||||||
|
_entryData->_enableFilter = value;
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionFolderEdit::SetWidgetVisibility()
|
||||||
|
{
|
||||||
|
SetLayoutVisible(_filterLayout, _entryData->_enableFilter);
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
109
plugins/base/macro-condition-folder.hpp
Normal file
109
plugins/base/macro-condition-folder.hpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "macro-condition-edit.hpp"
|
||||||
|
#include "file-selection.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
|
#include "variable-line-edit.hpp"
|
||||||
|
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class MacroConditionFolder : public QObject, public MacroCondition {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MacroConditionFolder(Macro *m);
|
||||||
|
bool CheckCondition();
|
||||||
|
bool Save(obs_data_t *obj) const;
|
||||||
|
bool Load(obs_data_t *obj);
|
||||||
|
std::string GetShortDesc() const;
|
||||||
|
std::string GetId() const { return id; };
|
||||||
|
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroConditionFolder>(m);
|
||||||
|
}
|
||||||
|
void SetFolder(const std::string &);
|
||||||
|
StringVariable GetFolder() const { return _folder; }
|
||||||
|
|
||||||
|
enum class Condition {
|
||||||
|
ANY,
|
||||||
|
FILE_ADD,
|
||||||
|
FILE_CHANGE,
|
||||||
|
FILE_REMOVE,
|
||||||
|
FOLDER_ADD,
|
||||||
|
FOLDER_REMOVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
Condition _condition = Condition::ANY;
|
||||||
|
bool _enableFilter = false;
|
||||||
|
RegexConfig _regex = RegexConfig(true);
|
||||||
|
StringVariable _filter = ".*";
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void DirectoryChanged(const QString &);
|
||||||
|
void FileChanged(const QString &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetupWatcher();
|
||||||
|
void SetTempVarValues();
|
||||||
|
void SetupTempVars();
|
||||||
|
|
||||||
|
StringVariable _folder = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||||
|
|
||||||
|
std::unique_ptr<QFileSystemWatcher> _watcher;
|
||||||
|
std::string _lastWatchedValue = "";
|
||||||
|
|
||||||
|
std::mutex _mutex;
|
||||||
|
bool _matched = false;
|
||||||
|
QSet<QString> _newFiles;
|
||||||
|
QSet<QString> _changedFiles;
|
||||||
|
QSet<QString> _removedFiles;
|
||||||
|
QSet<QString> _newDirs;
|
||||||
|
QSet<QString> _removedDirs;
|
||||||
|
QSet<QString> _currentFiles;
|
||||||
|
QSet<QString> _currentDirs;
|
||||||
|
|
||||||
|
static bool _registered;
|
||||||
|
static const std::string id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroConditionFolderEdit : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MacroConditionFolderEdit(
|
||||||
|
QWidget *parent,
|
||||||
|
std::shared_ptr<MacroConditionFolder> cond = nullptr);
|
||||||
|
void UpdateEntryData();
|
||||||
|
static QWidget *Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroCondition> cond)
|
||||||
|
{
|
||||||
|
return new MacroConditionFolderEdit(
|
||||||
|
parent,
|
||||||
|
std::dynamic_pointer_cast<MacroConditionFolder>(cond));
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ConditionChanged(int index);
|
||||||
|
void PathChanged(const QString &text);
|
||||||
|
void EnableFilterChanged(int value);
|
||||||
|
void RegexChanged(const RegexConfig &);
|
||||||
|
void FilterChanged();
|
||||||
|
signals:
|
||||||
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
|
QComboBox *_conditions;
|
||||||
|
FileSelection *_folder;
|
||||||
|
QCheckBox *_enableFilter;
|
||||||
|
QHBoxLayout *_filterLayout;
|
||||||
|
RegexConfigWidget *_regex;
|
||||||
|
VariableLineEdit *_filter;
|
||||||
|
|
||||||
|
std::shared_ptr<MacroConditionFolder> _entryData;
|
||||||
|
bool _loading = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -181,12 +181,6 @@ static bool windowIsValid(HWND window)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD id;
|
|
||||||
GetWindowThreadProcessId(window, &id);
|
|
||||||
if (id == GetCurrentProcessId()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only accept for top level windows and not controls within them
|
// Only accept for top level windows and not controls within them
|
||||||
if (window != GetAncestor(window, GA_ROOT)) {
|
if (window != GetAncestor(window, GA_ROOT)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
|||||||
obs_module_text("AdvSceneSwitcher.twitchConnectionTab.title"));
|
obs_module_text("AdvSceneSwitcher.twitchConnectionTab.title"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool twitchTabIsSelected(QTabWidget *tabWidget)
|
||||||
|
{
|
||||||
|
return tabWidget->tabText(tabWidget->currentIndex()) ==
|
||||||
|
obs_module_text("AdvSceneSwitcher.twitchConnectionTab.title");
|
||||||
|
}
|
||||||
|
|
||||||
TwitchConnectionsTable *TwitchConnectionsTable::Create()
|
TwitchConnectionsTable *TwitchConnectionsTable::Create()
|
||||||
{
|
{
|
||||||
tabWidget = new TwitchConnectionsTable();
|
tabWidget = new TwitchConnectionsTable();
|
||||||
@@ -211,11 +217,11 @@ static void setupTab(QTabWidget *tab)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
auto timer = new QTimer(tabWidget);
|
QWidget::connect(tab, &QTabWidget::currentChanged, [tab]() {
|
||||||
timer->setInterval(1000);
|
if (twitchTabIsSelected(tab)) {
|
||||||
QWidget::connect(timer, &QTimer::timeout,
|
updateConnectionStatus(tabWidget->Table());
|
||||||
[]() { updateConnectionStatus(tabWidget->Table()); });
|
}
|
||||||
timer->start();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
Reference in New Issue
Block a user