Compare commits

...

134 Commits

Author SHA1 Message Date
WarmUpTill
e3ff6f34fc Update OpenCV to 4.10.0
This adds support for the MSVC toolset 19.40.x
2024-06-11 19:01:01 +02:00
WarmUpTill
ed75a221fe Fix crashes in combination with older MSVC redistributable versions
Since the switch of Windows 2022 GitHub actions runner image to version
20240603.1.1 the Visual Studio version was bumped to 17.10.34928.147.
This results in MSVC version 19.40.33811.0 being used at build time.
Combining this with e.g. the MSVC redistributable version 14.34.31938.0
results in segfaults when calling std::mutex::lock.

See also:
https://developercommunity.visualstudio.com/t/Invalid-code-generation-in-release-1940/10678572
2024-06-09 00:39:54 +02:00
WarmUpTill
7b48cb2b8d Fix locale error 2024-06-09 00:39:51 +02:00
WarmUpTill
9d4c8ab475 Fix stylesheet of StatusControl not being set to stopped state 2024-06-07 23:05:08 +02:00
WarmUpTill
3d61ea7d25 Add option to reevaluate condition state in "Macro" action 2024-06-07 21:44:21 +02:00
WarmUpTill
99c8a46296 Add option to ignore pause during condition check 2024-06-07 21:44:21 +02:00
WarmUpTill
7e1d20031c Add HelpIcon
A QLabel consisting of a question mark icon and tooltip
2024-06-07 21:44:21 +02:00
WarmUpTill
0e4b000fb1 Set default action of tpye "Macro" to "Run" 2024-06-07 21:44:21 +02:00
WarmUpTill
fc2451ae08 Rename MacroProperties to MacroSettings 2024-06-07 21:44:21 +02:00
WarmUpTill
be44577967 Add option to set input variable values to Macro action 2024-06-07 21:44:21 +02:00
WarmUpTill
3480ff238e Add macro input variable support 2024-06-07 21:44:21 +02:00
WarmUpTill
ee68427036 Add HighligthMacroSettingsButton() 2024-06-07 21:44:21 +02:00
WarmUpTill
37c32dd1ed Move StringList from base to lib 2024-06-07 21:44:21 +02:00
WarmUpTill
c05a92d417 Refactor widget highlighting 2024-06-07 21:44:21 +02:00
WarmUpTill
d72a0c0d38 Add VariableSelectionDialog 2024-06-07 21:44:21 +02:00
WarmUpTill
235b31fccc Add GUARD_LOADING_AND_LOCK
Helper macro to reduce repetition
2024-06-07 21:44:21 +02:00
WarmUpTill
286f781f4d Add ListEditor helper widget 2024-06-07 21:44:21 +02:00
WarmUpTill
b7516cac5b Fix crash when adding resources to resources tab
A crash could occur if the plugin window was closed and reopened and a
new resource was added to either the Variable, Action queue, Websocket,
or Twitch tab.

No context object was provided to the signal handlers.
The signal provider is intentionally not deleted when the UI is closed.
Because of this, the signal connections were never cleared when the
underlying tab widget was destroyed.
So, in the case of closing and reopening the settings window, the old
connection with the outdated widget pointers would be called again.
2024-06-05 01:05:31 +02:00
WarmUpTill
45967a090f Add tempvar support to filter condition 2024-06-05 01:05:31 +02:00
WarmUpTill
687fb4623e Add tempvar support to source condition 2024-06-05 01:05:31 +02:00
WarmUpTill
4157ae4ed2 Add option to check source height and width 2024-06-05 01:05:31 +02:00
WarmUpTill
5e1469b0d8 Don't restore last opened tab if that tab is no longer visible 2024-06-05 01:05:31 +02:00
WarmUpTill
b3bde6c59f Fix imported variables / actions not being added to respective tabs 2024-06-05 01:05:31 +02:00
WarmUpTill
032f5e2fe9 Improve visibility of plugin stop warning flash in status dock 2024-06-05 01:05:31 +02:00
WarmUpTill
91c053dfd8 Re-add tooltips for the "last used" cells on the variable tab 2024-06-05 01:05:31 +02:00
Przemek Pawlas
1f1ef4ca03 Add variable values swap action 2024-05-25 19:04:09 +02:00
WarmUpTill
7cc44b4470 Fix folder watch condition filter handling 2024-05-19 03:46:53 +02:00
WarmUpTill
54356d9410 Add tooltip to macro property selection in variable action 2024-05-19 01:44:10 +02:00
WarmUpTill
defbdf8b7a Add folder condition
It allows you to watch for chagnes in a given folder
2024-05-19 01:44:10 +02:00
WarmUpTill
a01af6cfc3 Adapt code to be compatible with older MSVC redistributable versions 2024-05-18 23:31:04 +02:00
WarmUpTill
4d9c7f1054 Fix typo 2024-05-18 23:31:04 +02:00
WarmUpTill
ced36e2b5b Add nodiscard to sync-helpers 2024-05-18 23:31:04 +02:00
WarmUpTill
d2749f29b0 Add note about limitations of the window focus action 2024-05-14 20:23:36 +02:00
WarmUpTill
b78c8bc1de Assume minimum volume when no volume update was received within 250ms
Without this timeout the peak volume update, which was received last,
would be used permanently until the next update arrives.
This might only take place when the source produces audio output again.
2024-05-14 19:12:49 +02:00
WarmUpTill
cc4a3560d8 Fix "Window" actions not being applied to OBS windows 2024-05-12 01:29:06 +02:00
WarmUpTill
3ab2ea9e66 Only update Twitch tab content when switching to it
This should reduce the potential lag introduced when querying the token
status using the Twitch API
2024-05-12 01:28:51 +02:00
WarmUpTill
69b82ee2a3 Fix audio condition incorrectly reporting very low volume levels
OBS might very rarely not update _peak quickly enough when very low
intervals are configured on the General tab.
In that case _peak might be set to negative infinity still, which will
result in unexpected behavior, so we use the previously valid peak value
instead.
2024-05-10 15:11:20 +02:00
WarmUpTill
f14e6bf252 Fix crash when access to midi devices is denied 2024-05-10 15:11:20 +02:00
WarmUpTill
e51ae79b4c Improve window action
* The focus action no longer restores maximized windows
* The focus, maximize, minimize, and close actions will be performed on
  all handles matching the given window title
2024-05-10 15:11:20 +02:00
WarmUpTill
8f55856fd5 Fix macro segment highlighting not being in sync
Each macro segment had its own internal timer to handle highlighting.
This handing was now moved centrally to the macro tab.
2024-05-10 15:11:20 +02:00
WarmUpTill
a360d53419 Enable right-click to select macro segments
This will be used to enable copy-paste via the context menu available
when right-clicking a macro segment
2024-05-10 15:11:20 +02:00
WarmUpTill
d28164d02f Fix initial replay buffer save not triggering condition 2024-05-10 15:11:20 +02:00
WarmUpTill
f723212394 Add copy-paste support for individual macro segments 2024-05-10 15:11:20 +02:00
WarmUpTill
a0cb08d18f Fix potential crash when creating actions 2024-05-10 15:11:20 +02:00
WarmUpTill
d18044b764 Add handling for fading of else-controls 2024-05-10 15:11:20 +02:00
WarmUpTill
406b5d61f5 Add option to conditionally run action of other macros 2024-05-10 15:11:20 +02:00
WarmUpTill
9a4eef4a83 Adjust busy loop sleep to 10ms
This is now in line with the new minimum interval value
2024-05-10 15:11:20 +02:00
WarmUpTill
efaf9a2ef3 Add option to check if hotkey is released
This was already possible previously by inverting the logic with a
"not", however, this should it make it more obvious that this type of
check is possible.
2024-05-09 23:19:08 +02:00
WarmUpTill
edb2952fd6 Add temp vars to file condition 2024-05-09 22:14:43 +02:00
Denilson Sá Maia
088828a351 Fixed typo 2024-05-03 20:04:22 +02:00
WarmUpTill
e359b7fa00 Add Twitch connection tab 2024-05-01 19:44:57 +02:00
WarmUpTill
44fc5177d6 Add action queue tab 2024-05-01 19:44:57 +02:00
WarmUpTill
604a27141f Move files 2024-05-01 19:44:57 +02:00
WarmUpTill
fd65a84d7d Improve buildnumber handling 2024-05-01 19:44:57 +02:00
WarmUpTill
966b389807 Rework variable tab to use tab helpers 2024-05-01 19:44:57 +02:00
WarmUpTill
a6bcce5a63 Add websocket connections tab 2024-05-01 19:44:57 +02:00
WarmUpTill
530fbdd282 Add ResourceTable class
Base class to be used to display items in a table
2024-05-01 19:44:57 +02:00
WarmUpTill
54f1051456 Ease detection of calling obs_module_text() too early 2024-05-01 19:44:57 +02:00
WarmUpTill
d136bf5561 Fix crash when on startup when registering new tab 2024-05-01 19:44:57 +02:00
WarmUpTill
5cba948e02 Decouple plugin init, load, cleanup handling from SwitcherData 2024-05-01 19:44:57 +02:00
WarmUpTill
ab922f6735 Rename and refactor websocket helpers 2024-05-01 19:44:57 +02:00
WarmUpTill
d71c87535f Add RemoveItemsByName()
Helper function to remove Items by name from std::queue of Item
2024-05-01 19:44:57 +02:00
WarmUpTill
ce501bd972 Move / add tabWidget related helper functions 2024-05-01 19:44:57 +02:00
WarmUpTill
ec5944f53c Fix Debian cmake configure issue
The "CI" environment variable was defined but "GITHUB_RUN_ID" was not
resulting in a broken if statement:

if(true AND)

https://github.com/WarmUpTill/SceneSwitcher/issues/1090
2024-04-25 19:31:18 +02:00
WarmUpTill
5e64e0bbaa Disable "psabi" warning for x86 Debian build
https://github.com/WarmUpTill/SceneSwitcher/issues/1091
2024-04-25 19:31:18 +02:00
WarmUpTill
e16f1d732d Fix layout of the "System tray notification" action 2024-04-25 19:30:43 +02:00
WarmUpTill
cdacfc946c Save macro segment settings in separate object
To avoid conflicts with other setting of the segment
2024-04-20 17:39:47 +02:00
WarmUpTill
26e854f0a3 Open macro selection dialog at plugin window location 2024-04-20 17:39:47 +02:00
WarmUpTill
2bc89364b2 Refactor NameDialog 2024-04-20 17:39:47 +02:00
WarmUpTill
e080d2de9b Add option to set custom labels on macro segments 2024-04-20 17:39:47 +02:00
WarmUpTill
ca1262aeb7 Resize temp var selection when repopulating list 2024-04-20 17:39:47 +02:00
WarmUpTill
cbc95e2095 Add option to export macro as plain json 2024-04-20 17:39:47 +02:00
WarmUpTill
9c5051cbf8 Increase scene item index selection maximum to 999 2024-04-20 17:39:47 +02:00
WarmUpTill
e6ca3390a2 Resize settings selection when repopulating list 2024-04-20 17:39:47 +02:00
Przemek Pawlas
e74b531905 Fix saving scene item index range 2024-04-15 19:17:28 +02:00
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
272 changed files with 8740 additions and 2421 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 "coreutils"
brew "cmake"
brew "git"
brew "jq"
brew "xcbeautify"

View File

@@ -163,9 +163,13 @@ ${_usage_host:-}"
local product_name
local product_version
local git_tag="$(git describe --tags)"
read -r product_name product_version <<< \
"$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})"
log_info "Using git tag as version identifier '${git_tag}'"
product_version="${git_tag}"
if [[ ${host_os} == macos ]] {
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}
popd
local output_name="${product_name}-${product_version}-${target##*-}-linux-gnu"
if (( package )) {
log_group "Packaging ${product_name}..."
pushd ${project_root}
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
}
log_group "Archiving ${product_name}..."
local output_name="${product_name}-${product_version}-${target##*-}-linux-gnu"
local _tarflags='cJf'
if (( _loglevel > 1 || ${+CI} )) _tarflags="v${_tarflags}"

View File

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

0
.github/test vendored Normal file
View File

5
.gitignore vendored
View File

@@ -14,7 +14,9 @@
!/tests
!.clang-format
!.cmake-format.json
!.gitattributes
!.gitignore
!.pre-commit-config.yaml
!BUILDING.md
!buildspec.json
!CMakeLists.txt
@@ -22,6 +24,9 @@
!LICENSE
!README.md
# Exclude .orig leftovers
*.orig
# Exclude lock files
*.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**)
- 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)
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/:
```
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:
```
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:
```
cd SceneSwitcher
cmake --preset windows-x64
```
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
```
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 |
| ----------- | ----------- |
@@ -83,10 +85,10 @@ You can rely on the CI scripts to build the dependencies for you, although it is
# 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`.
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 conditions should inherit from the `MacroCondition` class and must implement the following functions:
@@ -97,12 +99,12 @@ public:
MacroConditionExample(Macro *m) : MacroCondition(m) {}
// This function should perform the condition check
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
bool Save(obs_data_t *obj);
// This function should load the condition data from "obj"
bool Save(obs_data_t *data);
// This function should load the condition data from "data"
// 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
// The _id is defined below
std::string GetId() { return _id; };
@@ -120,7 +122,7 @@ private:
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()`)
```
class MacroConditionExampleEdit : public QWidget {
@@ -151,7 +153,7 @@ bool MacroConditionExample::_registered = MacroConditionFactory::Register(
MacroConditionExample::id, // Unique string identifying this condition type
{
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)
true // Condition type supports duration modifiers (default true)
}
@@ -167,16 +169,18 @@ The differences are highlighted in the comments below.
class MacroActionExample : public MacroAction {
public:
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
// If false is returned the macro will aborted
bool PerformAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
bool Save(obs_data_t *data);
bool Load(obs_data_t *data);
std::string GetId() { return _id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionExample>(m);
}
// Optional:
// Might be called when action is inserted to action queue
void ResolveVariablesToFixedValues();
private:
static bool _registered;
@@ -207,11 +211,11 @@ MacroActionFactory::Register(
MacroActionExample::id, // Unique string identifying this action type
{
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
}
);
```
## 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.
For example `plugins/video` will only be loaded if the `OpenCV` dependencies are met.
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.

View File

@@ -120,14 +120,16 @@ target_sources(
lib/macro/macro-export-import-dialog.hpp
lib/macro/macro-helpers.cpp
lib/macro/macro-helpers.hpp
lib/macro/macro-input.cpp
lib/macro/macro-input.hpp
lib/macro/macro-list.cpp
lib/macro/macro-list.hpp
lib/macro/macro-properties.cpp
lib/macro/macro-properties.hpp
lib/macro/macro-ref.cpp
lib/macro/macro-ref.hpp
lib/macro/macro-run-button.cpp
lib/macro/macro-run-button.hpp
lib/macro/macro-segment-copy-paste.cpp
lib/macro/macro-segment-copy-paste.hpp
lib/macro/macro-segment-list.cpp
lib/macro/macro-segment-list.hpp
lib/macro/macro-segment-selection.cpp
@@ -136,6 +138,8 @@ target_sources(
lib/macro/macro-segment.hpp
lib/macro/macro-selection.cpp
lib/macro/macro-selection.hpp
lib/macro/macro-settings.cpp
lib/macro/macro-settings.hpp
lib/macro/macro-tab.cpp
lib/macro/macro-tree.cpp
lib/macro/macro-tree.hpp
@@ -145,10 +149,16 @@ target_sources(
# Utility function sources
target_sources(
${LIB_NAME}
PRIVATE lib/utils/action-queue.cpp
lib/utils/action-queue.hpp
PRIVATE lib/queue/action-queue.cpp
lib/queue/action-queue.hpp
lib/queue/action-queue-tab.cpp
lib/queue/action-queue-tab.hpp
lib/utils/backup.cpp
lib/utils/backup.hpp
lib/utils/curl-helper.cpp
lib/utils/curl-helper.hpp
lib/utils/double-slider.cpp
lib/utils/double-slider.hpp
lib/utils/duration-control.cpp
lib/utils/duration-control.hpp
lib/utils/duration.cpp
@@ -158,10 +168,14 @@ target_sources(
lib/utils/file-selection.hpp
lib/utils/filter-combo-box.cpp
lib/utils/filter-combo-box.hpp
lib/utils/help-icon.hpp
lib/utils/help-icon.cpp
lib/utils/item-selection-helpers.cpp
lib/utils/item-selection-helpers.hpp
lib/utils/layout-helpers.cpp
lib/utils/layout-helpers.hpp
lib/utils/list-editor.cpp
lib/utils/list-editor.hpp
lib/utils/log-helper.cpp
lib/utils/log-helper.hpp
lib/utils/math-helpers.cpp
@@ -187,6 +201,10 @@ target_sources(
lib/utils/regex-config.hpp
lib/utils/resizing-text-edit.cpp
lib/utils/resizing-text-edit.hpp
lib/utils/resource-table.cpp
lib/utils/resource-table.hpp
lib/utils/resource-table-hotkey-handler.cpp
lib/utils/resource-table-hotkey-handler.hpp
lib/utils/scene-selection.cpp
lib/utils/scene-selection.hpp
lib/utils/scene-switch-helpers.cpp
@@ -197,6 +215,8 @@ target_sources(
lib/utils/section.hpp
lib/utils/selection-helpers.cpp
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.hpp
lib/utils/source-helpers.cpp
@@ -207,10 +227,14 @@ target_sources(
lib/utils/splitter-helpers.hpp
lib/utils/status-control.cpp
lib/utils/status-control.hpp
lib/utils/string-list.cpp
lib/utils/string-list.hpp
lib/utils/switch-button.cpp
lib/utils/switch-button.hpp
lib/utils/sync-helpers.cpp
lib/utils/sync-helpers.hpp
lib/utils/tab-helpers.cpp
lib/utils/tab-helpers.hpp
lib/utils/temp-variable.cpp
lib/utils/temp-variable.hpp
lib/utils/ui-helpers.cpp
@@ -228,6 +252,7 @@ target_sources(
lib/variables/variable-string.cpp
lib/variables/variable-string.hpp
lib/variables/variable-tab.cpp
lib/variables/variable-tab.hpp
lib/variables/variable-text-edit.cpp
lib/variables/variable-text-edit.hpp
lib/variables/variable.cpp
@@ -262,6 +287,7 @@ target_include_directories(
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/lib"
"${CMAKE_CURRENT_SOURCE_DIR}/lib/legacy"
"${CMAKE_CURRENT_SOURCE_DIR}/lib/macro"
"${CMAKE_CURRENT_SOURCE_DIR}/lib/queue"
"${CMAKE_CURRENT_SOURCE_DIR}/lib/utils"
"${CMAKE_CURRENT_SOURCE_DIR}/lib/variables"
"${CMAKE_CURRENT_BINARY_DIR}/forms")
@@ -319,6 +345,9 @@ if(OS_WINDOWS)
if(MSVC)
target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267
/bigobj)
# Workaround for MSVC incompatibility in CI environment
add_compile_definitions(${target_name} _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
endif()
target_sources(${LIB_NAME} PRIVATE lib/win/advanced-scene-switcher-win.cpp)
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)

View File

@@ -22,4 +22,5 @@ sudo snap install obs-studio
## Contributing
- 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`.

View File

@@ -49,7 +49,16 @@ invoke_formatter() {
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)
if (( _loglevel > 2 )) format_args+=(--verbose)

View File

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

View File

@@ -12,8 +12,14 @@ if(NOT DEFINED PLUGIN_BUILD_NUMBER AND EXISTS "${_BUILD_NUMBER_CACHE}")
file(READ "${_BUILD_NUMBER_CACHE}" PLUGIN_BUILD_NUMBER)
math(EXPR PLUGIN_BUILD_NUMBER "${PLUGIN_BUILD_NUMBER}+1")
elseif(NOT DEFINED PLUGIN_BUILD_NUMBER)
if($ENV{CI} AND $ENV{GITHUB_RUN_ID})
set(PLUGIN_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}")
if($ENV{CI})
if($ENV{GITHUB_RUN_ID})
set(PLUGIN_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}")
elseif($ENV{GITLAB_RUN_ID})
set(PLUGIN_BUILD_NUMBER "$ENV{GITLAB_RUN_ID}")
else()
set(PLUGIN_BUILD_NUMBER "1")
endif()
else()
set(PLUGIN_BUILD_NUMBER "1")
endif()

View File

@@ -68,13 +68,21 @@ else()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
# Disable warning for https://github.com/WarmUpTill/SceneSwitcher/issues/1091
add_compile_options(-Wno-error=psabi)
# Disable false-positive warning in GCC 12.1.0 and later
add_compile_options(-Wno-error=maybe-uninitialized)
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0)
add_compile_options(-Wno-error=maybe-uninitialized)
add_compile_options(-Wno-error=stringop-overflow)
endif()
# Add warning for infinite recursion (added in GCC 12)
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
add_compile_options(-Winfinite-recursion)
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang)
add_compile_options(-Wno-error=null-pointer-subtraction)
endif()
# 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.name="Name:"
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.onChange="Nur bei Änderung ausführen"
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1"
AdvSceneSwitcher.macroTab.exists="Makro-Name existiert bereits"
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
AdvSceneSwitcher.macroTab.copy="Kopie des aktuellen Makros erstellen"
AdvSceneSwitcher.macroTab.group="Ausgewählte Elemente gruppieren"
AdvSceneSwitcher.macroTab.ungroup="Gruppierung ausgewählter Gruppen aufheben"
@@ -288,8 +287,8 @@ AdvSceneSwitcher.condition.sceneOrder.entry="Auf{{scenes}}{{sources}}{{condition
AdvSceneSwitcher.condition.hotkey="Hotkey"
AdvSceneSwitcher.condition.hotkey.name="Makro-Trigger-Hotkey"
AdvSceneSwitcher.condition.hotkey.tip="Hinweis: Die Tastenkombinationen für diesen Hotkey können in den OBS-Einstellungen konfiguriert werden"
AdvSceneSwitcher.condition.hotkey.entry.line1="Hotkey ist gedrückt"
AdvSceneSwitcher.condition.hotkey.entry.line2="Name: {{name}}"
AdvSceneSwitcher.condition.hotkey.entry.keyState="Hotkey ist{{keyState}}"
AdvSceneSwitcher.condition.hotkey.entry.name="Name:{{name}}"
AdvSceneSwitcher.condition.replay="Replay Buffer"
AdvSceneSwitcher.condition.replay.state.stopped="Replay Buffer gestoppt"
AdvSceneSwitcher.condition.replay.state.started="Replay Buffer gestartet"
@@ -445,7 +444,7 @@ AdvSceneSwitcher.action.filter="Filter"
AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
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.source="Quelle"
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.pressSettingsButton="Drücken der Einstellungstaste"
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.getSettings="Aktuelle Einstellungen abfragen"
AdvSceneSwitcher.action.media="Medien"
@@ -471,7 +469,6 @@ AdvSceneSwitcher.action.macro.type.unpause="Nicht mehr pausieren"
AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen"
AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen"
AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Plugin-Status"
AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen"
AdvSceneSwitcher.action.pluginState.type.noMatch="Ändern des Nichtübereinstimmungsverhaltens:"
@@ -545,7 +542,7 @@ AdvSceneSwitcher.action.sceneCollection="Szenensammlung"
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.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.none="keines"
AdvSceneSwitcher.action.sequence.restart="Neustart am Anfang, wenn das Ende der Liste erreicht ist"
@@ -571,6 +568,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.currentSegmentValue="Aktueller Wert:"
AdvSceneSwitcher.noSettingsButtons="Keine Buttons gefunden!"
; Transition Tab
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.cooldown="After performing actions skip performing actions for"
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.saveWindowGeo="Save window position and size"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
@@ -68,14 +72,62 @@ AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one optio
; Variables Tab
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.tooltip.variableAddButton="Add new variable"
AdvSceneSwitcher.variableTab.tooltip.variableRemoveButton="Remove selected variables"
AdvSceneSwitcher.variableTab.header.name="Name"
AdvSceneSwitcher.variableTab.header.value="Value"
AdvSceneSwitcher.variableTab.header.saveLoadBehavior="Save/Load behavior"
AdvSceneSwitcher.variableTab.header.lastUse="Last used"
AdvSceneSwitcher.variableTab.neverNused="Never"
AdvSceneSwitcher.variableTab.lastUsed="%1 seconds ago"
AdvSceneSwitcher.variableTab.variableAddButton.tooltip="Add new variable"
AdvSceneSwitcher.variableTab.variableRemoveButton.tooltip="Remove selected variables"
AdvSceneSwitcher.variableTab.removeSingleVariablePopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.variableTab.removeMultipleVariablesPopup.text="Are you sure you want to remove %1 variables?"
AdvSceneSwitcher.variableTab.name.header="Name"
AdvSceneSwitcher.variableTab.value.header="Value"
AdvSceneSwitcher.variableTab.saveLoadBehavior.header="Save/Load behavior"
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"
; Action Queue Tab
AdvSceneSwitcher.actionQueueTab.title="Action Queues"
AdvSceneSwitcher.actionQueueTab.help="Action queues are executed sequentially but in parallel to the reset of the macro system.\nThe first action added to the queue will be the first one to be processed.\n\nClick on the highlighted plus symbol to add a new queue."
AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="Add new action queue"
AdvSceneSwitcher.actionQueueTab.queueRemoveButton.tooltip="Remove selected action queues"
AdvSceneSwitcher.actionQueueTab.name.header="Name"
AdvSceneSwitcher.actionQueueTab.size.header="Size"
AdvSceneSwitcher.actionQueueTab.isRunning.header="Is running"
AdvSceneSwitcher.actionQueueTab.runOnStartup.header="Run on startup"
AdvSceneSwitcher.actionQueueTab.yes="Yes"
AdvSceneSwitcher.actionQueueTab.no="No"
AdvSceneSwitcher.actionQueueTab.removeSingleQueuePopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.actionQueueTab.removeMultipleQueuesPopup.text="Are you sure you want to remove %1 action queues?"
; Websocket Connections Tab
AdvSceneSwitcher.websocketConnectionTab.title="Websocket Connections"
AdvSceneSwitcher.websocketConnectionTab.help="Websocket connections can be used to communicate with other OBS instances or programs.\n\nClick on the highlighted plus symbol to add a new connection."
AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip="Add new webscoket connection"
AdvSceneSwitcher.websocketConnectionTab.websocketConnectionRemoveButton.tooltip="Remove selected websocket connections"
AdvSceneSwitcher.websocketConnectionTab.name.header="Name"
AdvSceneSwitcher.websocketConnectionTab.address.header="Address"
AdvSceneSwitcher.websocketConnectionTab.port.header="Port"
AdvSceneSwitcher.websocketConnectionTab.protocol.header="Is using OBS protocol?"
AdvSceneSwitcher.websocketConnectionTab.protocol.yes="Yes"
AdvSceneSwitcher.websocketConnectionTab.protocol.no="No"
AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 connections?"
; Twitch Connections Tab
AdvSceneSwitcher.twitchConnectionTab.title="Twitch Connections"
AdvSceneSwitcher.twitchConnectionTab.help="Twitch connections can be used to use Twitch events as triggers to execute actions or perform actions on the linked Twitch account.\n\nClick on the highlighted plus symbol to add a new connection."
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip.tooltip="Add new Twitch connection"
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionRemoveButton.tooltip="Remove selected Twitch connections"
AdvSceneSwitcher.twitchConnectionTab.name.header="Name"
AdvSceneSwitcher.twitchConnectionTab.isValid.header="Is valid?"
AdvSceneSwitcher.twitchConnectionTab.permissionCount.header="Number of permissions"
AdvSceneSwitcher.twitchConnectionTab.yes="Yes"
AdvSceneSwitcher.twitchConnectionTab.no="No"
AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 Twitch connections?"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro"
@@ -90,19 +142,21 @@ AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
AdvSceneSwitcher.macroTab.edit.action="Action type:"
AdvSceneSwitcher.macroTab.add="Add new macro"
AdvSceneSwitcher.macroTab.addGroup="Add new macro group"
AdvSceneSwitcher.macroTab.name="Name:"
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.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.onChange="Perform actions only on condition change"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Group %1"
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Are you sure you want to delete \"%1\" and all its elements?"
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Are you sure you want to delete %1 macros?"
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Are you sure you want to delete \"%1\"?"
AdvSceneSwitcher.macroTab.macroNameExists="The name \"%1\" is already used by a macro."
AdvSceneSwitcher.macroTab.groupNameExists="The name \"%1\" is already used by a macro group."
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="Are you sure you want to remove %1 macros/groups?"
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Are you sure you want to remove \"%1\" group and all its elements?"
AdvSceneSwitcher.macroTab.contextMenuAdd="Add"
AdvSceneSwitcher.macroTab.copy="Duplicate Macro"
AdvSceneSwitcher.macroTab.group="Group Selected Macros"
@@ -111,17 +165,27 @@ AdvSceneSwitcher.macroTab.rename="Rename"
AdvSceneSwitcher.macroTab.remove="Remove"
AdvSceneSwitcher.macroTab.export="Export"
AdvSceneSwitcher.macroTab.export.info="Paste the string below into the import dialog to import the selected macros:"
AdvSceneSwitcher.macroTab.export.usePlainText="Use plain text"
AdvSceneSwitcher.macroTab.import="Import"
AdvSceneSwitcher.macroTab.import.info="Paste the export string into the below text box to import macros:"
AdvSceneSwitcher.macroTab.import.invalid="Invalid import data provided!"
AdvSceneSwitcher.macroTab.import.nameConflict="Cannot continue importing macro \"%1\" as a macro with the same name already exists!\nDo you want to continue with the import of \"%2\" and choose a new name? (Will be skipped otherwise)"
AdvSceneSwitcher.macroTab.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.collapseAll="Collapse all"
AdvSceneSwitcher.macroTab.maximize="Maximize"
AdvSceneSwitcher.macroTab.minimize="Minimize"
AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label"
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label"
AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..."
AdvSceneSwitcher.macroTab.segment.copy="Copy"
AdvSceneSwitcher.macroTab.segment.paste="Paste"
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
AdvSceneSwitcher.macroTab.generalSettings="General settings"
AdvSceneSwitcher.macroTab.inputSettings="Input settings"
AdvSceneSwitcher.macroTab.inputSettings.description="This section will allow you to specify variables, which will be treated as input parameters for the selected macro.\nWhen executing the macro using the \"Macro\" action type, you will have the option to set the values for those variables."
AdvSceneSwitcher.macroTab.inputSettings.invalid="<Invalid selection>"
AdvSceneSwitcher.macroTab.inputSettings.noInputs="No inputs are defined for the selected macro!\nOpen the settings of the macro to define inputs.\nThe settings button is highlighted above."
AdvSceneSwitcher.macroTab.dockSettings="Dock settings"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
@@ -129,6 +193,7 @@ AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performe
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.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.currentDockAddRunButton="Add button to run the macro"
AdvSceneSwitcher.macroTab.currentDockAddPauseButton="Add button to pause or unpause the macro"
@@ -165,7 +230,7 @@ AdvSceneSwitcher.macroTab.tooltip.elseActionTopButton="Move selected action to t
AdvSceneSwitcher.macroTab.tooltip.elseActionUpButton="Move selected action up in the else section"
AdvSceneSwitcher.macroTab.tooltip.elseActionDownButton="Move selected action down in the else section"
AdvSceneSwitcher.macroTab.tooltip.elseActionBottomButton="Move selected action to the bottom in the else section"
AdvSceneSwitcher.macroTab.tooltip.openMacroPropertiesButton="Open macro properties"
AdvSceneSwitcher.macroTab.tooltip.openMacroSettingsButton="Open macro settings"
AdvSceneSwitcher.macroDock.pause="Pause"
AdvSceneSwitcher.macroDock.unpause="Unpause"
@@ -392,11 +457,16 @@ AdvSceneSwitcher.condition.source.type.settingsMatch="Settings match"
AdvSceneSwitcher.condition.source.type.settingsChanged="Settings changed"
AdvSceneSwitcher.condition.source.type.individualSettingMatches="Setting value matches"
AdvSceneSwitcher.condition.source.type.individualSettingChanged="Setting value changed"
AdvSceneSwitcher.condition.source.type.width="Width"
AdvSceneSwitcher.condition.source.type.height="Height"
AdvSceneSwitcher.condition.source.sizeCompare.less="Smaller than"
AdvSceneSwitcher.condition.source.sizeCompare.equal="Equals"
AdvSceneSwitcher.condition.source.sizeCompare.more="Bigger than"
AdvSceneSwitcher.condition.source.refresh="Refresh"
AdvSceneSwitcher.condition.source.refreshTooltip="Repopulate the source settings selection with the settings of the source which's name matches the variable value."
AdvSceneSwitcher.condition.source.sceneVisibilityHint="Scene specific visibility can be checked using the \"Scene item visibility\" condition"
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}}{{conditions}}{{settingSelection}}{{refresh}}"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}}{{conditions}}{{settingSelection}}{{refresh}}{{sizeCompareMethods}}{{size}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.source.entry.line3="{{regex}}{{getSettings}}"
AdvSceneSwitcher.condition.virtualCamera="Virtual camera"
@@ -423,10 +493,12 @@ AdvSceneSwitcher.condition.sceneOrder.type.position="Is at position"
AdvSceneSwitcher.condition.sceneOrder.positionInfo="The position value starts at the bottom with 0 and increases by one for each scene item including the ones in scene groups"
AdvSceneSwitcher.condition.sceneOrder.entry="On{{scenes}}{{sources}}{{conditions}}{{sources2}}{{position}}"
AdvSceneSwitcher.condition.hotkey="Hotkey"
AdvSceneSwitcher.condition.hotkey.pressed="pressed"
AdvSceneSwitcher.condition.hotkey.released="released"
AdvSceneSwitcher.condition.hotkey.name="Macro trigger hotkey"
AdvSceneSwitcher.condition.hotkey.tip="Note: You can configure the keybindings for this hotkey in the OBS settings window"
AdvSceneSwitcher.condition.hotkey.entry.line1="Hotkey is pressed"
AdvSceneSwitcher.condition.hotkey.entry.line2="Name:{{name}}"
AdvSceneSwitcher.condition.hotkey.entry.keyState="Hotkey is{{keyState}}"
AdvSceneSwitcher.condition.hotkey.entry.name="Name:{{name}}"
AdvSceneSwitcher.condition.replay="Replay buffer"
AdvSceneSwitcher.condition.replay.state.stopped="Replay buffer stopped"
AdvSceneSwitcher.condition.replay.state.started="Replay buffer started"
@@ -531,7 +603,7 @@ AdvSceneSwitcher.condition.run="Run"
AdvSceneSwitcher.condition.run.entry="Process exits before timeout of{{timeout}}seconds"
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Check for exit code{{exitCode}}"
AdvSceneSwitcher.condition.midi="MIDI"
AdvSceneSwitcher.condition.midi.entry="Mesasge was received from{{device}}which matches:"
AdvSceneSwitcher.condition.midi.entry="Message was received from{{device}}which matches:"
AdvSceneSwitcher.condition.midi.entry.listen="Set MIDI message selection to messages incoming on selected device:{{listenButton}}"
AdvSceneSwitcher.condition.display="Display"
AdvSceneSwitcher.condition.display.type.displayName="Name of connected displays matches"
@@ -611,6 +683,26 @@ AdvSceneSwitcher.condition.queue.type.stopped="Stopped"
AdvSceneSwitcher.condition.queue.type.size="Size"
AdvSceneSwitcher.condition.queue.entry.startStop="Queue{{queues}}is{{conditions}}"
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}}"
AdvSceneSwitcher.condition.folder="Folder watch"
AdvSceneSwitcher.condition.folder.tooltip="This condition type will allow you to monitor the contents of a folder.\nNote that the monitoring will *not* recursively scan for changes in sub directories within directories of the selected folder!\nNote that if there are several changes during a short period of time, some of the changes might not emit this signal.\nHowever, the last change in the sequence of changes always will."
AdvSceneSwitcher.condition.folder.condition.any="Any change happened"
AdvSceneSwitcher.condition.folder.condition.fileAdd="A file was added"
AdvSceneSwitcher.condition.folder.condition.fileChange="A file was modified"
AdvSceneSwitcher.condition.folder.condition.fileRemove="A file was removed"
AdvSceneSwitcher.condition.folder.condition.folderAdd="A directory was added"
AdvSceneSwitcher.condition.folder.condition.folderRemove="A directory was removed"
AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}"
AdvSceneSwitcher.condition.folder.enableFilter="Only evaluate to true, if the changed path matches a patern"
AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
; Macro Actions
AdvSceneSwitcher.action.scene="Switch scene"
@@ -632,6 +724,8 @@ AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
AdvSceneSwitcher.action.audio.type.syncOffset="Set sync offset"
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.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"
@@ -640,7 +734,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.wait="Wait for fade to complete."
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.type.stop="Stop recording"
AdvSceneSwitcher.action.recording.type.start="Start recording"
@@ -668,7 +762,8 @@ AdvSceneSwitcher.action.streaming.type.username="Set username"
AdvSceneSwitcher.action.streaming.type.password="Set password"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
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.type.show="Show"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
@@ -681,9 +776,10 @@ AdvSceneSwitcher.action.filter.type.enable="Enable"
AdvSceneSwitcher.action.filter.type.disable="Disable"
AdvSceneSwitcher.action.filter.type.toggle="Toggle"
AdvSceneSwitcher.action.filter.type.settings="Set settings"
AdvSceneSwitcher.action.filter.type.pressSettingsButton="Press settings button"
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.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.getSettings="Get current settings"
AdvSceneSwitcher.action.filter.inputMethod.individualManual="Set to fixed value"
@@ -701,7 +797,6 @@ AdvSceneSwitcher.action.source.type.deinterlaceOrder="Set deinterlace field orde
AdvSceneSwitcher.action.source.type.openInteractionDialog="Open interaction dialog"
AdvSceneSwitcher.action.source.type.openFilterDialog="Open filter 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.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\""
@@ -739,12 +834,24 @@ AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pause"
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
AdvSceneSwitcher.action.macro.type.run="Run actions"
AdvSceneSwitcher.action.macro.type.run="Run macro"
AdvSceneSwitcher.action.macro.type.run.conditions.ignore="Do not consider condition state"
AdvSceneSwitcher.action.macro.type.run.conditions.true="Only if conditions evaluate to true"
AdvSceneSwitcher.action.macro.type.run.conditions.false="Only if conditions evaluate to false"
AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState="Reevaluate the condition state before executing this action"
AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState.help="The plugin operates in phases:\n * The phase evaluating the macro conditions\n * The phase running macro actions\nMacros executed before this particular action might have side effects on the condition state of macros.\nCheck this option if you want those side effects to be taken into account when evaluating the condition state."
AdvSceneSwitcher.action.macro.type.run.actionType.regular="actions"
AdvSceneSwitcher.action.macro.type.run.actionType.else="else-actions"
AdvSceneSwitcher.action.macro.type.run.skipWhenPaused="Skip execution when macro is paused"
AdvSceneSwitcher.action.macro.type.run.setInputs="Set inputs"
AdvSceneSwitcher.action.macro.type.run.setInputs.description="Input variables can be defined in the macro settings.\nDefining input variables will allow you to modify the values of variables before an action is executed."
AdvSceneSwitcher.action.macro.type.stop="Stop actions"
AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.macro.entry.run="{{actions}}{{actionTypes}}of{{macros}}"
AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}of{{conditionMacros}}"
AdvSceneSwitcher.action.macro.entry.other="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Plugin state"
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
AdvSceneSwitcher.action.pluginState.type.noMatch="Change the no-match behaviour:"
@@ -829,6 +936,7 @@ AdvSceneSwitcher.action.systray.title="Title:"
AdvSceneSwitcher.action.systray.message="Message:"
AdvSceneSwitcher.action.systray.icon="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.save.default="Default"
AdvSceneSwitcher.action.screenshot.save.custom="Custom"
@@ -888,7 +996,13 @@ AdvSceneSwitcher.action.variable.type.sceneItemCount="Set to scene item count of
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
AdvSceneSwitcher.action.variable.type.setToTempvar.help="This action type will allow you to extract values out of segments of the current macro and assign those values to the selected variable.\nFor example, you can get the current scene name from a scene condition and assign this name to a variable."
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
AdvSceneSwitcher.action.variable.type.padValue="Pad current value"
AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values"
AdvSceneSwitcher.action.variable.truncateOrPadDirection.left="Left"
AdvSceneSwitcher.action.variable.truncateOrPadDirection.right="Right"
AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":"
AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:"
AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4"
@@ -900,7 +1014,9 @@ AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
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.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}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
AdvSceneSwitcher.action.variable.entry.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.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
@@ -962,6 +1078,17 @@ AdvSceneSwitcher.action.queue.type.start="Start"
AdvSceneSwitcher.action.queue.type.stop="Stop"
AdvSceneSwitcher.action.queue.entry.add="{{actions}}actions of macro{{macros}}to 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.setFocusWindow.limitation="The focus action can only be performed under certain circumstances"
AdvSceneSwitcher.action.window.type.setFocusWindow.limitation.details="At least one of the following conditions must be true:\n OBS is the foreground process.\n There is currently no foreground window, and thus no foreground process.\n OBS received the last input event."
AdvSceneSwitcher.action.window.type.maximizeWindow="Maximize window"
AdvSceneSwitcher.action.window.type.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
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
@@ -1004,6 +1131,7 @@ AdvSceneSwitcher.variable.save="Save / load behavior"
AdvSceneSwitcher.variable.save.dontSave="Don't save variable value"
AdvSceneSwitcher.variable.save.save="Save variable value"
AdvSceneSwitcher.variable.save.default="Set to value"
AdvSceneSwitcher.variable.selectionDialog="Select variable:"
AdvSceneSwitcher.tooltip.availableVariables="Variables are supported, use ${VarName} to retrieve the value of VarName"
@@ -1034,6 +1162,7 @@ AdvSceneSwitcher.actionQueues.configure="Configure action queue settings"
AdvSceneSwitcher.actionQueues.invalid="Invalid action queue selection"
AdvSceneSwitcher.actionQueues.name="Name:"
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.stopped="Queue is stopped"
AdvSceneSwitcher.actionQueues.start="Start action queue"
@@ -1586,6 +1715,15 @@ AdvSceneSwitcher.tempVar.macro.matchedCount.description="The number of macros of
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.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 +1755,26 @@ AdvSceneSwitcher.tempVar.streaming.keyframeInterval.description="Stream keyframe
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.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.tempVar.file.content="File content"
AdvSceneSwitcher.tempVar.file.date="File modification date"
AdvSceneSwitcher.tempVar.folder.newFiles="New files"
AdvSceneSwitcher.tempVar.folder.changedFiles="Changed files"
AdvSceneSwitcher.tempVar.folder.removedFiles="Removed files"
AdvSceneSwitcher.tempVar.folder.newDirs="New directories"
AdvSceneSwitcher.tempVar.folder.removedDirs="Removed directories"
AdvSceneSwitcher.tempVar.source.width="Source width"
AdvSceneSwitcher.tempVar.source.height="Source height"
AdvSceneSwitcher.tempVar.source.setting="Setting value"
AdvSceneSwitcher.tempVar.source.settings="Settings string"
AdvSceneSwitcher.tempVar.filter.setting="Setting value"
AdvSceneSwitcher.tempVar.filter.settings="Settings string"
AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene"
@@ -1642,10 +1800,27 @@ AdvSceneSwitcher.enterText="--enter text--"
AdvSceneSwitcher.enterURL="--enter URL--"
AdvSceneSwitcher.selectHotkey="--select hotkey--"
AdvSceneSwitcher.selectDisplay="--select display--"
AdvSceneSwitcher.selectSetting="--select setting--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
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.type.sourceName="Source name"
AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}"
@@ -1689,6 +1864,8 @@ AdvSceneSwitcher.audio.monitor.none="Monitor Off"
AdvSceneSwitcher.audio.monitor.monitorOnly="Monitor Only (mute 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 :)
; Transition Tab
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.name="Nombre:"
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.onChange="Realizar acciones solo en el cambio de condición"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.exists="El nombre de la macro ya existe"
AdvSceneSwitcher.macroTab.copy="Crear copia"
AdvSceneSwitcher.macroTab.expandAll="Expandir todo"
AdvSceneSwitcher.macroTab.collapseAll="Contraer todo"
@@ -240,8 +238,7 @@ AdvSceneSwitcher.condition.sceneOrder.entry="En{{scenes}}{{sources}}{{conditions
AdvSceneSwitcher.condition.hotkey="Tecla de acceso rápido"
AdvSceneSwitcher.condition.hotkey.name="Tecla de acceso directo de activación de macro"
AdvSceneSwitcher.condition.hotkey.tip="Nota: puede configurar las combinaciones de teclas para esta tecla de acceso rápido en la ventana de configuración de OBS"
AdvSceneSwitcher.condition.hotkey.entry.line1="Se presiona la tecla de acceso rápido"
AdvSceneSwitcher.condition.hotkey.entry.line2="Nombre: {{name}}"
AdvSceneSwitcher.condition.hotkey.entry.name="Nombre:{{name}}"
AdvSceneSwitcher.condition.replay="Búfer de reproducción"
AdvSceneSwitcher.condition.replay.state.stopped="Búfer de reproducción detenido"
AdvSceneSwitcher.condition.replay.state.started="Búfer de reproducción iniciado"
@@ -367,7 +364,7 @@ AdvSceneSwitcher.action.filter="Filtro"
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
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.source="Fuente"
AdvSceneSwitcher.action.source.type.enable="Habilitar"
@@ -389,7 +386,6 @@ AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
AdvSceneSwitcher.action.macro.type.run="Ejecutar"
AdvSceneSwitcher.action.macro.type.stop="Detener"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Estado del complemento"
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:"
@@ -456,7 +452,7 @@ AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
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.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.none="ninguno"
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.name="Nom :"
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.onChange="Exécuter des actions uniquement en cas de changement de condition"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1"
AdvSceneSwitcher.macroTab.exists="Le nom de la macro existe déjà"
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Êtes-vous sûr de vouloir supprimer \"%1\" et tous ses éléments ?"
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Êtes-vous sûr de vouloir supprimer %1 macros ?"
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="Ê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.contextMenuAdd="Ajouter"
AdvSceneSwitcher.macroTab.copy="Dupliquer la macro"
AdvSceneSwitcher.macroTab.group="Grouper les macros sélectionnées"
@@ -353,8 +351,7 @@ AdvSceneSwitcher.condition.sceneOrder.entry="Sur{{scenes}}{{sources}}{{condition
AdvSceneSwitcher.condition.hotkey="Raccourci clavier"
AdvSceneSwitcher.condition.hotkey.name="Raccourci clavier de déclenchement de macro"
AdvSceneSwitcher.condition.hotkey.tip="Remarque : Vous pouvez configurer les raccourcis clavier pour ce raccourci dans la fenêtre de paramètres d'OBS"
AdvSceneSwitcher.condition.hotkey.entry.line1="Raccourci clavier est enfoncé"
AdvSceneSwitcher.condition.hotkey.entry.line2="Nom :{{name}}"
AdvSceneSwitcher.condition.hotkey.entry.name="Nom:{{name}}"
AdvSceneSwitcher.condition.replay="Tampon de répétition"
AdvSceneSwitcher.condition.replay.state.stopped="Tampon de répétition arrêté"
AdvSceneSwitcher.condition.replay.state.started="Tampon de répétition démarré"
@@ -523,7 +520,7 @@ AdvSceneSwitcher.action.filter.type.enable="Activer"
AdvSceneSwitcher.action.filter.type.disable="Désactiver"
AdvSceneSwitcher.action.filter.type.toggle="Basculer"
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.source="Source"
AdvSceneSwitcher.action.source.type.enable="Activer"
@@ -535,7 +532,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.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.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.getSettings="Obtenir les paramètres actuels"
AdvSceneSwitcher.action.source.deinterlaceMode.disable="Désactiver"
@@ -661,7 +657,7 @@ AdvSceneSwitcher.action.sceneCollection="Collection de scènes"
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.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.none="aucun"
AdvSceneSwitcher.action.sequence.restart="Recommencer depuis le début une fois la fin de la liste atteinte"
@@ -740,6 +736,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.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !"
AdvSceneSwitcher.noSettingsButtons="Aucun bouton trouvé !"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition"
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.name="Имя:"
AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
AdvSceneSwitcher.macroTab.exists="Имя макроса уже существует"
AdvSceneSwitcher.macroTab.copy="Создать копию"
; Macro Logic
AdvSceneSwitcher.logic.none="Игнорировать вход"

View File

@@ -72,11 +72,9 @@ AdvSceneSwitcher.macroTab.edit.action="Eylem türü:"
AdvSceneSwitcher.macroTab.add="Yeni Makro ekle"
AdvSceneSwitcher.macroTab.name="İsim:"
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.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin"
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.exists="Macro adı zaten mevcut"
AdvSceneSwitcher.macroTab.copy="Kopya oluştur"
AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült"
@@ -297,7 +295,7 @@ AdvSceneSwitcher.action.filter="Filtrele"
AdvSceneSwitcher.action.filter.type.enable="Etkin"
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
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.source="Kaynak"
AdvSceneSwitcher.action.source.type.enable="Etkin"
@@ -317,7 +315,6 @@ AdvSceneSwitcher.action.macro.type.pause="Duraklat"
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"

View File

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

2
deps/opencv vendored

View File

@@ -163,12 +163,31 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_52">
<item>
<widget class="QCheckBox" name="verboseLogging">
<widget class="QLabel" name="label_21">
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging</string>
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel</string>
</property>
</widget>
</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>
<spacer name="horizontalSpacer_49">
<property name="orientation">
@@ -883,6 +902,9 @@
</item>
<item>
<widget class="QCheckBox" name="runMacroInParallel">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.runInParallel</string>
</property>
<property name="text">
<string/>
</property>
@@ -903,6 +925,9 @@
</item>
<item>
<widget class="QCheckBox" name="runMacroOnChange">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.onChange</string>
</property>
<property name="text">
<string/>
</property>
@@ -935,7 +960,7 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="macroProperties">
<widget class="QPushButton" name="macroSettings">
<property name="maximumSize">
<size>
<width>22</width>
@@ -943,7 +968,7 @@
</size>
</property>
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.openMacroPropertiesButton</string>
<string>AdvSceneSwitcher.macroTab.tooltip.openMacroSettingsButton</string>
</property>
<property name="text">
<string/>
@@ -1577,104 +1602,6 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="variableTab">
<attribute name="title">
<string>AdvSceneSwitcher.variableTab.title</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_33">
<item>
<layout class="QGridLayout" name="gridLayout_25">
<item row="0" column="0">
<widget class="QTableWidget" name="variables">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="variablesHelp">
<property name="text">
<string>AdvSceneSwitcher.variableTab.help</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_30">
<item>
<widget class="QPushButton" name="variableAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>AdvSceneSwitcher.variableTab.tooltip.variableAddButton</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="variableRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>AdvSceneSwitcher.variableTab.tooltip.variableRemoveButton</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_211">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="networkTab">
<attribute name="title">
<string>AdvSceneSwitcher.networkTab.title</string>
@@ -5078,7 +5005,7 @@
<tabstop>checkInterval</tabstop>
<tabstop>startupBehavior</tabstop>
<tabstop>autoStartEvent</tabstop>
<tabstop>verboseLogging</tabstop>
<tabstop>logLevel</tabstop>
<tabstop>saveWindowGeo</tabstop>
<tabstop>showTrayNotifications</tabstop>
<tabstop>uiHintsDisable</tabstop>
@@ -5106,7 +5033,7 @@
<tabstop>runMacro</tabstop>
<tabstop>runMacroInParallel</tabstop>
<tabstop>runMacroOnChange</tabstop>
<tabstop>macroProperties</tabstop>
<tabstop>macroSettings</tabstop>
<tabstop>conditionsList</tabstop>
<tabstop>conditionAdd</tabstop>
<tabstop>conditionRemove</tabstop>

View File

@@ -1,4 +1,5 @@
#include "advanced-scene-switcher.hpp"
#include "backup.hpp"
#include "curl-helper.hpp"
#include "log-helper.hpp"
#include "macro-helpers.hpp"
@@ -10,6 +11,7 @@
#include "status-control.hpp"
#include "switcher-data.hpp"
#include "ui-helpers.hpp"
#include "tab-helpers.hpp"
#include "utility.hpp"
#include "version.h"
@@ -17,7 +19,6 @@
#include <obs-frontend-api.h>
#include <QAction>
#include <QDirIterator>
#include <QFileDialog>
#include <QMainWindow>
#include <QTextStream>
#include <regex>
@@ -44,7 +45,7 @@ AdvSceneSwitcher::~AdvSceneSwitcher()
{
if (switcher) {
switcher->settingsWindowOpened = false;
switcher->lastOpenedTab = ui->tabWidget->currentIndex();
SaveLastOpenedTab(ui->tabWidget);
}
}
@@ -112,11 +113,11 @@ void AdvSceneSwitcher::LoadUI()
SetupSceneGroupTab();
SetupTriggerTab();
SetupMacroTab();
SetupVariableTab();
SetupOtherTabs(ui->tabWidget);
SetDeprecationWarnings();
SetTabOrder();
SetCurrentTab();
SetTabOrder(ui->tabWidget);
SetCurrentTab(ui->tabWidget);
RestoreWindowGeo();
CheckFirstTimeSetup();
@@ -135,6 +136,17 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
}
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();
}
}
}
return QDialog::eventFilter(obj, event);
@@ -143,7 +155,6 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
/******************************************************************************
* Saving and loading
******************************************************************************/
static void AskForBackup(const QString &json);
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
{
@@ -192,34 +203,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
******************************************************************************/
@@ -257,7 +240,7 @@ void SwitcherData::Thread()
if (duration.count() < 1) {
blog(LOG_INFO,
"detected busy loop - refusing to sleep less than 1ms");
duration = std::chrono::milliseconds(50);
duration = std::chrono::milliseconds(10);
}
}
@@ -539,9 +522,7 @@ bool SwitcherData::AnySceneTransitionStarted()
extern "C" EXPORT void FreeSceneSwitcher()
{
PlatformCleanup();
for (const auto &cleanupStep : switcher->pluginCleanupSteps) {
cleanupStep();
}
RunPluginCleanupSteps();
delete switcher;
switcher = nullptr;
@@ -635,6 +616,8 @@ static void handleShutdown()
// TODO: Look for a way to possibly resolve this
obs_frontend_save();
}
BackupSettingsOfCurrentVersion();
}
static void handleSceneCollectionChanging()
@@ -767,15 +750,38 @@ QWidget *GetSettingsWindow()
return SettingsWindowIsOpened() ? AdvSceneSwitcher::window : nullptr;
}
void SetupActionQueues();
extern "C" EXPORT void RunPluginPostLoadSteps()
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
{
for (const auto &postLoadStep : switcher->pluginPostLoadSteps) {
postLoadStep();
static QObject *highlight = nullptr;
if ((highlight && enable) || (!highlight && !enable)) {
return;
}
if (highlight && !enable) {
highlight->deleteLater();
highlight = nullptr;
return;
}
if (!HighlightUIElementsEnabled()) {
return;
}
highlight = HighlightWidget(ui->macroSettings, Qt::green);
}
void HighligthMacroSettingsButton(bool enable)
{
auto window = GetSettingsWindow();
if (!window) {
return;
}
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton(
enable);
}
void SetupActionQueues();
extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,
translateFunc translate)
{
@@ -789,9 +795,7 @@ extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,
SetupDock();
SetupActionQueues();
for (const auto &initStep : switcher->pluginInitSteps) {
initStep();
}
RunPluginInitSteps();
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
obs_frontend_add_event_callback(OBSEvent, switcher);

View File

@@ -12,6 +12,7 @@ class MacroActionEdit;
class MacroConditionEdit;
class Duration;
class SequenceWidget;
enum class LogicType;
struct SceneGroup;
/*******************************************************************************
@@ -34,8 +35,6 @@ public:
void LoadUI();
void SetTabOrder();
void SetCurrentTab();
void RestoreWindowGeo();
void CheckFirstTimeSetup();
@@ -55,14 +54,13 @@ public slots:
void NoMatchDelayDurationChanged(const Duration &);
void CooldownDurationChanged(const Duration &);
void on_startupBehavior_currentIndexChanged(int index);
void on_logLevel_currentIndexChanged(int index);
void on_autoStartEvent_currentIndexChanged(int index);
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
void on_checkInterval_valueChanged(int value);
void on_tabMoved(int from, int to);
void on_tabWidget_currentChanged(int index);
void on_exportSettings_clicked();
void on_importSettings_clicked();
void on_verboseLogging_stateChanged(int state);
void on_saveWindowGeo_stateChanged(int state);
void on_showTrayNotifications_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state);
@@ -81,6 +79,7 @@ public:
bool AddNewMacro(std::shared_ptr<Macro> &res, std::string &name,
std::string format = "");
void RemoveMacro(std::shared_ptr<Macro> &);
void RemoveSelectedMacros();
void RenameMacro(std::shared_ptr<Macro> &, const QString &name);
std::shared_ptr<Macro> GetSelectedMacro();
std::vector<std::shared_ptr<Macro>> GetSelectedMacros();
@@ -97,6 +96,7 @@ public:
void SetConditionData(Macro &m);
void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2);
void HighligthMacroSettingsButton(bool enable = true);
public slots:
void on_macroAdd_clicked();
@@ -135,7 +135,7 @@ public slots:
void ShowMacroElseActionsContextMenu(const QPoint &);
void ShowMacroConditionsContextMenu(const QPoint &);
void CopyMacro();
void RenameCurrentMacro();
void RenameSelectedMacro();
void ExportMacros();
void ImportMacros();
void ExpandAllActions();
@@ -154,12 +154,16 @@ public slots:
void SetElseActionsStateToVisible();
void MacroActionSelectionChanged(int idx);
void MacroActionReorder(int to, int target);
void AddMacroAction(Macro *macro, int idx, const std::string &id,
obs_data_t *data);
void AddMacroAction(int idx);
void RemoveMacroAction(int idx);
void MoveMacroActionUp(int idx);
void MoveMacroActionDown(int idx);
void MacroElseActionSelectionChanged(int idx);
void MacroElseActionReorder(int to, int target);
void AddMacroElseAction(Macro *macro, int idx, const std::string &id,
obs_data_t *data);
void AddMacroElseAction(int idx);
void RemoveMacroElseAction(int idx);
void SwapElseActions(Macro *m, int pos1, int pos2);
@@ -168,16 +172,16 @@ public slots:
void MacroConditionSelectionChanged(int idx);
void MacroConditionReorder(int to, int target);
void AddMacroCondition(int idx);
void AddMacroCondition(Macro *macro, int idx, const std::string &id,
obs_data_t *data, LogicType logic);
void RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx);
void FadeOutActionControls();
void FadeOutConditionControls();
void ResetOpacityActionControls();
void ResetOpacityConditionControls();
void HighlightControls();
void HighlightOnChange();
void on_macroProperties_clicked();
void on_macroSettings_clicked();
void CopyMacroSegment();
void PasteMacroSegment();
signals:
void MacroAdded(const QString &name);
@@ -186,9 +190,6 @@ signals:
void MacroSegmentOrderChanged();
void SegmentTempVarsChanged();
void HighlightMacrosChanged(bool value);
void HighlightActionsChanged(bool value);
void HighlightElseActionsChanged(bool value);
void HighlightConditionsChanged(bool value);
void ConnectionAdded(const QString &);
void ConnectionRenamed(const QString &oldName, const QString &newName);
@@ -208,16 +209,6 @@ private:
/* --- End of macro tab section --- */
/* --- Begin of variable tab section --- */
public:
void SetupVariableTab();
public slots:
void on_variableAdd_clicked();
void on_variableRemove_clicked();
/* --- End of variable tab section --- */
/* --- Begin of legacy tab section --- */
public:
void ClearFrames(QListWidget *list);
@@ -420,5 +411,6 @@ public slots:
void OpenSettingsWindow();
QWidget *GetSettingsWindow();
void HighligthMacroSettingsButton(bool enable);
} // namespace advss

View File

@@ -9,6 +9,7 @@
#include "splitter-helpers.hpp"
#include "status-control.hpp"
#include "switcher-data.hpp"
#include "tab-helpers.hpp"
#include "ui-helpers.hpp"
#include "utility.hpp"
#include "variable.hpp"
@@ -19,17 +20,6 @@
namespace advss {
static constexpr std::array<const char *, 19> tabNames = {
"generalTab", "macroTab", "variableTab",
"windowTitleTab", "executableTab", "screenRegionTab",
"mediaTab", "fileTab", "randomTab",
"timeTab", "idleTab", "sceneSequenceTab",
"audioTab", "videoTab", "networkTab",
"sceneGroupTab", "transitionsTab", "pauseTab",
"sceneTriggerTab"};
static std::vector<int> tabOrder = std::vector<int>(tabNames.size());
void AdvSceneSwitcher::reject()
{
close();
@@ -112,6 +102,14 @@ void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int 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)
{
if (loading) {
@@ -157,15 +155,6 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *)
obs_frontend_save();
}
void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state)
{
if (loading) {
return;
}
switcher->verbose = state;
}
void AdvSceneSwitcher::on_saveWindowGeo_stateChanged(int state)
{
if (loading) {
@@ -245,14 +234,7 @@ void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
ui->tabWidget->setTabEnabled(idx, !state);
ui->tabWidget->setStyleSheet(
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
#else
ui->tabWidget->setTabVisible(idx, !state);
#endif
}
}
}
@@ -364,67 +346,6 @@ void AdvSceneSwitcher::on_importSettings_clicked()
}
}
static int findTabIndex(QTabWidget *tabWidget, int pos)
{
int at = -1;
QString tabName = tabNames.at(pos);
QWidget *page = tabWidget->findChild<QWidget *>(tabName);
if (page) {
at = tabWidget->indexOf(page);
}
if (at == -1) {
blog(LOG_INFO, "failed to find tab %s",
tabName.toUtf8().constData());
}
return at;
}
static bool tabWidgetOrderValid()
{
auto tmp = std::vector<int>(tabNames.size());
std::iota(tmp.begin(), tmp.end(), 0);
for (auto &p : tmp) {
auto it = std::find(tabOrder.begin(), tabOrder.end(), p);
if (it == tabOrder.end()) {
return false;
}
}
return true;
}
static void resetTabWidgetOrder()
{
tabOrder = std::vector<int>(tabNames.size());
std::iota(tabOrder.begin(), tabOrder.end(), 0);
}
void AdvSceneSwitcher::SetTabOrder()
{
if (!tabWidgetOrderValid()) {
resetTabWidgetOrder();
}
QTabBar *bar = ui->tabWidget->tabBar();
for (int i = 0; i < bar->count(); ++i) {
int curPos = findTabIndex(ui->tabWidget, tabOrder[i]);
if (i != curPos && curPos != -1) {
bar->moveTab(curPos, i);
}
}
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
}
void AdvSceneSwitcher::SetCurrentTab()
{
if (switcher->lastOpenedTab >= 0) {
ui->tabWidget->setCurrentIndex(switcher->lastOpenedTab);
}
}
static bool windowPosValid(QPoint pos)
{
return !!QGuiApplication::screenAt(pos);
@@ -447,17 +368,12 @@ void AdvSceneSwitcher::CheckFirstTimeSetup()
}
}
void AdvSceneSwitcher::on_tabMoved(int from, int to)
void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
{
if (loading) {
return;
}
std::swap(tabOrder[from], tabOrder[to]);
}
void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
{
switcher->showFrame = false;
ClearFrames(ui->screenRegionSwitches);
SetShowFrames();
@@ -515,7 +431,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
}
LoadMacros(obj);
LoadGlobalMacroProperties(obj);
LoadGlobalMacroSettings(obj);
loadWindowTitleSwitches(obj);
loadScreenRegionSwitches(obj);
loadPauseSwitches(obj);
@@ -538,7 +454,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
RunPostLoadSteps();
// Reset on startup and scene collection change
switcher->lastOpenedTab = -1;
ResetLastOpenedTab();
startupLoadDone = true;
}
@@ -550,7 +466,7 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
saveSceneGroups(obj);
SaveMacros(obj);
SaveGlobalMacroProperties(obj);
SaveGlobalMacroSettings(obj);
SaveVariables(obj);
saveWindowTitleSwitches(obj);
saveScreenRegionSwitches(obj);
@@ -598,7 +514,7 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
obs_data_set_int(obj, "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",
showSystemTrayNotifications);
obs_data_set_bool(obj, "disableHints", disableHints);
@@ -649,7 +565,7 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *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 =
obs_data_get_bool(obj, "showSystemTrayNotifications");
disableHints = obs_data_get_bool(obj, "disableHints");
@@ -686,13 +602,7 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
void SwitcherData::SaveUISettings(obs_data_t *obj)
{
OBSDataArrayAutoRelease tabWidgetOrder = obs_data_array_create();
for (size_t i = 0; i < tabNames.size(); i++) {
OBSDataAutoRelease entry = obs_data_create();
obs_data_set_int(entry, tabNames[i], tabOrder[i]);
obs_data_array_push_back(tabWidgetOrder, entry);
}
obs_data_set_array(obj, "tabWidgetOrder", tabWidgetOrder);
SaveTabOrder(obj);
obs_data_set_bool(obj, "saveWindowGeo", saveWindowGeo);
obs_data_set_int(obj, "windowPosX", windowPos.x());
@@ -706,28 +616,7 @@ void SwitcherData::SaveUISettings(obs_data_t *obj)
void SwitcherData::LoadUISettings(obs_data_t *obj)
{
OBSDataArrayAutoRelease defaultTabWidgetOrder = obs_data_array_create();
for (size_t i = 0; i < tabNames.size(); i++) {
OBSDataAutoRelease entry = obs_data_create();
obs_data_set_default_int(entry, tabNames[i], i);
obs_data_array_push_back(defaultTabWidgetOrder, entry);
}
obs_data_set_default_array(obj, "tabWidgetOrder",
defaultTabWidgetOrder);
tabOrder.clear();
OBSDataArrayAutoRelease tabWidgetOrder =
obs_data_get_array(obj, "tabWidgetOrder");
for (size_t i = 0; i < tabNames.size(); i++) {
OBSDataAutoRelease entry =
obs_data_array_item(tabWidgetOrder, i);
tabOrder.emplace_back(
(int)(obs_data_get_int(entry, tabNames[i])));
}
if (!tabWidgetOrderValid()) {
resetTabWidgetOrder();
}
LoadTabOrder(obj);
saveWindowGeo = obs_data_get_bool(obj, "saveWindowGeo");
windowPos = {(int)obs_data_get_int(obj, "windowPosX"),
@@ -940,7 +829,8 @@ void AdvSceneSwitcher::SetupGeneralTab()
SIGNAL(DurationChanged(const Duration &)), this,
SLOT(CooldownDurationChanged(const Duration &)));
ui->verboseLogging->setChecked(switcher->verbose);
ui->logLevel->setCurrentIndex(static_cast<int>(switcher->logLevel));
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
ui->showTrayNotifications->setChecked(
switcher->showSystemTrayNotifications);

View File

@@ -12,7 +12,7 @@
namespace advss {
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
SceneGroupEditWidget *typeEdit = nullptr;
std::deque<SceneGroup> &GetSceneGroups()
@@ -141,7 +141,7 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
placeHolderText = format.arg(++i);
}
bool accepted = AdvSSNameDialog::AskForName(
bool accepted = NameDialog::AskForName(
this, obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"),
obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"), name,
placeHolderText);
@@ -170,7 +170,8 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
item->setData(Qt::UserRole, text);
ui->sceneGroups->setCurrentItem(item);
ui->sceneGroupAdd->disconnect(addPulse);
addPulse->deleteLater();
addPulse = nullptr;
ui->sceneGroupHelp->setVisible(false);
emit SceneGroupAdded(QString::fromStdString(name));
@@ -512,8 +513,8 @@ void AdvSceneSwitcher::SetupSceneGroupTab()
if (switcher->sceneGroups.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->sceneGroupAdd,
QColor(Qt::green));
addPulse = HighlightWidget(ui->sceneGroupAdd,
QColor(Qt::green));
}
ui->sceneGroupHelp->setVisible(true);
} else {

View File

@@ -12,7 +12,7 @@
namespace advss {
bool SceneTrigger::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_triggerAdd_clicked()
{
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_triggerAdd_clicked()
listAddClicked(ui->sceneTriggers,
new SceneTriggerWidget(this,
&switcher->sceneTriggers.back()),
ui->triggerAdd, &addPulse);
ui->triggerAdd, addPulse);
ui->triggerHelp->setVisible(false);
}
@@ -370,8 +370,8 @@ void AdvSceneSwitcher::SetupTriggerTab()
if (switcher->sceneTriggers.size() == 0) {
if (!switcher->disableHints) {
addPulse =
PulseWidget(ui->triggerAdd, QColor(Qt::green));
addPulse = HighlightWidget(ui->triggerAdd,
QColor(Qt::green));
}
ui->triggerHelp->setVisible(true);
} else {

View File

@@ -12,7 +12,7 @@
namespace advss {
bool AudioSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_audioAdd_clicked()
{
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_audioAdd_clicked()
AudioSwitchWidget *sw =
new AudioSwitchWidget(this, &switcher->audioSwitches.back());
listAddClicked(ui->audioSwitches, sw, ui->audioAdd, &addPulse);
listAddClicked(ui->audioSwitches, sw, ui->audioAdd, addPulse);
ui->audioHelp->setVisible(false);
}
@@ -104,7 +104,7 @@ void SwitcherData::checkAudioSwitchFallback(OBSWeakSource &scene,
scene = audioFallback.getScene();
transition = audioFallback.transition;
if (verbose) {
if (VerboseLoggingEnabled()) {
audioFallback.logMatch();
}
}
@@ -165,7 +165,7 @@ bool SwitcherData::checkAudioSwitch(OBSWeakSource &scene,
transition = s.transition;
match = true;
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logMatch();
}
@@ -232,7 +232,8 @@ void AdvSceneSwitcher::SetupAudioTab()
if (switcher->audioSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
addPulse = HighlightWidget(ui->audioAdd,
QColor(Qt::green));
}
ui->audioHelp->setVisible(true);
} else {
@@ -437,10 +438,8 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
audioVolumeThreshold->setMaximum(100);
audioVolumeThreshold->setMinimum(0);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
audioVolumeThreshold, SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
[=](double) { SyncSliderAndValueSelection(true); });
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this,
SLOT(VolumeThresholdChanged(int)));
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
@@ -487,6 +486,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
switchData = s;
loading = false;
SyncSliderAndValueSelection(false);
}
AudioSwitch *AudioSwitchWidget::getSwitchData()
@@ -509,6 +509,21 @@ void AudioSwitchWidget::swapSwitchData(AudioSwitchWidget *s1,
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()
{
delete volMeter;
@@ -520,10 +535,8 @@ void AudioSwitchWidget::UpdateVolmeterSource()
QLayout *layout = this->layout();
layout->addWidget(volMeter);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
audioVolumeThreshold, SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
[=](double) { SyncSliderAndValueSelection(true); });
// Slider will default to 0 so set it manually once
volMeter->GetSlider()->setValue(switchData->volumeThreshold);
@@ -547,8 +560,11 @@ void AudioSwitchWidget::VolumeThresholdChanged(int vol)
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)

View File

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

View File

@@ -9,7 +9,7 @@
namespace advss {
bool ExecutableSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_executableAdd_clicked()
{
@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_executableAdd_clicked()
listAddClicked(ui->executables,
new ExecutableSwitchWidget(
this, &switcher->executableSwitches.back()),
ui->executableAdd, &addPulse);
ui->executableAdd, addPulse);
ui->exeHelp->setVisible(false);
}
@@ -119,7 +119,7 @@ bool SwitcherData::checkExeSwitch(OBSWeakSource &scene,
scene = s.getScene();
transition = s.transition;
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logMatch();
}
break;
@@ -177,8 +177,8 @@ void AdvSceneSwitcher::SetupExecutableTab()
if (switcher->executableSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->executableAdd,
QColor(Qt::green));
addPulse = HighlightWidget(ui->executableAdd,
QColor(Qt::green));
}
ui->exeHelp->setVisible(true);
} else {

View File

@@ -18,7 +18,7 @@
namespace advss {
bool FileSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
static std::hash<std::string> strHash;
void AdvSceneSwitcher::on_browseButton_clicked()
@@ -274,7 +274,7 @@ bool SwitcherData::checkFileContent(OBSWeakSource &scene,
transition = s.transition;
match = true;
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logMatch();
}
break;
@@ -291,7 +291,7 @@ void AdvSceneSwitcher::on_fileAdd_clicked()
listAddClicked(ui->fileSwitches,
new FileSwitchWidget(this,
&switcher->fileSwitches.back()),
ui->fileAdd, &addPulse);
ui->fileAdd, addPulse);
ui->fileHelp->setVisible(false);
}
@@ -441,7 +441,8 @@ void AdvSceneSwitcher::SetupFileTab()
if (switcher->fileSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->fileAdd, QColor(Qt::green));
addPulse =
HighlightWidget(ui->fileAdd, QColor(Qt::green));
}
ui->fileHelp->setVisible(true);
} else {

View File

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

View File

@@ -9,7 +9,7 @@
namespace advss {
bool MediaSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
constexpr auto media_played_to_end_idx = 8;
constexpr auto media_any_idx = 9;
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_mediaAdd_clicked()
listAddClicked(ui->mediaSwitches,
new MediaSwitchWidget(this,
&switcher->mediaSwitches.back()),
ui->mediaAdd, &addPulse);
ui->mediaAdd, addPulse);
ui->mediaHelp->setVisible(false);
}
@@ -193,7 +193,7 @@ bool SwitcherData::checkMediaSwitch(OBSWeakSource &scene,
scene = mediaSwitch.getScene();
transition = mediaSwitch.transition;
if (verbose) {
if (VerboseLoggingEnabled()) {
mediaSwitch.logMatch();
}
}
@@ -252,7 +252,8 @@ void AdvSceneSwitcher::SetupMediaTab()
if (switcher->mediaSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
addPulse = HighlightWidget(ui->mediaAdd,
QColor(Qt::green));
}
ui->mediaHelp->setVisible(true);
} else {

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

View File

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

View File

@@ -10,7 +10,7 @@
namespace advss {
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_pauseAdd_clicked()
{
@@ -20,7 +20,7 @@ void AdvSceneSwitcher::on_pauseAdd_clicked()
listAddClicked(ui->pauseEntries,
new PauseEntryWidget(this,
&switcher->pauseEntries.back()),
ui->pauseAdd, &addPulse);
ui->pauseAdd, addPulse);
ui->pauseHelp->setVisible(false);
}
@@ -264,7 +264,8 @@ void AdvSceneSwitcher::SetupPauseTab()
if (switcher->pauseEntries.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->pauseAdd, QColor(Qt::green));
addPulse = HighlightWidget(ui->pauseAdd,
QColor(Qt::green));
}
ui->pauseHelp->setVisible(true);
} else {

View File

@@ -9,7 +9,7 @@
namespace advss {
bool RandomSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_randomAdd_clicked()
{
@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_randomAdd_clicked()
listAddClicked(ui->randomSwitches,
new RandomSwitchWidget(this,
&switcher->randomSwitches.back()),
ui->randomAdd, &addPulse);
ui->randomAdd, addPulse);
ui->randomHelp->setVisible(false);
}
@@ -76,7 +76,7 @@ bool SwitcherData::checkRandom(OBSWeakSource &scene, OBSWeakSource &transition,
lastRandomScene = r.scene;
lastRandomSceneGroup = r.group;
if (verbose) {
if (VerboseLoggingEnabled()) {
r.logMatch();
}
break;
@@ -131,8 +131,8 @@ void AdvSceneSwitcher::SetupRandomTab()
if (switcher->randomSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse =
PulseWidget(ui->randomAdd, QColor(Qt::green));
addPulse = HighlightWidget(ui->randomAdd,
QColor(Qt::green));
}
ui->randomHelp->setVisible(true);
} else {
@@ -147,8 +147,8 @@ void AdvSceneSwitcher::SetupRandomTab()
}");
if (switcher->switchIfNotMatching != NoMatchBehavior::RANDOM_SWITCH) {
if (!switcher->disableHints) {
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0));
HighlightWidget(ui->randomDisabledWarning,
QColor(Qt::red), QColor(0, 0, 0, 0));
}
} else {
ui->randomDisabledWarning->setVisible(false);

View File

@@ -11,7 +11,7 @@
namespace advss {
bool ScreenRegionSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::ClearFrames(QListWidget *list)
{
@@ -79,7 +79,7 @@ void AdvSceneSwitcher::on_screenRegionAdd_clicked()
listAddClicked(ui->screenRegionSwitches,
new ScreenRegionWidget(
this, &switcher->screenRegionSwitches.back()),
ui->screenRegionAdd, &addPulse);
ui->screenRegionAdd, addPulse);
ui->regionHelp->setVisible(false);
}
@@ -183,7 +183,7 @@ bool SwitcherData::checkScreenRegionSwitch(OBSWeakSource &scene,
transition = s.transition;
minRegionSize = regionSize;
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logMatch();
}
break;
@@ -248,8 +248,8 @@ void AdvSceneSwitcher::SetupRegionTab()
if (switcher->screenRegionSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->screenRegionAdd,
QColor(Qt::green));
addPulse = HighlightWidget(ui->screenRegionAdd,
QColor(Qt::green));
}
ui->regionHelp->setVisible(true);
} else {

View File

@@ -16,7 +16,7 @@ namespace advss {
constexpr auto max_extend_text_size = 150;
bool SceneSequenceSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
{
@@ -26,7 +26,7 @@ void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
listAddClicked(ui->sceneSequenceSwitches,
new SequenceWidget(
this, &switcher->sceneSequenceSwitches.back()),
ui->sceneSequenceAdd, &addPulse);
ui->sceneSequenceAdd, addPulse);
ui->sequenceHelp->setVisible(false);
}
@@ -225,13 +225,13 @@ bool SwitcherData::checkSceneSequence(OBSWeakSource &scene,
scene = s.getScene();
transition = s.transition;
setPrevSceneAfterLinger = s.usePreviousScene;
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logMatch();
}
}
s.advanceActiveSequence();
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logAdvanceSequence();
}
@@ -299,8 +299,8 @@ void AdvSceneSwitcher::SetupSequenceTab()
if (switcher->sceneSequenceSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->sceneSequenceAdd,
QColor(Qt::green));
addPulse = HighlightWidget(ui->sceneSequenceAdd,
QColor(Qt::green));
}
ui->sequenceHelp->setVisible(true);
} else {

View File

@@ -7,7 +7,7 @@
namespace advss {
bool TimeSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_timeAdd_clicked()
{
@@ -17,7 +17,7 @@ void AdvSceneSwitcher::on_timeAdd_clicked()
listAddClicked(ui->timeSwitches,
new TimeSwitchWidget(this,
&switcher->timeSwitches.back()),
ui->timeAdd, &addPulse);
ui->timeAdd, addPulse);
ui->timeHelp->setVisible(false);
}
@@ -142,7 +142,7 @@ bool SwitcherData::checkTimeSwitch(OBSWeakSource &scene,
transition = s.transition;
match = true;
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logMatch();
}
break;
@@ -197,7 +197,8 @@ void AdvSceneSwitcher::SetupTimeTab()
if (switcher->timeSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->timeAdd, QColor(Qt::green));
addPulse =
HighlightWidget(ui->timeAdd, QColor(Qt::green));
}
ui->timeHelp->setVisible(true);
} else {

View File

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

View File

@@ -14,7 +14,7 @@
namespace advss {
bool VideoSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_videoAdd_clicked()
{
@@ -24,7 +24,7 @@ void AdvSceneSwitcher::on_videoAdd_clicked()
VideoSwitchWidget *sw =
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
listAddClicked(ui->videoSwitches, sw, ui->videoAdd, &addPulse);
listAddClicked(ui->videoSwitches, sw, ui->videoAdd, addPulse);
ui->videoHelp->setVisible(false);
}
@@ -148,7 +148,7 @@ bool SwitcherData::checkVideoSwitch(OBSWeakSource &scene,
match = true;
scene = s.getScene();
transition = s.transition;
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logMatch();
}
}
@@ -202,7 +202,8 @@ void AdvSceneSwitcher::SetupVideoTab()
if (switcher->videoSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->videoAdd, QColor(Qt::green));
addPulse = HighlightWidget(ui->videoAdd,
QColor(Qt::green));
}
ui->videoHelp->setVisible(true);
} else {

View File

@@ -11,7 +11,7 @@
namespace advss {
bool WindowSwitch::pause = false;
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_windowAdd_clicked()
{
@@ -21,7 +21,7 @@ void AdvSceneSwitcher::on_windowAdd_clicked()
listAddClicked(ui->windowSwitches,
new WindowSwitchWidget(this,
&switcher->windowSwitches.back()),
ui->windowAdd, &addPulse);
ui->windowAdd, addPulse);
ui->windowHelp->setVisible(false);
}
@@ -249,7 +249,7 @@ bool SwitcherData::checkWindowTitleSwitch(OBSWeakSource &scene,
}
if (match) {
if (verbose) {
if (VerboseLoggingEnabled()) {
s.logMatch();
}
break;
@@ -335,8 +335,8 @@ void AdvSceneSwitcher::SetupTitleTab()
if (switcher->windowSwitches.size() == 0) {
if (!switcher->disableHints) {
addPulse =
PulseWidget(ui->windowAdd, QColor(Qt::green));
addPulse = HighlightWidget(ui->windowAdd,
QColor(Qt::green));
}
ui->windowHelp->setVisible(true);
} else {

View File

@@ -1,8 +1,9 @@
#include "macro-action-edit.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro-helpers.hpp"
#include "macro-properties.hpp"
#include "macro-settings.hpp"
#include "macro.hpp"
#include "plugin-state-helpers.hpp"
#include "section.hpp"
#include "switch-button.hpp"
@@ -28,8 +29,7 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData,
const std::string &id)
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightActions,
parent),
: MacroSegmentEdit(parent),
_actionSelection(new FilterComboBox()),
_enable(new SwitchButton()),
_entryData(entryData)
@@ -39,8 +39,6 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
SLOT(ActionSelectionChanged(const QString &)));
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
SLOT(ActionEnableChanged(bool)));
QWidget::connect(window(), SIGNAL(HighlightActionsChanged(bool)), this,
SLOT(EnableHighlight(bool)));
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
SLOT(UpdateActionState()));
@@ -88,6 +86,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
*_entryData = MacroActionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx);
(*_entryData)->PostLoad();
RunPostLoadSteps();
}
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
@@ -159,6 +158,34 @@ std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
return *_entryData;
}
void AdvSceneSwitcher::AddMacroAction(Macro *macro, int idx,
const std::string &id, obs_data_t *data)
{
if (idx < 0 || idx > (int)macro->Actions().size()) {
assert(false);
return;
}
{
auto lock = LockContext();
macro->Actions().emplace(macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro));
if (data) {
macro->Actions().at(idx)->Load(data);
}
macro->Actions().at(idx)->PostLoad();
RunPostLoadSteps();
macro->UpdateActionIndices();
ui->actionsList->Insert(
idx,
new MacroActionEdit(this, &macro->Actions()[idx], id));
SetActionData(*macro);
}
HighlightAction(idx);
ui->actionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::AddMacroAction(int idx)
{
auto macro = GetSelectedMacro();
@@ -167,6 +194,7 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
}
if (idx < 0 || idx > (int)macro->Actions().size()) {
assert(false);
return;
}
@@ -176,26 +204,13 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
} else {
id = MacroAction::GetDefaultID();
}
{
auto lock = LockContext();
macro->Actions().emplace(
macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro.get()));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
macro->Actions().at(idx)->Load(data);
macro->Actions().at(idx)->PostLoad();
obs_data_release(data);
}
macro->UpdateActionIndices();
ui->actionsList->Insert(
idx,
new MacroActionEdit(this, &macro->Actions()[idx], id));
SetActionData(*macro);
OBSDataAutoRelease data;
if (idx - 1 >= 0) {
data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
}
HighlightAction(idx);
emit(MacroSegmentOrderChanged());
AddMacroAction(macro.get(), idx, id, data);
}
void AdvSceneSwitcher::on_actionAdd_clicked()
@@ -213,7 +228,6 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
if (currentActionIdx != -1) {
MacroActionSelectionChanged(currentActionIdx + 1);
}
ui->actionsList->SetHelpMsgVisible(false);
}
void AdvSceneSwitcher::RemoveMacroAction(int idx)
@@ -308,7 +322,6 @@ void AdvSceneSwitcher::on_elseActionAdd_clicked()
if (currentElseActionIdx != -1) {
MacroElseActionSelectionChanged(currentElseActionIdx + 1);
}
ui->elseActionsList->SetHelpMsgVisible(false);
}
void AdvSceneSwitcher::on_elseActionRemove_clicked()
@@ -447,6 +460,36 @@ void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::AddMacroElseAction(Macro *macro, int idx,
const std::string &id,
obs_data_t *data)
{
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
assert(false);
return;
}
{
auto lock = LockContext();
macro->ElseActions().emplace(macro->ElseActions().begin() + idx,
MacroActionFactory::Create(id,
macro));
if (data) {
macro->ElseActions().at(idx)->Load(data);
}
macro->ElseActions().at(idx)->PostLoad();
RunPostLoadSteps();
macro->UpdateElseActionIndices();
ui->elseActionsList->Insert(
idx, new MacroActionEdit(
this, &macro->ElseActions()[idx], id));
SetElseActionData(*macro);
}
HighlightElseAction(idx);
ui->elseActionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::AddMacroElseAction(int idx)
{
auto macro = GetSelectedMacro();
@@ -455,6 +498,7 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
}
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
assert(false);
return;
}
@@ -464,25 +508,13 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
} else {
id = MacroAction::GetDefaultID();
}
{
auto lock = LockContext();
macro->ElseActions().emplace(
macro->ElseActions().begin() + idx,
MacroActionFactory::Create(id, macro.get()));
if (idx - 1 >= 0) {
OBSDataAutoRelease data = obs_data_create();
macro->ElseActions().at(idx - 1)->Save(data);
macro->ElseActions().at(idx)->Load(data);
}
macro->ElseActions().at(idx)->PostLoad();
macro->UpdateElseActionIndices();
ui->elseActionsList->Insert(
idx, new MacroActionEdit(
this, &macro->ElseActions()[idx], id));
SetElseActionData(*macro);
OBSDataAutoRelease data;
if (idx - 1 >= 0) {
data = obs_data_create();
macro->ElseActions().at(idx - 1)->Save(data);
}
HighlightElseAction(idx);
emit(MacroSegmentOrderChanged());
AddMacroElseAction(macro.get(), idx, id, data);
}
void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
@@ -503,7 +535,7 @@ void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
macro->UpdateElseActionIndices();
SetActionData(*macro);
SetElseActionData(*macro);
}
MacroElseActionSelectionChanged(-1);
lastInteracted = MacroSection::ELSE_ACTIONS;

View File

@@ -1,4 +1,5 @@
#include "macro-action-macro.hpp"
#include "help-icon.hpp"
#include "layout-helpers.hpp"
#include "macro.hpp"
@@ -48,9 +49,7 @@ bool MacroActionMacro::PerformAction()
macro->ResetRunCount();
break;
case Action::RUN:
if (!macro->Paused()) {
macro->PerformActions(true, false, true);
}
RunActions(macro.get());
break;
case Action::STOP:
macro->Stop();
@@ -88,32 +87,32 @@ void MacroActionMacro::LogAction() const
}
switch (_action) {
case Action::PAUSE:
vblog(LOG_INFO, "paused \"%s\"", macro->Name().c_str());
ablog(LOG_INFO, "paused \"%s\"", macro->Name().c_str());
break;
case Action::UNPAUSE:
vblog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str());
ablog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str());
break;
case Action::RESET_COUNTER:
vblog(LOG_INFO, "reset counter for \"%s\"",
ablog(LOG_INFO, "reset counter for \"%s\"",
macro->Name().c_str());
break;
case Action::RUN:
vblog(LOG_INFO, "run nested macro \"%s\"",
ablog(LOG_INFO, "run nested macro \"%s\"",
macro->Name().c_str());
break;
case Action::STOP:
vblog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str());
ablog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str());
break;
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());
break;
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());
break;
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());
break;
default:
@@ -127,6 +126,7 @@ bool MacroActionMacro::Save(obs_data_t *obj) const
_macro.Save(obj);
_actionIndex.Save(obj, "actionIndex");
obs_data_set_int(obj, "action", static_cast<int>(_action));
_runOptions.Save(obj);
return true;
}
@@ -137,6 +137,15 @@ bool MacroActionMacro::Load(obs_data_t *obj)
_actionIndex.Load(obj, "actionIndex");
_action = static_cast<MacroActionMacro::Action>(
obs_data_get_int(obj, "action"));
_runOptions.Load(obj);
return true;
}
bool MacroActionMacro::PostLoad()
{
MacroRefAction::PostLoad();
MacroAction::PostLoad();
_runOptions.macro.PostLoad();
return true;
}
@@ -145,11 +154,83 @@ std::string MacroActionMacro::GetShortDesc() const
return _macro.Name();
}
static inline void populateActionSelection(QComboBox *list)
std::shared_ptr<MacroAction> MacroActionMacro::Create(Macro *m)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
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 void runActionsHelper(Macro *macro, bool runElseActions, bool setInputs,
const StringList &inputs)
{
if (setInputs) {
macro->GetInputVariables().SetValues(inputs);
}
macro->PerformActions(!runElseActions, false, true);
}
void MacroActionMacro::RunActions(Macro *actionMacro) const
{
if (_runOptions.skipWhenPaused && actionMacro->Paused()) {
return;
}
if (_runOptions.logic == RunOptions::Logic::IGNORE_CONDITIONS) {
runActionsHelper(actionMacro, _runOptions.runElseActions,
_runOptions.setInputs, _runOptions.inputs);
return;
}
auto conditionMacro = _runOptions.macro.GetMacro();
if (!conditionMacro) {
return;
}
if (_runOptions.reevaluateConditionState) {
conditionMacro->CeckMatch(true);
}
if ((_runOptions.logic == RunOptions::Logic::CONDITIONS &&
conditionMacro->Matched()) ||
(_runOptions.logic == RunOptions::Logic::INVERT_CONDITIONS &&
!conditionMacro->Matched())) {
runActionsHelper(actionMacro, _runOptions.runElseActions,
_runOptions.setInputs, _runOptions.inputs);
}
}
static void populateActionSelection(QComboBox *list)
{
for (const auto &[_, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
static void populateConditionBehaviorSelection(QComboBox *list)
{
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.conditions.ignore"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.conditions.true"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.conditions.false"));
}
static void populateActionTypeSelection(QComboBox *list)
{
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.actionType.regular"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.actionType.else"));
}
MacroActionMacroEdit::MacroActionMacroEdit(
@@ -158,9 +239,28 @@ MacroActionMacroEdit::MacroActionMacroEdit(
_macros(new MacroSelection(parent)),
_actionIndex(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::ACTION)),
_actions(new QComboBox())
_actions(new QComboBox()),
_conditionMacros(new MacroSelection(parent)),
_conditionBehaviors(new QComboBox()),
_reevaluateConditionState(new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.macro.type.run."
"updateConditionMatchState"))),
_actionTypes(new QComboBox()),
_skipWhenPaused(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.skipWhenPaused"))),
_setInputs(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.setInputs"))),
_inputs(new MacroInputEdit()),
_entryLayout(new QHBoxLayout()),
_conditionLayout(new QHBoxLayout()),
_reevaluateConditionStateLayout(new QHBoxLayout()),
_setInputsLayout(new QHBoxLayout())
{
populateActionSelection(_actions);
populateConditionBehaviorSelection(_conditionBehaviors);
populateActionTypeSelection(_actionTypes);
_conditionMacros->HideSelectedMacro();
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
@@ -169,20 +269,53 @@ MacroActionMacroEdit::MacroActionMacroEdit(
QWidget::connect(_actionIndex,
SIGNAL(SelectionChanged(const IntVariable &)), this,
SLOT(ActionIndexChanged(const IntVariable &)));
QWidget::connect(_conditionMacros,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ConditionMacroChanged(const QString &)));
QWidget::connect(_conditionBehaviors, SIGNAL(currentIndexChanged(int)),
this, SLOT(ConditionBehaviorChanged(int)));
QWidget::connect(_actionTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionTypeChanged(int)));
QWidget::connect(_skipWhenPaused, SIGNAL(stateChanged(int)), this,
SLOT(SkipWhenPausedChanged(int)));
QWidget::connect(_setInputs, SIGNAL(stateChanged(int)), this,
SLOT(SetInputsChanged(int)));
QWidget::connect(_inputs,
SIGNAL(MacroInputValuesChanged(const StringList &)),
this, SLOT(InputsChanged(const StringList &)));
QWidget::connect(_reevaluateConditionState, SIGNAL(stateChanged(int)),
this, SLOT(ReevaluateConditionStateChanged(int)));
auto mainLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
mainLayout,
{{"{{actions}}", _actions},
{"{{actionIndex}}", _actionIndex},
{"{{macros}}", _macros}});
setLayout(mainLayout);
_setInputsLayout->addWidget(_setInputs);
_setInputsLayout->addWidget(new HelpIcon(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.setInputs.description")));
_setInputsLayout->addStretch();
_reevaluateConditionStateLayout->addWidget(_reevaluateConditionState);
_reevaluateConditionStateLayout->addWidget(new HelpIcon(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState.help")));
_reevaluateConditionStateLayout->addStretch();
auto layout = new QVBoxLayout();
layout->addLayout(_entryLayout);
layout->addLayout(_conditionLayout);
layout->addLayout(_reevaluateConditionStateLayout);
layout->addLayout(_setInputsLayout);
layout->addWidget(_inputs);
layout->addWidget(_skipWhenPaused);
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void HighligthMacroSettingsButton(bool enable);
MacroActionMacroEdit::~MacroActionMacroEdit()
{
HighligthMacroSettingsButton(false);
}
void MacroActionMacroEdit::UpdateEntryData()
{
if (!_entryData) {
@@ -192,45 +325,132 @@ void MacroActionMacroEdit::UpdateEntryData()
_actionIndex->SetValue(_entryData->_actionIndex);
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
_macros->SetCurrentMacro(_entryData->_macro);
_conditionMacros->SetCurrentMacro(_entryData->_runOptions.macro);
_conditionBehaviors->setCurrentIndex(
static_cast<int>(_entryData->_runOptions.logic));
_reevaluateConditionState->setChecked(
_entryData->_runOptions.reevaluateConditionState);
_actionTypes->setCurrentIndex(
_entryData->_runOptions.runElseActions ? 1 : 0);
_skipWhenPaused->setChecked(_entryData->_runOptions.skipWhenPaused);
_setInputs->setChecked(_entryData->_runOptions.setInputs);
SetupMacroInput(_entryData->_macro.GetMacro().get());
SetWidgetVisibility();
}
void MacroActionMacroEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_macro = text;
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
const auto &macro = _entryData->_macro.GetMacro();
_actionIndex->SetMacro(macro);
SetupMacroInput(macro.get());
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionMacro::Action>(value);
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_actionIndex = value;
}
void MacroActionMacroEdit::ConditionMacroChanged(const QString &text)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.macro = text;
}
void MacroActionMacroEdit::ConditionBehaviorChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.logic =
static_cast<MacroActionMacro::RunOptions::Logic>(value);
SetWidgetVisibility();
}
void MacroActionMacroEdit::ReevaluateConditionStateChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.reevaluateConditionState = value;
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionTypeChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.runElseActions = value;
}
void MacroActionMacroEdit::SkipWhenPausedChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.skipWhenPaused = value;
}
void MacroActionMacroEdit::SetInputsChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.setInputs = value;
SetWidgetVisibility();
}
void MacroActionMacroEdit::InputsChanged(const StringList &inputs)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.inputs = inputs;
adjustSize();
updateGeometry();
}
void MacroActionMacroEdit::SetWidgetVisibility()
{
_entryLayout->removeWidget(_actions);
_entryLayout->removeWidget(_actionIndex);
_entryLayout->removeWidget(_macros);
_entryLayout->removeWidget(_actionTypes);
_conditionLayout->removeWidget(_conditionBehaviors);
_conditionLayout->removeWidget(_conditionMacros);
ClearLayout(_entryLayout);
ClearLayout(_conditionLayout);
const std::unordered_map<std::string, QWidget *> placeholders = {
{"{{actions}}", _actions},
{"{{actionIndex}}", _actionIndex},
{"{{macros}}", _macros},
{"{{actionTypes}}", _actionTypes},
{"{{conditionBehaviors}}", _conditionBehaviors},
{"{{conditionMacros}}", _conditionMacros},
};
PlaceWidgets(
obs_module_text(
_entryData->_action == MacroActionMacro::Action::RUN
? "AdvSceneSwitcher.action.macro.entry.run"
: "AdvSceneSwitcher.action.macro.entry.other"),
_entryLayout, placeholders);
if (_entryData->_runOptions.logic ==
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS) {
_conditionLayout->addWidget(_conditionBehaviors);
_conditionLayout->addStretch();
} else {
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.macro.entry.run.condition"),
_conditionLayout, placeholders);
}
if (_entryData->_action == MacroActionMacro::Action::RUN ||
_entryData->_action == MacroActionMacro::Action::STOP) {
_macros->HideSelectedMacro();
@@ -245,6 +465,73 @@ void MacroActionMacroEdit::SetWidgetVisibility()
MacroActionMacro::Action::ENABLE_ACTION ||
_entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION;
_actionIndex->setVisible(isModifyingActionState);
SetLayoutVisible(_conditionLayout,
_entryData->_action == MacroActionMacro::Action::RUN);
const bool needsAdditionalConditionWidgets =
_entryData->_action == MacroActionMacro::Action::RUN &&
_entryData->_runOptions.logic !=
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS;
_conditionMacros->setVisible(needsAdditionalConditionWidgets);
SetLayoutVisible(_reevaluateConditionStateLayout,
needsAdditionalConditionWidgets);
SetLayoutVisible(_setInputsLayout,
_entryData->_action == MacroActionMacro::Action::RUN);
_inputs->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs);
HighligthMacroSettingsButton(_entryData->_action ==
MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs &&
!_inputs->HasInputsToSet());
_actionTypes->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN);
_skipWhenPaused->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN);
adjustSize();
updateGeometry();
}
void MacroActionMacroEdit::SetupMacroInput(Macro *macro) const
{
if (macro) {
_inputs->SetInputVariablesAndValues(
macro->GetInputVariables(),
_entryData->_runOptions.inputs);
} else {
_inputs->SetInputVariablesAndValues({}, {});
}
}
void MacroActionMacro::RunOptions::Save(obs_data_t *obj) const
{
OBSDataAutoRelease data = obs_data_create();
obs_data_set_int(data, "logic", static_cast<int>(logic));
obs_data_set_bool(data, "reevaluateConditionState",
reevaluateConditionState);
obs_data_set_bool(data, "runElseActions", runElseActions);
obs_data_set_bool(data, "skipWhenPaused", skipWhenPaused);
obs_data_set_bool(data, "setInputs", setInputs);
inputs.Save(data, "inputs");
macro.Save(data);
obs_data_set_obj(obj, "runOptions", data);
}
void MacroActionMacro::RunOptions::Load(obs_data_t *obj)
{
if (!obs_data_has_user_value(obj, "runOptions")) {
return;
}
OBSDataAutoRelease data = obs_data_get_obj(obj, "runOptions");
logic = static_cast<Logic>(obs_data_get_int(data, "logic"));
reevaluateConditionState =
obs_data_get_bool(data, "reevaluateConditionState");
runElseActions = obs_data_get_bool(data, "runElseActions");
skipWhenPaused = obs_data_get_bool(data, "skipWhenPaused");
setInputs = obs_data_get_bool(data, "setInputs");
inputs.Load(data, "inputs");
macro.Load(data);
}
} // namespace advss

View File

@@ -1,25 +1,44 @@
#pragma once
#include "macro-action-edit.hpp"
#include "macro-input.hpp"
#include "macro-selection.hpp"
#include "macro-segment-selection.hpp"
#include <QCheckBox>
#include <QHBoxLayout>
namespace advss {
class MacroActionMacro : public MacroRefAction {
class MacroActionMacro final : public MacroRefAction {
public:
MacroActionMacro(Macro *m) : MacroAction(m), MacroRefAction(m) {}
bool PerformAction();
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
bool PostLoad();
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionMacro>(m);
}
static std::shared_ptr<MacroAction> Create(Macro *m);
std::shared_ptr<MacroAction> Copy() const;
void ResolveVariablesToFixedValues();
struct RunOptions {
void Save(obs_data_t *obj) const;
void Load(obs_data_t *obj);
enum class Logic {
IGNORE_CONDITIONS,
CONDITIONS,
INVERT_CONDITIONS,
};
Logic logic;
bool reevaluateConditionState = false;
bool runElseActions = false;
bool skipWhenPaused = true;
bool setInputs = false;
StringList inputs;
MacroRef macro;
};
enum class Action {
PAUSE,
@@ -31,21 +50,25 @@ public:
ENABLE_ACTION,
TOGGLE_ACTION,
};
Action _action = Action::PAUSE;
Action _action = Action::RUN;
IntVariable _actionIndex = 1;
RunOptions _runOptions = {};
private:
void RunActions(Macro *actionMacro) const;
static bool _registered;
static const std::string id;
};
class MacroActionMacroEdit : public QWidget {
class MacroActionMacroEdit final : public QWidget {
Q_OBJECT
public:
MacroActionMacroEdit(
QWidget *parent,
std::shared_ptr<MacroActionMacro> entryData = nullptr);
~MacroActionMacroEdit();
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
@@ -59,18 +82,37 @@ private slots:
void MacroChanged(const QString &text);
void ActionChanged(int value);
void ActionIndexChanged(const IntVariable &value);
void ConditionMacroChanged(const QString &text);
void ConditionBehaviorChanged(int value);
void ReevaluateConditionStateChanged(int value);
void ActionTypeChanged(int value);
void SkipWhenPausedChanged(int value);
void SetInputsChanged(int value);
void InputsChanged(const StringList &);
signals:
void HeaderInfoChanged(const QString &);
protected:
private:
void SetWidgetVisibility();
void SetupMacroInput(Macro *) const;
MacroSelection *_macros;
MacroSegmentSelection *_actionIndex;
QComboBox *_actions;
MacroSelection *_conditionMacros;
QComboBox *_conditionBehaviors;
QCheckBox *_reevaluateConditionState;
QComboBox *_actionTypes;
QCheckBox *_skipWhenPaused;
QCheckBox *_setInputs;
MacroInputEdit *_inputs;
QHBoxLayout *_entryLayout;
QHBoxLayout *_conditionLayout;
QHBoxLayout *_reevaluateConditionStateLayout;
QHBoxLayout *_setInputsLayout;
std::shared_ptr<MacroActionMacro> _entryData;
private:
void SetWidgetVisibility();
bool _loading = true;
};

View File

@@ -31,7 +31,9 @@ void MacroActionQueue::AddActions(ActionQueue *queue)
auto actions = *GetMacroActions(macro.get());
for (const auto &action : actions) {
queue->Add(action);
if (action->Enabled()) {
queue->Add(action);
}
}
}
@@ -69,20 +71,20 @@ void MacroActionQueue::LogAction() const
}
switch (_action) {
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(),
GetActionQueueName(_queue).c_str());
break;
case Action::START_QUEUE:
vblog(LOG_INFO, "start queue \"%s\"",
ablog(LOG_INFO, "start queue \"%s\"",
GetActionQueueName(_queue).c_str());
break;
case Action::STOP_QUEUE:
vblog(LOG_INFO, "stop queue \"%s\"",
ablog(LOG_INFO, "stop queue \"%s\"",
GetActionQueueName(_queue).c_str());
break;
case Action::CLEAR_QUEUE:
vblog(LOG_INFO, "cleared queue \"%s\"",
ablog(LOG_INFO, "cleared queue \"%s\"",
GetActionQueueName(_queue).c_str());
break;
default:
@@ -114,6 +116,16 @@ std::string MacroActionQueue::GetShortDesc() const
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)
{
for (const auto &[_, name] : actionTypes) {

View File

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

View File

@@ -54,6 +54,12 @@ const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
{MacroActionVariable::Type::SCENE_ITEM_NAME,
"AdvSceneSwitcher.action.variable.type.sceneItemName"},
{MacroActionVariable::Type::PAD,
"AdvSceneSwitcher.action.variable.type.padValue"},
{MacroActionVariable::Type::TRUNCATE,
"AdvSceneSwitcher.action.variable.type.truncateValue"},
{MacroActionVariable::Type::SWAP_VALUES,
"AdvSceneSwitcher.action.variable.type.swapValues"},
};
static void apppend(Variable &var, const std::string &value)
@@ -198,7 +204,8 @@ void MacroActionVariable::SetToSceneItemName(Variable *var)
struct AskForInputParams {
AskForInputParams(const QString &prompt_, const QString &placeholder_)
: prompt(prompt_), placeholder(placeholder_){};
: prompt(prompt_),
placeholder(placeholder_){};
QString prompt;
QString placeholder;
std::optional<std::string> result;
@@ -216,6 +223,38 @@ static void askForInput(void *param)
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()
{
auto var = _variable.lock();
@@ -310,7 +349,10 @@ bool MacroActionVariable::PerformAction()
return true;
}
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;
}
case Type::SCENE_ITEM_COUNT: {
@@ -344,6 +386,25 @@ bool MacroActionVariable::PerformAction()
case Type::SCENE_ITEM_NAME:
SetToSceneItemName(var.get());
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;
case Type::SWAP_VALUES: {
auto var2 = _variable2.lock();
if (!var2) {
return true;
}
auto tempValue = var->Value();
var->SetValue(var2->Value());
var2->SetValue(tempValue);
return true;
}
}
return true;
@@ -377,6 +438,9 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
_scene.Save(obj);
_tempVar.Save(obj);
_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;
}
@@ -408,6 +472,13 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_scene.Load(obj);
_tempVar.Load(obj, GetMacro());
_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;
}
@@ -422,6 +493,16 @@ std::string MacroActionVariable::GetShortDesc() const
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)
{
DecrementCurrentSegmentVariableRef();
@@ -485,6 +566,19 @@ int MacroActionVariable::GetSegmentIndexValue() const
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()
{
auto segment = _macroSegment.lock();
@@ -503,6 +597,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(
QWidget *parent, std::shared_ptr<MacroActionVariable> entryData)
: QWidget(parent),
@@ -539,7 +643,12 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_scenes(new SceneSelectionWidget(this, true, false, true, true,
true)),
_tempVars(new TempVariableSelection(this)),
_tempVarsHelp(new HelpIcon(obs_module_text(
"AdvSceneSwitcher.action.variable.type.setToTempvar.help"))),
_sceneItemIndex(new VariableSpinBox()),
_direction(new QComboBox()),
_stringLength(new VariableSpinBox()),
_paddingCharSelection(new SingleCharSelection()),
_entryLayout(new QHBoxLayout())
{
_numValue->setMinimum(-9999999999);
@@ -559,7 +668,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_sceneItemIndex->setMinimum(1);
_stringLength->setMaximum(999);
populateTypeSelection(_actions);
populateDirectionSelection(_direction);
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
this, SLOT(VariableChanged(const QString &)));
@@ -615,8 +726,17 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_sceneItemIndex,
SIGNAL(NumberVariableChanged(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},
{"{{variables2}}", _variables2},
{"{{actions}}", _actions},
@@ -637,10 +757,15 @@ MacroActionVariableEdit::MacroActionVariableEdit(
{"{{envVariableName}}", _envVariable},
{"{{scenes}}", _scenes},
{"{{tempVars}}", _tempVars},
{"{{tempVarsHelp}}", _tempVarsHelp},
{"{{sceneItemIndex}}", _sceneItemIndex},
{"{{direction}}", _direction},
{"{{stringLength}}", _stringLength},
{"{{paddingCharSelection}}", _paddingCharSelection},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
_entryLayout, widgetPlaceholders);
PlaceWidgets(
obs_module_text("AdvSceneSwitcher.action.variable.entry.other"),
_entryLayout, widgetPlaceholders);
PlaceWidgets(
obs_module_text(
@@ -733,6 +858,10 @@ void MacroActionVariableEdit::UpdateEntryData()
_scenes->SetScene(_entryData->_scene);
_tempVars->SetVariable(_entryData->_tempVar);
_sceneItemIndex->SetValue(_entryData->_sceneItemIndex);
_direction->setCurrentIndex(static_cast<int>(_entryData->_direction));
_stringLength->SetValue(_entryData->_stringLength);
_paddingCharSelection->setText(
QChar::fromLatin1(_entryData->_paddingChar));
SetWidgetVisibility();
}
@@ -1089,6 +1218,7 @@ void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
auto lock = LockContext();
_entryData->_tempVar = var;
SetWidgetVisibility();
}
void MacroActionVariableEdit::SceneItemIndexChanged(
@@ -1102,12 +1232,85 @@ void MacroActionVariableEdit::SceneItemIndexChanged(
_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()
{
if (!_entryData) {
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},
{"{{tempVarsHelp}}", _tempVarsHelp},
{"{{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 ||
_entryData->_type == MacroActionVariable::Type::APPEND ||
_entryData->_type == MacroActionVariable::Type::MATH_EXPRESSION ||
@@ -1119,8 +1322,9 @@ void MacroActionVariableEdit::SetWidgetVisibility()
AddStretchIfNecessary(_entryLayout);
}
_variables2->setVisible(_entryData->_type ==
MacroActionVariable::Type::APPEND_VAR);
_variables2->setVisible(
_entryData->_type == MacroActionVariable::Type::APPEND_VAR ||
_entryData->_type == MacroActionVariable::Type::SWAP_VALUES);
_strValue->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SET_FIXED_VALUE ||
@@ -1196,8 +1400,20 @@ void MacroActionVariableEdit::SetWidgetVisibility()
MacroActionVariable::Type::SCENE_ITEM_NAME);
_tempVars->setVisible(_entryData->_type ==
MacroActionVariable::Type::SET_TO_TEMPVAR);
_tempVarsHelp->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SET_TO_TEMPVAR &&
!_entryData->_tempVar.HasValidID());
_sceneItemIndex->setVisible(_entryData->_type ==
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();
updateGeometry();
}

View File

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

View File

@@ -25,7 +25,7 @@ bool MacroAction::Load(obs_data_t *obj)
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)
@@ -38,6 +38,8 @@ bool MacroAction::Enabled() const
return _enabled;
}
void MacroAction::ResolveVariablesToFixedValues() {}
std::string_view MacroAction::GetDefaultID()
{
return "scene_switch";

View File

@@ -8,10 +8,17 @@ class EXPORT MacroAction : public MacroSegment {
public:
MacroAction(Macro *m, bool supportsVariableValue = false);
virtual ~MacroAction() = default;
virtual std::shared_ptr<MacroAction> Copy() const = 0;
virtual bool PerformAction() = 0;
virtual void LogAction() const;
virtual bool Save(obs_data_t *obj) const = 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);
bool Enabled() const;

View File

@@ -1,8 +1,9 @@
#include "macro-condition-edit.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro-properties.hpp"
#include "macro-settings.hpp"
#include "macro.hpp"
#include "path-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "section.hpp"
#include "ui-helpers.hpp"
#include "utility.hpp"
@@ -120,8 +121,7 @@ void DurationModifierEdit::Collapse(bool collapse)
MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root)
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightConditions,
parent),
: MacroSegmentEdit(parent),
_logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()),
_dur(new DurationModifierEdit()),
@@ -138,8 +138,6 @@ MacroConditionEdit::MacroConditionEdit(
QWidget::connect(_dur, SIGNAL(ModifierChanged(DurationModifier::Type)),
this,
SLOT(DurationModifierChanged(DurationModifier::Type)));
QWidget::connect(window(), SIGNAL(HighlightConditionsChanged(bool)),
this, SLOT(EnableHighlight(bool)));
populateLogicSelection(_logicSelection, root);
populateConditionSelection(_conditionSelection);
@@ -254,6 +252,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
(*_entryData)->SetIndex(idx);
(*_entryData)->SetLogicType(logic);
(*_entryData)->PostLoad();
RunPostLoadSteps();
}
auto widget =
MacroConditionFactory::CreateWidget(id, this, *_entryData);
@@ -297,6 +296,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
}
if (idx < 0 || idx > (int)macro->Conditions().size()) {
assert(false);
return;
}
@@ -313,18 +313,34 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
id = MacroCondition::GetDefaultID();
logic = LogicType::ROOT_NONE;
}
OBSDataAutoRelease data;
if (idx - 1 >= 0) {
data = obs_data_create();
macro->Conditions().at(idx - 1)->Save(data);
}
AddMacroCondition(macro.get(), idx, id, data.Get(), logic);
}
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
const std::string &id,
obs_data_t *data, LogicType logic)
{
if (idx < 0 || idx > (int)macro->Conditions().size()) {
assert(false);
return;
}
{
auto lock = LockContext();
auto cond = macro->Conditions().emplace(
macro->Conditions().begin() + idx,
MacroConditionFactory::Create(id, macro.get()));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Conditions().at(idx - 1)->Save(data);
MacroConditionFactory::Create(id, macro));
if (data) {
macro->Conditions().at(idx)->Load(data);
obs_data_release(data);
}
macro->Conditions().at(idx)->PostLoad();
RunPostLoadSteps();
(*cond)->SetLogicType(logic);
macro->UpdateConditionIndices();
ui->conditionsList->Insert(
@@ -334,6 +350,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
SetConditionData(*macro);
}
HighlightCondition(idx);
ui->conditionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged());
}
@@ -352,7 +369,6 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
if (currentConditionIdx != -1) {
MacroConditionSelectionChanged(currentConditionIdx + 1);
}
ui->conditionsList->SetHelpMsgVisible(false);
}
void AdvSceneSwitcher::RemoveMacroCondition(int idx)

View File

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

View File

@@ -167,7 +167,7 @@ void MacroDock::Highlight()
}
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
macro->WasExecutedSince(_lastHighlightCheckTime)) {
PulseWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
HighlightWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
}
_lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
}

View File

@@ -8,8 +8,13 @@
namespace advss {
static bool usePlainText = false;
MacroExportImportDialog::MacroExportImportDialog(Type type)
: QDialog(nullptr), _importExportString(new QPlainTextEdit(this))
: QDialog(nullptr),
_importExportString(new QPlainTextEdit(this)),
_usePlainText(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.export.usePlainText")))
{
_importExportString->setReadOnly(type == Type::EXPORT_MACRO);
auto label = new QLabel(obs_module_text(
@@ -29,9 +34,15 @@ MacroExportImportDialog::MacroExportImportDialog(Type type)
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
buttons->setCenterButtons(true);
_usePlainText->setChecked(usePlainText);
_usePlainText->setVisible(type == Type::EXPORT_MACRO);
connect(_usePlainText, &QCheckBox::stateChanged, this,
&MacroExportImportDialog::UsePlainTextChanged);
auto layout = new QVBoxLayout;
layout->addWidget(label);
layout->addWidget(_importExportString);
layout->addWidget(_usePlainText);
layout->addWidget(buttons);
setLayout(layout);
@@ -64,6 +75,21 @@ void MacroExportImportDialog::ExportMacros(const QString &json)
dialog.exec();
}
void MacroExportImportDialog::UsePlainTextChanged(int value)
{
if (usePlainText == (!!value)) {
return;
}
const auto current = _importExportString->toPlainText();
if (usePlainText) {
_importExportString->setPlainText(compressMacroString(current));
} else {
_importExportString->setPlainText(
decompressMacroString(current));
}
usePlainText = value;
}
static bool isValidData(const QString &json)
{
OBSDataAutoRelease data =

View File

@@ -1,4 +1,5 @@
#pragma once
#include <QCheckBox>
#include <QDialog>
#include <QPlainTextEdit>
@@ -13,8 +14,12 @@ public:
static void ExportMacros(const QString &json);
static bool ImportMacros(QString &json);
private slots:
void UsePlainTextChanged(int);
private:
QPlainTextEdit *_importExportString;
QCheckBox *_usePlainText;
};
} // namespace advss

247
lib/macro/macro-input.cpp Normal file
View File

@@ -0,0 +1,247 @@
#include "macro-input.hpp"
#include "layout-helpers.hpp"
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "variable-line-edit.hpp"
#include <algorithm>
#include <obs.hpp>
namespace advss {
void MacroInputVariables::Save(obs_data_t *obj) const
{
OBSDataArrayAutoRelease array = obs_data_array_create();
for (auto &var : _inputVariables) {
OBSDataAutoRelease arrayObj = obs_data_create();
auto variable = var.lock();
if (variable) {
obs_data_set_string(arrayObj, "variableName",
variable->Name().c_str());
} else {
obs_data_set_bool(arrayObj, "invalidVariable", true);
}
obs_data_array_push_back(array, arrayObj);
}
obs_data_set_array(obj, "inputVariables", array);
}
void MacroInputVariables::Load(obs_data_t *obj)
{
OBSDataArrayAutoRelease array =
obs_data_get_array(obj, "inputVariables");
const size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease arrayObj = obs_data_array_item(array, i);
const bool isInvalid =
obs_data_get_bool(arrayObj, "invalidVariable");
if (isInvalid) {
_inputVariables.emplace_back();
} else {
const auto name =
obs_data_get_string(arrayObj, "variableName");
_inputVariables.emplace_back(
GetWeakVariableByName(name));
}
}
}
void MacroInputVariables::SetValues(const StringList &values)
{
if (_inputVariables.size() != (size_t)values.size()) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
blog(LOG_WARNING,
"%s - sizes do not match (variables: %ld, values %d)",
__func__, _inputVariables.size(), values.size());
#else
blog(LOG_WARNING,
"%s - sizes do not match (variables: %ld, values %lld)",
__func__, _inputVariables.size(), values.size());
#endif
}
for (size_t index = 0;
index < _inputVariables.size() && index < (size_t)values.size();
index++) {
auto variable = _inputVariables.at(index).lock();
if (!variable) {
continue;
}
variable->SetValue(values.at(index));
}
}
MacroInputSelection::MacroInputSelection(QWidget *parent) : ListEditor(parent)
{
_list->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
}
void MacroInputSelection::SetInputs(const MacroInputVariables &inputs)
{
_currentSelection = inputs;
for (const auto &var : inputs._inputVariables) {
auto variable = var.lock();
const auto name =
variable
? QString::fromStdString(variable->Name())
: obs_module_text(
"AdvSceneSwitcher.macroTab.inputSettings.invalid");
QVariant v = QVariant::fromValue(name);
auto item = new QListWidgetItem(name, _list);
item->setData(Qt::UserRole, name);
}
UpdateListSize();
}
void MacroInputSelection::Add()
{
std::string varName;
bool accepted = VariableSelectionDialog::AskForVariable(this, varName);
if (!accepted || varName.empty()) {
return;
}
_currentSelection._inputVariables.emplace_back(
GetWeakVariableByName(varName));
QVariant v = QVariant::fromValue(QString::fromStdString(varName));
auto item = new QListWidgetItem(QString::fromStdString(varName), _list);
item->setData(Qt::UserRole, QString::fromStdString(varName));
UpdateListSize();
}
void MacroInputSelection::Remove()
{
auto item = _list->currentItem();
int idx = _list->currentRow();
if (!item || idx == -1) {
return;
}
_currentSelection._inputVariables.erase(
std::next(_currentSelection._inputVariables.begin(), idx));
delete item;
UpdateListSize();
}
void MacroInputSelection::Up()
{
int idx = _list->currentRow();
if (idx < 1) {
return;
}
_list->insertItem(idx - 1, _list->takeItem(idx));
_list->setCurrentRow(idx - 1);
std::iter_swap(
std::next(_currentSelection._inputVariables.begin(), idx),
std::next(_currentSelection._inputVariables.begin(), idx - 1));
}
void MacroInputSelection::Down()
{
int idx = _list->currentRow();
if (idx < 0 || idx >= _list->count()) {
return;
}
_list->insertItem(idx + 1, _list->takeItem(idx));
_list->setCurrentRow(idx + 1);
std::iter_swap(
std::next(_currentSelection._inputVariables.begin(), idx + 1),
std::next(_currentSelection._inputVariables.begin(), idx));
}
void MacroInputSelection::Clicked(QListWidgetItem *item)
{
std::string varName;
bool accepted = VariableSelectionDialog::AskForVariable(this, varName);
if (!accepted || varName.empty()) {
return;
}
item->setText(QString::fromStdString(varName));
int idx = _list->currentRow();
_currentSelection._inputVariables.at(idx) =
GetWeakVariableByName(varName);
}
MacroInputEdit::MacroInputEdit(QWidget *parent)
: QWidget(parent),
_layout(new QGridLayout())
{
setLayout(_layout);
}
void MacroInputEdit::SetInputVariablesAndValues(
const MacroInputVariables &inputs, const StringList &values)
{
_variables = inputs;
_values = values;
if ((size_t)_values.size() < _variables._inputVariables.size()) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
for (int i = 0;
_variables._inputVariables.size() - (size_t)_values.size();
i++) {
_values.push_back({});
}
#else
_values.resize(_variables._inputVariables.size());
#endif
}
SetupWidgets();
}
bool MacroInputEdit::HasInputsToSet() const
{
return !_variables._inputVariables.empty();
}
void HighligthMacroSettingsButton(bool enable);
void MacroInputEdit::SetupWidgets()
{
ClearLayout(_layout);
auto &vars = _variables._inputVariables;
if (vars.empty()) {
_layout->addWidget(new QLabel(obs_module_text(
"AdvSceneSwitcher.macroTab.inputSettings.noInputs")));
adjustSize();
updateGeometry();
return;
}
for (size_t index = 0; index < vars.size(); index++) {
auto variable = vars.at(index).lock();
QLabel *label = new QLabel(
variable
? QString::fromStdString(variable->Name())
: obs_module_text(
"AdvSceneSwitcher.macroTab.inputSettings.invalid"));
_layout->addWidget(label, index, 0);
auto valueEdit = new VariableLineEdit(this);
valueEdit->setEnabled(!!variable);
valueEdit->setText(_values.at(index));
connect(valueEdit, &VariableLineEdit::editingFinished,
[this, valueEdit, index]() {
_values[index] =
valueEdit->text().toStdString();
emit MacroInputValuesChanged(_values);
});
_layout->addWidget(valueEdit, index, 1);
}
adjustSize();
updateGeometry();
}
} // namespace advss

63
lib/macro/macro-input.hpp Normal file
View File

@@ -0,0 +1,63 @@
#pragma once
#include "list-editor.hpp"
#include "string-list.hpp"
#include "variable.hpp"
#include <QGridLayout>
#include <memory>
#include <vector>
namespace advss {
class MacroInputVariables {
public:
void Save(obs_data_t *obj) const;
void Load(obs_data_t *obj);
void SetValues(const StringList &values);
private:
std::vector<std::weak_ptr<Variable>> _inputVariables;
friend class MacroInputSelection;
friend class MacroInputEdit;
};
class MacroInputSelection final : public ListEditor {
Q_OBJECT
public:
MacroInputSelection(QWidget *parent = nullptr);
void SetInputs(const MacroInputVariables &inputs);
MacroInputVariables GetInputs() const { return _currentSelection; }
private slots:
void Add();
void Remove();
void Up();
void Down();
void Clicked(QListWidgetItem *);
private:
MacroInputVariables _currentSelection;
};
class MacroInputEdit final : public QWidget {
Q_OBJECT
public:
MacroInputEdit(QWidget *parent = nullptr);
void SetInputVariablesAndValues(const MacroInputVariables &inputs,
const StringList &values);
bool HasInputsToSet() const;
signals:
void MacroInputValuesChanged(const StringList &);
private:
void SetupWidgets();
MacroInputVariables _variables;
StringList _values;
QGridLayout *_layout;
};
} // namespace advss

View File

@@ -7,68 +7,16 @@
namespace advss {
MacroList::MacroList(QWidget *parent, bool allowDuplicates, bool reorder)
: QWidget(parent),
_list(new QListWidget()),
_add(new QPushButton()),
_remove(new QPushButton()),
_up(new QPushButton()),
_down(new QPushButton()),
_controlsLayout(new QHBoxLayout()),
_allowDuplicates(allowDuplicates),
_reorder(reorder)
: ListEditor(parent, reorder),
_allowDuplicates(allowDuplicates)
{
_add->setMaximumWidth(22);
_add->setProperty("themeID",
QVariant(QString::fromUtf8("addIconSmall")));
_add->setFlat(true);
_remove->setMaximumWidth(22);
_remove->setProperty("themeID",
QVariant(QString::fromUtf8("removeIconSmall")));
_remove->setFlat(true);
_up->setMaximumWidth(22);
_up->setProperty("themeID",
QVariant(QString::fromUtf8("upArrowIconSmall")));
_up->setFlat(true);
_down->setMaximumWidth(22);
_down->setProperty("themeID",
QVariant(QString::fromUtf8("downArrowIconSmall")));
_down->setFlat(true);
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(Add()));
QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(Remove()));
QWidget::connect(_up, SIGNAL(clicked()), this, SLOT(Up()));
QWidget::connect(_down, SIGNAL(clicked()), this, SLOT(Down()));
QWidget::connect(_list, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
this, SLOT(MacroItemClicked(QListWidgetItem *)));
QWidget::connect(window(),
SIGNAL(MacroRenamed(const QString &, const QString &)),
this,
SLOT(MacroRename(const QString &, const QString &)));
QWidget::connect(window(), SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
_controlsLayout->addWidget(_add);
_controlsLayout->addWidget(_remove);
if (reorder) {
QFrame *line = new QFrame();
line->setFrameShape(QFrame::VLine);
line->setFrameShadow(QFrame::Sunken);
_controlsLayout->addWidget(line);
}
_controlsLayout->addWidget(_up);
_controlsLayout->addWidget(_down);
_controlsLayout->addStretch();
auto mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(_list);
mainLayout->addLayout(_controlsLayout);
setLayout(mainLayout);
_up->setVisible(reorder);
_down->setVisible(reorder);
SetMacroListSize();
UpdateListSize();
}
void MacroList::SetContent(const std::vector<MacroRef> &macros)
@@ -89,7 +37,7 @@ void MacroList::SetContent(const std::vector<MacroRef> &macros)
new QListWidgetItem(listEntryName, _list);
item->setData(Qt::UserRole, listEntryName);
}
SetMacroListSize();
UpdateListSize();
}
void MacroList::AddControl(QWidget *widget)
@@ -122,7 +70,7 @@ void MacroList::MacroRemove(const QString &name)
delete _list->item(idx);
idx = FindEntry(name.toStdString());
}
SetMacroListSize();
UpdateListSize();
}
void MacroList::Add()
@@ -142,7 +90,7 @@ void MacroList::Add()
auto item =
new QListWidgetItem(QString::fromStdString(macroName), _list);
item->setData(Qt::UserRole, QString::fromStdString(macroName));
SetMacroListSize();
UpdateListSize();
emit Added(macroName);
}
@@ -154,7 +102,7 @@ void MacroList::Remove()
return;
}
delete item;
SetMacroListSize();
UpdateListSize();
emit Removed(idx);
}
@@ -178,7 +126,7 @@ void MacroList::Down()
}
}
void MacroList::MacroItemClicked(QListWidgetItem *item)
void MacroList::Clicked(QListWidgetItem *item)
{
std::string macroName;
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName);
@@ -216,10 +164,4 @@ int MacroList::FindEntry(const std::string &macro)
return idx;
}
void MacroList::SetMacroListSize()
{
SetHeightToContentHeight(_list);
adjustSize();
}
} // namespace advss

View File

@@ -1,13 +1,10 @@
#pragma once
#include "list-editor.hpp"
#include "macro-ref.hpp"
#include <QListWidget>
#include <QPushButton>
#include <QHBoxLayout>
namespace advss {
class ADVSS_EXPORT MacroList : public QWidget {
class ADVSS_EXPORT MacroList final : public ListEditor {
Q_OBJECT
public:
MacroList(QWidget *parent, bool allowDuplicates, bool reorder);
@@ -22,7 +19,7 @@ private slots:
void Remove();
void Up();
void Down();
void MacroItemClicked(QListWidgetItem *);
void Clicked(QListWidgetItem *);
signals:
void Added(const std::string &);
@@ -33,16 +30,8 @@ signals:
private:
int FindEntry(const std::string &macro);
void SetMacroListSize();
QListWidget *_list;
QPushButton *_add;
QPushButton *_remove;
QPushButton *_up;
QPushButton *_down;
QHBoxLayout *_controlsLayout;
const bool _allowDuplicates;
const bool _reorder;
};
} // namespace advss

View File

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

View File

@@ -0,0 +1,104 @@
#include "advanced-scene-switcher.hpp"
#include "macro.hpp"
#include <QShortcut>
namespace advss {
struct MacroSegmentCopyInfo {
enum class Type { NONE, CONDITION, ACTION, ELSE };
Type type = Type::NONE;
std::shared_ptr<MacroSegment> segment;
};
static MacroSegmentCopyInfo copyInfo;
void AdvSceneSwitcher::CopyMacroSegment()
{
copyInfo.segment.reset();
copyInfo.type = MacroSegmentCopyInfo::Type::NONE;
if (currentConditionIdx == -1 && currentActionIdx == -1 &&
currentElseActionIdx == -1) {
return;
}
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (currentConditionIdx != -1) {
copyInfo.type = MacroSegmentCopyInfo::Type::CONDITION;
copyInfo.segment = macro->Conditions().at(currentConditionIdx);
} else if (currentActionIdx != -1) {
copyInfo.type = MacroSegmentCopyInfo::Type::ACTION;
copyInfo.segment = macro->Actions().at(currentActionIdx);
} else if (currentElseActionIdx != -1) {
copyInfo.type = MacroSegmentCopyInfo::Type::ELSE;
copyInfo.segment =
macro->ElseActions().at(currentElseActionIdx);
} else {
assert(false);
}
}
void AdvSceneSwitcher::PasteMacroSegment()
{
if (copyInfo.type == MacroSegmentCopyInfo::Type::NONE) {
return;
}
auto macro = GetSelectedMacro();
if (!macro || !copyInfo.segment) {
return;
}
OBSDataAutoRelease data = obs_data_create();
copyInfo.segment->Save(data);
switch (copyInfo.type) {
case MacroSegmentCopyInfo::Type::CONDITION: {
const auto condition = std::static_pointer_cast<MacroCondition>(
copyInfo.segment);
auto logic = condition->GetLogicType();
if (logic > LogicType::ROOT_LAST &&
macro->Conditions().empty()) {
logic = LogicType::ROOT_NONE;
}
if (logic < LogicType::ROOT_LAST &&
!macro->Conditions().empty()) {
logic = LogicType::OR;
}
AddMacroCondition(macro.get(), macro->Conditions().size(),
copyInfo.segment->GetId(), data.Get(), logic);
break;
}
case MacroSegmentCopyInfo::Type::ACTION:
AddMacroAction(macro.get(), macro->Actions().size(),
copyInfo.segment->GetId(), data.Get());
break;
case MacroSegmentCopyInfo::Type::ELSE:
AddMacroElseAction(macro.get(), macro->ElseActions().size(),
copyInfo.segment->GetId(), data.Get());
break;
default:
break;
}
}
bool MacroSegmentIsInClipboard()
{
return copyInfo.type != MacroSegmentCopyInfo::Type::NONE;
}
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window)
{
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window);
QWidget::connect(copyShortcut, &QShortcut::activated, window,
&AdvSceneSwitcher::CopyMacroSegment);
auto pasteShortcut = new QShortcut(QKeySequence("Ctrl+V"), window);
QWidget::connect(pasteShortcut, &QShortcut::activated, window,
&AdvSceneSwitcher::PasteMacroSegment);
}
} // namespace advss

View File

@@ -0,0 +1,8 @@
namespace advss {
class AdvSceneSwitcher;
bool MacroSegmentIsInClipboard();
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
} // namespace advss

View File

@@ -130,7 +130,7 @@ void MacroSegmentList::Highlight(int idx, QColor color)
if (!widget) {
return;
}
PulseWidget(widget, color, QColor(0, 0, 0, 0), true);
HighlightWidget(widget, color, QColor(0, 0, 0, 0), true);
}
void MacroSegmentList::SetCollapsed(bool collapse)
@@ -176,7 +176,8 @@ bool MacroSegmentList::eventFilter(QObject *object, QEvent *event)
void MacroSegmentList::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
if (event->button() == Qt::LeftButton ||
event->button() == Qt::RightButton) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragPosition = GetDragIndex(event->globalPos());
#else
@@ -246,6 +247,11 @@ MacroSegmentEdit *MacroSegmentList::WidgetAt(int idx)
return static_cast<MacroSegmentEdit *>(item->widget());
}
MacroSegmentEdit *MacroSegmentList::WidgetAt(const QPoint &pos)
{
return WidgetAt(GetSegmentIndexFromPos(mapToGlobal(pos)));
}
void MacroSegmentList::HideLastDropLine()
{
if (_dropLineIdx >= 0 && _dropLineIdx < _contentLayout->count()) {
@@ -319,7 +325,7 @@ QRect MacroSegmentList::GetContentItemRectWithPadding(int idx)
return rect;
}
int MacroSegmentList::GetWidgetIdx(const QPoint &pos)
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos)
{
int idx = -1;
for (int i = 0; i < _contentLayout->count(); ++i) {
@@ -359,7 +365,7 @@ void MacroSegmentList::CheckScroll()
void MacroSegmentList::CheckDropLine(const QPoint &pos)
{
int idx = GetWidgetIdx(pos);
int idx = GetSegmentIndexFromPos(pos);
if (idx == _dragPosition) {
return;
}
@@ -410,7 +416,7 @@ bool MacroSegmentList::IsInListArea(const QPoint &pos)
int MacroSegmentList::GetDropIndex(const QPoint &pos)
{
int idx = GetWidgetIdx(pos);
int idx = GetSegmentIndexFromPos(pos);
if (idx == _dragPosition) {
return -1;
}

View File

@@ -18,6 +18,7 @@ public:
void SetHelpMsg(const QString &msg);
void SetHelpMsgVisible(bool visible);
MacroSegmentEdit *WidgetAt(int idx);
MacroSegmentEdit *WidgetAt(const QPoint &);
void Insert(int idx, MacroSegmentEdit *widget);
void Add(QWidget *widget);
void Remove(int idx);
@@ -44,7 +45,7 @@ protected:
private:
int GetDragIndex(const QPoint &);
int GetDropIndex(const QPoint &);
int GetWidgetIdx(const QPoint &);
int GetSegmentIndexFromPos(const QPoint &);
void CheckScroll();
void CheckDropLine(const QPoint &);
bool IsInListArea(const QPoint &);

View File

@@ -175,7 +175,7 @@ void MacroSegmentSelection::MarkSelectedSegment()
index - 1, QColor(Qt::lightGray));
}
PulseWidget(this, QColor(Qt::lightGray), QColor(0, 0, 0, 0), true);
HighlightWidget(this, QColor(Qt::lightGray), QColor(0, 0, 0, 0), true);
}
} // namespace advss

View File

@@ -4,7 +4,6 @@
#include "section.hpp"
#include "ui-helpers.hpp"
#include <obs.hpp>
#include <QApplication>
#include <QEvent>
#include <QLabel>
@@ -14,19 +13,37 @@
namespace advss {
MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue)
: _macro(m), _supportsVariableValue(supportsVariableValue)
: _macro(m),
_supportsVariableValue(supportsVariableValue)
{
}
bool MacroSegment::Save(obs_data_t *obj) const
{
obs_data_set_bool(obj, "collapsed", static_cast<int>(_collapsed));
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "collapsed", _collapsed);
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
obs_data_set_string(data, "customLabel", _customLabel.c_str());
obs_data_set_obj(obj, "segmentSettings", data);
return true;
}
bool MacroSegment::Load(obs_data_t *obj)
{
_collapsed = obs_data_get_bool(obj, "collapsed");
// TODO: remove this fallback at some point
if (obs_data_has_user_value(obj, "segmentSettings")) {
OBSDataAutoRelease data =
obs_data_get_obj(obj, "segmentSettings");
_collapsed = obs_data_get_bool(data, "collapsed");
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
_customLabel = obs_data_get_string(data, "customLabel");
} else {
_collapsed = obs_data_get_bool(obj, "collapsed");
_useCustomLabel = false;
_customLabel = obs_module_text(
"AdvSceneSwitcher.macroTab.segment.defaultCustomLabel");
}
ClearAvailableTempvars();
return true;
}
@@ -42,12 +59,12 @@ std::string MacroSegment::GetShortDesc() const
return "";
}
void MacroSegment::SetHighlight()
void MacroSegment::EnableHighlight()
{
_highlight = true;
}
bool MacroSegment::Highlight()
bool MacroSegment::GetHighlightAndReset()
{
if (_highlight) {
_highlight = false;
@@ -177,7 +194,7 @@ void DecrementVariableRef(MacroSegment *segment)
segment->_variableRefs--;
}
MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent)
MacroSegmentEdit::MacroSegmentEdit(QWidget *parent)
: QWidget(parent),
_section(new Section(300)),
_headerInfo(new QLabel()),
@@ -186,8 +203,7 @@ MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent)
_noBorderframe(new QFrame),
_borderFrame(new QFrame),
_dropLineAbove(new QFrame),
_dropLineBelow(new QFrame),
_showHighlight(highlight)
_dropLineBelow(new QFrame)
{
_dropLineAbove->setLineWidth(3);
_dropLineAbove->setFixedHeight(11);
@@ -265,10 +281,6 @@ MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent)
// Enable dragging while clicking on the header text
_headerInfo->installEventFilter(this);
_timer.setInterval(1500);
connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight()));
_timer.start();
}
bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
@@ -296,6 +308,12 @@ bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
void MacroSegmentEdit::HeaderInfoChanged(const QString &text)
{
if (Data() && Data()->GetUseCustomLabel()) {
_headerInfo->show();
_headerInfo->setText(
QString::fromStdString(Data()->GetCustomLabel()));
return;
}
_headerInfo->setVisible(!text.isEmpty());
_headerInfo->setText(text);
}
@@ -307,22 +325,6 @@ void MacroSegmentEdit::Collapsed(bool collapsed)
}
}
void MacroSegmentEdit::Highlight()
{
if (!Data()) {
return;
}
if (_showHighlight && Data()->Highlight()) {
PulseWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
}
}
void MacroSegmentEdit::EnableHighlight(bool value)
{
_showHighlight = value;
}
void MacroSegmentEdit::SetFocusPolicyOfWidgets()
{
QList<QWidget *> widgets = this->findChildren<QWidget *>();

View File

@@ -11,7 +11,7 @@
#include <QFrame>
#include <QVBoxLayout>
#include <QTimer>
#include <obs.hpp>
#include <obs-data.h>
class QLabel;
@@ -28,13 +28,17 @@ public:
int GetIndex() const { return _idx; }
void SetCollapsed(bool collapsed) { _collapsed = collapsed; }
bool GetCollapsed() const { return _collapsed; }
void SetUseCustomLabel(bool enable) { _useCustomLabel = enable; }
bool GetUseCustomLabel() const { return _useCustomLabel; }
void SetCustomLabel(const std::string &label) { _customLabel = label; }
std::string GetCustomLabel() const { return _customLabel; }
virtual bool Save(obs_data_t *obj) const = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual bool PostLoad();
virtual std::string GetShortDesc() const;
virtual std::string GetId() const = 0;
void SetHighlight();
bool Highlight();
void EnableHighlight();
bool GetHighlightAndReset();
virtual std::string GetVariableValue() const;
protected:
@@ -63,6 +67,11 @@ private:
bool _highlight = false;
bool _collapsed = false;
// Custom header labels
bool _useCustomLabel = false;
std::string _customLabel = obs_module_text(
"AdvSceneSwitcher.macroTab.segment.defaultCustomLabel");
// Variable helpers
const bool _supportsVariableValue = false;
int _variableRefs = 0;
@@ -78,7 +87,7 @@ class MacroSegmentEdit : public QWidget {
Q_OBJECT
public:
MacroSegmentEdit(bool highlight, QWidget *parent = nullptr);
MacroSegmentEdit(QWidget *parent = nullptr);
// Use this function to avoid accidental edits when scrolling through
// list of actions and conditions
void SetFocusPolicyOfWidgets();
@@ -86,11 +95,11 @@ public:
void SetSelected(bool);
virtual std::shared_ptr<MacroSegment> Data() const = 0;
protected slots:
public slots:
void HeaderInfoChanged(const QString &);
protected slots:
void Collapsed(bool);
void Highlight();
void EnableHighlight(bool);
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
@@ -116,8 +125,8 @@ private:
void ShowDropLine(DropLineState);
// The reason for using two separate frame widget each with their own
// stylesheet and changing their visibility vs. using a single frame
// The reason for using two separate frame widgets, each with their own
// stylesheet, and changing their visibility vs. using a single frame
// and changing the stylesheet at runtime is that the operation of
// adjusting the stylesheet is very expensive and can take multiple
// hundred milliseconds per widget.
@@ -132,9 +141,6 @@ private:
QFrame *_dropLineAbove;
QFrame *_dropLineBelow;
bool _showHighlight;
QTimer _timer;
friend class MacroSegmentList;
};

View File

@@ -95,7 +95,9 @@ void MacroSelection::MacroRename(const QString &oldName, const QString &newName)
}
}
MacroSelectionDialog::MacroSelectionDialog(QWidget *)
MacroSelectionDialog::MacroSelectionDialog(QWidget *parent)
: QDialog(parent),
_macroSelection(new MacroSelection(this))
{
setModal(true);
setWindowModality(Qt::WindowModality::ApplicationModal);
@@ -103,21 +105,20 @@ MacroSelectionDialog::MacroSelectionDialog(QWidget *)
setMinimumWidth(350);
setMinimumHeight(70);
QDialogButtonBox *buttonbox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
auto buttonbox = new QDialogButtonBox(QDialogButtonBox::Ok |
QDialogButtonBox::Cancel);
buttonbox->setCenterButtons(true);
connect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
_macroSelection = new MacroSelection(window());
auto *selectionLayout = new QHBoxLayout;
auto selectionLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{macroSelection}}", _macroSelection},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.askForMacro"),
selectionLayout, widgetPlaceholders);
auto *layout = new QVBoxLayout();
auto layout = new QVBoxLayout();
layout->addLayout(selectionLayout);
layout->addWidget(buttonbox);
setLayout(layout);
@@ -125,7 +126,7 @@ MacroSelectionDialog::MacroSelectionDialog(QWidget *)
bool MacroSelectionDialog::AskForMacro(QWidget *parent, std::string &macroName)
{
MacroSelectionDialog dialog(parent);
MacroSelectionDialog dialog(GetSettingsWindow());
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
if (dialog.exec() != DialogCode::Accepted) {

View File

@@ -1,4 +1,4 @@
#include "macro-properties.hpp"
#include "macro-settings.hpp"
#include "layout-helpers.hpp"
#include "obs-module-helper.hpp"
#include "macro.hpp"
@@ -10,9 +10,9 @@
namespace advss {
static MacroProperties macroProperties;
static GlobalMacroSettings macroSettings;
void MacroProperties::Save(obs_data_t *obj) const
void GlobalMacroSettings::Save(obs_data_t *obj) const
{
auto data = obs_data_create();
obs_data_set_bool(data, "highlightExecuted", _highlightExecuted);
@@ -20,13 +20,16 @@ void MacroProperties::Save(obs_data_t *obj) const
obs_data_set_bool(data, "highlightActions", _highlightActions);
obs_data_set_bool(data, "newMacroRegisterHotkey",
_newMacroRegisterHotkeys);
obs_data_set_obj(obj, "macroProperties", data);
obs_data_set_obj(obj, "macroSettings", data);
obs_data_release(data);
}
void MacroProperties::Load(obs_data_t *obj)
void GlobalMacroSettings::Load(obs_data_t *obj)
{
auto data = obs_data_get_obj(obj, "macroProperties");
auto data = obs_data_get_obj(
obj, obs_data_has_user_value(obj, "macroProperties")
? "macroProperties"
: "macroSettings");
obs_data_set_default_bool(data, "highlightExecuted", true);
obs_data_set_default_bool(data, "highlightConditions", true);
obs_data_set_default_bool(data, "highlightActions", true);
@@ -38,9 +41,9 @@ void MacroProperties::Load(obs_data_t *obj)
obs_data_release(data);
}
MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
const MacroProperties &prop,
Macro *macro)
MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
const GlobalMacroSettings &settings,
Macro *macro)
: QDialog(parent),
_executed(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightExecutedMacros"))),
@@ -54,6 +57,9 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
_currentSkipOnStartup(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
_currentStopActionsIfNotDone(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone"))),
_currentInputs(new MacroInputSelection()),
_currentMacroRegisterDock(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentRegisterDock"))),
_currentMacroDockAddRunButton(new QCheckBox(obs_module_text(
@@ -96,8 +102,19 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
auto generalLayout = new QVBoxLayout;
generalLayout->addWidget(_currentSkipOnStartup);
generalLayout->addWidget(_currentStopActionsIfNotDone);
generalOptions->setLayout(generalLayout);
auto inputOptions = new QGroupBox(
obs_module_text("AdvSceneSwitcher.macroTab.inputSettings"));
auto description = new QLabel(obs_module_text(
"AdvSceneSwitcher.macroTab.inputSettings.description"));
description->setWordWrap(true);
auto inputLayout = new QVBoxLayout();
inputLayout->addWidget(description);
inputLayout->addWidget(_currentInputs);
inputOptions->setLayout(inputLayout);
int row = 0;
_dockLayout->addWidget(_currentMacroRegisterDock, row, 1, 1, 2);
row++;
@@ -154,13 +171,13 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(_currentMacroRegisterDock, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::DockEnableChanged);
&MacroSettingsDialog::DockEnableChanged);
connect(_currentMacroDockAddRunButton, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::RunButtonEnableChanged);
&MacroSettingsDialog::RunButtonEnableChanged);
connect(_currentMacroDockAddPauseButton, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::PauseButtonEnableChanged);
&MacroSettingsDialog::PauseButtonEnableChanged);
connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::StatusLabelEnableChanged);
&MacroSettingsDialog::StatusLabelEnableChanged);
auto scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
@@ -171,6 +188,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
layout->addWidget(highlightOptions);
layout->addWidget(hotkeyOptions);
layout->addWidget(generalOptions);
layout->addWidget(inputOptions);
layout->addWidget(_dockOptions);
layout->setContentsMargins(0, 0, 0, 0);
scrollArea->setWidget(contentWidget);
@@ -180,18 +198,23 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
dialogLayout->addWidget(buttonbox);
setLayout(dialogLayout);
_executed->setChecked(prop._highlightExecuted);
_conditions->setChecked(prop._highlightConditions);
_actions->setChecked(prop._highlightActions);
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys);
_executed->setChecked(settings._highlightExecuted);
_conditions->setChecked(settings._highlightConditions);
_actions->setChecked(settings._highlightActions);
_newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
if (!macro || macro->IsGroup()) {
hotkeyOptions->hide();
generalOptions->hide();
inputOptions->hide();
_dockOptions->hide();
return;
}
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
_currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone());
_currentInputs->SetInputs(macro->GetInputVariables());
const bool dockEnabled = macro->DockEnabled();
_currentMacroRegisterDock->setChecked(dockEnabled);
_currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton());
@@ -239,7 +262,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
20);
}
void MacroPropertiesDialog::DockEnableChanged(int enabled)
void MacroSettingsDialog::DockEnableChanged(int enabled)
{
_currentMacroDockAddRunButton->setVisible(enabled);
_currentMacroDockAddPauseButton->setVisible(enabled);
@@ -260,40 +283,41 @@ void MacroPropertiesDialog::DockEnableChanged(int enabled)
SetGridLayoutRowVisible(
_dockLayout, _conditionsFalseTextRow,
enabled && _currentMacroDockAddStatusLabel->isChecked());
Resize();
}
void MacroPropertiesDialog::RunButtonEnableChanged(int enabled)
void MacroSettingsDialog::RunButtonEnableChanged(int enabled)
{
SetGridLayoutRowVisible(_dockLayout, _runButtonTextRow, enabled);
Resize();
}
void MacroPropertiesDialog::PauseButtonEnableChanged(int enabled)
void MacroSettingsDialog::PauseButtonEnableChanged(int enabled)
{
SetGridLayoutRowVisible(_dockLayout, _pauseButtonTextRow, enabled);
SetGridLayoutRowVisible(_dockLayout, _unpauseButtonTextRow, enabled);
Resize();
}
void MacroPropertiesDialog::StatusLabelEnableChanged(int enabled)
void MacroSettingsDialog::StatusLabelEnableChanged(int enabled)
{
SetGridLayoutRowVisible(_dockLayout, _conditionsTrueTextRow, enabled);
SetGridLayoutRowVisible(_dockLayout, _conditionsFalseTextRow, enabled);
Resize();
}
void MacroPropertiesDialog::Resize()
void MacroSettingsDialog::Resize()
{
_dockOptions->adjustSize();
_dockOptions->updateGeometry();
}
bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
MacroProperties &userInput,
Macro *macro)
bool MacroSettingsDialog::AskForSettings(QWidget *parent,
GlobalMacroSettings &userInput,
Macro *macro)
{
MacroPropertiesDialog dialog(parent, userInput, macro);
MacroSettingsDialog dialog(parent, userInput, macro);
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
if (dialog.exec() != DialogCode::Accepted) {
return false;
@@ -310,6 +334,8 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
macro->EnablePauseHotkeys(
dialog._currentMacroRegisterHotkeys->isChecked());
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
macro->SetStopActionsIfNotDone(
dialog._currentStopActionsIfNotDone->isChecked());
macro->EnableDock(dialog._currentMacroRegisterDock->isChecked());
macro->SetDockHasRunButton(
dialog._currentMacroDockAddRunButton->isChecked());
@@ -328,22 +354,23 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
dialog._conditionsTrueStatusText->text().toStdString());
macro->SetConditionsFalseStatusText(
dialog._conditionsFalseStatusText->text().toStdString());
macro->SetInputVariables(dialog._currentInputs->GetInputs());
return true;
}
MacroProperties &GetGlobalMacroProperties()
GlobalMacroSettings &GetGlobalMacroSettings()
{
return macroProperties;
return macroSettings;
}
void SaveGlobalMacroProperties(obs_data_t *obj)
void SaveGlobalMacroSettings(obs_data_t *obj)
{
macroProperties.Save(obj);
macroSettings.Save(obj);
}
void LoadGlobalMacroProperties(obs_data_t *obj)
void LoadGlobalMacroSettings(obs_data_t *obj)
{
macroProperties.Load(obj);
macroSettings.Load(obj);
}
} // namespace advss

View File

@@ -1,5 +1,6 @@
#pragma once
#include "variable-line-edit.hpp"
#include "macro-input.hpp"
#include <QWidget>
#include <QDialog>
@@ -13,7 +14,7 @@ namespace advss {
class Macro;
class MacroProperties {
class GlobalMacroSettings {
public:
void Save(obs_data_t *obj) const;
void Load(obs_data_t *obj);
@@ -25,13 +26,14 @@ public:
};
// Dialog for configuring global and individual macro specific settings
class MacroPropertiesDialog : public QDialog {
class MacroSettingsDialog : public QDialog {
Q_OBJECT
public:
MacroPropertiesDialog(QWidget *parent, const MacroProperties &,
Macro *macro);
static bool AskForSettings(QWidget *parent, MacroProperties &userInput,
MacroSettingsDialog(QWidget *parent, const GlobalMacroSettings &,
Macro *macro);
static bool AskForSettings(QWidget *parent,
GlobalMacroSettings &userInput,
Macro *macro);
private slots:
void DockEnableChanged(int);
@@ -49,6 +51,8 @@ private:
// Current macro specific settings
QCheckBox *_currentMacroRegisterHotkeys;
QCheckBox *_currentSkipOnStartup;
QCheckBox *_currentStopActionsIfNotDone;
MacroInputSelection *_currentInputs;
QCheckBox *_currentMacroRegisterDock;
QCheckBox *_currentMacroDockAddRunButton;
QCheckBox *_currentMacroDockAddPauseButton;
@@ -69,8 +73,8 @@ private:
int _conditionsFalseTextRow = -1;
};
MacroProperties &GetGlobalMacroProperties();
void SaveGlobalMacroProperties(obs_data_t *obj);
void LoadGlobalMacroProperties(obs_data_t *obj);
GlobalMacroSettings &GetGlobalMacroSettings();
void SaveGlobalMacroSettings(obs_data_t *obj);
void LoadGlobalMacroSettings(obs_data_t *obj);
} // namespace advss

View File

@@ -1,8 +1,10 @@
#include "advanced-scene-switcher.hpp"
#include "action-queue.hpp"
#include "macro-action-edit.hpp"
#include "macro-condition-edit.hpp"
#include "macro-export-import-dialog.hpp"
#include "macro-properties.hpp"
#include "macro-settings.hpp"
#include "macro-segment-copy-paste.hpp"
#include "macro-tree.hpp"
#include "macro.hpp"
#include "math-helpers.hpp"
@@ -13,6 +15,7 @@
#include "utility.hpp"
#include "version.h"
#include <obs-frontend-api.h>
#include <QColor>
#include <QGraphicsOpacityEffect>
#include <QMenu>
@@ -20,7 +23,7 @@
namespace advss {
static QMetaObject::Connection addPulse;
static QObject *addPulse = nullptr;
static QTimer onChangeHighlightTimer;
static bool macroNameExists(const std::string &name)
@@ -28,6 +31,25 @@ static bool macroNameExists(const std::string &name)
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,
std::string &name, std::string format)
{
@@ -46,7 +68,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
placeHolderText = fmt.arg(++i);
}
bool accepted = AdvSSNameDialog::AskForName(
bool accepted = NameDialog::AskForName(
this, obs_module_text("AdvSceneSwitcher.macroTab.add"),
obs_module_text("AdvSceneSwitcher.macroTab.name"), name,
placeHolderText);
@@ -59,14 +81,12 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
return false;
}
if (macroNameExists(name)) {
DisplayMessage(
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
if (!newMacroNameIsValid(name)) {
return false;
}
res = std::make_shared<Macro>(
name, GetGlobalMacroProperties()._newMacroRegisterHotkeys);
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys);
return true;
}
@@ -79,7 +99,8 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
}
ui->macros->Add(newMacro);
ui->macroAdd->disconnect(addPulse);
addPulse->deleteLater();
addPulse = nullptr;
emit MacroAdded(QString::fromStdString(name));
}
@@ -92,7 +113,7 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> &macro)
auto name = QString::fromStdString(macro->Name());
if (macro->IsGroup() && macro->GroupSize() > 0) {
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.macroTab.groupDeleteConfirm");
"AdvSceneSwitcher.macroTab.removeGroupPopup.text");
if (!DisplayMessage(deleteWarning.arg(name), true)) {
return;
}
@@ -102,6 +123,41 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> &macro)
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,
const QString &name)
{
@@ -115,32 +171,7 @@ void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> &macro,
void AdvSceneSwitcher::on_macroRemove_clicked()
{
auto macros = GetSelectedMacros();
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);
}
RemoveSelectedMacros();
}
void AdvSceneSwitcher::on_macroUp_clicked()
@@ -161,32 +192,26 @@ void AdvSceneSwitcher::on_macroDown_clicked()
ui->macros->Down(macro);
}
static bool newMacroNameValid(const std::string &name)
{
if (!macroNameExists(name)) {
return true;
}
DisplayMessage(obs_module_text("AdvSceneSwitcher.macroTab.exists"));
return false;
}
void AdvSceneSwitcher::RenameCurrentMacro()
void AdvSceneSwitcher::RenameSelectedMacro()
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
std::string oldName = macro->Name();
std::string name;
if (!AdvSSNameDialog::AskForName(
if (!NameDialog::AskForName(
this, obs_module_text("AdvSceneSwitcher.windowTitle"),
obs_module_text("AdvSceneSwitcher.item.newName"), name,
QString::fromStdString(oldName))) {
return;
}
if (name.empty() || name == oldName || !newMacroNameValid(name)) {
if (name.empty() || name == oldName || !newMacroNameIsValid(name)) {
return;
}
RenameMacro(macro, QString::fromStdString(name));
const QSignalBlocker b(ui->macroName);
@@ -247,6 +272,7 @@ void AdvSceneSwitcher::ExportMacros()
}
obs_data_set_array(data, "macros", macroArray);
SaveVariables(data);
SaveActionQueues(data);
obs_data_set_string(data, "version", g_GIT_TAG);
auto json = obs_data_get_json(data);
QString exportString(json);
@@ -346,8 +372,11 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
}
std::string newName;
bool accepted = AdvSSNameDialog::AskForName(
this, obs_module_text("AdvSceneSwitcher.macroTab.add"),
bool accepted = NameDialog::AskForName(
this,
obs_module_text(macro->IsGroup()
? "AdvSceneSwitcher.macroTab.addGroup"
: "AdvSceneSwitcher.macroTab.add"),
obs_module_text("AdvSceneSwitcher.macroTab.name"), newName,
placeHolderText);
@@ -359,9 +388,7 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
return false;
}
if (macroNameExists(newName)) {
DisplayMessage(
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
if (!newMacroNameIsValid(newName)) {
return ResolveMacroImportNameConflict(macro);
}
@@ -369,26 +396,6 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
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()
{
QString json;
@@ -403,7 +410,8 @@ void AdvSceneSwitcher::ImportMacros()
ImportMacros();
return;
}
importVariables(data);
ImportVariables(data);
ImportQueues(data);
auto version = obs_data_get_string(data, "version");
if (strcmp(version, g_GIT_TAG) != 0) {
@@ -424,6 +432,7 @@ void AdvSceneSwitcher::ImportMacros()
OBSDataAutoRelease array_obj = obs_data_array_item(array, i);
auto macro = std::make_shared<Macro>();
macro->Load(array_obj);
RunPostLoadSteps();
if (macroNameExists(macro->Name()) &&
!ResolveMacroImportNameConflict(macro)) {
@@ -454,7 +463,7 @@ void AdvSceneSwitcher::ImportMacros()
RunPostLoadSteps();
ui->macros->Reset(GetMacros(),
GetGlobalMacroProperties()._highlightExecuted);
GetGlobalMacroSettings()._highlightExecuted);
}
void AdvSceneSwitcher::on_macroName_editingFinished()
@@ -468,7 +477,7 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
QString oldName = QString::fromStdString(macro->Name());
if (newName.isEmpty() || newName == oldName ||
!newMacroNameValid(newName.toStdString())) {
!newMacroNameIsValid(newName.toStdString())) {
ui->macroName->setText(oldName);
return;
}
@@ -729,12 +738,13 @@ void AdvSceneSwitcher::MacroSelectionChanged()
return;
}
SetEditMacro(*macro);
obs_frontend_save();
}
void AdvSceneSwitcher::HighlightOnChange()
{
if (!GetGlobalMacroProperties()._highlightActions &&
!GetGlobalMacroProperties()._highlightExecuted) {
if (!GetGlobalMacroSettings()._highlightActions &&
!GetGlobalMacroSettings()._highlightExecuted) {
return;
}
@@ -744,23 +754,21 @@ void AdvSceneSwitcher::HighlightOnChange()
}
if (macro->OnChangePreventedActionsRecently()) {
PulseWidget(ui->runMacroOnChange, Qt::yellow, Qt::transparent,
true);
HighlightWidget(ui->runMacroOnChange, Qt::yellow,
Qt::transparent, true);
}
}
void AdvSceneSwitcher::on_macroProperties_clicked()
void AdvSceneSwitcher::on_macroSettings_clicked()
{
MacroProperties prop = GetGlobalMacroProperties();
bool accepted = MacroPropertiesDialog::AskForSettings(
GlobalMacroSettings prop = GetGlobalMacroSettings();
bool accepted = MacroSettingsDialog::AskForSettings(
this, prop, GetSelectedMacro().get());
if (!accepted) {
return;
}
GetGlobalMacroProperties() = prop;
GetGlobalMacroSettings() = prop;
emit HighlightMacrosChanged(prop._highlightExecuted);
emit HighlightActionsChanged(prop._highlightActions);
emit HighlightConditionsChanged(prop._highlightConditions);
}
static void moveControlsToSplitter(QSplitter *splitter, int idx,
@@ -775,7 +783,7 @@ static void moveControlsToSplitter(QSplitter *splitter, int idx,
splitter->setStyleSheet("QSplitter::handle {background: transparent;}");
}
bool shouldRestoreSplitter(const QList<int> &pos)
static bool shouldRestoreSplitter(const QList<int> &pos)
{
if (pos.size() == 0) {
return false;
@@ -789,15 +797,46 @@ bool shouldRestoreSplitter(const QList<int> &pos)
return true;
}
static void runSegmentHighligtChecksHelper(MacroSegmentList *list)
{
MacroSegmentEdit *widget = nullptr;
for (int i = 0; (widget = list->WidgetAt(i)); i++) {
if (widget->Data() && widget->Data()->GetHighlightAndReset()) {
list->Highlight(i);
}
}
}
static void runSegmentHighligtChecks(AdvSceneSwitcher *ss)
{
if (!ss || !HighlightUIElementsEnabled()) {
return;
}
auto macro = ss->GetSelectedMacro();
if (!macro) {
return;
}
const auto &settings = GetGlobalMacroSettings();
if (settings._highlightConditions) {
runSegmentHighligtChecksHelper(ss->ui->conditionsList);
}
if (settings._highlightActions) {
runSegmentHighligtChecksHelper(ss->ui->actionsList);
runSegmentHighligtChecksHelper(ss->ui->elseActionsList);
}
}
void AdvSceneSwitcher::SetupMacroTab()
{
ui->macroElseActions->installEventFilter(this);
ui->macros->installEventFilter(this);
if (GetMacros().size() == 0 && !switcher->disableHints) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
addPulse = HighlightWidget(ui->macroAdd, QColor(Qt::green));
}
ui->macros->Reset(GetMacros(),
GetGlobalMacroProperties()._highlightExecuted);
GetGlobalMacroSettings()._highlightExecuted);
connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this,
SLOT(MacroSelectionAboutToChange()));
connect(ui->macros, SIGNAL(MacroSelectionChanged()), this,
@@ -882,6 +921,14 @@ void AdvSceneSwitcher::SetupMacroTab()
switcher->macroListMacroEditSplitterPosition);
}
}
SetupSegmentCopyPasteShortcutHandlers(this);
// Macro segment highlight
auto timer = new QTimer(this);
connect(timer, &QTimer::timeout,
[this]() { runSegmentHighligtChecks(this); });
timer->start(1500);
}
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
@@ -915,7 +962,7 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
auto rename = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.rename"), this,
&AdvSceneSwitcher::RenameCurrentMacro);
&AdvSceneSwitcher::RenameSelectedMacro);
rename->setEnabled(ui->macros->SingleItemSelected());
auto remove = menu.addAction(
@@ -934,49 +981,107 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
menu.exec(globalPos);
}
static void handleCustomLabelChange(MacroSegmentEdit *segmentEdit,
QAction *contextMenuOption)
{
bool enable = contextMenuOption->isChecked();
auto segment = segmentEdit->Data();
segment->SetUseCustomLabel(enable);
if (!enable) {
segmentEdit->HeaderInfoChanged(
QString::fromStdString(segment->GetShortDesc()));
return;
}
std::string label;
bool accepted = NameDialog::AskForName(
GetSettingsWindow(),
obs_module_text(
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
"", label, QString::fromStdString(segment->GetCustomLabel()));
if (!accepted) {
segment->SetUseCustomLabel(false);
return;
}
segment->SetCustomLabel(label);
segmentEdit->HeaderInfoChanged("");
}
static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
std::function<void(AdvSceneSwitcher *)> expand,
std::function<void(AdvSceneSwitcher *)> collapse,
std::function<void(AdvSceneSwitcher *)> maximize,
std::function<void(AdvSceneSwitcher *)> minimize)
std::function<void(AdvSceneSwitcher *)> minimize,
MacroSegmentList *list)
{
QMenu menu;
auto segmentEdit = list->WidgetAt(pos);
auto copy = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss,
[ss]() { ss->CopyMacroSegment(); });
copy->setEnabled(!!segmentEdit);
auto paste = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.segment.paste"), ss,
[ss]() { ss->PasteMacroSegment(); });
paste->setEnabled(MacroSegmentIsInClipboard());
menu.addSeparator();
if (segmentEdit) {
auto customLabel = menu.addAction(obs_module_text(
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
customLabel->setCheckable(true);
auto segment = segmentEdit ? segmentEdit->Data() : nullptr;
customLabel->setChecked(segment &&
segment->GetUseCustomLabel());
QWidget::connect(customLabel, &QAction::triggered,
std::bind(handleCustomLabelChange, segmentEdit,
customLabel));
}
menu.addSeparator();
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
ss, [ss, expand]() { expand(ss); });
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"),
ss, [ss, collapse]() { collapse(ss); });
menu.addSeparator();
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
ss, [ss, maximize]() { maximize(ss); });
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
ss, [ss, minimize]() { minimize(ss); });
menu.exec(pos);
menu.exec(list->mapToGlobal(pos));
}
void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, ui->actionsList->mapToGlobal(pos),
&AdvSceneSwitcher::ExpandAllActions,
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllActions,
&AdvSceneSwitcher::CollapseAllActions,
&AdvSceneSwitcher::MaximizeActions,
&AdvSceneSwitcher::MinimizeActions);
&AdvSceneSwitcher::MinimizeActions, ui->actionsList);
}
void AdvSceneSwitcher::ShowMacroElseActionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, ui->elseActionsList->mapToGlobal(pos),
&AdvSceneSwitcher::ExpandAllElseActions,
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllElseActions,
&AdvSceneSwitcher::CollapseAllElseActions,
&AdvSceneSwitcher::MaximizeElseActions,
&AdvSceneSwitcher::MinimizeElseActions);
&AdvSceneSwitcher::MinimizeElseActions,
ui->elseActionsList);
}
void AdvSceneSwitcher::ShowMacroConditionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, ui->conditionsList->mapToGlobal(pos),
&AdvSceneSwitcher::ExpandAllConditions,
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllConditions,
&AdvSceneSwitcher::CollapseAllConditions,
&AdvSceneSwitcher::MaximizeConditions,
&AdvSceneSwitcher::MinimizeConditions);
&AdvSceneSwitcher::MinimizeConditions,
ui->conditionsList);
}
void AdvSceneSwitcher::CopyMacro()
@@ -1002,7 +1107,8 @@ void AdvSceneSwitcher::CopyMacro()
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
ui->macros->Add(newMacro, macro);
ui->macroAdd->disconnect(addPulse);
addPulse->deleteLater();
addPulse = nullptr;
emit MacroAdded(QString::fromStdString(name));
}
@@ -1294,58 +1400,47 @@ static void fade(QWidget *widget, bool fadeOut)
animation->start(QPropertyAnimation::DeleteWhenStopped);
}
void AdvSceneSwitcher::FadeOutActionControls()
void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
{
fade(ui->actionAdd, true);
fade(ui->actionRemove, true);
fade(ui->actionTop, true);
fade(ui->actionUp, true);
fade(ui->actionDown, true);
fade(ui->actionBottom, true);
}
void AdvSceneSwitcher::FadeOutConditionControls()
{
fade(ui->conditionAdd, true);
fade(ui->conditionRemove, true);
fade(ui->conditionTop, true);
fade(ui->conditionUp, true);
fade(ui->conditionDown, true);
fade(ui->conditionBottom, true);
}
void AdvSceneSwitcher::ResetOpacityActionControls()
{
fade(ui->actionAdd, false);
fade(ui->actionRemove, false);
fade(ui->actionTop, false);
fade(ui->actionUp, false);
fade(ui->actionDown, false);
fade(ui->actionBottom, false);
}
void AdvSceneSwitcher::ResetOpacityConditionControls()
{
fade(ui->conditionAdd, false);
fade(ui->conditionRemove, false);
fade(ui->conditionTop, false);
fade(ui->conditionUp, false);
fade(ui->conditionDown, false);
fade(ui->conditionBottom, false);
for (const auto &widget : widgets) {
fade(widget, fadeOut);
}
}
void AdvSceneSwitcher::HighlightControls()
{
if ((currentActionIdx == -1 && currentConditionIdx == -1) ||
(currentActionIdx != -1 && currentConditionIdx != -1)) {
ResetOpacityActionControls();
ResetOpacityConditionControls();
const std::vector<QWidget *> conditionControls{
ui->conditionAdd, ui->conditionRemove, ui->conditionTop,
ui->conditionUp, ui->conditionDown, ui->conditionBottom,
};
const std::vector<QWidget *> actionControls{
ui->actionAdd, ui->actionRemove, ui->actionTop,
ui->actionUp, ui->actionDown, ui->actionBottom,
};
const std::vector<QWidget *> elseActionControls{
ui->elseActionAdd, ui->elseActionRemove, ui->elseActionTop,
ui->elseActionUp, ui->elseActionDown, ui->elseActionBottom,
};
if ((currentActionIdx == -1 && currentConditionIdx == -1 &&
currentElseActionIdx == -1)) {
fadeWidgets(conditionControls, false);
fadeWidgets(actionControls, false);
fadeWidgets(elseActionControls, false);
} else if (currentConditionIdx != -1) {
fadeWidgets(conditionControls, false);
fadeWidgets(actionControls, true);
fadeWidgets(elseActionControls, true);
} else if (currentActionIdx != -1) {
FadeOutConditionControls();
ResetOpacityActionControls();
fadeWidgets(conditionControls, true);
fadeWidgets(actionControls, false);
fadeWidgets(elseActionControls, true);
} else if (currentElseActionIdx != -1) {
fadeWidgets(conditionControls, true);
fadeWidgets(actionControls, true);
fadeWidgets(elseActionControls, false);
} else {
FadeOutActionControls();
ResetOpacityConditionControls();
assert(false);
}
}

View File

@@ -108,7 +108,7 @@ void MacroTreeItem::HighlightIfExecuted()
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
_macro->WasExecutedSince(_lastHighlightCheckTime)) {
PulseWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
HighlightWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
}
_lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
}
@@ -173,6 +173,7 @@ void MacroTreeItem::Update(bool force)
} else if (_type == Type::Group) {
_expand = new SourceTreeSubItemCheckBox();
_expand->setProperty("sourceTreeSubItem", true);
_expand->setSizePolicy(QSizePolicy::Maximum,
QSizePolicy::Maximum);
_expand->setMaximumSize(10, 16);

View File

@@ -17,6 +17,7 @@ class QSpacerItem;
class QHBoxLayout;
// 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 {
Q_OBJECT
};

View File

@@ -101,7 +101,7 @@ void Macro::PrepareMoveToGroup(std::shared_ptr<Macro> group,
}
}
bool Macro::CeckMatch()
bool Macro::CeckMatch(bool ignorePause)
{
if (_isGroup) {
return false;
@@ -109,7 +109,7 @@ bool Macro::CeckMatch()
_matched = false;
for (auto &c : _conditions) {
if (_paused) {
if (_paused && !ignorePause) {
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
return false;
}
@@ -137,37 +137,37 @@ bool Macro::CeckMatch()
case LogicType::AND:
_matched = _matched && cond;
if (cond) {
c->SetHighlight();
c->EnableHighlight();
}
break;
case LogicType::OR:
_matched = _matched || cond;
if (cond) {
c->SetHighlight();
c->EnableHighlight();
}
break;
case LogicType::AND_NOT:
_matched = _matched && !cond;
if (!cond) {
c->SetHighlight();
c->EnableHighlight();
}
break;
case LogicType::OR_NOT:
_matched = _matched || !cond;
if (!cond) {
c->SetHighlight();
c->EnableHighlight();
}
break;
case LogicType::ROOT_NONE:
_matched = cond;
if (cond) {
c->SetHighlight();
c->EnableHighlight();
}
break;
case LogicType::ROOT_NOT:
_matched = !cond;
if (!cond) {
c->SetHighlight();
c->EnableHighlight();
}
break;
default:
@@ -185,6 +185,7 @@ bool Macro::CeckMatch()
if (!_conditionSateChanged && _performActionsOnChange) {
_onPreventedActionExecution = true;
}
_lastMatched = _matched;
_lastCheckTime = std::chrono::high_resolution_clock::now();
return _matched;
@@ -193,9 +194,17 @@ bool Macro::CeckMatch()
bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
{
if (!_done) {
vblog(LOG_INFO, "macro %s already running", _name.c_str());
return !forceParallel;
vblog(LOG_INFO, "Macro %s already running", _name.c_str());
if (!_stopActionsIfNotDone) {
return !forceParallel;
}
Stop();
vblog(LOG_INFO, "Stopped macro %s actions to rerun them",
_name.c_str());
}
std::function<bool(bool)> runFunc =
match ? std::bind(&Macro::RunActions, this,
std::placeholders::_1)
@@ -213,6 +222,7 @@ bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
} else {
ret = runFunc(ignorePause);
}
_lastExecutionTime = std::chrono::high_resolution_clock::now();
auto group = _parent.lock();
if (group) {
@@ -304,7 +314,7 @@ bool Macro::RunActionsHelper(
break;
}
if (action->Enabled()) {
action->SetHighlight();
action->EnableHighlight();
}
}
_done = true;
@@ -339,6 +349,11 @@ void Macro::SetMatchOnChange(bool onChange)
_performActionsOnChange = onChange;
}
void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone)
{
_stopActionsIfNotDone = stopActionsIfNotDone;
}
void Macro::SetPaused(bool pause)
{
if (_paused && !pause) {
@@ -373,6 +388,16 @@ void Macro::Stop()
}
}
MacroInputVariables Macro::GetInputVariables() const
{
return _inputVariables;
}
void Macro::SetInputVariables(const MacroInputVariables &inputVariables)
{
_inputVariables = inputVariables;
}
std::vector<TempVariable> Macro::GetTempVars(MacroSegment *filter) const
{
std::vector<TempVariable> res;
@@ -550,6 +575,7 @@ bool Macro::Save(obs_data_t *obj) const
obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
obs_data_set_bool(obj, "group", _isGroup);
if (_isGroup) {
@@ -600,6 +626,8 @@ bool Macro::Save(obs_data_t *obj) const
}
obs_data_set_array(obj, "elseActions", elseActions);
_inputVariables.Save(obj);
return true;
}
@@ -644,6 +672,7 @@ bool Macro::Load(obs_data_t *obj)
_runInParallel = obs_data_get_bool(obj, "parallel");
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
_isGroup = obs_data_get_bool(obj, "group");
if (_isGroup) {
@@ -651,6 +680,7 @@ bool Macro::Load(obs_data_t *obj)
obs_data_get_obj(obj, "groupData");
_isCollapsed = obs_data_get_bool(groupData, "collapsed");
_groupSize = obs_data_get_int(groupData, "size");
return true;
}
@@ -735,6 +765,9 @@ bool Macro::Load(obs_data_t *obj)
}
}
UpdateElseActionIndices();
_inputVariables.Load(obj);
return true;
}
@@ -806,10 +839,10 @@ void Macro::ResetUIHelpers()
{
_onPreventedActionExecution = false;
for (auto c : _conditions) {
c->Highlight();
c->GetHighlightAndReset();
}
for (auto a : _actions) {
a->Highlight();
a->GetHighlightAndReset();
}
}

View File

@@ -2,6 +2,7 @@
#include "macro-action.hpp"
#include "macro-condition.hpp"
#include "macro-helpers.hpp"
#include "macro-input.hpp"
#include "macro-ref.hpp"
#include "variable-string.hpp"
#include "temp-variable.hpp"
@@ -13,7 +14,7 @@
#include <memory>
#include <map>
#include <thread>
#include <obs-data.h>
#include <obs.hpp>
#include <obs-module-helper.hpp>
namespace advss {
@@ -24,30 +25,45 @@ class Macro {
public:
Macro(const std::string &name = "", const bool addHotkey = false);
virtual ~Macro();
bool CeckMatch();
bool PerformActions(bool match, bool forceParallel = false,
bool ignorePause = false);
bool Matched() const { return _matched; }
bool ShouldRunActions() const;
int64_t MsSinceLastCheck() const;
std::string Name() const { return _name; }
void SetName(const std::string &name);
void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
bool RunInParallel() const { return _runInParallel; }
bool CeckMatch(bool ignorePause = false);
bool Matched() const { return _matched; }
int64_t MsSinceLastCheck() const;
bool ShouldRunActions() const;
bool PerformActions(bool match, bool forceParallel = false,
bool ignorePause = false);
void SetPaused(bool pause = true);
bool Paused() const { return _paused; }
bool WasPausedSince(
const std::chrono::high_resolution_clock::time_point &) const;
void Stop();
bool GetStop() const { return _stop; }
void ResetTimers();
void SetMatchOnChange(bool onChange);
bool MatchOnChange() const { return _performActionsOnChange; }
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
bool SkipExecOnStart() const { return _skipExecOnStart; }
void SetStopActionsIfNotDone(bool stopActionsIfNotDone);
bool StopActionsIfNotDone() const { return _stopActionsIfNotDone; }
int RunCount() const { return _runCount; };
void ResetRunCount() { _runCount = 0; };
void ResetTimers();
void AddHelperThread(std::thread &&);
bool GetStop() const { return _stop; }
void Stop();
void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
bool RunInParallel() const { return _runInParallel; }
// Input variables
MacroInputVariables GetInputVariables() const;
void SetInputVariables(const MacroInputVariables &);
// Temporary variable helpers
std::vector<TempVariable> GetTempVars(MacroSegment *filter) const;
@@ -92,19 +108,21 @@ public:
bool SwitchesScene() const;
// UI helpers
const QList<int> &GetActionConditionSplitterPosition() const;
void SetActionConditionSplitterPosition(const QList<int>);
const QList<int> &GetElseActionSplitterPosition() const;
const QList<int> &GetActionConditionSplitterPosition() const;
void SetElseActionSplitterPosition(const QList<int>);
const QList<int> &GetElseActionSplitterPosition() const;
bool HasValidSplitterPositions() const;
bool WasExecutedSince(
const std::chrono::high_resolution_clock::time_point &) const;
bool OnChangePreventedActionsRecently();
void ResetUIHelpers();
// Hotkeys
void EnablePauseHotkeys(bool);
bool PauseHotkeysEnabled() const;
// Docks
void EnableDock(bool);
bool DockEnabled() const { return _registerDock; }
void SetDockHasRunButton(bool value);
@@ -168,6 +186,7 @@ private:
bool _conditionSateChanged = false;
bool _performActionsOnChange = true;
bool _skipExecOnStart = false;
bool _stopActionsIfNotDone = false;
bool _paused = false;
int _runCount = 0;
bool _registerHotkeys = true;
@@ -175,6 +194,8 @@ private:
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
MacroInputVariables _inputVariables;
// UI helpers
bool _onPreventedActionExecution = false;

View File

@@ -0,0 +1,258 @@
#include "action-queue-tab.hpp"
#include "action-queue.hpp"
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "sync-helpers.hpp"
#include "tab-helpers.hpp"
#include "ui-helpers.hpp"
#include <QTimer>
namespace advss {
static void setupTab(QTabWidget *);
static ActionQueueTable *tabWidget = nullptr;
void RegisterActionQueueTab()
{
AddPluginInitStep([]() {
AddSetupTabCallback("actionQueueTab", ActionQueueTable::Create,
setupTab);
});
}
static void setTabVisible(QTabWidget *tabWidget, bool visible)
{
SetTabVisibleByName(
tabWidget, visible,
obs_module_text("AdvSceneSwitcher.actionQueueTab.title"));
}
ActionQueueTable *ActionQueueTable::Create()
{
tabWidget = new ActionQueueTable();
return tabWidget;
}
void ActionQueueTable::Add()
{
auto newQueue = std::make_shared<ActionQueue>();
auto accepted =
ActionQueueSettingsDialog::AskForSettings(this, *newQueue);
if (!accepted) {
return;
}
{
auto lock = LockContext();
auto &queues = GetActionQueues();
queues.emplace_back(newQueue);
}
ActionQueueSignalManager::Instance()->Add(
QString::fromStdString(newQueue->Name()));
}
static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
{
assert(queue);
auto result = QStringList();
if (addName) {
result << QString::fromStdString(queue->Name());
}
result << QString::number(queue->Size())
<< QString::fromStdString(obs_module_text(
queue->IsRunning()
? "AdvSceneSwitcher.actionQueueTab.yes"
: "AdvSceneSwitcher.actionQueueTab.no"))
<< QString::fromStdString(obs_module_text(
queue->RunsOnStartup()
? "AdvSceneSwitcher.actionQueueTab.yes"
: "AdvSceneSwitcher.actionQueueTab.no"));
return result;
}
static void updateQueueStatus(QTableWidget *table)
{
for (int row = 0; row < table->rowCount(); row++) {
auto item = table->item(row, 0);
if (!item) {
continue;
}
auto weakQueue = GetWeakActionQueueByQString(item->text());
auto queue = weakQueue.lock();
if (!queue) {
continue;
}
UpdateItemTableRow(table, row,
getCellLabels(queue.get(), false));
}
}
static void openSettingsDialog()
{
auto selectedRows =
tabWidget->Table()->selectionModel()->selectedRows();
if (selectedRows.empty()) {
return;
}
auto cell = tabWidget->Table()->item(selectedRows.last().row(), 0);
if (!cell) {
return;
}
auto weakQueue = GetWeakActionQueueByQString(cell->text());
auto queue = weakQueue.lock();
if (!queue) {
return;
}
auto oldName = queue->Name();
bool accepted = ActionQueueSettingsDialog::AskForSettings(
tabWidget->Table(), *queue.get());
if (accepted && oldName != queue->Name()) {
ActionQueueSignalManager::Instance()->Rename(
QString::fromStdString(oldName),
QString::fromStdString(queue->Name()));
}
}
static void removeQueuesWithNames(const QStringList &queueNames)
{
for (const auto &name : queueNames) {
auto queue = GetWeakActionQueueByQString(name).lock();
if (!queue) {
continue;
}
auto &queues = GetActionQueues();
queues.erase(
std::remove_if(
queues.begin(), queues.end(),
[queue](const std::shared_ptr<Item> &item) {
return item == queue;
}),
queues.end());
}
}
void ActionQueueTable::Remove()
{
auto selectedRows =
tabWidget->Table()->selectionModel()->selectedRows();
if (selectedRows.empty()) {
return;
}
QStringList queueNames;
for (const auto &row : selectedRows) {
auto cell = tabWidget->Table()->item(row.row(), 0);
if (!cell) {
continue;
}
queueNames << cell->text();
}
int queueNameCount = queueNames.size();
if (queueNameCount == 1) {
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.actionQueueTab.removeSingleQueuePopup.text");
if (!DisplayMessage(deleteWarning.arg(queueNames.at(0)),
true)) {
return;
}
} else {
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.actionQueueTab.removeMultipleQueuesPopup.text");
if (!DisplayMessage(deleteWarning.arg(queueNameCount), true)) {
return;
}
}
{
auto lock = LockContext();
RemoveItemsByName(GetActionQueues(), queueNames);
}
for (const auto &name : queueNames) {
ActionQueueSignalManager::Instance()->Remove(name);
}
}
ActionQueueTable::ActionQueueTable(QTabWidget *parent)
: ResourceTable(
parent,
obs_module_text("AdvSceneSwitcher.actionQueueTab.help"),
obs_module_text(
"AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip"),
obs_module_text(
"AdvSceneSwitcher.actionQueueTab.queueRemoveButton.tooltip"),
QStringList()
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.name.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.size.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.isRunning.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.runOnStartup.header"),
openSettingsDialog)
{
for (const auto &queue : GetActionQueues()) {
auto q = std::static_pointer_cast<ActionQueue>(queue);
AddItemTableRow(Table(), getCellLabels(q.get()));
}
SetHelpVisible(GetActionQueues().empty());
}
static void setupTab(QTabWidget *tab)
{
if (GetActionQueues().empty()) {
setTabVisible(tab, false);
}
QWidget::connect(ActionQueueSignalManager::Instance(),
&ActionQueueSignalManager::Rename, tab,
[](const QString &oldName, const QString &newName) {
RenameItemTableRow(tabWidget->Table(), oldName,
newName);
});
QWidget::connect(
ActionQueueSignalManager::Instance(),
&ActionQueueSignalManager::Add, tab,
[tab](const QString &name) {
AddItemTableRow(
tabWidget->Table(),
getCellLabels(GetWeakActionQueueByQString(name)
.lock()
.get()));
tabWidget->SetHelpVisible(false);
tabWidget->HighlightAddButton(false);
setTabVisible(tab, true);
});
QWidget::connect(ActionQueueSignalManager::Instance(),
&ActionQueueSignalManager::Remove, tab,
[](const QString &name) {
RemoveItemTableRow(tabWidget->Table(), name);
if (tabWidget->Table()->rowCount() == 0) {
tabWidget->SetHelpVisible(true);
tabWidget->HighlightAddButton(true);
}
});
auto timer = new QTimer(tabWidget);
timer->setInterval(1000);
QWidget::connect(timer, &QTimer::timeout,
[]() { updateQueueStatus(tabWidget->Table()); });
timer->start();
}
} // namespace advss

View File

@@ -0,0 +1,20 @@
#pragma once
#include "resource-table.hpp"
namespace advss {
class ActionQueueTable final : public ResourceTable {
Q_OBJECT
public:
static ActionQueueTable *Create();
private slots:
void Add();
void Remove();
private:
ActionQueueTable(QTabWidget *parent = nullptr);
};
} // namespace advss

View File

@@ -1,11 +1,19 @@
#include "action-queue.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp"
namespace advss {
static std::deque<std::shared_ptr<Item>> queues;
std::deque<std::shared_ptr<Item>> &GetActionQueues()
{
return queues;
}
void RegisterActionQueueTab();
void SetupActionQueues()
{
static bool done = false;
@@ -14,6 +22,7 @@ void SetupActionQueues()
}
AddSaveStep(SaveActionQueues);
AddLoadStep(LoadActionQueues);
RegisterActionQueueTab();
done = true;
}
@@ -33,6 +42,7 @@ void ActionQueue::Save(obs_data_t *obj) const
{
obs_data_set_string(obj, "name", _name.c_str());
obs_data_set_bool(obj, "runOnStartup", _runOnStartup);
obs_data_set_bool(obj, "resolveVariablesOnAdd", _resolveVariablesOnAdd);
}
void ActionQueue::Load(obs_data_t *obj)
@@ -40,6 +50,8 @@ void ActionQueue::Load(obs_data_t *obj)
std::lock_guard<std::mutex> lock(_mutex);
_name = obs_data_get_string(obj, "name");
_runOnStartup = obs_data_get_bool(obj, "runOnStartup");
_resolveVariablesOnAdd =
obs_data_get_bool(obj, "resolveVariablesOnAdd");
if (_runOnStartup) {
Start();
@@ -48,9 +60,13 @@ void ActionQueue::Load(obs_data_t *obj)
void ActionQueue::Start()
{
if (_thread.joinable()) {
if (!_stop) {
return;
}
if (_thread.joinable()) {
_thread.join();
}
_stop = false;
_thread = std::thread(&ActionQueue::RunActions, this);
}
@@ -59,6 +75,10 @@ void ActionQueue::Stop()
{
_stop = true;
_cv.notify_all();
if (std::this_thread::get_id() == _thread.get_id()) {
return;
}
if (_thread.joinable()) {
_thread.join();
}
@@ -66,7 +86,7 @@ void ActionQueue::Stop()
bool ActionQueue::IsRunning() const
{
return _thread.joinable();
return !_stop;
}
bool ActionQueue::RunsOnStartup() const
@@ -80,10 +100,21 @@ void ActionQueue::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);
_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();
}
@@ -120,8 +151,11 @@ void ActionQueue::RunActions()
continue;
}
vblog(LOG_INFO, "Performing action '%s' in queue '%s'",
action->GetId().c_str(), _name.c_str());
if (ActionLoggingEnabled()) {
blog(LOG_INFO, "Performing action '%s' in queue '%s'",
action->GetId().c_str(), _name.c_str());
action->LogAction();
}
action->PerformAction();
}
}
@@ -139,6 +173,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
_clear(new QPushButton(
obs_module_text("AdvSceneSwitcher.actionQueues.clear"))),
_runOnStartup(new QCheckBox()),
_resolveVariablesOnAdd(new QCheckBox()),
_queue(settings)
{
QWidget::connect(_startStopToggle, SIGNAL(clicked()), this,
@@ -146,6 +181,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
QWidget::connect(_clear, SIGNAL(clicked()), this, SLOT(ClearClicked()));
_runOnStartup->setChecked(settings._runOnStartup);
_resolveVariablesOnAdd->setChecked(settings._resolveVariablesOnAdd);
UpdateLabels();
auto layout = new QGridLayout();
@@ -167,6 +203,14 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
_runOnStartup->setToolTip(
obs_module_text("AdvSceneSwitcher.actionQueues.runOnStartup"));
++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(_startStopToggle, row, 1);
++row;
@@ -194,6 +238,8 @@ bool ActionQueueSettingsDialog::AskForSettings(QWidget *parent,
settings._name = dialog._name->text().toStdString();
settings._runOnStartup = dialog._runOnStartup->isChecked();
settings._resolveVariablesOnAdd =
dialog._resolveVariablesOnAdd->isChecked();
return true;
}
@@ -329,6 +375,43 @@ void LoadActionQueues(obs_data_t *obj)
}
}
static bool queueWithNameExists(const std::string &name)
{
return !GetWeakActionQueueByName(name).expired();
}
static void signalImportedQueues(void *varsPtr)
{
auto queues = std::unique_ptr<std::vector<std::shared_ptr<Item>>>(
static_cast<std::vector<std::shared_ptr<Item>> *>(varsPtr));
for (const auto &queue : *queues) {
ActionQueueSignalManager::Instance()->Add(
QString::fromStdString(queue->Name()));
}
}
void ImportQueues(obs_data_t *data)
{
OBSDataArrayAutoRelease array =
obs_data_get_array(data, "actionQueues");
size_t count = obs_data_array_count(array);
auto importedQueues = new std::vector<std::shared_ptr<Item>>;
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);
importedQueues->emplace_back(queue);
}
QeueUITask(signalImportedQueues, importedQueues);
}
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name)
{
for (const auto &queue : queues) {

View File

@@ -38,7 +38,8 @@ private:
void RunActions();
bool _runOnStartup = true;
std::atomic_bool _stop = {false};
bool _resolveVariablesOnAdd = true;
std::atomic_bool _stop = {true};
std::mutex _mutex;
std::condition_variable _cv;
std::thread _thread;
@@ -66,6 +67,7 @@ private:
QLabel *_queueSize;
QPushButton *_clear;
QCheckBox *_runOnStartup;
QCheckBox *_resolveVariablesOnAdd;
ActionQueue &_queue;
};
@@ -93,9 +95,11 @@ signals:
void Remove(const QString &);
};
std::deque<std::shared_ptr<Item>> &GetActionQueues();
void SetupActionQueues();
void SaveActionQueues(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> GetWeakActionQueueByQString(const QString &name);
std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &);

View File

@@ -187,24 +187,6 @@ void SwitcherData::RunPostLoadSteps()
postLoadSteps.clear();
}
void SwitcherData::AddPluginInitStep(std::function<void()> function)
{
std::lock_guard<std::mutex> lock(switcher->m);
pluginInitSteps.emplace_back(function);
}
void SwitcherData::AddPluginPostLoadStep(std::function<void()> function)
{
std::lock_guard<std::mutex> lock(switcher->m);
pluginPostLoadSteps.emplace_back(function);
}
void SwitcherData::AddPluginCleanupStep(std::function<void()> function)
{
std::lock_guard<std::mutex> lock(switcher->m);
pluginCleanupSteps.emplace_back(function);
}
void SwitcherData::AddSaveStep(std::function<void(obs_data_t *)> function)
{
std::lock_guard<std::mutex> lock(switcher->m);

View File

@@ -16,7 +16,7 @@
#include "switch-video.hpp"
#include "switch-network.hpp"
#include "macro-properties.hpp"
#include "macro-settings.hpp"
#include "duration-control.hpp"
#include "priority-helper.hpp"
#include "plugin-state-helpers.hpp"
@@ -65,9 +65,6 @@ public:
void AddPostLoadStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>, bool lock = true);
void RunPostLoadSteps();
void AddPluginInitStep(std::function<void()>);
void AddPluginPostLoadStep(std::function<void()>);
void AddPluginCleanupStep(std::function<void()>);
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPreviousSceneAsMatch,
bool &macroMatch);
@@ -106,9 +103,6 @@ public:
std::vector<std::function<void(obs_data_t *)>> loadSteps;
std::vector<std::function<void()>> postLoadSteps;
std::vector<std::function<void()>> resetIntervalSteps;
std::vector<std::function<void()>> pluginInitSteps;
std::vector<std::function<void()>> pluginPostLoadSteps;
std::vector<std::function<void()>> pluginCleanupSteps;
bool firstBoot = true;
bool transitionActive = false;
@@ -141,7 +135,9 @@ public:
bool showSystemTrayNotifications = false;
bool transitionOverrideOverride = false;
bool adjustActiveTransitionType = true;
bool verbose = false;
enum class LogLevel { DEFAULT, PRINT_ACTION, VERBOSE };
LogLevel logLevel = LogLevel::DEFAULT;
/* --- End of General tab section --- */
@@ -169,7 +165,6 @@ public:
/* --- Start of UI section --- */
bool settingsWindowOpened = false;
int lastOpenedTab = -1;
std::string lastImportPath;
QStringList loadFailureLibs;
bool warnPluginLoadFailure = true;

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 "log-helper.hpp"
#include <QDir>
#include <QFileInfo>
#include <curl/curl.h>
#include <obs.hpp>
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);
}
void Duration::ResolveVariables()
{
_value.ResolveVariables();
}
} // namespace advss

View File

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

19
lib/utils/help-icon.cpp Normal file
View File

@@ -0,0 +1,19 @@
#include "help-icon.hpp"
#include "ui-helpers.hpp"
namespace advss {
HelpIcon::HelpIcon(const QString &tooltip, QWidget *parent) : QLabel(parent)
{
auto path = GetThemeTypeName() == "Light"
? ":/res/images/help.svg"
: ":/res/images/help_light.svg";
QIcon icon(path);
QPixmap pixmap = icon.pixmap(QSize(16, 16));
setPixmap(pixmap);
if (!tooltip.isEmpty()) {
setToolTip(tooltip);
}
}
} // namespace advss

15
lib/utils/help-icon.hpp Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include "export-symbol-helper.hpp"
#include <QLabel>
namespace advss {
class ADVSS_EXPORT HelpIcon : public QLabel {
Q_OBJECT
public:
HelpIcon(const QString &tooltip = "", QWidget *parent = nullptr);
};
} // namespace advss

View File

@@ -177,7 +177,7 @@ void ItemSelection::RenameItem()
Item *item = variant.value<Item *>();
std::string name;
bool accepted = AdvSSNameDialog::AskForName(
bool accepted = NameDialog::AskForName(
this, obs_module_text("AdvSceneSwitcher.windowTitle"),
obs_module_text("AdvSceneSwitcher.item.newName"), name,
QString::fromStdString(item->Name()));
@@ -347,4 +347,18 @@ void Item::Save(obs_data_t *obj) const
obs_data_set_string(obj, "name", _name.c_str());
}
void RemoveItemsByName(std::deque<std::shared_ptr<Item>> &items,
const QStringList &names)
{
for (const auto &name : names) {
items.erase(std::remove_if(
items.begin(), items.end(),
[name](const std::shared_ptr<Item> &item) {
return item->Name() ==
name.toStdString();
}),
items.end());
}
}
} // namespace advss

View File

@@ -32,6 +32,9 @@ protected:
friend ItemSettingsDialog;
};
void EXPORT RemoveItemsByName(std::deque<std::shared_ptr<Item>> &items,
const QStringList &names);
class ItemSettingsDialog : public QDialog {
Q_OBJECT
@@ -78,6 +81,7 @@ public:
"AdvSceneSwitcher.item.nameNotAvailable",
std::string_view configureTooltip = "", QWidget *parent = 0);
virtual ~ItemSelection() = default;
Item *GetCurrentItem();
void SetItem(const std::string &);
void ShowRenameContextMenu(bool value);
@@ -96,8 +100,6 @@ signals:
void ItemRenamed(const QString &oldName, const QString &name);
private:
Item *GetCurrentItem();
FilterComboBox *_selection;
QPushButton *_modify;
CreateItemFunc _create;

77
lib/utils/list-editor.cpp Normal file
View File

@@ -0,0 +1,77 @@
#include "list-editor.hpp"
#include "ui-helpers.hpp"
namespace advss {
ListEditor::ListEditor(QWidget *parent, bool reorder)
: QWidget(parent),
_list(new QListWidget()),
_add(new QPushButton()),
_remove(new QPushButton()),
_up(new QPushButton()),
_down(new QPushButton()),
_controlsLayout(new QHBoxLayout()),
_mainLayout(new QVBoxLayout())
{
_add->setMaximumWidth(22);
_add->setProperty("themeID",
QVariant(QString::fromUtf8("addIconSmall")));
_add->setFlat(true);
_remove->setMaximumWidth(22);
_remove->setProperty("themeID",
QVariant(QString::fromUtf8("removeIconSmall")));
_remove->setFlat(true);
_up->setMaximumWidth(22);
_up->setProperty("themeID",
QVariant(QString::fromUtf8("upArrowIconSmall")));
_up->setFlat(true);
_down->setMaximumWidth(22);
_down->setProperty("themeID",
QVariant(QString::fromUtf8("downArrowIconSmall")));
_down->setFlat(true);
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(Add()));
QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(Remove()));
QWidget::connect(_up, SIGNAL(clicked()), this, SLOT(Up()));
QWidget::connect(_down, SIGNAL(clicked()), this, SLOT(Down()));
QWidget::connect(_list, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
this, SLOT(Clicked(QListWidgetItem *)));
_controlsLayout->setContentsMargins(0, 0, 0, 0);
_controlsLayout->addWidget(_add);
_controlsLayout->addWidget(_remove);
if (reorder) {
auto line = new QFrame();
line->setFrameShape(QFrame::VLine);
line->setFrameShadow(QFrame::Sunken);
_controlsLayout->addWidget(line);
_controlsLayout->addWidget(_up);
_controlsLayout->addWidget(_down);
}
_controlsLayout->addStretch();
_mainLayout->setContentsMargins(0, 0, 0, 0);
_mainLayout->addWidget(_list);
_mainLayout->addLayout(_controlsLayout);
setLayout(_mainLayout);
}
void ListEditor::showEvent(QShowEvent *e)
{
QWidget::showEvent(e);
// This is necessary as the list viewport might not be updated yet while
// the list was hidden.
// Thus, previous calls to UpdateListSize() might not have resized the
// widget correctly, for example due to not regarding the horizontal
// scrollbar yet.
UpdateListSize();
}
void ListEditor::UpdateListSize()
{
SetHeightToContentHeight(_list);
adjustSize();
updateGeometry();
}
} // namespace advss

38
lib/utils/list-editor.hpp Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include "export-symbol-helper.hpp"
#include <QPushButton>
#include <QListWidget>
#include <QLayout>
namespace advss {
class ADVSS_EXPORT ListEditor : public QWidget {
Q_OBJECT
public:
ListEditor(QWidget *parent = nullptr, bool reorder = true);
protected:
void showEvent(QShowEvent *);
private slots:
virtual void Add() = 0;
virtual void Remove() = 0;
virtual void Up(){};
virtual void Down(){};
virtual void Clicked(QListWidgetItem *) {}
protected:
void UpdateListSize();
QListWidget *_list;
QPushButton *_add;
QPushButton *_remove;
QPushButton *_up;
QPushButton *_down;
QHBoxLayout *_controlsLayout;
QVBoxLayout *_mainLayout;
};
} // namespace advss

View File

@@ -5,7 +5,16 @@ namespace advss {
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

View File

@@ -3,14 +3,26 @@
namespace advss {
// Print log with "[adv-ss] " prefix
#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, ...) \
do { \
if (VerboseLoggingEnabled()) { \
blog(level, msg, ##__VA_ARGS__); \
} \
} 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();
// Returns true if log level is set to "action" or "verbose"
EXPORT bool ActionLoggingEnabled();
} // namespace advss

View File

@@ -5,22 +5,22 @@
namespace advss {
AdvSSNameDialog::AdvSSNameDialog(QWidget *parent) : QDialog(parent)
NameDialog::NameDialog(QWidget *parent)
: QDialog(parent),
_label(new QLabel(this)),
_userText(new QLineEdit(this))
{
setModal(true);
setWindowModality(Qt::WindowModality::WindowModal);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setFixedWidth(555);
setMinimumHeight(100);
QVBoxLayout *layout = new QVBoxLayout;
auto layout = new QVBoxLayout;
setLayout(layout);
label = new QLabel(this);
layout->addWidget(label);
label->setText("Set Text");
userText = new QLineEdit(this);
layout->addWidget(userText);
layout->addWidget(_label);
layout->addWidget(_userText);
QDialogButtonBox *buttonbox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
@@ -30,43 +30,44 @@ AdvSSNameDialog::AdvSSNameDialog(QWidget *parent) : QDialog(parent)
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
static bool IsWhitespace(char ch)
static bool isWhitespace(char ch)
{
return ch == ' ' || ch == '\t';
}
static void CleanWhitespace(std::string &str)
static void cleanWhitespace(std::string &str)
{
while (str.size() && IsWhitespace(str.back()))
while (str.size() && isWhitespace(str.back())) {
str.erase(str.end() - 1);
while (str.size() && IsWhitespace(str.front()))
}
while (str.size() && isWhitespace(str.front())) {
str.erase(str.begin());
}
}
bool AdvSSNameDialog::AskForName(QWidget *parent, const QString &title,
const QString &text,
std::string &userTextInput,
const QString &placeHolder, int maxSize,
bool clean)
bool NameDialog::AskForName(QWidget *parent, const QString &title,
const QString &prompt, std::string &userTextInput,
const QString &placeHolder, int maxSize, bool clean)
{
if (maxSize <= 0 || maxSize > 32767) {
maxSize = 170;
}
AdvSSNameDialog dialog(parent);
NameDialog dialog(parent);
dialog.setWindowTitle(title);
dialog.label->setText(text);
dialog.userText->setMaxLength(maxSize);
dialog.userText->setText(placeHolder);
dialog.userText->selectAll();
dialog._label->setVisible(!prompt.isEmpty());
dialog._label->setText(prompt);
dialog._userText->setMaxLength(maxSize);
dialog._userText->setText(placeHolder);
dialog._userText->selectAll();
if (dialog.exec() != DialogCode::Accepted) {
return false;
}
userTextInput = dialog.userText->text().toUtf8().constData();
userTextInput = dialog._userText->text().toUtf8().constData();
if (clean) {
CleanWhitespace(userTextInput);
cleanWhitespace(userTextInput);
}
return true;
}

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