Compare commits

...

59 Commits

Author SHA1 Message Date
WarmUpTill
01437183ac Fix checkbox icon of groups in macro tree not being set on OBS 30.1.2
This change is necessary because of
https://github.com/obsproject/obs-studio/pull/9584
2024-04-07 20:23:54 +02:00
WarmUpTill
e5ab2ceca3 Fix build issue with GCC 13.2.0 and clang-17 2024-04-04 20:49:10 +02:00
WarmUpTill
e19f4ddf7c Fix variable deletion via variable tab not updating variable selections
The order of operations was incorrect.
First the signal that a variable was removed was propagated before the
variable was actually selected.
So e.g. scene item selections would receive the signal that a variable
was deleted but when repopulating the scene item selection the variable
still exists resulting in the list of available entries not to change.
2024-04-02 17:59:17 +02:00
WarmUpTill
4e3a062084 Only insert enabled actions to action queues 2024-04-02 17:59:17 +02:00
WarmUpTill
96db0ad507 Fix crash when stopping queue from within the queue 2024-04-02 17:59:17 +02:00
WarmUpTill
6b4bcc074a Add option to press buttons in the properties dialog of filters 2024-04-02 17:59:17 +02:00
WarmUpTill
14dd390680 Add option to truncate or pad variable value 2024-04-02 17:59:17 +02:00
WarmUpTill
3fe8ea8961 Fix timer condition not properly using random values 2024-04-02 17:59:17 +02:00
WarmUpTill
3ee913ea98 Add option to enable or disable sources on audio tracks 2024-04-02 17:59:17 +02:00
WarmUpTill
d18277653c Resolve settings groups in source selection 2024-04-02 17:59:17 +02:00
WarmUpTill
8c478cc330 Add control description to source selection 2024-04-02 17:59:17 +02:00
WarmUpTill
003ffc161b Show warning if system tray is disabled in OBS settings 2024-04-02 17:59:17 +02:00
Przemek Pawlas
74dc0c871b Add Run action/condition temp vars 2024-03-24 14:44:24 +01:00
WarmUpTill
c8545dea4d Fix crash when setting variable to invalid environment variable 2024-03-22 21:55:47 +01:00
WarmUpTill
e1b8edd444 Improve macro name conflict error messages 2024-03-22 21:55:47 +01:00
WarmUpTill
f3996855c3 Add example OBS websocket message 2024-03-22 21:55:47 +01:00
WarmUpTill
ea17e8ea08 Add tooltips 2024-03-17 12:50:12 +01:00
WarmUpTill
00fc313609 Add clipboard condition type 2024-03-15 22:00:57 +01:00
Przemek Pawlas
c72f9abc2b Add pre-commit hooks 2024-03-15 21:27:08 +01:00
Przemek Pawlas
6df818503e Add macro option to stop and rerun actions 2024-03-15 21:27:08 +01:00
WarmUpTill
b17a6cc109 Formatting changes 2024-03-12 21:56:25 +01:00
WarmUpTill
82d1aed472 CI: Fix clang format check using incorrect folder 2024-03-12 21:56:25 +01:00
WarmUpTill
eb6fc6c140 CI: Skip updating of git binaries
This seems to break the CI step "Install macOS build requirements" as
python is attempted to be installed as a dependency of git for which a
symlink cannot be created successfully.
2024-03-12 21:56:25 +01:00
Przemek Pawlas
2710406f5f Add QString arg checking to locale CI script 2024-03-10 13:48:57 +01:00
Przemek Pawlas
a634499423 Fix some locale inconsistencies 2024-03-10 13:48:57 +01:00
Przemek Pawlas
71b3fbaa19 Error CI if other languages have redundant extras 2024-03-10 13:48:57 +01:00
Przemek Pawlas
c656bb4571 Fix invalid JSON storage of source/filter double values 2024-03-10 13:48:57 +01:00
Przemek Pawlas
6c4e4b8cd8 Don't show 2 warning popups for groups with macros 2024-03-10 13:48:57 +01:00
Przemek Pawlas
7b1a256f8a Add Del/F2 keyboard shortcuts for macro and var removal/renames 2024-03-10 13:48:57 +01:00
Przemek Pawlas
66f9eaf8c2 Exclude *.orig files in .gitignore 2024-03-10 13:48:57 +01:00
Przemek Pawlas
7fa0ba3355 Add change count to var tab tooltip 2024-03-10 13:48:57 +01:00
Przemek Pawlas
eec9244e4c Variable related refactors 2024-03-10 13:48:57 +01:00
Przemek Pawlas
e265e4828e Add tooltip with full var value to variable tab 2024-03-10 13:48:57 +01:00
Przemek Pawlas
ad6e720912 Add last changed column to variable tab 2024-03-10 13:48:57 +01:00
WarmUpTill
70dad8ebb4 Handle MIDI device disconnect and reconnect 2024-03-09 12:00:52 +01:00
WarmUpTill
497b3b3e04 Clean up macro import and add action queues 2024-03-09 12:00:52 +01:00
WarmUpTill
891811aa47 Convert audio volume thresholds to new format 2024-03-09 09:18:29 +01:00
WarmUpTill
be9ddaa4c9 Increase precision of percent based volume threshold input 2024-03-09 09:18:29 +01:00
WarmUpTill
03494c2915 Adapt to volume widget slider changes 2024-03-09 09:18:29 +01:00
WarmUpTill
bcf7c247bf Increase precision of volume widget slider 2024-03-09 09:18:29 +01:00
WarmUpTill
132d89b350 Fix virtual camera action selection causing a crash
The Create() and Copy() functions were not correctly implemented and
returned a nullptr
2024-03-06 12:53:43 +01:00
WarmUpTill
daf16357c9 Fix output volume check of audio condition not working
Only the percent based selection was not working properly since the
calculation was off by a factor of 100.
2024-03-03 20:54:15 +01:00
WarmUpTill
8080abc93c Fix crash when opening setting window 2024-03-03 20:54:15 +01:00
WarmUpTill
902715bd3e Save settings when switching to new macro
This should ensure that settings are saved more frequently and less data
is lost in case the plugin crashes with the settings dialog opened.
2024-03-03 20:54:15 +01:00
WarmUpTill
8271a6e8db CI: Don't attempt to rename files if not required 2024-03-02 16:38:24 +01:00
WarmUpTill
6c121a093b Add "Log" action
This action will allow you to write custom messages to the OBS log
2024-03-02 15:27:02 +01:00
WarmUpTill
ab0cce9eaa Add log level which prints performed actions 2024-03-02 15:27:02 +01:00
WarmUpTill
9acbead87b Update BUILDING.md 2024-03-02 15:27:02 +01:00
WarmUpTill
03e71e9183 Add support to resolve variables on action queue add 2024-03-02 15:27:02 +01:00
WarmUpTill
f8dadd38b4 Add ResolveVariables()
Settings will be set to fixed values based on the values of the
variables used.
2024-03-02 15:27:02 +01:00
WarmUpTill
2928775f5b Add option to resolve variables on queue add 2024-03-02 15:27:02 +01:00
WarmUpTill
673ac8078b Update lastVariableChange when using settings dialog to change value 2024-03-02 15:27:02 +01:00
WarmUpTill
234ae27205 Fix crash when running RunPostLoadSteps()
RunPostLoadSteps() could be called with "stale" post load steps as
RunPostLoadSteps() was never called when a copy of an action or
condition was created
2024-03-02 15:27:02 +01:00
WarmUpTill
a740a29798 Cleanup includes 2024-03-02 15:27:02 +01:00
WarmUpTill
341a58b8e4 Add window action
It allows to set the focus window and close windows.
Only supported on Windows for now.
2024-03-02 14:03:00 +01:00
Michael Kirsch
ba9a16e718 Force LF for formatter script for WSL 2024-02-29 20:31:50 +01:00
WarmUpTill
2b2bab8fbc Backup setting on exit to plugin config dir
Only one version of the settings file will be kept per scene collection
and plugin version combination
2024-02-28 12:53:02 +01:00
WarmUpTill
f50f31ec4c Allow stop of current macro when using run button 2024-02-26 20:56:26 +01:00
WarmUpTill
3a1c353ea3 Use git tag as version identifier in package scripts 2024-02-24 22:51:24 +01:00
200 changed files with 4061 additions and 1028 deletions

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
/build-aux/*.zsh eol=lf
/build-aux/.functions/* eol=lf

View File

@@ -1,6 +1,5 @@
brew "ccache" brew "ccache"
brew "coreutils" brew "coreutils"
brew "cmake" brew "cmake"
brew "git"
brew "jq" brew "jq"
brew "xcbeautify" brew "xcbeautify"

View File

@@ -163,9 +163,13 @@ ${_usage_host:-}"
local product_name local product_name
local product_version local product_version
local git_tag="$(git describe --tags)"
read -r product_name product_version <<< \ read -r product_name product_version <<< \
"$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})" "$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})"
log_info "Using git tag as version identifier '${git_tag}'"
product_version="${git_tag}"
if [[ ${host_os} == macos ]] { if [[ ${host_os} == macos ]] {
autoload -Uz check_packages read_codesign read_codesign_installer read_codesign_pass autoload -Uz check_packages read_codesign read_codesign_installer read_codesign_pass
@@ -247,15 +251,21 @@ ${_usage_host:-}"
cmake --build build_${target##*-} --config ${config} -t package_source ${cmake_args} cmake --build build_${target##*-} --config ${config} -t package_source ${cmake_args}
popd popd
local output_name="${product_name}-${product_version}-${target##*-}-linux-gnu"
if (( package )) { if (( package )) {
log_group "Packaging ${product_name}..." log_group "Packaging ${product_name}..."
pushd ${project_root} pushd ${project_root}
cmake --build build_${target##*-} --config ${config} -t package ${cmake_args} cmake --build build_${target##*-} --config ${config} -t package ${cmake_args}
if [ ! -e ${project_root}/release/${output_name}.deb ]; then
mv ${project_root}/release/*.deb ${project_root}/release/${output_name}.deb
mv ${project_root}/release/*.ddeb ${project_root}/release/${output_name}.ddeb
fi
popd popd
} }
log_group "Archiving ${product_name}..." log_group "Archiving ${product_name}..."
local output_name="${product_name}-${product_version}-${target##*-}-linux-gnu"
local _tarflags='cJf' local _tarflags='cJf'
if (( _loglevel > 1 || ${+CI} )) _tarflags="v${_tarflags}" if (( _loglevel > 1 || ${+CI} )) _tarflags="v${_tarflags}"

View File

@@ -48,6 +48,10 @@ function Package {
$ProductName = $BuildSpec.name $ProductName = $BuildSpec.name
$ProductVersion = $BuildSpec.version $ProductVersion = $BuildSpec.version
$GitOutput = git describe --tags
Log-Information "Using git tag as version identifier '${GitOutput}'"
$ProductVersion = $GitOutput
$OutputName = "${ProductName}-${ProductVersion}-windows-${Target}" $OutputName = "${ProductName}-${ProductVersion}-windows-${Target}"
if ( ! $SkipDeps ) { if ( ! $SkipDeps ) {

0
.github/test vendored Normal file
View File

5
.gitignore vendored
View File

@@ -14,7 +14,9 @@
!/tests !/tests
!.clang-format !.clang-format
!.cmake-format.json !.cmake-format.json
!.gitattributes
!.gitignore !.gitignore
!.pre-commit-config.yaml
!BUILDING.md !BUILDING.md
!buildspec.json !buildspec.json
!CMakeLists.txt !CMakeLists.txt
@@ -22,6 +24,9 @@
!LICENSE !LICENSE
!README.md !README.md
# Exclude .orig leftovers
*.orig
# Exclude lock files # Exclude lock files
*.lock.json *.lock.json

24
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,24 @@
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v16.0.6
hooks:
- id: clang-format
args: [-fallback-style=none]
types_or: [c++, c]
exclude: |
(?x)^(
deps/.*|
tests/catch\.hpp|
.*\.mm
)$
- repo: https://github.com/cheshirekow/cmake-format-precommit
rev: v0.6.13
hooks:
- id: cmake-format
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.2
hooks:
- id: ruff-format
types_or: [python]

View File

@@ -4,11 +4,13 @@ You have the option to ...
- either add the plugin to the OBS source tree directly and build the plugin while building OBS itself. (**in tree**) - either add the plugin to the OBS source tree directly and build the plugin while building OBS itself. (**in tree**)
- or you can move the sources of this plugin outside of the OBS source tree and build it separately from OBS. (**out of tree**) - or you can move the sources of this plugin outside of the OBS source tree and build it separately from OBS. (**out of tree**)
As both methods require you to have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio) and [CMake](https://cmake.org/download/) it is recommended to build the plugin in tree as it is easier to set up and will enable straightforward debugging. Both methods require [CMake](https://cmake.org/download/).
The plugin can be compiled for OBS 25 and above, although using the latest version of OBS is recommended to support all features. The plugin can be compiled for OBS 27 and above, although using the latest version of OBS is recommended to support all features.
## Compiling in tree (recommended for development) ## Compiling in tree (recommended for development)
This section assumes that you have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio).
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/: Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
``` ```
cd obs-studio/UI/frontend-plugins/ cd obs-studio/UI/frontend-plugins/
@@ -46,12 +48,12 @@ cmake -DOpenCV_DIR="C:/Users/BuildUser/Documents/OBS/opencv/build/" -DLeptonica_
First you will need to clone the plugin sources by running the following command: First you will need to clone the plugin sources by running the following command:
``` ```
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
cd SceneSwitcher
``` ```
Next you will need [CMake](https://cmake.org/download/) and run the command with suitable arguments for your particular platform. Next you will need [CMake](https://cmake.org/download/) and run it with suitable arguments for your particular platform.
For example, on Windows you might want to run this command: For example, on Windows you might want to run this command:
``` ```
cd SceneSwitcher
cmake --preset windows-x64 cmake --preset windows-x64
``` ```
Next, you can build the plugin and install the files into a folder named release using the following commands: Next, you can build the plugin and install the files into a folder named release using the following commands:
@@ -72,7 +74,7 @@ cd SceneSwitcher
cmake -DOpenCV_DIR="C:/Users/BuildUser/Documents/OBS/opencv/build/" -DLeptonica_DIR="C:/Users/BuildUser/Documents/OBS/leptonica/build" -DTesseract_DIR="C:/Users/BuildUser/Documents/OBS/tesseract/build/lib/cmake/tesseract" --preset windows-x64 cmake -DOpenCV_DIR="C:/Users/BuildUser/Documents/OBS/opencv/build/" -DLeptonica_DIR="C:/Users/BuildUser/Documents/OBS/leptonica/build" -DTesseract_DIR="C:/Users/BuildUser/Documents/OBS/tesseract/build/lib/cmake/tesseract" --preset windows-x64
``` ```
You can rely on the CI scripts to build the dependencies for you, although it is not guaranteed that they will work in every environment: You can rely on the CI scripts to build the dependencies for you, although it is not guaranteed that they will function in every environment:
| Platform | Command | | Platform | Command |
| ----------- | ----------- | | ----------- | ----------- |
@@ -83,10 +85,10 @@ You can rely on the CI scripts to build the dependencies for you, although it is
# Contributing # Contributing
Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out. Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out!
If you would like to expand upon the macro system by adding a new condition or action type have a look at the examples in `plugins/base`. If you would like to expand upon the macro system by adding a new condition or action type have a look at the examples in `plugins/base`.
In general changes in the `lib/legacy` folder should be avoided. In general changes in the `lib/legacy` folder should be avoided, if possible.
## Macro condition ## Macro condition
Macro conditions should inherit from the `MacroCondition` class and must implement the following functions: Macro conditions should inherit from the `MacroCondition` class and must implement the following functions:
@@ -97,12 +99,12 @@ public:
MacroConditionExample(Macro *m) : MacroCondition(m) {} MacroConditionExample(Macro *m) : MacroCondition(m) {}
// This function should perform the condition check // This function should perform the condition check
bool CheckCondition(); bool CheckCondition();
// This function should store the required condition data to "obj" // This function should store the required condition data to "data"
// For example called on OBS shutdown // For example called on OBS shutdown
bool Save(obs_data_t *obj); bool Save(obs_data_t *data);
// This function should load the condition data from "obj" // This function should load the condition data from "data"
// For example called on OBS startup // For example called on OBS startup
bool Load(obs_data_t *obj); bool Load(obs_data_t *data);
// This function should return a unique id for this condition type // This function should return a unique id for this condition type
// The _id is defined below // The _id is defined below
std::string GetId() { return _id; }; std::string GetId() { return _id; };
@@ -120,7 +122,7 @@ private:
static const std::string _id; static const std::string _id;
}; };
``` ```
When defining the widget used to control the settings of the condition type, it is important to add a static `Create()` method. When defining the widget used to control the settings of the condition type, a static `Create()` method is required.
It will be called whenever a new condition MacroConditionExample is created. (See `MacroConditionFactory::Register()`) It will be called whenever a new condition MacroConditionExample is created. (See `MacroConditionFactory::Register()`)
``` ```
class MacroConditionExampleEdit : public QWidget { class MacroConditionExampleEdit : public QWidget {
@@ -151,7 +153,7 @@ bool MacroConditionExample::_registered = MacroConditionFactory::Register(
MacroConditionExample::id, // Unique string identifying this condition type MacroConditionExample::id, // Unique string identifying this condition type
{ {
MacroConditionExample::Create, // Function called to create the object performing the condition MacroConditionExample::Create, // Function called to create the object performing the condition
MacroConditionExampleEdit::Create, // Function called to create the widget configure the condition MacroConditionExampleEdit::Create, // Function called to create the widget to configure the settings
"AdvSceneSwitcher.condition.example", // User facing name of the condition type (will be translated) "AdvSceneSwitcher.condition.example", // User facing name of the condition type (will be translated)
true // Condition type supports duration modifiers (default true) true // Condition type supports duration modifiers (default true)
} }
@@ -167,16 +169,18 @@ The differences are highlighted in the comments below.
class MacroActionExample : public MacroAction { class MacroActionExample : public MacroAction {
public: public:
MacroActionExample(Macro *m) : MacroAction(m) {} MacroActionExample(Macro *m) : MacroAction(m) {}
static std::shared_ptr<MacroAction> Create(Macro *m);
// Used to create a copy of the action (used for action queues)
std::shared_ptr<MacroAction> Copy() const;
// This function should perform the action // This function should perform the action
// If false is returned the macro will aborted // If false is returned the macro will aborted
bool PerformAction(); bool PerformAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *data);
bool Load(obs_data_t *obj); bool Load(obs_data_t *data);
std::string GetId() { return _id; }; std::string GetId() { return _id; };
static std::shared_ptr<MacroAction> Create(Macro *m) // Optional:
{ // Might be called when action is inserted to action queue
return std::make_shared<MacroActionExample>(m); void ResolveVariablesToFixedValues();
}
private: private:
static bool _registered; static bool _registered;
@@ -207,11 +211,11 @@ MacroActionFactory::Register(
MacroActionExample::id, // Unique string identifying this action type MacroActionExample::id, // Unique string identifying this action type
{ {
MacroActionExample::Create, // Function called to create the object performing the action MacroActionExample::Create, // Function called to create the object performing the action
MacroActionExampleEdit::Create, // Function called to create the widget configure the action MacroActionExampleEdit::Create, // Function called to create the widget to configure the settings
"AdvSceneSwitcher.action.example" // User facing name of the action type "AdvSceneSwitcher.action.example" // User facing name of the action type
} }
); );
``` ```
## External dependencies ## External dependencies
If your intention is to add macro functionality which depends on external libraries, which is likely not to exist on all user setups, have a look at the folders in the `plugins/` directory, which are not named base. If your intention is to add macro functionality which depends on external libraries, which might not exist on all user environments, have a look at the folders in the `plugins/` directory, which are not named `base`.
For example `plugins/video` will only be loaded if the `OpenCV` dependencies are met. For example, `plugins/video` will only be loaded if the `OpenCV` dependencies are met.

View File

@@ -147,8 +147,12 @@ target_sources(
${LIB_NAME} ${LIB_NAME}
PRIVATE lib/utils/action-queue.cpp PRIVATE lib/utils/action-queue.cpp
lib/utils/action-queue.hpp lib/utils/action-queue.hpp
lib/utils/backup.cpp
lib/utils/backup.hpp
lib/utils/curl-helper.cpp lib/utils/curl-helper.cpp
lib/utils/curl-helper.hpp lib/utils/curl-helper.hpp
lib/utils/double-slider.cpp
lib/utils/double-slider.hpp
lib/utils/duration-control.cpp lib/utils/duration-control.cpp
lib/utils/duration-control.hpp lib/utils/duration-control.hpp
lib/utils/duration.cpp lib/utils/duration.cpp
@@ -197,6 +201,8 @@ target_sources(
lib/utils/section.hpp lib/utils/section.hpp
lib/utils/selection-helpers.cpp lib/utils/selection-helpers.cpp
lib/utils/selection-helpers.hpp lib/utils/selection-helpers.hpp
lib/utils/single-char-selection.cpp
lib/utils/single-char-selection.hpp
lib/utils/slider-spinbox.cpp lib/utils/slider-spinbox.cpp
lib/utils/slider-spinbox.hpp lib/utils/slider-spinbox.hpp
lib/utils/source-helpers.cpp lib/utils/source-helpers.cpp

View File

@@ -22,4 +22,5 @@ sudo snap install obs-studio
## Contributing ## Contributing
- If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin. - If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin.
- You can optionally use [pre-commit](https://pre-commit.com) to automatically handle formatting.
- If you wish to contribute translations, feel free to submit pull requests for the corresponding files under `data/locale`. - If you wish to contribute translations, feel free to submit pull requests for the corresponding files under `data/locale`.

View File

@@ -49,7 +49,16 @@ invoke_formatter() {
exit 2 exit 2
} }
local -a source_files=(src/**/*.(c|cpp|h|hpp|m|mm)(.N)) local -a source_files=()
for folder ("lib" "module" "plugins" "tests") {
source_files+=(${folder}/**/*.(c|cpp|h|hpp)(.N))
}
for file (${source_files}) {
if [[ $file == *"catch.hpp" ]]; then
source_files=("${source_files[@]/$file}")
fi
}
local -a format_args=(-style=file -fallback-style=none) local -a format_args=(-style=file -fallback-style=none)
if (( _loglevel > 2 )) format_args+=(--verbose) if (( _loglevel > 2 )) format_args+=(--verbose)

View File

@@ -1,19 +1,22 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import sys
import argparse import argparse
import os
import re
import sys
defaultLocaleFile = "en-US.ini" defaultLocaleFile = "en-US.ini"
class localeEntry: class localeEntry:
locale = "" locale = ""
placeholders = [] widgetPlaceholders = []
qStringArgs = []
def __init__(self, locale, widgets) -> None: def __init__(self, locale, widgetPlaceholders, qStringArgs) -> None:
self.locale = locale self.locale = locale
self.placeholders = widgets self.widgetPlaceholders = widgetPlaceholders
self.qStringArgs = qStringArgs
def getNonDefaultLocales(dir): def getNonDefaultLocales(dir):
@@ -22,21 +25,33 @@ def getNonDefaultLocales(dir):
f = os.path.join(dir, filename) f = os.path.join(dir, filename)
if os.path.isfile(f) and not f.endswith(defaultLocaleFile): if os.path.isfile(f) and not f.endswith(defaultLocaleFile):
files.append(f) files.append(f)
return files return files
def getAllLocaleEntriesWithWidgetPlaceholders(file): def getAllLocaleEntries(file):
localeEntries = [] localeEntries = []
with open(file, 'r', encoding='UTF-8') as f: with open(file, "r", encoding="UTF-8") as f:
for line in f.readlines(): for line in f.readlines():
widgetPlaceholders = [] widgetPlaceholders = []
qStringArgs = []
if line.startswith(";"):
continue
for word in line.split("{{"): for word in line.split("{{"):
if not "}}" in word: if not "}}" in word:
continue continue
word = "{{" + word[:word.rfind("}}")] + "}}" placeholder = "{{" + word[: word.rfind("}}")] + "}}"
widgetPlaceholders.append(word) widgetPlaceholders.append(placeholder)
localeEntries.append(localeEntry(
line.split("=")[0], widgetPlaceholders)) for match in re.finditer(r"%\d+", line):
qStringArgs.append(match.group(0))
localeEntries.append(
localeEntry(line.split("=")[0], widgetPlaceholders, qStringArgs)
)
return localeEntries return localeEntries
@@ -44,47 +59,85 @@ def getLocaleEntryFrom(entry, list):
for element in list: for element in list:
if element.locale == entry.locale: if element.locale == entry.locale:
return element return element
return None return None
def checkWidgetPlacehodlers(file, expectedPlaceholders): def checkLocaleEntries(file, expectedLocaleEntries):
localeEntries = getAllLocaleEntriesWithWidgetPlaceholders(file) localeEntries = getAllLocaleEntries(file)
result = True result = True
for localeEntry in localeEntries: for localeEntry in localeEntries:
expectedEntry = getLocaleEntryFrom(localeEntry, expectedPlaceholders) expectedLocaleEntry = getLocaleEntryFrom(localeEntry, expectedLocaleEntries)
if expectedEntry is None:
if expectedLocaleEntry is None:
result = False
print( print(
"WARNING: Locale entry \"{}\" from \"{}\" not found in \"{}\"".format(localeEntry.locale, file, defaultLocaleFile)) 'ERROR: Locale entry "{}" from "{}" not found in "{}"'.format(
localeEntry.locale, file, defaultLocaleFile
)
)
continue continue
for p in localeEntry.placeholders:
if p not in expectedEntry.placeholders: for placeholder in localeEntry.widgetPlaceholders:
print("WARNING: Locale entry \"{}\" from \"{}\" does contain \"{}\" while \"{}\" does not".format( if placeholder not in expectedLocaleEntry.widgetPlaceholders:
localeEntry.locale, file, p, defaultLocaleFile))
for p in expectedEntry.placeholders:
if p not in localeEntry.placeholders:
result = False result = False
print("ERROR: Locale entry \"{}\" from \"{}\" does not contain \"{}\"".format( print(
localeEntry.locale, file, p)) 'ERROR: Locale entry "{}" from "{}" does contain "{}" widget placeholder while "{}" does not'.format(
localeEntry.locale, file, placeholder, defaultLocaleFile
)
)
for placeholder in expectedLocaleEntry.widgetPlaceholders:
if placeholder not in localeEntry.widgetPlaceholders:
result = False
print(
'ERROR: Locale entry "{}" from "{}" does not contain "{}" widget placeholder'.format(
localeEntry.locale, file, placeholder
)
)
for arg in localeEntry.qStringArgs:
if arg not in expectedLocaleEntry.qStringArgs:
result = False
print(
'ERROR: Locale entry "{}" from "{}" does contain "{}" QString arg while "{}" does not'.format(
localeEntry.locale, file, arg, defaultLocaleFile
)
)
for arg in expectedLocaleEntry.qStringArgs:
if arg not in localeEntry.qStringArgs:
result = False
print(
'ERROR: Locale entry "{}" from "{}" does not contain "{}" QString arg'.format(
localeEntry.locale, file, arg
)
)
return result return result
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Checks for inconsistencies regarding widget placeholders in the different locale files.') description="Checks for inconsistencies regarding widget placeholders in the different locale files."
parser.add_argument( )
'-p', '--path', help='Path to locale folder', required=True) parser.add_argument("-p", "--path", help="Path to locale folder", required=True)
args = parser.parse_args() args = parser.parse_args()
placeholders = getAllLocaleEntriesWithWidgetPlaceholders( defaultLocaleEntries = getAllLocaleEntries(
os.path.join(args.path, defaultLocaleFile)) os.path.join(args.path, defaultLocaleFile)
)
nonDefaultLocales = getNonDefaultLocales(args.path) nonDefaultLocales = getNonDefaultLocales(args.path)
result = True result = True
for f in nonDefaultLocales: for file in nonDefaultLocales:
if checkWidgetPlacehodlers(f, placeholders) == False: if checkLocaleEntries(file, defaultLocaleEntries) == False:
result = False result = False
if result == False: if result == False:
sys.exit(1) sys.exit(1)
print("SUCCESS: No issues found!") print("SUCCESS: No issues found!")
sys.exit(0) sys.exit(0)

View File

@@ -70,11 +70,14 @@ endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU) if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
# Disable false-positive warning in GCC 12.1.0 and later # Disable false-positive warning in GCC 12.1.0 and later
add_compile_options(-Wno-error=maybe-uninitialized) add_compile_options(-Wno-error=maybe-uninitialized)
add_compile_options(-Wno-error=stringop-overflow)
# Add warning for infinite recursion (added in GCC 12) # Add warning for infinite recursion (added in GCC 12)
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
add_compile_options(-Winfinite-recursion) add_compile_options(-Winfinite-recursion)
endif() endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang)
add_compile_options(-Wno-error=null-pointer-subtraction)
endif() endif()
# Enable compiler and build tracing (requires Ninja generator) # Enable compiler and build tracing (requires Ninja generator)

View File

@@ -75,13 +75,12 @@ AdvSceneSwitcher.macroTab.edit.action="Aktions-Typ:"
AdvSceneSwitcher.macroTab.add="Neues Makro hinzufügen" AdvSceneSwitcher.macroTab.add="Neues Makro hinzufügen"
AdvSceneSwitcher.macroTab.name="Name:" AdvSceneSwitcher.macroTab.name="Name:"
AdvSceneSwitcher.macroTab.run="Makro ausführen" AdvSceneSwitcher.macroTab.run="Makro ausführen"
AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt." AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt.\nSoll die aktuelle Ausführung gestoppt werden?"
AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen" AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen"
AdvSceneSwitcher.macroTab.onChange="Nur bei Änderung ausführen" AdvSceneSwitcher.macroTab.onChange="Nur bei Änderung ausführen"
AdvSceneSwitcher.macroTab.defaultname="Makro %1" AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1" AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1"
AdvSceneSwitcher.macroTab.exists="Makro-Name existiert bereits" AdvSceneSwitcher.macroTab.removeGroupPopup.text="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
AdvSceneSwitcher.macroTab.copy="Kopie des aktuellen Makros erstellen" AdvSceneSwitcher.macroTab.copy="Kopie des aktuellen Makros erstellen"
AdvSceneSwitcher.macroTab.group="Ausgewählte Elemente gruppieren" AdvSceneSwitcher.macroTab.group="Ausgewählte Elemente gruppieren"
AdvSceneSwitcher.macroTab.ungroup="Gruppierung ausgewählter Gruppen aufheben" AdvSceneSwitcher.macroTab.ungroup="Gruppierung ausgewählter Gruppen aufheben"
@@ -445,7 +444,7 @@ AdvSceneSwitcher.action.filter="Filter"
AdvSceneSwitcher.action.filter.type.enable="Aktivieren" AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren" AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
AdvSceneSwitcher.action.filter.type.settings="Einstellungen festlegen" AdvSceneSwitcher.action.filter.type.settings="Einstellungen festlegen"
AdvSceneSwitcher.action.filter.entry="Auf{{sources}}{{actions}}{{filters}}{{filters}}{{refresh}}" AdvSceneSwitcher.action.filter.entry="Auf{{sources}}{{actions}}{{filters}}{{filters}}{{refresh}}{{settingsButtons}}"
AdvSceneSwitcher.action.filter.getSettings="Aktuelle Einstellungen abfragen" AdvSceneSwitcher.action.filter.getSettings="Aktuelle Einstellungen abfragen"
AdvSceneSwitcher.action.source="Quelle" AdvSceneSwitcher.action.source="Quelle"
AdvSceneSwitcher.action.source.type.enable="Aktivieren" AdvSceneSwitcher.action.source.type.enable="Aktivieren"
@@ -454,7 +453,6 @@ AdvSceneSwitcher.action.source.type.settings="Einstellungen festlegen"
AdvSceneSwitcher.action.source.type.refreshSettings="Aktualisieren der Quelleneinstellungen" AdvSceneSwitcher.action.source.type.refreshSettings="Aktualisieren der Quelleneinstellungen"
AdvSceneSwitcher.action.source.type.pressSettingsButton="Drücken der Einstellungstaste" AdvSceneSwitcher.action.source.type.pressSettingsButton="Drücken der Einstellungstaste"
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Kann verwendet werden, um Browser-, Medien- usw. Quellen zu aktualisieren" AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Kann verwendet werden, um Browser-, Medien- usw. Quellen zu aktualisieren"
AdvSceneSwitcher.action.source.noSettingsButtons="Keine Buttons gefunden!"
AdvSceneSwitcher.action.source.warning="Warnung: Das globale Aktivieren und Deaktivieren von Quellen kann nicht über die OBS-Benutzeroberfläche gesteuert werden" AdvSceneSwitcher.action.source.warning="Warnung: Das globale Aktivieren und Deaktivieren von Quellen kann nicht über die OBS-Benutzeroberfläche gesteuert werden"
AdvSceneSwitcher.action.source.getSettings="Aktuelle Einstellungen abfragen" AdvSceneSwitcher.action.source.getSettings="Aktuelle Einstellungen abfragen"
AdvSceneSwitcher.action.media="Medien" AdvSceneSwitcher.action.media="Medien"
@@ -545,7 +543,7 @@ AdvSceneSwitcher.action.sceneCollection="Szenensammlung"
AdvSceneSwitcher.action.sceneCollection.entry="Aktive Szenensammlung umschalten auf {{sceneCollections}}" AdvSceneSwitcher.action.sceneCollection.entry="Aktive Szenensammlung umschalten auf {{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="Hinweis: Alle danach folgenden Aktionen werden nicht ausgeführt, da durch die Änderung der Szenensammlung auch die Einstellungen des Szenenwechslers neu geladen werden.\nDie Szenensammlungsaktion wird ignoriert, solange das Einstellungsfenster geöffnet ist." AdvSceneSwitcher.action.sceneCollection.warning="Hinweis: Alle danach folgenden Aktionen werden nicht ausgeführt, da durch die Änderung der Szenensammlung auch die Einstellungen des Szenenwechslers neu geladen werden.\nDie Szenensammlungsaktion wird ignoriert, solange das Einstellungsfenster geöffnet ist."
AdvSceneSwitcher.action.sequence="Sequenz" AdvSceneSwitcher.action.sequence="Sequenz"
AdvSceneSwitcher.action.sequence.entry="Jedes Mal, wenn diese Aktion ausgeführt wird, wird das nächste Makro in der Liste ausgeführt (pausierte Makros werden ignoriert)" ; AdvSceneSwitcher.action.sequence.entry="Jedes Mal, wenn diese Aktion ausgeführt wird, wird das nächste Makro in der Liste ausgeführt (pausierte Makros werden ignoriert)"
AdvSceneSwitcher.action.sequence.status="Zuletzt ausgeführtes Makro: %1 - Nächstes auszuführendes Makro: %2" AdvSceneSwitcher.action.sequence.status="Zuletzt ausgeführtes Makro: %1 - Nächstes auszuführendes Makro: %2"
AdvSceneSwitcher.action.sequence.status.none="keines" AdvSceneSwitcher.action.sequence.status.none="keines"
AdvSceneSwitcher.action.sequence.restart="Neustart am Anfang, wenn das Ende der Liste erreicht ist" AdvSceneSwitcher.action.sequence.restart="Neustart am Anfang, wenn das Ende der Liste erreicht ist"
@@ -571,6 +569,7 @@ AdvSceneSwitcher.action.variable.actionNoVariableSupport="Das Abrufen von Variab
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:" AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:"
AdvSceneSwitcher.noSettingsButtons="Keine Buttons gefunden!"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Szenenübergänge" AdvSceneSwitcher.transitionTab.title="Szenenübergänge"

View File

@@ -26,6 +26,10 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Switch to an
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Switch to:" AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Switch to:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After performing actions skip performing actions for" AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After performing actions skip performing actions for"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!" AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel="Log level:"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="Default"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.printActions="Log performed actions"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="Verbose logging"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging" AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size" AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications" AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
@@ -68,14 +72,21 @@ AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one optio
; Variables Tab ; Variables Tab
AdvSceneSwitcher.variableTab.title="Variables" AdvSceneSwitcher.variableTab.title="Variables"
AdvSceneSwitcher.variableTab.help="Variables can be used in many places throughout the plugin.\n\nClick on the highlighted plus symbol to add a new variable." AdvSceneSwitcher.variableTab.help="Variables can be used in many places throughout the plugin.\n\nClick on the highlighted plus symbol to add a new variable."
AdvSceneSwitcher.variableTab.tooltip.variableAddButton="Add new variable" AdvSceneSwitcher.variableTab.variableAddButton.tooltip="Add new variable"
AdvSceneSwitcher.variableTab.tooltip.variableRemoveButton="Remove selected variables" AdvSceneSwitcher.variableTab.variableRemoveButton.tooltip="Remove selected variables"
AdvSceneSwitcher.variableTab.header.name="Name" AdvSceneSwitcher.variableTab.removeSingleVariablePopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.variableTab.header.value="Value" AdvSceneSwitcher.variableTab.removeMultipleVariablesPopup.text="Are you sure you want to remove %1 variables?"
AdvSceneSwitcher.variableTab.header.saveLoadBehavior="Save/Load behavior" AdvSceneSwitcher.variableTab.name.header="Name"
AdvSceneSwitcher.variableTab.header.lastUse="Last used" AdvSceneSwitcher.variableTab.value.header="Value"
AdvSceneSwitcher.variableTab.neverNused="Never" AdvSceneSwitcher.variableTab.saveLoadBehavior.header="Save/Load behavior"
AdvSceneSwitcher.variableTab.lastUsed="%1 seconds ago" AdvSceneSwitcher.variableTab.saveLoadBehavior.text.default="Default to \"%1\""
AdvSceneSwitcher.variableTab.lastUsed.header="Last used"
AdvSceneSwitcher.variableTab.lastUsed.text="%1 seconds ago"
AdvSceneSwitcher.variableTab.lastUsed.text.never="Never"
AdvSceneSwitcher.variableTab.lastChanged.header="Last changed"
AdvSceneSwitcher.variableTab.lastChanged.text="%1 seconds ago"
AdvSceneSwitcher.variableTab.lastChanged.text.none="No change since launch"
AdvSceneSwitcher.variableTab.lastChanged.tooltip="Times changed: %1\n\nPrevious value: %2"
; Macro Tab ; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro" AdvSceneSwitcher.macroTab.title="Macro"
@@ -90,19 +101,21 @@ AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
AdvSceneSwitcher.macroTab.edit.condition="Condition type:" AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
AdvSceneSwitcher.macroTab.edit.action="Action type:" AdvSceneSwitcher.macroTab.edit.action="Action type:"
AdvSceneSwitcher.macroTab.add="Add new macro" AdvSceneSwitcher.macroTab.add="Add new macro"
AdvSceneSwitcher.macroTab.addGroup="Add new macro group"
AdvSceneSwitcher.macroTab.name="Name:" AdvSceneSwitcher.macroTab.name="Name:"
AdvSceneSwitcher.macroTab.run="Run macro" AdvSceneSwitcher.macroTab.run="Run macro"
AdvSceneSwitcher.macroTab.run.tooltip="Run all macro actions regardless of condition.\nIf else actions are available, hold Shift and click the button to execute them instead." AdvSceneSwitcher.macroTab.run.tooltip="Run all macro actions regardless of condition.\nIf else actions are available, hold Shift and click the button to execute them instead."
AdvSceneSwitcher.macroTab.runElse="Run macro (else)" AdvSceneSwitcher.macroTab.runElse="Run macro (else)"
AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the actions failed or the macro is running already." AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the actions failed or the macro is running already.\nDo you want to stop it?"
AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros" AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros"
AdvSceneSwitcher.macroTab.onChange="Perform actions only on condition change" AdvSceneSwitcher.macroTab.onChange="Perform actions only on condition change"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Group %1" AdvSceneSwitcher.macroTab.defaultGroupName="Group %1"
AdvSceneSwitcher.macroTab.exists="Macro name exists already" AdvSceneSwitcher.macroTab.macroNameExists="The name \"%1\" is already used by a macro."
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Are you sure you want to delete \"%1\" and all its elements?" AdvSceneSwitcher.macroTab.groupNameExists="The name \"%1\" is already used by a macro group."
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Are you sure you want to delete %1 macros?" AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Are you sure you want to delete \"%1\"?" AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="Are you sure you want to remove %1 macros/groups?"
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Are you sure you want to remove \"%1\" group and all its elements?"
AdvSceneSwitcher.macroTab.contextMenuAdd="Add" AdvSceneSwitcher.macroTab.contextMenuAdd="Add"
AdvSceneSwitcher.macroTab.copy="Duplicate Macro" AdvSceneSwitcher.macroTab.copy="Duplicate Macro"
AdvSceneSwitcher.macroTab.group="Group Selected Macros" AdvSceneSwitcher.macroTab.group="Group Selected Macros"
@@ -114,7 +127,7 @@ AdvSceneSwitcher.macroTab.export.info="Paste the string below into the import di
AdvSceneSwitcher.macroTab.import="Import" AdvSceneSwitcher.macroTab.import="Import"
AdvSceneSwitcher.macroTab.import.info="Paste the export string into the below text box to import macros:" AdvSceneSwitcher.macroTab.import.info="Paste the export string into the below text box to import macros:"
AdvSceneSwitcher.macroTab.import.invalid="Invalid import data provided!" AdvSceneSwitcher.macroTab.import.invalid="Invalid import data provided!"
AdvSceneSwitcher.macroTab.import.nameConflict="Cannot continue importing macro \"%1\" as a macro with the same name already exists!\nDo you want to continue with the import of \"%2\" and choose a new name? (Will be skipped otherwise)" AdvSceneSwitcher.macroTab.import.nameConflict="Cannot continue with import of \"%1\" as the name is already in use!\nDo want to choose a new name for the imported \"%2\"? (It will be skipped otherwise)"
AdvSceneSwitcher.macroTab.expandAll="Expand all" AdvSceneSwitcher.macroTab.expandAll="Expand all"
AdvSceneSwitcher.macroTab.collapseAll="Collapse all" AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
AdvSceneSwitcher.macroTab.maximize="Maximize" AdvSceneSwitcher.macroTab.maximize="Maximize"
@@ -129,6 +142,7 @@ AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performe
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros" AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro" AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro"
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Skip execution of actions of current macro on startup" AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Skip execution of actions of current macro on startup"
AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone="Stop and rerun actions of the currently selected macro, if the actions are still running, when a new execution is triggered"
AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually" AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually"
AdvSceneSwitcher.macroTab.currentDockAddRunButton="Add button to run the macro" AdvSceneSwitcher.macroTab.currentDockAddRunButton="Add button to run the macro"
AdvSceneSwitcher.macroTab.currentDockAddPauseButton="Add button to pause or unpause the macro" AdvSceneSwitcher.macroTab.currentDockAddPauseButton="Add button to pause or unpause the macro"
@@ -611,6 +625,15 @@ AdvSceneSwitcher.condition.queue.type.stopped="Stopped"
AdvSceneSwitcher.condition.queue.type.size="Size" AdvSceneSwitcher.condition.queue.type.size="Size"
AdvSceneSwitcher.condition.queue.entry.startStop="Queue{{queues}}is{{conditions}}" AdvSceneSwitcher.condition.queue.entry.startStop="Queue{{queues}}is{{conditions}}"
AdvSceneSwitcher.condition.queue.entry.size="Queue{{queues}}{{conditions}}is less than{{size}}" AdvSceneSwitcher.condition.queue.entry.size="Queue{{queues}}{{conditions}}is less than{{size}}"
AdvSceneSwitcher.condition.clipboard="Clipboard"
AdvSceneSwitcher.condition.clipboard.placeholder="Clipboard text"
AdvSceneSwitcher.condition.clipboard.urlTooltip="Depending on the OS files might be represented as URLs also!"
AdvSceneSwitcher.condition.clipboard.condition.changed="Clipboard content changed"
AdvSceneSwitcher.condition.clipboard.condition.isText="Clipboard contains text"
AdvSceneSwitcher.condition.clipboard.condition.isImage="Clipboard contains an image"
AdvSceneSwitcher.condition.clipboard.condition.isURL="Clipboard contains an URL"
AdvSceneSwitcher.condition.clipboard.condition.matches="Clipboard content matches"
AdvSceneSwitcher.condition.clipboard.condition.entry="{{conditions}}{{regex}}{{urlInfo}}"
; Macro Actions ; Macro Actions
AdvSceneSwitcher.action.scene="Switch scene" AdvSceneSwitcher.action.scene="Switch scene"
@@ -632,6 +655,8 @@ AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume" AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
AdvSceneSwitcher.action.audio.type.syncOffset="Set sync offset" AdvSceneSwitcher.action.audio.type.syncOffset="Set sync offset"
AdvSceneSwitcher.action.audio.type.monitor="Set audio monitoring" AdvSceneSwitcher.action.audio.type.monitor="Set audio monitoring"
AdvSceneSwitcher.action.audio.type.enableOnTrack="Enable on audio track"
AdvSceneSwitcher.action.audio.type.disableOnTrack="Disable on audio track"
AdvSceneSwitcher.action.audio.type.balance="Set balance" AdvSceneSwitcher.action.audio.type.balance="Set balance"
AdvSceneSwitcher.action.audio.balance.description="Move the slider in the direction of the audio channel you want to focus on." AdvSceneSwitcher.action.audio.balance.description="Move the slider in the direction of the audio channel you want to focus on."
AdvSceneSwitcher.action.audio.fade.type.duration="over a duration of" AdvSceneSwitcher.action.audio.fade.type.duration="over a duration of"
@@ -640,7 +665,7 @@ AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade{{fadeTypes}}{{duration
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade{{fadeTypes}}{{rate}}per second." AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade{{fadeTypes}}{{rate}}per second."
AdvSceneSwitcher.action.audio.fade.wait="Wait for fade to complete." AdvSceneSwitcher.action.audio.fade.wait="Wait for fade to complete."
AdvSceneSwitcher.action.audio.fade.abort="Abort already active fade." AdvSceneSwitcher.action.audio.fade.abort="Abort already active fade."
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{volumeDB}}{{percentDBToggle}}{{syncOffset}}{{monitorTypes}}" AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{volumeDB}}{{percentDBToggle}}{{syncOffset}}{{monitorTypes}}{{track}}"
AdvSceneSwitcher.action.recording="Recording" AdvSceneSwitcher.action.recording="Recording"
AdvSceneSwitcher.action.recording.type.stop="Stop recording" AdvSceneSwitcher.action.recording.type.stop="Stop recording"
AdvSceneSwitcher.action.recording.type.start="Start recording" AdvSceneSwitcher.action.recording.type.start="Start recording"
@@ -668,7 +693,8 @@ AdvSceneSwitcher.action.streaming.type.username="Set username"
AdvSceneSwitcher.action.streaming.type.password="Set password" AdvSceneSwitcher.action.streaming.type.password="Set password"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}" AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
AdvSceneSwitcher.action.run="Run" AdvSceneSwitcher.action.run="Run"
AdvSceneSwitcher.action.run.entry.wait="{{wait}}Wait for process exit or at most {{timeout}}" AdvSceneSwitcher.action.run.wait.entry="{{wait}}Wait for process exit or at most {{timeout}}{{waitHelp}}"
AdvSceneSwitcher.action.run.wait.help.tooltip="Note that macro properties won't work if you leave this unticked, as the process spawns detached from the rest of logic and there's no control over it."
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility" AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
AdvSceneSwitcher.action.sceneVisibility.type.show="Show" AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide" AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
@@ -681,9 +707,10 @@ AdvSceneSwitcher.action.filter.type.enable="Enable"
AdvSceneSwitcher.action.filter.type.disable="Disable" AdvSceneSwitcher.action.filter.type.disable="Disable"
AdvSceneSwitcher.action.filter.type.toggle="Toggle" AdvSceneSwitcher.action.filter.type.toggle="Toggle"
AdvSceneSwitcher.action.filter.type.settings="Set settings" AdvSceneSwitcher.action.filter.type.settings="Set settings"
AdvSceneSwitcher.action.filter.type.pressSettingsButton="Press settings button"
AdvSceneSwitcher.action.filter.refresh="Refresh" AdvSceneSwitcher.action.filter.refresh="Refresh"
AdvSceneSwitcher.action.filter.refreshTooltip="Repopulate the filter settings selection with the settings of the filter which's name matches the variable value." AdvSceneSwitcher.action.filter.refreshTooltip="Repopulate the filter settings selection with the settings of the filter which's name matches the variable value."
AdvSceneSwitcher.action.filter.entry="On{{sources}}{{actions}}{{filters}}{{refresh}}" AdvSceneSwitcher.action.filter.entry="On{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
AdvSceneSwitcher.action.filter.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}" AdvSceneSwitcher.action.filter.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
AdvSceneSwitcher.action.filter.getSettings="Get current settings" AdvSceneSwitcher.action.filter.getSettings="Get current settings"
AdvSceneSwitcher.action.filter.inputMethod.individualManual="Set to fixed value" AdvSceneSwitcher.action.filter.inputMethod.individualManual="Set to fixed value"
@@ -701,7 +728,6 @@ AdvSceneSwitcher.action.source.type.deinterlaceOrder="Set deinterlace field orde
AdvSceneSwitcher.action.source.type.openInteractionDialog="Open interaction dialog" AdvSceneSwitcher.action.source.type.openInteractionDialog="Open interaction dialog"
AdvSceneSwitcher.action.source.type.openFilterDialog="Open filter dialog" AdvSceneSwitcher.action.source.type.openFilterDialog="Open filter dialog"
AdvSceneSwitcher.action.source.type.openPropertiesDialog="Open properties dialog" AdvSceneSwitcher.action.source.type.openPropertiesDialog="Open properties dialog"
AdvSceneSwitcher.action.source.noSettingsButtons="No buttons found!"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}{{refresh}}" AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}{{refresh}}"
AdvSceneSwitcher.action.source.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}" AdvSceneSwitcher.action.source.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI\nYou might be looking for \"Scene item visibility\"" AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI\nYou might be looking for \"Scene item visibility\""
@@ -829,6 +855,7 @@ AdvSceneSwitcher.action.systray.title="Title:"
AdvSceneSwitcher.action.systray.message="Message:" AdvSceneSwitcher.action.systray.message="Message:"
AdvSceneSwitcher.action.systray.icon="Icon:" AdvSceneSwitcher.action.systray.icon="Icon:"
AdvSceneSwitcher.action.systray.iconHint="Leave blank to use the default icon" AdvSceneSwitcher.action.systray.iconHint="Leave blank to use the default icon"
AdvSceneSwitcher.action.systray.disabled="System tray notifications seem to be disabled in the OBS settings!"
AdvSceneSwitcher.action.screenshot="Screenshot" AdvSceneSwitcher.action.screenshot="Screenshot"
AdvSceneSwitcher.action.screenshot.save.default="Default" AdvSceneSwitcher.action.screenshot.save.default="Default"
AdvSceneSwitcher.action.screenshot.save.custom="Custom" AdvSceneSwitcher.action.screenshot.save.custom="Custom"
@@ -889,6 +916,10 @@ 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.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.truncateValue="Truncate current value"
AdvSceneSwitcher.action.variable.truncateOrPadDirection.left="Left"
AdvSceneSwitcher.action.variable.truncateOrPadDirection.right="Right"
AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":" AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":"
AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:" AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:"
AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4" AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4"
@@ -900,7 +931,9 @@ 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="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{sceneItemIndex}}" AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{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.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}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:" AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}" AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
@@ -962,6 +995,15 @@ AdvSceneSwitcher.action.queue.type.start="Start"
AdvSceneSwitcher.action.queue.type.stop="Stop" AdvSceneSwitcher.action.queue.type.stop="Stop"
AdvSceneSwitcher.action.queue.entry.add="{{actions}}actions of macro{{macros}}to queue{{queues}}" AdvSceneSwitcher.action.queue.entry.add="{{actions}}actions of macro{{macros}}to queue{{queues}}"
AdvSceneSwitcher.action.queue.entry.other="{{actions}}queue{{queues}}" AdvSceneSwitcher.action.queue.entry.other="{{actions}}queue{{queues}}"
AdvSceneSwitcher.action.window="Window"
AdvSceneSwitcher.action.window.type.setFocusWindow="Focus window"
AdvSceneSwitcher.action.window.type.maximizeWindow="Maximize window"
AdvSceneSwitcher.action.window.type.minimizeWindow="Minimize window"
AdvSceneSwitcher.action.window.type.closeWindow="Close window"
AdvSceneSwitcher.action.window.entry="{{actions}}{{windows}}{{regex}}"
AdvSceneSwitcher.action.log="Log"
AdvSceneSwitcher.action.log.placeholder="My log message!"
AdvSceneSwitcher.action.log.entry="Write to OBS log:{{logMessage}}"
; Hotkey ; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher" AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
@@ -1034,6 +1076,7 @@ AdvSceneSwitcher.actionQueues.configure="Configure action queue settings"
AdvSceneSwitcher.actionQueues.invalid="Invalid action queue selection" AdvSceneSwitcher.actionQueues.invalid="Invalid action queue selection"
AdvSceneSwitcher.actionQueues.name="Name:" AdvSceneSwitcher.actionQueues.name="Name:"
AdvSceneSwitcher.actionQueues.runOnStartup="Run action queue when starting the plugin" AdvSceneSwitcher.actionQueues.runOnStartup="Run action queue when starting the plugin"
AdvSceneSwitcher.actionQueues.resolveVariablesOnAdd="Resolve variables when action is inserted into the queue"
AdvSceneSwitcher.actionQueues.running="Queue is running" AdvSceneSwitcher.actionQueues.running="Queue is running"
AdvSceneSwitcher.actionQueues.stopped="Queue is stopped" AdvSceneSwitcher.actionQueues.stopped="Queue is stopped"
AdvSceneSwitcher.actionQueues.start="Start action queue" AdvSceneSwitcher.actionQueues.start="Start action queue"
@@ -1586,6 +1629,15 @@ AdvSceneSwitcher.tempVar.macro.matchedCount.description="The number of macros of
AdvSceneSwitcher.tempVar.process.name="Process name" AdvSceneSwitcher.tempVar.process.name="Process name"
AdvSceneSwitcher.tempVar.run.process.id="Process ID"
AdvSceneSwitcher.tempVar.run.process.id.description="PID of the process assigned by the system."
AdvSceneSwitcher.tempVar.run.process.exitCode="Process exit code"
AdvSceneSwitcher.tempVar.run.process.exitCode.description="Numeric code with which the process exited. Usually 0 is positive and other values are error codes."
AdvSceneSwitcher.tempVar.run.process.stream.output="Process standard output stream"
AdvSceneSwitcher.tempVar.run.process.stream.output.description="Full standard output stream, usually numbered 1 and what you see in CLI after running the command."
AdvSceneSwitcher.tempVar.run.process.stream.error="Process standard error stream"
AdvSceneSwitcher.tempVar.run.process.stream.error.description="Full standard error stream, usually numbered 2."
AdvSceneSwitcher.tempVar.recording.durationSeconds="Recording duration" AdvSceneSwitcher.tempVar.recording.durationSeconds="Recording duration"
AdvSceneSwitcher.tempVar.recording.durationSeconds.description="Recording duration in seconds.\nThis value does not change while the recording is paused and will be reset to zero if the recording is stopped." AdvSceneSwitcher.tempVar.recording.durationSeconds.description="Recording duration in seconds.\nThis value does not change while the recording is paused and will be reset to zero if the recording is stopped."
@@ -1617,6 +1669,9 @@ AdvSceneSwitcher.tempVar.streaming.keyframeInterval.description="Stream keyframe
AdvSceneSwitcher.tempVar.streaming.durationSeconds="Stream duration" AdvSceneSwitcher.tempVar.streaming.durationSeconds="Stream duration"
AdvSceneSwitcher.tempVar.streaming.durationSeconds.description="Seconds passed since the stream was started.\nThis value will be zero if the stream is stopped." AdvSceneSwitcher.tempVar.streaming.durationSeconds.description="Seconds passed since the stream was started.\nThis value will be zero if the stream is stopped."
AdvSceneSwitcher.tempVar.clipboard.text="Clipboard text"
AdvSceneSwitcher.tempVar.clipboard.text.description="The text contained in the clipboard.\nWill be empty if the clipboard does not contain text."
AdvSceneSwitcher.selectScene="--select scene--" AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene" AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene" AdvSceneSwitcher.selectCurrentScene="Current Scene"
@@ -1642,10 +1697,27 @@ AdvSceneSwitcher.enterText="--enter text--"
AdvSceneSwitcher.enterURL="--enter URL--" AdvSceneSwitcher.enterURL="--enter URL--"
AdvSceneSwitcher.selectHotkey="--select hotkey--" AdvSceneSwitcher.selectHotkey="--select hotkey--"
AdvSceneSwitcher.selectDisplay="--select display--" AdvSceneSwitcher.selectDisplay="--select display--"
AdvSceneSwitcher.selectSetting="--select setting--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved" AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB" AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB"
AdvSceneSwitcher.settings.suffix.type.invalid=" (Invalid)"
AdvSceneSwitcher.settings.suffix.type.bool=" (Bool)"
AdvSceneSwitcher.settings.suffix.type.int=" (Int)"
AdvSceneSwitcher.settings.suffix.type.float=" (Float)"
AdvSceneSwitcher.settings.suffix.type.text=" (Text)"
AdvSceneSwitcher.settings.suffix.type.path=" (Path)"
AdvSceneSwitcher.settings.suffix.type.list=" (List)"
AdvSceneSwitcher.settings.suffix.type.color=" (Color)"
AdvSceneSwitcher.settings.suffix.type.button=" (Button)"
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
AdvSceneSwitcher.settings.suffix.type.editableList=" (Editable list)"
AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
AdvSceneSwitcher.settings.suffix.type.group=" (Group)"
AdvSceneSwitcher.settings.suffix.type.colorAlpha=" (Color alpha)"
AdvSceneSwitcher.setting.select="--select setting--"
AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type" AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type"
AdvSceneSwitcher.sceneItemSelection.type.sourceName="Source name" AdvSceneSwitcher.sceneItemSelection.type.sourceName="Source name"
AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}" AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}"
@@ -1689,6 +1761,8 @@ AdvSceneSwitcher.audio.monitor.none="Monitor Off"
AdvSceneSwitcher.audio.monitor.monitorOnly="Monitor Only (mute output)" AdvSceneSwitcher.audio.monitor.monitorOnly="Monitor Only (mute output)"
AdvSceneSwitcher.audio.monitor.both="Monitor and Output" AdvSceneSwitcher.audio.monitor.both="Monitor and Output"
AdvSceneSwitcher.noSettingsButtons="No buttons found!"
; Legacy tabs below - please don't waste your time adding translations for these :) ; Legacy tabs below - please don't waste your time adding translations for these :)
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition" AdvSceneSwitcher.transitionTab.title="Transition"

View File

@@ -72,11 +72,9 @@ AdvSceneSwitcher.macroTab.edit.action="Tipo de acción:"
AdvSceneSwitcher.macroTab.add="Agregar nueva macro" AdvSceneSwitcher.macroTab.add="Agregar nueva macro"
AdvSceneSwitcher.macroTab.name="Nombre:" AdvSceneSwitcher.macroTab.name="Nombre:"
AdvSceneSwitcher.macroTab.run="Ejecutar macro" AdvSceneSwitcher.macroTab.run="Ejecutar macro"
AdvSceneSwitcher.macroTab.runFail="Error al ejecutar \"%1\" si se ha producido un error en una de las acciones o si la macro ya se está ejecutando."
AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros" AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros"
AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición" AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.exists="El nombre de la macro ya existe"
AdvSceneSwitcher.macroTab.copy="Crear copia" AdvSceneSwitcher.macroTab.copy="Crear copia"
AdvSceneSwitcher.macroTab.expandAll="Expandir todo" AdvSceneSwitcher.macroTab.expandAll="Expandir todo"
AdvSceneSwitcher.macroTab.collapseAll="Contraer todo" AdvSceneSwitcher.macroTab.collapseAll="Contraer todo"
@@ -367,7 +365,7 @@ AdvSceneSwitcher.action.filter="Filtro"
AdvSceneSwitcher.action.filter.type.enable="Habilitar" AdvSceneSwitcher.action.filter.type.enable="Habilitar"
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar" AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
AdvSceneSwitcher.action.filter.type.settings="Establecer configuración" AdvSceneSwitcher.action.filter.type.settings="Establecer configuración"
AdvSceneSwitcher.action.filter.entry="En{{sources}}{{actions}}{{filters}}{{refresh}}" AdvSceneSwitcher.action.filter.entry="En{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
AdvSceneSwitcher.action.filter.getSettings="Obtener la configuración actual" AdvSceneSwitcher.action.filter.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.action.source="Fuente" AdvSceneSwitcher.action.source="Fuente"
AdvSceneSwitcher.action.source.type.enable="Habilitar" AdvSceneSwitcher.action.source.type.enable="Habilitar"
@@ -456,7 +454,7 @@ AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
AdvSceneSwitcher.action.sceneCollection.entry="Cambiar la colección de escenas activa a {{sceneCollections}}" AdvSceneSwitcher.action.sceneCollection.entry="Cambiar la colección de escenas activa a {{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="Nota: Cualquier acción posterior a esta no se ejecutará, ya que la colección de escenas cambiante también volverá a cargar la configuración del conmutador de escenas.\nLa acción de la colección de escenas se ignorará mientras se abra la ventana de configuración". AdvSceneSwitcher.action.sceneCollection.warning="Nota: Cualquier acción posterior a esta no se ejecutará, ya que la colección de escenas cambiante también volverá a cargar la configuración del conmutador de escenas.\nLa acción de la colección de escenas se ignorará mientras se abra la ventana de configuración".
AdvSceneSwitcher.action.sequence="Secuencia" AdvSceneSwitcher.action.sequence="Secuencia"
AdvSceneSwitcher.action.sequence.entry="Cada vez que se realiza esta acción, ejecute la siguiente macro de la lista (las macros en pausa se ignoran)" ; AdvSceneSwitcher.action.sequence.entry="Cada vez que se realiza esta acción, ejecute la siguiente macro de la lista (las macros en pausa se ignoran)"
AdvSceneSwitcher.action.sequence.status="Última macro ejecutada: %1 - Siguiente macro a ejecutar: %2" AdvSceneSwitcher.action.sequence.status="Última macro ejecutada: %1 - Siguiente macro a ejecutar: %2"
AdvSceneSwitcher.action.sequence.status.none="ninguno" AdvSceneSwitcher.action.sequence.status.none="ninguno"
AdvSceneSwitcher.action.sequence.restart="Reiniciar desde el principio una vez que se alcance el final de la lista" AdvSceneSwitcher.action.sequence.restart="Reiniciar desde el principio una vez que se alcance el final de la lista"

View File

@@ -78,15 +78,13 @@ AdvSceneSwitcher.macroTab.edit.action="Type d'action :"
AdvSceneSwitcher.macroTab.add="Ajouter une nouvelle macro" AdvSceneSwitcher.macroTab.add="Ajouter une nouvelle macro"
AdvSceneSwitcher.macroTab.name="Nom :" AdvSceneSwitcher.macroTab.name="Nom :"
AdvSceneSwitcher.macroTab.run="Exécuter la macro" AdvSceneSwitcher.macroTab.run="Exécuter la macro"
AdvSceneSwitcher.macroTab.runFail="Échec de l'exécution de \"%1\" !\nSoit l'une des actions a échoué, soit la macro est déjà en cours d'exécution."
AdvSceneSwitcher.macroTab.runInParallel="Exécuter la macro en parallèle avec d'autres macros" AdvSceneSwitcher.macroTab.runInParallel="Exécuter la macro en parallèle avec d'autres macros"
AdvSceneSwitcher.macroTab.onChange="Exécuter des actions uniquement en cas de changement de condition" AdvSceneSwitcher.macroTab.onChange="Exécuter des actions uniquement en cas de changement de condition"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1" AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1"
AdvSceneSwitcher.macroTab.exists="Le nom de la macro existe déjà" AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Êtes-vous sûr de vouloir supprimer \"%1\" et tous ses éléments ?" AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="Êtes-vous sûr de vouloir supprimer %1 macros ?"
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Êtes-vous sûr de vouloir supprimer %1 macros ?" AdvSceneSwitcher.macroTab.removeGroupPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" et tous ses éléments ?"
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
AdvSceneSwitcher.macroTab.contextMenuAdd="Ajouter" AdvSceneSwitcher.macroTab.contextMenuAdd="Ajouter"
AdvSceneSwitcher.macroTab.copy="Dupliquer la macro" AdvSceneSwitcher.macroTab.copy="Dupliquer la macro"
AdvSceneSwitcher.macroTab.group="Grouper les macros sélectionnées" AdvSceneSwitcher.macroTab.group="Grouper les macros sélectionnées"
@@ -523,7 +521,7 @@ AdvSceneSwitcher.action.filter.type.enable="Activer"
AdvSceneSwitcher.action.filter.type.disable="Désactiver" AdvSceneSwitcher.action.filter.type.disable="Désactiver"
AdvSceneSwitcher.action.filter.type.toggle="Basculer" AdvSceneSwitcher.action.filter.type.toggle="Basculer"
AdvSceneSwitcher.action.filter.type.settings="Définir les paramètres" AdvSceneSwitcher.action.filter.type.settings="Définir les paramètres"
AdvSceneSwitcher.action.filter.entry="Sur{{sources}}{{actions}}{{filters}}{{refresh}}" AdvSceneSwitcher.action.filter.entry="Sur{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
AdvSceneSwitcher.action.filter.getSettings="Obtenir les paramètres actuels" AdvSceneSwitcher.action.filter.getSettings="Obtenir les paramètres actuels"
AdvSceneSwitcher.action.source="Source" AdvSceneSwitcher.action.source="Source"
AdvSceneSwitcher.action.source.type.enable="Activer" AdvSceneSwitcher.action.source.type.enable="Activer"
@@ -535,7 +533,6 @@ AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Peut être utilisé
AdvSceneSwitcher.action.source.type.deinterlaceMode="Définir le mode de désentrelacement" AdvSceneSwitcher.action.source.type.deinterlaceMode="Définir le mode de désentrelacement"
AdvSceneSwitcher.action.source.type.deinterlaceOrder="Définir l'ordre de désentrelacement des champs" AdvSceneSwitcher.action.source.type.deinterlaceOrder="Définir l'ordre de désentrelacement des champs"
AdvSceneSwitcher.action.source.type.openInteractionDialog="Ouvrir la boîte de dialogue d'interaction" AdvSceneSwitcher.action.source.type.openInteractionDialog="Ouvrir la boîte de dialogue d'interaction"
AdvSceneSwitcher.action.source.noSettingsButtons="Aucun bouton trouvé !"
AdvSceneSwitcher.action.source.warning="Avertissement : Activer et désactiver les sources globalement ne peut pas être contrôlé par l'interface utilisateur d'OBS\nVous pourriez rechercher \"Visibilité de l'élément de scène\"" AdvSceneSwitcher.action.source.warning="Avertissement : Activer et désactiver les sources globalement ne peut pas être contrôlé par l'interface utilisateur d'OBS\nVous pourriez rechercher \"Visibilité de l'élément de scène\""
AdvSceneSwitcher.action.source.getSettings="Obtenir les paramètres actuels" AdvSceneSwitcher.action.source.getSettings="Obtenir les paramètres actuels"
AdvSceneSwitcher.action.source.deinterlaceMode.disable="Désactiver" AdvSceneSwitcher.action.source.deinterlaceMode.disable="Désactiver"
@@ -661,7 +658,7 @@ AdvSceneSwitcher.action.sceneCollection="Collection de scènes"
AdvSceneSwitcher.action.sceneCollection.entry="Changer la collection de scènes active vers{{sceneCollections}}" AdvSceneSwitcher.action.sceneCollection.entry="Changer la collection de scènes active vers{{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="Note : Toutes les actions suivantes ne seront pas exécutées car le changement de collection de scènes rechargera également les paramètres du commutateur de scènes. L'action de collection de scènes sera ignorée lorsque la fenêtre des paramètres est ouverte." AdvSceneSwitcher.action.sceneCollection.warning="Note : Toutes les actions suivantes ne seront pas exécutées car le changement de collection de scènes rechargera également les paramètres du commutateur de scènes. L'action de collection de scènes sera ignorée lorsque la fenêtre des paramètres est ouverte."
AdvSceneSwitcher.action.sequence="Séquence" AdvSceneSwitcher.action.sequence="Séquence"
AdvSceneSwitcher.action.sequence.entry="Chaque fois que cette action est effectuée, exécutez le macro suivant dans la liste (les macros en pause sont ignorés)" ; AdvSceneSwitcher.action.sequence.entry="Chaque fois que cette action est effectuée, exécutez le macro suivant dans la liste (les macros en pause sont ignorés)"
AdvSceneSwitcher.action.sequence.status="Dernier macro exécuté : %1 - Prochain macro à exécuter : %2" AdvSceneSwitcher.action.sequence.status="Dernier macro exécuté : %1 - Prochain macro à exécuter : %2"
AdvSceneSwitcher.action.sequence.status.none="aucun" AdvSceneSwitcher.action.sequence.status.none="aucun"
AdvSceneSwitcher.action.sequence.restart="Recommencer depuis le début une fois la fin de la liste atteinte" AdvSceneSwitcher.action.sequence.restart="Recommencer depuis le début une fois la fin de la liste atteinte"
@@ -740,6 +737,8 @@ AdvSceneSwitcher.action.twitch.type.channel.info.category.set="Définir la caté
AdvSceneSwitcher.action.twitch.type.commercial.start="Démarrer une publicité d'une durée de" AdvSceneSwitcher.action.twitch.type.commercial.start="Démarrer une publicité d'une durée de"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !"
AdvSceneSwitcher.noSettingsButtons="Aucun bouton trouvé !"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition" AdvSceneSwitcher.transitionTab.title="Transition"
AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliser une transition pour le passage automatisé de la scène A à la scène B" AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliser une transition pour le passage automatisé de la scène A à la scène B"

View File

@@ -69,7 +69,6 @@ AdvSceneSwitcher.macroTab.edit.action="Тип действия:"
AdvSceneSwitcher.macroTab.add="Добавить новый макрос" AdvSceneSwitcher.macroTab.add="Добавить новый макрос"
AdvSceneSwitcher.macroTab.name="Имя:" AdvSceneSwitcher.macroTab.name="Имя:"
AdvSceneSwitcher.macroTab.defaultname="Макрос %1" AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
AdvSceneSwitcher.macroTab.exists="Имя макроса уже существует"
AdvSceneSwitcher.macroTab.copy="Создать копию" AdvSceneSwitcher.macroTab.copy="Создать копию"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="Игнорировать вход" AdvSceneSwitcher.logic.none="Игнорировать вход"

View File

@@ -72,11 +72,9 @@ AdvSceneSwitcher.macroTab.edit.action="Eylem türü:"
AdvSceneSwitcher.macroTab.add="Yeni Makro ekle" AdvSceneSwitcher.macroTab.add="Yeni Makro ekle"
AdvSceneSwitcher.macroTab.name="İsim:" AdvSceneSwitcher.macroTab.name="İsim:"
AdvSceneSwitcher.macroTab.run="Makro Çalıştırma" AdvSceneSwitcher.macroTab.run="Makro Çalıştırma"
AdvSceneSwitcher.macroTab.runFail="\"%1\" çalıştırılamadı!\n Eylemlerden biri başarısız oldu veya makro zaten çalışıyor."
AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın" AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın"
AdvSceneSwitcher.macroTab.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin" AdvSceneSwitcher.macroTab.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin"
AdvSceneSwitcher.macroTab.defaultname="Makro %1" AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.exists="Macro adı zaten mevcut"
AdvSceneSwitcher.macroTab.copy="Kopya oluştur" AdvSceneSwitcher.macroTab.copy="Kopya oluştur"
AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet" AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült" AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült"
@@ -297,7 +295,7 @@ AdvSceneSwitcher.action.filter="Filtrele"
AdvSceneSwitcher.action.filter.type.enable="Etkin" AdvSceneSwitcher.action.filter.type.enable="Etkin"
AdvSceneSwitcher.action.filter.type.disable="Etkisiz" AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
AdvSceneSwitcher.action.filter.type.settings="Ayarları yap" AdvSceneSwitcher.action.filter.type.settings="Ayarları yap"
AdvSceneSwitcher.action.filter.entry="Açık{{sources}}{{actions}}{{filters}}{{refresh}}" AdvSceneSwitcher.action.filter.entry="Açık{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
AdvSceneSwitcher.action.filter.getSettings="Mevcut ayarları al" AdvSceneSwitcher.action.filter.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.action.source="Kaynak" AdvSceneSwitcher.action.source="Kaynak"
AdvSceneSwitcher.action.source.type.enable="Etkin" AdvSceneSwitcher.action.source.type.enable="Etkin"

View File

@@ -77,15 +77,13 @@ AdvSceneSwitcher.macroTab.edit.action="动作类型:"
AdvSceneSwitcher.macroTab.add="添加新宏" AdvSceneSwitcher.macroTab.add="添加新宏"
AdvSceneSwitcher.macroTab.name="名称:" AdvSceneSwitcher.macroTab.name="名称:"
AdvSceneSwitcher.macroTab.run="运行宏" AdvSceneSwitcher.macroTab.run="运行宏"
AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n其中一个操作失败或者宏已在运行."
AdvSceneSwitcher.macroTab.runInParallel="与其他宏同时运行宏" AdvSceneSwitcher.macroTab.runInParallel="与其他宏同时运行宏"
AdvSceneSwitcher.macroTab.onChange="仅在条件更改时执行操作" AdvSceneSwitcher.macroTab.onChange="仅在条件更改时执行操作"
AdvSceneSwitcher.macroTab.defaultname="宏 %1" AdvSceneSwitcher.macroTab.defaultname="宏 %1"
AdvSceneSwitcher.macroTab.defaultGroupName="组 %1" AdvSceneSwitcher.macroTab.defaultGroupName="组 %1"
AdvSceneSwitcher.macroTab.exists="宏名称已存在" AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="你确定你要删除 \"%1\"?"
AdvSceneSwitcher.macroTab.groupDeleteConfirm="您确定要删除 \"%1\"及其所有元素吗?" AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="您确定要删除 %1 宏吗?"
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="您确定要删除 %1 宏吗?" AdvSceneSwitcher.macroTab.removeGroupPopup.text="您确定要删除 \"%1\"及其所有元素吗?"
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="你确定你要删除 \"%1\"?"
AdvSceneSwitcher.macroTab.contextMenuAdd="增加" AdvSceneSwitcher.macroTab.contextMenuAdd="增加"
AdvSceneSwitcher.macroTab.copy="创建副本" AdvSceneSwitcher.macroTab.copy="创建副本"
AdvSceneSwitcher.macroTab.group="对选定的宏进行分组" AdvSceneSwitcher.macroTab.group="对选定的宏进行分组"
@@ -502,7 +500,7 @@ AdvSceneSwitcher.action.filter="滤镜"
AdvSceneSwitcher.action.filter.type.enable="开启" AdvSceneSwitcher.action.filter.type.enable="开启"
AdvSceneSwitcher.action.filter.type.disable="关闭" AdvSceneSwitcher.action.filter.type.disable="关闭"
AdvSceneSwitcher.action.filter.type.settings="参数设置" AdvSceneSwitcher.action.filter.type.settings="参数设置"
AdvSceneSwitcher.action.filter.entry="置{{sources}}{{actions}}{{filters}}{{refresh}}" AdvSceneSwitcher.action.filter.entry="置{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
AdvSceneSwitcher.action.filter.getSettings="获取当前设置" AdvSceneSwitcher.action.filter.getSettings="获取当前设置"
AdvSceneSwitcher.action.source="源" AdvSceneSwitcher.action.source="源"
AdvSceneSwitcher.action.source.type.enable="启用" AdvSceneSwitcher.action.source.type.enable="启用"
@@ -511,7 +509,6 @@ AdvSceneSwitcher.action.source.type.settings="配置设置"
AdvSceneSwitcher.action.source.type.refreshSettings="刷新源设置" AdvSceneSwitcher.action.source.type.refreshSettings="刷新源设置"
AdvSceneSwitcher.action.source.type.pressSettingsButton="按下设置按钮" AdvSceneSwitcher.action.source.type.pressSettingsButton="按下设置按钮"
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="可用于刷新浏览器、媒体、源、etc" AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="可用于刷新浏览器、媒体、源、etc"
AdvSceneSwitcher.action.source.noSettingsButtons="未找到按钮!"
AdvSceneSwitcher.action.source.warning="警告OBS UI无法控制全局启用和禁用源注意通过这个设置禁用我测试你必须通过这个在启用才行不然点击小眼睛你无法显示" AdvSceneSwitcher.action.source.warning="警告OBS UI无法控制全局启用和禁用源注意通过这个设置禁用我测试你必须通过这个在启用才行不然点击小眼睛你无法显示"
AdvSceneSwitcher.action.source.getSettings="获取当前设置" AdvSceneSwitcher.action.source.getSettings="获取当前设置"
AdvSceneSwitcher.action.media="媒体" AdvSceneSwitcher.action.media="媒体"
@@ -608,7 +605,7 @@ AdvSceneSwitcher.action.sceneCollection="场景集合"
AdvSceneSwitcher.action.sceneCollection.entry="将活动场景集合切换到 {{sceneCollections}}" AdvSceneSwitcher.action.sceneCollection.entry="将活动场景集合切换到 {{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="注意:在此之后的任何操作都不会执行,因为更改场景集合也会重新加载场景切换器设置。\n打开“设置”窗口时场景采集操作将被忽略." AdvSceneSwitcher.action.sceneCollection.warning="注意:在此之后的任何操作都不会执行,因为更改场景集合也会重新加载场景切换器设置。\n打开“设置”窗口时场景采集操作将被忽略."
AdvSceneSwitcher.action.sequence="场景序列" AdvSceneSwitcher.action.sequence="场景序列"
AdvSceneSwitcher.action.sequence.entry="每次执行此操作时,运行列表中的下一个宏(暂停的宏将被忽略)" ; AdvSceneSwitcher.action.sequence.entry="每次执行此操作时,运行列表中的下一个宏(暂停的宏将被忽略)"
AdvSceneSwitcher.action.sequence.status="上次执行的宏:%1 - 要执行的下一个宏: %2" AdvSceneSwitcher.action.sequence.status="上次执行的宏:%1 - 要执行的下一个宏: %2"
AdvSceneSwitcher.action.sequence.status.none="无" AdvSceneSwitcher.action.sequence.status.none="无"
AdvSceneSwitcher.action.sequence.restart="到达列表末尾后,从开头重新启动" AdvSceneSwitcher.action.sequence.restart="到达列表末尾后,从开头重新启动"
@@ -663,6 +660,8 @@ AdvSceneSwitcher.action.midi.entry="发送消息到 {{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="将MIDI消息选择设置为 {{listenDevices}} 上传入的消息: {{listenButton}}" AdvSceneSwitcher.action.midi.entry.listen="将MIDI消息选择设置为 {{listenDevices}} 上传入的消息: {{listenButton}}"
AdvSceneSwitcher.action.osc="打开声音控制" AdvSceneSwitcher.action.osc="打开声音控制"
AdvSceneSwitcher.noSettingsButtons="未找到按钮!"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="转场特效" AdvSceneSwitcher.transitionTab.title="转场特效"
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效" AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"

View File

@@ -163,12 +163,31 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_52"> <layout class="QHBoxLayout" name="horizontalLayout_52">
<item> <item>
<widget class="QCheckBox" name="verboseLogging"> <widget class="QLabel" name="label_21">
<property name="text"> <property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging</string> <string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QComboBox" name="logLevel">
<item>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default</string>
</property>
</item>
<item>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.printActions</string>
</property>
</item>
<item>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose</string>
</property>
</item>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer_49"> <spacer name="horizontalSpacer_49">
<property name="orientation"> <property name="orientation">
@@ -883,6 +902,9 @@
</item> </item>
<item> <item>
<widget class="QCheckBox" name="runMacroInParallel"> <widget class="QCheckBox" name="runMacroInParallel">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.runInParallel</string>
</property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
@@ -903,6 +925,9 @@
</item> </item>
<item> <item>
<widget class="QCheckBox" name="runMacroOnChange"> <widget class="QCheckBox" name="runMacroOnChange">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.onChange</string>
</property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
@@ -1629,7 +1654,7 @@
</size> </size>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.variableTab.tooltip.variableAddButton</string> <string>AdvSceneSwitcher.variableTab.variableAddButton.tooltip</string>
</property> </property>
<property name="flat"> <property name="flat">
<bool>true</bool> <bool>true</bool>
@@ -1648,7 +1673,7 @@
</size> </size>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.variableTab.tooltip.variableRemoveButton</string> <string>AdvSceneSwitcher.variableTab.variableRemoveButton.tooltip</string>
</property> </property>
<property name="flat"> <property name="flat">
<bool>true</bool> <bool>true</bool>
@@ -5078,7 +5103,7 @@
<tabstop>checkInterval</tabstop> <tabstop>checkInterval</tabstop>
<tabstop>startupBehavior</tabstop> <tabstop>startupBehavior</tabstop>
<tabstop>autoStartEvent</tabstop> <tabstop>autoStartEvent</tabstop>
<tabstop>verboseLogging</tabstop> <tabstop>logLevel</tabstop>
<tabstop>saveWindowGeo</tabstop> <tabstop>saveWindowGeo</tabstop>
<tabstop>showTrayNotifications</tabstop> <tabstop>showTrayNotifications</tabstop>
<tabstop>uiHintsDisable</tabstop> <tabstop>uiHintsDisable</tabstop>

View File

@@ -1,4 +1,5 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "backup.hpp"
#include "curl-helper.hpp" #include "curl-helper.hpp"
#include "log-helper.hpp" #include "log-helper.hpp"
#include "macro-helpers.hpp" #include "macro-helpers.hpp"
@@ -17,7 +18,6 @@
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <QAction> #include <QAction>
#include <QDirIterator> #include <QDirIterator>
#include <QFileDialog>
#include <QMainWindow> #include <QMainWindow>
#include <QTextStream> #include <QTextStream>
#include <regex> #include <regex>
@@ -135,6 +135,23 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
} }
SetElseActionsStateToVisible(); SetElseActionsStateToVisible();
} else if (eventType == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
auto pressedKey = keyEvent->key();
if (obj == ui->macros && ui->macros->isVisible()) {
if (pressedKey == Qt::Key_F2) {
RenameSelectedMacro();
} else if (pressedKey == Qt::Key_Delete) {
RemoveSelectedMacros();
}
} else if (obj == ui->variables && ui->variables->isVisible()) {
if (pressedKey == Qt::Key_F2) {
OpenSettingsForSelectedVariable();
} else if (pressedKey == Qt::Key_Delete) {
RemoveSelectedVariables();
}
}
} }
return QDialog::eventFilter(obj, event); return QDialog::eventFilter(obj, event);
@@ -143,7 +160,6 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
/****************************************************************************** /******************************************************************************
* Saving and loading * Saving and loading
******************************************************************************/ ******************************************************************************/
static void AskForBackup(const QString &json);
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
{ {
@@ -192,34 +208,6 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
} }
} }
static void AskForBackup(const QString &json)
{
const bool backupWasConfirmed = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true, false);
if (!backupWasConfirmed) {
return;
}
QString path = QFileDialog::getSaveFileName(
nullptr,
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle"),
GetDefaultSettingsSaveLocation(),
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
if (path.isEmpty()) {
return;
}
QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
auto out = QTextStream(&file);
out << json;
}
/****************************************************************************** /******************************************************************************
* Main switcher thread * Main switcher thread
******************************************************************************/ ******************************************************************************/
@@ -635,6 +623,8 @@ static void handleShutdown()
// TODO: Look for a way to possibly resolve this // TODO: Look for a way to possibly resolve this
obs_frontend_save(); obs_frontend_save();
} }
BackupSettingsOfCurrentVersion();
} }
static void handleSceneCollectionChanging() static void handleSceneCollectionChanging()

View File

@@ -55,6 +55,7 @@ public slots:
void NoMatchDelayDurationChanged(const Duration &); void NoMatchDelayDurationChanged(const Duration &);
void CooldownDurationChanged(const Duration &); void CooldownDurationChanged(const Duration &);
void on_startupBehavior_currentIndexChanged(int index); void on_startupBehavior_currentIndexChanged(int index);
void on_logLevel_currentIndexChanged(int index);
void on_autoStartEvent_currentIndexChanged(int index); void on_autoStartEvent_currentIndexChanged(int index);
void on_noMatchSwitchScene_currentTextChanged(const QString &text); void on_noMatchSwitchScene_currentTextChanged(const QString &text);
void on_checkInterval_valueChanged(int value); void on_checkInterval_valueChanged(int value);
@@ -62,7 +63,6 @@ public slots:
void on_tabWidget_currentChanged(int index); void on_tabWidget_currentChanged(int index);
void on_exportSettings_clicked(); void on_exportSettings_clicked();
void on_importSettings_clicked(); void on_importSettings_clicked();
void on_verboseLogging_stateChanged(int state);
void on_saveWindowGeo_stateChanged(int state); void on_saveWindowGeo_stateChanged(int state);
void on_showTrayNotifications_stateChanged(int state); void on_showTrayNotifications_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state); void on_uiHintsDisable_stateChanged(int state);
@@ -81,6 +81,7 @@ public:
bool AddNewMacro(std::shared_ptr<Macro> &res, std::string &name, bool AddNewMacro(std::shared_ptr<Macro> &res, std::string &name,
std::string format = ""); std::string format = "");
void RemoveMacro(std::shared_ptr<Macro> &); void RemoveMacro(std::shared_ptr<Macro> &);
void RemoveSelectedMacros();
void RenameMacro(std::shared_ptr<Macro> &, const QString &name); void RenameMacro(std::shared_ptr<Macro> &, const QString &name);
std::shared_ptr<Macro> GetSelectedMacro(); std::shared_ptr<Macro> GetSelectedMacro();
std::vector<std::shared_ptr<Macro>> GetSelectedMacros(); std::vector<std::shared_ptr<Macro>> GetSelectedMacros();
@@ -135,7 +136,7 @@ public slots:
void ShowMacroElseActionsContextMenu(const QPoint &); void ShowMacroElseActionsContextMenu(const QPoint &);
void ShowMacroConditionsContextMenu(const QPoint &); void ShowMacroConditionsContextMenu(const QPoint &);
void CopyMacro(); void CopyMacro();
void RenameCurrentMacro(); void RenameSelectedMacro();
void ExportMacros(); void ExportMacros();
void ImportMacros(); void ImportMacros();
void ExpandAllActions(); void ExpandAllActions();
@@ -216,6 +217,9 @@ public slots:
void on_variableAdd_clicked(); void on_variableAdd_clicked();
void on_variableRemove_clicked(); void on_variableRemove_clicked();
void OpenSettingsForSelectedVariable();
void RemoveSelectedVariables();
/* --- End of variable tab section --- */ /* --- End of variable tab section --- */
/* --- Begin of legacy tab section --- */ /* --- Begin of legacy tab section --- */

View File

@@ -112,6 +112,14 @@ void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
static_cast<SwitcherData::StartupBehavior>(index); static_cast<SwitcherData::StartupBehavior>(index);
} }
void AdvSceneSwitcher::on_logLevel_currentIndexChanged(int value)
{
if (loading) {
return;
}
switcher->logLevel = static_cast<SwitcherData::LogLevel>(value);
}
void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index) void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
{ {
if (loading) { if (loading) {
@@ -157,15 +165,6 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *)
obs_frontend_save(); obs_frontend_save();
} }
void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state)
{
if (loading) {
return;
}
switcher->verbose = state;
}
void AdvSceneSwitcher::on_saveWindowGeo_stateChanged(int state) void AdvSceneSwitcher::on_saveWindowGeo_stateChanged(int state)
{ {
if (loading) { if (loading) {
@@ -598,7 +597,7 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
obs_data_set_int(obj, "autoStartEvent", obs_data_set_int(obj, "autoStartEvent",
static_cast<int>(autoStartEvent)); static_cast<int>(autoStartEvent));
obs_data_set_bool(obj, "verbose", verbose); obs_data_set_int(obj, "logLevel", static_cast<int>(logLevel));
obs_data_set_bool(obj, "showSystemTrayNotifications", obs_data_set_bool(obj, "showSystemTrayNotifications",
showSystemTrayNotifications); showSystemTrayNotifications);
obs_data_set_bool(obj, "disableHints", disableHints); obs_data_set_bool(obj, "disableHints", disableHints);
@@ -649,7 +648,7 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
autoStartEvent = autoStartEvent =
static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent")); static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent"));
verbose = obs_data_get_bool(obj, "verbose"); logLevel = static_cast<LogLevel>(obs_data_get_int(obj, "logLevel"));
showSystemTrayNotifications = showSystemTrayNotifications =
obs_data_get_bool(obj, "showSystemTrayNotifications"); obs_data_get_bool(obj, "showSystemTrayNotifications");
disableHints = obs_data_get_bool(obj, "disableHints"); disableHints = obs_data_get_bool(obj, "disableHints");
@@ -940,7 +939,8 @@ void AdvSceneSwitcher::SetupGeneralTab()
SIGNAL(DurationChanged(const Duration &)), this, SIGNAL(DurationChanged(const Duration &)), this,
SLOT(CooldownDurationChanged(const Duration &))); SLOT(CooldownDurationChanged(const Duration &)));
ui->verboseLogging->setChecked(switcher->verbose); ui->logLevel->setCurrentIndex(static_cast<int>(switcher->logLevel));
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo); ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
ui->showTrayNotifications->setChecked( ui->showTrayNotifications->setChecked(
switcher->showSystemTrayNotifications); switcher->showSystemTrayNotifications);

View File

@@ -104,7 +104,7 @@ void SwitcherData::checkAudioSwitchFallback(OBSWeakSource &scene,
scene = audioFallback.getScene(); scene = audioFallback.getScene();
transition = audioFallback.transition; transition = audioFallback.transition;
if (verbose) { if (VerboseLoggingEnabled()) {
audioFallback.logMatch(); audioFallback.logMatch();
} }
} }
@@ -165,7 +165,7 @@ bool SwitcherData::checkAudioSwitch(OBSWeakSource &scene,
transition = s.transition; transition = s.transition;
match = true; match = true;
if (verbose) { if (VerboseLoggingEnabled()) {
s.logMatch(); s.logMatch();
} }
@@ -437,10 +437,8 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
audioVolumeThreshold->setMaximum(100); audioVolumeThreshold->setMaximum(100);
audioVolumeThreshold->setMinimum(0); audioVolumeThreshold->setMinimum(0);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)), QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
audioVolumeThreshold, SLOT(setValue(int))); [=](double) { SyncSliderAndValueSelection(true); });
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this, QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this,
SLOT(VolumeThresholdChanged(int))); SLOT(VolumeThresholdChanged(int)));
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
@@ -487,6 +485,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
switchData = s; switchData = s;
loading = false; loading = false;
SyncSliderAndValueSelection(false);
} }
AudioSwitch *AudioSwitchWidget::getSwitchData() AudioSwitch *AudioSwitchWidget::getSwitchData()
@@ -509,6 +508,21 @@ void AudioSwitchWidget::swapSwitchData(AudioSwitchWidget *s1,
s2->setSwitchData(t); s2->setSwitchData(t);
} }
void AudioSwitchWidget::SyncSliderAndValueSelection(bool sliderMoved)
{
if (loading || !switchData) {
return;
}
if (sliderMoved) {
auto sliderPosition = volMeter->GetSlider()->DoubleValue();
audioVolumeThreshold->setValue(sliderPosition);
} else {
volMeter->GetSlider()->SetDoubleVal(
switchData->volumeThreshold);
}
}
void AudioSwitchWidget::UpdateVolmeterSource() void AudioSwitchWidget::UpdateVolmeterSource()
{ {
delete volMeter; delete volMeter;
@@ -520,10 +534,8 @@ void AudioSwitchWidget::UpdateVolmeterSource()
QLayout *layout = this->layout(); QLayout *layout = this->layout();
layout->addWidget(volMeter); layout->addWidget(volMeter);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)), QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
audioVolumeThreshold, SLOT(setValue(int))); [=](double) { SyncSliderAndValueSelection(true); });
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
// Slider will default to 0 so set it manually once // Slider will default to 0 so set it manually once
volMeter->GetSlider()->setValue(switchData->volumeThreshold); volMeter->GetSlider()->setValue(switchData->volumeThreshold);
@@ -547,8 +559,11 @@ void AudioSwitchWidget::VolumeThresholdChanged(int vol)
return; return;
} }
std::lock_guard<std::mutex> lock(switcher->m); {
switchData->volumeThreshold = vol; std::lock_guard<std::mutex> lock(switcher->m);
switchData->volumeThreshold = vol;
}
SyncSliderAndValueSelection(false);
} }
void AudioSwitchWidget::ConditionChanged(int cond) void AudioSwitchWidget::ConditionChanged(int cond)

View File

@@ -79,6 +79,7 @@ private slots:
void ConditionChanged(int cond); void ConditionChanged(int cond);
void DurationChanged(const Duration &); void DurationChanged(const Duration &);
void IgnoreInactiveChanged(int state); void IgnoreInactiveChanged(int state);
void SyncSliderAndValueSelection(bool sliderMoved);
private: private:
QComboBox *audioSources; QComboBox *audioSources;

View File

@@ -119,7 +119,7 @@ bool SwitcherData::checkExeSwitch(OBSWeakSource &scene,
scene = s.getScene(); scene = s.getScene();
transition = s.transition; transition = s.transition;
if (verbose) { if (VerboseLoggingEnabled()) {
s.logMatch(); s.logMatch();
} }
break; break;

View File

@@ -274,7 +274,7 @@ bool SwitcherData::checkFileContent(OBSWeakSource &scene,
transition = s.transition; transition = s.transition;
match = true; match = true;
if (verbose) { if (VerboseLoggingEnabled()) {
s.logMatch(); s.logMatch();
} }
break; break;

View File

@@ -53,7 +53,7 @@ bool SwitcherData::checkIdleSwitch(OBSWeakSource &scene,
match = true; match = true;
idleData.alreadySwitched = true; idleData.alreadySwitched = true;
if (verbose) { if (VerboseLoggingEnabled()) {
idleData.logMatch(); idleData.logMatch();
} }
} else { } else {

View File

@@ -193,7 +193,7 @@ bool SwitcherData::checkMediaSwitch(OBSWeakSource &scene,
scene = mediaSwitch.getScene(); scene = mediaSwitch.getScene();
transition = mediaSwitch.transition; transition = mediaSwitch.transition;
if (verbose) { if (VerboseLoggingEnabled()) {
mediaSwitch.logMatch(); mediaSwitch.logMatch();
} }
} }

View File

@@ -253,7 +253,7 @@ void WSServer::sendMessage(SceneSwitchInfo sceneSwitch, bool preview)
} }
} }
if (switcher->verbose) { if (VerboseLoggingEnabled()) {
blog(LOG_INFO, "server sent message:\n%s", message.c_str()); blog(LOG_INFO, "server sent message:\n%s", message.c_str());
} }
} }
@@ -305,7 +305,7 @@ std::string processMessage(std::string payload)
std::string ret = "message ok"; std::string ret = "message ok";
auto transition = GetWeakTransitionByName(transitionName.c_str()); auto transition = GetWeakTransitionByName(transitionName.c_str());
if (switcher->verbose && !transition) { if (VerboseLoggingEnabled() && !transition) {
ret += " - ignoring invalid transition: '" + transitionName + ret += " - ignoring invalid transition: '" + transitionName +
"'"; "'";
} }
@@ -481,7 +481,7 @@ void WSClient::onMessage(connection_hdl hdl, client::message_ptr message)
errorCodeMessage.c_str()); errorCodeMessage.c_str());
} }
if (switcher->verbose) { if (VerboseLoggingEnabled()) {
blog(LOG_INFO, "client sent message:\n%s", response.c_str()); blog(LOG_INFO, "client sent message:\n%s", response.c_str());
} }
} }

View File

@@ -132,6 +132,6 @@ public:
}; };
QRunnable *CreateFunctionRunnable(std::function<void()> func); QRunnable *CreateFunctionRunnable(std::function<void()> func);
} } // namespace Compatability
} // namespace advss } // namespace advss

View File

@@ -76,7 +76,7 @@ bool SwitcherData::checkRandom(OBSWeakSource &scene, OBSWeakSource &transition,
lastRandomScene = r.scene; lastRandomScene = r.scene;
lastRandomSceneGroup = r.group; lastRandomSceneGroup = r.group;
if (verbose) { if (VerboseLoggingEnabled()) {
r.logMatch(); r.logMatch();
} }
break; break;

View File

@@ -183,7 +183,7 @@ bool SwitcherData::checkScreenRegionSwitch(OBSWeakSource &scene,
transition = s.transition; transition = s.transition;
minRegionSize = regionSize; minRegionSize = regionSize;
if (verbose) { if (VerboseLoggingEnabled()) {
s.logMatch(); s.logMatch();
} }
break; break;

View File

@@ -225,13 +225,13 @@ bool SwitcherData::checkSceneSequence(OBSWeakSource &scene,
scene = s.getScene(); scene = s.getScene();
transition = s.transition; transition = s.transition;
setPrevSceneAfterLinger = s.usePreviousScene; setPrevSceneAfterLinger = s.usePreviousScene;
if (verbose) { if (VerboseLoggingEnabled()) {
s.logMatch(); s.logMatch();
} }
} }
s.advanceActiveSequence(); s.advanceActiveSequence();
if (verbose) { if (VerboseLoggingEnabled()) {
s.logAdvanceSequence(); s.logAdvanceSequence();
} }

View File

@@ -142,7 +142,7 @@ bool SwitcherData::checkTimeSwitch(OBSWeakSource &scene,
transition = s.transition; transition = s.transition;
match = true; match = true;
if (verbose) { if (VerboseLoggingEnabled()) {
s.logMatch(); s.logMatch();
} }
break; break;

View File

@@ -168,7 +168,7 @@ void SwitcherData::checkDefaultSceneTransitions()
for (auto &t : defaultSceneTransitions) { for (auto &t : defaultSceneTransitions) {
if (t.checkMatch(currentScene)) { if (t.checkMatch(currentScene)) {
if (verbose) { if (VerboseLoggingEnabled()) {
t.logMatch(); t.logMatch();
} }
t.setTransition(); t.setTransition();
@@ -427,7 +427,8 @@ DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent,
{ {
QHBoxLayout *mainLayout = new QHBoxLayout; QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", scenes}, {"{{transitions}}", transitions}}; {"{{scenes}}", scenes},
{"{{transitions}}", transitions}};
PlaceWidgets( PlaceWidgets(
obs_module_text( obs_module_text(
"AdvSceneSwitcher.transitionTab.defaultTransitionEntry"), "AdvSceneSwitcher.transitionTab.defaultTransitionEntry"),

View File

@@ -148,7 +148,7 @@ bool SwitcherData::checkVideoSwitch(OBSWeakSource &scene,
match = true; match = true;
scene = s.getScene(); scene = s.getScene();
transition = s.transition; transition = s.transition;
if (verbose) { if (VerboseLoggingEnabled()) {
s.logMatch(); s.logMatch();
} }
} }

View File

@@ -249,7 +249,7 @@ bool SwitcherData::checkWindowTitleSwitch(OBSWeakSource &scene,
} }
if (match) { if (match) {
if (verbose) { if (VerboseLoggingEnabled()) {
s.logMatch(); s.logMatch();
} }
break; break;

View File

@@ -3,6 +3,7 @@
#include "macro-helpers.hpp" #include "macro-helpers.hpp"
#include "macro-properties.hpp" #include "macro-properties.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "plugin-state-helpers.hpp"
#include "section.hpp" #include "section.hpp"
#include "switch-button.hpp" #include "switch-button.hpp"
@@ -88,6 +89,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
*_entryData = MacroActionFactory::Create(id, macro); *_entryData = MacroActionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx); (*_entryData)->SetIndex(idx);
(*_entryData)->PostLoad(); (*_entryData)->PostLoad();
RunPostLoadSteps();
} }
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData); auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)), QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
@@ -185,9 +187,10 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
auto data = obs_data_create(); auto data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data); macro->Actions().at(idx - 1)->Save(data);
macro->Actions().at(idx)->Load(data); macro->Actions().at(idx)->Load(data);
macro->Actions().at(idx)->PostLoad();
obs_data_release(data); obs_data_release(data);
} }
macro->Actions().at(idx)->PostLoad();
RunPostLoadSteps();
macro->UpdateActionIndices(); macro->UpdateActionIndices();
ui->actionsList->Insert( ui->actionsList->Insert(
idx, idx,
@@ -475,6 +478,7 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
macro->ElseActions().at(idx)->Load(data); macro->ElseActions().at(idx)->Load(data);
} }
macro->ElseActions().at(idx)->PostLoad(); macro->ElseActions().at(idx)->PostLoad();
RunPostLoadSteps();
macro->UpdateElseActionIndices(); macro->UpdateElseActionIndices();
ui->elseActionsList->Insert( ui->elseActionsList->Insert(
idx, new MacroActionEdit( idx, new MacroActionEdit(

View File

@@ -88,32 +88,32 @@ void MacroActionMacro::LogAction() const
} }
switch (_action) { switch (_action) {
case Action::PAUSE: case Action::PAUSE:
vblog(LOG_INFO, "paused \"%s\"", macro->Name().c_str()); ablog(LOG_INFO, "paused \"%s\"", macro->Name().c_str());
break; break;
case Action::UNPAUSE: case Action::UNPAUSE:
vblog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str()); ablog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str());
break; break;
case Action::RESET_COUNTER: case Action::RESET_COUNTER:
vblog(LOG_INFO, "reset counter for \"%s\"", ablog(LOG_INFO, "reset counter for \"%s\"",
macro->Name().c_str()); macro->Name().c_str());
break; break;
case Action::RUN: case Action::RUN:
vblog(LOG_INFO, "run nested macro \"%s\"", ablog(LOG_INFO, "run nested macro \"%s\"",
macro->Name().c_str()); macro->Name().c_str());
break; break;
case Action::STOP: case Action::STOP:
vblog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str()); ablog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str());
break; break;
case Action::DISABLE_ACTION: case Action::DISABLE_ACTION:
vblog(LOG_INFO, "disabled action %d of macro \"%s\"", ablog(LOG_INFO, "disabled action %d of macro \"%s\"",
_actionIndex.GetValue(), macro->Name().c_str()); _actionIndex.GetValue(), macro->Name().c_str());
break; break;
case Action::ENABLE_ACTION: case Action::ENABLE_ACTION:
vblog(LOG_INFO, "enabled action %d of macro \"%s\"", ablog(LOG_INFO, "enabled action %d of macro \"%s\"",
_actionIndex.GetValue(), macro->Name().c_str()); _actionIndex.GetValue(), macro->Name().c_str());
break; break;
case Action::TOGGLE_ACTION: case Action::TOGGLE_ACTION:
vblog(LOG_INFO, "toggled action %d of macro \"%s\"", ablog(LOG_INFO, "toggled action %d of macro \"%s\"",
_actionIndex.GetValue(), macro->Name().c_str()); _actionIndex.GetValue(), macro->Name().c_str());
break; break;
default: default:
@@ -145,6 +145,21 @@ std::string MacroActionMacro::GetShortDesc() const
return _macro.Name(); return _macro.Name();
} }
std::shared_ptr<MacroAction> MacroActionMacro::Create(Macro *m)
{
return std::make_shared<MacroActionMacro>(m);
}
std::shared_ptr<MacroAction> MacroActionMacro::Copy() const
{
return std::make_shared<MacroActionMacro>(*this);
}
void MacroActionMacro::ResolveVariablesToFixedValues()
{
_actionIndex.ResolveVariables();
}
static inline void populateActionSelection(QComboBox *list) static inline void populateActionSelection(QComboBox *list)
{ {
for (auto entry : actionTypes) { for (auto entry : actionTypes) {

View File

@@ -16,10 +16,9 @@ public:
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc() const; std::string GetShortDesc() const;
std::string GetId() const { return id; }; std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m) static std::shared_ptr<MacroAction> Create(Macro *m);
{ std::shared_ptr<MacroAction> Copy() const;
return std::make_shared<MacroActionMacro>(m); void ResolveVariablesToFixedValues();
}
enum class Action { enum class Action {
PAUSE, PAUSE,

View File

@@ -31,7 +31,9 @@ void MacroActionQueue::AddActions(ActionQueue *queue)
auto actions = *GetMacroActions(macro.get()); auto actions = *GetMacroActions(macro.get());
for (const auto &action : actions) { for (const auto &action : actions) {
queue->Add(action); if (action->Enabled()) {
queue->Add(action);
}
} }
} }
@@ -69,20 +71,20 @@ void MacroActionQueue::LogAction() const
} }
switch (_action) { switch (_action) {
case Action::ADD_TO_QUEUE: case Action::ADD_TO_QUEUE:
vblog(LOG_INFO, "queued actions of \"%s\" to \"%s\"", ablog(LOG_INFO, "queued actions of \"%s\" to \"%s\"",
GetMacroName(macro.get()).c_str(), GetMacroName(macro.get()).c_str(),
GetActionQueueName(_queue).c_str()); GetActionQueueName(_queue).c_str());
break; break;
case Action::START_QUEUE: case Action::START_QUEUE:
vblog(LOG_INFO, "start queue \"%s\"", ablog(LOG_INFO, "start queue \"%s\"",
GetActionQueueName(_queue).c_str()); GetActionQueueName(_queue).c_str());
break; break;
case Action::STOP_QUEUE: case Action::STOP_QUEUE:
vblog(LOG_INFO, "stop queue \"%s\"", ablog(LOG_INFO, "stop queue \"%s\"",
GetActionQueueName(_queue).c_str()); GetActionQueueName(_queue).c_str());
break; break;
case Action::CLEAR_QUEUE: case Action::CLEAR_QUEUE:
vblog(LOG_INFO, "cleared queue \"%s\"", ablog(LOG_INFO, "cleared queue \"%s\"",
GetActionQueueName(_queue).c_str()); GetActionQueueName(_queue).c_str());
break; break;
default: default:
@@ -114,6 +116,16 @@ std::string MacroActionQueue::GetShortDesc() const
return GetActionQueueName(_queue); return GetActionQueueName(_queue);
} }
std::shared_ptr<MacroAction> MacroActionQueue::Create(Macro *m)
{
return std::make_shared<MacroActionQueue>(m);
}
std::shared_ptr<MacroAction> MacroActionQueue::Copy() const
{
return std::make_shared<MacroActionQueue>(*this);
}
static inline void populateActionSelection(QComboBox *list) static inline void populateActionSelection(QComboBox *list)
{ {
for (const auto &[_, name] : actionTypes) { for (const auto &[_, name] : actionTypes) {

View File

@@ -16,10 +16,8 @@ public:
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc() const; std::string GetShortDesc() const;
std::string GetId() const { return id; }; std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m) static std::shared_ptr<MacroAction> Create(Macro *m);
{ std::shared_ptr<MacroAction> Copy() const;
return std::make_shared<MacroActionQueue>(m);
}
enum class Action { enum class Action {
ADD_TO_QUEUE, ADD_TO_QUEUE,

View File

@@ -54,6 +54,10 @@ const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
"AdvSceneSwitcher.action.variable.type.setToTempvar"}, "AdvSceneSwitcher.action.variable.type.setToTempvar"},
{MacroActionVariable::Type::SCENE_ITEM_NAME, {MacroActionVariable::Type::SCENE_ITEM_NAME,
"AdvSceneSwitcher.action.variable.type.sceneItemName"}, "AdvSceneSwitcher.action.variable.type.sceneItemName"},
{MacroActionVariable::Type::PAD,
"AdvSceneSwitcher.action.variable.type.padValue"},
{MacroActionVariable::Type::TRUNCATE,
"AdvSceneSwitcher.action.variable.type.truncateValue"},
}; };
static void apppend(Variable &var, const std::string &value) static void apppend(Variable &var, const std::string &value)
@@ -198,7 +202,8 @@ void MacroActionVariable::SetToSceneItemName(Variable *var)
struct AskForInputParams { struct AskForInputParams {
AskForInputParams(const QString &prompt_, const QString &placeholder_) AskForInputParams(const QString &prompt_, const QString &placeholder_)
: prompt(prompt_), placeholder(placeholder_){}; : prompt(prompt_),
placeholder(placeholder_){};
QString prompt; QString prompt;
QString placeholder; QString placeholder;
std::optional<std::string> result; std::optional<std::string> result;
@@ -216,6 +221,38 @@ static void askForInput(void *param)
parameters->resultReady = true; parameters->resultReady = true;
} }
static std::string truncate(const std::string &input,
MacroActionVariable::Direction direction,
size_t length)
{
if (input.length() <= length) {
return input;
}
if (direction == MacroActionVariable::Direction::RIGHT) {
return input.substr(0, length);
} else {
return input.substr(input.length() - length);
}
}
static std::string pad(const std::string &input,
MacroActionVariable::Direction direction, size_t length,
char paddingChar)
{
if (input.length() >= length) {
return input;
}
if (direction == MacroActionVariable::Direction::RIGHT) {
return input +
std::string(length - input.length(), paddingChar);
} else {
return std::string(length - input.length(), paddingChar) +
input;
}
}
bool MacroActionVariable::PerformAction() bool MacroActionVariable::PerformAction()
{ {
auto var = _variable.lock(); auto var = _variable.lock();
@@ -310,7 +347,10 @@ bool MacroActionVariable::PerformAction()
return true; return true;
} }
case Type::ENV_VARIABLE: { case Type::ENV_VARIABLE: {
var->SetValue(std::getenv(_envVariableName.c_str())); auto value = std::getenv(_envVariableName.c_str());
if (value) {
var->SetValue(value);
}
return true; return true;
} }
case Type::SCENE_ITEM_COUNT: { case Type::SCENE_ITEM_COUNT: {
@@ -344,6 +384,14 @@ bool MacroActionVariable::PerformAction()
case Type::SCENE_ITEM_NAME: case Type::SCENE_ITEM_NAME:
SetToSceneItemName(var.get()); SetToSceneItemName(var.get());
return true; return true;
case Type::PAD:
var->SetValue(pad(var->Value(), _direction, _stringLength,
_paddingChar));
return true;
case Type::TRUNCATE:
var->SetValue(
truncate(var->Value(), _direction, _stringLength));
return true;
} }
return true; return true;
@@ -377,6 +425,9 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
_scene.Save(obj); _scene.Save(obj);
_tempVar.Save(obj); _tempVar.Save(obj);
_sceneItemIndex.Save(obj, "sceneItemIndex"); _sceneItemIndex.Save(obj, "sceneItemIndex");
obs_data_set_int(obj, "direction", static_cast<int>(_direction));
_stringLength.Save(obj, "stringLength");
obs_data_set_int(obj, "paddingChar", _paddingChar);
return true; return true;
} }
@@ -408,6 +459,13 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_scene.Load(obj); _scene.Load(obj);
_tempVar.Load(obj, GetMacro()); _tempVar.Load(obj, GetMacro());
_sceneItemIndex.Load(obj, "sceneItemIndex"); _sceneItemIndex.Load(obj, "sceneItemIndex");
_direction = static_cast<Direction>(obs_data_get_int(obj, "direction"));
_stringLength.Load(obj, "stringLength");
if (obs_data_has_user_value(obj, "paddingChar")) {
_paddingChar = obs_data_get_int(obj, "paddingChar");
} else {
_paddingChar = ' ';
}
return true; return true;
} }
@@ -422,6 +480,16 @@ std::string MacroActionVariable::GetShortDesc() const
return GetWeakVariableName(_variable); return GetWeakVariableName(_variable);
} }
std::shared_ptr<MacroAction> MacroActionVariable::Create(Macro *m)
{
return std::make_shared<MacroActionVariable>(m);
}
std::shared_ptr<MacroAction> MacroActionVariable::Copy() const
{
return std::make_shared<MacroActionVariable>(*this);
}
void MacroActionVariable::SetSegmentIndexValue(int value) void MacroActionVariable::SetSegmentIndexValue(int value)
{ {
DecrementCurrentSegmentVariableRef(); DecrementCurrentSegmentVariableRef();
@@ -485,6 +553,19 @@ int MacroActionVariable::GetSegmentIndexValue() const
return -1; return -1;
} }
void MacroActionVariable::ResolveVariablesToFixedValues()
{
_strValue.ResolveVariables();
_findStr.ResolveVariables();
_replaceStr.ResolveVariables();
_mathExpression.ResolveVariables();
_inputPrompt.ResolveVariables();
_inputPlaceholder.ResolveVariables();
_envVariableName.ResolveVariables();
_scene.ResolveVariables();
_sceneItemIndex.ResolveVariables();
}
void MacroActionVariable::DecrementCurrentSegmentVariableRef() void MacroActionVariable::DecrementCurrentSegmentVariableRef()
{ {
auto segment = _macroSegment.lock(); auto segment = _macroSegment.lock();
@@ -503,6 +584,16 @@ static inline void populateTypeSelection(QComboBox *list)
} }
} }
static inline void populateDirectionSelection(QComboBox *list)
{
list->addItems(
QStringList()
<< obs_module_text(
"AdvSceneSwitcher.action.variable.truncateOrPadDirection.left")
<< obs_module_text(
"AdvSceneSwitcher.action.variable.truncateOrPadDirection.right"));
}
MacroActionVariableEdit::MacroActionVariableEdit( MacroActionVariableEdit::MacroActionVariableEdit(
QWidget *parent, std::shared_ptr<MacroActionVariable> entryData) QWidget *parent, std::shared_ptr<MacroActionVariable> entryData)
: QWidget(parent), : QWidget(parent),
@@ -540,6 +631,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
true)), true)),
_tempVars(new TempVariableSelection(this)), _tempVars(new TempVariableSelection(this)),
_sceneItemIndex(new VariableSpinBox()), _sceneItemIndex(new VariableSpinBox()),
_direction(new QComboBox()),
_stringLength(new VariableSpinBox()),
_paddingCharSelection(new SingleCharSelection()),
_entryLayout(new QHBoxLayout()) _entryLayout(new QHBoxLayout())
{ {
_numValue->setMinimum(-9999999999); _numValue->setMinimum(-9999999999);
@@ -559,7 +653,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding, _inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred); QSizePolicy::Preferred);
_sceneItemIndex->setMinimum(1); _sceneItemIndex->setMinimum(1);
_stringLength->setMaximum(999);
populateTypeSelection(_actions); populateTypeSelection(_actions);
populateDirectionSelection(_direction);
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)), QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
this, SLOT(VariableChanged(const QString &))); this, SLOT(VariableChanged(const QString &)));
@@ -615,8 +711,17 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_sceneItemIndex, _sceneItemIndex,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)), SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(SceneItemIndexChanged(const NumberVariable<int> &))); this, SLOT(SceneItemIndexChanged(const NumberVariable<int> &)));
QWidget::connect(_direction, SIGNAL(currentIndexChanged(int)), this,
SLOT(DirectionChanged(int)));
QWidget::connect(
_stringLength,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(StringLengthChanged(const NumberVariable<int> &)));
QWidget::connect(_paddingCharSelection,
SIGNAL(CharChanged(const QString &)), this,
SLOT(CharSelectionChanged(const QString &)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{variables}}", _variables}, {"{{variables}}", _variables},
{"{{variables2}}", _variables2}, {"{{variables2}}", _variables2},
{"{{actions}}", _actions}, {"{{actions}}", _actions},
@@ -638,9 +743,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
{"{{scenes}}", _scenes}, {"{{scenes}}", _scenes},
{"{{tempVars}}", _tempVars}, {"{{tempVars}}", _tempVars},
{"{{sceneItemIndex}}", _sceneItemIndex}, {"{{sceneItemIndex}}", _sceneItemIndex},
{"{{direction}}", _direction},
{"{{stringLength}}", _stringLength},
{"{{paddingCharSelection}}", _paddingCharSelection},
}; };
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"), PlaceWidgets(
_entryLayout, widgetPlaceholders); obs_module_text("AdvSceneSwitcher.action.variable.entry.other"),
_entryLayout, widgetPlaceholders);
PlaceWidgets( PlaceWidgets(
obs_module_text( obs_module_text(
@@ -733,6 +842,10 @@ void MacroActionVariableEdit::UpdateEntryData()
_scenes->SetScene(_entryData->_scene); _scenes->SetScene(_entryData->_scene);
_tempVars->SetVariable(_entryData->_tempVar); _tempVars->SetVariable(_entryData->_tempVar);
_sceneItemIndex->SetValue(_entryData->_sceneItemIndex); _sceneItemIndex->SetValue(_entryData->_sceneItemIndex);
_direction->setCurrentIndex(static_cast<int>(_entryData->_direction));
_stringLength->SetValue(_entryData->_stringLength);
_paddingCharSelection->setText(
QChar::fromLatin1(_entryData->_paddingChar));
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -1102,12 +1215,84 @@ void MacroActionVariableEdit::SceneItemIndexChanged(
_entryData->_sceneItemIndex = value; _entryData->_sceneItemIndex = value;
} }
void MacroActionVariableEdit::DirectionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_direction =
static_cast<MacroActionVariable::Direction>(value);
}
void MacroActionVariableEdit::StringLengthChanged(
const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_stringLength = value;
}
void MacroActionVariableEdit::CharSelectionChanged(const QString &character)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
if (character.isEmpty()) {
_entryData->_paddingChar = ' ';
} else {
_entryData->_paddingChar = character.toStdString().at(0);
}
}
void MacroActionVariableEdit::SetWidgetVisibility() void MacroActionVariableEdit::SetWidgetVisibility()
{ {
if (!_entryData) { if (!_entryData) {
return; return;
} }
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{variables}}", _variables},
{"{{variables2}}", _variables2},
{"{{actions}}", _actions},
{"{{strValue}}", _strValue},
{"{{numValue}}", _numValue},
{"{{segmentIndex}}", _segmentIdx},
{"{{mathExpression}}", _mathExpression},
{"{{envVariableName}}", _envVariable},
{"{{scenes}}", _scenes},
{"{{tempVars}}", _tempVars},
{"{{sceneItemIndex}}", _sceneItemIndex},
{"{{direction}}", _direction},
{"{{stringLength}}", _stringLength},
{"{{paddingCharSelection}}", _paddingCharSelection},
};
const char *layoutString = "";
if (_entryData->_type == MacroActionVariable::Type::PAD) {
layoutString = obs_module_text(
"AdvSceneSwitcher.action.variable.entry.pad");
} else if (_entryData->_type == MacroActionVariable::Type::TRUNCATE) {
layoutString = obs_module_text(
"AdvSceneSwitcher.action.variable.entry.truncate");
} else {
layoutString = obs_module_text(
"AdvSceneSwitcher.action.variable.entry.other");
}
for (const auto &[_, widget] : widgetPlaceholders) {
_entryLayout->removeWidget(widget);
}
ClearLayout(_entryLayout);
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
if (_entryData->_type == MacroActionVariable::Type::SET_FIXED_VALUE || if (_entryData->_type == MacroActionVariable::Type::SET_FIXED_VALUE ||
_entryData->_type == MacroActionVariable::Type::APPEND || _entryData->_type == MacroActionVariable::Type::APPEND ||
_entryData->_type == MacroActionVariable::Type::MATH_EXPRESSION || _entryData->_type == MacroActionVariable::Type::MATH_EXPRESSION ||
@@ -1198,6 +1383,14 @@ void MacroActionVariableEdit::SetWidgetVisibility()
MacroActionVariable::Type::SET_TO_TEMPVAR); MacroActionVariable::Type::SET_TO_TEMPVAR);
_sceneItemIndex->setVisible(_entryData->_type == _sceneItemIndex->setVisible(_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_NAME); MacroActionVariable::Type::SCENE_ITEM_NAME);
_direction->setVisible(
_entryData->_type == MacroActionVariable::Type::PAD ||
_entryData->_type == MacroActionVariable::Type::TRUNCATE);
_stringLength->setVisible(
_entryData->_type == MacroActionVariable::Type::PAD ||
_entryData->_type == MacroActionVariable::Type::TRUNCATE);
_paddingCharSelection->setVisible(_entryData->_type ==
MacroActionVariable::Type::PAD);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();
} }

View File

@@ -4,6 +4,7 @@
#include "regex-config.hpp" #include "regex-config.hpp"
#include "resizing-text-edit.hpp" #include "resizing-text-edit.hpp"
#include "scene-selection.hpp" #include "scene-selection.hpp"
#include "single-char-selection.hpp"
#include "variable-line-edit.hpp" #include "variable-line-edit.hpp"
#include "variable-text-edit.hpp" #include "variable-text-edit.hpp"
#include "variable-spinbox.hpp" #include "variable-spinbox.hpp"
@@ -17,15 +18,14 @@ public:
bool PerformAction(); bool PerformAction();
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
bool PostLoad() override; bool PostLoad();
std::string GetShortDesc() const; std::string GetShortDesc() const;
std::string GetId() const { return id; }; std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m) static std::shared_ptr<MacroAction> Create(Macro *m);
{ std::shared_ptr<MacroAction> Copy() const;
return std::make_shared<MacroActionVariable>(m);
}
void SetSegmentIndexValue(int); void SetSegmentIndexValue(int);
int GetSegmentIndexValue() const; int GetSegmentIndexValue() const;
void ResolveVariablesToFixedValues();
enum class Type { enum class Type {
SET_FIXED_VALUE, SET_FIXED_VALUE,
@@ -46,6 +46,8 @@ public:
EXTRACT_JSON, EXTRACT_JSON,
SET_TO_TEMPVAR, SET_TO_TEMPVAR,
SCENE_ITEM_NAME, SCENE_ITEM_NAME,
PAD,
TRUNCATE,
}; };
Type _type = Type::SET_FIXED_VALUE; Type _type = Type::SET_FIXED_VALUE;
@@ -80,6 +82,11 @@ public:
TempVariableRef _tempVar; TempVariableRef _tempVar;
IntVariable _sceneItemIndex = 1; IntVariable _sceneItemIndex = 1;
enum class Direction { LEFT, RIGHT };
Direction _direction = Direction::LEFT;
IntVariable _stringLength = 1;
char _paddingChar = '0';
private: private:
void DecrementCurrentSegmentVariableRef(); void DecrementCurrentSegmentVariableRef();
void HandleIndexSubString(Variable *); void HandleIndexSubString(Variable *);
@@ -136,6 +143,9 @@ private slots:
void SceneChanged(const SceneSelection &); void SceneChanged(const SceneSelection &);
void SelectionChanged(const TempVariableRef &var); void SelectionChanged(const TempVariableRef &var);
void SceneItemIndexChanged(const NumberVariable<int> &); void SceneItemIndexChanged(const NumberVariable<int> &);
void DirectionChanged(int);
void StringLengthChanged(const NumberVariable<int> &);
void CharSelectionChanged(const QString &);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -176,6 +186,9 @@ private:
SceneSelectionWidget *_scenes; SceneSelectionWidget *_scenes;
TempVariableSelection *_tempVars; TempVariableSelection *_tempVars;
VariableSpinBox *_sceneItemIndex; VariableSpinBox *_sceneItemIndex;
QComboBox *_direction;
VariableSpinBox *_stringLength;
SingleCharSelection *_paddingCharSelection;
QHBoxLayout *_entryLayout; QHBoxLayout *_entryLayout;
std::shared_ptr<MacroActionVariable> _entryData; std::shared_ptr<MacroActionVariable> _entryData;

View File

@@ -25,7 +25,7 @@ bool MacroAction::Load(obs_data_t *obj)
void MacroAction::LogAction() const void MacroAction::LogAction() const
{ {
vblog(LOG_INFO, "performed action %s", GetId().c_str()); ablog(LOG_INFO, "performed action %s", GetId().c_str());
} }
void MacroAction::SetEnabled(bool value) void MacroAction::SetEnabled(bool value)
@@ -38,6 +38,8 @@ bool MacroAction::Enabled() const
return _enabled; return _enabled;
} }
void MacroAction::ResolveVariablesToFixedValues() {}
std::string_view MacroAction::GetDefaultID() std::string_view MacroAction::GetDefaultID()
{ {
return "scene_switch"; return "scene_switch";

View File

@@ -8,10 +8,17 @@ class EXPORT MacroAction : public MacroSegment {
public: public:
MacroAction(Macro *m, bool supportsVariableValue = false); MacroAction(Macro *m, bool supportsVariableValue = false);
virtual ~MacroAction() = default; virtual ~MacroAction() = default;
virtual std::shared_ptr<MacroAction> Copy() const = 0;
virtual bool PerformAction() = 0; virtual bool PerformAction() = 0;
virtual void LogAction() const;
virtual bool Save(obs_data_t *obj) const = 0; virtual bool Save(obs_data_t *obj) const = 0;
virtual bool Load(obs_data_t *obj) = 0; virtual bool Load(obs_data_t *obj) = 0;
virtual void LogAction() const;
// Used to resolve variables before actions are added to action queues
virtual void ResolveVariablesToFixedValues();
void SetEnabled(bool); void SetEnabled(bool);
bool Enabled() const; bool Enabled() const;

View File

@@ -3,6 +3,7 @@
#include "macro-properties.hpp" #include "macro-properties.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "path-helpers.hpp" #include "path-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "section.hpp" #include "section.hpp"
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -254,6 +255,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
(*_entryData)->SetIndex(idx); (*_entryData)->SetIndex(idx);
(*_entryData)->SetLogicType(logic); (*_entryData)->SetLogicType(logic);
(*_entryData)->PostLoad(); (*_entryData)->PostLoad();
RunPostLoadSteps();
} }
auto widget = auto widget =
MacroConditionFactory::CreateWidget(id, this, *_entryData); MacroConditionFactory::CreateWidget(id, this, *_entryData);
@@ -325,6 +327,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
obs_data_release(data); obs_data_release(data);
} }
macro->Conditions().at(idx)->PostLoad(); macro->Conditions().at(idx)->PostLoad();
RunPostLoadSteps();
(*cond)->SetLogicType(logic); (*cond)->SetLogicType(logic);
macro->UpdateConditionIndices(); macro->UpdateConditionIndices();
ui->conditionsList->Insert( ui->conditionsList->Insert(

View File

@@ -25,7 +25,7 @@ public:
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
bool PostLoad() override; bool PostLoad();
std::string GetShortDesc() const; std::string GetShortDesc() const;
std::string GetId() const { return id; }; std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m) static std::shared_ptr<MacroCondition> Create(Macro *m)

View File

@@ -9,7 +9,8 @@
namespace advss { namespace advss {
MacroExportImportDialog::MacroExportImportDialog(Type type) MacroExportImportDialog::MacroExportImportDialog(Type type)
: QDialog(nullptr), _importExportString(new QPlainTextEdit(this)) : QDialog(nullptr),
_importExportString(new QPlainTextEdit(this))
{ {
_importExportString->setReadOnly(type == Type::EXPORT_MACRO); _importExportString->setReadOnly(type == Type::EXPORT_MACRO);
auto label = new QLabel(obs_module_text( auto label = new QLabel(obs_module_text(

View File

@@ -54,6 +54,8 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))), "AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
_currentSkipOnStartup(new QCheckBox(obs_module_text( _currentSkipOnStartup(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))), "AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
_currentStopActionsIfNotDone(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone"))),
_currentMacroRegisterDock(new QCheckBox(obs_module_text( _currentMacroRegisterDock(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentRegisterDock"))), "AdvSceneSwitcher.macroTab.currentRegisterDock"))),
_currentMacroDockAddRunButton(new QCheckBox(obs_module_text( _currentMacroDockAddRunButton(new QCheckBox(obs_module_text(
@@ -96,6 +98,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings")); obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
auto generalLayout = new QVBoxLayout; auto generalLayout = new QVBoxLayout;
generalLayout->addWidget(_currentSkipOnStartup); generalLayout->addWidget(_currentSkipOnStartup);
generalLayout->addWidget(_currentStopActionsIfNotDone);
generalOptions->setLayout(generalLayout); generalOptions->setLayout(generalLayout);
int row = 0; int row = 0;
@@ -184,14 +187,17 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
_conditions->setChecked(prop._highlightConditions); _conditions->setChecked(prop._highlightConditions);
_actions->setChecked(prop._highlightActions); _actions->setChecked(prop._highlightActions);
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys); _newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys);
if (!macro || macro->IsGroup()) { if (!macro || macro->IsGroup()) {
hotkeyOptions->hide(); hotkeyOptions->hide();
generalOptions->hide(); generalOptions->hide();
_dockOptions->hide(); _dockOptions->hide();
return; return;
} }
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled()); _currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart()); _currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
_currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone());
const bool dockEnabled = macro->DockEnabled(); const bool dockEnabled = macro->DockEnabled();
_currentMacroRegisterDock->setChecked(dockEnabled); _currentMacroRegisterDock->setChecked(dockEnabled);
_currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton()); _currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton());
@@ -260,6 +266,7 @@ void MacroPropertiesDialog::DockEnableChanged(int enabled)
SetGridLayoutRowVisible( SetGridLayoutRowVisible(
_dockLayout, _conditionsFalseTextRow, _dockLayout, _conditionsFalseTextRow,
enabled && _currentMacroDockAddStatusLabel->isChecked()); enabled && _currentMacroDockAddStatusLabel->isChecked());
Resize(); Resize();
} }
@@ -310,6 +317,8 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
macro->EnablePauseHotkeys( macro->EnablePauseHotkeys(
dialog._currentMacroRegisterHotkeys->isChecked()); dialog._currentMacroRegisterHotkeys->isChecked());
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked()); macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
macro->SetStopActionsIfNotDone(
dialog._currentStopActionsIfNotDone->isChecked());
macro->EnableDock(dialog._currentMacroRegisterDock->isChecked()); macro->EnableDock(dialog._currentMacroRegisterDock->isChecked());
macro->SetDockHasRunButton( macro->SetDockHasRunButton(
dialog._currentMacroDockAddRunButton->isChecked()); dialog._currentMacroDockAddRunButton->isChecked());
@@ -328,6 +337,7 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
dialog._conditionsTrueStatusText->text().toStdString()); dialog._conditionsTrueStatusText->text().toStdString());
macro->SetConditionsFalseStatusText( macro->SetConditionsFalseStatusText(
dialog._conditionsFalseStatusText->text().toStdString()); dialog._conditionsFalseStatusText->text().toStdString());
return true; return true;
} }

View File

@@ -49,6 +49,7 @@ private:
// Current macro specific settings // Current macro specific settings
QCheckBox *_currentMacroRegisterHotkeys; QCheckBox *_currentMacroRegisterHotkeys;
QCheckBox *_currentSkipOnStartup; QCheckBox *_currentSkipOnStartup;
QCheckBox *_currentStopActionsIfNotDone;
QCheckBox *_currentMacroRegisterDock; QCheckBox *_currentMacroRegisterDock;
QCheckBox *_currentMacroDockAddRunButton; QCheckBox *_currentMacroDockAddRunButton;
QCheckBox *_currentMacroDockAddPauseButton; QCheckBox *_currentMacroDockAddPauseButton;

View File

@@ -96,11 +96,15 @@ void MacroRunButton::Pressed()
bool ret = _elseStateActive ? macro->PerformActions(false, true, true) bool ret = _elseStateActive ? macro->PerformActions(false, true, true)
: macro->PerformActions(true, true, true); : macro->PerformActions(true, true, true);
if (ret) {
return;
}
if (!ret) { QString err = obs_module_text("AdvSceneSwitcher.macroTab.runFail");
QString err = const bool abortMacro = DisplayMessage(
obs_module_text("AdvSceneSwitcher.macroTab.runFail"); err.arg(QString::fromStdString(macro->Name())), true);
DisplayMessage(err.arg(QString::fromStdString(macro->Name()))); if (abortMacro) {
macro->Stop();
} }
} }

View File

@@ -4,7 +4,6 @@
#include "section.hpp" #include "section.hpp"
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include <obs.hpp>
#include <QApplication> #include <QApplication>
#include <QEvent> #include <QEvent>
#include <QLabel> #include <QLabel>
@@ -14,7 +13,8 @@
namespace advss { namespace advss {
MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue) MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue)
: _macro(m), _supportsVariableValue(supportsVariableValue) : _macro(m),
_supportsVariableValue(supportsVariableValue)
{ {
} }

View File

@@ -11,7 +11,7 @@
#include <QFrame> #include <QFrame>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QTimer> #include <QTimer>
#include <obs.hpp> #include <obs-data.h>
class QLabel; class QLabel;
@@ -116,8 +116,8 @@ private:
void ShowDropLine(DropLineState); void ShowDropLine(DropLineState);
// The reason for using two separate frame widget each with their own // The reason for using two separate frame widgets, each with their own
// stylesheet and changing their visibility vs. using a single frame // stylesheet, and changing their visibility vs. using a single frame
// and changing the stylesheet at runtime is that the operation of // and changing the stylesheet at runtime is that the operation of
// adjusting the stylesheet is very expensive and can take multiple // adjusting the stylesheet is very expensive and can take multiple
// hundred milliseconds per widget. // hundred milliseconds per widget.

View File

@@ -1,4 +1,5 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "action-queue.hpp"
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "macro-export-import-dialog.hpp" #include "macro-export-import-dialog.hpp"
@@ -13,6 +14,7 @@
#include "utility.hpp" #include "utility.hpp"
#include "version.h" #include "version.h"
#include <obs-frontend-api.h>
#include <QColor> #include <QColor>
#include <QGraphicsOpacityEffect> #include <QGraphicsOpacityEffect>
#include <QMenu> #include <QMenu>
@@ -28,6 +30,25 @@ static bool macroNameExists(const std::string &name)
return !!GetMacroByName(name.c_str()); return !!GetMacroByName(name.c_str());
} }
static bool newMacroNameIsValid(const std::string &name)
{
if (!macroNameExists(name)) {
return true;
}
auto macro = GetMacroByName(name.c_str());
if (!macro) {
return false;
}
QString fmt = obs_module_text(
macro->IsGroup() ? "AdvSceneSwitcher.macroTab.groupNameExists"
: "AdvSceneSwitcher.macroTab.macroNameExists");
DisplayMessage(fmt.arg(QString::fromStdString(name)));
return false;
}
bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res, bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
std::string &name, std::string format) std::string &name, std::string format)
{ {
@@ -59,9 +80,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
return false; return false;
} }
if (macroNameExists(name)) { if (!newMacroNameIsValid(name)) {
DisplayMessage(
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
return false; return false;
} }
@@ -92,7 +111,7 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> &macro)
auto name = QString::fromStdString(macro->Name()); auto name = QString::fromStdString(macro->Name());
if (macro->IsGroup() && macro->GroupSize() > 0) { if (macro->IsGroup() && macro->GroupSize() > 0) {
QString deleteWarning = obs_module_text( QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.macroTab.groupDeleteConfirm"); "AdvSceneSwitcher.macroTab.removeGroupPopup.text");
if (!DisplayMessage(deleteWarning.arg(name), true)) { if (!DisplayMessage(deleteWarning.arg(name), true)) {
return; return;
} }
@@ -102,6 +121,41 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> &macro)
emit MacroRemoved(name); emit MacroRemoved(name);
} }
void AdvSceneSwitcher::RemoveSelectedMacros()
{
auto macros = GetSelectedMacros();
if (macros.empty()) {
return;
}
int macroCount = macros.size();
if (macroCount == 1) {
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text");
auto macro = macros.at(0);
deleteWarning = deleteWarning.arg(
QString::fromStdString(macro->Name()));
if ((!macro->IsGroup() || macro->GroupSize() == 0) &&
!DisplayMessage(deleteWarning, true)) {
return;
}
RemoveMacro(macro);
return;
}
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text");
if (!DisplayMessage(deleteWarning.arg(macroCount), true)) {
return;
}
for (auto &macro : macros) {
RemoveMacro(macro);
}
}
void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> &macro, void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> &macro,
const QString &name) const QString &name)
{ {
@@ -115,32 +169,7 @@ void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> &macro,
void AdvSceneSwitcher::on_macroRemove_clicked() void AdvSceneSwitcher::on_macroRemove_clicked()
{ {
auto macros = GetSelectedMacros(); RemoveSelectedMacros();
if (macros.empty()) {
return;
}
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;
}
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation");
if (!DisplayMessage(deleteWarning.arg(macros.size()), true)) {
return;
}
for (auto &macro : macros) {
RemoveMacro(macro);
}
} }
void AdvSceneSwitcher::on_macroUp_clicked() void AdvSceneSwitcher::on_macroUp_clicked()
@@ -161,21 +190,13 @@ void AdvSceneSwitcher::on_macroDown_clicked()
ui->macros->Down(macro); ui->macros->Down(macro);
} }
static bool newMacroNameValid(const std::string &name) void AdvSceneSwitcher::RenameSelectedMacro()
{
if (!macroNameExists(name)) {
return true;
}
DisplayMessage(obs_module_text("AdvSceneSwitcher.macroTab.exists"));
return false;
}
void AdvSceneSwitcher::RenameCurrentMacro()
{ {
auto macro = GetSelectedMacro(); auto macro = GetSelectedMacro();
if (!macro) { if (!macro) {
return; return;
} }
std::string oldName = macro->Name(); std::string oldName = macro->Name();
std::string name; std::string name;
if (!AdvSSNameDialog::AskForName( if (!AdvSSNameDialog::AskForName(
@@ -184,9 +205,11 @@ void AdvSceneSwitcher::RenameCurrentMacro()
QString::fromStdString(oldName))) { QString::fromStdString(oldName))) {
return; return;
} }
if (name.empty() || name == oldName || !newMacroNameValid(name)) {
if (name.empty() || name == oldName || !newMacroNameIsValid(name)) {
return; return;
} }
RenameMacro(macro, QString::fromStdString(name)); RenameMacro(macro, QString::fromStdString(name));
const QSignalBlocker b(ui->macroName); const QSignalBlocker b(ui->macroName);
@@ -247,6 +270,7 @@ void AdvSceneSwitcher::ExportMacros()
} }
obs_data_set_array(data, "macros", macroArray); obs_data_set_array(data, "macros", macroArray);
SaveVariables(data); SaveVariables(data);
SaveActionQueues(data);
obs_data_set_string(data, "version", g_GIT_TAG); obs_data_set_string(data, "version", g_GIT_TAG);
auto json = obs_data_get_json(data); auto json = obs_data_get_json(data);
QString exportString(json); QString exportString(json);
@@ -347,7 +371,10 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
std::string newName; std::string newName;
bool accepted = AdvSSNameDialog::AskForName( bool accepted = AdvSSNameDialog::AskForName(
this, obs_module_text("AdvSceneSwitcher.macroTab.add"), this,
obs_module_text(macro->IsGroup()
? "AdvSceneSwitcher.macroTab.addGroup"
: "AdvSceneSwitcher.macroTab.add"),
obs_module_text("AdvSceneSwitcher.macroTab.name"), newName, obs_module_text("AdvSceneSwitcher.macroTab.name"), newName,
placeHolderText); placeHolderText);
@@ -359,9 +386,7 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
return false; return false;
} }
if (macroNameExists(newName)) { if (!newMacroNameIsValid(newName)) {
DisplayMessage(
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
return ResolveMacroImportNameConflict(macro); return ResolveMacroImportNameConflict(macro);
} }
@@ -369,26 +394,6 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
return true; return true;
} }
static bool variableWithNameExists(const std::string &name)
{
return !!GetVariableByName(name);
}
static void importVariables(obs_data_t *obj)
{
OBSDataArrayAutoRelease array = obs_data_get_array(obj, "variables");
size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease data = obs_data_array_item(array, i);
auto var = Variable::Create();
var->Load(data);
if (variableWithNameExists(var->Name())) {
continue;
}
GetVariables().emplace_back(var);
}
}
void AdvSceneSwitcher::ImportMacros() void AdvSceneSwitcher::ImportMacros()
{ {
QString json; QString json;
@@ -403,7 +408,8 @@ void AdvSceneSwitcher::ImportMacros()
ImportMacros(); ImportMacros();
return; return;
} }
importVariables(data); ImportVariables(data);
ImportQueues(data);
auto version = obs_data_get_string(data, "version"); auto version = obs_data_get_string(data, "version");
if (strcmp(version, g_GIT_TAG) != 0) { if (strcmp(version, g_GIT_TAG) != 0) {
@@ -424,6 +430,7 @@ void AdvSceneSwitcher::ImportMacros()
OBSDataAutoRelease array_obj = obs_data_array_item(array, i); OBSDataAutoRelease array_obj = obs_data_array_item(array, i);
auto macro = std::make_shared<Macro>(); auto macro = std::make_shared<Macro>();
macro->Load(array_obj); macro->Load(array_obj);
RunPostLoadSteps();
if (macroNameExists(macro->Name()) && if (macroNameExists(macro->Name()) &&
!ResolveMacroImportNameConflict(macro)) { !ResolveMacroImportNameConflict(macro)) {
@@ -468,7 +475,7 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
QString oldName = QString::fromStdString(macro->Name()); QString oldName = QString::fromStdString(macro->Name());
if (newName.isEmpty() || newName == oldName || if (newName.isEmpty() || newName == oldName ||
!newMacroNameValid(newName.toStdString())) { !newMacroNameIsValid(newName.toStdString())) {
ui->macroName->setText(oldName); ui->macroName->setText(oldName);
return; return;
} }
@@ -729,6 +736,7 @@ void AdvSceneSwitcher::MacroSelectionChanged()
return; return;
} }
SetEditMacro(*macro); SetEditMacro(*macro);
obs_frontend_save();
} }
void AdvSceneSwitcher::HighlightOnChange() void AdvSceneSwitcher::HighlightOnChange()
@@ -792,6 +800,7 @@ bool shouldRestoreSplitter(const QList<int> &pos)
void AdvSceneSwitcher::SetupMacroTab() void AdvSceneSwitcher::SetupMacroTab()
{ {
ui->macroElseActions->installEventFilter(this); ui->macroElseActions->installEventFilter(this);
ui->macros->installEventFilter(this);
if (GetMacros().size() == 0 && !switcher->disableHints) { if (GetMacros().size() == 0 && !switcher->disableHints) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green)); addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
@@ -915,7 +924,7 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
auto rename = menu.addAction( auto rename = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.rename"), this, obs_module_text("AdvSceneSwitcher.macroTab.rename"), this,
&AdvSceneSwitcher::RenameCurrentMacro); &AdvSceneSwitcher::RenameSelectedMacro);
rename->setEnabled(ui->macros->SingleItemSelected()); rename->setEnabled(ui->macros->SingleItemSelected());
auto remove = menu.addAction( auto remove = menu.addAction(

View File

@@ -173,6 +173,7 @@ void MacroTreeItem::Update(bool force)
} else if (_type == Type::Group) { } else if (_type == Type::Group) {
_expand = new SourceTreeSubItemCheckBox(); _expand = new SourceTreeSubItemCheckBox();
_expand->setProperty("sourceTreeSubItem", true);
_expand->setSizePolicy(QSizePolicy::Maximum, _expand->setSizePolicy(QSizePolicy::Maximum,
QSizePolicy::Maximum); QSizePolicy::Maximum);
_expand->setMaximumSize(10, 16); _expand->setMaximumSize(10, 16);

View File

@@ -17,6 +17,7 @@ class QSpacerItem;
class QHBoxLayout; class QHBoxLayout;
// Only used to enable applying "SourceTreeSubItemCheckBox" stylesheet // Only used to enable applying "SourceTreeSubItemCheckBox" stylesheet
// Can be removed once the minimum supported OBS version is greater than 30.1
class SourceTreeSubItemCheckBox : public QCheckBox { class SourceTreeSubItemCheckBox : public QCheckBox {
Q_OBJECT Q_OBJECT
}; };

View File

@@ -193,9 +193,17 @@ bool Macro::CeckMatch()
bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause) bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
{ {
if (!_done) { if (!_done) {
vblog(LOG_INFO, "macro %s already running", _name.c_str()); vblog(LOG_INFO, "Macro %s already running", _name.c_str());
return !forceParallel;
if (!_stopActionsIfNotDone) {
return !forceParallel;
}
Stop();
vblog(LOG_INFO, "Stopped macro %s actions to rerun them",
_name.c_str());
} }
std::function<bool(bool)> runFunc = std::function<bool(bool)> runFunc =
match ? std::bind(&Macro::RunActions, this, match ? std::bind(&Macro::RunActions, this,
std::placeholders::_1) std::placeholders::_1)
@@ -213,6 +221,7 @@ bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
} else { } else {
ret = runFunc(ignorePause); ret = runFunc(ignorePause);
} }
_lastExecutionTime = std::chrono::high_resolution_clock::now(); _lastExecutionTime = std::chrono::high_resolution_clock::now();
auto group = _parent.lock(); auto group = _parent.lock();
if (group) { if (group) {
@@ -550,6 +559,7 @@ bool Macro::Save(obs_data_t *obj) const
obs_data_set_bool(obj, "parallel", _runInParallel); obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "onChange", _performActionsOnChange); obs_data_set_bool(obj, "onChange", _performActionsOnChange);
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart); obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
obs_data_set_bool(obj, "group", _isGroup); obs_data_set_bool(obj, "group", _isGroup);
if (_isGroup) { if (_isGroup) {
@@ -644,6 +654,7 @@ bool Macro::Load(obs_data_t *obj)
_runInParallel = obs_data_get_bool(obj, "parallel"); _runInParallel = obs_data_get_bool(obj, "parallel");
_performActionsOnChange = obs_data_get_bool(obj, "onChange"); _performActionsOnChange = obs_data_get_bool(obj, "onChange");
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart"); _skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
_isGroup = obs_data_get_bool(obj, "group"); _isGroup = obs_data_get_bool(obj, "group");
if (_isGroup) { if (_isGroup) {
@@ -651,6 +662,7 @@ bool Macro::Load(obs_data_t *obj)
obs_data_get_obj(obj, "groupData"); obs_data_get_obj(obj, "groupData");
_isCollapsed = obs_data_get_bool(groupData, "collapsed"); _isCollapsed = obs_data_get_bool(groupData, "collapsed");
_groupSize = obs_data_get_int(groupData, "size"); _groupSize = obs_data_get_int(groupData, "size");
return true; return true;
} }
@@ -735,6 +747,7 @@ bool Macro::Load(obs_data_t *obj)
} }
} }
UpdateElseActionIndices(); UpdateElseActionIndices();
return true; return true;
} }

View File

@@ -13,7 +13,7 @@
#include <memory> #include <memory>
#include <map> #include <map>
#include <thread> #include <thread>
#include <obs-data.h> #include <obs.hpp>
#include <obs-module-helper.hpp> #include <obs-module-helper.hpp>
namespace advss { namespace advss {
@@ -24,6 +24,7 @@ class Macro {
public: public:
Macro(const std::string &name = "", const bool addHotkey = false); Macro(const std::string &name = "", const bool addHotkey = false);
virtual ~Macro(); virtual ~Macro();
bool CeckMatch(); bool CeckMatch();
bool PerformActions(bool match, bool forceParallel = false, bool PerformActions(bool match, bool forceParallel = false,
bool ignorePause = false); bool ignorePause = false);
@@ -42,6 +43,11 @@ public:
bool MatchOnChange() const { return _performActionsOnChange; } bool MatchOnChange() const { return _performActionsOnChange; }
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; } void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
bool SkipExecOnStart() const { return _skipExecOnStart; } bool SkipExecOnStart() const { return _skipExecOnStart; }
void SetStopActionsIfNotDone(bool stopActionsIfNotDone)
{
_stopActionsIfNotDone = stopActionsIfNotDone;
}
bool StopActionsIfNotDone() const { return _stopActionsIfNotDone; }
int RunCount() const { return _runCount; }; int RunCount() const { return _runCount; };
void ResetRunCount() { _runCount = 0; }; void ResetRunCount() { _runCount = 0; };
void ResetTimers(); void ResetTimers();
@@ -168,6 +174,7 @@ private:
bool _conditionSateChanged = false; bool _conditionSateChanged = false;
bool _performActionsOnChange = true; bool _performActionsOnChange = true;
bool _skipExecOnStart = false; bool _skipExecOnStart = false;
bool _stopActionsIfNotDone = false;
bool _paused = false; bool _paused = false;
int _runCount = 0; int _runCount = 0;
bool _registerHotkeys = true; bool _registerHotkeys = true;

View File

@@ -141,7 +141,9 @@ public:
bool showSystemTrayNotifications = false; bool showSystemTrayNotifications = false;
bool transitionOverrideOverride = false; bool transitionOverrideOverride = false;
bool adjustActiveTransitionType = true; bool adjustActiveTransitionType = true;
bool verbose = false;
enum class LogLevel { DEFAULT, PRINT_ACTION, VERBOSE };
LogLevel logLevel = LogLevel::DEFAULT;
/* --- End of General tab section --- */ /* --- End of General tab section --- */

View File

@@ -33,6 +33,7 @@ void ActionQueue::Save(obs_data_t *obj) const
{ {
obs_data_set_string(obj, "name", _name.c_str()); obs_data_set_string(obj, "name", _name.c_str());
obs_data_set_bool(obj, "runOnStartup", _runOnStartup); obs_data_set_bool(obj, "runOnStartup", _runOnStartup);
obs_data_set_bool(obj, "resolveVariablesOnAdd", _resolveVariablesOnAdd);
} }
void ActionQueue::Load(obs_data_t *obj) void ActionQueue::Load(obs_data_t *obj)
@@ -40,6 +41,8 @@ void ActionQueue::Load(obs_data_t *obj)
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
_name = obs_data_get_string(obj, "name"); _name = obs_data_get_string(obj, "name");
_runOnStartup = obs_data_get_bool(obj, "runOnStartup"); _runOnStartup = obs_data_get_bool(obj, "runOnStartup");
_resolveVariablesOnAdd =
obs_data_get_bool(obj, "resolveVariablesOnAdd");
if (_runOnStartup) { if (_runOnStartup) {
Start(); Start();
@@ -48,9 +51,13 @@ void ActionQueue::Load(obs_data_t *obj)
void ActionQueue::Start() void ActionQueue::Start()
{ {
if (_thread.joinable()) { if (!_stop) {
return; return;
} }
if (_thread.joinable()) {
_thread.join();
}
_stop = false; _stop = false;
_thread = std::thread(&ActionQueue::RunActions, this); _thread = std::thread(&ActionQueue::RunActions, this);
} }
@@ -59,6 +66,10 @@ void ActionQueue::Stop()
{ {
_stop = true; _stop = true;
_cv.notify_all(); _cv.notify_all();
if (std::this_thread::get_id() == _thread.get_id()) {
return;
}
if (_thread.joinable()) { if (_thread.joinable()) {
_thread.join(); _thread.join();
} }
@@ -66,7 +77,7 @@ void ActionQueue::Stop()
bool ActionQueue::IsRunning() const bool ActionQueue::IsRunning() const
{ {
return _thread.joinable(); return !_stop;
} }
bool ActionQueue::RunsOnStartup() const bool ActionQueue::RunsOnStartup() const
@@ -80,10 +91,21 @@ void ActionQueue::Clear()
_actions.clear(); _actions.clear();
} }
void ActionQueue::Add(const std::shared_ptr<MacroAction> &actions) void ActionQueue::Add(const std::shared_ptr<MacroAction> &action)
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
_actions.emplace_back(actions); if (_resolveVariablesOnAdd) {
auto copy = action->Copy();
copy->GetMacro();
OBSDataAutoRelease data = obs_data_create();
action->Save(data);
copy->Load(data);
copy->PostLoad();
copy->ResolveVariablesToFixedValues();
_actions.emplace_back(copy);
} else {
_actions.emplace_back(action);
}
_cv.notify_all(); _cv.notify_all();
} }
@@ -120,8 +142,11 @@ void ActionQueue::RunActions()
continue; continue;
} }
vblog(LOG_INFO, "Performing action '%s' in queue '%s'", if (ActionLoggingEnabled()) {
action->GetId().c_str(), _name.c_str()); blog(LOG_INFO, "Performing action '%s' in queue '%s'",
action->GetId().c_str(), _name.c_str());
action->LogAction();
}
action->PerformAction(); action->PerformAction();
} }
} }
@@ -139,6 +164,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
_clear(new QPushButton( _clear(new QPushButton(
obs_module_text("AdvSceneSwitcher.actionQueues.clear"))), obs_module_text("AdvSceneSwitcher.actionQueues.clear"))),
_runOnStartup(new QCheckBox()), _runOnStartup(new QCheckBox()),
_resolveVariablesOnAdd(new QCheckBox()),
_queue(settings) _queue(settings)
{ {
QWidget::connect(_startStopToggle, SIGNAL(clicked()), this, QWidget::connect(_startStopToggle, SIGNAL(clicked()), this,
@@ -146,6 +172,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
QWidget::connect(_clear, SIGNAL(clicked()), this, SLOT(ClearClicked())); QWidget::connect(_clear, SIGNAL(clicked()), this, SLOT(ClearClicked()));
_runOnStartup->setChecked(settings._runOnStartup); _runOnStartup->setChecked(settings._runOnStartup);
_resolveVariablesOnAdd->setChecked(settings._resolveVariablesOnAdd);
UpdateLabels(); UpdateLabels();
auto layout = new QGridLayout(); auto layout = new QGridLayout();
@@ -167,6 +194,14 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
_runOnStartup->setToolTip( _runOnStartup->setToolTip(
obs_module_text("AdvSceneSwitcher.actionQueues.runOnStartup")); obs_module_text("AdvSceneSwitcher.actionQueues.runOnStartup"));
++row; ++row;
layout->addWidget(
new QLabel(obs_module_text(
"AdvSceneSwitcher.actionQueues.resolveVariablesOnAdd")),
row, 0);
layout->addWidget(_resolveVariablesOnAdd, row, 1);
_resolveVariablesOnAdd->setToolTip(obs_module_text(
"AdvSceneSwitcher.actionQueues.resolveVariablesOnAdd"));
++row;
layout->addWidget(_queueRunStatus, row, 0); layout->addWidget(_queueRunStatus, row, 0);
layout->addWidget(_startStopToggle, row, 1); layout->addWidget(_startStopToggle, row, 1);
++row; ++row;
@@ -194,6 +229,8 @@ bool ActionQueueSettingsDialog::AskForSettings(QWidget *parent,
settings._name = dialog._name->text().toStdString(); settings._name = dialog._name->text().toStdString();
settings._runOnStartup = dialog._runOnStartup->isChecked(); settings._runOnStartup = dialog._runOnStartup->isChecked();
settings._resolveVariablesOnAdd =
dialog._resolveVariablesOnAdd->isChecked();
return true; return true;
} }
@@ -329,6 +366,27 @@ void LoadActionQueues(obs_data_t *obj)
} }
} }
static bool queueWithNameExists(const std::string &name)
{
return !GetWeakActionQueueByName(name).expired();
}
void ImportQueues(obs_data_t *data)
{
OBSDataArrayAutoRelease array =
obs_data_get_array(data, "actionQueues");
size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease arrayElement = obs_data_array_item(array, i);
auto queue = ActionQueue::Create();
queue->Load(arrayElement);
if (queueWithNameExists(queue->Name())) {
continue;
}
queues.emplace_back(queue);
}
}
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name) std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name)
{ {
for (const auto &queue : queues) { for (const auto &queue : queues) {

View File

@@ -38,7 +38,8 @@ private:
void RunActions(); void RunActions();
bool _runOnStartup = true; bool _runOnStartup = true;
std::atomic_bool _stop = {false}; bool _resolveVariablesOnAdd = true;
std::atomic_bool _stop = {true};
std::mutex _mutex; std::mutex _mutex;
std::condition_variable _cv; std::condition_variable _cv;
std::thread _thread; std::thread _thread;
@@ -66,6 +67,7 @@ private:
QLabel *_queueSize; QLabel *_queueSize;
QPushButton *_clear; QPushButton *_clear;
QCheckBox *_runOnStartup; QCheckBox *_runOnStartup;
QCheckBox *_resolveVariablesOnAdd;
ActionQueue &_queue; ActionQueue &_queue;
}; };
@@ -96,6 +98,7 @@ signals:
void SetupActionQueues(); void SetupActionQueues();
void SaveActionQueues(obs_data_t *); void SaveActionQueues(obs_data_t *);
void LoadActionQueues(obs_data_t *); void LoadActionQueues(obs_data_t *);
void ImportQueues(obs_data_t *);
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name); std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name);
std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name); std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name);
std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &); std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &);

86
lib/utils/backup.cpp Normal file
View File

@@ -0,0 +1,86 @@
#include "backup.hpp"
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "path-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp"
#include "version.h"
#include <obs-frontend-api.h>
#include <obs-module.h>
#include <QFileDialog>
#include <QTextStream>
#include <util/config-file.h>
namespace advss {
void AskForBackup(const QString &json)
{
const bool backupWasConfirmed = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true, false);
if (!backupWasConfirmed) {
return;
}
QString path = QFileDialog::getSaveFileName(
nullptr,
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle"),
GetDefaultSettingsSaveLocation(),
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
if (path.isEmpty()) {
return;
}
QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
auto out = QTextStream(&file);
out << json;
}
void BackupSettingsOfCurrentVersion()
{
auto sceneCollectionName = obs_frontend_get_current_scene_collection();
const std::string fileName =
std::string("settings-backup-") +
(sceneCollectionName ? sceneCollectionName : "-") + "-" +
g_GIT_TAG + ".json";
bfree(sceneCollectionName);
auto settingsFile = obs_module_config_path(fileName.c_str());
if (!settingsFile) {
return;
}
QString dirPath = QFileInfo(settingsFile).absolutePath();
QDir dir(dirPath);
if (!dir.exists()) {
if (!dir.mkpath(dirPath)) {
blog(LOG_WARNING,
"failed to create directory to save automated backup");
bfree(settingsFile);
return;
}
}
QFile file(settingsFile);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
bfree(settingsFile);
return;
}
OBSDataAutoRelease data = obs_data_create();
SavePluginSettings(data);
auto settings = obs_data_get_json(data);
QString settingsString = QString(settings ? settings : "");
auto out = QTextStream(&file);
out << settingsString;
bfree(settingsFile);
}
} // namespace advss

9
lib/utils/backup.hpp Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <QString>
namespace advss {
void AskForBackup(const QString &json);
void BackupSettingsOfCurrentVersion();
} // namespace advss

View File

@@ -1,9 +1,9 @@
#include "curl-helper.hpp" #include "curl-helper.hpp"
#include "log-helper.hpp"
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <curl/curl.h> #include <curl/curl.h>
#include <obs.hpp>
namespace advss { namespace advss {

View File

@@ -0,0 +1,63 @@
#include "double-slider.hpp"
#include <QWheelEvent>
#include <cmath>
namespace advss {
SliderIgnoreScroll::SliderIgnoreScroll(QWidget *parent) : QSlider(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
SliderIgnoreScroll::SliderIgnoreScroll(Qt::Orientation orientation,
QWidget *parent)
: QSlider(parent)
{
setFocusPolicy(Qt::StrongFocus);
setOrientation(orientation);
}
void SliderIgnoreScroll::wheelEvent(QWheelEvent *event)
{
if (!hasFocus()) {
event->ignore();
} else {
QSlider::wheelEvent(event);
}
}
DoubleSlider::DoubleSlider(QWidget *parent) : SliderIgnoreScroll(parent)
{
connect(this, &DoubleSlider::valueChanged, [this](int val) {
emit DoubleValChanged((minVal / minStep + val) * minStep);
});
}
void DoubleSlider::SetDoubleConstraints(double newMin, double newMax,
double newStep, double val)
{
minVal = newMin;
maxVal = newMax;
minStep = newStep;
double total = maxVal - minVal;
int intMax = int(total / minStep);
setMinimum(0);
setMaximum(intMax);
setSingleStep(1);
SetDoubleVal(val);
}
double DoubleSlider::DoubleValue()
{
return (minVal / minStep + value()) * minStep;
}
void DoubleSlider::SetDoubleVal(double val)
{
setValue(lround((val - minVal) / minStep));
}
} // namespace advss

View File

@@ -0,0 +1,40 @@
// Based on OBS DoubleSlider and SliderIgnoreScroll implementation
#pragma once
#include "export-symbol-helper.hpp"
#include <QSlider>
namespace advss {
class SliderIgnoreScroll : public QSlider {
Q_OBJECT
public:
SliderIgnoreScroll(QWidget *parent = nullptr);
SliderIgnoreScroll(Qt::Orientation orientation,
QWidget *parent = nullptr);
protected:
virtual void wheelEvent(QWheelEvent *event) override;
};
class ADVSS_EXPORT DoubleSlider : public SliderIgnoreScroll {
Q_OBJECT
double minVal = 0, maxVal = 100, minStep = 1;
public:
DoubleSlider(QWidget *parent = nullptr);
void SetDoubleConstraints(double newMin, double newMax, double newStep,
double val);
double DoubleValue();
signals:
void DoubleValChanged(double val);
public slots:
void SetDoubleVal(double val);
};
} // namespace advss

View File

@@ -165,4 +165,9 @@ void Duration::SetUnit(Unit u)
_value = _value * (prevMultiplier / newMultiplier); _value = _value * (prevMultiplier / newMultiplier);
} }
void Duration::ResolveVariables()
{
_value.ResolveVariables();
}
} // namespace advss } // namespace advss

View File

@@ -34,8 +34,10 @@ public:
// Only use this function if you intend to convert old settings formats // Only use this function if you intend to convert old settings formats
EXPORT void SetUnit(Unit u); EXPORT void SetUnit(Unit u);
EXPORT void ResolveVariables();
private: private:
NumberVariable<double> _value = 0.; DoubleVariable _value = 0.;
Unit _unit = Unit::SECONDS; Unit _unit = Unit::SECONDS;
std::chrono::high_resolution_clock::time_point _startTime; std::chrono::high_resolution_clock::time_point _startTime;

View File

@@ -5,7 +5,16 @@ namespace advss {
bool VerboseLoggingEnabled() bool VerboseLoggingEnabled()
{ {
return GetSwitcher() && GetSwitcher()->verbose; return GetSwitcher() &&
GetSwitcher()->logLevel == SwitcherData::LogLevel::VERBOSE;
}
bool ActionLoggingEnabled()
{
return GetSwitcher() &&
(GetSwitcher()->logLevel ==
SwitcherData::LogLevel::PRINT_ACTION ||
GetSwitcher()->logLevel == SwitcherData::LogLevel::VERBOSE);
} }
} // namespace advss } // namespace advss

View File

@@ -3,14 +3,26 @@
namespace advss { namespace advss {
// Print log with "[adv-ss] " prefix
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__) #define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
// Print log with "[adv-ss] " if log level is set to "verbose"
#define vblog(level, msg, ...) \ #define vblog(level, msg, ...) \
do { \ do { \
if (VerboseLoggingEnabled()) { \ if (VerboseLoggingEnabled()) { \
blog(level, msg, ##__VA_ARGS__); \ blog(level, msg, ##__VA_ARGS__); \
} \ } \
} while (0) } while (0)
// Print log with "[adv-ss] " if log level is set to "action" or "verbose"
#define ablog(level, msg, ...) \
do { \
if (ActionLoggingEnabled()) { \
blog(level, msg, ##__VA_ARGS__); \
} \
} while (0)
// Returns true if log level is set to "verbose"
EXPORT bool VerboseLoggingEnabled(); EXPORT bool VerboseLoggingEnabled();
// Returns true if log level is set to "action" or "verbose"
EXPORT bool ActionLoggingEnabled();
} // namespace advss } // namespace advss

View File

@@ -3,6 +3,11 @@
namespace advss { namespace advss {
void SavePluginSettings(obs_data_t *obj)
{
GetSwitcher()->SaveSettings(obj);
}
void LoadPluginSettings(obs_data_t *obj) void LoadPluginSettings(obs_data_t *obj)
{ {
GetSwitcher()->LoadSettings(obj); GetSwitcher()->LoadSettings(obj);

View File

@@ -5,6 +5,7 @@
namespace advss { namespace advss {
void SavePluginSettings(obs_data_t *);
EXPORT void LoadPluginSettings(obs_data_t *); EXPORT void LoadPluginSettings(obs_data_t *);
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>); EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);

View File

@@ -181,6 +181,12 @@ std::string SceneSelection::ToString(bool resolve) const
return ""; return "";
} }
void SceneSelection::ResolveVariables()
{
_scene = GetScene();
_type = Type::SCENE;
}
SceneSelection SceneSelectionWidget::CurrentSelection() SceneSelection SceneSelectionWidget::CurrentSelection()
{ {
SceneSelection s; SceneSelection s;

View File

@@ -26,6 +26,7 @@ public:
EXPORT Type GetType() const { return _type; } EXPORT Type GetType() const { return _type; }
EXPORT OBSWeakSource GetScene(bool advance = true) const; EXPORT OBSWeakSource GetScene(bool advance = true) const;
EXPORT std::string ToString(bool resolve = false) const; EXPORT std::string ToString(bool resolve = false) const;
EXPORT void ResolveVariables();
private: private:
OBSWeakSource _scene; OBSWeakSource _scene;

View File

@@ -7,7 +7,8 @@
namespace advss { namespace advss {
Section::Section(const int animationDuration, QWidget *parent) Section::Section(const int animationDuration, QWidget *parent)
: QWidget(parent), _animationDuration(animationDuration) : QWidget(parent),
_animationDuration(animationDuration)
{ {
_toggleButton = new QToolButton(this); _toggleButton = new QToolButton(this);
_headerLine = new QFrame(this); _headerLine = new QFrame(this);

View File

@@ -2,7 +2,6 @@
#include "export-symbol-helper.hpp" #include "export-symbol-helper.hpp"
#include <deque> #include <deque>
#include <obs.hpp>
#include <QComboBox> #include <QComboBox>
#include <QStringList> #include <QStringList>
#include <string> #include <string>

View File

@@ -0,0 +1,22 @@
#include "single-char-selection.hpp"
namespace advss {
SingleCharSelection::SingleCharSelection(QWidget *parent) : QLineEdit(parent)
{
setMaxLength(1);
setMaximumWidth(50);
connect(this, &QLineEdit::textChanged, this,
&SingleCharSelection::CharChanged);
}
void SingleCharSelection::HandleTextChanged(const QString &text)
{
if (text.length() == 1) {
emit CharChanged(text);
} else if (text.length() > 1) {
setText(text.left(1));
}
}
} // namespace advss

View File

@@ -0,0 +1,18 @@
#pragma once
#include <QLineEdit>
namespace advss {
class SingleCharSelection : public QLineEdit {
Q_OBJECT
public:
SingleCharSelection(QWidget *parent = nullptr);
signals:
void CharChanged(const QString &character);
private slots:
void HandleTextChanged(const QString &text);
};
} // namespace advss

View File

@@ -88,6 +88,11 @@ void SourceSelection::SetSource(OBSWeakSource source)
_source = source; _source = source;
} }
void SourceSelection::ResolveVariables()
{
SetSource(GetSource());
}
std::string SourceSelection::ToString(bool resolve) const std::string SourceSelection::ToString(bool resolve) const
{ {
switch (_type) { switch (_type) {

View File

@@ -19,6 +19,7 @@ public:
EXPORT Type GetType() const { return _type; } EXPORT Type GetType() const { return _type; }
EXPORT OBSWeakSource GetSource() const; EXPORT OBSWeakSource GetSource() const;
EXPORT void SetSource(OBSWeakSource); EXPORT void SetSource(OBSWeakSource);
EXPORT void ResolveVariables();
EXPORT std::string ToString(bool resolve = false) const; EXPORT std::string ToString(bool resolve = false) const;
EXPORT bool operator==(const SourceSelection &) const; EXPORT bool operator==(const SourceSelection &) const;

View File

@@ -17,7 +17,10 @@ namespace advss {
TempVariable::TempVariable(const std::string &id, const std::string &name, TempVariable::TempVariable(const std::string &id, const std::string &name,
const std::string &description, const std::string &description,
const std::shared_ptr<MacroSegment> &segment) const std::shared_ptr<MacroSegment> &segment)
: _id(id), _name(name), _description(description), _segment(segment) : _id(id),
_name(name),
_description(description),
_segment(segment)
{ {
} }
@@ -439,7 +442,9 @@ void NotifyUIAboutTempVarChange()
AutoUpdateTooltipLabel::AutoUpdateTooltipLabel( AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
QWidget *parent, std::function<QString()> updateFunc) QWidget *parent, std::function<QString()> updateFunc)
: QLabel(parent), _updateFunc(updateFunc), _timer(new QTimer(this)) : QLabel(parent),
_updateFunc(updateFunc),
_timer(new QTimer(this))
{ {
connect(_timer, &QTimer::timeout, this, connect(_timer, &QTimer::timeout, this,
&AutoUpdateTooltipLabel::UpdateTooltip); &AutoUpdateTooltipLabel::UpdateTooltip);

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include "filter-combo-box.hpp" #include "filter-combo-box.hpp"
#include <obs.hpp> #include <obs-data.h>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <QEnterEvent> #include <QEnterEvent>

View File

@@ -3,7 +3,6 @@
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <obs.hpp>
#include <QGraphicsColorizeEffect> #include <QGraphicsColorizeEffect>
#include <QMainWindow> #include <QMainWindow>
#include <QPropertyAnimation> #include <QPropertyAnimation>

View File

@@ -90,15 +90,17 @@ void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
} }
VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent) VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent)
: QSlider(parent) : DoubleSlider(parent)
{ {
fad = fader; fad = fader;
} }
VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
QWidget *parent) QWidget *parent)
: QSlider(orientation, parent) : DoubleSlider(parent)
{ {
SetDoubleConstraints(0, 100, 0.01, 0);
setOrientation(orientation);
fad = fader; fad = fader;
} }
@@ -219,7 +221,6 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
nameLabel->setText(sourceName); nameLabel->setText(sourceName);
slider->setMinimum(0); slider->setMinimum(0);
slider->setMaximum(int(FADER_PRECISION));
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this); obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this); obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
@@ -592,7 +593,9 @@ void VolumeMeter::mousePressEvent(QMouseEvent *event)
VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter, VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
bool vertical) bool vertical)
: QWidget(parent), obs_volmeter(obs_volmeter), vertical(vertical) : QWidget(parent),
obs_volmeter(obs_volmeter),
vertical(vertical)
{ {
setAttribute(Qt::WA_OpaquePaintEvent, true); setAttribute(Qt::WA_OpaquePaintEvent, true);

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#include "double-slider.hpp"
#include <obs.hpp> #include <obs.hpp>
#include <QWidget> #include <QWidget>
@@ -284,7 +285,7 @@ private:
QLabel *nameLabel; QLabel *nameLabel;
QLabel *volLabel; QLabel *volLabel;
VolumeMeter *volMeter; VolumeMeter *volMeter;
QSlider *slider; DoubleSlider *slider;
QPushButton *config = nullptr; QPushButton *config = nullptr;
float levelTotal; float levelTotal;
float levelCount; float levelCount;
@@ -327,10 +328,10 @@ public:
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; } inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
void refreshColors(); void refreshColors();
QSlider *GetSlider() const { return slider; } DoubleSlider *GetSlider() const { return slider; }
}; };
class VolumeSlider : public QSlider { class VolumeSlider : public DoubleSlider {
Q_OBJECT Q_OBJECT
public: public:

View File

@@ -1,8 +1,6 @@
#pragma once #pragma once
#include "variable.hpp" #include "variable.hpp"
#include <obs.hpp>
namespace advss { namespace advss {
template<typename T> class NumberVariable { template<typename T> class NumberVariable {
@@ -24,6 +22,7 @@ public:
Type GetType() const { return _type; } Type GetType() const { return _type; }
bool IsFixedType() const { return _type == Type::FIXED_VALUE; } bool IsFixedType() const { return _type == Type::FIXED_VALUE; }
std::weak_ptr<Variable> GetVariable() const { return _variable; } std::weak_ptr<Variable> GetVariable() const { return _variable; }
void ResolveVariables();
private: private:
Type _type = Type::FIXED_VALUE; Type _type = Type::FIXED_VALUE;

View File

@@ -93,3 +93,9 @@ template<typename T> NumberVariable<T>::operator T() const
{ {
return GetValue(); return GetValue();
} }
template<typename T> void NumberVariable<T>::ResolveVariables()
{
_value = GetValue();
_type = Type::FIXED_VALUE;
}

View File

@@ -50,6 +50,12 @@ void StringVariable::Save(obs_data_t *obj, const char *name) const
obs_data_set_string(obj, name, _value.c_str()); obs_data_set_string(obj, name, _value.c_str());
} }
void StringVariable::ResolveVariables()
{
Resolve();
_value = _resolvedValue;
}
const char *StringVariable::c_str() const char *StringVariable::c_str()
{ {
Resolve(); Resolve();

View File

@@ -2,7 +2,7 @@
#include "variable.hpp" #include "variable.hpp"
#include <string> #include <string>
#include <obs.hpp> #include <obs-data.h>
namespace advss { namespace advss {
@@ -27,6 +27,8 @@ public:
EXPORT void Load(obs_data_t *obj, const char *name); EXPORT void Load(obs_data_t *obj, const char *name);
EXPORT void Save(obs_data_t *obj, const char *name) const; EXPORT void Save(obs_data_t *obj, const char *name) const;
EXPORT void ResolveVariables();
private: private:
void Resolve() const; void Resolve() const;

View File

@@ -1,4 +1,5 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "ui-helpers.hpp"
#include "variable.hpp" #include "variable.hpp"
#include <QTableWidgetItem> #include <QTableWidgetItem>
@@ -12,6 +13,7 @@ static void setVariableTabVisible(QTabWidget *tabWidget, bool visible)
obs_module_text("AdvSceneSwitcher.variableTab.title")) { obs_module_text("AdvSceneSwitcher.variableTab.title")) {
continue; continue;
} }
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread // TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
tabWidget->setTabEnabled(idx, visible); tabWidget->setTabEnabled(idx, visible);
@@ -23,9 +25,10 @@ static void setVariableTabVisible(QTabWidget *tabWidget, bool visible)
} }
} }
static QString getSaveActionString(Variable *variable) static QString formatSaveActionText(Variable *variable)
{ {
QString saveAction; QString saveAction;
switch (variable->GetSaveAction()) { switch (variable->GetSaveAction()) {
case Variable::SaveAction::DONT_SAVE: case Variable::SaveAction::DONT_SAVE:
saveAction = obs_module_text( saveAction = obs_module_text(
@@ -38,26 +41,54 @@ static QString getSaveActionString(Variable *variable)
case Variable::SaveAction::SET_DEFAULT: case Variable::SaveAction::SET_DEFAULT:
saveAction = saveAction =
QString(obs_module_text( QString(obs_module_text(
"AdvSceneSwitcher.variable.save.default")) + "AdvSceneSwitcher.variableTab.saveLoadBehavior.text.default"))
" \"" + .arg(QString::fromStdString(
QString::fromStdString(variable->GetDefaultValue()) + variable->GetDefaultValue()));
"\"";
break; break;
default: default:
break; break;
} }
return saveAction; return saveAction;
} }
static QString getLastUsedString(Variable *variable) static QString formatLastUsedText(Variable *variable)
{ {
auto lastUsed = variable->SecondsSinceLastUse(); auto lastUsed = variable->GetSecondsSinceLastUse();
if (!lastUsed) { if (!lastUsed) {
return obs_module_text( return obs_module_text(
"AdvSceneSwitcher.variableTab.neverNused"); "AdvSceneSwitcher.variableTab.lastUsed.text.never");
} }
QString fmt = obs_module_text("AdvSceneSwitcher.variableTab.lastUsed");
return fmt.arg(QString::number(*lastUsed)); QString text =
obs_module_text("AdvSceneSwitcher.variableTab.lastUsed.text");
return text.arg(QString::number(*lastUsed));
}
static QString formatLastChangedText(Variable *variable)
{
auto lastChanged = variable->GetSecondsSinceLastChange();
if (!lastChanged) {
return obs_module_text(
"AdvSceneSwitcher.variableTab.lastChanged.text.none");
}
QString text = obs_module_text(
"AdvSceneSwitcher.variableTab.lastChanged.text");
return text.arg(QString::number(*lastChanged));
}
static QString formatLastChangedTooltip(Variable *variable)
{
auto lastChanged = variable->GetSecondsSinceLastChange();
if (!lastChanged) {
return QString();
}
QString tooltip = obs_module_text(
"AdvSceneSwitcher.variableTab.lastChanged.tooltip");
return tooltip.arg(QString::number(variable->GetValueChangeCount()))
.arg(QString::fromStdString(variable->GetPreviousValue()));
} }
static void addVariableRow(QTableWidget *table, Variable *variable) static void addVariableRow(QTableWidget *table, Variable *variable)
@@ -75,16 +106,18 @@ static void addVariableRow(QTableWidget *table, Variable *variable)
auto *item = auto *item =
new QTableWidgetItem(QString::fromStdString(variable->Name())); new QTableWidgetItem(QString::fromStdString(variable->Name()));
table->setItem(row, col, item); table->setItem(row, col, item);
col++; auto varValue = QString::fromStdString(variable->Value(false));
item = new QTableWidgetItem( item = new QTableWidgetItem(varValue);
QString::fromStdString(variable->Value(false))); item->setToolTip(varValue);
table->setItem(row, col, item); table->setItem(row, ++col, item);
col++; item = new QTableWidgetItem(formatSaveActionText(variable));
item = new QTableWidgetItem(getSaveActionString(variable)); table->setItem(row, ++col, item);
table->setItem(row, col, item); item = new QTableWidgetItem(formatLastUsedText(variable));
col++; table->setItem(row, ++col, item);
item = new QTableWidgetItem(getLastUsedString(variable)); item = new QTableWidgetItem(formatLastChangedText(variable));
table->setItem(row, col, item); item->setToolTip(formatLastChangedTooltip(variable));
table->setItem(row, ++col, item);
table->sortByColumn(0, Qt::AscendingOrder); table->sortByColumn(0, Qt::AscendingOrder);
} }
@@ -100,25 +133,31 @@ static void removeVariableRow(QTableWidget *table, const QString &name)
table->sortByColumn(0, Qt::AscendingOrder); table->sortByColumn(0, Qt::AscendingOrder);
} }
static void updateVaribleStatus(QTableWidget *table) static void updateVariableStatus(QTableWidget *table)
{ {
auto lock = LockContext();
for (int row = 0; row < table->rowCount(); row++) { for (int row = 0; row < table->rowCount(); row++) {
auto item = table->item(row, 0); auto item = table->item(row, 0);
if (!item) { if (!item) {
continue; continue;
} }
auto weakVariable = GetWeakVariableByQString(item->text()); auto weakVariable = GetWeakVariableByQString(item->text());
auto variable = weakVariable.lock(); auto variable = weakVariable.lock();
if (!variable) { if (!variable) {
continue; continue;
} }
item = table->item(row, 1); item = table->item(row, 1);
item->setText(QString::fromStdString(variable->Value(false))); auto varValue = QString::fromStdString(variable->Value(false));
item->setText(varValue);
item->setToolTip(varValue);
item = table->item(row, 2); item = table->item(row, 2);
item->setText(getSaveActionString(variable.get())); item->setText(formatSaveActionText(variable.get()));
item = table->item(row, 3); item = table->item(row, 3);
item->setText(getLastUsedString(variable.get())); item->setText(formatLastUsedText(variable.get()));
item = table->item(row, 4);
item->setText(formatLastChangedText(variable.get()));
item->setToolTip(formatLastChangedTooltip(variable.get()));
} }
} }
@@ -130,6 +169,7 @@ static void renameVariable(QTableWidget *table, const QString &oldName,
if (!item) { if (!item) {
continue; continue;
} }
if (item->text() == oldName) { if (item->text() == oldName) {
item->setText(newName); item->setText(newName);
table->sortByColumn(0, Qt::AscendingOrder); table->sortByColumn(0, Qt::AscendingOrder);
@@ -141,30 +181,10 @@ static void renameVariable(QTableWidget *table, const QString &oldName,
assert(false); assert(false);
} }
static void openSettingsDialogAtRow(QTableWidget *table, int row)
{
auto item = table->item(row, 0);
if (!item) {
return;
}
auto weakVariable = GetWeakVariableByQString(item->text());
auto variable = weakVariable.lock();
if (!variable) {
return;
}
auto oldName = variable->Name();
bool accepted =
VariableSettingsDialog::AskForSettings(table, *variable.get());
if (accepted && oldName != variable->Name()) {
VariableSignalManager::Instance()->Rename(
QString::fromStdString(oldName),
QString::fromStdString(variable->Name()));
}
}
void AdvSceneSwitcher::SetupVariableTab() void AdvSceneSwitcher::SetupVariableTab()
{ {
ui->variables->installEventFilter(this);
if (GetVariables().empty()) { if (GetVariables().empty()) {
setVariableTabVisible(ui->tabWidget, false); setVariableTabVisible(ui->tabWidget, false);
} else { } else {
@@ -173,12 +193,14 @@ void AdvSceneSwitcher::SetupVariableTab()
static const QStringList horizontalHeaders = static const QStringList horizontalHeaders =
QStringList() QStringList()
<< obs_module_text("AdvSceneSwitcher.variableTab.header.name") << obs_module_text("AdvSceneSwitcher.variableTab.name.header")
<< obs_module_text("AdvSceneSwitcher.variableTab.header.value") << obs_module_text("AdvSceneSwitcher.variableTab.value.header")
<< obs_module_text( << obs_module_text(
"AdvSceneSwitcher.variableTab.header.saveLoadBehavior") "AdvSceneSwitcher.variableTab.saveLoadBehavior.header")
<< obs_module_text( << obs_module_text(
"AdvSceneSwitcher.variableTab.header.lastUse"); "AdvSceneSwitcher.variableTab.lastUsed.header")
<< obs_module_text(
"AdvSceneSwitcher.variableTab.lastChanged.header");
auto &variables = GetVariables(); auto &variables = GetVariables();
@@ -217,66 +239,47 @@ void AdvSceneSwitcher::SetupVariableTab()
ui->variablesHelp->show(); ui->variablesHelp->show();
} }
}); });
QWidget::connect(ui->variables, &QTableWidget::cellDoubleClicked, QWidget::connect(ui->variables, &QTableWidget::cellDoubleClicked, this,
[this](int row, int _) { &AdvSceneSwitcher::OpenSettingsForSelectedVariable);
openSettingsDialogAtRow(ui->variables, row);
});
auto timer = new QTimer(this); auto timer = new QTimer(this);
timer->setInterval(1000); timer->setInterval(1000);
QWidget::connect(timer, &QTimer::timeout, QWidget::connect(timer, &QTimer::timeout,
[this]() { updateVaribleStatus(ui->variables); }); [this]() { updateVariableStatus(ui->variables); });
timer->start(); timer->start();
} }
void AdvSceneSwitcher::on_variableAdd_clicked() void AdvSceneSwitcher::OpenSettingsForSelectedVariable()
{ {
auto newVariable = std::make_shared<Variable>(); auto selectedRows = ui->variables->selectionModel()->selectedRows();
auto accepted =
VariableSettingsDialog::AskForSettings(this, *newVariable);
if (!accepted) {
return;
}
{
auto lock = LockContext();
auto &variables = GetVariables();
variables.emplace_back(newVariable);
}
VariableSignalManager::Instance()->Add(
QString::fromStdString(newVariable->Name()));
}
void AdvSceneSwitcher::on_variableRemove_clicked()
{
auto selectedItems = ui->variables->selectedItems();
QList<int> selectedRows;
for (auto item : selectedItems) {
int row = item->row();
if (!selectedRows.contains(row)) {
selectedRows.append(row);
}
}
if (selectedRows.empty()) { if (selectedRows.empty()) {
return; return;
} }
QStringList names; auto cell = ui->variables->item(selectedRows.last().row(), 0);
for (int row : selectedRows) { if (!cell) {
auto item = ui->variables->item(row, 0); return;
if (!item) {
continue;
}
names << item->text();
} }
for (const auto &name : names) { auto weakVariable = GetWeakVariableByQString(cell->text());
VariableSignalManager::Instance()->Remove(name); auto variable = weakVariable.lock();
if (!variable) {
return;
} }
auto lock = LockContext(); auto oldName = variable->Name();
for (const auto &name : names) { bool accepted = VariableSettingsDialog::AskForSettings(ui->variables,
*variable.get());
if (accepted && oldName != variable->Name()) {
VariableSignalManager::Instance()->Rename(
QString::fromStdString(oldName),
QString::fromStdString(variable->Name()));
}
}
static void removeVariablesWithNames(const QStringList &varNames)
{
for (const auto &name : varNames) {
auto variable = GetVariableByQString(name); auto variable = GetVariableByQString(name);
if (!variable) { if (!variable) {
continue; continue;
@@ -293,4 +296,70 @@ void AdvSceneSwitcher::on_variableRemove_clicked()
} }
} }
void AdvSceneSwitcher::RemoveSelectedVariables()
{
auto selectedRows = ui->variables->selectionModel()->selectedRows();
if (selectedRows.empty()) {
return;
}
QStringList varNames;
for (auto row : selectedRows) {
auto cell = ui->variables->item(row.row(), 0);
if (!cell) {
continue;
}
varNames << cell->text();
}
int varNameCount = varNames.size();
if (varNameCount == 1) {
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.variableTab.removeSingleVariablePopup.text");
if (!DisplayMessage(deleteWarning.arg(varNames.at(0)), true)) {
return;
}
} else {
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.variableTab.removeMultipleVariablesPopup.text");
if (!DisplayMessage(deleteWarning.arg(varNameCount), true)) {
return;
}
}
{
auto lock = LockContext();
removeVariablesWithNames(varNames);
}
for (const auto &name : varNames) {
VariableSignalManager::Instance()->Remove(name);
}
}
void AdvSceneSwitcher::on_variableAdd_clicked()
{
auto newVariable = std::make_shared<Variable>();
auto accepted =
VariableSettingsDialog::AskForSettings(this, *newVariable);
if (!accepted) {
return;
}
{
auto lock = LockContext();
auto &variables = GetVariables();
variables.emplace_back(newVariable);
}
VariableSignalManager::Instance()->Add(
QString::fromStdString(newVariable->Name()));
}
void AdvSceneSwitcher::on_variableRemove_clicked()
{
RemoveSelectedVariables();
}
} // namespace advss } // namespace advss

View File

@@ -13,11 +13,6 @@ static std::deque<std::shared_ptr<Item>> variables;
// when resolving strings containing variables, etc. // when resolving strings containing variables, etc.
static std::chrono::high_resolution_clock::time_point lastVariableChange{}; static std::chrono::high_resolution_clock::time_point lastVariableChange{};
std::chrono::high_resolution_clock::time_point GetLastVariableChangeTime()
{
return lastVariableChange;
}
Variable::Variable() : Item() Variable::Variable() : Item()
{ {
lastVariableChange = std::chrono::high_resolution_clock::now(); lastVariableChange = std::chrono::high_resolution_clock::now();
@@ -34,11 +29,13 @@ void Variable::Load(obs_data_t *obj)
_saveAction = _saveAction =
static_cast<SaveAction>(obs_data_get_int(obj, "saveAction")); static_cast<SaveAction>(obs_data_get_int(obj, "saveAction"));
_defaultValue = obs_data_get_string(obj, "defaultValue"); _defaultValue = obs_data_get_string(obj, "defaultValue");
if (_saveAction == SaveAction::SAVE) { if (_saveAction == SaveAction::SAVE) {
_value = obs_data_get_string(obj, "value"); SetValue(obs_data_get_string(obj, "value"));
} else if (_saveAction == SaveAction::SET_DEFAULT) { } else if (_saveAction == SaveAction::SET_DEFAULT) {
_value = _defaultValue; SetValue(_defaultValue);
} }
lastVariableChange = std::chrono::high_resolution_clock::now(); lastVariableChange = std::chrono::high_resolution_clock::now();
} }
@@ -46,9 +43,11 @@ void Variable::Save(obs_data_t *obj) const
{ {
Item::Save(obj); Item::Save(obj);
obs_data_set_int(obj, "saveAction", static_cast<int>(_saveAction)); obs_data_set_int(obj, "saveAction", static_cast<int>(_saveAction));
if (_saveAction == SaveAction::SAVE) { if (_saveAction == SaveAction::SAVE) {
obs_data_set_string(obj, "value", _value.c_str()); obs_data_set_string(obj, "value", _value.c_str());
} }
obs_data_set_string(obj, "defaultValue", _defaultValue.c_str()); obs_data_set_string(obj, "defaultValue", _defaultValue.c_str());
} }
@@ -57,6 +56,7 @@ std::string Variable::Value(bool updateLastUsed) const
if (updateLastUsed) { if (updateLastUsed) {
UpdateLastUsed(); UpdateLastUsed();
} }
return _value; return _value;
} }
@@ -72,128 +72,61 @@ std::optional<int> Variable::IntValue() const
void Variable::SetValue(const std::string &value) void Variable::SetValue(const std::string &value)
{ {
_previousValue = _value;
_value = value; _value = value;
UpdateLastUsed(); UpdateLastUsed();
UpdateLastChanged();
lastVariableChange = std::chrono::high_resolution_clock::now(); lastVariableChange = std::chrono::high_resolution_clock::now();
} }
void Variable::SetValue(double value) void Variable::SetValue(double value)
{ {
_value = ToString(value); SetValue(ToString(value));
UpdateLastUsed();
lastVariableChange = std::chrono::high_resolution_clock::now();
} }
std::optional<uint64_t> Variable::SecondsSinceLastUse() const std::optional<uint64_t> Variable::GetSecondsSinceLastUse() const
{ {
if (_lastUsed.time_since_epoch().count() == 0) { if (_lastUsed.time_since_epoch().count() == 0) {
return {}; return {};
} }
const auto now = std::chrono::high_resolution_clock::now(); const auto now = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::seconds>(now - _lastUsed) return std::chrono::duration_cast<std::chrono::seconds>(now - _lastUsed)
.count(); .count();
} }
std::optional<uint64_t> Variable::GetSecondsSinceLastChange() const
{
if (_lastChanged.time_since_epoch().count() == 0) {
return {};
}
const auto now = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::seconds>(now -
_lastChanged)
.count();
}
void Variable::UpdateLastUsed() const void Variable::UpdateLastUsed() const
{ {
_lastUsed = std::chrono::high_resolution_clock::now(); _lastUsed = std::chrono::high_resolution_clock::now();
} }
std::deque<std::shared_ptr<Item>> &GetVariables() void Variable::UpdateLastChanged()
{ {
return variables; if (_previousValue != _value) {
} _lastChanged = std::chrono::high_resolution_clock::now();
++_valueChangeCount;
Variable *GetVariableByName(const std::string &name)
{
for (const auto &v : variables) {
if (v->Name() == name) {
return dynamic_cast<Variable *>(v.get());
}
} }
return nullptr;
}
Variable *GetVariableByQString(const QString &name)
{
return GetVariableByName(name.toStdString());
}
std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name)
{
for (const auto &v : variables) {
if (v->Name() == name) {
std::weak_ptr<Variable> wp =
std::dynamic_pointer_cast<Variable>(v);
return wp;
}
}
return std::weak_ptr<Variable>();
}
std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name)
{
return GetWeakVariableByName(name.toStdString());
}
QStringList GetVariablesNameList()
{
QStringList list;
for (const auto &var : variables) {
list << QString::fromStdString(var->Name());
}
list.sort();
return list;
}
std::string GetWeakVariableName(std::weak_ptr<Variable> var_)
{
auto var = var_.lock();
if (!var) {
return obs_module_text("AdvSceneSwitcher.variable.invalid");
}
return var->Name();
}
void SaveVariables(obs_data_t *obj)
{
obs_data_array_t *variablesArray = obs_data_array_create();
for (const auto &v : variables) {
obs_data_t *array_obj = obs_data_create();
v->Save(array_obj);
obs_data_array_push_back(variablesArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "variables", variablesArray);
obs_data_array_release(variablesArray);
}
void LoadVariables(obs_data_t *obj)
{
variables.clear();
obs_data_array_t *variablesArray = obs_data_get_array(obj, "variables");
size_t count = obs_data_array_count(variablesArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(variablesArray, i);
auto var = Variable::Create();
variables.emplace_back(var);
variables.back()->Load(array_obj);
obs_data_release(array_obj);
}
obs_data_array_release(variablesArray);
} }
static void populateSaveActionSelection(QComboBox *list) static void populateSaveActionSelection(QComboBox *list)
{ {
list->addItem( list->addItems(
obs_module_text("AdvSceneSwitcher.variable.save.dontSave")); {obs_module_text("AdvSceneSwitcher.variable.save.dontSave"),
list->addItem(obs_module_text("AdvSceneSwitcher.variable.save.save")); obs_module_text("AdvSceneSwitcher.variable.save.save"),
list->addItem( obs_module_text("AdvSceneSwitcher.variable.save.default")});
obs_module_text("AdvSceneSwitcher.variable.save.default"));
} }
VariableSettingsDialog::VariableSettingsDialog(QWidget *parent, VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
@@ -220,6 +153,7 @@ VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
layout->addWidget( layout->addWidget(
new QLabel(obs_module_text("AdvSceneSwitcher.variable.name")), new QLabel(obs_module_text("AdvSceneSwitcher.variable.name")),
row, 0); row, 0);
QHBoxLayout *nameLayout = new QHBoxLayout; QHBoxLayout *nameLayout = new QHBoxLayout;
nameLayout->addWidget(_name); nameLayout->addWidget(_name);
nameLayout->addWidget(_nameHint); nameLayout->addWidget(_nameHint);
@@ -233,13 +167,14 @@ VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
layout->addWidget( layout->addWidget(
new QLabel(obs_module_text("AdvSceneSwitcher.variable.save")), new QLabel(obs_module_text("AdvSceneSwitcher.variable.save")),
row, 0); row, 0);
auto saveLayout = new QVBoxLayout; auto saveLayout = new QVBoxLayout;
saveLayout->addWidget(_save); saveLayout->addWidget(_save);
saveLayout->addWidget(_defaultValue); saveLayout->addWidget(_defaultValue);
saveLayout->addStretch(); saveLayout->addStretch();
layout->addLayout(saveLayout, row, 1); layout->addLayout(saveLayout, row, 1);
++row; layout->addWidget(_buttonbox, ++row, 0, 1, -1);
layout->addWidget(_buttonbox, row, 0, 1, -1);
layout->setSizeConstraint(QLayout::SetFixedSize); layout->setSizeConstraint(QLayout::SetFixedSize);
setLayout(layout); setLayout(layout);
} }
@@ -262,11 +197,13 @@ bool VariableSettingsDialog::AskForSettings(QWidget *parent, Variable &settings)
} }
settings._name = dialog._name->text().toStdString(); settings._name = dialog._name->text().toStdString();
settings._value = dialog._value->toPlainText().toStdString(); settings.SetValue(dialog._value->toPlainText().toStdString());
settings._defaultValue = settings._defaultValue =
dialog._defaultValue->toPlainText().toStdString(); dialog._defaultValue->toPlainText().toStdString();
settings._saveAction = settings._saveAction =
static_cast<Variable::SaveAction>(dialog._save->currentIndex()); static_cast<Variable::SaveAction>(dialog._save->currentIndex());
lastVariableChange = std::chrono::high_resolution_clock::now();
return true; return true;
} }
@@ -274,9 +211,9 @@ static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
{ {
Variable &VariableSettings = dynamic_cast<Variable &>(settings); Variable &VariableSettings = dynamic_cast<Variable &>(settings);
if (VariableSettingsDialog::AskForSettings(parent, VariableSettings)) { if (VariableSettingsDialog::AskForSettings(parent, VariableSettings)) {
lastVariableChange = std::chrono::high_resolution_clock::now();
return true; return true;
} }
return false; return false;
} }
@@ -340,4 +277,128 @@ VariableSignalManager *VariableSignalManager::Instance()
return &manager; return &manager;
} }
std::deque<std::shared_ptr<Item>> &GetVariables()
{
return variables;
}
Variable *GetVariableByName(const std::string &name)
{
for (const auto &v : variables) {
if (v->Name() == name) {
return dynamic_cast<Variable *>(v.get());
}
}
return nullptr;
}
Variable *GetVariableByQString(const QString &name)
{
return GetVariableByName(name.toStdString());
}
std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name)
{
for (const auto &v : variables) {
if (v->Name() == name) {
std::weak_ptr<Variable> wp =
std::dynamic_pointer_cast<Variable>(v);
return wp;
}
}
return std::weak_ptr<Variable>();
}
std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name)
{
return GetWeakVariableByName(name.toStdString());
}
QStringList GetVariablesNameList()
{
QStringList list;
for (const auto &var : variables) {
list << QString::fromStdString(var->Name());
}
list.sort();
return list;
}
std::string GetWeakVariableName(std::weak_ptr<Variable> var_)
{
auto var = var_.lock();
if (!var) {
return obs_module_text("AdvSceneSwitcher.variable.invalid");
}
return var->Name();
}
static bool variableWithNameExists(const std::string &name)
{
return !!GetVariableByName(name);
}
void SaveVariables(obs_data_t *obj)
{
obs_data_array_t *variablesArray = obs_data_array_create();
for (const auto &v : variables) {
obs_data_t *array_obj = obs_data_create();
v->Save(array_obj);
obs_data_array_push_back(variablesArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "variables", variablesArray);
obs_data_array_release(variablesArray);
}
void LoadVariables(obs_data_t *obj)
{
variables.clear();
obs_data_array_t *variablesArray = obs_data_get_array(obj, "variables");
size_t count = obs_data_array_count(variablesArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(variablesArray, i);
auto var = Variable::Create();
variables.emplace_back(var);
variables.back()->Load(array_obj);
obs_data_release(array_obj);
}
obs_data_array_release(variablesArray);
}
void ImportVariables(obs_data_t *data)
{
obs_data_array_t *array = obs_data_get_array(data, "variables");
size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
obs_data_t *arrayElement = obs_data_array_item(array, i);
auto var = Variable::Create();
var->Load(arrayElement);
obs_data_release(arrayElement);
if (variableWithNameExists(var->Name())) {
continue;
}
GetVariables().emplace_back(var);
}
obs_data_array_release(array);
}
std::chrono::high_resolution_clock::time_point GetLastVariableChangeTime()
{
return lastVariableChange;
}
} // namespace advss } // namespace advss

View File

@@ -17,14 +17,6 @@ class Variable : public Item {
public: public:
Variable(); Variable();
~Variable(); ~Variable();
void Load(obs_data_t *obj);
void Save(obs_data_t *obj) const;
EXPORT std::string Value(bool updateLastUsed = true) const;
EXPORT std::optional<double> DoubleValue() const;
EXPORT std::optional<int> IntValue() const;
void SetValue(const std::string &value);
void SetValue(double value);
std::string GetDefaultValue() const { return _defaultValue; }
static std::shared_ptr<Item> Create() static std::shared_ptr<Item> Create()
{ {
return std::make_shared<Variable>(); return std::make_shared<Variable>();
@@ -35,15 +27,31 @@ public:
SAVE, SAVE,
SET_DEFAULT, SET_DEFAULT,
}; };
void Load(obs_data_t *obj);
void Save(obs_data_t *obj) const;
EXPORT std::string Value(bool updateLastUsed = true) const;
EXPORT std::optional<double> DoubleValue() const;
EXPORT std::optional<int> IntValue() const;
std::string GetPreviousValue() const { return _previousValue; };
std::string GetDefaultValue() const { return _defaultValue; }
void SetValue(const std::string &value);
void SetValue(double value);
SaveAction GetSaveAction() const { return _saveAction; } SaveAction GetSaveAction() const { return _saveAction; }
std::optional<uint64_t> SecondsSinceLastUse() const; int GetValueChangeCount() const { return _valueChangeCount; }
std::optional<uint64_t> GetSecondsSinceLastUse() const;
std::optional<uint64_t> GetSecondsSinceLastChange() const;
void UpdateLastUsed() const; void UpdateLastUsed() const;
void UpdateLastChanged();
private: private:
SaveAction _saveAction = SaveAction::DONT_SAVE; SaveAction _saveAction = SaveAction::DONT_SAVE;
std::string _value = ""; std::string _value = "";
std::string _previousValue = "";
std::string _defaultValue = ""; std::string _defaultValue = "";
int _valueChangeCount = 0;
mutable std::chrono::high_resolution_clock::time_point _lastUsed; mutable std::chrono::high_resolution_clock::time_point _lastUsed;
mutable std::chrono::high_resolution_clock::time_point _lastChanged;
friend VariableSelection; friend VariableSelection;
friend VariableSettingsDialog; friend VariableSettingsDialog;
@@ -86,11 +94,7 @@ signals:
void Remove(const QString &); void Remove(const QString &);
}; };
void SaveVariables(obs_data_t *obj);
void LoadVariables(obs_data_t *obj);
std::deque<std::shared_ptr<Item>> &GetVariables(); std::deque<std::shared_ptr<Item>> &GetVariables();
EXPORT Variable *GetVariableByName(const std::string &name); EXPORT Variable *GetVariableByName(const std::string &name);
EXPORT Variable *GetVariableByQString(const QString &name); EXPORT Variable *GetVariableByQString(const QString &name);
EXPORT std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name); EXPORT std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name);
@@ -98,6 +102,10 @@ EXPORT std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name);
std::string GetWeakVariableName(std::weak_ptr<Variable>); std::string GetWeakVariableName(std::weak_ptr<Variable>);
EXPORT QStringList GetVariablesNameList(); EXPORT QStringList GetVariablesNameList();
void SaveVariables(obs_data_t *obj);
void LoadVariables(obs_data_t *obj);
void ImportVariables(obs_data_t *obj);
std::chrono::high_resolution_clock::time_point GetLastVariableChangeTime(); std::chrono::high_resolution_clock::time_point GetLastVariableChangeTime();
} // namespace advss } // namespace advss

View File

@@ -17,6 +17,8 @@ target_sources(
macro-action-hotkey.hpp macro-action-hotkey.hpp
macro-action-http.cpp macro-action-http.cpp
macro-action-http.hpp macro-action-http.hpp
macro-action-log.cpp
macro-action-log.hpp
macro-action-media.cpp macro-action-media.cpp
macro-action-media.hpp macro-action-media.hpp
macro-action-osc.cpp macro-action-osc.cpp
@@ -69,8 +71,12 @@ target_sources(
macro-action-wait.hpp macro-action-wait.hpp
macro-action-websocket.cpp macro-action-websocket.cpp
macro-action-websocket.hpp macro-action-websocket.hpp
macro-action-window.cpp
macro-action-window.hpp
macro-condition-audio.cpp macro-condition-audio.cpp
macro-condition-audio.hpp macro-condition-audio.hpp
macro-condition-clipboard.cpp
macro-condition-clipboard.hpp
macro-condition-cursor.cpp macro-condition-cursor.cpp
macro-condition-cursor.hpp macro-condition-cursor.hpp
macro-condition-date.cpp macro-condition-date.cpp
@@ -154,6 +160,8 @@ target_sources(
utils/scene-item-selection.hpp utils/scene-item-selection.hpp
utils/scene-item-transform-helpers.cpp utils/scene-item-transform-helpers.cpp
utils/scene-item-transform-helpers.hpp utils/scene-item-transform-helpers.hpp
utils/source-properties-button.cpp
utils/source-properties-button.hpp
utils/source-settings-helpers.cpp utils/source-settings-helpers.cpp
utils/source-settings-helpers.hpp utils/source-settings-helpers.hpp
utils/source-setting.cpp utils/source-setting.cpp

View File

@@ -29,6 +29,10 @@ static const std::map<MacroActionAudio::Action, std::string> actionTypes = {
"AdvSceneSwitcher.action.audio.type.monitor"}, "AdvSceneSwitcher.action.audio.type.monitor"},
{MacroActionAudio::Action::BALANCE, {MacroActionAudio::Action::BALANCE,
"AdvSceneSwitcher.action.audio.type.balance"}, "AdvSceneSwitcher.action.audio.type.balance"},
{MacroActionAudio::Action::ENABLE_ON_TRACK,
"AdvSceneSwitcher.action.audio.type.enableOnTrack"},
{MacroActionAudio::Action::DISABLE_ON_TRACK,
"AdvSceneSwitcher.action.audio.type.disableOnTrack"},
}; };
static const std::map<MacroActionAudio::FadeType, std::string> fadeTypes = { static const std::map<MacroActionAudio::FadeType, std::string> fadeTypes = {
@@ -205,6 +209,28 @@ void MacroActionAudio::StartFade() const
} }
} }
static void setMixerEnable(obs_source_t *source, const int mixerIdx,
const bool enable)
{
if (mixerIdx < 0) {
blog(LOG_INFO, "refusing to %s mixer id %d for %s",
enable ? "enable" : "disable", mixerIdx,
obs_source_get_name(source));
return;
}
uint32_t mixers = obs_source_get_audio_mixers(source);
uint32_t new_mixers = mixers;
if (enable) {
new_mixers |= (1 << mixerIdx);
} else {
new_mixers &= ~(1 << mixerIdx);
}
obs_source_set_audio_mixers(source, new_mixers);
}
bool MacroActionAudio::PerformAction() bool MacroActionAudio::PerformAction()
{ {
auto s = obs_weak_source_get_source(_audioSource.GetSource()); auto s = obs_weak_source_get_source(_audioSource.GetSource());
@@ -232,6 +258,12 @@ bool MacroActionAudio::PerformAction()
case Action::BALANCE: case Action::BALANCE:
obs_source_set_balance_value(s, _balance); obs_source_set_balance_value(s, _balance);
break; break;
case Action::ENABLE_ON_TRACK:
setMixerEnable(s, _track - 1, true);
break;
case Action::DISABLE_ON_TRACK:
setMixerEnable(s, _track - 1, false);
break;
default: default:
break; break;
} }
@@ -244,7 +276,7 @@ void MacroActionAudio::LogAction() const
auto it = actionTypes.find(_action); auto it = actionTypes.find(_action);
if (it != actionTypes.end()) { if (it != actionTypes.end()) {
auto &[_, action] = *it; auto &[_, action] = *it;
vblog(LOG_INFO, ablog(LOG_INFO,
"performed action \"%s\" for source \"%s\" with volume %f with fade %d %f", "performed action \"%s\" for source \"%s\" with volume %f with fade %d %f",
action.c_str(), _audioSource.ToString(true).c_str(), action.c_str(), _audioSource.ToString(true).c_str(),
GetVolume(), _fade, _duration.Seconds()); GetVolume(), _fade, _duration.Seconds());
@@ -263,6 +295,7 @@ bool MacroActionAudio::Save(obs_data_t *obj) const
obs_data_set_int(obj, "monitor", _monitorType); obs_data_set_int(obj, "monitor", _monitorType);
_syncOffset.Save(obj, "syncOffset"); _syncOffset.Save(obj, "syncOffset");
_balance.Save(obj, "balance"); _balance.Save(obj, "balance");
_track.Save(obj, "track");
_volume.Save(obj, "volume"); _volume.Save(obj, "volume");
_rate.Save(obj, "rate"); _rate.Save(obj, "rate");
obs_data_set_bool(obj, "fade", _fade); obs_data_set_bool(obj, "fade", _fade);
@@ -271,7 +304,7 @@ bool MacroActionAudio::Save(obs_data_t *obj) const
obs_data_set_bool(obj, "abortActiveFade", _abortActiveFade); obs_data_set_bool(obj, "abortActiveFade", _abortActiveFade);
obs_data_set_bool(obj, "useDb", _useDb); obs_data_set_bool(obj, "useDb", _useDb);
_volumeDB.Save(obj, "volumeDB"); _volumeDB.Save(obj, "volumeDB");
obs_data_set_int(obj, "version", 2); obs_data_set_int(obj, "version", 3);
return true; return true;
} }
@@ -312,13 +345,18 @@ bool MacroActionAudio::Load(obs_data_t *obj)
_abortActiveFade = false; _abortActiveFade = false;
} }
if (obs_data_get_int(obj, "version") != 2) { if (obs_data_get_int(obj, "version") < 2) {
_useDb = false; _useDb = false;
_volumeDB = 0.0; _volumeDB = 0.0;
} else { } else {
_useDb = obs_data_get_bool(obj, "useDb"); _useDb = obs_data_get_bool(obj, "useDb");
_volumeDB.Load(obj, "volumeDB"); _volumeDB.Load(obj, "volumeDB");
} }
if (obs_data_get_int(obj, "version") < 3) {
_track = 1;
} else {
_track.Load(obj, "track");
}
return true; return true;
} }
@@ -328,6 +366,27 @@ std::string MacroActionAudio::GetShortDesc() const
return _audioSource.ToString(); return _audioSource.ToString();
} }
std::shared_ptr<MacroAction> MacroActionAudio::Create(Macro *m)
{
return std::make_shared<MacroActionAudio>(m);
}
std::shared_ptr<MacroAction> MacroActionAudio::Copy() const
{
return std::make_shared<MacroActionAudio>(*this);
}
void MacroActionAudio::ResolveVariablesToFixedValues()
{
_audioSource.ResolveVariables();
_syncOffset.ResolveVariables();
_balance.ResolveVariables();
_volume.ResolveVariables();
_volumeDB.ResolveVariables();
_duration.ResolveVariables();
_rate.ResolveVariables();
}
static inline void populateActionSelection(QComboBox *list) static inline void populateActionSelection(QComboBox *list)
{ {
for (const auto &[action, name] : actionTypes) { for (const auto &[action, name] : actionTypes) {
@@ -360,6 +419,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
0., 1., "", 0., 1., "",
obs_module_text( obs_module_text(
"AdvSceneSwitcher.action.audio.balance.description"))), "AdvSceneSwitcher.action.audio.balance.description"))),
_track(new VariableSpinBox),
_volumePercent(new VariableSpinBox), _volumePercent(new VariableSpinBox),
_volumeDB(new VariableDoubleSpinBox), _volumeDB(new VariableDoubleSpinBox),
_percentDBToggle(new QPushButton), _percentDBToggle(new QPushButton),
@@ -377,6 +437,9 @@ MacroActionAudioEdit::MacroActionAudioEdit(
_syncOffset->setMaximum(20000); _syncOffset->setMaximum(20000);
_syncOffset->setSuffix("ms"); _syncOffset->setSuffix("ms");
_track->setMinimum(1);
_track->setMaximum(32);
_volumePercent->setMinimum(0); _volumePercent->setMinimum(0);
_volumePercent->setMaximum(2000); _volumePercent->setMaximum(2000);
_volumePercent->setSuffix("%"); _volumePercent->setSuffix("%");
@@ -412,6 +475,10 @@ MacroActionAudioEdit::MacroActionAudioEdit(
this, SLOT(BalanceChanged(const NumberVariable<double> &))); this, SLOT(BalanceChanged(const NumberVariable<double> &)));
QWidget::connect(_monitorTypes, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_monitorTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(MonitorTypeChanged(int))); SLOT(MonitorTypeChanged(int)));
QWidget::connect(
_track,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(TrackChanged(const NumberVariable<int> &)));
QWidget::connect( QWidget::connect(
_volumePercent, _volumePercent,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)), SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
@@ -443,6 +510,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
{"{{syncOffset}}", _syncOffset}, {"{{syncOffset}}", _syncOffset},
{"{{monitorTypes}}", _monitorTypes}, {"{{monitorTypes}}", _monitorTypes},
{"{{balance}}", _balance}, {"{{balance}}", _balance},
{"{{track}}", _track},
{"{{volume}}", _volumePercent}, {"{{volume}}", _volumePercent},
{"{{volumeDB}}", _volumeDB}, {"{{volumeDB}}", _volumeDB},
{"{{percentDBToggle}}", _percentDBToggle}, {"{{percentDBToggle}}", _percentDBToggle},
@@ -498,6 +566,10 @@ void MacroActionAudioEdit::SetWidgetVisibility()
MacroActionAudio::Action::MONITOR); MacroActionAudio::Action::MONITOR);
_balance->setVisible(_entryData->_action == _balance->setVisible(_entryData->_action ==
MacroActionAudio::Action::BALANCE); MacroActionAudio::Action::BALANCE);
_track->setVisible(_entryData->_action ==
MacroActionAudio::Action::ENABLE_ON_TRACK ||
_entryData->_action ==
MacroActionAudio::Action::DISABLE_ON_TRACK);
_fadeTypeLayout->removeWidget(_fade); _fadeTypeLayout->removeWidget(_fade);
_fadeTypeLayout->removeWidget(_fadeTypes); _fadeTypeLayout->removeWidget(_fadeTypes);
@@ -562,6 +634,7 @@ void MacroActionAudioEdit::UpdateEntryData()
_syncOffset->SetValue(_entryData->_syncOffset); _syncOffset->SetValue(_entryData->_syncOffset);
_monitorTypes->setCurrentIndex(_entryData->_monitorType); _monitorTypes->setCurrentIndex(_entryData->_monitorType);
_balance->SetDoubleValue(_entryData->_balance); _balance->SetDoubleValue(_entryData->_balance);
_track->SetValue(_entryData->_track);
_volumePercent->SetValue(_entryData->_volume); _volumePercent->SetValue(_entryData->_volume);
_volumeDB->SetValue(_entryData->_volumeDB); _volumeDB->SetValue(_entryData->_volumeDB);
_fade->setChecked(_entryData->_fade); _fade->setChecked(_entryData->_fade);
@@ -627,6 +700,16 @@ void MacroActionAudioEdit::BalanceChanged(const NumberVariable<double> &value)
_entryData->_balance = value; _entryData->_balance = value;
} }
void MacroActionAudioEdit::TrackChanged(const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_track = value;
}
void MacroActionAudioEdit::VolumeDBChanged(const NumberVariable<double> &value) void MacroActionAudioEdit::VolumeDBChanged(const NumberVariable<double> &value)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {

View File

@@ -20,10 +20,9 @@ public:
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc() const; std::string GetShortDesc() const;
std::string GetId() const { return id; }; std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m) static std::shared_ptr<MacroAction> Create(Macro *m);
{ std::shared_ptr<MacroAction> Copy() const;
return std::make_shared<MacroActionAudio>(m); void ResolveVariablesToFixedValues();
}
SourceSelection _audioSource; SourceSelection _audioSource;
@@ -35,6 +34,8 @@ public:
SYNC_OFFSET, SYNC_OFFSET,
MONITOR, MONITOR,
BALANCE, BALANCE,
ENABLE_ON_TRACK,
DISABLE_ON_TRACK,
}; };
enum class FadeType { enum class FadeType {
@@ -44,15 +45,16 @@ public:
Action _action = Action::MUTE; Action _action = Action::MUTE;
FadeType _fadeType = FadeType::DURATION; FadeType _fadeType = FadeType::DURATION;
NumberVariable<int> _syncOffset = 0; IntVariable _syncOffset = 0;
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE; obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
NumberVariable<double> _balance = 0.5; DoubleVariable _balance = 0.5;
IntVariable _track = 1;
bool _useDb = false; bool _useDb = false;
NumberVariable<int> _volume = 0; IntVariable _volume = 0;
NumberVariable<double> _volumeDB = 0.0; DoubleVariable _volumeDB = 0.0;
bool _fade = false; bool _fade = false;
Duration _duration; Duration _duration;
NumberVariable<double> _rate = 100.; DoubleVariable _rate = 100.;
bool _wait = false; bool _wait = false;
bool _abortActiveFade = false; bool _abortActiveFade = false;
@@ -92,6 +94,7 @@ private slots:
void SyncOffsetChanged(const NumberVariable<int> &value); void SyncOffsetChanged(const NumberVariable<int> &value);
void MonitorTypeChanged(int value); void MonitorTypeChanged(int value);
void BalanceChanged(const NumberVariable<double> &value); void BalanceChanged(const NumberVariable<double> &value);
void TrackChanged(const NumberVariable<int> &value);
void VolumeChanged(const NumberVariable<int> &value); void VolumeChanged(const NumberVariable<int> &value);
void VolumeDBChanged(const NumberVariable<double> &value); void VolumeDBChanged(const NumberVariable<double> &value);
void PercentDBClicked(); void PercentDBClicked();
@@ -111,6 +114,7 @@ protected:
VariableSpinBox *_syncOffset; VariableSpinBox *_syncOffset;
QComboBox *_monitorTypes; QComboBox *_monitorTypes;
SliderSpinBox *_balance; SliderSpinBox *_balance;
VariableSpinBox *_track;
VariableSpinBox *_volumePercent; VariableSpinBox *_volumePercent;
VariableDoubleSpinBox *_volumeDB; VariableDoubleSpinBox *_volumeDB;
QPushButton *_percentDBToggle; QPushButton *_percentDBToggle;

View File

@@ -1,6 +1,7 @@
#include "macro-action-clipboard.hpp" #include "macro-action-clipboard.hpp"
#include "curl-helper.hpp" #include "curl-helper.hpp"
#include <obs.hpp>
#include <QApplication> #include <QApplication>
#include <QClipboard> #include <QClipboard>
#include <QImage> #include <QImage>
@@ -92,6 +93,16 @@ static void copyImageFromUrl(void *param)
setMimeTypeParams(params, clipboard); setMimeTypeParams(params, clipboard);
} }
std::shared_ptr<MacroAction> MacroActionClipboard::Create(Macro *m)
{
return std::make_shared<MacroActionClipboard>(m);
}
std::shared_ptr<MacroAction> MacroActionClipboard::Copy() const
{
return std::make_shared<MacroActionClipboard>(*this);
}
bool MacroActionClipboard::PerformAction() bool MacroActionClipboard::PerformAction()
{ {
switch (_action) { switch (_action) {
@@ -158,6 +169,12 @@ bool MacroActionClipboard::Load(obs_data_t *obj)
return true; return true;
} }
void MacroActionClipboard::ResolveVariablesToFixedValues()
{
_text.ResolveVariables();
_url.ResolveVariables();
}
static inline void populateActionSelection(QComboBox *list) static inline void populateActionSelection(QComboBox *list)
{ {
for (const auto &[action, name] : actionTypes) { for (const auto &[action, name] : actionTypes) {

View File

@@ -22,16 +22,16 @@ struct ClipboardImageQueueParams : ClipboardQueueParams {
class MacroActionClipboard : public MacroAction { class MacroActionClipboard : public MacroAction {
public: public:
MacroActionClipboard(Macro *m) : MacroAction(m) {} MacroActionClipboard(Macro *m) : MacroAction(m) {}
static std::shared_ptr<MacroAction> Create(Macro *m) static std::shared_ptr<MacroAction> Create(Macro *m);
{ std::shared_ptr<MacroAction> Copy() const;
return std::make_shared<MacroActionClipboard>(m);
}
std::string GetId() const { return id; }; std::string GetId() const { return id; };
bool PerformAction(); bool PerformAction();
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
void ResolveVariablesToFixedValues();
enum class Action { enum class Action {
COPY_TEXT, COPY_TEXT,
COPY_IMAGE, COPY_IMAGE,

Some files were not shown because too many files have changed in this diff Show More