mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
13 Commits
1.21.0-bet
...
1.21.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bedbf6fb8 | ||
|
|
853150e458 | ||
|
|
1ddd48e008 | ||
|
|
17d24d642e | ||
|
|
87a46684cd | ||
|
|
e4c5193945 | ||
|
|
2e969bf6dd | ||
|
|
6db26d9f7b | ||
|
|
4f02c9ec36 | ||
|
|
2fdd39fba5 | ||
|
|
fe1ddc3a94 | ||
|
|
cff2d805db | ||
|
|
34425d3e47 |
1
.github/actions/run-tests/action.yml
vendored
1
.github/actions/run-tests/action.yml
vendored
@@ -16,6 +16,7 @@ runs:
|
||||
shell: zsh {0}
|
||||
run: |
|
||||
if [[ '${{ inputs.target }}' != 'x86_64' ]]; then
|
||||
echo tests skipped!
|
||||
exit 0
|
||||
fi
|
||||
${{ inputs.workingDirectory }}/build_x86_64/tests/advanced-scene-switcher-tests
|
||||
|
||||
10
.github/scripts/.build.zsh
vendored
10
.github/scripts/.build.zsh
vendored
@@ -170,11 +170,17 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
|
||||
local product_name
|
||||
local product_version
|
||||
local git_tag="$(git describe --tags)"
|
||||
|
||||
read -r product_name product_version <<< \
|
||||
"$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})"
|
||||
|
||||
|
||||
|
||||
if [[ "${git_tag}" =~ '^([0-9]+\.){0,2}(\*|[0-9]+)$' ]] {
|
||||
log_info "Using git tag as version identifier '${git_tag}'"
|
||||
product_version="${git_tag}"
|
||||
} else {
|
||||
log_info "Using buildspec.json version identifier '${product_version}'"
|
||||
}
|
||||
|
||||
case ${host_os} {
|
||||
macos)
|
||||
|
||||
9
.github/scripts/.package.zsh
vendored
9
.github/scripts/.package.zsh
vendored
@@ -128,9 +128,18 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
|
||||
local product_name
|
||||
local product_version
|
||||
local git_tag="$(git describe --tags)"
|
||||
|
||||
read -r product_name product_version <<< \
|
||||
"$(jq -r '. | {name, version} | join(" ")' ${project_root}/buildspec.json)"
|
||||
|
||||
if [[ "${git_tag}" =~ '^([0-9]+\.){0,2}(\*|[0-9]+)$' ]] {
|
||||
log_info "Using git tag as version identifier '${git_tag}'"
|
||||
product_version="${git_tag}"
|
||||
} else {
|
||||
log_info "Using buildspec.json version identifier '${product_version}'"
|
||||
}
|
||||
|
||||
if [[ ${host_os} == 'macos' ]] {
|
||||
autoload -Uz check_packages read_codesign read_codesign_installer read_codesign_pass
|
||||
|
||||
|
||||
3
.github/workflows/main.yml
vendored
3
.github/workflows/main.yml
vendored
@@ -425,10 +425,11 @@ jobs:
|
||||
for file in ${{ github.workspace }}/**/@(*.pkg|*.exe|*.deb|*.zip); do
|
||||
echo " ${file##*/}: $(sha256sum "${file}" | cut -d " " -f 1)" >> ${{ github.workspace }}/CHECKSUMS.txt
|
||||
done
|
||||
cat ${{ github.workspace }}/CHECKSUMS.txt
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
prerelease: true
|
||||
|
||||
@@ -343,7 +343,8 @@ target_include_directories(
|
||||
${LIB_NAME}
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deps/obs-websocket/lib")
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deps/obs-websocket/lib"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deps/exprtk")
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
@@ -354,7 +355,8 @@ if(OS_WINDOWS)
|
||||
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE UNICODE _UNICODE)
|
||||
if(MSVC)
|
||||
target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267)
|
||||
target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267
|
||||
/bigobj)
|
||||
endif()
|
||||
target_sources(${LIB_NAME} PRIVATE src/win/advanced-scene-switcher-win.cpp)
|
||||
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||
|
||||
@@ -30,6 +30,8 @@ AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose loggi
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="Display warning if plugins cannot be loaded"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>Loading of the following plugin libraries was unsuccessful, which could result in some Advanced Scene Switcher functions not being available:%1Check the OBS logs for details.<br>This message can be disabled on the General tab.</body></html>"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
|
||||
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
|
||||
AdvSceneSwitcher.generalTab.priority="Priority"
|
||||
@@ -83,6 +85,7 @@ AdvSceneSwitcher.macroTab.defaultGroupName="Group %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Are you sure you want to delete \"%1\" and all its elements?"
|
||||
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Are you sure you want to delete %1 macros?"
|
||||
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Are you sure you want to delete \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="Add"
|
||||
AdvSceneSwitcher.macroTab.copy="Duplicate Macro"
|
||||
AdvSceneSwitcher.macroTab.group="Group Selected Macros"
|
||||
@@ -943,10 +946,7 @@ AdvSceneSwitcher.process.addArgumentDescription="Add new argument:"
|
||||
AdvSceneSwitcher.process.entry="Run{{filePath}}{{advancedSettings}}"
|
||||
AdvSceneSwitcher.process.entry.workingDirectory="Working directory:{{workingDirectory}}"
|
||||
|
||||
AdvSceneSwitcher.math.notANumber="not a valid number"
|
||||
AdvSceneSwitcher.math.expressionFail="failed evaluate expression"
|
||||
AdvSceneSwitcher.math.expressionFailParentheses="failed evaluate expression (missing Parentheses?)"
|
||||
AdvSceneSwitcher.math.notFullyResolved="not all operands were used"
|
||||
AdvSceneSwitcher.math.expressionFail="Failed evaluate expression"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
|
||||
41032
deps/exprtk/exprtk.hpp
vendored
Normal file
41032
deps/exprtk/exprtk.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -256,6 +256,30 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_26">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="warnPluginLoadFailure">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_19">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_24">
|
||||
<item>
|
||||
|
||||
@@ -43,9 +43,7 @@ AdvSceneSwitcher::AdvSceneSwitcher(QWidget *parent)
|
||||
{
|
||||
switcher->settingsWindowOpened = true;
|
||||
ui->setupUi(this);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
switcher->Prune();
|
||||
loadUI();
|
||||
}
|
||||
@@ -58,26 +56,51 @@ AdvSceneSwitcher::~AdvSceneSwitcher()
|
||||
}
|
||||
}
|
||||
|
||||
bool translationAvailable()
|
||||
static bool translationAvailable()
|
||||
{
|
||||
return !!strcmp(obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
"AdvSceneSwitcher.pluginName");
|
||||
}
|
||||
|
||||
static void DisplayMissingDependencyWarning()
|
||||
{
|
||||
if (!switcher->warnPluginLoadFailure ||
|
||||
switcher->loadFailureLibs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString failedLibsString = "<ul>";
|
||||
for (const auto &lib : switcher->loadFailureLibs) {
|
||||
failedLibsString += "<li>" + lib + "</li>";
|
||||
}
|
||||
failedLibsString += "</ul>";
|
||||
QString warning(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage"));
|
||||
DisplayMessage(warning.arg(failedLibsString));
|
||||
}
|
||||
|
||||
static void DisplayMissingDataDirWarning()
|
||||
{
|
||||
if (translationAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString msg = "Failed to find plug-in's 'data' directory.\n"
|
||||
"Please check installation instructions!\n\n"
|
||||
"Data most likely expected at:\n\n";
|
||||
#ifdef _WIN32
|
||||
msg += QString::fromStdString(
|
||||
(std::filesystem::current_path().string()));
|
||||
msg += "/";
|
||||
#endif
|
||||
msg += obs_get_module_data_path(obs_current_module());
|
||||
DisplayMessage(msg);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::loadUI()
|
||||
{
|
||||
if (!translationAvailable()) {
|
||||
QString msg = "Failed to find plug-in's 'data' directory.\n"
|
||||
"Please check installation instructions!\n\n"
|
||||
"Data most likely expected at:\n\n";
|
||||
#ifdef _WIN32
|
||||
msg += QString::fromStdString(
|
||||
(std::filesystem::current_path().string()));
|
||||
msg += "/";
|
||||
#endif
|
||||
msg += obs_get_module_data_path(obs_current_module());
|
||||
(void)DisplayMessage(msg);
|
||||
}
|
||||
DisplayMissingDataDirWarning();
|
||||
DisplayMissingDependencyWarning();
|
||||
|
||||
setupGeneralTab();
|
||||
setupTitleTab();
|
||||
@@ -687,6 +710,7 @@ void LoadPlugins()
|
||||
blog(LOG_WARNING, "failed to load \"%s\": %s",
|
||||
file.toStdString().c_str(),
|
||||
lib->errorString().toStdString().c_str());
|
||||
switcher->loadFailureLibs << file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +202,7 @@ public slots:
|
||||
void on_saveWindowGeo_stateChanged(int state);
|
||||
void on_showTrayNotifications_stateChanged(int state);
|
||||
void on_uiHintsDisable_stateChanged(int state);
|
||||
void on_warnPluginLoadFailure_stateChanged(int state);
|
||||
void on_hideLegacyTabs_stateChanged(int state);
|
||||
|
||||
void on_exportSettings_clicked();
|
||||
|
||||
@@ -172,6 +172,15 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
||||
switcher->disableHints = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_warnPluginLoadFailure_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->warnPluginLoadFailure = state;
|
||||
}
|
||||
|
||||
bool isLegacyTab(const QString &name)
|
||||
{
|
||||
return name == obs_module_text(
|
||||
@@ -538,8 +547,8 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
// selections to be available.
|
||||
loadSceneGroups(obj);
|
||||
loadVariables(obj);
|
||||
loadMacros(obj);
|
||||
loadConnections(obj);
|
||||
loadMacros(obj);
|
||||
loadWindowTitleSwitches(obj);
|
||||
loadScreenRegionSwitches(obj);
|
||||
loadPauseSwitches(obj);
|
||||
@@ -617,6 +626,7 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
||||
obs_data_set_bool(obj, "showSystemTrayNotifications",
|
||||
showSystemTrayNotifications);
|
||||
obs_data_set_bool(obj, "disableHints", disableHints);
|
||||
obs_data_set_bool(obj, "warnPluginLoadFailure", warnPluginLoadFailure);
|
||||
obs_data_set_bool(obj, "hideLegacyTabs", hideLegacyTabs);
|
||||
|
||||
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
|
||||
@@ -673,6 +683,8 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
showSystemTrayNotifications =
|
||||
obs_data_get_bool(obj, "showSystemTrayNotifications");
|
||||
disableHints = obs_data_get_bool(obj, "disableHints");
|
||||
obs_data_set_default_bool(obj, "warnPluginLoadFailure", true);
|
||||
warnPluginLoadFailure = obs_data_get_bool(obj, "warnPluginLoadFailure");
|
||||
obs_data_set_default_bool(obj, "hideLegacyTabs", true);
|
||||
hideLegacyTabs = obs_data_get_bool(obj, "hideLegacyTabs");
|
||||
|
||||
@@ -971,6 +983,7 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
ui->showTrayNotifications->setChecked(
|
||||
switcher->showSystemTrayNotifications);
|
||||
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
||||
ui->warnPluginLoadFailure->setChecked(switcher->warnPluginLoadFailure);
|
||||
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);
|
||||
|
||||
for (int p : switcher->functionNamesByPriority) {
|
||||
|
||||
@@ -9,16 +9,6 @@ bool MacroActionSudioMode::_registered = MacroActionFactory::Register(
|
||||
{MacroActionSudioMode::Create, MacroActionSudioModeEdit::Create,
|
||||
"AdvSceneSwitcher.action.studioMode"});
|
||||
|
||||
// TODO: Remove in future version (backwards compatibility)
|
||||
static const std::string idOld1 = "scene_swap";
|
||||
static const std::string idOld2 = "preview_scene";
|
||||
static bool oldRegisterd1 = MacroActionFactory::Register(
|
||||
idOld1, {MacroActionSudioMode::Create, MacroActionSudioModeEdit::Create,
|
||||
"AdvSceneSwitcher.action.studioMode"});
|
||||
static bool oldRegisterd2 = MacroActionFactory::Register(
|
||||
idOld2, {MacroActionSudioMode::Create, MacroActionSudioModeEdit::Create,
|
||||
"AdvSceneSwitcher.action.studioMode"});
|
||||
|
||||
const static std::map<StudioModeAction, std::string> actionTypes = {
|
||||
{StudioModeAction::SWAP_SCENE,
|
||||
"AdvSceneSwitcher.action.studioMode.type.swap"},
|
||||
@@ -98,10 +88,6 @@ bool MacroActionSudioMode::Load(obs_data_t *obj)
|
||||
_action =
|
||||
static_cast<StudioModeAction>(obs_data_get_int(obj, "action"));
|
||||
_scene.Load(obj);
|
||||
// TODO: Remove in future version (backwards compatibility)
|
||||
if (obs_data_get_string(obj, "id") == idOld2) {
|
||||
_action = StudioModeAction::SET_SCENE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ void MacroActionVariable::HandleMathExpression(Variable *var)
|
||||
|
||||
bool MacroActionVariable::PerformAction()
|
||||
{
|
||||
auto var = GetVariableByName(_variableName);
|
||||
auto var = _variable.lock();
|
||||
if (!var) {
|
||||
return true;
|
||||
}
|
||||
@@ -132,7 +132,7 @@ bool MacroActionVariable::PerformAction()
|
||||
apppend(*var, _strValue);
|
||||
break;
|
||||
case Type::APPEND_VAR: {
|
||||
auto var2 = GetVariableByName(_variable2Name);
|
||||
auto var2 = _variable2.lock();
|
||||
if (!var2) {
|
||||
return true;
|
||||
}
|
||||
@@ -171,18 +171,18 @@ bool MacroActionVariable::PerformAction()
|
||||
}
|
||||
case Type::SUBSTRING: {
|
||||
if (_regex.Enabled()) {
|
||||
HandleRegexSubString(var);
|
||||
HandleRegexSubString(var.get());
|
||||
return true;
|
||||
}
|
||||
HandleIndexSubString(var);
|
||||
HandleIndexSubString(var.get());
|
||||
return true;
|
||||
}
|
||||
case Type::FIND_AND_REPLACE: {
|
||||
HandleFindAndReplace(var);
|
||||
HandleFindAndReplace(var.get());
|
||||
return true;
|
||||
}
|
||||
case Type::MATH_EXPRESSION: {
|
||||
HandleMathExpression(var);
|
||||
HandleMathExpression(var.get());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -193,8 +193,10 @@ bool MacroActionVariable::PerformAction()
|
||||
bool MacroActionVariable::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "variableName", _variableName.c_str());
|
||||
obs_data_set_string(obj, "variable2Name", _variable2Name.c_str());
|
||||
obs_data_set_string(obj, "variableName",
|
||||
GetWeakVariableName(_variable).c_str());
|
||||
obs_data_set_string(obj, "variable2Name",
|
||||
GetWeakVariableName(_variable2).c_str());
|
||||
obs_data_set_string(obj, "strValue", _strValue.c_str());
|
||||
obs_data_set_double(obj, "numValue", _numValue);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_type));
|
||||
@@ -213,8 +215,10 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
|
||||
bool MacroActionVariable::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_variableName = obs_data_get_string(obj, "variableName");
|
||||
_variable2Name = obs_data_get_string(obj, "variable2Name");
|
||||
_variable =
|
||||
GetWeakVariableByName(obs_data_get_string(obj, "variableName"));
|
||||
_variable2 = GetWeakVariableByName(
|
||||
obs_data_get_string(obj, "variable2Name"));
|
||||
_strValue = obs_data_get_string(obj, "strValue");
|
||||
_numValue = obs_data_get_double(obj, "numValue");
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "condition"));
|
||||
@@ -238,7 +242,7 @@ bool MacroActionVariable::PostLoad()
|
||||
|
||||
std::string MacroActionVariable::GetShortDesc() const
|
||||
{
|
||||
return _variableName;
|
||||
return GetWeakVariableName(_variable);
|
||||
}
|
||||
|
||||
void MacroActionVariable::SetSegmentIndexValue(int value)
|
||||
@@ -463,8 +467,8 @@ void MacroActionVariableEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_variables->SetVariable(_entryData->_variableName);
|
||||
_variables2->SetVariable(_entryData->_variable2Name);
|
||||
_variables->SetVariable(_entryData->_variable);
|
||||
_variables2->SetVariable(_entryData->_variable2);
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_strValue->setPlainText(QString::fromStdString(_entryData->_strValue));
|
||||
_numValue->setValue(_entryData->_numValue);
|
||||
@@ -489,7 +493,7 @@ void MacroActionVariableEdit::VariableChanged(const QString &text)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_variableName = text.toStdString();
|
||||
_entryData->_variable = GetWeakVariableByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::Variable2Changed(const QString &text)
|
||||
@@ -499,7 +503,7 @@ void MacroActionVariableEdit::Variable2Changed(const QString &text)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_variable2Name = text.toStdString();
|
||||
_entryData->_variable2 = GetWeakVariableByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::ActionChanged(int value)
|
||||
|
||||
@@ -36,8 +36,8 @@ public:
|
||||
};
|
||||
|
||||
Type _type = Type::SET_FIXED_VALUE;
|
||||
std::string _variableName = "";
|
||||
std::string _variable2Name = "";
|
||||
std::weak_ptr<Variable> _variable;
|
||||
std::weak_ptr<Variable> _variable2;
|
||||
std::string _strValue = "";
|
||||
double _numValue = 0;
|
||||
int _subStringStart = 0;
|
||||
|
||||
@@ -18,7 +18,7 @@ static std::map<MacroActionWebsocket::Type, std::string> actionTypes = {
|
||||
|
||||
void MacroActionWebsocket::SendRequest()
|
||||
{
|
||||
auto connection = GetConnectionByName(_connection);
|
||||
auto connection = _connection.lock();
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
@@ -46,7 +46,7 @@ void MacroActionWebsocket::LogAction() const
|
||||
switch (_type) {
|
||||
case MacroActionWebsocket::Type::REQUEST:
|
||||
vblog(LOG_INFO, "sent msg \"%s\" via \"%s\"", _message.c_str(),
|
||||
_connection.c_str());
|
||||
GetWeakConnectionName(_connection).c_str());
|
||||
break;
|
||||
case MacroActionWebsocket::Type::EVENT:
|
||||
vblog(LOG_INFO, "sent event \"%s\" to connected clients",
|
||||
@@ -62,7 +62,8 @@ bool MacroActionWebsocket::Save(obs_data_t *obj) const
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
_message.Save(obj, "message");
|
||||
obs_data_set_string(obj, "connection", _connection.c_str());
|
||||
obs_data_set_string(obj, "connection",
|
||||
GetWeakConnectionName(_connection).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -71,14 +72,15 @@ bool MacroActionWebsocket::Load(obs_data_t *obj)
|
||||
MacroAction::Load(obj);
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||
_message.Load(obj, "message");
|
||||
_connection = obs_data_get_string(obj, "connection");
|
||||
_connection =
|
||||
GetWeakConnectionByName(obs_data_get_string(obj, "connection"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionWebsocket::GetShortDesc() const
|
||||
{
|
||||
if (_type == Type::REQUEST) {
|
||||
return _connection;
|
||||
return GetWeakConnectionName(_connection);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -206,6 +208,6 @@ void MacroActionWebsocketEdit::ConnectionSelectionChanged(
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_connection = connection.toStdString();
|
||||
_entryData->_connection = GetWeakConnectionByQString(connection);
|
||||
emit(HeaderInfoChanged(connection));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
|
||||
Type _type = Type::REQUEST;
|
||||
StringVariable _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
std::string _connection;
|
||||
std::weak_ptr<Connection> _connection;
|
||||
|
||||
private:
|
||||
void SendRequest();
|
||||
|
||||
@@ -76,8 +76,8 @@ bool MacroConditionVariable::ValueChanged(const Variable &var)
|
||||
|
||||
bool MacroConditionVariable::CompareVariables()
|
||||
{
|
||||
auto var1 = GetVariableByName(_variableName);
|
||||
auto var2 = GetVariableByName(_variable2Name);
|
||||
auto var1 = _variable.lock();
|
||||
auto var2 = _variable2.lock();
|
||||
if (!var1 || !var2) {
|
||||
return false;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ bool MacroConditionVariable::CompareVariables()
|
||||
|
||||
bool MacroConditionVariable::CheckCondition()
|
||||
{
|
||||
auto var = GetVariableByName(_variableName);
|
||||
auto var = _variable.lock();
|
||||
if (!var) {
|
||||
return false;
|
||||
}
|
||||
@@ -137,8 +137,10 @@ bool MacroConditionVariable::CheckCondition()
|
||||
bool MacroConditionVariable::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "variableName", _variableName.c_str());
|
||||
obs_data_set_string(obj, "variable2Name", _variable2Name.c_str());
|
||||
obs_data_set_string(obj, "variableName",
|
||||
GetWeakVariableName(_variable).c_str());
|
||||
obs_data_set_string(obj, "variable2Name",
|
||||
GetWeakVariableName(_variable2).c_str());
|
||||
obs_data_set_string(obj, "strValue", _strValue.c_str());
|
||||
obs_data_set_double(obj, "numValue", _numValue);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_type));
|
||||
@@ -149,8 +151,10 @@ bool MacroConditionVariable::Save(obs_data_t *obj) const
|
||||
bool MacroConditionVariable::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_variableName = obs_data_get_string(obj, "variableName");
|
||||
_variable2Name = obs_data_get_string(obj, "variable2Name");
|
||||
_variable =
|
||||
GetWeakVariableByName(obs_data_get_string(obj, "variableName"));
|
||||
_variable2 = GetWeakVariableByName(
|
||||
obs_data_get_string(obj, "variable2Name"));
|
||||
_strValue = obs_data_get_string(obj, "strValue");
|
||||
_numValue = obs_data_get_double(obj, "numValue");
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "condition"));
|
||||
@@ -165,7 +169,7 @@ bool MacroConditionVariable::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroConditionVariable::GetShortDesc() const
|
||||
{
|
||||
return _variableName;
|
||||
return GetWeakVariableName(_variable);
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
@@ -236,8 +240,8 @@ void MacroConditionVariableEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_variables->SetVariable(_entryData->_variableName);
|
||||
_variables2->SetVariable(_entryData->_variable2Name);
|
||||
_variables->SetVariable(_entryData->_variable);
|
||||
_variables2->SetVariable(_entryData->_variable2);
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_strValue->setPlainText(QString::fromStdString(_entryData->_strValue));
|
||||
_numValue->setValue(_entryData->_numValue);
|
||||
@@ -252,7 +256,7 @@ void MacroConditionVariableEdit::VariableChanged(const QString &text)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_variableName = text.toStdString();
|
||||
_entryData->_variable = GetWeakVariableByQString(text);
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::Variable2Changed(const QString &text)
|
||||
@@ -263,7 +267,7 @@ void MacroConditionVariableEdit::Variable2Changed(const QString &text)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_variable2Name = text.toStdString();
|
||||
_entryData->_variable2 = GetWeakVariableByQString(text);
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::ConditionChanged(int value)
|
||||
|
||||
@@ -35,8 +35,8 @@ public:
|
||||
};
|
||||
|
||||
Type _type = Type::EQUALS;
|
||||
std::string _variableName = "";
|
||||
std::string _variable2Name = "";
|
||||
std::weak_ptr<Variable> _variable;
|
||||
std::weak_ptr<Variable> _variable2;
|
||||
std::string _strValue = "";
|
||||
double _numValue = 0;
|
||||
RegexConfig _regex;
|
||||
|
||||
@@ -38,7 +38,7 @@ bool MacroConditionWebsocket::CheckCondition()
|
||||
messages = &switcher->websocketMessages;
|
||||
break;
|
||||
case MacroConditionWebsocket::Type::EVENT: {
|
||||
auto connection = GetConnectionByName(_connection);
|
||||
auto connection = _connection.lock();
|
||||
if (!connection) {
|
||||
return false;
|
||||
}
|
||||
@@ -76,7 +76,8 @@ bool MacroConditionWebsocket::Save(obs_data_t *obj) const
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
_message.Save(obj, "message");
|
||||
_regex.Save(obj);
|
||||
obs_data_set_string(obj, "connection", _connection.c_str());
|
||||
obs_data_set_string(obj, "connection",
|
||||
GetWeakConnectionName(_connection).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -91,7 +92,8 @@ bool MacroConditionWebsocket::Load(obs_data_t *obj)
|
||||
_regex.CreateBackwardsCompatibleRegex(
|
||||
obs_data_get_bool(obj, "useRegex"), false);
|
||||
}
|
||||
_connection = obs_data_get_string(obj, "connection");
|
||||
_connection =
|
||||
GetWeakConnectionByName(obs_data_get_string(obj, "connection"));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -100,7 +102,7 @@ std::string MacroConditionWebsocket::GetShortDesc() const
|
||||
if (_type == Type::REQUEST) {
|
||||
return "";
|
||||
}
|
||||
return _connection;
|
||||
return GetWeakConnectionName(_connection);
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
@@ -237,7 +239,7 @@ void MacroConditionWebsocketEdit::ConnectionSelectionChanged(
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_connection = connection.toStdString();
|
||||
_entryData->_connection = GetWeakConnectionByQString(connection);
|
||||
emit(HeaderInfoChanged(connection));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
Type _type = Type::REQUEST;
|
||||
StringVariable _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
RegexConfig _regex;
|
||||
std::string _connection;
|
||||
std::weak_ptr<Connection> _connection;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
|
||||
@@ -44,6 +44,9 @@ MacroDock::MacroDock(Macro *m, QWidget *parent)
|
||||
wrapper->setFrameShadow(QFrame::Sunken);
|
||||
wrapper->setLayout(layout);
|
||||
setWidget(wrapper);
|
||||
|
||||
setFloating(true);
|
||||
hide();
|
||||
}
|
||||
|
||||
void MacroDock::SetName(const QString &name)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "macro-properties.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <obs-module.h>
|
||||
|
||||
@@ -52,8 +51,6 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setMinimumWidth(500);
|
||||
setMinimumHeight(300);
|
||||
|
||||
auto highlightOptions = new QGroupBox(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.highlightSettings"));
|
||||
@@ -70,13 +67,13 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
hotkeyLayout->addWidget(_currentMacroRegisterHotkeys);
|
||||
hotkeyOptions->setLayout(hotkeyLayout);
|
||||
|
||||
auto dockOptions = new QGroupBox(
|
||||
_dockOptions = new QGroupBox(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.dockSettings"));
|
||||
QVBoxLayout *dockLayout = new QVBoxLayout;
|
||||
dockLayout->addWidget(_currentMacroRegisterDock);
|
||||
dockLayout->addWidget(_currentMacroDockAddRunButton);
|
||||
dockLayout->addWidget(_currentMacroDockAddPauseButton);
|
||||
dockOptions->setLayout(dockLayout);
|
||||
_dockOptions->setLayout(dockLayout);
|
||||
|
||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
@@ -90,7 +87,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(highlightOptions);
|
||||
layout->addWidget(hotkeyOptions);
|
||||
layout->addWidget(dockOptions);
|
||||
layout->addWidget(_dockOptions);
|
||||
layout->addWidget(buttonbox);
|
||||
setLayout(layout);
|
||||
|
||||
@@ -100,7 +97,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys);
|
||||
if (!macro || macro->IsGroup()) {
|
||||
hotkeyOptions->hide();
|
||||
dockOptions->hide();
|
||||
_dockOptions->hide();
|
||||
return;
|
||||
}
|
||||
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
|
||||
@@ -117,6 +114,10 @@ void MacroPropertiesDialog::DockEnableChanged(int enabled)
|
||||
{
|
||||
_currentMacroDockAddRunButton->setVisible(enabled);
|
||||
_currentMacroDockAddPauseButton->setVisible(enabled);
|
||||
_dockOptions->adjustSize();
|
||||
_dockOptions->updateGeometry();
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QWidget>
|
||||
#include <QDialog>
|
||||
#include <QCheckBox>
|
||||
#include <QGroupBox>
|
||||
#include <obs-data.h>
|
||||
|
||||
class MacroProperties {
|
||||
@@ -39,4 +40,5 @@ private:
|
||||
QCheckBox *_currentMacroRegisterDock;
|
||||
QCheckBox *_currentMacroDockAddRunButton;
|
||||
QCheckBox *_currentMacroDockAddPauseButton;
|
||||
QGroupBox *_dockOptions;
|
||||
};
|
||||
|
||||
@@ -125,6 +125,13 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
|
||||
}
|
||||
|
||||
if (macros.size() == 1) {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation");
|
||||
if (!DisplayMessage(deleteWarning.arg(QString::fromStdString(
|
||||
macros.at(0)->Name())),
|
||||
true)) {
|
||||
return;
|
||||
}
|
||||
RemoveMacro(macros.at(0));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ MacroIndexToModelIndex(int realIdx,
|
||||
inCollapsedGroup = false;
|
||||
continue;
|
||||
}
|
||||
const auto &m = macros[i];
|
||||
const auto &m = macros.at(i);
|
||||
inCollapsedGroup = m->IsGroup() && m->IsCollapsed();
|
||||
groupSize = m->GroupSize();
|
||||
modelIdx++;
|
||||
@@ -256,15 +256,24 @@ MacroIndexToModelIndex(int realIdx,
|
||||
void MacroTreeModel::MoveItemBefore(const std::shared_ptr<Macro> &item,
|
||||
const std::shared_ptr<Macro> &before)
|
||||
{
|
||||
if (item == before) {
|
||||
if (!item || !before || item == before) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto modelFromEndIdx = GetItemModelIndex(item);
|
||||
auto modelFromIdx = modelFromEndIdx;
|
||||
auto modelTo = GetItemModelIndex(before);
|
||||
auto macroFrom = GetItemMacroIndex(item);
|
||||
auto macroTo = GetItemMacroIndex(before);
|
||||
int modelFromIdx = 0, modelFromEndIdx = 0, modelTo = 0, macroFrom = 0,
|
||||
macroTo = 0;
|
||||
try {
|
||||
modelFromEndIdx = GetItemModelIndex(item);
|
||||
modelFromIdx = modelFromEndIdx;
|
||||
modelTo = GetItemModelIndex(before);
|
||||
macroFrom = GetItemMacroIndex(item);
|
||||
macroTo = GetItemMacroIndex(before);
|
||||
} catch (const std::out_of_range &) {
|
||||
blog(LOG_WARNING,
|
||||
"error while trying to move item \"%s\" before \"%s\"",
|
||||
item->Name().c_str(), before->Name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (before->IsSubitem()) {
|
||||
modelTo -= before->IsCollapsed() ? 0 : before->GroupSize();
|
||||
@@ -300,15 +309,24 @@ void MacroTreeModel::MoveItemBefore(const std::shared_ptr<Macro> &item,
|
||||
void MacroTreeModel::MoveItemAfter(const std::shared_ptr<Macro> &item,
|
||||
const std::shared_ptr<Macro> &after)
|
||||
{
|
||||
if (item == after) {
|
||||
if (!item || !after || item == after) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto modelFromIdx = GetItemModelIndex(item);
|
||||
auto modelFromEndIdx = modelFromIdx;
|
||||
auto modelTo = GetItemModelIndex(after);
|
||||
auto macroFrom = GetItemMacroIndex(item);
|
||||
auto macroTo = GetItemMacroIndex(after);
|
||||
int modelFromIdx = 0, modelFromEndIdx = 0, modelTo = 0, macroFrom = 0,
|
||||
macroTo = 0;
|
||||
try {
|
||||
modelFromIdx = GetItemModelIndex(item);
|
||||
modelFromEndIdx = modelFromIdx;
|
||||
modelTo = GetItemModelIndex(after);
|
||||
macroFrom = GetItemMacroIndex(item);
|
||||
macroTo = GetItemMacroIndex(after);
|
||||
} catch (const std::out_of_range &) {
|
||||
blog(LOG_WARNING,
|
||||
"error while trying to move item \"%s\" after \"%s\"",
|
||||
item->Name().c_str(), after->Name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (after->IsGroup()) {
|
||||
modelTo += after->IsCollapsed() ? 0 : after->GroupSize();
|
||||
|
||||
@@ -267,6 +267,11 @@ void Macro::SetOnChangeHighlight()
|
||||
_onChangeTriggered = true;
|
||||
}
|
||||
|
||||
bool Macro::DockIsVisible() const
|
||||
{
|
||||
return _dock && _dockAction && _dock->isVisible();
|
||||
}
|
||||
|
||||
void Macro::SetPaused(bool pause)
|
||||
{
|
||||
if (_paused && !pause) {
|
||||
@@ -341,13 +346,7 @@ bool Macro::Save(obs_data_t *obj) const
|
||||
return true;
|
||||
}
|
||||
|
||||
obs_data_set_bool(obj, "registerDock", _registerDock);
|
||||
// The object name is used to restore the position of the dock
|
||||
if (_registerDock) {
|
||||
SetDockWidgetName();
|
||||
}
|
||||
obs_data_set_bool(obj, "dockHasRunButton", _dockHasRunButton);
|
||||
obs_data_set_bool(obj, "dockHasPauseButton", _dockHasPauseButton);
|
||||
SaveDockSettings(obj);
|
||||
|
||||
obs_data_set_bool(obj, "registerHotkeys", _registerHotkeys);
|
||||
obs_data_array_t *pauseHotkey = obs_hotkey_save(_pauseHotkey);
|
||||
@@ -438,9 +437,7 @@ bool Macro::Load(obs_data_t *obj)
|
||||
return true;
|
||||
}
|
||||
|
||||
_dockHasRunButton = obs_data_get_bool(obj, "dockHasRunButton");
|
||||
_dockHasPauseButton = obs_data_get_bool(obj, "dockHasPauseButton");
|
||||
EnableDock(obs_data_get_bool(obj, "registerDock"));
|
||||
LoadDockSettings(obj);
|
||||
|
||||
obs_data_set_default_bool(obj, "registerHotkeys", true);
|
||||
_registerHotkeys = obs_data_get_bool(obj, "registerHotkeys");
|
||||
@@ -583,22 +580,103 @@ bool Macro::PauseHotkeysEnabled()
|
||||
return _registerHotkeys;
|
||||
}
|
||||
|
||||
void Macro::SaveDockSettings(obs_data_t *obj) const
|
||||
{
|
||||
auto dockSettings = obs_data_create();
|
||||
obs_data_set_bool(dockSettings, "register", _registerDock);
|
||||
// The object name is used to restore the position of the dock
|
||||
if (_registerDock) {
|
||||
SetDockWidgetName();
|
||||
}
|
||||
obs_data_set_bool(dockSettings, "hasRunButton", _dockHasRunButton);
|
||||
obs_data_set_bool(dockSettings, "hasPauseButton", _dockHasPauseButton);
|
||||
if (_dock) {
|
||||
auto window = static_cast<QMainWindow *>(
|
||||
obs_frontend_get_main_window());
|
||||
obs_data_set_bool(dockSettings, "isFloating",
|
||||
_dock->isFloating());
|
||||
obs_data_set_bool(dockSettings, "isVisible", DockIsVisible());
|
||||
obs_data_set_int(dockSettings, "area",
|
||||
window->dockWidgetArea(_dock));
|
||||
obs_data_set_string(
|
||||
dockSettings, "geometry",
|
||||
_dock->saveGeometry().toBase64().constData());
|
||||
}
|
||||
obs_data_set_obj(obj, "dockSettings", dockSettings);
|
||||
obs_data_release(dockSettings);
|
||||
}
|
||||
|
||||
void Macro::LoadDockSettings(obs_data_t *obj)
|
||||
{
|
||||
auto dockSettings = obs_data_get_obj(obj, "dockSettings");
|
||||
if (!dockSettings) {
|
||||
// TODO: Remove this fallback
|
||||
_dockHasRunButton = obs_data_get_bool(obj, "dockHasRunButton");
|
||||
_dockHasPauseButton =
|
||||
obs_data_get_bool(obj, "dockHasPauseButton");
|
||||
EnableDock(obs_data_get_bool(obj, "registerDock"));
|
||||
return;
|
||||
}
|
||||
|
||||
const bool dockEnabled = obs_data_get_bool(dockSettings, "register");
|
||||
_dockIsVisible = obs_data_get_bool(dockSettings, "isVisible");
|
||||
if (dockEnabled) {
|
||||
_dockHasRunButton =
|
||||
obs_data_get_bool(dockSettings, "hasRunButton");
|
||||
_dockHasPauseButton =
|
||||
obs_data_get_bool(dockSettings, "hasPauseButton");
|
||||
_dockIsFloating = obs_data_get_bool(dockSettings, "isFloating");
|
||||
_dockArea = static_cast<Qt::DockWidgetArea>(
|
||||
obs_data_get_int(dockSettings, "area"));
|
||||
auto geometryStr =
|
||||
obs_data_get_string(dockSettings, "geometry");
|
||||
if (geometryStr && strlen(geometryStr)) {
|
||||
_dockGeo =
|
||||
QByteArray::fromBase64(QByteArray(geometryStr));
|
||||
}
|
||||
}
|
||||
EnableDock(dockEnabled);
|
||||
obs_data_release(dockSettings);
|
||||
}
|
||||
|
||||
void Macro::EnableDock(bool value)
|
||||
{
|
||||
if (_registerDock == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset dock regardless
|
||||
RemoveDock();
|
||||
if (!_registerDock) {
|
||||
_dock = new MacroDock(this,
|
||||
static_cast<QMainWindow *>(
|
||||
obs_frontend_get_main_window()));
|
||||
_dockAction =
|
||||
static_cast<QAction *>(obs_frontend_add_dock(_dock));
|
||||
SetDockWidgetName();
|
||||
|
||||
// Unregister dock
|
||||
if (_registerDock) {
|
||||
_dockIsFloating = true;
|
||||
_dockGeo = QByteArray();
|
||||
_registerDock = value;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new dock widget
|
||||
auto window =
|
||||
static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||
_dock = new MacroDock(this, window);
|
||||
SetDockWidgetName(); // Used by OBS to restore position
|
||||
|
||||
// Register new dock
|
||||
_dockAction = static_cast<QAction *>(obs_frontend_add_dock(_dock));
|
||||
|
||||
// Note that OBSBasic::OBSInit() has precedence over the visibility and
|
||||
// geometry set here.
|
||||
// The function calls here are only intended to attempt to restore the
|
||||
// dock status when switching scene collections.
|
||||
_dock->setVisible(_dockIsVisible);
|
||||
if (window->dockWidgetArea(_dock) != _dockArea) {
|
||||
window->addDockWidget(_dockArea, _dock);
|
||||
}
|
||||
if (_dock->isFloating() != _dockIsFloating) {
|
||||
_dock->setFloating(_dockIsFloating);
|
||||
}
|
||||
_dock->restoreGeometry(_dockGeo);
|
||||
_registerDock = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "macro-ref.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
@@ -96,7 +97,10 @@ private:
|
||||
void RunActions(bool &ret, bool ignorePause);
|
||||
void RunActions(bool ignorePause);
|
||||
void SetOnChangeHighlight();
|
||||
bool DockIsVisible() const;
|
||||
void SetDockWidgetName() const;
|
||||
void SaveDockSettings(obs_data_t *obj) const;
|
||||
void LoadDockSettings(obs_data_t *obj);
|
||||
void RemoveDock();
|
||||
|
||||
std::string _name = "";
|
||||
@@ -125,6 +129,10 @@ private:
|
||||
bool _registerDock = false;
|
||||
bool _dockHasRunButton = true;
|
||||
bool _dockHasPauseButton = true;
|
||||
bool _dockIsFloating = true;
|
||||
bool _dockIsVisible = false;
|
||||
Qt::DockWidgetArea _dockArea;
|
||||
QByteArray _dockGeo;
|
||||
MacroDock *_dock = nullptr;
|
||||
QAction *_dockAction = nullptr;
|
||||
|
||||
|
||||
@@ -147,6 +147,10 @@ cv::Mat preprocessForOCR(const QImage &image, const QColor &color)
|
||||
std::string runOCR(tesseract::TessBaseAPI *ocr, const QImage &image,
|
||||
const QColor &color)
|
||||
{
|
||||
if (image.isNull()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
#ifdef OCR_SUPPORT
|
||||
auto mat = preprocessForOCR(image, color);
|
||||
ocr->SetImage(mat.data, mat.cols, mat.rows, 1, mat.step);
|
||||
|
||||
@@ -32,7 +32,7 @@ bool PatternMatchParameters::Load(obs_data_t *obj)
|
||||
}
|
||||
useAlphaAsMask = obs_data_get_bool(data, "useAlphaAsMask");
|
||||
// TODO: Remove this fallback in a future version
|
||||
if (!obs_data_has_user_value(obj, "matchMode")) {
|
||||
if (!obs_data_has_user_value(data, "matchMode")) {
|
||||
matchMode = cv::TM_CCORR_NORMED;
|
||||
} else {
|
||||
matchMode = static_cast<cv::TemplateMatchModes>(
|
||||
|
||||
@@ -146,6 +146,9 @@ StatusDock::StatusDock(QWidget *parent) : OBSDock(parent)
|
||||
wrapper->setFrameShadow(QFrame::Sunken);
|
||||
wrapper->setLayout(layout);
|
||||
setWidget(wrapper);
|
||||
|
||||
setFloating(true);
|
||||
hide();
|
||||
}
|
||||
|
||||
void SetupDock()
|
||||
|
||||
@@ -106,6 +106,9 @@ struct SwitcherData {
|
||||
|
||||
int interval = default_interval;
|
||||
|
||||
QStringList loadFailureLibs;
|
||||
bool warnPluginLoadFailure = true;
|
||||
|
||||
obs_source_t *waitScene = nullptr;
|
||||
OBSWeakSource currentScene = nullptr;
|
||||
OBSWeakSource previousScene = nullptr;
|
||||
|
||||
@@ -133,6 +133,32 @@ Connection *GetConnectionByName(const std::string &name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::weak_ptr<Connection> GetWeakConnectionByName(const std::string &name)
|
||||
{
|
||||
for (const auto &c : switcher->connections) {
|
||||
if (c->Name() == name) {
|
||||
std::weak_ptr<Connection> wp =
|
||||
std::dynamic_pointer_cast<Connection>(c);
|
||||
return wp;
|
||||
}
|
||||
}
|
||||
return std::weak_ptr<Connection>();
|
||||
}
|
||||
|
||||
std::weak_ptr<Connection> GetWeakConnectionByQString(const QString &name)
|
||||
{
|
||||
return GetWeakConnectionByName(name.toStdString());
|
||||
}
|
||||
|
||||
std::string GetWeakConnectionName(std::weak_ptr<Connection> connection)
|
||||
{
|
||||
auto con = connection.lock();
|
||||
if (!con) {
|
||||
return "invalid connection selection";
|
||||
}
|
||||
return con->Name();
|
||||
}
|
||||
|
||||
bool ConnectionNameAvailable(const QString &name)
|
||||
{
|
||||
return !GetConnectionByName(name);
|
||||
@@ -187,6 +213,18 @@ void ConnectionSelection::SetConnection(const std::string &con)
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSelection::SetConnection(
|
||||
const std::weak_ptr<Connection> &connection_)
|
||||
{
|
||||
const QSignalBlocker blocker(_selection);
|
||||
auto connection = connection_.lock();
|
||||
if (connection) {
|
||||
SetConnection(connection->Name());
|
||||
} else {
|
||||
_selection->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
||||
const Connection &settings)
|
||||
: ItemSettingsDialog(settings, switcher->connections,
|
||||
|
||||
@@ -56,6 +56,9 @@ private:
|
||||
|
||||
Connection *GetConnectionByName(const QString &);
|
||||
Connection *GetConnectionByName(const std::string &);
|
||||
std::weak_ptr<Connection> GetWeakConnectionByName(const std::string &name);
|
||||
std::weak_ptr<Connection> GetWeakConnectionByQString(const QString &name);
|
||||
std::string GetWeakConnectionName(std::weak_ptr<Connection>);
|
||||
|
||||
class ConnectionSettingsDialog : public ItemSettingsDialog {
|
||||
Q_OBJECT
|
||||
@@ -92,4 +95,5 @@ class ConnectionSelection : public ItemSelection {
|
||||
public:
|
||||
ConnectionSelection(QWidget *parent = 0);
|
||||
void SetConnection(const std::string &);
|
||||
void SetConnection(const std::weak_ptr<Connection> &);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#include "math-helpers.hpp"
|
||||
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cmath>
|
||||
#include <exprtk.hpp>
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
const char *obs_module_text(const char *text)
|
||||
@@ -15,313 +10,21 @@ const char *obs_module_text(const char *text)
|
||||
#include <obs-module.h>
|
||||
#endif
|
||||
|
||||
static std::vector<std::string_view> nonOperatorSeparators = {
|
||||
" ",
|
||||
"\t",
|
||||
};
|
||||
typedef exprtk::expression<double> expression_t;
|
||||
typedef exprtk::parser<double> parser_t;
|
||||
|
||||
static std::vector<std::string_view> brackets = {
|
||||
"(",
|
||||
")",
|
||||
};
|
||||
|
||||
static std::vector<std::string_view> unaryOperators = {
|
||||
"abs(", "sin(", "cos(", "tan(", "sqrt(",
|
||||
};
|
||||
|
||||
static std::vector<std::string_view> binaryOperators = {
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
};
|
||||
|
||||
static bool isBracket(const std::string &token)
|
||||
std::variant<double, std::string> EvalMathExpression(const std::string &expr)
|
||||
{
|
||||
return std::find(brackets.begin(), brackets.end(), token) !=
|
||||
brackets.end();
|
||||
}
|
||||
expression_t expression;
|
||||
|
||||
static bool isUnaryOperator(const std::string &token)
|
||||
{
|
||||
return std::find(unaryOperators.begin(), unaryOperators.end(), token) !=
|
||||
unaryOperators.end();
|
||||
}
|
||||
parser_t parser;
|
||||
|
||||
static bool isBinaryOperator(const std::string &token)
|
||||
{
|
||||
return std::find(binaryOperators.begin(), binaryOperators.end(),
|
||||
token) != binaryOperators.end();
|
||||
}
|
||||
|
||||
static bool isOperator(const std::string &token)
|
||||
{
|
||||
return isUnaryOperator(token) || isBinaryOperator(token);
|
||||
}
|
||||
|
||||
static void addOperatorToken(std::vector<std::string> &tokens,
|
||||
const std::string &separator)
|
||||
{
|
||||
if (separator.back() != '(') {
|
||||
tokens.push_back(separator);
|
||||
return;
|
||||
if (parser.compile(expr, expression)) {
|
||||
return expression.value();
|
||||
}
|
||||
tokens.push_back("(");
|
||||
tokens.push_back(separator);
|
||||
}
|
||||
|
||||
static std::vector<std::string> splitStringIntoTokens(const std::string &input)
|
||||
{
|
||||
auto separators = nonOperatorSeparators;
|
||||
separators.insert(separators.end(), brackets.begin(), brackets.end());
|
||||
separators.insert(separators.end(), unaryOperators.begin(),
|
||||
unaryOperators.end());
|
||||
separators.insert(separators.end(), binaryOperators.begin(),
|
||||
binaryOperators.end());
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
std::string::size_type start = 0;
|
||||
|
||||
while (start < input.length()) {
|
||||
std::string::size_type min_pos = std::string::npos;
|
||||
std::string separator;
|
||||
|
||||
// Find the next occurrence of each separator
|
||||
for (const auto &sep : separators) {
|
||||
std::string::size_type pos = input.find(sep, start);
|
||||
if (pos != std::string::npos && pos < min_pos) {
|
||||
min_pos = pos;
|
||||
separator = sep;
|
||||
}
|
||||
}
|
||||
|
||||
// If a separator was found, add the token before it to the vector
|
||||
if (min_pos != std::string::npos) {
|
||||
const auto token = input.substr(start, min_pos - start);
|
||||
start = min_pos + separator.length();
|
||||
|
||||
if (!std::all_of(token.begin(), token.end(), isspace)) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
// If the separator itself was an operator, add it as a
|
||||
// token to the vector
|
||||
if (isOperator(separator)) {
|
||||
addOperatorToken(tokens, separator);
|
||||
}
|
||||
if (isBracket(separator)) {
|
||||
tokens.push_back(separator);
|
||||
}
|
||||
}
|
||||
// Otherwise, add the remaining string to the vector and exit the
|
||||
// loop
|
||||
else {
|
||||
tokens.push_back(input.substr(start));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
static int precedence(const std::string &op)
|
||||
{
|
||||
if (op == "*" || op == "/") {
|
||||
return 2;
|
||||
} else if (op == "+" || op == "-") {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getErrorMsg(const std::string &expr, const std::string &msg)
|
||||
{
|
||||
return std::string(obs_module_text(
|
||||
"AdvSceneSwitcher.math.expressionFail")) +
|
||||
" \"" + expr + "\":\n" + msg;
|
||||
}
|
||||
|
||||
static std::string getErrorMsg(const std::string &expr,
|
||||
std::stack<std::string> binaryOperators,
|
||||
std::stack<double> operands,
|
||||
const std::string &msg)
|
||||
{
|
||||
std::string errMsg = getErrorMsg(expr, msg);
|
||||
errMsg += "\n\n ---" +
|
||||
std::string(
|
||||
obs_module_text("AdvSceneSwitcher.math.operators")) +
|
||||
" ---\n";
|
||||
while (!binaryOperators.empty()) {
|
||||
errMsg += binaryOperators.top() + "\n";
|
||||
binaryOperators.pop();
|
||||
}
|
||||
|
||||
errMsg +=
|
||||
"\n\n --- " +
|
||||
std::string(obs_module_text("AdvSceneSwitcher.math.operands")) +
|
||||
" ---\n";
|
||||
while (!operands.empty()) {
|
||||
errMsg += std::to_string(operands.top()) + "\n";
|
||||
operands.pop();
|
||||
}
|
||||
|
||||
return errMsg;
|
||||
}
|
||||
|
||||
static bool evalHelper(std::stack<std::string> &operators,
|
||||
std::stack<double> &operands)
|
||||
{
|
||||
std::string op = operators.top();
|
||||
operators.pop();
|
||||
|
||||
if (isUnaryOperator(op)) {
|
||||
if (operands.size() < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double value = operands.top();
|
||||
operands.pop();
|
||||
double result;
|
||||
if (op == "abs(") {
|
||||
result = abs(value);
|
||||
} else if (op == "sin(") {
|
||||
result = sin(value);
|
||||
} else if (op == "cos(") {
|
||||
result = cos(value);
|
||||
} else if (op == "tan(") {
|
||||
result = cos(value);
|
||||
} else if (op == "sqrt(") {
|
||||
result = sqrt(value);
|
||||
}
|
||||
operands.push(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (operands.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
double op2 = operands.top();
|
||||
operands.pop();
|
||||
double op1 = operands.top();
|
||||
operands.pop();
|
||||
double result;
|
||||
|
||||
if (op == "+") {
|
||||
result = op1 + op2;
|
||||
} else if (op == "-") {
|
||||
result = op1 - op2;
|
||||
} else if (op == "*") {
|
||||
result = op1 * op2;
|
||||
} else if (op == "/") {
|
||||
result = op1 / op2;
|
||||
}
|
||||
operands.push(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::variant<double, std::string>
|
||||
EvalMathExpression(const std::string &expression)
|
||||
{
|
||||
// Create a stack to store operands and operators
|
||||
std::stack<double> operands;
|
||||
std::stack<std::string> operators;
|
||||
|
||||
auto tokens = splitStringIntoTokens(expression);
|
||||
|
||||
// Loop through each token in the expression
|
||||
for (const auto &token : tokens) {
|
||||
// If the token is a number, push it onto the operand stack
|
||||
if (isdigit(token[0])) {
|
||||
auto operand = GetDouble(token);
|
||||
if (!operand.has_value()) {
|
||||
return getErrorMsg(
|
||||
expression,
|
||||
"\"" + token + "\" " +
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.math.notANumber"));
|
||||
}
|
||||
operands.push(*operand);
|
||||
}
|
||||
// If the token is an operator, evaluate higher-precedence
|
||||
// operators first
|
||||
else if (isBinaryOperator(token)) {
|
||||
while (!operators.empty() &&
|
||||
precedence(operators.top()) >=
|
||||
precedence(token)) {
|
||||
if (operators.empty() || operands.empty()) {
|
||||
return getErrorMsg(expression,
|
||||
operators, operands,
|
||||
"");
|
||||
}
|
||||
if (!evalHelper(operators, operands)) {
|
||||
return getErrorMsg(expression,
|
||||
operators, operands,
|
||||
"");
|
||||
}
|
||||
}
|
||||
operators.push(token);
|
||||
}
|
||||
// If the token is an opening bracket, push it onto the
|
||||
// operator stack
|
||||
else if (token == "(") {
|
||||
operators.push(token);
|
||||
}
|
||||
// If the token is a closing bracket, evaluate the expression
|
||||
// inside the brackets
|
||||
else if (token == ")") {
|
||||
if (operators.empty()) {
|
||||
return getErrorMsg(
|
||||
expression, operators, operands,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.math.expressionFailParentheses"));
|
||||
}
|
||||
while (operators.top() != "(") {
|
||||
if (operators.empty() || operands.empty()) {
|
||||
return getErrorMsg(expression,
|
||||
operators, operands,
|
||||
"");
|
||||
}
|
||||
if (!evalHelper(operators, operands) ||
|
||||
operators.empty()) {
|
||||
return getErrorMsg(
|
||||
expression, operators, operands,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.math.expressionFailParentheses"));
|
||||
}
|
||||
}
|
||||
operators.pop(); // Pop the opening bracket
|
||||
|
||||
}
|
||||
|
||||
else if (isUnaryOperator(token)) {
|
||||
operators.push(token);
|
||||
}
|
||||
|
||||
else if (!token.empty()) {
|
||||
return getErrorMsg(
|
||||
expression,
|
||||
std::string(obs_module_text(
|
||||
"AdvSceneSwitcher.math.invalidToken")) +
|
||||
" \"" + token + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate any remaining operators in the stack
|
||||
while (!operators.empty()) {
|
||||
if (!evalHelper(operators, operands) || operands.empty()) {
|
||||
return getErrorMsg(expression, operators, operands, "");
|
||||
}
|
||||
}
|
||||
|
||||
// The result is the top operand on the stack
|
||||
if (operands.size() != 1) {
|
||||
return getErrorMsg(
|
||||
expression, operators, operands,
|
||||
std::string(obs_module_text(
|
||||
"AdvSceneSwitcher.math.notFullyResolved")));
|
||||
}
|
||||
return operands.top();
|
||||
" \"" + expr;
|
||||
}
|
||||
|
||||
bool IsValidNumber(const std::string &str)
|
||||
|
||||
@@ -11,8 +11,10 @@ target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE "${ADVSS_SOURCE_DIR}/src" "${ADVSS_SOURCE_DIR}/src/legacy"
|
||||
"${ADVSS_SOURCE_DIR}/src/macro-core" "${ADVSS_SOURCE_DIR}/src/utils"
|
||||
"${ADVSS_SOURCE_DIR}/forms")
|
||||
install(TARGETS advanced-scene-switcher-lib
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
"${ADVSS_SOURCE_DIR}/forms" "${ADVSS_SOURCE_DIR}/deps/exprtk")
|
||||
if(MSVC)
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267
|
||||
/bigobj)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
@@ -11,6 +11,12 @@ TEST_CASE("Expressions are evaluated successfully", "[math-helpers]")
|
||||
REQUIRE(doubleValuePtr != nullptr);
|
||||
REQUIRE(*doubleValuePtr == 1.0);
|
||||
|
||||
expressionResult = EvalMathExpression("-1");
|
||||
doubleValuePtr = std::get_if<double>(&expressionResult);
|
||||
|
||||
REQUIRE(doubleValuePtr != nullptr);
|
||||
REQUIRE(*doubleValuePtr == -1.0);
|
||||
|
||||
expressionResult = EvalMathExpression("1 + 2");
|
||||
doubleValuePtr = std::get_if<double>(&expressionResult);
|
||||
|
||||
@@ -29,8 +35,27 @@ TEST_CASE("Expressions are evaluated successfully", "[math-helpers]")
|
||||
REQUIRE(doubleValuePtr != nullptr);
|
||||
REQUIRE(*doubleValuePtr == 9.0);
|
||||
|
||||
expressionResult = EvalMathExpression("(1 - 2) * 3");
|
||||
doubleValuePtr = std::get_if<double>(&expressionResult);
|
||||
|
||||
REQUIRE(doubleValuePtr != nullptr);
|
||||
REQUIRE(*doubleValuePtr == -3.0);
|
||||
|
||||
expressionResult = EvalMathExpression("(1-2)*3");
|
||||
doubleValuePtr = std::get_if<double>(&expressionResult);
|
||||
|
||||
REQUIRE(doubleValuePtr != nullptr);
|
||||
REQUIRE(*doubleValuePtr == -3.0);
|
||||
|
||||
expressionResult =
|
||||
EvalMathExpression("cos(abs(1 - sqrt(10 - 2 * 3)) -1)");
|
||||
EvalMathExpression("cos(abs(1 - sqrt(10 - 2 * 3)) - 1)");
|
||||
doubleValuePtr = std::get_if<double>(&expressionResult);
|
||||
|
||||
REQUIRE(doubleValuePtr != nullptr);
|
||||
REQUIRE(*doubleValuePtr == 1.0);
|
||||
|
||||
expressionResult =
|
||||
EvalMathExpression("cos(abs(1 - sqrt(10 -2 * 3)) -1)");
|
||||
doubleValuePtr = std::get_if<double>(&expressionResult);
|
||||
|
||||
REQUIRE(doubleValuePtr != nullptr);
|
||||
|
||||
Reference in New Issue
Block a user