Compare commits

...

83 Commits

Author SHA1 Message Date
WarmUpTill
510f83246e Only emit signals when value changed
* Fixes crash when deleting macro in case macro selection is in focus
* Prevent unnecessary create operation when switching macro segment type
2024-08-16 06:49:27 +02:00
WarmUpTill
c969b21f93 Fix MacroSelection not properly unhiding first macro in selection 2024-08-16 06:49:27 +02:00
WarmUpTill
6ec40ef8e9 Cleanup 2024-08-16 06:49:27 +02:00
WarmUpTill
27859e83b3 Fix crash when creating macro which uses macro segment factories 2024-08-16 06:49:27 +02:00
WarmUpTill
263565700a Fix crashes related to widget highlighting 2024-08-16 06:49:27 +02:00
WarmUpTill
5bb10a4aef Resolve unused parameter warnings 2024-08-15 01:02:03 +02:00
WarmUpTill
ee72bb192c Improve corrupted installation detection 2024-08-15 01:02:03 +02:00
WarmUpTill
1a1028cbaa Paste copied macro action depending on cursor position
If cursor is above else action section paste it there.
If cursor is above regular action section paste it there.

In case of neither paste in original position.
2024-08-14 22:02:55 +02:00
WarmUpTill
2f4f8bcc74 Add IsCursorInWidgetArea() 2024-08-14 22:02:55 +02:00
WarmUpTill
4113615cd6 Add context menu to change custom label for macro segments 2024-08-14 22:02:55 +02:00
WarmUpTill
9522d7c0b4 Adapt to new OBS dock API
* The plugin will no longer attempt to restore dock positions on scene
  collection change.
* When a macro is being renamed the dock widget with the current name
  will be removed.
  A new dock with the new name will have to be opened manually.
* The status dock position will be when updating to a version containing
  this changei from an older version.
2024-08-14 21:51:42 +02:00
WarmUpTill
cc3ea79836 buildspec: Update OBS Studio dependency to 30.0.2 2024-08-14 21:51:42 +02:00
WarmUpTill
f72e802d00 Add API link 2024-08-14 21:51:42 +02:00
WarmUpTill
36201cbfb4 Add API to register new macro condition and action types 2024-08-14 21:51:42 +02:00
WarmUpTill
685e28d161 Make read and write functions of variables thread-safe 2024-08-14 21:51:42 +02:00
WarmUpTill
82d23dcf0e Exclude python cache directories 2024-08-14 21:51:42 +02:00
WarmUpTill
ad322d54f0 Fix crash in case of high frequency resize events
I am not sure if this could really happen in normal use.
Was discovered after implementing the script properties UI widgets.
2024-08-14 21:51:42 +02:00
WarmUpTill
8bac6fe829 Set Run condition default timeout to one second
The action is often used in combination with python scripts which
usually take longer to initialize than 100ms
2024-08-14 21:51:42 +02:00
WarmUpTill
ac7531decb Fix crash in MIDI initialization 2024-08-14 21:51:42 +02:00
Junior Anzolin
7af4969ec6 Add portuguese brazil translation (#1168)
* Add portuguese brazil translation
2024-08-13 19:36:27 +02:00
WarmUpTill
a088325968 Refresh pattern data when pattern file changes 2024-08-11 16:03:08 +02:00
WarmUpTill
57bc58be8e Add duration modifier tests 2024-08-11 16:03:08 +02:00
WarmUpTill
f9730b1bc2 Refactor DurationModifier to enable testing 2024-08-11 16:03:08 +02:00
WarmUpTill
d4025214e5 Cleanup duplicate "[advss]" log prefix 2024-08-11 16:03:08 +02:00
WarmUpTill
c431e6c31d Show warning if default action or condition cannot be created 2024-08-11 16:03:08 +02:00
WarmUpTill
9941cfe9b5 Install libproc2-dev in Linux build environment
This ensures that the plugin will attempt to load either libprocps-dev
or libproc2-dev libraries at runtime, instead of just the libprocps-dev
library

Dep packages taken from:
 * https://packages.debian.org/bookworm/libproc2-0
 * https://packages.debian.org/bookworm/libproc2-dev
2024-08-11 16:03:08 +02:00
WarmUpTill
15998012e5 Change default verbosity of busy loop detection log
This is mostly relevant when analysing issues, so it does not make sense
to potentially spam the OBS log with this message
2024-08-11 16:03:08 +02:00
WarmUpTill
bcb043f997 Add condition logic tests 2024-08-11 16:03:08 +02:00
WarmUpTill
74116382b1 Enable include during unit testing 2024-08-11 16:03:08 +02:00
WarmUpTill
24f33fb0d2 Refactor condition logic to enable testing 2024-08-11 16:03:08 +02:00
WarmUpTill
d80df57ef8 Increase default threshold value for image pattern matching
The previous default value could cause a lot of false positive
detections and thus cause confusion if the image detection is working
correctly or not.
2024-08-11 16:03:08 +02:00
WarmUpTill
240c47975c Rework Media condition
* Split state and time check
* Improve signal handling
* Prevent exponential memory groth when using the "any" / "all" check
* Improve layout
2024-08-07 21:22:45 +02:00
WarmUpTill
bedb3b8dc6 Change plugin folder name
Requested by lindenkron to ease development of a plugin manager
2024-07-09 21:21:10 +02:00
WarmUpTill
8032dde045 Prevent users from accidentally enabling action cooldowns 2024-07-09 21:21:10 +02:00
WarmUpTill
bfc51b2df5 Add option to set individual transform settings 2024-07-09 21:00:25 +02:00
WarmUpTill
2160da59f3 Add support to check values of individual transform settings 2024-07-09 21:00:25 +02:00
WarmUpTill
d3164e3ed6 Add TransformSettingSelection 2024-07-09 21:00:25 +02:00
WarmUpTill
17c72b772d Display plugin load failure warning only once 2024-07-09 19:10:51 +02:00
WarmUpTill
3ff9a1f270 Add USB condition type
It allows users to check if a given USB device is currently connected
2024-07-08 20:54:26 +02:00
WarmUpTill
7d120d8b6a Fix MacOS version of install_advss_plugin_dependency_file 2024-07-08 20:54:26 +02:00
WarmUpTill
53cfe4b030 Add libusb dependency 2024-07-08 20:54:26 +02:00
WarmUpTill
4dcd748a1f Switch to github repo of openssl 2024-07-08 20:54:26 +02:00
WarmUpTill
223ce0d0a0 Fix potential crash in StatusControl 2024-07-03 13:02:34 +02:00
WarmUpTill
5bd3341681 Add option to clear message buffer on match 2024-07-03 13:02:34 +02:00
WarmUpTill
bb527d6910 Improve error logging of commercial start via Twitch action 2024-07-03 12:55:49 +02:00
WarmUpTill
dfedbcf31a Add option to set maximum replay buffer time 2024-06-26 19:19:41 +02:00
WarmUpTill
1bbfd16b3f Add option to check disk space available
Based on the OBS stats dock implementation, which queries the drive to
which outputs would write to
2024-06-22 15:40:47 +02:00
WarmUpTill
7f5737d03f Use default cursor icon on macro segment control hover
Previously the cursor icon would be set to Qt::SplitVCursor as those
widgets were moved into the splitter handle layout
2024-06-20 21:41:28 +02:00
WarmUpTill
7702541d81 Cleanup
* Increase update frequency of AutoUpdateTooltipLabel
* Prevent crash in TempVariableRef::PostLoad()
* Ensure that correct widget is passed for MacroSelection connections
2024-06-20 21:41:28 +02:00
WarmUpTill
3bc15e585c Switch QPushButton to QToolButton
This will be more consistent with the widget styles used throughout OBS
and solves a few layout issues
2024-06-20 21:41:28 +02:00
WarmUpTill
d77101b6aa Restore fullscreen projector display selection
Instead of discarding the monitor selection now only no longer perform
the action while the monitor setup is changed.
2024-06-18 21:24:28 +02:00
WarmUpTill
c1e23f9c7a Improve slide show condition
* Add temp var support
* Add regex support for path check
* Improve layout stretch handling
2024-06-16 18:42:06 +02:00
WarmUpTill
70415cd8f4 Add support for "slideshow_v2" 2024-06-16 18:42:06 +02:00
WarmUpTill
f50cd76493 Fix i386 build issue 2024-06-16 11:04:05 +02:00
炭酸コーラ
e4af698881 Add Japanese translation 2024-06-15 10:11:45 +02:00
WarmUpTill
33120a359c Fix Twitch "goal type" temp var tooltips being cut off early 2024-06-15 10:11:45 +02:00
WarmUpTill
091a364097 Print line numbers in check-locale.py in case of error 2024-06-15 10:11:45 +02:00
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
186 changed files with 14570 additions and 2447 deletions

View File

@@ -9,4 +9,5 @@ package 'libxtst-dev'
package 'libxss-dev' package 'libxss-dev'
package 'libopencv-dev' package 'libopencv-dev'
package 'libtesseract-dev' package 'libtesseract-dev'
package 'libprocps-dev' package 'libprocps-dev'
package 'libusb-1.0-0-dev'

View File

@@ -3,3 +3,4 @@ brew "coreutils"
brew "cmake" brew "cmake"
brew "jq" brew "jq"
brew "xcbeautify" brew "xcbeautify"
brew "automake"

View File

@@ -1,3 +1,4 @@
package 'cmake', path: 'Cmake\bin', bin: 'cmake' package 'cmake', path: 'Cmake\bin', bin: 'cmake'
package 'innosetup', path: 'Inno Setup 6', bin: 'iscc' package 'innosetup', path: 'Inno Setup 6', bin: 'iscc'
package 'OpenSSL', path: 'OpenSSL', bin: 'openssl' package 'OpenSSL', path: 'OpenSSL', bin: 'openssl'
package 'Microsoft.VisualStudio.Locator', path: 'vswhere', bin: 'vswhere'

View File

@@ -380,7 +380,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
pushd ${advss_dep_path} pushd ${advss_dep_path}
log_info "Prepare openssl ..." log_info "Prepare openssl ..."
rm -rf openssl rm -rf openssl
git clone git://git.openssl.org/openssl.git --branch openssl-3.1.2 --depth 1 git clone https://github.com/openssl/openssl.git --branch openssl-3.1.2 --depth 1
mv openssl openssl_x86 mv openssl openssl_x86
cp -r openssl_x86 openssl_arm cp -r openssl_x86 openssl_arm
@@ -405,9 +405,63 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
log_info "Clean up openssl dir..." log_info "Clean up openssl dir..."
mv openssl_x86 openssl mv openssl_x86 openssl
rm -rf openssl_arm rm -rf openssl_arm
popd
pushd ${project_root}/deps/libusb
log_info "Prepare libusb ..."
log_info "Building libusb x86 (deps) ..."
export MACOSX_DEPLOYMENT_TARGET=10.9
mkdir ${project_root}/deps/libusb/out_x86
./autogen.sh
./configure --host=x86_64-apple-darwin --prefix=${advss_dep_path}
make && make install
log_info "Building libusb x86 ..."
make clean
rm -r ${project_root}/deps/libusb/out_x86
mkdir ${project_root}/deps/libusb/out_x86
./configure --host=aarch64-apple-darwin --prefix=${project_root}/deps/libusb/out_x86
make && make install
log_info "Building libusb arm ..."
make clean
export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)
export CC=$(xcrun --sdk macosx --find clang)
export CXX=$(xcrun --sdk macosx --find clang++)
export CFLAGS="-arch arm64 -isysroot $SDKROOT"
export CXXFLAGS="-arch arm64 -isysroot $SDKROOT"
export LDFLAGS="-arch arm64 -isysroot $SDKROOT"
export MACOSX_DEPLOYMENT_TARGET=10.15
mkdir ${project_root}/deps/libusb/out_arm
./configure --host=aarch64-apple-darwin --prefix=${project_root}/deps/libusb/out_arm
make && make install
log_info "Combine arm and x86 libusb binaries ..."
lipo -create ${project_root}/deps/libusb/out_x86/lib/libusb-1.0.0.dylib \
${project_root}/deps/libusb/out_arm/lib/libusb-1.0.0.dylib \
-output ${advss_dep_path}/lib/temp-libusb-1.0.0.dylib
mv ${advss_dep_path}/lib/temp-libusb-1.0.0.dylib ${advss_dep_path}/lib/libusb-1.0.0.dylib
install_name_tool -id @rpath/libusb-1.0.0.dylib ${advss_dep_path}/lib/libusb-1.0.0.dylib
log_info "Clean up libusb ..."
unset SDKROOT
unset CC
unset CXX
unset CFLAGS
unset CXXFLAGS
unset LDFLAGS
unset MACOSX_DEPLOYMENT_TARGET
popd
;; ;;
linux) linux)
# Nothing to do for now # Hacky workaround to support libproc2 with Ubuntu 22 build environment
local lsb_version=$(lsb_release -r | cut -f 2 || true)
if [[ "${lsb_version}" == '22.04' ]] {
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
}
;; ;;
} }
} }

View File

@@ -199,6 +199,26 @@ function Build {
Log-Information "Install tesseract..." Log-Information "Install tesseract..."
Invoke-External cmake --install "${TesseractBuildPath}" --prefix "${ADVSSDepPath}" @TesseractCmakeArgs Invoke-External cmake --install "${TesseractBuildPath}" --prefix "${ADVSSDepPath}" @TesseractCmakeArgs
Push-Location -Stack BuildLibusbTemp
Ensure-Location $ProjectRoot
$LibusbPath = "${ProjectRoot}/deps/libusb"
Log-Information "Building libusb..."
$msbuildExe = vswhere -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1
if ($msbuildExe) {
Invoke-External $msbuildExe "${LibusbPath}/msvc/libusb.sln" /property:Configuration=Release /property:Platform=x64
$libusbBuildResultDirectory = "${LibusbPath}/build/v143/x64/Release"
if (-not (Test-Path -Path $libusbBuildResultDirectory)) {
$libusbBuildResultDirectory = "${LibusbPath}/x64/Release/dll"
}
Copy-Item -Path "${libusbBuildResultDirectory}/*" -Destination ${ADVSSDepPath} -Recurse -Force
} else {
Log-Information "Failed to locate msbuild.exe - skipping libusb build"
}
} }
Build Build

6
.gitignore vendored
View File

@@ -7,6 +7,7 @@
!/cmake !/cmake
!/data !/data
!/deps !/deps
!/scripting
!/forms !/forms
!/lib !/lib
!/module !/module
@@ -34,4 +35,7 @@
.DS_Store .DS_Store
# Exclude CMake build number cache # Exclude CMake build number cache
/cmake/.CMakeBuildNumber /cmake/.CMakeBuildNumber
# Exclude python caches
/**/__pycache__

3
.gitmodules vendored
View File

@@ -25,3 +25,6 @@
[submodule "deps/json"] [submodule "deps/json"]
path = deps/json path = deps/json
url = https://github.com/nlohmann/json.git url = https://github.com/nlohmann/json.git
[submodule "deps/libusb"]
path = deps/libusb
url = https://github.com/libusb/libusb.git

View File

@@ -96,6 +96,8 @@ target_sources(
lib/macro/macro-action-macro.hpp lib/macro/macro-action-macro.hpp
lib/macro/macro-action-queue.cpp lib/macro/macro-action-queue.cpp
lib/macro/macro-action-queue.hpp lib/macro/macro-action-queue.hpp
lib/macro/macro-action-script.cpp
lib/macro/macro-action-script.hpp
lib/macro/macro-action-variable.cpp lib/macro/macro-action-variable.cpp
lib/macro/macro-action-variable.hpp lib/macro/macro-action-variable.hpp
lib/macro/macro-action.cpp lib/macro/macro-action.cpp
@@ -108,6 +110,8 @@ target_sources(
lib/macro/macro-condition-macro.hpp lib/macro/macro-condition-macro.hpp
lib/macro/macro-condition-queue.cpp lib/macro/macro-condition-queue.cpp
lib/macro/macro-condition-queue.hpp lib/macro/macro-condition-queue.hpp
lib/macro/macro-condition-script.cpp
lib/macro/macro-condition-script.hpp
lib/macro/macro-condition-tempvar.cpp lib/macro/macro-condition-tempvar.cpp
lib/macro/macro-condition-tempvar.hpp lib/macro/macro-condition-tempvar.hpp
lib/macro/macro-condition-variable.cpp lib/macro/macro-condition-variable.cpp
@@ -120,24 +124,30 @@ target_sources(
lib/macro/macro-export-import-dialog.hpp lib/macro/macro-export-import-dialog.hpp
lib/macro/macro-helpers.cpp lib/macro/macro-helpers.cpp
lib/macro/macro-helpers.hpp lib/macro/macro-helpers.hpp
lib/macro/macro-input.cpp
lib/macro/macro-input.hpp
lib/macro/macro-list.cpp lib/macro/macro-list.cpp
lib/macro/macro-list.hpp lib/macro/macro-list.hpp
lib/macro/macro-properties.cpp
lib/macro/macro-properties.hpp
lib/macro/macro-ref.cpp lib/macro/macro-ref.cpp
lib/macro/macro-ref.hpp lib/macro/macro-ref.hpp
lib/macro/macro-run-button.cpp lib/macro/macro-run-button.cpp
lib/macro/macro-run-button.hpp lib/macro/macro-run-button.hpp
lib/macro/macro-script-handler.cpp
lib/macro/macro-script-handler.hpp
lib/macro/macro-segment-copy-paste.cpp lib/macro/macro-segment-copy-paste.cpp
lib/macro/macro-segment-copy-paste.hpp lib/macro/macro-segment-copy-paste.hpp
lib/macro/macro-segment-list.cpp lib/macro/macro-segment-list.cpp
lib/macro/macro-segment-list.hpp lib/macro/macro-segment-list.hpp
lib/macro/macro-segment-script.cpp
lib/macro/macro-segment-script.hpp
lib/macro/macro-segment-selection.cpp lib/macro/macro-segment-selection.cpp
lib/macro/macro-segment-selection.hpp lib/macro/macro-segment-selection.hpp
lib/macro/macro-segment.cpp lib/macro/macro-segment.cpp
lib/macro/macro-segment.hpp lib/macro/macro-segment.hpp
lib/macro/macro-selection.cpp lib/macro/macro-selection.cpp
lib/macro/macro-selection.hpp lib/macro/macro-selection.hpp
lib/macro/macro-settings.cpp
lib/macro/macro-settings.hpp
lib/macro/macro-tab.cpp lib/macro/macro-tab.cpp
lib/macro/macro-tree.cpp lib/macro/macro-tree.cpp
lib/macro/macro-tree.hpp lib/macro/macro-tree.hpp
@@ -153,12 +163,18 @@ target_sources(
lib/queue/action-queue-tab.hpp lib/queue/action-queue-tab.hpp
lib/utils/backup.cpp lib/utils/backup.cpp
lib/utils/backup.hpp lib/utils/backup.hpp
lib/utils/condition-logic.cpp
lib/utils/condition-logic.hpp
lib/utils/curl-helper.cpp lib/utils/curl-helper.cpp
lib/utils/curl-helper.hpp lib/utils/curl-helper.hpp
lib/utils/cursor-shape-changer.cpp
lib/utils/cursor-shape-changer.hpp
lib/utils/double-slider.cpp lib/utils/double-slider.cpp
lib/utils/double-slider.hpp lib/utils/double-slider.hpp
lib/utils/duration-control.cpp lib/utils/duration-control.cpp
lib/utils/duration-control.hpp lib/utils/duration-control.hpp
lib/utils/duration-modifier.cpp
lib/utils/duration-modifier.hpp
lib/utils/duration.cpp lib/utils/duration.cpp
lib/utils/duration.hpp lib/utils/duration.hpp
lib/utils/export-symbol-helper.hpp lib/utils/export-symbol-helper.hpp
@@ -166,10 +182,16 @@ target_sources(
lib/utils/file-selection.hpp lib/utils/file-selection.hpp
lib/utils/filter-combo-box.cpp lib/utils/filter-combo-box.cpp
lib/utils/filter-combo-box.hpp 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.cpp
lib/utils/item-selection-helpers.hpp lib/utils/item-selection-helpers.hpp
lib/utils/layout-helpers.cpp lib/utils/layout-helpers.cpp
lib/utils/layout-helpers.hpp lib/utils/layout-helpers.hpp
lib/utils/list-controls.cpp
lib/utils/list-controls.hpp
lib/utils/list-editor.cpp
lib/utils/list-editor.hpp
lib/utils/log-helper.cpp lib/utils/log-helper.cpp
lib/utils/log-helper.hpp lib/utils/log-helper.hpp
lib/utils/math-helpers.cpp lib/utils/math-helpers.cpp
@@ -182,7 +204,6 @@ target_sources(
lib/utils/name-dialog.hpp lib/utils/name-dialog.hpp
lib/utils/non-modal-dialog.cpp lib/utils/non-modal-dialog.cpp
lib/utils/non-modal-dialog.hpp lib/utils/non-modal-dialog.hpp
lib/utils/obs-dock.hpp
lib/utils/obs-module-helper.cpp lib/utils/obs-module-helper.cpp
lib/utils/obs-module-helper.hpp lib/utils/obs-module-helper.hpp
lib/utils/path-helpers.cpp lib/utils/path-helpers.cpp
@@ -191,6 +212,9 @@ target_sources(
lib/utils/plugin-state-helpers.hpp lib/utils/plugin-state-helpers.hpp
lib/utils/priority-helper.cpp lib/utils/priority-helper.cpp
lib/utils/priority-helper.hpp lib/utils/priority-helper.hpp
lib/utils/properties-view.cpp
lib/utils/properties-view.hpp
lib/utils/properties-view.moc.hpp
lib/utils/regex-config.cpp lib/utils/regex-config.cpp
lib/utils/regex-config.hpp lib/utils/regex-config.hpp
lib/utils/resizing-text-edit.cpp lib/utils/resizing-text-edit.cpp
@@ -221,6 +245,8 @@ target_sources(
lib/utils/splitter-helpers.hpp lib/utils/splitter-helpers.hpp
lib/utils/status-control.cpp lib/utils/status-control.cpp
lib/utils/status-control.hpp lib/utils/status-control.hpp
lib/utils/string-list.cpp
lib/utils/string-list.hpp
lib/utils/switch-button.cpp lib/utils/switch-button.cpp
lib/utils/switch-button.hpp lib/utils/switch-button.hpp
lib/utils/sync-helpers.cpp lib/utils/sync-helpers.cpp
@@ -252,6 +278,11 @@ target_sources(
# --- End of section --- # --- End of section ---
# Subfolder for advanced scene switcher plugins
set(ADVSS_PLUGIN_FOLDER "advanced-scene-switcher-plugins")
target_compile_definitions(
${LIB_NAME} PRIVATE ADVSS_PLUGIN_FOLDER=\"${ADVSS_PLUGIN_FOLDER}\")
include(cmake/common/advss_helpers.cmake) include(cmake/common/advss_helpers.cmake)
setup_obs_lib_dependency(${LIB_NAME}) setup_obs_lib_dependency(${LIB_NAME})
setup_obs_lib_dependency(${PROJECT_NAME}) setup_obs_lib_dependency(${PROJECT_NAME})
@@ -337,6 +368,9 @@ if(OS_WINDOWS)
if(MSVC) if(MSVC)
target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267 target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267
/bigobj) /bigobj)
# Workaround for MSVC incompatibility in CI environment
add_compile_definitions(${target_name} _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
endif() endif()
target_sources(${LIB_NAME} PRIVATE lib/win/advanced-scene-switcher-win.cpp) target_sources(${LIB_NAME} PRIVATE lib/win/advanced-scene-switcher-win.cpp)
add_definitions(-D_WEBSOCKETPP_CPP11_STL_) add_definitions(-D_WEBSOCKETPP_CPP11_STL_)

View File

@@ -22,5 +22,6 @@ sudo snap install obs-studio
## Contributing ## Contributing
- If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin. - If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin.
- You can add custom conditions and actions at runtime using the API described [here](https://github.com/WarmUpTill/SceneSwitcher/wiki/Scripting).
- You can optionally use [pre-commit](https://pre-commit.com) to automatically handle formatting. - You can optionally use [pre-commit](https://pre-commit.com) to automatically handle formatting.
- If you wish to contribute translations, feel free to submit pull requests for the corresponding files under `data/locale`. - If you wish to contribute translations, feel free to submit pull requests for the corresponding files under `data/locale`.

View File

@@ -12,11 +12,13 @@ class localeEntry:
locale = "" locale = ""
widgetPlaceholders = [] widgetPlaceholders = []
qStringArgs = [] qStringArgs = []
lineNum = -1
def __init__(self, locale, widgetPlaceholders, qStringArgs) -> None: def __init__(self, locale, widgetPlaceholders, qStringArgs, lineNum) -> None:
self.locale = locale self.locale = locale
self.widgetPlaceholders = widgetPlaceholders self.widgetPlaceholders = widgetPlaceholders
self.qStringArgs = qStringArgs self.qStringArgs = qStringArgs
self.lineNum = lineNum
def getNonDefaultLocales(dir): def getNonDefaultLocales(dir):
@@ -32,7 +34,7 @@ def getNonDefaultLocales(dir):
def getAllLocaleEntries(file): def getAllLocaleEntries(file):
localeEntries = [] localeEntries = []
with open(file, "r", encoding="UTF-8") as f: with open(file, "r", encoding="UTF-8") as f:
for line in f.readlines(): for lineNum, line in enumerate(f):
widgetPlaceholders = [] widgetPlaceholders = []
qStringArgs = [] qStringArgs = []
@@ -49,7 +51,9 @@ def getAllLocaleEntries(file):
qStringArgs.append(match.group(0)) qStringArgs.append(match.group(0))
localeEntries.append( localeEntries.append(
localeEntry(line.split("=")[0], widgetPlaceholders, qStringArgs) localeEntry(
line.split("=")[0], widgetPlaceholders, qStringArgs, lineNum + 1
)
) )
return localeEntries return localeEntries
@@ -73,8 +77,8 @@ def checkLocaleEntries(file, expectedLocaleEntries):
if expectedLocaleEntry is None: if expectedLocaleEntry is None:
result = False result = False
print( print(
'ERROR: Locale entry "{}" from "{}" not found in "{}"'.format( 'ERROR: Locale entry "{}" from "{}:{}" not found in "{}"'.format(
localeEntry.locale, file, defaultLocaleFile localeEntry.locale, file, localeEntry.lineNum, defaultLocaleFile
) )
) )
continue continue
@@ -83,8 +87,13 @@ def checkLocaleEntries(file, expectedLocaleEntries):
if placeholder not in expectedLocaleEntry.widgetPlaceholders: if placeholder not in expectedLocaleEntry.widgetPlaceholders:
result = False result = False
print( print(
'ERROR: Locale entry "{}" from "{}" does contain "{}" widget placeholder while "{}" does not'.format( 'ERROR: Locale entry "{}" from "{}:{}" does contain "{}" widget placeholder while "{}:{}" does not'.format(
localeEntry.locale, file, placeholder, defaultLocaleFile localeEntry.locale,
file,
localeEntry.lineNum,
placeholder,
defaultLocaleFile,
expectedLocaleEntry.lineNum,
) )
) )
@@ -92,8 +101,8 @@ def checkLocaleEntries(file, expectedLocaleEntries):
if placeholder not in localeEntry.widgetPlaceholders: if placeholder not in localeEntry.widgetPlaceholders:
result = False result = False
print( print(
'ERROR: Locale entry "{}" from "{}" does not contain "{}" widget placeholder'.format( 'ERROR: Locale entry "{}" from "{}:{}" does not contain "{}" widget placeholder'.format(
localeEntry.locale, file, placeholder localeEntry.locale, file, localeEntry.lineNum, placeholder
) )
) )
@@ -101,8 +110,13 @@ def checkLocaleEntries(file, expectedLocaleEntries):
if arg not in expectedLocaleEntry.qStringArgs: if arg not in expectedLocaleEntry.qStringArgs:
result = False result = False
print( print(
'ERROR: Locale entry "{}" from "{}" does contain "{}" QString arg while "{}" does not'.format( 'ERROR: Locale entry "{}" from "{}:{}" does contain "{}" QString arg while "{}:{}" does not'.format(
localeEntry.locale, file, arg, defaultLocaleFile localeEntry.locale,
file,
localeEntry.lineNum,
arg,
defaultLocaleFile,
expectedLocaleEntry.lineNum,
) )
) )
@@ -110,8 +124,8 @@ def checkLocaleEntries(file, expectedLocaleEntries):
if arg not in localeEntry.qStringArgs: if arg not in localeEntry.qStringArgs:
result = False result = False
print( print(
'ERROR: Locale entry "{}" from "{}" does not contain "{}" QString arg'.format( 'ERROR: Locale entry "{}" from "{}:{}" does not contain "{}" QString arg'.format(
localeEntry.locale, file, arg localeEntry.locale, file, localeEntry.lineNum, arg
) )
) )

View File

@@ -1,33 +1,33 @@
{ {
"dependencies": { "dependencies": {
"obs-studio": { "obs-studio": {
"version": "29.1.2", "version": "30.0.2",
"baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags", "baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags",
"label": "OBS sources", "label": "OBS sources",
"hashes": { "hashes": {
"macos": "215f1fa5772c5dd9f3d6e35b0cb573912b00320149666a77864f9d305525504b", "macos": "be12c3ad0a85713750d8325e4b1db75086223402d7080d0e3c2833d7c5e83c27",
"windows-x64": "46d451f3f42b9d2c59339ec268165849c7b7904cdf1cc2a8d44c015815a9e37d" "windows-x64": "970058c49322cfa9cd6d620abb393fed89743ba7e74bd9dbb6ebe0ea8141d9c7"
} }
}, },
"prebuilt": { "prebuilt": {
"version": "2023-04-12", "version": "2023-11-03",
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download", "baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
"label": "Pre-Built obs-deps", "label": "Pre-Built obs-deps",
"hashes": { "hashes": {
"macos": "9535c6e1ad96f7d49960251e85a245774088d48da1d602bb82f734b10219125a", "macos": "90c2fc069847ec2768dcc867c1c63b112c615ed845a907dc44acab7a97181974",
"windows-x64": "c13a14a1acc4224b21304d97b63da4121de1ed6981297e50496fbc474abc0503" "windows-x64": "d0825a6fb65822c993a3059edfba70d72d2e632ef74893588cf12b1f0d329ce6"
} }
}, },
"qt6": { "qt6": {
"version": "2023-04-12", "version": "2023-11-03",
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download", "baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
"label": "Pre-Built Qt6", "label": "Pre-Built Qt6",
"hashes": { "hashes": {
"macos": "eb7614544ab4f3d2c6052c797635602280ca5b028a6b987523d8484222ce45d1", "macos": "ba4a7152848da0053f63427a2a2cb0a199af3992997c0db08564df6f48c9db98",
"windows-x64": "4d39364b8a8dee5aa24fcebd8440d5c22bb4551c6b440ffeacce7d61f2ed1add" "windows-x64": "bc57dedf76b47119a6dce0435a2f21b35b08c8f2948b1cb34a157320f77732d1"
}, },
"debugSymbols": { "debugSymbols": {
"windows-x64": "f34ee5067be19ed370268b15c53684b7b8aaa867dc800b68931df905d679e31f" "windows-x64": "fd8ecd1d8cd2ef049d9f4d7fb5c134f784836d6020758094855dfa98bd025036"
} }
} }
}, },

View File

@@ -8,14 +8,11 @@ if(BUILD_OUT_OF_TREE)
endif() endif()
endif() endif()
# Subfolder for advanced scene switcher plugins
set(_PLUGIN_FOLDER "adv-ss-plugins")
# --- MACOS section --- # --- MACOS section ---
if(OS_MACOS) if(OS_MACOS)
set(ADVSS_BUNDLE_DIR "advanced-scene-switcher.plugin") set(ADVSS_BUNDLE_DIR "advanced-scene-switcher.plugin")
set(ADVSS_BUNDLE_MODULE_DIR "${ADVSS_BUNDLE_DIR}/Contents/MacOS") set(ADVSS_BUNDLE_MODULE_DIR "${ADVSS_BUNDLE_DIR}/Contents/MacOS")
set(ADVSS_BUNDLE_PLUGIN_DIR ${ADVSS_BUNDLE_MODULE_DIR}/${_PLUGIN_FOLDER}) set(ADVSS_BUNDLE_PLUGIN_DIR ${ADVSS_BUNDLE_MODULE_DIR}/${ADVSS_PLUGIN_FOLDER})
function(install_advss_lib_helper target where) function(install_advss_lib_helper target where)
install( install(
@@ -60,10 +57,36 @@ if(OS_MACOS)
${dep}_Development) ${dep}_Development)
endfunction() endfunction()
function(install_advss_plugin_dependency_file ${target} dep) function(install_advss_plugin_dependency_file target dep)
target_sources(advanced-scene-switcher PRIVATE ${dep}) get_filename_component(_FILENAME ${dep} NAME)
set_source_files_properties(${dep} PROPERTIES MACOSX_PACKAGE_LOCATION string(REGEX REPLACE "\\.[^.]*$" "" _FILENAMENOEXT ${_FILENAME})
${ADVSS_BUNDLE_PLUGIN_DIR}) set(_DEP_NAME "${target}-${_FILENAMENOEXT}")
install(
FILES "${dep}"
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
COMPONENT ${_DEP_NAME}_Runtime
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
COMPONENT ${_DEP_NAME}_Runtime
NAMELINK_COMPONENT ${_DEP_NAME}_Development)
install(
FILES "${dep}"
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
COMPONENT obs_${_DEP_NAME}
EXCLUDE_FROM_ALL
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
COMPONENT obs_${_DEP_NAME}
EXCLUDE_FROM_ALL)
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND
"${CMAKE_COMMAND}" --install ${CMAKE_CURRENT_BINARY_DIR} --config
$<CONFIG> --prefix ${CMAKE_INSTALL_PREFIX} --component obs_${_DEP_NAME}
COMMENT "Installing ${_DEP_NAME} to OBS rundir\n"
VERBATIM)
endfunction() endfunction()
# --- End of section --- # --- End of section ---
@@ -128,8 +151,8 @@ else()
function(install_advss_plugin target) function(install_advss_plugin target)
plugin_install_helper( plugin_install_helper(
"${target}" "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" "${target}" "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
"${_PLUGIN_FOLDER}") "${ADVSS_PLUGIN_FOLDER}")
if(NOT OS_WINDOWS) if(NOT OS_WINDOWS)
set_target_properties(${target} PROPERTIES INSTALL_RPATH set_target_properties(${target} PROPERTIES INSTALL_RPATH
"$ORIGIN:$ORIGIN/..") "$ORIGIN:$ORIGIN/..")
@@ -143,12 +166,12 @@ else()
${dep} ${dep}
RUNTIME RUNTIME
DESTINATION DESTINATION
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
COMPONENT COMPONENT
${dep}_Runtime ${dep}_Runtime
LIBRARY LIBRARY
DESTINATION DESTINATION
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
COMPONENT COMPONENT
${dep}_Runtime ${dep}_Runtime
NAMELINK_COMPONENT NAMELINK_COMPONENT
@@ -159,13 +182,13 @@ else()
${dep} ${dep}
RUNTIME RUNTIME
DESTINATION DESTINATION
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
COMPONENT COMPONENT
obs_${dep} obs_${dep}
EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
LIBRARY LIBRARY
DESTINATION DESTINATION
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
COMPONENT COMPONENT
obs_${dep} obs_${dep}
EXCLUDE_FROM_ALL) EXCLUDE_FROM_ALL)
@@ -188,18 +211,18 @@ else()
install( install(
FILES "${dep}" FILES "${dep}"
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
COMPONENT ${_DEP_NAME}_Runtime COMPONENT ${_DEP_NAME}_Runtime
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
COMPONENT ${_DEP_NAME}_Runtime COMPONENT ${_DEP_NAME}_Runtime
NAMELINK_COMPONENT ${_DEP_NAME}_Development) NAMELINK_COMPONENT ${_DEP_NAME}_Development)
install( install(
FILES "${dep}" FILES "${dep}"
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
COMPONENT obs_${_DEP_NAME} COMPONENT obs_${_DEP_NAME}
EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}" DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
COMPONENT obs_${_DEP_NAME} COMPONENT obs_${_DEP_NAME}
EXCLUDE_FROM_ALL) EXCLUDE_FROM_ALL)
@@ -309,7 +332,6 @@ function(install_advss_plugin_dependency)
if(NOT PARSED_ARGS_TARGET) if(NOT PARSED_ARGS_TARGET)
message(FATAL_ERROR "You must provide a target") message(FATAL_ERROR "You must provide a target")
endif() endif()
set(_PLUGIN_FOLDER "adv-ss-plugins")
foreach(_DEPENDENCY ${PARSED_ARGS_DEPENDENCIES}) foreach(_DEPENDENCY ${PARSED_ARGS_DEPENDENCIES})
if(EXISTS ${_DEPENDENCY}) if(EXISTS ${_DEPENDENCY})
install_advss_plugin_dependency_file(${PARSED_ARGS_TARGET} ${_DEPENDENCY}) install_advss_plugin_dependency_file(${PARSED_ARGS_TARGET} ${_DEPENDENCY})

View File

@@ -159,7 +159,7 @@ AdvSceneSwitcher.condition.media.source="Quelle"
AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in" AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in"
AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in" AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!" AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}Status ist{{states}}und{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}Status ist{{states}}und{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="entspricht genau" AdvSceneSwitcher.condition.video.condition.match="entspricht genau"
AdvSceneSwitcher.condition.video.condition.differ="stimmt nicht überein" AdvSceneSwitcher.condition.video.condition.differ="stimmt nicht überein"
@@ -321,11 +321,8 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Nächster Treffer bei: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren"
AdvSceneSwitcher.condition.sceneTransform="Szenenelement transformieren" AdvSceneSwitcher.condition.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Transformation erhalten" AdvSceneSwitcher.condition.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Auf{{scenes}}{{sources}}{{types}}" AdvSceneSwitcher.condition.sceneTransform.condition.match="entspricht Transformation"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="entspricht Transformation" AdvSceneSwitcher.condition.sceneTransform.condition.changed="hat sich geändert"
AdvSceneSwitcher.condition.sceneTransform.entry.type.changed="hat sich geändert"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Übergang" AdvSceneSwitcher.condition.transition="Übergang"
AdvSceneSwitcher.condition.transition.type.current="Aktueller Übergangstyp ist" AdvSceneSwitcher.condition.transition.type.current="Aktueller Übergangstyp ist"
AdvSceneSwitcher.condition.transition.type.duration="Aktuelle Übergangsdauer beträgt" AdvSceneSwitcher.condition.transition.type.duration="Aktuelle Übergangsdauer beträgt"
@@ -427,7 +424,6 @@ AdvSceneSwitcher.action.replay.saveWarn="Warnung: Ein zu häufiges Speichern kan
AdvSceneSwitcher.action.replay.type.stop="Replay Buffer stoppen" AdvSceneSwitcher.action.replay.type.stop="Replay Buffer stoppen"
AdvSceneSwitcher.action.replay.type.start="Replay Buffer starten" AdvSceneSwitcher.action.replay.type.start="Replay Buffer starten"
AdvSceneSwitcher.action.replay.type.save="Replay Buffer speichern" AdvSceneSwitcher.action.replay.type.save="Replay Buffer speichern"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Stream" AdvSceneSwitcher.action.streaming="Stream"
AdvSceneSwitcher.action.streaming.type.stop="Stream stoppen" AdvSceneSwitcher.action.streaming.type.stop="Stream stoppen"
AdvSceneSwitcher.action.streaming.type.start="Stream starten" AdvSceneSwitcher.action.streaming.type.start="Stream starten"
@@ -499,7 +495,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}" AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren" AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten" AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}" AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
AdvSceneSwitcher.action.file="Datei" AdvSceneSwitcher.action.file="Datei"
AdvSceneSwitcher.action.file.type.write="Schreiben" AdvSceneSwitcher.action.file.type.write="Schreiben"
AdvSceneSwitcher.action.file.type.append="Anhängen" AdvSceneSwitcher.action.file.type.append="Anhängen"

View File

@@ -1,7 +1,7 @@
AdvSceneSwitcher.pluginName="Advanced Scene Switcher" AdvSceneSwitcher.pluginName="Advanced Scene Switcher"
AdvSceneSwitcher.windowTitle="Advanced Scene Switcher" AdvSceneSwitcher.windowTitle="Advanced Scene Switcher"
; General Tab # General Tab
AdvSceneSwitcher.generalTab.title="General" AdvSceneSwitcher.generalTab.title="General"
AdvSceneSwitcher.generalTab.status="Status" AdvSceneSwitcher.generalTab.status="Status"
AdvSceneSwitcher.generalTab.status.hotkeytips="Hotkeys can be defined in the OBS settings" AdvSceneSwitcher.generalTab.status.hotkeytips="Hotkeys can be defined in the OBS settings"
@@ -37,6 +37,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
AdvSceneSwitcher.generalTab.generalBehavior.comboBoxFilterDisable="Disable filtering by typing in drop down menus" AdvSceneSwitcher.generalTab.generalBehavior.comboBoxFilterDisable="Disable filtering by typing in drop down menus"
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="Display warning if plugins cannot be loaded" AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="Display warning if plugins cannot be loaded"
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>Loading of the following plugin libraries was unsuccessful, which could result in some Advanced Scene Switcher functions not being available:%1Check the OBS logs for details.<br>This message can be disabled on the General tab.</body></html>" AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>Loading of the following plugin libraries was unsuccessful, which could result in some Advanced Scene Switcher functions not being available:%1Check the OBS logs for details.<br>This message can be disabled on the General tab.</body></html>"
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros" AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior" AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
AdvSceneSwitcher.generalTab.priority="Priority" AdvSceneSwitcher.generalTab.priority="Priority"
@@ -69,7 +70,7 @@ AdvSceneSwitcher.generalTab.transitionOverride="Set transition overrides"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type" AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type" AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
; Variables Tab # Variables Tab
AdvSceneSwitcher.variableTab.title="Variables" AdvSceneSwitcher.variableTab.title="Variables"
AdvSceneSwitcher.variableTab.help="Variables can be used in many places throughout the plugin.\n\nClick on the highlighted plus symbol to add a new variable." AdvSceneSwitcher.variableTab.help="Variables can be used in many places throughout the plugin.\n\nClick on the highlighted plus symbol to add a new variable."
AdvSceneSwitcher.variableTab.variableAddButton.tooltip="Add new variable" AdvSceneSwitcher.variableTab.variableAddButton.tooltip="Add new variable"
@@ -88,7 +89,7 @@ AdvSceneSwitcher.variableTab.lastChanged.text="%1 seconds ago"
AdvSceneSwitcher.variableTab.lastChanged.text.none="No change since launch" AdvSceneSwitcher.variableTab.lastChanged.text.none="No change since launch"
AdvSceneSwitcher.variableTab.lastChanged.tooltip="Times changed: %1\n\nPrevious value: %2" AdvSceneSwitcher.variableTab.lastChanged.tooltip="Times changed: %1\n\nPrevious value: %2"
; Action Queue Tab # Action Queue Tab
AdvSceneSwitcher.actionQueueTab.title="Action Queues" 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.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.queueAddButton.tooltip="Add new action queue"
@@ -99,10 +100,10 @@ AdvSceneSwitcher.actionQueueTab.isRunning.header="Is running"
AdvSceneSwitcher.actionQueueTab.runOnStartup.header="Run on startup" AdvSceneSwitcher.actionQueueTab.runOnStartup.header="Run on startup"
AdvSceneSwitcher.actionQueueTab.yes="Yes" AdvSceneSwitcher.actionQueueTab.yes="Yes"
AdvSceneSwitcher.actionQueueTab.no="No" AdvSceneSwitcher.actionQueueTab.no="No"
AdvSceneSwitcher.actionQueueTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?" AdvSceneSwitcher.actionQueueTab.removeSingleQueuePopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.actionQueueTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 action queues?" AdvSceneSwitcher.actionQueueTab.removeMultipleQueuesPopup.text="Are you sure you want to remove %1 action queues?"
; Websocket Connections Tab # Websocket Connections Tab
AdvSceneSwitcher.websocketConnectionTab.title="Websocket Connections" 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.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.websocketConnectionAddButton.tooltip="Add new webscoket connection"
@@ -116,7 +117,7 @@ AdvSceneSwitcher.websocketConnectionTab.protocol.no="No"
AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?" 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?" AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 connections?"
; Twitch Connections Tab # Twitch Connections Tab
AdvSceneSwitcher.twitchConnectionTab.title="Twitch Connections" 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.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.twitchConnectionAddButton.tooltip.tooltip="Add new Twitch connection"
@@ -129,7 +130,7 @@ AdvSceneSwitcher.twitchConnectionTab.no="No"
AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?" 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?" AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 Twitch connections?"
; Macro Tab # Macro Tab
AdvSceneSwitcher.macroTab.title="Macro" AdvSceneSwitcher.macroTab.title="Macro"
AdvSceneSwitcher.macroTab.macros="Macros" AdvSceneSwitcher.macroTab.macros="Macros"
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab." AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
@@ -175,13 +176,20 @@ AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
AdvSceneSwitcher.macroTab.maximize="Maximize" AdvSceneSwitcher.macroTab.maximize="Maximize"
AdvSceneSwitcher.macroTab.minimize="Minimize" AdvSceneSwitcher.macroTab.minimize="Minimize"
AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label" AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label"
AdvSceneSwitcher.macroTab.segment.customLabelRename="Rename custom label"
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label" AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label"
AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..." AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..."
AdvSceneSwitcher.macroTab.segment.copy="Copy" AdvSceneSwitcher.macroTab.segment.copy="Copy"
AdvSceneSwitcher.macroTab.segment.paste="Paste" AdvSceneSwitcher.macroTab.segment.paste="Paste"
AdvSceneSwitcher.macroTab.segment.pasteAction="Paste as action"
AdvSceneSwitcher.macroTab.segment.pasteElseAction="Paste as else-action"
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings" AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings" AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
AdvSceneSwitcher.macroTab.generalSettings="General 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.dockSettings="Dock settings"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros" AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently" AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
@@ -226,7 +234,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.elseActionUpButton="Move selected action up in the else section"
AdvSceneSwitcher.macroTab.tooltip.elseActionDownButton="Move selected action down 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.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.pause="Pause"
AdvSceneSwitcher.macroDock.unpause="Unpause" AdvSceneSwitcher.macroDock.unpause="Unpause"
@@ -234,11 +242,11 @@ AdvSceneSwitcher.macroDock.run="Run"
AdvSceneSwitcher.macroDock.statusLabel.true="Conditions are true." AdvSceneSwitcher.macroDock.statusLabel.true="Conditions are true."
AdvSceneSwitcher.macroDock.statusLabel.false="Conditions are false." AdvSceneSwitcher.macroDock.statusLabel.false="Conditions are false."
; Macro List # Macro List
AdvSceneSwitcher.macroList.deleted="deleted" AdvSceneSwitcher.macroList.deleted="deleted"
AdvSceneSwitcher.macroList.duplicate="\"%1\" is alreay selected!" AdvSceneSwitcher.macroList.duplicate="\"%1\" is alreay selected!"
; Macro Logic # Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry" AdvSceneSwitcher.logic.none="Ignore entry"
AdvSceneSwitcher.logic.and="And" AdvSceneSwitcher.logic.and="And"
AdvSceneSwitcher.logic.or="Or" AdvSceneSwitcher.logic.or="Or"
@@ -247,7 +255,8 @@ AdvSceneSwitcher.logic.orNot="Or not"
AdvSceneSwitcher.logic.rootNone="If" AdvSceneSwitcher.logic.rootNone="If"
AdvSceneSwitcher.logic.not="If not" AdvSceneSwitcher.logic.not="If not"
; Macro Conditions # Macro Conditions
AdvSceneSwitcher.condition.unknown="Unknown condition"
AdvSceneSwitcher.condition.audio="Audio" AdvSceneSwitcher.condition.audio="Audio"
AdvSceneSwitcher.condition.audio.state.below="below" AdvSceneSwitcher.condition.audio.state.below="below"
AdvSceneSwitcher.condition.audio.state.exact="exactly" AdvSceneSwitcher.condition.audio.state.exact="exactly"
@@ -303,11 +312,14 @@ AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{condition
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Media" AdvSceneSwitcher.condition.media="Media"
AdvSceneSwitcher.condition.media.checkType.state="State matches"
AdvSceneSwitcher.condition.media.checkType.time="Time restriction matches"
AdvSceneSwitcher.condition.media.source="Source" AdvSceneSwitcher.condition.media.source="Source"
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on" AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
AdvSceneSwitcher.condition.media.allOnScene="All media sources on" AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!" AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}state is{{states}}and{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.layout="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}{{states}}{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}state is{{states}}and{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="exactly matches" AdvSceneSwitcher.condition.video.condition.match="exactly matches"
AdvSceneSwitcher.condition.video.condition.differ="does not match" AdvSceneSwitcher.condition.video.condition.differ="does not match"
@@ -453,11 +465,16 @@ AdvSceneSwitcher.condition.source.type.settingsMatch="Settings match"
AdvSceneSwitcher.condition.source.type.settingsChanged="Settings changed" AdvSceneSwitcher.condition.source.type.settingsChanged="Settings changed"
AdvSceneSwitcher.condition.source.type.individualSettingMatches="Setting value matches" AdvSceneSwitcher.condition.source.type.individualSettingMatches="Setting value matches"
AdvSceneSwitcher.condition.source.type.individualSettingChanged="Setting value changed" 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.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.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.sceneVisibilityHint="Scene specific visibility can be checked using the \"Scene item visibility\" condition"
AdvSceneSwitcher.condition.source.getSettings="Get current settings" 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.line2="{{settings}}"
AdvSceneSwitcher.condition.source.entry.line3="{{regex}}{{getSettings}}" AdvSceneSwitcher.condition.source.entry.line3="{{regex}}{{getSettings}}"
AdvSceneSwitcher.condition.virtualCamera="Virtual camera" AdvSceneSwitcher.condition.virtualCamera="Virtual camera"
@@ -522,11 +539,17 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}On repeat update selected date to repeat date" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}On repeat update selected date to repeat date"
AdvSceneSwitcher.condition.sceneTransform="Scene item transform" AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform" AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="On{{scenes}}{{sources}}{{types}}" AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="Get current value"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="matches transform" AdvSceneSwitcher.condition.sceneTransform.settingsType.all="All"
AdvSceneSwitcher.condition.sceneTransform.entry.type.changed="has changed" AdvSceneSwitcher.condition.sceneTransform.settingsType.single="Single"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}" AdvSceneSwitcher.condition.sceneTransform.condition.match="matches"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}}{{getSettings}}" AdvSceneSwitcher.condition.sceneTransform.condition.changed="has changed"
AdvSceneSwitcher.condition.sceneTransform.compare.less="less"
AdvSceneSwitcher.condition.sceneTransform.compare.equal="equal"
AdvSceneSwitcher.condition.sceneTransform.compare.more="larger than"
AdvSceneSwitcher.condition.sceneTransform.entry="On{{scenes}}{{settingsType}}transform setting of{{sources}}{{conditions}}"
AdvSceneSwitcher.condition.sceneTransform.entry.singleSettingCompare="{{setting}}{{compare}}{{singleSettingValue}}"
AdvSceneSwitcher.condition.sceneTransform.entry.options="{{regex}}{{getSettings}}{{getCurrentValue}}"
AdvSceneSwitcher.condition.transition="Transition" AdvSceneSwitcher.condition.transition="Transition"
AdvSceneSwitcher.condition.transition.type.current="Current transition type is" AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is" AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
@@ -611,7 +634,7 @@ AdvSceneSwitcher.condition.slideshow.condition.slideChanged="Slide changed"
AdvSceneSwitcher.condition.slideshow.condition.slideIndex="Current slide number is" AdvSceneSwitcher.condition.slideshow.condition.slideIndex="Current slide number is"
AdvSceneSwitcher.condition.slideshow.condition.slidePath="Current slide path is" AdvSceneSwitcher.condition.slideshow.condition.slidePath="Current slide path is"
AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="Information about the slide show status will only be updated based on the configured time between slides" AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="Information about the slide show status will only be updated based on the configured time between slides"
AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}" AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}{{regex}}"
AdvSceneSwitcher.condition.twitch="Twitch" AdvSceneSwitcher.condition.twitch="Twitch"
AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online="Stream went online" AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online="Stream went online"
AdvSceneSwitcher.condition.twitch.type.event.channel.stream.offline="Stream went offline" AdvSceneSwitcher.condition.twitch.type.event.channel.stream.offline="Stream went offline"
@@ -694,8 +717,19 @@ AdvSceneSwitcher.condition.folder.condition.folderRemove="A directory was remove
AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}" 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.enableFilter="Only evaluate to true, if the changed path matches a patern"
AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}" AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
AdvSceneSwitcher.condition.usb="USB"
AdvSceneSwitcher.condition.usb.description="A USB device matching the following properties is connected:"
AdvSceneSwitcher.condition.usb.vendorID="Vendor ID:"
AdvSceneSwitcher.condition.usb.productID="Product ID:"
AdvSceneSwitcher.condition.usb.busNumber="Bus Number:"
AdvSceneSwitcher.condition.usb.deviceAddress="Device Address:"
AdvSceneSwitcher.condition.usb.vendorName="Vendor Name:"
AdvSceneSwitcher.condition.usb.productName="Product Name:"
AdvSceneSwitcher.condition.usb.serialNumber="Serial Number:"
AdvSceneSwitcher.condition.noDevicesFoundWarning="No USB devices detected!\nThe plugin might not have the required permissions to check for USB devices."
; Macro Actions # Macro Actions
AdvSceneSwitcher.action.unknown="Unknown action"
AdvSceneSwitcher.action.scene="Switch scene" AdvSceneSwitcher.action.scene="Switch scene"
AdvSceneSwitcher.action.scene.type.program="Program" AdvSceneSwitcher.action.scene.type.program="Program"
AdvSceneSwitcher.action.scene.type.preview="Preview" AdvSceneSwitcher.action.scene.type.preview="Preview"
@@ -739,10 +773,12 @@ AdvSceneSwitcher.action.recording.split.hint="Make sure to enable automatic file
AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}" AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
AdvSceneSwitcher.action.replay="Replay buffer" AdvSceneSwitcher.action.replay="Replay buffer"
AdvSceneSwitcher.action.replay.saveWarn="Warning: Saving too frequently might result in the replay buffer not actually being saved!" AdvSceneSwitcher.action.replay.saveWarn="Warning: Saving too frequently might result in the replay buffer not actually being saved!"
AdvSceneSwitcher.action.replay.durationWarn="Warning: Changing the maximum replay time will only apply the next time the replay buffer is started!"
AdvSceneSwitcher.action.replay.type.stop="Stop replay buffer" AdvSceneSwitcher.action.replay.type.stop="Stop replay buffer"
AdvSceneSwitcher.action.replay.type.start="Start replay buffer" AdvSceneSwitcher.action.replay.type.start="Start replay buffer"
AdvSceneSwitcher.action.replay.type.save="Save replay buffer" AdvSceneSwitcher.action.replay.type.save="Save replay buffer"
AdvSceneSwitcher.action.replay.entry="{{actions}}" AdvSceneSwitcher.action.replay.type.duration="Set maximum replay time"
AdvSceneSwitcher.action.replay.entry="{{actions}}{{duration}}"
AdvSceneSwitcher.action.streaming="Streaming" AdvSceneSwitcher.action.streaming="Streaming"
AdvSceneSwitcher.action.streaming.type.stop="Stop streaming" AdvSceneSwitcher.action.streaming.type.stop="Stop streaming"
AdvSceneSwitcher.action.streaming.type.start="Start streaming" AdvSceneSwitcher.action.streaming.type.start="Start streaming"
@@ -829,9 +865,13 @@ 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.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.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.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.regular="actions"
AdvSceneSwitcher.action.macro.type.run.actionType.else="else-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.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.stop="Stop actions"
AdvSceneSwitcher.action.macro.type.disableAction="Disable action" AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
AdvSceneSwitcher.action.macro.type.enableAction="Enable action" AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
@@ -879,7 +919,8 @@ AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position" AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}" AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Scene item transform" AdvSceneSwitcher.action.sceneTransform="Scene item transform"
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform" AdvSceneSwitcher.action.sceneTransform.type.manual="Set transform string"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Set transform setting"
AdvSceneSwitcher.action.sceneTransform.type.reset="Reset transform" AdvSceneSwitcher.action.sceneTransform.type.reset="Reset transform"
AdvSceneSwitcher.action.sceneTransform.type.rotate="Rotate by" AdvSceneSwitcher.action.sceneTransform.type.rotate="Rotate by"
AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="Flip horizontally" AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="Flip horizontally"
@@ -890,7 +931,8 @@ AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="Center to screen"
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Center vertically" AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Center vertically"
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Center horizontally" AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Center horizontally"
AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform" AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}" AdvSceneSwitcher.action.sceneTransform.getCurrentValue="Get current value"
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
AdvSceneSwitcher.action.file="File" AdvSceneSwitcher.action.file="File"
AdvSceneSwitcher.action.file.type.write="Write" AdvSceneSwitcher.action.file.type.write="Write"
AdvSceneSwitcher.action.file.type.append="Append" AdvSceneSwitcher.action.file.type.append="Append"
@@ -987,6 +1029,7 @@ AdvSceneSwitcher.action.variable.type.setToTempvar.help="This action type will a
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index" AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
AdvSceneSwitcher.action.variable.type.padValue="Pad current value" AdvSceneSwitcher.action.variable.type.padValue="Pad current value"
AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value" AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values"
AdvSceneSwitcher.action.variable.truncateOrPadDirection.left="Left" AdvSceneSwitcher.action.variable.truncateOrPadDirection.left="Left"
AdvSceneSwitcher.action.variable.truncateOrPadDirection.right="Right" AdvSceneSwitcher.action.variable.truncateOrPadDirection.right="Right"
AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":" AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":"
@@ -1076,7 +1119,7 @@ AdvSceneSwitcher.action.log="Log"
AdvSceneSwitcher.action.log.placeholder="My log message!" AdvSceneSwitcher.action.log.placeholder="My log message!"
AdvSceneSwitcher.action.log.entry="Write to OBS log:{{logMessage}}" AdvSceneSwitcher.action.log.entry="Write to OBS log:{{logMessage}}"
; Hotkey # Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher" AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher" AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the Advanced Scene Switcher" AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the Advanced Scene Switcher"
@@ -1103,6 +1146,11 @@ AdvSceneSwitcher.item.emptyName="Empty name not allowed!"
AdvSceneSwitcher.item.nameNotAvailable="Name not available!" AdvSceneSwitcher.item.nameNotAvailable="Name not available!"
AdvSceneSwitcher.item.nameReserved="Name reserved!" AdvSceneSwitcher.item.nameReserved="Name reserved!"
AdvSceneSwitcher.listControls.add="Add"
AdvSceneSwitcher.listControls.remove="Remove"
AdvSceneSwitcher.listControls.up="Up"
AdvSceneSwitcher.listControls.down="Down"
AdvSceneSwitcher.macroSegmentSelection.invalid="Invalid selection!" AdvSceneSwitcher.macroSegmentSelection.invalid="Invalid selection!"
AdvSceneSwitcher.validation.text.maxLength="Only %1 characters are allowed in this field!" AdvSceneSwitcher.validation.text.maxLength="Only %1 characters are allowed in this field!"
@@ -1117,6 +1165,7 @@ AdvSceneSwitcher.variable.save="Save / load behavior"
AdvSceneSwitcher.variable.save.dontSave="Don't save variable value" AdvSceneSwitcher.variable.save.dontSave="Don't save variable value"
AdvSceneSwitcher.variable.save.save="Save variable value" AdvSceneSwitcher.variable.save.save="Save variable value"
AdvSceneSwitcher.variable.save.default="Set to 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" AdvSceneSwitcher.tooltip.availableVariables="Variables are supported, use ${VarName} to retrieve the value of VarName"
@@ -1425,24 +1474,13 @@ AdvSceneSwitcher.tempVar.twitch.status.prediction.description="The status of the
AdvSceneSwitcher.tempVar.twitch.id.goal="Goal ID" AdvSceneSwitcher.tempVar.twitch.id.goal="Goal ID"
AdvSceneSwitcher.tempVar.twitch.type.goal="Goal type" AdvSceneSwitcher.tempVar.twitch.type.goal="Goal type"
AdvSceneSwitcher.tempVar.twitch.type.goal.description="The type of goal. Possible values are:\n AdvSceneSwitcher.tempVar.twitch.type.goal.description="The type of goal. Possible values are:\n follow — The goal is to increase followers.\n subscription — The goal is to increase subscriptions. This type shows the net increase or decrease in tier points associated with the subscriptions.\n subscription_count — The goal is to increase subscriptions. This type shows the net increase or decrease in the number of subscriptions.\n new_subscription — The goal is to increase subscriptions. This type shows only the net increase in tier points associated with the subscriptions (it does not account for users that unsubscribed since the goal started).\n new_subscription_count — The goal is to increase subscriptions. This type shows only the net increase in the number of subscriptions (it does not account for users that unsubscribed since the goal started)."
follow — The goal is to increase followers.\n
subscription — The goal is to increase subscriptions. This type shows the net increase or decrease in tier points associated with the subscriptions.\n
subscription_count — The goal is to increase subscriptions. This type shows the net increase or decrease in the number of subscriptions.\n
new_subscription — The goal is to increase subscriptions. This type shows only the net increase in tier points associated with the subscriptions (it does not account for users that unsubscribed since the goal started).\n
new_subscription_count — The goal is to increase subscriptions. This type shows only the net increase in the number of subscriptions (it does not account for users that unsubscribed since the goal started)."
AdvSceneSwitcher.tempVar.twitch.description.goal="Goal description" AdvSceneSwitcher.tempVar.twitch.description.goal="Goal description"
AdvSceneSwitcher.tempVar.twitch.description.goal.description="A description of the goal, if specified. The description may contain a maximum of 40 characters." AdvSceneSwitcher.tempVar.twitch.description.goal.description="A description of the goal, if specified. The description may contain a maximum of 40 characters."
AdvSceneSwitcher.tempVar.twitch.is_achieved.goal="Goal is achieved" AdvSceneSwitcher.tempVar.twitch.is_achieved.goal="Goal is achieved"
AdvSceneSwitcher.tempVar.twitch.is_achieved.goal.description="A Boolean value that indicates whether the broadcaster achieved their goal. Is true if the goal was achieved; otherwise, false." AdvSceneSwitcher.tempVar.twitch.is_achieved.goal.description="A Boolean value that indicates whether the broadcaster achieved their goal. Is true if the goal was achieved; otherwise, false."
AdvSceneSwitcher.tempVar.twitch.current_amount.goal="Goal current amount" AdvSceneSwitcher.tempVar.twitch.current_amount.goal="Goal current amount"
AdvSceneSwitcher.tempVar.twitch.current_amount.goal.description="The goal's current value.\n AdvSceneSwitcher.tempVar.twitch.current_amount.goal.description="The goal's current value.\nThe goal's type determines how this value is increased or decreased.\n If type is follow, this field is set to the broadcaster's current number of followers. This number increases with new followers and decreases when users unfollow the broadcaster.\n If type is subscription, this field is increased and decreased by the points value associated with the subscription tier. For example, if a tier-two subscription is worth 2 points, this field is increased or decreased by 2, not 1.\n If type is subscription_count, this field is increased by 1 for each new subscription and decreased by 1 for each user that unsubscribes.\n If type is new_subscription, this field is increased by the points value associated with the subscription tier. For example, if a tier-two subscription is worth 2 points, this field is increased by 2, not 1.\n If type is new_subscription_count, this field is increased by 1 for each new subscription."
The goal's type determines how this value is increased or decreased.\n
If type is follow, this field is set to the broadcaster's current number of followers. This number increases with new followers and decreases when users unfollow the broadcaster.\n
If type is subscription, this field is increased and decreased by the points value associated with the subscription tier. For example, if a tier-two subscription is worth 2 points, this field is increased or decreased by 2, not 1.\n
If type is subscription_count, this field is increased by 1 for each new subscription and decreased by 1 for each user that unsubscribes.\n
If type is new_subscription, this field is increased by the points value associated with the subscription tier. For example, if a tier-two subscription is worth 2 points, this field is increased by 2, not 1.\n
If type is new_subscription_count, this field is increased by 1 for each new subscription."
AdvSceneSwitcher.tempVar.twitch.target_amount.goal="Goal target amount" AdvSceneSwitcher.tempVar.twitch.target_amount.goal="Goal target amount"
AdvSceneSwitcher.tempVar.twitch.target_amount.goal.description="The goal's target value. For example, if the broadcaster has 200 followers before creating the goal, and their goal is to double that number, this field is set to 400." AdvSceneSwitcher.tempVar.twitch.target_amount.goal.description="The goal's target value. For example, if the broadcaster has 200 followers before creating the goal, and their goal is to double that number, this field is set to 400."
AdvSceneSwitcher.tempVar.twitch.started_at.goal="Goal start time" AdvSceneSwitcher.tempVar.twitch.started_at.goal="Goal start time"
@@ -1752,6 +1790,32 @@ AdvSceneSwitcher.tempVar.folder.removedFiles="Removed files"
AdvSceneSwitcher.tempVar.folder.newDirs="New directories" AdvSceneSwitcher.tempVar.folder.newDirs="New directories"
AdvSceneSwitcher.tempVar.folder.removedDirs="Removed 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.tempVar.slideShow.index="Slide index"
AdvSceneSwitcher.tempVar.slideShow.index.description="The index of the slide currently being displayed.\nIf the index cannot be determined, -1 is returned."
AdvSceneSwitcher.tempVar.slideShow.path="Slide path"
AdvSceneSwitcher.tempVar.slideShow.path.description="The filesystem path of the slide currently being displayed."
AdvSceneSwitcher.tempVar.slideShow.fileName="Slide file name"
AdvSceneSwitcher.tempVar.slideShow.fileName.description="The name of file of the slide currently being displayed."
AdvSceneSwitcher.tempVar.usb.vendorID="Vendor ID"
AdvSceneSwitcher.tempVar.usb.productID="Product ID"
AdvSceneSwitcher.tempVar.usb.busNumber="Bus Number"
AdvSceneSwitcher.tempVar.usb.deviceAddress="Device Address"
AdvSceneSwitcher.tempVar.usb.vendorName="Vendor Name"
AdvSceneSwitcher.tempVar.usb.productName="Product Name"
AdvSceneSwitcher.tempVar.usb.serialNumber="Serial Number"
AdvSceneSwitcher.tempVar.transform.setting="Setting value"
AdvSceneSwitcher.tempVar.transform.settings="Settings string"
AdvSceneSwitcher.selectScene="--select scene--" AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene" AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene" AdvSceneSwitcher.selectCurrentScene="Current Scene"
@@ -1796,6 +1860,20 @@ AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
AdvSceneSwitcher.settings.suffix.type.group=" (Group)" AdvSceneSwitcher.settings.suffix.type.group=" (Group)"
AdvSceneSwitcher.settings.suffix.type.colorAlpha=" (Color alpha)" AdvSceneSwitcher.settings.suffix.type.colorAlpha=" (Color alpha)"
AdvSceneSwitcher.setting.select="--select setting--" AdvSceneSwitcher.setting.select="--select setting--"
AdvSceneSwitcher.setting.transform.alignment="Positional Alignment"
AdvSceneSwitcher.setting.transform.cropBottom="[Crop]Bottom"
AdvSceneSwitcher.setting.transform.cropLeft="[Crop]Left"
AdvSceneSwitcher.setting.transform.cropRight="[Crop]Right"
AdvSceneSwitcher.setting.transform.cropTop="[Crop]Top"
AdvSceneSwitcher.setting.transform.boundingBoxType="Bounding Box Type"
AdvSceneSwitcher.setting.transform.boundingBoxAlign="Alignment in Bounding Box"
AdvSceneSwitcher.setting.transform.boundingBoxSize="Bounding Box Size"
AdvSceneSwitcher.setting.transform.rotation="Rotation"
AdvSceneSwitcher.setting.transform.scale="Scale"
AdvSceneSwitcher.setting.transform.size="Size"
AdvSceneSwitcher.setting.transform.position="Position"
AdvSceneSwitcher.setting.transform.height="Height"
AdvSceneSwitcher.setting.transform.width="Width"
AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type" AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type"
@@ -1843,8 +1921,46 @@ AdvSceneSwitcher.audio.monitor.both="Monitor and Output"
AdvSceneSwitcher.noSettingsButtons="No buttons found!" AdvSceneSwitcher.noSettingsButtons="No buttons found!"
; Legacy tabs below - please don't waste your time adding translations for these :) AdvSceneSwitcher.clearBufferOnMatch="Clear message buffer when matching message was found"
; Transition Tab
AdvSceneSwitcher.script.settings="Settings"
AdvSceneSwitcher.script.timeout="Script timeout:{{timeout}}"
# This secion is copied from the OBS locale files
# commonly shared locale
Browse="Browse"
Show="Show"
Hide="Hide"
# properties window
Basic.PropertiesWindow="Properties for '%1'"
Basic.PropertiesWindow.AutoSelectFormat="%1 (autoselect: %2)"
Basic.PropertiesWindow.SelectColor="Select color"
Basic.PropertiesWindow.SelectFont="Select font"
Basic.PropertiesWindow.SelectFont.WindowTitle="Pick a Font"
Basic.PropertiesWindow.ConfirmTitle="Settings Changed"
Basic.PropertiesWindow.Confirm="There are unsaved changes. Do you want to keep them?"
Basic.PropertiesWindow.NoProperties="No properties available"
Basic.PropertiesWindow.AddFiles="Add Files"
Basic.PropertiesWindow.AddDir="Add Directory"
Basic.PropertiesWindow.AddURL="Add Path/URL"
Basic.PropertiesWindow.AddEditableListDir="Add directory to '%1'"
Basic.PropertiesWindow.AddEditableListFiles="Add files to '%1'"
Basic.PropertiesWindow.AddEditableListEntry="Add entry to '%1'"
Basic.PropertiesWindow.EditEditableListEntry="Edit entry from '%1'"
# properties view
Basic.PropertiesView.FPS.Simple="Simple FPS Values"
Basic.PropertiesView.FPS.Rational="Rational FPS Values"
Basic.PropertiesView.FPS.ValidFPSRanges="Valid FPS Ranges:"
Basic.PropertiesView.UrlButton.Text="Open this link in your default web browser?"
Basic.PropertiesView.UrlButton.Text.Url="URL: %1"
Basic.PropertiesView.UrlButton.OpenUrl="Open URL"
# Legacy tabs below - please don't waste your time adding translations for these :)
# Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition" AdvSceneSwitcher.transitionTab.title="Transition"
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B" AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>" AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
@@ -1855,7 +1971,7 @@ AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Click on the plus symbol
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Switch transition{{defTransitionDelay}}after scene change." AdvSceneSwitcher.transitionTab.defaultTransition.delay="Switch transition{{defTransitionDelay}}after scene change."
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="The delay is used to avoid cancelled scene switches, which can happen if the transition type is changed while a transition is still ongoing." AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="The delay is used to avoid cancelled scene switches, which can happen if the transition type is changed while a transition is still ongoing."
; Pause Scenes Tab # Pause Scenes Tab
AdvSceneSwitcher.pauseTab.title="Pause" AdvSceneSwitcher.pauseTab.title="Pause"
AdvSceneSwitcher.pauseTab.pauseOnScene="Pause the Scene Switcher on scene" AdvSceneSwitcher.pauseTab.pauseOnScene="Pause the Scene Switcher on scene"
AdvSceneSwitcher.pauseTab.pauseInFocus1="Pause the Scene Switcher when " AdvSceneSwitcher.pauseTab.pauseInFocus1="Pause the Scene Switcher when "
@@ -1866,7 +1982,7 @@ AdvSceneSwitcher.pauseTab.pauseTargetAll="all"
AdvSceneSwitcher.pauseTab.pauseEntry="Pause{{pauseTargets}}checks when{{pauseTypes}}{{scenes}}{{windows}}" AdvSceneSwitcher.pauseTab.pauseEntry="Pause{{pauseTargets}}checks when{{pauseTypes}}{{scenes}}{{windows}}"
AdvSceneSwitcher.pauseTab.help="On this tab you can configure to pause individual switching methods if a scene is active or window is in focus.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.pauseTab.help="On this tab you can configure to pause individual switching methods if a scene is active or window is in focus.\n\nClick on the highlighted plus symbol to continue."
; Window Title Tab # Window Title Tab
AdvSceneSwitcher.windowTitleTab.title="Title" AdvSceneSwitcher.windowTitleTab.title="Title"
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Enter either direct window titles or valid regex. You can check syntax and matches for regular expressions using <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a></p></body></html>" AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Enter either direct window titles or valid regex. You can check syntax and matches for regular expressions using <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a></p></body></html>"
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Ignore this window name" AdvSceneSwitcher.windowTitleTab.stayInFocus1="Ignore this window name"
@@ -1878,14 +1994,14 @@ AdvSceneSwitcher.windowTitleTab.entry="{{windows}}{{scenes}}{{transitions}}{{ful
AdvSceneSwitcher.windowTitleTab.windowsHelp="Switch scenes based on the window title of running applications.\nThe following additional conditions can be selected:\nThe window is Fullscreen\nThe window is maximized\nThe window is focused\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.windowTitleTab.windowsHelp="Switch scenes based on the window title of running applications.\nThe following additional conditions can be selected:\nThe window is Fullscreen\nThe window is maximized\nThe window is focused\n\nClick on the highlighted plus symbol to continue."
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="If a window title is ignored the scene switcher will act as if the previously selected window is still in focus.\nThis will allow you to avoid scene switches, if you frequently switch to a different window, which shall not trigger a scene change.\n\nChoose a window or enter a window title above and click on the plus symbol below to add it to the list." AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="If a window title is ignored the scene switcher will act as if the previously selected window is still in focus.\nThis will allow you to avoid scene switches, if you frequently switch to a different window, which shall not trigger a scene change.\n\nChoose a window or enter a window title above and click on the plus symbol below to add it to the list."
; Executable Tab # Executable Tab
AdvSceneSwitcher.executableTab.title="Executable" AdvSceneSwitcher.executableTab.title="Executable"
AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven" AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven"
AdvSceneSwitcher.executableTab.requiresFocus="only if focused" AdvSceneSwitcher.executableTab.requiresFocus="only if focused"
AdvSceneSwitcher.executableTab.entry="When{{processes}}is running switch to{{scenes}}using{{transitions}}{{requiresFocus}}" AdvSceneSwitcher.executableTab.entry="When{{processes}}is running switch to{{scenes}}using{{transitions}}{{requiresFocus}}"
AdvSceneSwitcher.executableTab.help="This tab will allow you to automatically switch scenes if a process is running.\nThis can be useful in situations where the window name could change or is not known.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.executableTab.help="This tab will allow you to automatically switch scenes if a process is running.\nThis can be useful in situations where the window name could change or is not known.\n\nClick on the highlighted plus symbol to continue."
; Screen Region Tab # Screen Region Tab
AdvSceneSwitcher.screenRegionTab.title="Region" AdvSceneSwitcher.screenRegionTab.title="Region"
AdvSceneSwitcher.screenRegionTab.currentPosition="Cursor is currently at:" AdvSceneSwitcher.screenRegionTab.currentPosition="Cursor is currently at:"
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Show guide frames" AdvSceneSwitcher.screenRegionTab.showGuideFrames="Show guide frames"
@@ -1894,7 +2010,7 @@ AdvSceneSwitcher.screenRegionTab.excludeScenes.None="No selection"
AdvSceneSwitcher.screenRegionTab.entry="If cursor is in{{minX}}{{minY}}x{{maxX}}{{maxY}}switch to{{scenes}}using{{transitions}}unless in{{excludeScenes}}" AdvSceneSwitcher.screenRegionTab.entry="If cursor is in{{minX}}{{minY}}x{{maxX}}{{maxY}}switch to{{scenes}}using{{transitions}}unless in{{excludeScenes}}"
AdvSceneSwitcher.screenRegionTab.help="This tab will allow you to automatically switch scenes based on the current position of your mouse cursor.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.screenRegionTab.help="This tab will allow you to automatically switch scenes based on the current position of your mouse cursor.\n\nClick on the highlighted plus symbol to continue."
; Media Tab # Media Tab
AdvSceneSwitcher.mediaTab.title="Media" AdvSceneSwitcher.mediaTab.title="Media"
AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro" AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro"
AdvSceneSwitcher.mediaTab.states.none="None" AdvSceneSwitcher.mediaTab.states.none="None"
@@ -1915,7 +2031,7 @@ AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Time remaining longer"
AdvSceneSwitcher.mediaTab.entry="When{{mediaSources}}state is{{states}}and{{timeRestrictions}}{{time}}switch to{{scenes}}using{{transitions}}" AdvSceneSwitcher.mediaTab.entry="When{{mediaSources}}state is{{states}}and{{timeRestrictions}}{{time}}switch to{{scenes}}using{{transitions}}"
AdvSceneSwitcher.mediaTab.help="This tab will allow you to switch scenes based on the states of media sources.\nFor example, you can automatically switch back to the previous scene once the selected media sourced ended its playback.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.mediaTab.help="This tab will allow you to switch scenes based on the states of media sources.\nFor example, you can automatically switch back to the previous scene once the selected media sourced ended its playback.\n\nClick on the highlighted plus symbol to continue."
; File Tab # File Tab
AdvSceneSwitcher.fileTab.title="File" AdvSceneSwitcher.fileTab.title="File"
AdvSceneSwitcher.fileTab.readWriteSceneFile="Read / write scene from / to file" AdvSceneSwitcher.fileTab.readWriteSceneFile="Read / write scene from / to file"
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Write the name of the current scene to this file:" AdvSceneSwitcher.fileTab.currentSceneOutputFile="Write the name of the current scene to this file:"
@@ -1940,13 +2056,13 @@ AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
AdvSceneSwitcher.fileTab.entry3="{{useRegex}}{{checkModificationDate}}{{checkFileContent}}" AdvSceneSwitcher.fileTab.entry3="{{useRegex}}{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.fileTab.help="This tab will allow you to automatically switch scenes based on the content of remote or local files.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.fileTab.help="This tab will allow you to automatically switch scenes based on the content of remote or local files.\n\nClick on the highlighted plus symbol to continue."
; Random Tab # Random Tab
AdvSceneSwitcher.randomTab.title="Random" AdvSceneSwitcher.randomTab.title="Random"
AdvSceneSwitcher.randomTab.randomDisabledWarning="Functionality disabled - To activate select \"If no switch condition is met switch to any scene in Random tab\" on General tab" AdvSceneSwitcher.randomTab.randomDisabledWarning="Functionality disabled - To activate select \"If no switch condition is met switch to any scene in Random tab\" on General tab"
AdvSceneSwitcher.randomTab.entry="If no switch condition is met switch to{{scenes}}using{{transitions}}for{{delay}}" AdvSceneSwitcher.randomTab.entry="If no switch condition is met switch to{{scenes}}using{{transitions}}for{{delay}}"
AdvSceneSwitcher.randomTab.help="The scene switcher will randomly choose an entry on this tab to switch to for the configured time.\nNote that the same entry will not be chosen twice in a row.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.randomTab.help="The scene switcher will randomly choose an entry on this tab to switch to for the configured time.\nNote that the same entry will not be chosen twice in a row.\n\nClick on the highlighted plus symbol to continue."
; Time Tab # Time Tab
AdvSceneSwitcher.timeTab.title="Time" AdvSceneSwitcher.timeTab.title="Time"
AdvSceneSwitcher.timeTab.anyDay="On any day" AdvSceneSwitcher.timeTab.anyDay="On any day"
AdvSceneSwitcher.timeTab.mondays="Mondays" AdvSceneSwitcher.timeTab.mondays="Mondays"
@@ -1961,14 +2077,14 @@ AdvSceneSwitcher.timeTab.afterstart.tip="The time relative to the start of strea
AdvSceneSwitcher.timeTab.entry="{{triggers}}at{{time}}switch to{{scenes}}using{{transitions}}" AdvSceneSwitcher.timeTab.entry="{{triggers}}at{{time}}switch to{{scenes}}using{{transitions}}"
AdvSceneSwitcher.timeTab.help="This tab will allow you to automatically switch to a different scene based on the current local time.\n\nNote that the scene switcher will only switch scenes at the exact time you specified.\nMake sure you have configured the priority settings on the General tab to your liking so the selected time point will not be missed due to other switching methods having a higher priority.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.timeTab.help="This tab will allow you to automatically switch to a different scene based on the current local time.\n\nNote that the scene switcher will only switch scenes at the exact time you specified.\nMake sure you have configured the priority settings on the General tab to your liking so the selected time point will not be missed due to other switching methods having a higher priority.\n\nClick on the highlighted plus symbol to continue."
; Idle Tab # Idle Tab
AdvSceneSwitcher.idleTab.title="Idle" AdvSceneSwitcher.idleTab.title="Idle"
AdvSceneSwitcher.idleTab.enable="Enable Idle Detection" AdvSceneSwitcher.idleTab.enable="Enable Idle Detection"
AdvSceneSwitcher.idleTab.idleswitch="After{{duration}}of no keyboard or mouse inputs switch to scene{{scenes}}using the{{transitions}}" AdvSceneSwitcher.idleTab.idleswitch="After{{duration}}of no keyboard or mouse inputs switch to scene{{scenes}}using the{{transitions}}"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Do not switch if" AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Do not switch if"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="is in focus" AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="is in focus"
; Scene Sequence Tab # Scene Sequence Tab
AdvSceneSwitcher.sceneSequenceTab.title="Sequence" AdvSceneSwitcher.sceneSequenceTab.title="Sequence"
AdvSceneSwitcher.sceneSequenceTab.description="A sequence of automatic scene switches can be cancelled by either pausing/stopping the scene switcher or manually switching to a different scene" AdvSceneSwitcher.sceneSequenceTab.description="A sequence of automatic scene switches can be cancelled by either pausing/stopping the scene switcher or manually switching to a different scene"
AdvSceneSwitcher.sceneSequenceTab.save="Save scene sequences to file" AdvSceneSwitcher.sceneSequenceTab.save="Save scene sequences to file"
@@ -1985,7 +2101,7 @@ AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After{{delay}}switch to{{scenes}}using{{transitions}}" AdvSceneSwitcher.sceneSequenceTab.extendEntry="After{{delay}}switch to{{scenes}}using{{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
; Audio Tab # Audio Tab
AdvSceneSwitcher.audioTab.title="Audio" AdvSceneSwitcher.audioTab.title="Audio"
AdvSceneSwitcher.audioTab.condition.above="above" AdvSceneSwitcher.audioTab.condition.above="above"
AdvSceneSwitcher.audioTab.condition.below="below" AdvSceneSwitcher.audioTab.condition.below="below"
@@ -1995,7 +2111,7 @@ AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="If multiple entries match
AdvSceneSwitcher.audioTab.multiMatchfallback="... for{{duration}}seconds switch to{{scenes}}using{{transitions}}" AdvSceneSwitcher.audioTab.multiMatchfallback="... for{{duration}}seconds switch to{{scenes}}using{{transitions}}"
AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue."
; Video Tab # Video Tab
AdvSceneSwitcher.videoTab.title="Video" AdvSceneSwitcher.videoTab.title="Video"
AdvSceneSwitcher.videoTab.getScreenshot="Get screenshot for selected entry" AdvSceneSwitcher.videoTab.getScreenshot="Get screenshot for selected entry"
AdvSceneSwitcher.videoTab.getScreenshotHelp="Get Screenshot of the currently selected entry's video source and automatically set it as the target image" AdvSceneSwitcher.videoTab.getScreenshotHelp="Get Screenshot of the currently selected entry's video source and automatically set it as the target image"
@@ -2008,7 +2124,7 @@ AdvSceneSwitcher.videoTab.ignoreInactiveSource="unless source is inactive"
AdvSceneSwitcher.videoTab.entry="When{{videoSources}}{{condition}}{{filePath}}{{browseButton}}for{{duration}}switch to{{scenes}}using{{transitions}}{{ignoreInactiveSource}}" AdvSceneSwitcher.videoTab.entry="When{{videoSources}}{{condition}}{{filePath}}{{browseButton}}for{{duration}}switch to{{scenes}}using{{transitions}}{{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to switch scenes based on the current video output of selected sources.<br/>Make sure to check out <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> for an even better implementation of this functionality.<br/><br/> Click on the highlighted plus symbol to continue.</p></body></html>" AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to switch scenes based on the current video output of selected sources.<br/>Make sure to check out <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> for an even better implementation of this functionality.<br/><br/> Click on the highlighted plus symbol to continue.</p></body></html>"
; Network Tab # Network Tab
AdvSceneSwitcher.networkTab.title="Network" AdvSceneSwitcher.networkTab.title="Network"
AdvSceneSwitcher.networkTab.description="This tab will allow you to remotely control the active scene of another OBS instance.\nPlease note that the scene names have to match exactly on all OBS instances." AdvSceneSwitcher.networkTab.description="This tab will allow you to remotely control the active scene of another OBS instance.\nPlease note that the scene names have to match exactly on all OBS instances."
AdvSceneSwitcher.networkTab.warning="Running the server outside of a local network will allow third parties to read the active scene." AdvSceneSwitcher.networkTab.warning="Running the server outside of a local network will allow third parties to read the active scene."
@@ -2033,7 +2149,7 @@ AdvSceneSwitcher.networkTab.client.status.connecting="Connecting"
AdvSceneSwitcher.networkTab.client.status.connected="Connected" AdvSceneSwitcher.networkTab.client.status.connected="Connected"
AdvSceneSwitcher.networkTab.client.reconnect="Force reconnect" AdvSceneSwitcher.networkTab.client.reconnect="Force reconnect"
; Scene Group Tab # Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Scene Group" AdvSceneSwitcher.sceneGroupTab.title="Scene Group"
AdvSceneSwitcher.sceneGroupTab.list="Scene Groups" AdvSceneSwitcher.sceneGroupTab.list="Scene Groups"
AdvSceneSwitcher.sceneGroupTab.edit="Edit Scene Groups" AdvSceneSwitcher.sceneGroupTab.edit="Edit Scene Groups"
@@ -2053,7 +2169,7 @@ AdvSceneSwitcher.sceneGroupTab.exists="Scene Group or Scene name exists already"
AdvSceneSwitcher.sceneGroupTab.help="Scene Groups can be selected as a target just like a regular scene.\n\nAs the name suggests a scene group is a collection of multiple scenes.\nThe scene group will advance through the list of its assigned scenes depending on the configured settings, which can be found on the right side.\n\nYou can configure the scene group to advance to the next scene in the list:\nAfter a number of times the scene group is selected as a target.\nAfter a certain amount of time has passed.\nOr randomly.\n\nFor example, a scene group containing the scenes ...\nScene 1\nScene 2\nScene 3 \n... will activate \"Scene 1\" the first time it is selected as a target.\nThe second time it will activate \"Scene 2\".\nThe remaining times \"Scene 3\" will be activated.\n\nClick the highlighted plus symbol below to add a new scene group." AdvSceneSwitcher.sceneGroupTab.help="Scene Groups can be selected as a target just like a regular scene.\n\nAs the name suggests a scene group is a collection of multiple scenes.\nThe scene group will advance through the list of its assigned scenes depending on the configured settings, which can be found on the right side.\n\nYou can configure the scene group to advance to the next scene in the list:\nAfter a number of times the scene group is selected as a target.\nAfter a certain amount of time has passed.\nOr randomly.\n\nFor example, a scene group containing the scenes ...\nScene 1\nScene 2\nScene 3 \n... will activate \"Scene 1\" the first time it is selected as a target.\nThe second time it will activate \"Scene 2\".\nThe remaining times \"Scene 3\" will be activated.\n\nClick the highlighted plus symbol below to add a new scene group."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Select the scene group you want to modify on the left.\n\nSelect a scene to add to this scene group by selecting the scene above and clicking the plus symbol below.\n\nA scene can be added multiple times to the same scene group." AdvSceneSwitcher.sceneGroupTab.scenes.help="Select the scene group you want to modify on the left.\n\nSelect a scene to add to this scene group by selecting the scene above and clicking the plus symbol below.\n\nA scene can be added multiple times to the same scene group."
; Scene Trigger Tab # Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Scene Triggers" AdvSceneSwitcher.sceneTriggerTab.title="Scene Triggers"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--select trigger--" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--select trigger--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="is active" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="is active"

View File

@@ -128,7 +128,7 @@ AdvSceneSwitcher.condition.media="Medios"
AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada" AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada"
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas" AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!" AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.layout.legacy="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}es{{states}}y{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="coincide exactamente" AdvSceneSwitcher.condition.video.condition.match="coincide exactamente"
AdvSceneSwitcher.condition.video.condition.differ="no coincide" AdvSceneSwitcher.condition.video.condition.differ="no coincide"
@@ -269,10 +269,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima coincidencia en: %
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena" AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación" AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="En{{scenes}}{{sources}}{{types}}" AdvSceneSwitcher.condition.sceneTransform.condition.match="coincide con la transformación"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="coincide con la transformación"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Transición" AdvSceneSwitcher.condition.transition="Transición"
AdvSceneSwitcher.condition.transition.type.current="El tipo de transición actual es" AdvSceneSwitcher.condition.transition.type.current="El tipo de transición actual es"
AdvSceneSwitcher.condition.transition.type.duration="La duración de la transición actual es" AdvSceneSwitcher.condition.transition.type.duration="La duración de la transición actual es"
@@ -348,7 +345,6 @@ AdvSceneSwitcher.action.replay.saveWarn="Advertencia: ¡Guardar con demasiada fr
AdvSceneSwitcher.action.replay.type.stop="Detener el búfer de reproducción" AdvSceneSwitcher.action.replay.type.stop="Detener el búfer de reproducción"
AdvSceneSwitcher.action.replay.type.start="Iniciar búfer de reproducción" AdvSceneSwitcher.action.replay.type.start="Iniciar búfer de reproducción"
AdvSceneSwitcher.action.replay.type.save="Guardar búfer de reproducción" AdvSceneSwitcher.action.replay.type.save="Guardar búfer de reproducción"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Transmisión" AdvSceneSwitcher.action.streaming="Transmisión"
AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión" AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión"
AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión" AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión"
@@ -416,7 +412,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}" AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena" AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación" AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}" AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
AdvSceneSwitcher.action.file="Archivo" AdvSceneSwitcher.action.file="Archivo"
AdvSceneSwitcher.action.file.type.write="Escribir" AdvSceneSwitcher.action.file.type.write="Escribir"
AdvSceneSwitcher.action.file.type.append="Agregar" AdvSceneSwitcher.action.file.type.append="Agregar"

View File

@@ -196,7 +196,7 @@ AdvSceneSwitcher.condition.media.source="Source"
AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène" AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène"
AdvSceneSwitcher.condition.media.allOnScene="Toutes les sources média sur la scène" AdvSceneSwitcher.condition.media.allOnScene="Toutes les sources média sur la scène"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Malheureusement, toutes les sources média ne se comportent pas de la même manière (par exemple, l'état \"Arrêté\" de la source Media par rapport à la source Vidéo VLC).\nPar conséquent, veuillez expérimenter ce qui fonctionne le mieux pour votre configuration !" AdvSceneSwitcher.condition.media.inconsistencyInfo="Malheureusement, toutes les sources média ne se comportent pas de la même manière (par exemple, l'état \"Arrêté\" de la source Media par rapport à la source Vidéo VLC).\nPar conséquent, veuillez expérimenter ce qui fonctionne le mieux pour votre configuration !"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}l'état est{{states}}et{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}l'état est{{states}}et{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Vidéo" AdvSceneSwitcher.condition.video="Vidéo"
AdvSceneSwitcher.condition.video.condition.match="correspond exactement à" AdvSceneSwitcher.condition.video.condition.match="correspond exactement à"
AdvSceneSwitcher.condition.video.condition.differ="ne correspond pas à" AdvSceneSwitcher.condition.video.condition.differ="ne correspond pas à"
@@ -382,8 +382,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Prochaine correspondance à
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}À la répétition, mettre à jour la date sélectionnée pour la date de répétition" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}À la répétition, mettre à jour la date sélectionnée pour la date de répétition"
AdvSceneSwitcher.condition.sceneTransform="Transformation de l'élément de la scène" AdvSceneSwitcher.condition.sceneTransform="Transformation de l'élément de la scène"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtenir la transformation" AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtenir la transformation"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Sur{{scenes}}{{sources}}{{types}}" AdvSceneSwitcher.condition.sceneTransform.condition.match="correspond à la transformation"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="correspond à la transformation"
AdvSceneSwitcher.condition.transition="Transition" AdvSceneSwitcher.condition.transition="Transition"
AdvSceneSwitcher.condition.transition.type.current="Le type de transition actuel est" AdvSceneSwitcher.condition.transition.type.current="Le type de transition actuel est"
AdvSceneSwitcher.condition.transition.type.duration="La durée de la transition actuelle est" AdvSceneSwitcher.condition.transition.type.duration="La durée de la transition actuelle est"
@@ -611,7 +610,7 @@ AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="Centrer à l'écran"
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Centrer verticalement" AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Centrer verticalement"
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Centrer horizontalement" AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Centrer horizontalement"
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtenir la transformation" AdvSceneSwitcher.action.sceneTransform.getTransform="Obtenir la transformation"
AdvSceneSwitcher.action.sceneTransform.entry="Sur{{scenes}}{{action}}{{rotation}}{{sources}}" AdvSceneSwitcher.action.sceneTransform.entry="Sur{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
AdvSceneSwitcher.action.file="Fichier" AdvSceneSwitcher.action.file="Fichier"
AdvSceneSwitcher.action.file.type.write="Écrire" AdvSceneSwitcher.action.file.type.write="Écrire"
AdvSceneSwitcher.action.file.type.append="Ajouter" AdvSceneSwitcher.action.file.type.append="Ajouter"

2086
data/locale/ja-JP.ini Normal file

File diff suppressed because it is too large Load Diff

2139
data/locale/pt-BR.ini Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -137,7 +137,6 @@ AdvSceneSwitcher.action.replay="Буфер воспроизведения"
AdvSceneSwitcher.action.replay.type.stop="Остановить буфер воспроизведения" AdvSceneSwitcher.action.replay.type.stop="Остановить буфер воспроизведения"
AdvSceneSwitcher.action.replay.type.start="Начать воспроизведение буфера" AdvSceneSwitcher.action.replay.type.start="Начать воспроизведение буфера"
AdvSceneSwitcher.action.replay.type.save="Сохранить буфер воспроизведения" AdvSceneSwitcher.action.replay.type.save="Сохранить буфер воспроизведения"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Потоковое вещание" AdvSceneSwitcher.action.streaming="Потоковое вещание"
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание" AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание" AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"

View File

@@ -117,7 +117,7 @@ AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFil
AdvSceneSwitcher.condition.media="Medya" AdvSceneSwitcher.condition.media="Medya"
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı" AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları " AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir" AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme" AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme"
@@ -226,10 +226,7 @@ AdvSceneSwitcher.condition.date.state.before="Önce"
AdvSceneSwitcher.condition.date.state.between="Arasında" AdvSceneSwitcher.condition.date.state.between="Arasında"
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü" AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al" AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}}{{types}}" AdvSceneSwitcher.condition.sceneTransform.condition.match="dönüşümle eşleşir"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="dönüşümle eşleşir"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Geçiş" AdvSceneSwitcher.condition.transition="Geçiş"
AdvSceneSwitcher.condition.transition.type.current="Geçerli geçiş türü" AdvSceneSwitcher.condition.transition.type.current="Geçerli geçiş türü"
AdvSceneSwitcher.condition.transition.type.duration="Mevcut geçiş süresi" AdvSceneSwitcher.condition.transition.type.duration="Mevcut geçiş süresi"
@@ -279,7 +276,6 @@ AdvSceneSwitcher.action.replay="Tekrar arabelleği"
AdvSceneSwitcher.action.replay.type.stop="Tekrar arabelleğini durdur" AdvSceneSwitcher.action.replay.type.stop="Tekrar arabelleğini durdur"
AdvSceneSwitcher.action.replay.type.start="Tekrar arabelleğini başlat" AdvSceneSwitcher.action.replay.type.start="Tekrar arabelleğini başlat"
AdvSceneSwitcher.action.replay.type.save="Tekrar arabelleğini kaydet" AdvSceneSwitcher.action.replay.type.save="Tekrar arabelleğini kaydet"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Yayın" AdvSceneSwitcher.action.streaming="Yayın"
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur" AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat" AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
@@ -345,7 +341,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}" AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü" AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al" AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}" AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
AdvSceneSwitcher.action.file="Dosya" AdvSceneSwitcher.action.file="Dosya"
AdvSceneSwitcher.action.file.type.write="Yaz" AdvSceneSwitcher.action.file.type.write="Yaz"
AdvSceneSwitcher.action.file.type.append="Ekle" AdvSceneSwitcher.action.file.type.append="Ekle"

View File

@@ -184,7 +184,7 @@ AdvSceneSwitcher.condition.media.source="源"
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源" AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源"
AdvSceneSwitcher.condition.media.allOnScene="所有媒体源" AdvSceneSwitcher.condition.media.allOnScene="所有媒体源"
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是并非所有媒体源类型的行为都相同例如媒体源与VLC视频源“停止”状态。\n所以请尝试适合您的设置" AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是并非所有媒体源类型的行为都相同例如媒体源与VLC视频源“停止”状态。\n所以请尝试适合您的设置"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}} 的状态是 {{states}}{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}的状态是{{states}}{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="视频" AdvSceneSwitcher.condition.video="视频"
AdvSceneSwitcher.condition.video.condition.match="完全匹配" AdvSceneSwitcher.condition.video.condition.match="完全匹配"
AdvSceneSwitcher.condition.video.condition.differ="不匹配" AdvSceneSwitcher.condition.video.condition.differ="不匹配"
@@ -365,10 +365,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="下一次匹配 在: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 重复时,将选定的日期更新为重复日期" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 重复时,将选定的日期更新为重复日期"
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变" AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了" AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="在 {{scenes}} {{sources}} {{types}}" AdvSceneSwitcher.condition.sceneTransform.condition.match="匹配变换"
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="匹配变换"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="转场" AdvSceneSwitcher.condition.transition="转场"
AdvSceneSwitcher.condition.transition.type.current="当前转场特效类型为" AdvSceneSwitcher.condition.transition.type.current="当前转场特效类型为"
AdvSceneSwitcher.condition.transition.type.duration="当前转场特效持续时间为" AdvSceneSwitcher.condition.transition.type.duration="当前转场特效持续时间为"
@@ -481,7 +478,6 @@ AdvSceneSwitcher.action.replay.saveWarn="警告:保存过于频繁可能会导
AdvSceneSwitcher.action.replay.type.stop="停止回放缓存" AdvSceneSwitcher.action.replay.type.stop="停止回放缓存"
AdvSceneSwitcher.action.replay.type.start="启动回放缓存" AdvSceneSwitcher.action.replay.type.start="启动回放缓存"
AdvSceneSwitcher.action.replay.type.save="保存回放缓存" AdvSceneSwitcher.action.replay.type.save="保存回放缓存"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="推流" AdvSceneSwitcher.action.streaming="推流"
AdvSceneSwitcher.action.streaming.type.stop="停止推流" AdvSceneSwitcher.action.streaming.type.stop="停止推流"
AdvSceneSwitcher.action.streaming.type.start="开始推流" AdvSceneSwitcher.action.streaming.type.start="开始推流"
@@ -556,7 +552,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置"
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}" AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
AdvSceneSwitcher.action.sceneTransform="场景项目转换" AdvSceneSwitcher.action.sceneTransform="场景项目转换"
AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换" AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换"
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}}{{action}}{{rotation}}{{sources}}" AdvSceneSwitcher.action.sceneTransform.entry="在{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
AdvSceneSwitcher.action.file="文件" AdvSceneSwitcher.action.file="文件"
AdvSceneSwitcher.action.file.type.write="写入" AdvSceneSwitcher.action.file.type.write="写入"
AdvSceneSwitcher.action.file.type.append="追加写入" AdvSceneSwitcher.action.file.type.append="追加写入"

1
deps/libusb vendored Submodule

Submodule deps/libusb added at d52e355daa

2
deps/opencv vendored

View File

@@ -68,7 +68,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>962</width> <width>962</width>
<height>1033</height> <height>1040</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_19"> <layout class="QVBoxLayout" name="verticalLayout_19">
@@ -390,7 +390,7 @@
<item> <item>
<layout class="QHBoxLayout" name="cooldownLayout"> <layout class="QHBoxLayout" name="cooldownLayout">
<item> <item>
<widget class="QLabel" name="label"> <widget class="QCheckBox" name="enableCooldown">
<property name="text"> <property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.cooldown</string> <string>AdvSceneSwitcher.generalTab.generalBehavior.cooldown</string>
</property> </property>
@@ -572,40 +572,22 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_32"> <layout class="QHBoxLayout" name="horizontalLayout_32">
<item> <item>
<widget class="QPushButton" name="priorityUp"> <widget class="QToolButton" name="priorityUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>../../../forms/images/up.png</normaloff>../../../forms/images/up.png</iconset> <normaloff>../../../forms/images/up.png</normaloff>../../../forms/images/up.png</iconset>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string> <string notr="true">upArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="priorityDown"> <widget class="QToolButton" name="priorityDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>../../../forms/images/down.png</normaloff>../../../forms/images/down.png</iconset> <normaloff>../../../forms/images/down.png</normaloff>../../../forms/images/down.png</iconset>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string> <string notr="true">downArrowIconSmall</string>
</property> </property>
@@ -739,135 +721,47 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_61"> <layout class="QHBoxLayout" name="macroControlLayout">
<item> <item>
<widget class="QPushButton" name="macroAdd"> <widget class="QToolButton" name="macroAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.macroAddButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.macroAddButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">addIconSmall</string> <string notr="true">addIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="macroRemove"> <widget class="QToolButton" name="macroRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.macroRemoveButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.macroRemoveButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string> <string notr="true">removeIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_130"> <widget class="QToolButton" name="macroUp">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_46">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_131">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="macroUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.macroUpButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.macroUpButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string> <string notr="true">upArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="macroDown"> <widget class="QToolButton" name="macroDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.macroDownButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.macroDownButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string> <string notr="true">downArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_65">
<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> </layout>
</item> </item>
</layout> </layout>
@@ -960,22 +854,13 @@
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QPushButton" name="macroProperties"> <widget class="QToolButton" name="macroSettings">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.openMacroPropertiesButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.openMacroSettingsButton</string>
</property> </property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">propertiesIconSmall</string> <string notr="true">propertiesIconSmall</string>
</property> </property>
@@ -1013,173 +898,67 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_12"> <layout class="QHBoxLayout" name="conditionControlsLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>9</number> <number>9</number>
</property> </property>
<item> <item>
<widget class="QPushButton" name="conditionAdd"> <widget class="QToolButton" name="conditionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionAddButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.conditionAddButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">addIconSmall</string> <string notr="true">addIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="conditionRemove"> <widget class="QToolButton" name="conditionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionRemoveButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.conditionRemoveButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string> <string notr="true">removeIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_135"> <widget class="QToolButton" name="conditionTop">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_48">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_134">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="conditionTop">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionTopButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.conditionTopButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="conditionUp"> <widget class="QToolButton" name="conditionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionUpButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.conditionUpButton</string>
</property> </property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string> <string notr="true">upArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="conditionDown"> <widget class="QToolButton" name="conditionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionDownButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.conditionDownButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string> <string notr="true">downArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="conditionBottom"> <widget class="QToolButton" name="conditionBottom">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionBottomButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.conditionBottomButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_15">
<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> </layout>
</item> </item>
</layout> </layout>
@@ -1213,155 +992,62 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_21"> <layout class="QHBoxLayout" name="actionControlsLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>9</number> <number>9</number>
</property> </property>
<item> <item>
<widget class="QPushButton" name="actionAdd"> <widget class="QToolButton" name="actionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionAddButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.actionAddButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">addIconSmall</string> <string notr="true">addIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="actionRemove"> <widget class="QToolButton" name="actionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionRemoveButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.actionRemoveButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string> <string notr="true">removeIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_133"> <widget class="QToolButton" name="actionTop">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_47">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_132">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="actionTop">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionTopButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.actionTopButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="actionUp"> <widget class="QToolButton" name="actionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionUpButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.actionUpButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string> <string notr="true">upArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="actionDown"> <widget class="QToolButton" name="actionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionDownButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.actionDownButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string> <string notr="true">downArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="actionBottom"> <widget class="QToolButton" name="actionBottom">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionBottomButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.actionBottomButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@@ -1378,22 +1064,13 @@
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QPushButton" name="toggleElseActions"> <widget class="QToolButton" name="toggleElseActions">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.toggleShowElseSection</string> <string>AdvSceneSwitcher.macroTab.tooltip.toggleShowElseSection</string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@@ -1425,170 +1102,64 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_33"> <layout class="QHBoxLayout" name="elseActionControlsLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>9</number> <number>9</number>
</property> </property>
<item> <item>
<widget class="QPushButton" name="elseActionAdd"> <widget class="QToolButton" name="elseActionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionAddButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.elseActionAddButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">addIconSmall</string> <string notr="true">addIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="elseActionRemove"> <widget class="QToolButton" name="elseActionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionRemoveButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.elseActionRemoveButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string> <string notr="true">removeIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_138"> <widget class="QToolButton" name="elseActionTop">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_50">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_139">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="elseActionTop">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionTopButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.elseActionTopButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="elseActionUp"> <widget class="QToolButton" name="elseActionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionUpButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.elseActionUpButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string> <string notr="true">upArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="elseActionDown"> <widget class="QToolButton" name="elseActionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionDownButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.elseActionDownButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0"> <property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string> <string notr="true">downArrowIconSmall</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="elseActionBottom"> <widget class="QToolButton" name="elseActionBottom">
<property name="maximumSize">
<size>
<width>22</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionBottomButton</string> <string>AdvSceneSwitcher.macroTab.tooltip.elseActionBottomButton</string>
</property> </property>
<property name="flat">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_22">
<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> </layout>
</item> </item>
</layout> </layout>
@@ -5033,7 +4604,7 @@
<tabstop>runMacro</tabstop> <tabstop>runMacro</tabstop>
<tabstop>runMacroInParallel</tabstop> <tabstop>runMacroInParallel</tabstop>
<tabstop>runMacroOnChange</tabstop> <tabstop>runMacroOnChange</tabstop>
<tabstop>macroProperties</tabstop> <tabstop>macroSettings</tabstop>
<tabstop>conditionsList</tabstop> <tabstop>conditionsList</tabstop>
<tabstop>conditionAdd</tabstop> <tabstop>conditionAdd</tabstop>
<tabstop>conditionRemove</tabstop> <tabstop>conditionRemove</tabstop>

View File

@@ -70,6 +70,9 @@ static void DisplayMissingDependencyWarning()
QString warning(obs_module_text( QString warning(obs_module_text(
"AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage")); "AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage"));
DisplayMessage(warning.arg(failedLibsString)); DisplayMessage(warning.arg(failedLibsString));
// Only display the warning once per plugin load
switcher->loadFailureLibs.clear();
} }
static void DisplayMissingDataDirWarning() static void DisplayMissingDataDirWarning()
@@ -90,10 +93,24 @@ static void DisplayMissingDataDirWarning()
DisplayMessage(msg); DisplayMessage(msg);
} }
bool CanCreateDefaultAction();
bool CanCreateDefaultCondition();
static void DisplayCorruptedInstallWarning()
{
if (CanCreateDefaultAction() && CanCreateDefaultCondition()) {
return;
}
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage"));
}
void AdvSceneSwitcher::LoadUI() void AdvSceneSwitcher::LoadUI()
{ {
DisplayMissingDataDirWarning(); DisplayMissingDataDirWarning();
DisplayMissingDependencyWarning(); DisplayMissingDependencyWarning();
DisplayCorruptedInstallWarning();
SetupGeneralTab(); SetupGeneralTab();
SetupTitleTab(); SetupTitleTab();
@@ -238,8 +255,8 @@ void SwitcherData::Thread()
duration = std::chrono::milliseconds(interval) + duration = std::chrono::milliseconds(interval) +
std::chrono::milliseconds(linger) - runTime; std::chrono::milliseconds(linger) - runTime;
if (duration.count() < 1) { if (duration.count() < 1) {
blog(LOG_INFO, vblog(LOG_INFO,
"detected busy loop - refusing to sleep less than 1ms"); "detected busy loop - refusing to sleep less than 1ms");
duration = std::chrono::milliseconds(10); duration = std::chrono::milliseconds(10);
} }
} }
@@ -705,7 +722,7 @@ static void LoadPlugins()
{ {
QFileInfo libPath( QFileInfo libPath(
QString(obs_get_module_binary_path(obs_current_module()))); QString(obs_get_module_binary_path(obs_current_module())));
QString pluginDir(libPath.absolutePath() + "/adv-ss-plugins"); QString pluginDir(libPath.absolutePath() + "/" ADVSS_PLUGIN_FOLDER);
#ifdef _WIN32 #ifdef _WIN32
QString libPattern = "*.dll"; QString libPattern = "*.dll";
SetDllDirectory(pluginDir.toStdWString().c_str()); SetDllDirectory(pluginDir.toStdWString().c_str());
@@ -750,6 +767,36 @@ QWidget *GetSettingsWindow()
return SettingsWindowIsOpened() ? AdvSceneSwitcher::window : nullptr; return SettingsWindowIsOpened() ? AdvSceneSwitcher::window : nullptr;
} }
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
{
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(); void SetupActionQueues();
extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module, extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "macro-segment-list.hpp" #include "macro-segment-list.hpp"
#include "condition-logic.hpp"
#include "log-helper.hpp" #include "log-helper.hpp"
#include <ui_advanced-scene-switcher.h> #include <ui_advanced-scene-switcher.h>
@@ -12,7 +13,6 @@ class MacroActionEdit;
class MacroConditionEdit; class MacroConditionEdit;
class Duration; class Duration;
class SequenceWidget; class SequenceWidget;
enum class LogicType;
struct SceneGroup; struct SceneGroup;
/******************************************************************************* /*******************************************************************************
@@ -53,6 +53,7 @@ public slots:
void on_noMatchRandomSwitch_clicked(); void on_noMatchRandomSwitch_clicked();
void NoMatchDelayDurationChanged(const Duration &); void NoMatchDelayDurationChanged(const Duration &);
void CooldownDurationChanged(const Duration &); void CooldownDurationChanged(const Duration &);
void on_enableCooldown_stateChanged(int state);
void on_startupBehavior_currentIndexChanged(int index); void on_startupBehavior_currentIndexChanged(int index);
void on_logLevel_currentIndexChanged(int index); void on_logLevel_currentIndexChanged(int index);
void on_autoStartEvent_currentIndexChanged(int index); void on_autoStartEvent_currentIndexChanged(int index);
@@ -96,6 +97,7 @@ public:
void SetConditionData(Macro &m); void SetConditionData(Macro &m);
void SwapActions(Macro *m, int pos1, int pos2); void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2); void SwapConditions(Macro *m, int pos1, int pos2);
void HighligthMacroSettingsButton(bool enable = true);
public slots: public slots:
void on_macroAdd_clicked(); void on_macroAdd_clicked();
@@ -172,13 +174,13 @@ public slots:
void MacroConditionReorder(int to, int target); void MacroConditionReorder(int to, int target);
void AddMacroCondition(int idx); void AddMacroCondition(int idx);
void AddMacroCondition(Macro *macro, int idx, const std::string &id, void AddMacroCondition(Macro *macro, int idx, const std::string &id,
obs_data_t *data, LogicType logic); obs_data_t *data, Logic::Type logic);
void RemoveMacroCondition(int idx); void RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx); void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx); void MoveMacroConditionDown(int idx);
void HighlightControls(); void HighlightControls();
void HighlightOnChange(); void HighlightOnChange();
void on_macroProperties_clicked(); void on_macroSettings_clicked();
void CopyMacroSegment(); void CopyMacroSegment();
void PasteMacroSegment(); void PasteMacroSegment();
@@ -410,5 +412,6 @@ public slots:
void OpenSettingsWindow(); void OpenSettingsWindow();
QWidget *GetSettingsWindow(); QWidget *GetSettingsWindow();
void HighligthMacroSettingsButton(bool enable);
} // namespace advss } // namespace advss

View File

@@ -91,6 +91,17 @@ void AdvSceneSwitcher::CooldownDurationChanged(const Duration &dur)
switcher->cooldown = dur; switcher->cooldown = dur;
} }
void AdvSceneSwitcher::on_enableCooldown_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->enableCooldown = state;
ui->cooldownTime->setEnabled(state);
}
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index) void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
{ {
if (loading) { if (loading) {
@@ -431,7 +442,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
} }
LoadMacros(obj); LoadMacros(obj);
LoadGlobalMacroProperties(obj); LoadGlobalMacroSettings(obj);
loadWindowTitleSwitches(obj); loadWindowTitleSwitches(obj);
loadScreenRegionSwitches(obj); loadScreenRegionSwitches(obj);
loadPauseSwitches(obj); loadPauseSwitches(obj);
@@ -466,7 +477,7 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
saveSceneGroups(obj); saveSceneGroups(obj);
SaveMacros(obj); SaveMacros(obj);
SaveGlobalMacroProperties(obj); SaveGlobalMacroSettings(obj);
SaveVariables(obj); SaveVariables(obj);
saveWindowTitleSwitches(obj); saveWindowTitleSwitches(obj);
saveScreenRegionSwitches(obj); saveScreenRegionSwitches(obj);
@@ -505,6 +516,7 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
noMatchDelay.Save(obj, "noMatchDelay"); noMatchDelay.Save(obj, "noMatchDelay");
cooldown.Save(obj, "cooldown"); cooldown.Save(obj, "cooldown");
obs_data_set_bool(obj, "enableCooldown", enableCooldown);
obs_data_set_bool(obj, "active", sceneColletionStop ? true : !stop); obs_data_set_bool(obj, "active", sceneColletionStop ? true : !stop);
sceneColletionStop = false; sceneColletionStop = false;
@@ -550,6 +562,11 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
noMatchDelay.Load(obj, "noMatchDelay"); noMatchDelay.Load(obj, "noMatchDelay");
cooldown.Load(obj, "cooldown"); cooldown.Load(obj, "cooldown");
if (!obs_data_has_user_value(obj, "enableCooldown")) {
enableCooldown = cooldown.Seconds() != 0;
} else {
enableCooldown = obs_data_get_bool(obj, "enableCooldown");
}
obs_data_set_default_bool(obj, "active", true); obs_data_set_default_bool(obj, "active", true);
stop = !obs_data_get_bool(obj, "active"); stop = !obs_data_get_bool(obj, "active");
@@ -653,7 +670,7 @@ void SwitcherData::CheckNoMatchSwitch(bool &match, OBSWeakSource &scene,
void SwitcherData::checkSwitchCooldown(bool &match) void SwitcherData::checkSwitchCooldown(bool &match)
{ {
if (!match) { if (!match || !enableCooldown) {
return; return;
} }
@@ -822,6 +839,8 @@ void AdvSceneSwitcher::SetupGeneralTab()
ui->checkInterval->setValue(switcher->interval); ui->checkInterval->setValue(switcher->interval);
ui->enableCooldown->setChecked(switcher->enableCooldown);
ui->cooldownTime->setEnabled(switcher->enableCooldown);
ui->cooldownTime->SetDuration(switcher->cooldown); ui->cooldownTime->SetDuration(switcher->cooldown);
ui->cooldownTime->setToolTip(obs_module_text( ui->cooldownTime->setToolTip(obs_module_text(
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint")); "AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));

View File

@@ -12,7 +12,7 @@
namespace advss { namespace advss {
static QMetaObject::Connection addPulse; static QObject *addPulse = nullptr;
SceneGroupEditWidget *typeEdit = nullptr; SceneGroupEditWidget *typeEdit = nullptr;
std::deque<SceneGroup> &GetSceneGroups() std::deque<SceneGroup> &GetSceneGroups()
@@ -170,7 +170,10 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
item->setData(Qt::UserRole, text); item->setData(Qt::UserRole, text);
ui->sceneGroups->setCurrentItem(item); ui->sceneGroups->setCurrentItem(item);
ui->sceneGroupAdd->disconnect(addPulse); if (addPulse) {
addPulse->deleteLater();
addPulse = nullptr;
}
ui->sceneGroupHelp->setVisible(false); ui->sceneGroupHelp->setVisible(false);
emit SceneGroupAdded(QString::fromStdString(name)); emit SceneGroupAdded(QString::fromStdString(name));
@@ -512,8 +515,8 @@ void AdvSceneSwitcher::SetupSceneGroupTab()
if (switcher->sceneGroups.size() == 0) { if (switcher->sceneGroups.size() == 0) {
if (!switcher->disableHints) { if (!switcher->disableHints) {
addPulse = PulseWidget(ui->sceneGroupAdd, addPulse = HighlightWidget(ui->sceneGroupAdd,
QColor(Qt::green)); QColor(Qt::green));
} }
ui->sceneGroupHelp->setVisible(true); ui->sceneGroupHelp->setVisible(true);
} else { } else {

View File

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

View File

@@ -12,7 +12,7 @@
namespace advss { namespace advss {
bool AudioSwitch::pause = false; bool AudioSwitch::pause = false;
static QMetaObject::Connection addPulse; static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_audioAdd_clicked() void AdvSceneSwitcher::on_audioAdd_clicked()
{ {
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_audioAdd_clicked()
AudioSwitchWidget *sw = AudioSwitchWidget *sw =
new AudioSwitchWidget(this, &switcher->audioSwitches.back()); new AudioSwitchWidget(this, &switcher->audioSwitches.back());
listAddClicked(ui->audioSwitches, sw, ui->audioAdd, &addPulse); listAddClicked(ui->audioSwitches, sw, addPulse);
ui->audioHelp->setVisible(false); ui->audioHelp->setVisible(false);
} }
@@ -232,7 +232,8 @@ void AdvSceneSwitcher::SetupAudioTab()
if (switcher->audioSwitches.size() == 0) { if (switcher->audioSwitches.size() == 0) {
if (!switcher->disableHints) { if (!switcher->disableHints) {
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green)); addPulse = HighlightWidget(ui->audioAdd,
QColor(Qt::green));
} }
ui->audioHelp->setVisible(true); ui->audioHelp->setVisible(true);
} else { } else {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ namespace advss {
constexpr auto max_extend_text_size = 150; constexpr auto max_extend_text_size = 150;
bool SceneSequenceSwitch::pause = false; bool SceneSequenceSwitch::pause = false;
static QMetaObject::Connection addPulse; static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_sceneSequenceAdd_clicked() void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
{ {
@@ -26,7 +26,7 @@ void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
listAddClicked(ui->sceneSequenceSwitches, listAddClicked(ui->sceneSequenceSwitches,
new SequenceWidget( new SequenceWidget(
this, &switcher->sceneSequenceSwitches.back()), this, &switcher->sceneSequenceSwitches.back()),
ui->sceneSequenceAdd, &addPulse); addPulse);
ui->sequenceHelp->setVisible(false); ui->sequenceHelp->setVisible(false);
} }
@@ -299,8 +299,8 @@ void AdvSceneSwitcher::SetupSequenceTab()
if (switcher->sceneSequenceSwitches.size() == 0) { if (switcher->sceneSequenceSwitches.size() == 0) {
if (!switcher->disableHints) { if (!switcher->disableHints) {
addPulse = PulseWidget(ui->sceneSequenceAdd, addPulse = HighlightWidget(ui->sceneSequenceAdd,
QColor(Qt::green)); QColor(Qt::green));
} }
ui->sequenceHelp->setVisible(true); ui->sequenceHelp->setVisible(true);
} else { } else {

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "macro-helpers.hpp" #include "macro-helpers.hpp"
#include "macro-properties.hpp" #include "macro-settings.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "plugin-state-helpers.hpp" #include "plugin-state-helpers.hpp"
#include "section.hpp" #include "section.hpp"

View File

@@ -1,7 +1,28 @@
#include "macro-action-factory.hpp" #include "macro-action-factory.hpp"
#include <mutex>
namespace advss { namespace advss {
namespace {
class MacroActionUnknown : public MacroAction {
public:
MacroActionUnknown(Macro *m) : MacroAction(m) {}
std::shared_ptr<MacroAction> Copy() const
{
return std::make_shared<MacroActionUnknown>(GetMacro());
}
bool PerformAction() { return true; };
bool Save(obs_data_t *obj) const { return MacroAction::Save(obj); };
bool Load(obs_data_t *obj) { return MacroAction::Load(obj); };
std::string GetId() const { return "unknown"; }
};
} // namespace
static std::recursive_mutex mutex;
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap() std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
{ {
static std::map<std::string, MacroActionInfo> _methods; static std::map<std::string, MacroActionInfo> _methods;
@@ -10,6 +31,7 @@ std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info) bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it == GetMap().end()) { if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info; GetMap()[id] = info;
return true; return true;
@@ -17,35 +39,61 @@ bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
return false; return false;
} }
bool MacroActionFactory::Deregister(const std::string &id)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
if (GetMap().count(id) == 0) {
return false;
}
GetMap().erase(id);
return true;
}
static std::shared_ptr<MacroAction> createUnknownAction(Macro *m)
{
return std::make_shared<MacroActionUnknown>(m);
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id, std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
Macro *m) Macro *m)
{ {
if (auto it = GetMap().find(id); it != GetMap().end()) std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._create(m); return it->second._create(m);
}
return nullptr; return createUnknownAction(m);
}
static QWidget *createUnknownActionWidget()
{
return new QLabel(obs_module_text("AdvSceneSwitcher.action.unknown"));
} }
QWidget *MacroActionFactory::CreateWidget(const std::string &id, QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent, QWidget *parent,
std::shared_ptr<MacroAction> action) std::shared_ptr<MacroAction> action)
{ {
if (auto it = GetMap().find(id); it != GetMap().end()) std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createWidget(parent, action); return it->second._createWidget(parent, action);
}
return nullptr; return createUnknownActionWidget();
} }
std::string MacroActionFactory::GetActionName(const std::string &id) std::string MacroActionFactory::GetActionName(const std::string &id)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) { if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name; return it->second._name;
} }
return "unknown action"; return obs_module_text("AdvSceneSwitcher.action.unknown");
} }
std::string MacroActionFactory::GetIdByName(const QString &name) std::string MacroActionFactory::GetIdByName(const QString &name)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
for (auto it : GetMap()) { for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) { if (name == obs_module_text(it.second._name.c_str())) {
return it.first; return it.first;
@@ -54,4 +102,14 @@ std::string MacroActionFactory::GetIdByName(const QString &name)
return ""; return "";
} }
bool CanCreateDefaultAction()
{
const auto action = MacroActionFactory::Create(
MacroAction::GetDefaultID().data(), nullptr);
if (!action) {
return false;
}
return action->GetId() == MacroAction::GetDefaultID().data();
}
} // namespace advss } // namespace advss

View File

@@ -6,10 +6,10 @@
namespace advss { namespace advss {
struct MacroActionInfo { struct MacroActionInfo {
using CreateAction = std::shared_ptr<MacroAction> (*)(Macro *m);
using CreateActionWidget = QWidget *(*)(QWidget *parent, using CreateActionWidget = QWidget *(*)(QWidget *parent,
std::shared_ptr<MacroAction>); std::shared_ptr<MacroAction>);
CreateAction _create = nullptr;
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
CreateActionWidget _createWidget = nullptr; CreateActionWidget _createWidget = nullptr;
std::string _name; std::string _name;
}; };
@@ -19,6 +19,7 @@ public:
MacroActionFactory() = delete; MacroActionFactory() = delete;
EXPORT static bool Register(const std::string &id, MacroActionInfo); EXPORT static bool Register(const std::string &id, MacroActionInfo);
static bool Deregister(const std::string &id);
static std::shared_ptr<MacroAction> Create(const std::string &id, static std::shared_ptr<MacroAction> Create(const std::string &id,
Macro *m); Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent, static QWidget *CreateWidget(const std::string &id, QWidget *parent,
@@ -31,4 +32,6 @@ private:
static std::map<std::string, MacroActionInfo> &GetMap(); static std::map<std::string, MacroActionInfo> &GetMap();
}; };
bool CanCreateDefaultAction();
} // namespace advss } // namespace advss

View File

@@ -1,4 +1,5 @@
#include "macro-action-macro.hpp" #include "macro-action-macro.hpp"
#include "help-icon.hpp"
#include "layout-helpers.hpp" #include "layout-helpers.hpp"
#include "macro.hpp" #include "macro.hpp"
@@ -168,6 +169,15 @@ void MacroActionMacro::ResolveVariablesToFixedValues()
_actionIndex.ResolveVariables(); _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 void MacroActionMacro::RunActions(Macro *actionMacro) const
{ {
if (_runOptions.skipWhenPaused && actionMacro->Paused()) { if (_runOptions.skipWhenPaused && actionMacro->Paused()) {
@@ -175,8 +185,8 @@ void MacroActionMacro::RunActions(Macro *actionMacro) const
} }
if (_runOptions.logic == RunOptions::Logic::IGNORE_CONDITIONS) { if (_runOptions.logic == RunOptions::Logic::IGNORE_CONDITIONS) {
actionMacro->PerformActions(!_runOptions.runElseActions, false, runActionsHelper(actionMacro, _runOptions.runElseActions,
true); _runOptions.setInputs, _runOptions.inputs);
return; return;
} }
@@ -185,12 +195,16 @@ void MacroActionMacro::RunActions(Macro *actionMacro) const
return; return;
} }
if (_runOptions.reevaluateConditionState) {
conditionMacro->CeckMatch(true);
}
if ((_runOptions.logic == RunOptions::Logic::CONDITIONS && if ((_runOptions.logic == RunOptions::Logic::CONDITIONS &&
conditionMacro->Matched()) || conditionMacro->Matched()) ||
(_runOptions.logic == RunOptions::Logic::INVERT_CONDITIONS && (_runOptions.logic == RunOptions::Logic::INVERT_CONDITIONS &&
!conditionMacro->Matched())) { !conditionMacro->Matched())) {
actionMacro->PerformActions(!_runOptions.runElseActions, false, runActionsHelper(actionMacro, _runOptions.runElseActions,
true); _runOptions.setInputs, _runOptions.inputs);
} }
} }
@@ -228,11 +242,19 @@ MacroActionMacroEdit::MacroActionMacroEdit(
_actions(new QComboBox()), _actions(new QComboBox()),
_conditionMacros(new MacroSelection(parent)), _conditionMacros(new MacroSelection(parent)),
_conditionBehaviors(new QComboBox()), _conditionBehaviors(new QComboBox()),
_reevaluateConditionState(new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.macro.type.run."
"updateConditionMatchState"))),
_actionTypes(new QComboBox()), _actionTypes(new QComboBox()),
_skipWhenPaused(new QCheckBox(obs_module_text( _skipWhenPaused(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.skipWhenPaused"))), "AdvSceneSwitcher.action.macro.type.run.skipWhenPaused"))),
_setInputs(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.setInputs"))),
_inputs(new MacroInputEdit()),
_entryLayout(new QHBoxLayout()), _entryLayout(new QHBoxLayout()),
_conditionLayout(new QHBoxLayout()) _conditionLayout(new QHBoxLayout()),
_reevaluateConditionStateLayout(new QHBoxLayout()),
_setInputsLayout(new QHBoxLayout())
{ {
populateActionSelection(_actions); populateActionSelection(_actions);
populateConditionBehaviorSelection(_conditionBehaviors); populateConditionBehaviorSelection(_conditionBehaviors);
@@ -256,10 +278,30 @@ MacroActionMacroEdit::MacroActionMacroEdit(
SLOT(ActionTypeChanged(int))); SLOT(ActionTypeChanged(int)));
QWidget::connect(_skipWhenPaused, SIGNAL(stateChanged(int)), this, QWidget::connect(_skipWhenPaused, SIGNAL(stateChanged(int)), this,
SLOT(SkipWhenPausedChanged(int))); 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)));
_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(); auto layout = new QVBoxLayout();
layout->addLayout(_entryLayout); layout->addLayout(_entryLayout);
layout->addLayout(_conditionLayout); layout->addLayout(_conditionLayout);
layout->addLayout(_reevaluateConditionStateLayout);
layout->addLayout(_setInputsLayout);
layout->addWidget(_inputs);
layout->addWidget(_skipWhenPaused); layout->addWidget(_skipWhenPaused);
setLayout(layout); setLayout(layout);
_entryData = entryData; _entryData = entryData;
@@ -267,6 +309,13 @@ MacroActionMacroEdit::MacroActionMacroEdit(
_loading = false; _loading = false;
} }
void HighligthMacroSettingsButton(bool enable);
MacroActionMacroEdit::~MacroActionMacroEdit()
{
HighligthMacroSettingsButton(false);
}
void MacroActionMacroEdit::UpdateEntryData() void MacroActionMacroEdit::UpdateEntryData()
{ {
if (!_entryData) { if (!_entryData) {
@@ -279,88 +328,89 @@ void MacroActionMacroEdit::UpdateEntryData()
_conditionMacros->SetCurrentMacro(_entryData->_runOptions.macro); _conditionMacros->SetCurrentMacro(_entryData->_runOptions.macro);
_conditionBehaviors->setCurrentIndex( _conditionBehaviors->setCurrentIndex(
static_cast<int>(_entryData->_runOptions.logic)); static_cast<int>(_entryData->_runOptions.logic));
_reevaluateConditionState->setChecked(
_entryData->_runOptions.reevaluateConditionState);
_actionTypes->setCurrentIndex( _actionTypes->setCurrentIndex(
_entryData->_runOptions.runElseActions ? 1 : 0); _entryData->_runOptions.runElseActions ? 1 : 0);
_skipWhenPaused->setChecked(_entryData->_runOptions.skipWhenPaused); _skipWhenPaused->setChecked(_entryData->_runOptions.skipWhenPaused);
_setInputs->setChecked(_entryData->_runOptions.setInputs);
SetupMacroInput(_entryData->_macro.GetMacro().get());
SetWidgetVisibility(); SetWidgetVisibility();
} }
void MacroActionMacroEdit::MacroChanged(const QString &text) void MacroActionMacroEdit::MacroChanged(const QString &text)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_macro = text; _entryData->_macro = text;
_actionIndex->SetMacro(_entryData->_macro.GetMacro()); const auto &macro = _entryData->_macro.GetMacro();
_actionIndex->SetMacro(macro);
SetupMacroInput(macro.get());
emit HeaderInfoChanged( emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc())); QString::fromStdString(_entryData->GetShortDesc()));
SetWidgetVisibility();
} }
void MacroActionMacroEdit::ActionChanged(int value) void MacroActionMacroEdit::ActionChanged(int value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionMacro::Action>(value); _entryData->_action = static_cast<MacroActionMacro::Action>(value);
SetWidgetVisibility(); SetWidgetVisibility();
} }
void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value) void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_actionIndex = value; _entryData->_actionIndex = value;
} }
void MacroActionMacroEdit::ConditionMacroChanged(const QString &text) void MacroActionMacroEdit::ConditionMacroChanged(const QString &text)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_runOptions.macro = text; _entryData->_runOptions.macro = text;
} }
void MacroActionMacroEdit::ConditionBehaviorChanged(int value) void MacroActionMacroEdit::ConditionBehaviorChanged(int value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_runOptions.logic = _entryData->_runOptions.logic =
static_cast<MacroActionMacro::RunOptions::Logic>(value); static_cast<MacroActionMacro::RunOptions::Logic>(value);
SetWidgetVisibility(); SetWidgetVisibility();
} }
void MacroActionMacroEdit::ReevaluateConditionStateChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.reevaluateConditionState = value;
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionTypeChanged(int value) void MacroActionMacroEdit::ActionTypeChanged(int value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_runOptions.runElseActions = value; _entryData->_runOptions.runElseActions = value;
} }
void MacroActionMacroEdit::SkipWhenPausedChanged(int value) void MacroActionMacroEdit::SkipWhenPausedChanged(int value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_runOptions.skipWhenPaused = value; _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() void MacroActionMacroEdit::SetWidgetVisibility()
{ {
_entryLayout->removeWidget(_actions); _entryLayout->removeWidget(_actions);
@@ -418,10 +468,22 @@ void MacroActionMacroEdit::SetWidgetVisibility()
SetLayoutVisible(_conditionLayout, SetLayoutVisible(_conditionLayout,
_entryData->_action == MacroActionMacro::Action::RUN); _entryData->_action == MacroActionMacro::Action::RUN);
_conditionMacros->setVisible( const bool needsAdditionalConditionWidgets =
_entryData->_action == MacroActionMacro::Action::RUN && _entryData->_action == MacroActionMacro::Action::RUN &&
_entryData->_runOptions.logic != _entryData->_runOptions.logic !=
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS); 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 == _actionTypes->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN); MacroActionMacro::Action::RUN);
_skipWhenPaused->setVisible(_entryData->_action == _skipWhenPaused->setVisible(_entryData->_action ==
@@ -431,12 +493,27 @@ void MacroActionMacroEdit::SetWidgetVisibility()
updateGeometry(); 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 void MacroActionMacro::RunOptions::Save(obs_data_t *obj) const
{ {
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_int(data, "logic", static_cast<int>(logic)); 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, "runElseActions", runElseActions);
obs_data_set_bool(data, "skipWhenPaused", skipWhenPaused); obs_data_set_bool(data, "skipWhenPaused", skipWhenPaused);
obs_data_set_bool(data, "setInputs", setInputs);
inputs.Save(data, "inputs");
macro.Save(data); macro.Save(data);
obs_data_set_obj(obj, "runOptions", data); obs_data_set_obj(obj, "runOptions", data);
} }
@@ -448,8 +525,12 @@ void MacroActionMacro::RunOptions::Load(obs_data_t *obj)
} }
OBSDataAutoRelease data = obs_data_get_obj(obj, "runOptions"); OBSDataAutoRelease data = obs_data_get_obj(obj, "runOptions");
logic = static_cast<Logic>(obs_data_get_int(data, "logic")); logic = static_cast<Logic>(obs_data_get_int(data, "logic"));
reevaluateConditionState =
obs_data_get_bool(data, "reevaluateConditionState");
runElseActions = obs_data_get_bool(data, "runElseActions"); runElseActions = obs_data_get_bool(data, "runElseActions");
skipWhenPaused = obs_data_get_bool(data, "skipWhenPaused"); skipWhenPaused = obs_data_get_bool(data, "skipWhenPaused");
setInputs = obs_data_get_bool(data, "setInputs");
inputs.Load(data, "inputs");
macro.Load(data); macro.Load(data);
} }

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "macro-input.hpp"
#include "macro-selection.hpp" #include "macro-selection.hpp"
#include "macro-segment-selection.hpp" #include "macro-segment-selection.hpp"
@@ -8,7 +9,7 @@
namespace advss { namespace advss {
class MacroActionMacro : public MacroRefAction { class MacroActionMacro final : public MacroRefAction {
public: public:
MacroActionMacro(Macro *m) : MacroAction(m), MacroRefAction(m) {} MacroActionMacro(Macro *m) : MacroAction(m), MacroRefAction(m) {}
bool PerformAction(); bool PerformAction();
@@ -31,8 +32,11 @@ public:
INVERT_CONDITIONS, INVERT_CONDITIONS,
}; };
Logic logic; Logic logic;
bool reevaluateConditionState = false;
bool runElseActions = false; bool runElseActions = false;
bool skipWhenPaused = true; bool skipWhenPaused = true;
bool setInputs = false;
StringList inputs;
MacroRef macro; MacroRef macro;
}; };
@@ -46,7 +50,7 @@ public:
ENABLE_ACTION, ENABLE_ACTION,
TOGGLE_ACTION, TOGGLE_ACTION,
}; };
Action _action = Action::PAUSE; Action _action = Action::RUN;
IntVariable _actionIndex = 1; IntVariable _actionIndex = 1;
RunOptions _runOptions = {}; RunOptions _runOptions = {};
@@ -57,13 +61,14 @@ private:
static const std::string id; static const std::string id;
}; };
class MacroActionMacroEdit : public QWidget { class MacroActionMacroEdit final : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
MacroActionMacroEdit( MacroActionMacroEdit(
QWidget *parent, QWidget *parent,
std::shared_ptr<MacroActionMacro> entryData = nullptr); std::shared_ptr<MacroActionMacro> entryData = nullptr);
~MacroActionMacroEdit();
void UpdateEntryData(); void UpdateEntryData();
static QWidget *Create(QWidget *parent, static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action) std::shared_ptr<MacroAction> action)
@@ -79,24 +84,33 @@ private slots:
void ActionIndexChanged(const IntVariable &value); void ActionIndexChanged(const IntVariable &value);
void ConditionMacroChanged(const QString &text); void ConditionMacroChanged(const QString &text);
void ConditionBehaviorChanged(int value); void ConditionBehaviorChanged(int value);
void ReevaluateConditionStateChanged(int value);
void ActionTypeChanged(int value); void ActionTypeChanged(int value);
void SkipWhenPausedChanged(int value); void SkipWhenPausedChanged(int value);
void SetInputsChanged(int value);
void InputsChanged(const StringList &);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
private: private:
void SetWidgetVisibility(); void SetWidgetVisibility();
void SetupMacroInput(Macro *) const;
MacroSelection *_macros; MacroSelection *_macros;
MacroSegmentSelection *_actionIndex; MacroSegmentSelection *_actionIndex;
QComboBox *_actions; QComboBox *_actions;
MacroSelection *_conditionMacros; MacroSelection *_conditionMacros;
QComboBox *_conditionBehaviors; QComboBox *_conditionBehaviors;
QCheckBox *_reevaluateConditionState;
QComboBox *_actionTypes; QComboBox *_actionTypes;
QCheckBox *_skipWhenPaused; QCheckBox *_skipWhenPaused;
QCheckBox *_setInputs;
MacroInputEdit *_inputs;
QHBoxLayout *_entryLayout; QHBoxLayout *_entryLayout;
QHBoxLayout *_conditionLayout; QHBoxLayout *_conditionLayout;
QHBoxLayout *_reevaluateConditionStateLayout;
QHBoxLayout *_setInputsLayout;
std::shared_ptr<MacroActionMacro> _entryData; std::shared_ptr<MacroActionMacro> _entryData;
bool _loading = true; bool _loading = true;

View File

@@ -0,0 +1,92 @@
#include "macro-action-script.hpp"
#include "layout-helpers.hpp"
#include "macro-helpers.hpp"
#include "properties-view.hpp"
#include "sync-helpers.hpp"
namespace advss {
MacroActionScript::MacroActionScript(Macro *m, const std::string &id,
const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &completionSignal)
: MacroAction(m),
MacroSegmentScript(defaultSettings, propertiesSignalName,
triggerSignal, completionSignal),
_id(id)
{
}
MacroActionScript::MacroActionScript(const advss::MacroActionScript &other)
: MacroAction(other.GetMacro()),
MacroSegmentScript(other),
_id(other._id)
{
}
bool MacroActionScript::PerformAction()
{
if (!ScriptHandler::ActionIdIsValid(_id)) {
blog(LOG_WARNING, "skipping unknown script action \"%s\"",
_id.c_str());
return true;
}
(void)SendTriggerSignal();
return true;
}
void MacroActionScript::LogAction() const
{
ablog(LOG_INFO, "performing script action \"%s\"", GetId().c_str());
}
bool MacroActionScript::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
MacroSegmentScript::Save(obj);
return true;
}
bool MacroActionScript::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
MacroSegmentScript::Load(obj);
return true;
}
std::shared_ptr<MacroAction> MacroActionScript::Copy() const
{
return std::make_shared<MacroActionScript>(*this);
}
void MacroActionScript::WaitForCompletion() const
{
using namespace std::chrono_literals;
auto start = std::chrono::high_resolution_clock::now();
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
start - start);
const auto timeoutMs = GetTimeoutSeconds() * 1000.0;
std::unique_lock<std::mutex> lock(*GetMutex());
while (!TriggerIsCompleted()) {
if (MacroWaitShouldAbort() || MacroIsStopped(GetMacro())) {
break;
}
if ((double)timePassed.count() > timeoutMs) {
blog(LOG_INFO, "script action timeout (%s)",
_id.c_str());
break;
}
GetMacroWaitCV().wait_for(lock, 10ms);
const auto now = std::chrono::high_resolution_clock::now();
timePassed =
std::chrono::duration_cast<std::chrono::milliseconds>(
now - start);
}
}
} // namespace advss

View File

@@ -0,0 +1,29 @@
#pragma once
#include "macro-action-edit.hpp"
#include "macro-script-handler.hpp"
#include "macro-segment-script.hpp"
namespace advss {
class MacroActionScript : public MacroAction, public MacroSegmentScript {
public:
MacroActionScript(Macro *m, const std::string &id,
const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &signalComplete);
MacroActionScript(const advss::MacroActionScript &);
bool PerformAction();
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() const { return _id; };
std::shared_ptr<MacroAction> Copy() const;
private:
void WaitForCompletion() const;
std::string _id = "";
};
} // namespace advss

View File

@@ -6,7 +6,6 @@
#include "macro.hpp" #include "macro.hpp"
#include "non-modal-dialog.hpp" #include "non-modal-dialog.hpp"
#include "source-helpers.hpp" #include "source-helpers.hpp"
#include "ui-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -59,6 +58,8 @@ const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
"AdvSceneSwitcher.action.variable.type.padValue"}, "AdvSceneSwitcher.action.variable.type.padValue"},
{MacroActionVariable::Type::TRUNCATE, {MacroActionVariable::Type::TRUNCATE,
"AdvSceneSwitcher.action.variable.type.truncateValue"}, "AdvSceneSwitcher.action.variable.type.truncateValue"},
{MacroActionVariable::Type::SWAP_VALUES,
"AdvSceneSwitcher.action.variable.type.swapValues"},
}; };
static void apppend(Variable &var, const std::string &value) static void apppend(Variable &var, const std::string &value)
@@ -393,6 +394,17 @@ bool MacroActionVariable::PerformAction()
var->SetValue( var->SetValue(
truncate(var->Value(), _direction, _stringLength)); truncate(var->Value(), _direction, _stringLength));
return true; 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; return true;
@@ -631,7 +643,8 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_scenes(new SceneSelectionWidget(this, true, false, true, true, _scenes(new SceneSelectionWidget(this, true, false, true, true,
true)), true)),
_tempVars(new TempVariableSelection(this)), _tempVars(new TempVariableSelection(this)),
_tempVarsHelp(new QLabel()), _tempVarsHelp(new HelpIcon(obs_module_text(
"AdvSceneSwitcher.action.variable.type.setToTempvar.help"))),
_sceneItemIndex(new VariableSpinBox()), _sceneItemIndex(new VariableSpinBox()),
_direction(new QComboBox()), _direction(new QComboBox()),
_stringLength(new VariableSpinBox()), _stringLength(new VariableSpinBox()),
@@ -659,15 +672,6 @@ MacroActionVariableEdit::MacroActionVariableEdit(
populateTypeSelection(_actions); populateTypeSelection(_actions);
populateDirectionSelection(_direction); populateDirectionSelection(_direction);
QString path = GetThemeTypeName() == "Light"
? ":/res/images/help.svg"
: ":/res/images/help_light.svg";
QIcon icon(path);
QPixmap pixmap = icon.pixmap(QSize(16, 16));
_tempVarsHelp->setPixmap(pixmap);
_tempVarsHelp->setToolTip(obs_module_text(
"AdvSceneSwitcher.action.variable.type.setToTempvar.help"));
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)), QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
this, SLOT(VariableChanged(const QString &))); this, SLOT(VariableChanged(const QString &)));
QWidget::connect(_variables2, SIGNAL(SelectionChanged(const QString &)), QWidget::connect(_variables2, SIGNAL(SelectionChanged(const QString &)),
@@ -1318,8 +1322,9 @@ void MacroActionVariableEdit::SetWidgetVisibility()
AddStretchIfNecessary(_entryLayout); AddStretchIfNecessary(_entryLayout);
} }
_variables2->setVisible(_entryData->_type == _variables2->setVisible(
MacroActionVariable::Type::APPEND_VAR); _entryData->_type == MacroActionVariable::Type::APPEND_VAR ||
_entryData->_type == MacroActionVariable::Type::SWAP_VALUES);
_strValue->setVisible( _strValue->setVisible(
_entryData->_type == _entryData->_type ==
MacroActionVariable::Type::SET_FIXED_VALUE || MacroActionVariable::Type::SET_FIXED_VALUE ||

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "help-icon.hpp"
#include "macro-segment-selection.hpp" #include "macro-segment-selection.hpp"
#include "regex-config.hpp" #include "regex-config.hpp"
#include "resizing-text-edit.hpp" #include "resizing-text-edit.hpp"
@@ -48,6 +49,7 @@ public:
SCENE_ITEM_NAME, SCENE_ITEM_NAME,
PAD, PAD,
TRUNCATE, TRUNCATE,
SWAP_VALUES,
}; };
Type _type = Type::SET_FIXED_VALUE; Type _type = Type::SET_FIXED_VALUE;
@@ -185,7 +187,7 @@ private:
VariableLineEdit *_envVariable; VariableLineEdit *_envVariable;
SceneSelectionWidget *_scenes; SceneSelectionWidget *_scenes;
TempVariableSelection *_tempVars; TempVariableSelection *_tempVars;
QLabel *_tempVarsHelp; HelpIcon *_tempVarsHelp;
VariableSpinBox *_sceneItemIndex; VariableSpinBox *_sceneItemIndex;
QComboBox *_direction; QComboBox *_direction;
VariableSpinBox *_stringLength; VariableSpinBox *_stringLength;

View File

@@ -1,6 +1,6 @@
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "macro-properties.hpp" #include "macro-settings.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "path-helpers.hpp" #include "path-helpers.hpp"
#include "plugin-state-helpers.hpp" #include "plugin-state-helpers.hpp"
@@ -10,26 +10,6 @@
namespace advss { namespace advss {
static inline void populateLogicSelection(QComboBox *list, bool root = false)
{
if (root) {
for (const auto &entry : MacroCondition::logicTypes) {
if (static_cast<int>(entry.first) < logic_root_offset) {
list->addItem(obs_module_text(
entry.second._name.c_str()));
}
}
} else {
for (const auto &entry : MacroCondition::logicTypes) {
if (static_cast<int>(entry.first) >=
logic_root_offset) {
list->addItem(obs_module_text(
entry.second._name.c_str()));
}
}
}
}
static inline void populateConditionSelection(QComboBox *list) static inline void populateConditionSelection(QComboBox *list)
{ {
for (const auto &[_, condition] : for (const auto &[_, condition] :
@@ -120,13 +100,13 @@ void DurationModifierEdit::Collapse(bool collapse)
MacroConditionEdit::MacroConditionEdit( MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData, QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root) const std::string &id, bool isRootCondition)
: MacroSegmentEdit(parent), : MacroSegmentEdit(parent),
_logicSelection(new QComboBox()), _logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()), _conditionSelection(new FilterComboBox()),
_dur(new DurationModifierEdit()), _dur(new DurationModifierEdit()),
_entryData(entryData), _entryData(entryData),
_isRoot(root) _isRoot(isRootCondition)
{ {
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)), QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(LogicSelectionChanged(int))); this, SLOT(LogicSelectionChanged(int)));
@@ -139,7 +119,7 @@ MacroConditionEdit::MacroConditionEdit(
this, this,
SLOT(DurationModifierChanged(DurationModifier::Type))); SLOT(DurationModifierChanged(DurationModifier::Type)));
populateLogicSelection(_logicSelection, root); Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition);
populateConditionSelection(_conditionSelection); populateConditionSelection(_conditionSelection);
_section->AddHeaderWidget(_logicSelection); _section->AddHeaderWidget(_logicSelection);
@@ -168,15 +148,10 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
return; return;
} }
LogicType type;
if (IsRootNode()) {
type = static_cast<LogicType>(idx);
} else {
type = static_cast<LogicType>(idx + logic_root_offset);
}
auto lock = LockContext(); auto lock = LockContext();
(*_entryData)->SetLogicType(type); const auto logic = static_cast<Logic::Type>(
_logicSelection->itemData(idx).toInt());
(*_entryData)->SetLogicType(logic);
} }
bool MacroConditionEdit::IsRootNode() bool MacroConditionEdit::IsRootNode()
@@ -186,13 +161,9 @@ bool MacroConditionEdit::IsRootNode()
void MacroConditionEdit::SetLogicSelection() void MacroConditionEdit::SetLogicSelection()
{ {
auto logic = (*_entryData)->GetLogicType(); const auto logic = (*_entryData)->GetLogicType();
if (IsRootNode()) { _logicSelection->setCurrentIndex(
_logicSelection->setCurrentIndex(static_cast<int>(logic)); _logicSelection->findData(static_cast<int>(logic)));
} else {
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
logic_root_offset);
}
} }
void MacroConditionEdit::SetRootNode(bool root) void MacroConditionEdit::SetRootNode(bool root)
@@ -200,7 +171,7 @@ void MacroConditionEdit::SetRootNode(bool root)
_isRoot = root; _isRoot = root;
const QSignalBlocker blocker(_logicSelection); const QSignalBlocker blocker(_logicSelection);
_logicSelection->clear(); _logicSelection->clear();
populateLogicSelection(_logicSelection, root); Logic::PopulateLogicTypeSelection(_logicSelection, root);
SetLogicSelection(); SetLogicSelection();
} }
@@ -301,17 +272,17 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
} }
std::string id; std::string id;
LogicType logic; Logic::Type logic;
if (idx >= 1) { if (idx >= 1) {
id = macro->Conditions().at(idx - 1)->GetId(); id = macro->Conditions().at(idx - 1)->GetId();
if (idx == 1) { if (idx == 1) {
logic = LogicType::OR; logic = Logic::Type::OR;
} else { } else {
logic = macro->Conditions().at(idx - 1)->GetLogicType(); logic = macro->Conditions().at(idx - 1)->GetLogicType();
} }
} else { } else {
id = MacroCondition::GetDefaultID(); id = MacroCondition::GetDefaultID();
logic = LogicType::ROOT_NONE; logic = Logic::Type::ROOT_NONE;
} }
OBSDataAutoRelease data; OBSDataAutoRelease data;
@@ -324,7 +295,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx, void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
const std::string &id, const std::string &id,
obs_data_t *data, LogicType logic) obs_data_t *data, Logic::Type logic)
{ {
if (idx < 0 || idx > (int)macro->Conditions().size()) { if (idx < 0 || idx > (int)macro->Conditions().size()) {
assert(false); assert(false);
@@ -389,7 +360,7 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
macro->UpdateConditionIndices(); macro->UpdateConditionIndices();
if (idx == 0 && macro->Conditions().size() > 0) { if (idx == 0 && macro->Conditions().size() > 0) {
auto newRoot = macro->Conditions().at(0); auto newRoot = macro->Conditions().at(0);
newRoot->SetLogicType(LogicType::ROOT_NONE); newRoot->SetLogicType(Logic::Type::ROOT_NONE);
static_cast<MacroConditionEdit *>( static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(0)) ui->conditionsList->WidgetAt(0))
->SetRootNode(true); ->SetRootNode(true);
@@ -540,22 +511,23 @@ void AdvSceneSwitcher::MacroConditionReorder(int to, int from)
auto lock = LockContext(); auto lock = LockContext();
auto condition = macro->Conditions().at(from); auto condition = macro->Conditions().at(from);
if (to == 0) { if (to == 0) {
condition->SetLogicType(LogicType::ROOT_NONE); condition->SetLogicType(Logic::Type::ROOT_NONE);
static_cast<MacroConditionEdit *>( static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(from)) ui->conditionsList->WidgetAt(from))
->SetRootNode(true); ->SetRootNode(true);
macro->Conditions().at(0)->SetLogicType(LogicType::AND); macro->Conditions().at(0)->SetLogicType(
Logic::Type::AND);
static_cast<MacroConditionEdit *>( static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(0)) ui->conditionsList->WidgetAt(0))
->SetRootNode(false); ->SetRootNode(false);
} }
if (from == 0) { if (from == 0) {
condition->SetLogicType(LogicType::AND); condition->SetLogicType(Logic::Type::AND);
static_cast<MacroConditionEdit *>( static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(from)) ui->conditionsList->WidgetAt(from))
->SetRootNode(false); ->SetRootNode(false);
macro->Conditions().at(1)->SetLogicType( macro->Conditions().at(1)->SetLogicType(
LogicType::ROOT_NONE); Logic::Type::ROOT_NONE);
static_cast<MacroConditionEdit *>( static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(1)) ui->conditionsList->WidgetAt(1))
->SetRootNode(true); ->SetRootNode(true);

View File

@@ -2,6 +2,7 @@
#include "macro-condition.hpp" #include "macro-condition.hpp"
#include "macro-condition-factory.hpp" #include "macro-condition-factory.hpp"
#include "filter-combo-box.hpp" #include "filter-combo-box.hpp"
#include "duration-control.hpp"
#include <memory> #include <memory>

View File

@@ -1,7 +1,24 @@
#include "macro-condition-factory.hpp" #include "macro-condition-factory.hpp"
#include <mutex>
namespace advss { namespace advss {
namespace {
class MacroConditionUnknown : public MacroCondition {
public:
MacroConditionUnknown(Macro *m) : MacroCondition(m) {}
bool CheckCondition() { return false; }
bool Save(obs_data_t *obj) const { return MacroCondition::Save(obj); };
bool Load(obs_data_t *obj) { return MacroCondition::Load(obj); };
std::string GetId() const { return "unknown"; }
};
} // namespace
static std::recursive_mutex mutex;
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap() std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
{ {
static std::map<std::string, MacroConditionInfo> _methods; static std::map<std::string, MacroConditionInfo> _methods;
@@ -11,6 +28,7 @@ std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
bool MacroConditionFactory::Register(const std::string &id, bool MacroConditionFactory::Register(const std::string &id,
MacroConditionInfo info) MacroConditionInfo info)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it == GetMap().end()) { if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info; GetMap()[id] = info;
return true; return true;
@@ -18,35 +36,60 @@ bool MacroConditionFactory::Register(const std::string &id,
return false; return false;
} }
bool MacroConditionFactory::Deregister(const std::string &id)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
if (GetMap().count(id) == 0) {
return false;
}
GetMap().erase(id);
return true;
}
static std::shared_ptr<MacroCondition> createUnknownCondition(Macro *m)
{
return std::make_shared<MacroConditionUnknown>(m);
}
std::shared_ptr<MacroCondition> std::shared_ptr<MacroCondition>
MacroConditionFactory::Create(const std::string &id, Macro *m) MacroConditionFactory::Create(const std::string &id, Macro *m)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) { if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._create(m); return it->second._create(m);
} }
return nullptr; return createUnknownCondition(m);
}
static QWidget *createUnknownConditionWidget()
{
return new QLabel(
obs_module_text("AdvSceneSwitcher.condition.unknown"));
} }
QWidget * QWidget *
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent, MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition> cond) std::shared_ptr<MacroCondition> cond)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) { if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createWidget(parent, cond); return it->second._createWidget(parent, cond);
} }
return nullptr; return createUnknownConditionWidget();
} }
std::string MacroConditionFactory::GetConditionName(const std::string &id) std::string MacroConditionFactory::GetConditionName(const std::string &id)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) { if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name; return it->second._name;
} }
return "unknown condition"; return obs_module_text("AdvSceneSwitcher.condition.unknown");
} }
std::string MacroConditionFactory::GetIdByName(const QString &name) std::string MacroConditionFactory::GetIdByName(const QString &name)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
for (auto it : GetMap()) { for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) { if (name == obs_module_text(it.second._name.c_str())) {
return it.first; return it.first;
@@ -57,10 +100,21 @@ std::string MacroConditionFactory::GetIdByName(const QString &name)
bool MacroConditionFactory::UsesDurationModifier(const std::string &id) bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) { if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._useDurationModifier; return it->second._useDurationModifier;
} }
return false; return false;
} }
bool CanCreateDefaultCondition()
{
const auto condition = MacroConditionFactory::Create(
MacroCondition::GetDefaultID().data(), nullptr);
if (!condition) {
return false;
}
return condition->GetId() == MacroCondition::GetDefaultID().data();
}
} // namespace advss } // namespace advss

View File

@@ -6,10 +6,11 @@
namespace advss { namespace advss {
struct MacroConditionInfo { struct MacroConditionInfo {
using CreateCondition = std::shared_ptr<MacroCondition> (*)(Macro *m);
using CreateConditionWidget = using CreateConditionWidget =
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>); QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
CreateCondition _create = nullptr;
std::function<std::shared_ptr<MacroCondition>(Macro *m)> _create =
nullptr;
CreateConditionWidget _createWidget = nullptr; CreateConditionWidget _createWidget = nullptr;
std::string _name; std::string _name;
bool _useDurationModifier = true; bool _useDurationModifier = true;
@@ -19,6 +20,7 @@ class MacroConditionFactory {
public: public:
MacroConditionFactory() = delete; MacroConditionFactory() = delete;
EXPORT static bool Register(const std::string &, MacroConditionInfo); EXPORT static bool Register(const std::string &, MacroConditionInfo);
static bool Deregister(const std::string &);
static std::shared_ptr<MacroCondition> Create(const std::string &, static std::shared_ptr<MacroCondition> Create(const std::string &,
Macro *m); Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent, static QWidget *CreateWidget(const std::string &id, QWidget *parent,
@@ -32,4 +34,6 @@ private:
static std::map<std::string, MacroConditionInfo> &GetMap(); static std::map<std::string, MacroConditionInfo> &GetMap();
}; };
bool CanCreateDefaultCondition();
} // namespace advss } // namespace advss

View File

@@ -0,0 +1,79 @@
#include "macro-condition-script.hpp"
#include "layout-helpers.hpp"
#include "macro-helpers.hpp"
#include "sync-helpers.hpp"
namespace advss {
MacroConditionScript::MacroConditionScript(
Macro *m, const std::string &id, const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &completionSignal)
: MacroCondition(m),
MacroSegmentScript(defaultSettings, propertiesSignalName,
triggerSignal, completionSignal),
_id(id)
{
}
MacroConditionScript::MacroConditionScript(
const advss::MacroConditionScript &other)
: MacroCondition(other.GetMacro()),
MacroSegmentScript(other),
_id(other._id)
{
}
bool MacroConditionScript::CheckCondition()
{
if (!ScriptHandler::ConditionIdIsValid(_id)) {
blog(LOG_WARNING, "skipping unknown script condition \"%s\"",
_id.c_str());
return false;
}
return SendTriggerSignal();
}
bool MacroConditionScript::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
MacroSegmentScript::Save(obj);
return true;
}
bool MacroConditionScript::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
MacroSegmentScript::Load(obj);
return true;
}
void MacroConditionScript::WaitForCompletion() const
{
using namespace std::chrono_literals;
auto start = std::chrono::high_resolution_clock::now();
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
start - start);
const auto timeoutMs = GetTimeoutSeconds() * 1000.0;
while (!TriggerIsCompleted()) {
if (MacroWaitShouldAbort() || MacroIsStopped(GetMacro())) {
break;
}
if ((double)timePassed.count() > timeoutMs) {
blog(LOG_INFO, "script condition timeout (%s)",
_id.c_str());
break;
}
std::this_thread::sleep_for(10ms);
const auto now = std::chrono::high_resolution_clock::now();
timePassed =
std::chrono::duration_cast<std::chrono::milliseconds>(
now - start);
}
}
} // namespace advss

View File

@@ -0,0 +1,27 @@
#pragma once
#include "macro-condition-edit.hpp"
#include "macro-script-handler.hpp"
#include "macro-segment-script.hpp"
namespace advss {
class MacroConditionScript : public MacroCondition, public MacroSegmentScript {
public:
MacroConditionScript(Macro *m, const std::string &id,
const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &signalComplete);
MacroConditionScript(const advss::MacroConditionScript &);
bool CheckCondition();
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() const { return _id; };
private:
void WaitForCompletion() const;
std::string _id = "";
};
} // namespace advss

View File

@@ -2,79 +2,6 @@
namespace advss { namespace advss {
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
{LogicType::AND, {"AdvSceneSwitcher.logic.and"}},
{LogicType::OR, {"AdvSceneSwitcher.logic.or"}},
{LogicType::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
{LogicType::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
{LogicType::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
};
void DurationModifier::Save(obs_data_t *obj, const char *condName,
const char *duration) const
{
obs_data_set_int(obj, condName, static_cast<int>(_type));
_dur.Save(obj, duration);
}
void DurationModifier::Load(obs_data_t *obj, const char *condName,
const char *duration)
{
// For backwards compatibility check if duration value exist without
// time constraint condition - if so assume DurationCondition::MORE
if (!obs_data_has_user_value(obj, condName) &&
obs_data_has_user_value(obj, duration)) {
obs_data_set_int(obj, condName, static_cast<int>(Type::MORE));
}
_type = static_cast<Type>(obs_data_get_int(obj, condName));
_dur.Load(obj, duration);
// TODO: remove this fallback
if (obs_data_has_user_value(obj, "displayUnit")) {
_dur.SetUnit(static_cast<Duration::Unit>(
obs_data_get_int(obj, "displayUnit")));
}
}
bool DurationModifier::DurationReached()
{
switch (_type) {
case DurationModifier::Type::NONE:
return true;
break;
case DurationModifier::Type::MORE:
return _dur.DurationReached();
break;
case DurationModifier::Type::EQUAL:
if (_dur.DurationReached() && !_timeReached) {
_timeReached = true;
return true;
}
break;
case DurationModifier::Type::LESS:
return !_dur.DurationReached();
break;
case DurationModifier::Type::WITHIN:
if (_dur.IsReset()) {
return false;
}
return !_dur.DurationReached();
break;
default:
break;
}
return false;
}
void DurationModifier::Reset()
{
_timeReached = false;
_dur.Reset();
}
MacroCondition::MacroCondition(Macro *m, bool supportsVariableValue) MacroCondition::MacroCondition(Macro *m, bool supportsVariableValue)
: MacroSegment(m, supportsVariableValue) : MacroSegment(m, supportsVariableValue)
{ {
@@ -84,73 +11,64 @@ bool MacroCondition::Save(obs_data_t *obj) const
{ {
MacroSegment::Save(obj); MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str()); obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_int(obj, "logic", static_cast<int>(_logic)); _logic.Save(obj, "logic");
_durationModifier.Save(obj);
// To avoid conflicts with conditions which also use the Duration class
// save the duration modifier in a separate obj
auto durObj = obs_data_create();
_duration.Save(durObj);
obs_data_set_obj(obj, "durationModifier", durObj);
obs_data_release(durObj);
return true; return true;
} }
bool MacroCondition::Load(obs_data_t *obj) bool MacroCondition::Load(obs_data_t *obj)
{ {
MacroSegment::Load(obj); MacroSegment::Load(obj);
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic")); _logic.Load(obj, "logic");
if (obs_data_has_user_value(obj, "durationModifier")) { _durationModifier.Load(obj);
auto durObj = obs_data_get_obj(obj, "durationModifier");
_duration.Load(durObj);
obs_data_release(durObj);
} else {
// For backwards compatibility
_duration.Load(obj);
}
return true; return true;
} }
void MacroCondition::ValidateLogicSelection(bool isRootCondition,
const char *context)
{
if (_logic.IsValidSelection(isRootCondition)) {
return;
}
if (_logic.IsRootType()) {
_logic.SetType(Logic::Type::ROOT_NONE);
blog(LOG_WARNING,
"setting invalid logic selection to 'if' for macro %s",
context);
return;
}
_logic.SetType(Logic::Type::NONE);
blog(LOG_WARNING,
"setting invalid logic selection to 'ignore' for macro %s",
context);
}
void MacroCondition::ResetDuration() void MacroCondition::ResetDuration()
{ {
_duration.Reset(); _durationModifier.ResetDuration();
} }
void MacroCondition::CheckDurationModifier(bool &val) bool MacroCondition::CheckDurationModifier(bool conditionValue)
{ {
if (_duration.GetType() != DurationModifier::Type::WITHIN && !val) { return _durationModifier.CheckConditionWithDurationModifier(
_duration.Reset(); conditionValue);
} }
if (_duration.GetType() == DurationModifier::Type::WITHIN && val) {
_duration.Reset(); DurationModifier MacroCondition::GetDurationModifier() const
} {
switch (_duration.GetType()) { return _durationModifier;
case DurationModifier::Type::NONE:
case DurationModifier::Type::MORE:
case DurationModifier::Type::EQUAL:
case DurationModifier::Type::LESS:
val = val && _duration.DurationReached();
return;
case DurationModifier::Type::WITHIN:
if (val) {
_duration.SetTimeRemaining(
_duration.GetDuration().Seconds());
}
val = val || _duration.DurationReached();
break;
default:
break;
}
} }
void MacroCondition::SetDurationModifier(DurationModifier::Type m) void MacroCondition::SetDurationModifier(DurationModifier::Type m)
{ {
_duration.SetModifier(m); _durationModifier.SetModifier(m);
} }
void MacroCondition::SetDuration(const Duration &duration) void MacroCondition::SetDuration(const Duration &duration)
{ {
_duration.SetValue(duration); _durationModifier.SetDuration(duration);
} }
std::string_view MacroCondition::GetDefaultID() std::string_view MacroCondition::GetDefaultID()

View File

@@ -1,63 +1,11 @@
#pragma once #pragma once
#include "macro-segment.hpp" #include "macro-segment.hpp"
#include "condition-logic.hpp"
#include "duration-modifier.hpp"
#include "macro-ref.hpp" #include "macro-ref.hpp"
#include <duration-control.hpp>
namespace advss { namespace advss {
constexpr auto logic_root_offset = 100;
enum class LogicType {
ROOT_NONE = 0,
ROOT_NOT,
ROOT_LAST,
// leave some space for potential expansion
NONE = 100,
AND,
OR,
AND_NOT,
OR_NOT,
LAST,
};
static inline bool isRootLogicType(LogicType l)
{
return static_cast<int>(l) < logic_root_offset;
}
struct LogicTypeInfo {
std::string _name;
};
class DurationModifier {
public:
enum class Type {
NONE,
MORE,
EQUAL,
LESS,
WITHIN, // Condition will remain true for a set a amount of time
// regardless of current state
};
void Save(obs_data_t *obj, const char *condName = "time_constraint",
const char *duration = "seconds") const;
void Load(obs_data_t *obj, const char *condName = "time_constraint",
const char *duration = "seconds");
void SetModifier(Type cond) { _type = cond; }
void SetTimeRemaining(const double &val) { _dur.SetTimeRemaining(val); }
void SetValue(const Duration &value) { _dur = value; }
Type GetType() { return _type; }
Duration GetDuration() { return _dur; }
bool DurationReached();
void Reset();
private:
Type _type = Type::NONE;
Duration _dur;
bool _timeReached = false;
};
class EXPORT MacroCondition : public MacroSegment { class EXPORT MacroCondition : public MacroSegment {
public: public:
MacroCondition(Macro *m, bool supportsVariableValue = false); MacroCondition(Macro *m, bool supportsVariableValue = false);
@@ -65,20 +13,24 @@ public:
virtual bool CheckCondition() = 0; virtual bool CheckCondition() = 0;
virtual bool Save(obs_data_t *obj) const = 0; virtual bool Save(obs_data_t *obj) const = 0;
virtual bool Load(obs_data_t *obj) = 0; virtual bool Load(obs_data_t *obj) = 0;
LogicType GetLogicType() { return _logic; }
void SetLogicType(LogicType logic) { _logic = logic; } Logic::Type GetLogicType() const { return _logic.GetType(); }
static const std::map<LogicType, LogicTypeInfo> logicTypes; void SetLogicType(const Logic::Type &logic) { _logic.SetType(logic); }
void ResetDuration();
void CheckDurationModifier(bool &val); void ValidateLogicSelection(bool isRootCondition, const char *context);
DurationModifier GetDurationModifier() { return _duration; }
DurationModifier GetDurationModifier() const;
void SetDurationModifier(DurationModifier::Type m); void SetDurationModifier(DurationModifier::Type m);
void SetDuration(const Duration &duration); void SetDuration(const Duration &duration);
void ResetDuration();
bool CheckDurationModifier(bool conditionValue);
static std::string_view GetDefaultID(); static std::string_view GetDefaultID();
private: private:
LogicType _logic = LogicType::ROOT_NONE; Logic _logic = Logic(Logic::Type::ROOT_NONE);
DurationModifier _duration; DurationModifier _durationModifier;
}; };
class EXPORT MacroRefCondition : virtual public MacroCondition { class EXPORT MacroRefCondition : virtual public MacroCondition {

View File

@@ -6,14 +6,14 @@
namespace advss { namespace advss {
MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent, MacroDock::MacroDock(std::weak_ptr<Macro> m,
const StringVariable &runButtonText, const StringVariable &runButtonText,
const StringVariable &pauseButtonText, const StringVariable &pauseButtonText,
const StringVariable &unpauseButtonText, const StringVariable &unpauseButtonText,
const StringVariable &conditionsTrueText, const StringVariable &conditionsTrueText,
const StringVariable &conditionsFalseText, const StringVariable &conditionsFalseText,
bool enableHighlight) bool enableHighlight)
: OBSDock(parent), : QFrame(),
_runButtonText(runButtonText), _runButtonText(runButtonText),
_pauseButtonText(pauseButtonText), _pauseButtonText(pauseButtonText),
_unpauseButtonText(unpauseButtonText), _unpauseButtonText(unpauseButtonText),
@@ -25,19 +25,16 @@ MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent,
_statusText(new QLabel(conditionsFalseText.c_str())), _statusText(new QLabel(conditionsFalseText.c_str())),
_macro(m) _macro(m)
{ {
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
auto macro = _macro.lock(); auto macro = _macro.lock();
if (macro) { if (macro) {
setWindowTitle(QString::fromStdString(macro->Name()));
_run->setVisible(macro->DockHasRunButton()); _run->setVisible(macro->DockHasRunButton());
_pauseToggle->setVisible(macro->DockHasPauseButton()); _pauseToggle->setVisible(macro->DockHasPauseButton());
_statusText->setVisible(macro->DockHasStatusLabel()); _statusText->setVisible(macro->DockHasStatusLabel());
} else {
setWindowTitle("<deleted macro>");
} }
setFeatures(DockWidgetClosable | DockWidgetMovable |
DockWidgetFloatable);
QWidget::connect(_run, SIGNAL(clicked()), this, SLOT(RunClicked())); QWidget::connect(_run, SIGNAL(clicked()), this, SLOT(RunClicked()));
QWidget::connect(_pauseToggle, SIGNAL(clicked()), this, QWidget::connect(_pauseToggle, SIGNAL(clicked()), this,
SLOT(PauseToggleClicked())); SLOT(PauseToggleClicked()));
@@ -52,21 +49,7 @@ MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent,
QWidget::connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight())); QWidget::connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight()));
_timer.start(500); _timer.start(500);
// QFrame wrapper is necessary to avoid dock being partially setLayout(layout);
// transparent
QFrame *wrapper = new QFrame;
wrapper->setFrameShape(QFrame::StyledPanel);
wrapper->setFrameShadow(QFrame::Sunken);
wrapper->setLayout(layout);
setWidget(wrapper);
setFloating(true);
hide();
}
void MacroDock::SetName(const QString &name)
{
setWindowTitle(name);
} }
void MacroDock::ShowRunButton(bool value) void MacroDock::ShowRunButton(bool value)
@@ -167,7 +150,7 @@ void MacroDock::Highlight()
} }
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 && if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
macro->WasExecutedSince(_lastHighlightCheckTime)) { 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(); _lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
} }

View File

@@ -1,10 +1,9 @@
#pragma once #pragma once
#include "obs-dock.hpp"
#include "variable-string.hpp" #include "variable-string.hpp"
#include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QTimer> #include <QTimer>
#include <QLabel>
#include <memory> #include <memory>
#include <chrono> #include <chrono>
@@ -12,18 +11,16 @@ namespace advss {
class Macro; class Macro;
class MacroDock : public OBSDock { class MacroDock : public QFrame {
Q_OBJECT Q_OBJECT
public: public:
MacroDock(std::weak_ptr<Macro>, QWidget *parent, MacroDock(std::weak_ptr<Macro>, const StringVariable &runButtonText,
const StringVariable &runButtonText,
const StringVariable &pauseButtonText, const StringVariable &pauseButtonText,
const StringVariable &unpauseButtonText, const StringVariable &unpauseButtonText,
const StringVariable &conditionsTrueText, const StringVariable &conditionsTrueText,
const StringVariable &conditionsFalseText, const StringVariable &conditionsFalseText,
bool enableHighlight); bool enableHighlight);
void SetName(const QString &);
void ShowRunButton(bool); void ShowRunButton(bool);
void SetRunButtonText(const StringVariable &); void SetRunButtonText(const StringVariable &);
void ShowPauseButton(bool); void ShowPauseButton(bool);

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

@@ -0,0 +1,241 @@
#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()) {
blog(LOG_WARNING,
"%s - sizes do not match (variables: %d, values %d)",
__func__, (int)_inputVariables.size(), (int)values.size());
}
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(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(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 { namespace advss {
MacroList::MacroList(QWidget *parent, bool allowDuplicates, bool reorder) MacroList::MacroList(QWidget *parent, bool allowDuplicates, bool reorder)
: QWidget(parent), : ListEditor(parent, reorder),
_list(new QListWidget()), _allowDuplicates(allowDuplicates)
_add(new QPushButton()),
_remove(new QPushButton()),
_up(new QPushButton()),
_down(new QPushButton()),
_controlsLayout(new QHBoxLayout()),
_allowDuplicates(allowDuplicates),
_reorder(reorder)
{ {
_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(), QWidget::connect(window(),
SIGNAL(MacroRenamed(const QString &, const QString &)), SIGNAL(MacroRenamed(const QString &, const QString &)),
this, this,
SLOT(MacroRename(const QString &, const QString &))); SLOT(MacroRename(const QString &, const QString &)));
QWidget::connect(window(), SIGNAL(MacroRemoved(const QString &)), this, QWidget::connect(window(), SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &))); SLOT(MacroRemove(const QString &)));
UpdateListSize();
_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();
} }
void MacroList::SetContent(const std::vector<MacroRef> &macros) void MacroList::SetContent(const std::vector<MacroRef> &macros)
@@ -89,12 +37,15 @@ void MacroList::SetContent(const std::vector<MacroRef> &macros)
new QListWidgetItem(listEntryName, _list); new QListWidgetItem(listEntryName, _list);
item->setData(Qt::UserRole, listEntryName); item->setData(Qt::UserRole, listEntryName);
} }
SetMacroListSize(); UpdateListSize();
} }
void MacroList::AddControl(QWidget *widget) QAction *MacroList::AddControl(QWidget *widget, bool addSeperator)
{ {
_controlsLayout->insertWidget(_controlsLayout->count() - 1, widget); if (addSeperator) {
_controls->addSeparator();
}
return _controls->addWidget(widget);
} }
int MacroList::CurrentRow() int MacroList::CurrentRow()
@@ -122,13 +73,13 @@ void MacroList::MacroRemove(const QString &name)
delete _list->item(idx); delete _list->item(idx);
idx = FindEntry(name.toStdString()); idx = FindEntry(name.toStdString());
} }
SetMacroListSize(); UpdateListSize();
} }
void MacroList::Add() void MacroList::Add()
{ {
std::string macroName; std::string macroName;
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName); bool accepted = MacroSelectionDialog::AskForMacro(macroName);
if (!accepted || macroName.empty()) { if (!accepted || macroName.empty()) {
return; return;
@@ -142,7 +93,7 @@ void MacroList::Add()
auto item = auto item =
new QListWidgetItem(QString::fromStdString(macroName), _list); new QListWidgetItem(QString::fromStdString(macroName), _list);
item->setData(Qt::UserRole, QString::fromStdString(macroName)); item->setData(Qt::UserRole, QString::fromStdString(macroName));
SetMacroListSize(); UpdateListSize();
emit Added(macroName); emit Added(macroName);
} }
@@ -154,7 +105,7 @@ void MacroList::Remove()
return; return;
} }
delete item; delete item;
SetMacroListSize(); UpdateListSize();
emit Removed(idx); emit Removed(idx);
} }
@@ -178,10 +129,10 @@ void MacroList::Down()
} }
} }
void MacroList::MacroItemClicked(QListWidgetItem *item) void MacroList::Clicked(QListWidgetItem *item)
{ {
std::string macroName; std::string macroName;
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName); bool accepted = MacroSelectionDialog::AskForMacro(macroName);
if (!accepted || macroName.empty()) { if (!accepted || macroName.empty()) {
return; return;
@@ -216,10 +167,4 @@ int MacroList::FindEntry(const std::string &macro)
return idx; return idx;
} }
void MacroList::SetMacroListSize()
{
SetHeightToContentHeight(_list);
adjustSize();
}
} // namespace advss } // namespace advss

View File

@@ -1,18 +1,15 @@
#pragma once #pragma once
#include "list-editor.hpp"
#include "macro-ref.hpp" #include "macro-ref.hpp"
#include <QListWidget>
#include <QPushButton>
#include <QHBoxLayout>
namespace advss { namespace advss {
class ADVSS_EXPORT MacroList : public QWidget { class ADVSS_EXPORT MacroList final : public ListEditor {
Q_OBJECT Q_OBJECT
public: public:
MacroList(QWidget *parent, bool allowDuplicates, bool reorder); MacroList(QWidget *parent, bool allowDuplicates, bool reorder);
void SetContent(const std::vector<MacroRef> &); void SetContent(const std::vector<MacroRef> &);
void AddControl(QWidget *); QAction *AddControl(QWidget *, bool addSeperator = true);
int CurrentRow(); int CurrentRow();
private slots: private slots:
@@ -22,7 +19,7 @@ private slots:
void Remove(); void Remove();
void Up(); void Up();
void Down(); void Down();
void MacroItemClicked(QListWidgetItem *); void Clicked(QListWidgetItem *);
signals: signals:
void Added(const std::string &); void Added(const std::string &);
@@ -33,16 +30,8 @@ signals:
private: private:
int FindEntry(const std::string &macro); 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 _allowDuplicates;
const bool _reorder;
}; };
} // namespace advss } // namespace advss

View File

@@ -0,0 +1,447 @@
#include "macro-script-handler.hpp"
#include "macro-action-script.hpp"
#include "macro-condition-script.hpp"
#include "plugin-state-helpers.hpp"
#include "log-helper.hpp"
#include "variable.hpp"
#include <obs-properties.h>
namespace advss {
std::mutex ScriptHandler::_mutex = {};
std::unordered_map<std::string, ScriptSegmentType> ScriptHandler::_actions = {};
std::unordered_map<std::string, ScriptSegmentType> ScriptHandler::_conditions =
{};
/* Procedure handler helpers */
#define RETURN_STATUS(status) \
{ \
calldata_set_bool(data, "success", status); \
return; \
}
#define RETURN_SUCCESS() RETURN_STATUS(true);
#define RETURN_FAILURE() RETURN_STATUS(false);
static constexpr std::string_view nameParam = "name";
static constexpr std::string_view defaultSettingsParam = "default_settings";
static constexpr std::string_view propertiesSignalParam =
"properties_signal_name";
static constexpr std::string_view triggerSignalParam = "trigger_signal_name";
static std::string getRegisterScriptSegmentDeclString(const char *funcName)
{
return std::string("bool ") + funcName + "(in string " +
nameParam.data() + ", in ptr " + defaultSettingsParam.data() +
", out string " + propertiesSignalParam.data() +
", out string " + triggerSignalParam.data() + ")";
}
static std::string getDeregisterScriptSegmentDeclString(const char *funcName)
{
return std::string("bool ") + funcName + "(in string " +
nameParam.data() + ")";
}
/* Script actions */
static constexpr std::string_view registerActionFuncName =
"advss_register_script_action";
static constexpr std::string_view deregisterActionFuncName =
"advss_deregister_script_action";
static const std::string registerScriptActionDeclString =
getRegisterScriptSegmentDeclString(registerActionFuncName.data());
static const std::string deregisterScriptActionDeclString =
getDeregisterScriptSegmentDeclString(deregisterActionFuncName.data());
/* Script conditions */
static constexpr std::string_view registerConditionFuncName =
"advss_register_script_condition";
static constexpr std::string_view deregisterConditionFuncName =
"advss_deregister_script_condition";
static const std::string registerScriptConditionDeclString =
getRegisterScriptSegmentDeclString(registerConditionFuncName.data());
static const std::string deregisterScriptConditionDeclString =
getDeregisterScriptSegmentDeclString(
deregisterConditionFuncName.data());
/* Script variables */
static constexpr std::string_view valueParam = "value";
static constexpr std::string_view getVariableValueFuncName =
"advss_get_variable_value";
static constexpr std::string_view setVariableValueFuncName =
"advss_set_variable_value";
static const std::string getVariableValueDeclString =
std::string("bool ") + getVariableValueFuncName.data() + "(in string " +
nameParam.data() + ", out string " + valueParam.data() + ")";
static const std::string setVariableValueDeclString =
std::string("bool ") + setVariableValueFuncName.data() + "(in string " +
nameParam.data() + ", in string " + valueParam.data() + ")";
static bool setup();
static bool setupDone = setup();
static bool setup()
{
proc_handler_t *ph = obs_get_proc_handler();
assert(ph != NULL);
proc_handler_add(ph, registerScriptActionDeclString.c_str(),
&ScriptHandler::RegisterScriptAction, nullptr);
proc_handler_add(ph, deregisterScriptActionDeclString.c_str(),
&ScriptHandler::DeregisterScriptAction, nullptr);
proc_handler_add(ph, registerScriptConditionDeclString.c_str(),
&ScriptHandler::RegisterScriptCondition, nullptr);
proc_handler_add(ph, deregisterScriptConditionDeclString.c_str(),
&ScriptHandler::DeregisterScriptCondition, nullptr);
proc_handler_add(ph, getVariableValueDeclString.c_str(),
&ScriptHandler::GetVariableValue, nullptr);
proc_handler_add(ph, setVariableValueDeclString.c_str(),
&ScriptHandler::SetVariableValue, nullptr);
return true;
}
static void replaceWhitespace(std::string &string)
{
std::transform(string.begin(), string.end(), string.begin(), [](char c) {
return std::isspace(static_cast<unsigned char>(c)) ? '_' : c;
});
}
static std::string nameToScriptID(const std::string &name)
{
return std::string("script_") + name;
}
static std::string getTriggerSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceWhitespace(signal);
signal += "_run";
signal += isAction ? "_action" : "_condition";
return signal;
}
static std::string getCompletionSignal(const std::string &name,
const bool isAction)
{
auto signal = getTriggerSignal(name, isAction);
signal += "_complete";
return signal;
}
static std::string getPropertiesSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceWhitespace(signal);
signal += isAction ? "_action" : "_condition";
signal += "_get_properties";
return signal;
}
void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
{
const char *actionName;
if (!calldata_get_string(data, nameParam.data(), &actionName) ||
strlen(actionName) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerScriptActionDeclString.data(), nameParam.data());
RETURN_FAILURE();
}
obs_data_t *defaultSettingsPtr = nullptr;
if (!calldata_get_ptr(data, defaultSettingsParam.data(),
&defaultSettingsPtr)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerActionFuncName.data(),
defaultSettingsParam.data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
OBSData defaultSettings(defaultSettingsPtr);
obs_data_release(defaultSettingsPtr);
if (_actions.count(actionName) > 0) {
blog(LOG_WARNING, "[%s] failed! Action \"%s\" already exists!",
registerActionFuncName.data(), actionName);
RETURN_FAILURE();
}
const std::string id = nameToScriptID(actionName);
auto triggerSignalName = getTriggerSignal(actionName, true);
auto completionSignalName = getCompletionSignal(actionName, true);
auto propertiesSignalName = getPropertiesSignal(actionName, true);
const auto createScriptAction =
[id, defaultSettings, propertiesSignalName, triggerSignalName,
completionSignalName](
Macro *m) -> std::shared_ptr<MacroAction> {
return std::make_shared<MacroActionScript>(
m, id, defaultSettings, propertiesSignalName,
triggerSignalName, completionSignalName);
};
if (!MacroActionFactory::Register(id, {createScriptAction,
MacroSegmentScriptEdit::Create,
actionName})) {
blog(LOG_WARNING,
"[%s] failed! Action id \"%s\" already exists!",
registerActionFuncName.data(), id.c_str());
RETURN_FAILURE();
}
blog(LOG_INFO, "[%s] successful for \"%s\"",
registerActionFuncName.data(), actionName);
calldata_set_string(data, triggerSignalParam.data(),
triggerSignalName.c_str());
calldata_set_string(data, propertiesSignalParam.data(),
propertiesSignalName.c_str());
_actions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
triggerSignalName,
completionSignalName));
RETURN_SUCCESS();
}
void ScriptHandler::DeregisterScriptAction(void *, calldata_t *data)
{
const char *actionName;
if (!calldata_get_string(data, nameParam.data(), &actionName) ||
strlen(actionName) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
deregisterActionFuncName.data(), nameParam.data());
RETURN_FAILURE();
}
const std::string id = nameToScriptID(actionName);
std::lock_guard<std::mutex> lock(_mutex);
if (_actions.count(id) == 0) {
blog(LOG_WARNING,
"[%s] failed! Action \"%s\" was never registered!",
deregisterActionFuncName.data(), id.c_str());
RETURN_FAILURE();
}
if (!MacroActionFactory::Deregister(id)) {
blog(LOG_WARNING,
"[%s] failed! Action id \"%s\" does not exist!",
deregisterActionFuncName.data(), id.c_str());
RETURN_FAILURE();
}
auto it = _actions.find(id);
if (it != _actions.end()) {
_actions.erase(it);
}
RETURN_SUCCESS();
}
void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
{
const char *conditionName;
if (!calldata_get_string(data, nameParam.data(), &conditionName) ||
strlen(conditionName) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerScriptConditionDeclString.data(),
nameParam.data());
RETURN_FAILURE();
}
obs_data_t *defaultSettingsPtr = nullptr;
if (!calldata_get_ptr(data, defaultSettingsParam.data(),
&defaultSettingsPtr)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerScriptConditionDeclString.data(),
defaultSettingsParam.data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
OBSData defaultSettings(defaultSettingsPtr);
obs_data_release(defaultSettingsPtr);
if (_conditions.count(conditionName) > 0) {
blog(LOG_WARNING,
"[%s] failed! Condition \"%s\" already exists!",
registerConditionFuncName.data(), conditionName);
RETURN_FAILURE();
}
const std::string id = nameToScriptID(conditionName);
auto triggerSignalName = getTriggerSignal(conditionName, false);
auto completionSignalName = getCompletionSignal(conditionName, false);
auto propertiesSignalName = getPropertiesSignal(conditionName, false);
const auto createScriptCondition =
[id, defaultSettings, propertiesSignalName, triggerSignalName,
completionSignalName](
Macro *m) -> std::shared_ptr<MacroCondition> {
return std::make_shared<MacroConditionScript>(
m, id, defaultSettings, propertiesSignalName,
triggerSignalName, completionSignalName);
};
if (!MacroConditionFactory::Register(
id, {createScriptCondition, MacroSegmentScriptEdit::Create,
conditionName})) {
blog(LOG_WARNING,
"[%s] failed! Condition id \"%s\" already exists!",
registerConditionFuncName.data(), id.c_str());
RETURN_FAILURE();
}
blog(LOG_INFO, "[%s] successful for \"%s\"",
registerConditionFuncName.data(), conditionName);
calldata_set_string(data, triggerSignalParam.data(),
triggerSignalName.c_str());
calldata_set_string(data, propertiesSignalParam.data(),
propertiesSignalName.c_str());
_conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
triggerSignalName,
completionSignalName));
RETURN_SUCCESS();
}
void ScriptHandler::DeregisterScriptCondition(void *, calldata_t *data)
{
const char *conditionName;
if (!calldata_get_string(data, nameParam.data(), &conditionName) ||
strlen(conditionName) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
deregisterConditionFuncName.data(), nameParam.data());
RETURN_FAILURE();
}
const std::string id = nameToScriptID(conditionName);
std::lock_guard<std::mutex> lock(_mutex);
if (_conditions.count(id) == 0) {
blog(LOG_WARNING,
"[%s] failed! Condition \"%s\" was never registered!",
deregisterConditionFuncName.data(), id.c_str());
RETURN_FAILURE();
}
if (!MacroConditionFactory::Deregister(id)) {
blog(LOG_WARNING,
"[%s] failed! Condition id \"%s\" does not exist!",
deregisterConditionFuncName.data(), id.c_str());
RETURN_FAILURE();
}
auto it = _conditions.find(id);
if (it != _conditions.end()) {
_conditions.erase(it);
}
RETURN_SUCCESS();
}
void ScriptHandler::GetVariableValue(void *, calldata_t *data)
{
const char *variableName;
if (!calldata_get_string(data, nameParam.data(), &variableName) ||
strlen(variableName) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
getVariableValueFuncName.data(), nameParam.data());
RETURN_FAILURE();
}
auto weakVariable = GetWeakVariableByName(variableName);
auto variable = weakVariable.lock();
if (!variable) {
blog(LOG_WARNING,
"[%s] failed! \"%s\" variable does not exist!",
getVariableValueFuncName.data(), nameParam.data());
RETURN_FAILURE();
}
calldata_set_string(data, valueParam.data(), variable->Value().c_str());
RETURN_SUCCESS();
}
void ScriptHandler::SetVariableValue(void *, calldata_t *data)
{
const char *variableName;
if (!calldata_get_string(data, nameParam.data(), &variableName) ||
strlen(variableName) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
getVariableValueFuncName.data(), nameParam.data());
RETURN_FAILURE();
}
const char *variableValue;
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
strlen(variableValue) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
getVariableValueFuncName.data(), valueParam.data());
RETURN_FAILURE();
}
auto weakVariable = GetWeakVariableByName(variableName);
auto variable = weakVariable.lock();
if (!variable) {
blog(LOG_WARNING,
"[%s] failed! \"%s\" variable does not exist!",
getVariableValueFuncName.data(), nameParam.data());
RETURN_FAILURE();
}
variable->SetValue(variableValue);
RETURN_SUCCESS();
}
bool ScriptHandler::ActionIdIsValid(const std::string &id)
{
std::lock_guard<std::mutex> lock(_mutex);
return _actions.count(id) > 0;
}
bool ScriptHandler::ConditionIdIsValid(const std::string &id)
{
std::lock_guard<std::mutex> lock(_mutex);
return _conditions.count(id) > 0;
}
static std::string signalNameToTriggerSignalDecl(const std::string &name)
{
return std::string("void ") + name + "()";
}
static std::string signalNameToPropertiesSignalDecl(const std::string &name)
{
return std::string("void ") + name + "(in ptr " +
GetPropertiesSignalParamName().data() + ")";
}
static std::string signalNameToCompletionSignalDecl(const std::string &name)
{
return std::string("bool ") + name + "(in int " +
GeCompletionIdParamName().data() + ")";
}
ScriptSegmentType::ScriptSegmentType(const std::string &id,
const std::string &propertiesSignal,
const std::string &triggerSignal,
const std::string &completionSignal)
: _id(id)
{
signal_handler_add(
obs_get_signal_handler(),
signalNameToPropertiesSignalDecl(propertiesSignal).c_str());
signal_handler_add(
obs_get_signal_handler(),
signalNameToTriggerSignalDecl(triggerSignal).c_str());
signal_handler_add(
obs_get_signal_handler(),
signalNameToCompletionSignalDecl(completionSignal).c_str());
}
} // namespace advss

View File

@@ -0,0 +1,59 @@
#pragma once
#include <obs.hpp>
#include <mutex>
#include <string>
#include <unordered_map>
namespace advss {
class ScriptSegmentType {
public:
ScriptSegmentType() = delete;
ScriptSegmentType(const std::string &id,
const std::string &propertiesSignal,
const std::string &triggerSignal,
const std::string &completionSignal);
private:
std::string _id;
};
class ScriptHandler {
public:
ScriptHandler() = delete;
static void RegisterScriptAction(void *ctx, calldata_t *data);
static void DeregisterScriptAction(void *ctx, calldata_t *data);
static void RegisterScriptCondition(void *ctx, calldata_t *data);
static void DeregisterScriptCondition(void *ctx, calldata_t *data);
static void GetVariableValue(void *ctx, calldata_t *data);
static void SetVariableValue(void *ctx, calldata_t *data);
static bool ActionIdIsValid(const std::string &id);
static bool ConditionIdIsValid(const std::string &id);
private:
static std::mutex _mutex;
static std::unordered_map<std::string, ScriptSegmentType> _actions;
static std::unordered_map<std::string, ScriptSegmentType> _conditions;
};
static constexpr std::string_view GetPropertiesSignalParamName()
{
return "properties";
}
static constexpr std::string_view GetActionCompletionSignalParamName()
{
return "completion_signal_name";
}
static constexpr std::string_view GeCompletionIdParamName()
{
return "completion_id";
}
static constexpr std::string_view GeResultSignalParamName()
{
return "result";
}
} // namespace advss

View File

@@ -1,5 +1,7 @@
#include "macro-segment-copy-paste.hpp"
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "ui-helpers.hpp"
#include <QShortcut> #include <QShortcut>
@@ -61,23 +63,35 @@ void AdvSceneSwitcher::PasteMacroSegment()
const auto condition = std::static_pointer_cast<MacroCondition>( const auto condition = std::static_pointer_cast<MacroCondition>(
copyInfo.segment); copyInfo.segment);
auto logic = condition->GetLogicType(); auto logic = condition->GetLogicType();
if (logic > LogicType::ROOT_LAST && if (logic > Logic::Type::ROOT_LAST &&
macro->Conditions().empty()) { macro->Conditions().empty()) {
logic = LogicType::ROOT_NONE; logic = Logic::Type::ROOT_NONE;
} }
if (logic < LogicType::ROOT_LAST && if (logic < Logic::Type::ROOT_LAST &&
!macro->Conditions().empty()) { !macro->Conditions().empty()) {
logic = LogicType::OR; logic = Logic::Type::OR;
} }
AddMacroCondition(macro.get(), macro->Conditions().size(), AddMacroCondition(macro.get(), macro->Conditions().size(),
copyInfo.segment->GetId(), data.Get(), logic); copyInfo.segment->GetId(), data.Get(), logic);
break; break;
} }
case MacroSegmentCopyInfo::Type::ACTION: case MacroSegmentCopyInfo::Type::ACTION:
if (IsCursorInWidgetArea(ui->macroElseActions)) {
AddMacroElseAction(macro.get(),
macro->ElseActions().size(),
copyInfo.segment->GetId(),
data.Get());
break;
}
AddMacroAction(macro.get(), macro->Actions().size(), AddMacroAction(macro.get(), macro->Actions().size(),
copyInfo.segment->GetId(), data.Get()); copyInfo.segment->GetId(), data.Get());
break; break;
case MacroSegmentCopyInfo::Type::ELSE: case MacroSegmentCopyInfo::Type::ELSE:
if (IsCursorInWidgetArea(ui->macroActions)) {
AddMacroAction(macro.get(), macro->Actions().size(),
copyInfo.segment->GetId(), data.Get());
break;
}
AddMacroElseAction(macro.get(), macro->ElseActions().size(), AddMacroElseAction(macro.get(), macro->ElseActions().size(),
copyInfo.segment->GetId(), data.Get()); copyInfo.segment->GetId(), data.Get());
break; break;
@@ -91,6 +105,12 @@ bool MacroSegmentIsInClipboard()
return copyInfo.type != MacroSegmentCopyInfo::Type::NONE; return copyInfo.type != MacroSegmentCopyInfo::Type::NONE;
} }
bool MacroActionIsInClipboard()
{
return copyInfo.type == MacroSegmentCopyInfo::Type::ACTION ||
copyInfo.type == MacroSegmentCopyInfo::Type::ELSE;
}
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window) void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window)
{ {
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window); auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window);

View File

@@ -3,6 +3,7 @@ namespace advss {
class AdvSceneSwitcher; class AdvSceneSwitcher;
bool MacroSegmentIsInClipboard(); bool MacroSegmentIsInClipboard();
bool MacroActionIsInClipboard();
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window); void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
} // namespace advss } // namespace advss

View File

@@ -130,7 +130,7 @@ void MacroSegmentList::Highlight(int idx, QColor color)
if (!widget) { if (!widget) {
return; return;
} }
PulseWidget(widget, color, QColor(0, 0, 0, 0), true); HighlightWidget(widget, color, QColor(0, 0, 0, 0), true);
} }
void MacroSegmentList::SetCollapsed(bool collapse) void MacroSegmentList::SetCollapsed(bool collapse)

View File

@@ -0,0 +1,219 @@
#include "macro-segment-script.hpp"
#include "layout-helpers.hpp"
#include "macro-action.hpp"
#include "macro-condition.hpp"
#include "macro-helpers.hpp"
#include "macro-script-handler.hpp"
#include "obs-module-helper.hpp"
#include "properties-view.hpp"
#include "sync-helpers.hpp"
namespace advss {
static std::atomic_int completionIdCounter = 0;
MacroSegmentScript::MacroSegmentScript(obs_data_t *defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &completionSignal)
: _settings(obs_data_get_defaults(defaultSettings)),
_propertiesSignal(propertiesSignalName),
_triggerSignal(triggerSignal),
_completionSignal(completionSignal)
{
signal_handler_connect(obs_get_signal_handler(),
completionSignal.c_str(),
&MacroSegmentScript::CompletionSignalReceived,
this);
}
MacroSegmentScript::MacroSegmentScript(const MacroSegmentScript &other)
: _settings(obs_data_create()),
_propertiesSignal(other._propertiesSignal),
_triggerSignal(other._triggerSignal),
_completionSignal(other._completionSignal)
{
signal_handler_connect(obs_get_signal_handler(),
_completionSignal.c_str(),
&MacroSegmentScript::CompletionSignalReceived,
this);
obs_data_apply(_settings.Get(), other._settings.Get());
}
bool MacroSegmentScript::Save(obs_data_t *obj) const
{
obs_data_set_obj(obj, "settings", _settings.Get());
_timeout.Save(obj);
return true;
}
bool MacroSegmentScript::Load(obs_data_t *obj)
{
OBSDataAutoRelease settings = obs_data_get_obj(obj, "settings");
obs_data_apply(_settings.Get(), settings);
_timeout.Load(obj);
return true;
}
obs_properties_t *MacroSegmentScript::GetProperties() const
{
auto data = calldata_create();
signal_handler_signal(obs_get_signal_handler(),
_propertiesSignal.c_str(), data);
obs_properties_t *properties = nullptr;
if (!calldata_get_ptr(data, GetPropertiesSignalParamName().data(),
&properties)) {
calldata_destroy(data);
return nullptr;
}
calldata_destroy(data);
return properties;
}
void MacroSegmentScript::UpdateSettings(obs_data_t *newSettings) const
{
obs_data_clear(_settings.Get());
obs_data_apply(_settings.Get(), newSettings);
}
bool MacroSegmentScript::SendTriggerSignal()
{
_completionId = ++completionIdCounter;
_triggerIsComplete = false;
_triggerResult = false;
auto data = calldata_create();
calldata_set_string(data, GetActionCompletionSignalParamName().data(),
_completionSignal.c_str());
calldata_set_int(data, GeCompletionIdParamName().data(), _completionId);
calldata_set_string(data, "settings", obs_data_get_json(GetSettings()));
signal_handler_signal(obs_get_signal_handler(), _triggerSignal.c_str(),
data);
calldata_destroy(data);
SetMacroAbortWait(false);
WaitForCompletion();
return _triggerResult;
}
void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
{
auto segment = static_cast<MacroSegmentScript *>(param);
long long int id;
if (!calldata_get_int(data, GeCompletionIdParamName().data(), &id)) {
blog(LOG_WARNING,
"received completion signal without \"%s\" parameter",
GeCompletionIdParamName().data());
return;
}
bool result;
if (!calldata_get_bool(data, GeResultSignalParamName().data(),
&result)) {
blog(LOG_WARNING,
"received completion signal without \"%s\" parameter",
GeResultSignalParamName().data());
return;
}
if (id != segment->_completionId) {
return;
}
segment->_triggerIsComplete = true;
segment->_triggerResult = result;
}
obs_properties_t *MacroSegmentScriptEdit::GetProperties(void *obj)
{
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
if (!segmentEdit) {
return nullptr;
}
return segmentEdit->_entryData->GetProperties();
}
void MacroSegmentScriptEdit::UpdateSettings(void *obj, obs_data_t *settings)
{
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
if (!segmentEdit || segmentEdit->_loading || !segmentEdit->_entryData) {
return;
}
auto lock = LockContext();
segmentEdit->_entryData->UpdateSettings(settings);
}
MacroSegmentScriptEdit::MacroSegmentScriptEdit(
QWidget *parent, std::shared_ptr<MacroSegmentScript> entryData)
: QWidget(parent),
_timeout(new DurationSelection(this))
{
QWidget::connect(_timeout, &DurationSelection::DurationChanged, this,
&MacroSegmentScriptEdit::TimeoutChanged);
auto timeoutLayout = new QHBoxLayout();
PlaceWidgets(obs_module_text("AdvSceneSwitcher.script.timeout"),
timeoutLayout, {{"{{timeout}}", _timeout}});
auto layout = new QVBoxLayout();
auto properties = entryData->GetProperties();
if (!!properties) {
obs_properties_destroy(properties);
// We need a separate OBSData object here as we can't risk
// entryData->_settings being modified while it is currently used
OBSDataAutoRelease data = obs_data_create();
obs_data_apply(data, entryData->GetSettings());
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(30, 0, 0)
auto propertiesView =
new OBSPropertiesView(data.Get(), this, GetProperties,
nullptr, UpdateSettings);
layout->addWidget(propertiesView);
connect(propertiesView, &OBSPropertiesView::PropertiesResized,
this, [this]() {
adjustSize();
updateGeometry();
});
#else
layout->addWidget(new QLabel(
"Displaying script properties not supported when compiled for OBS 29!"));
#endif
}
layout->addLayout(timeoutLayout);
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
adjustSize();
updateGeometry();
}
void MacroSegmentScriptEdit::UpdateEntryData()
{
_timeout->SetDuration(_entryData->_timeout);
}
QWidget *MacroSegmentScriptEdit::Create(QWidget *parent,
std::shared_ptr<MacroAction> segment)
{
return new MacroSegmentScriptEdit(
parent, std::dynamic_pointer_cast<MacroSegmentScript>(segment));
}
QWidget *MacroSegmentScriptEdit::Create(QWidget *parent,
std::shared_ptr<MacroCondition> segment)
{
return new MacroSegmentScriptEdit(
parent, std::dynamic_pointer_cast<MacroSegmentScript>(segment));
}
void MacroSegmentScriptEdit::TimeoutChanged(const Duration &timeout)
{
GUARD_LOADING_AND_LOCK();
_entryData->_timeout = timeout;
}
} // namespace advss

View File

@@ -0,0 +1,78 @@
#pragma once
#include "duration-control.hpp"
#include "macro-script-handler.hpp"
#include <atomic>
#include <obs-data.h>
namespace advss {
class Macro;
class MacroAction;
class MacroCondition;
class MacroSegmentScript {
public:
MacroSegmentScript(obs_data_t *defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &completionSignal);
MacroSegmentScript(const advss::MacroSegmentScript &);
protected:
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
obs_properties_t *GetProperties() const;
OBSData GetSettings() const { return _settings.Get(); }
void UpdateSettings(obs_data_t *newSettings) const;
bool SendTriggerSignal();
double GetTimeoutSeconds() const { return _timeout.Seconds(); };
bool TriggerIsCompleted() const { return _triggerIsComplete; }
private:
virtual void WaitForCompletion() const = 0;
static void CompletionSignalReceived(void *param, calldata_t *data);
OBSDataAutoRelease _settings;
std::string _propertiesSignal = "";
std::string _triggerSignal = "";
std::string _completionSignal = "";
std::atomic_bool _triggerIsComplete = {false};
bool _triggerResult = false;
int64_t _completionId = 0;
Duration _timeout = Duration(10.0);
friend class MacroSegmentScriptEdit;
};
class MacroSegmentScriptEdit : public QWidget {
Q_OBJECT
public:
MacroSegmentScriptEdit(
QWidget *parent,
std::shared_ptr<MacroSegmentScript> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> segment);
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> segment);
private slots:
void TimeoutChanged(const Duration &);
private:
static obs_properties_t *GetProperties(void *obj);
static void UpdateSettings(void *obj, obs_data_t *settings);
DurationSelection *_timeout;
std::shared_ptr<MacroSegmentScript> _entryData;
bool _loading = true;
};
} // namespace advss

View File

@@ -70,7 +70,8 @@ void MacroSegmentSelection::MacroSegmentOrderChanged()
SetupDescription(); SetupDescription();
} }
QString GetMacroSegmentDescription(Macro *macro, int idx, bool isCondition) static QString GetMacroSegmentDescription(Macro *macro, int idx,
bool isCondition)
{ {
if (!macro) { if (!macro) {
return ""; return "";
@@ -175,7 +176,7 @@ void MacroSegmentSelection::MarkSelectedSegment()
index - 1, QColor(Qt::lightGray)); 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 } // namespace advss

View File

@@ -19,11 +19,13 @@ MacroSelection::MacroSelection(QWidget *parent)
addItem(QString::fromStdString(m->Name())); addItem(QString::fromStdString(m->Name()));
} }
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this, QWidget::connect(GetSettingsWindow(),
SIGNAL(MacroAdded(const QString &)), this,
SLOT(MacroAdd(const QString &))); SLOT(MacroAdd(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this, QWidget::connect(GetSettingsWindow(),
SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &))); SLOT(MacroRemove(const QString &)));
QWidget::connect(parent, QWidget::connect(GetSettingsWindow(),
SIGNAL(MacroRenamed(const QString &, const QString &)), SIGNAL(MacroRenamed(const QString &, const QString &)),
this, this,
SLOT(MacroRename(const QString &, const QString &))); SLOT(MacroRename(const QString &, const QString &)));
@@ -66,7 +68,7 @@ void MacroSelection::HideSelectedMacro()
void MacroSelection::ShowAllMacros() void MacroSelection::ShowAllMacros()
{ {
auto v = qobject_cast<QListView *>(view()); auto v = qobject_cast<QListView *>(view());
for (int i = count(); i > 0; i--) { for (int i = count() - 1; i >= 0; i--) {
v->setRowHidden(i, false); v->setRowHidden(i, false);
} }
} }
@@ -124,7 +126,7 @@ MacroSelectionDialog::MacroSelectionDialog(QWidget *parent)
setLayout(layout); setLayout(layout);
} }
bool MacroSelectionDialog::AskForMacro(QWidget *parent, std::string &macroName) bool MacroSelectionDialog::AskForMacro(std::string &macroName)
{ {
MacroSelectionDialog dialog(GetSettingsWindow()); MacroSelectionDialog dialog(GetSettingsWindow());
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle")); dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));

View File

@@ -28,7 +28,7 @@ class MacroSelectionDialog : public QDialog {
public: public:
MacroSelectionDialog(QWidget *parent); MacroSelectionDialog(QWidget *parent);
static bool AskForMacro(QWidget *parent, std::string &macroName); static bool AskForMacro(std::string &macroName);
private: private:
MacroSelection *_macroSelection; MacroSelection *_macroSelection;

View File

@@ -1,4 +1,4 @@
#include "macro-properties.hpp" #include "macro-settings.hpp"
#include "layout-helpers.hpp" #include "layout-helpers.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include "macro.hpp" #include "macro.hpp"
@@ -10,9 +10,9 @@
namespace advss { 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(); auto data = obs_data_create();
obs_data_set_bool(data, "highlightExecuted", _highlightExecuted); 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, "highlightActions", _highlightActions);
obs_data_set_bool(data, "newMacroRegisterHotkey", obs_data_set_bool(data, "newMacroRegisterHotkey",
_newMacroRegisterHotkeys); _newMacroRegisterHotkeys);
obs_data_set_obj(obj, "macroProperties", data); obs_data_set_obj(obj, "macroSettings", data);
obs_data_release(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, "highlightExecuted", true);
obs_data_set_default_bool(data, "highlightConditions", true); obs_data_set_default_bool(data, "highlightConditions", true);
obs_data_set_default_bool(data, "highlightActions", true); obs_data_set_default_bool(data, "highlightActions", true);
@@ -38,9 +41,9 @@ void MacroProperties::Load(obs_data_t *obj)
obs_data_release(data); obs_data_release(data);
} }
MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent, MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
const MacroProperties &prop, const GlobalMacroSettings &settings,
Macro *macro) Macro *macro)
: QDialog(parent), : QDialog(parent),
_executed(new QCheckBox(obs_module_text( _executed(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightExecutedMacros"))), "AdvSceneSwitcher.macroTab.highlightExecutedMacros"))),
@@ -56,6 +59,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))), "AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
_currentStopActionsIfNotDone(new QCheckBox(obs_module_text( _currentStopActionsIfNotDone(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone"))), "AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone"))),
_currentInputs(new MacroInputSelection()),
_currentMacroRegisterDock(new QCheckBox(obs_module_text( _currentMacroRegisterDock(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentRegisterDock"))), "AdvSceneSwitcher.macroTab.currentRegisterDock"))),
_currentMacroDockAddRunButton(new QCheckBox(obs_module_text( _currentMacroDockAddRunButton(new QCheckBox(obs_module_text(
@@ -101,6 +105,16 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
generalLayout->addWidget(_currentStopActionsIfNotDone); generalLayout->addWidget(_currentStopActionsIfNotDone);
generalOptions->setLayout(generalLayout); 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; int row = 0;
_dockLayout->addWidget(_currentMacroRegisterDock, row, 1, 1, 2); _dockLayout->addWidget(_currentMacroRegisterDock, row, 1, 1, 2);
row++; row++;
@@ -157,13 +171,13 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(_currentMacroRegisterDock, &QCheckBox::stateChanged, this, connect(_currentMacroRegisterDock, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::DockEnableChanged); &MacroSettingsDialog::DockEnableChanged);
connect(_currentMacroDockAddRunButton, &QCheckBox::stateChanged, this, connect(_currentMacroDockAddRunButton, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::RunButtonEnableChanged); &MacroSettingsDialog::RunButtonEnableChanged);
connect(_currentMacroDockAddPauseButton, &QCheckBox::stateChanged, this, connect(_currentMacroDockAddPauseButton, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::PauseButtonEnableChanged); &MacroSettingsDialog::PauseButtonEnableChanged);
connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this, connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::StatusLabelEnableChanged); &MacroSettingsDialog::StatusLabelEnableChanged);
auto scrollArea = new QScrollArea(this); auto scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true); scrollArea->setWidgetResizable(true);
@@ -174,6 +188,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
layout->addWidget(highlightOptions); layout->addWidget(highlightOptions);
layout->addWidget(hotkeyOptions); layout->addWidget(hotkeyOptions);
layout->addWidget(generalOptions); layout->addWidget(generalOptions);
layout->addWidget(inputOptions);
layout->addWidget(_dockOptions); layout->addWidget(_dockOptions);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
scrollArea->setWidget(contentWidget); scrollArea->setWidget(contentWidget);
@@ -183,14 +198,15 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
dialogLayout->addWidget(buttonbox); dialogLayout->addWidget(buttonbox);
setLayout(dialogLayout); setLayout(dialogLayout);
_executed->setChecked(prop._highlightExecuted); _executed->setChecked(settings._highlightExecuted);
_conditions->setChecked(prop._highlightConditions); _conditions->setChecked(settings._highlightConditions);
_actions->setChecked(prop._highlightActions); _actions->setChecked(settings._highlightActions);
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys); _newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
if (!macro || macro->IsGroup()) { if (!macro || macro->IsGroup()) {
hotkeyOptions->hide(); hotkeyOptions->hide();
generalOptions->hide(); generalOptions->hide();
inputOptions->hide();
_dockOptions->hide(); _dockOptions->hide();
return; return;
} }
@@ -198,6 +214,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled()); _currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart()); _currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
_currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone()); _currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone());
_currentInputs->SetInputs(macro->GetInputVariables());
const bool dockEnabled = macro->DockEnabled(); const bool dockEnabled = macro->DockEnabled();
_currentMacroRegisterDock->setChecked(dockEnabled); _currentMacroRegisterDock->setChecked(dockEnabled);
_currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton()); _currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton());
@@ -245,7 +262,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
20); 20);
} }
void MacroPropertiesDialog::DockEnableChanged(int enabled) void MacroSettingsDialog::DockEnableChanged(int enabled)
{ {
_currentMacroDockAddRunButton->setVisible(enabled); _currentMacroDockAddRunButton->setVisible(enabled);
_currentMacroDockAddPauseButton->setVisible(enabled); _currentMacroDockAddPauseButton->setVisible(enabled);
@@ -270,37 +287,37 @@ void MacroPropertiesDialog::DockEnableChanged(int enabled)
Resize(); Resize();
} }
void MacroPropertiesDialog::RunButtonEnableChanged(int enabled) void MacroSettingsDialog::RunButtonEnableChanged(int enabled)
{ {
SetGridLayoutRowVisible(_dockLayout, _runButtonTextRow, enabled); SetGridLayoutRowVisible(_dockLayout, _runButtonTextRow, enabled);
Resize(); Resize();
} }
void MacroPropertiesDialog::PauseButtonEnableChanged(int enabled) void MacroSettingsDialog::PauseButtonEnableChanged(int enabled)
{ {
SetGridLayoutRowVisible(_dockLayout, _pauseButtonTextRow, enabled); SetGridLayoutRowVisible(_dockLayout, _pauseButtonTextRow, enabled);
SetGridLayoutRowVisible(_dockLayout, _unpauseButtonTextRow, enabled); SetGridLayoutRowVisible(_dockLayout, _unpauseButtonTextRow, enabled);
Resize(); Resize();
} }
void MacroPropertiesDialog::StatusLabelEnableChanged(int enabled) void MacroSettingsDialog::StatusLabelEnableChanged(int enabled)
{ {
SetGridLayoutRowVisible(_dockLayout, _conditionsTrueTextRow, enabled); SetGridLayoutRowVisible(_dockLayout, _conditionsTrueTextRow, enabled);
SetGridLayoutRowVisible(_dockLayout, _conditionsFalseTextRow, enabled); SetGridLayoutRowVisible(_dockLayout, _conditionsFalseTextRow, enabled);
Resize(); Resize();
} }
void MacroPropertiesDialog::Resize() void MacroSettingsDialog::Resize()
{ {
_dockOptions->adjustSize(); _dockOptions->adjustSize();
_dockOptions->updateGeometry(); _dockOptions->updateGeometry();
} }
bool MacroPropertiesDialog::AskForSettings(QWidget *parent, bool MacroSettingsDialog::AskForSettings(QWidget *parent,
MacroProperties &userInput, GlobalMacroSettings &userInput,
Macro *macro) Macro *macro)
{ {
MacroPropertiesDialog dialog(parent, userInput, macro); MacroSettingsDialog dialog(parent, userInput, macro);
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle")); dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
if (dialog.exec() != DialogCode::Accepted) { if (dialog.exec() != DialogCode::Accepted) {
return false; return false;
@@ -337,23 +354,23 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
dialog._conditionsTrueStatusText->text().toStdString()); dialog._conditionsTrueStatusText->text().toStdString());
macro->SetConditionsFalseStatusText( macro->SetConditionsFalseStatusText(
dialog._conditionsFalseStatusText->text().toStdString()); dialog._conditionsFalseStatusText->text().toStdString());
macro->SetInputVariables(dialog._currentInputs->GetInputs());
return true; 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 } // namespace advss

View File

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

View File

@@ -1,9 +1,10 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "action-queue.hpp" #include "action-queue.hpp"
#include "cursor-shape-changer.hpp"
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "macro-export-import-dialog.hpp" #include "macro-export-import-dialog.hpp"
#include "macro-properties.hpp" #include "macro-settings.hpp"
#include "macro-segment-copy-paste.hpp" #include "macro-segment-copy-paste.hpp"
#include "macro-tree.hpp" #include "macro-tree.hpp"
#include "macro.hpp" #include "macro.hpp"
@@ -23,7 +24,7 @@
namespace advss { namespace advss {
static QMetaObject::Connection addPulse; static QObject *addPulse = nullptr;
static QTimer onChangeHighlightTimer; static QTimer onChangeHighlightTimer;
static bool macroNameExists(const std::string &name) static bool macroNameExists(const std::string &name)
@@ -86,7 +87,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
} }
res = std::make_shared<Macro>( res = std::make_shared<Macro>(
name, GetGlobalMacroProperties()._newMacroRegisterHotkeys); name, GetGlobalMacroSettings()._newMacroRegisterHotkeys);
return true; return true;
} }
@@ -99,7 +100,10 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
} }
ui->macros->Add(newMacro); ui->macros->Add(newMacro);
ui->macroAdd->disconnect(addPulse); if (addPulse) {
addPulse->deleteLater();
addPulse = nullptr;
}
emit MacroAdded(QString::fromStdString(name)); emit MacroAdded(QString::fromStdString(name));
} }
@@ -462,7 +466,7 @@ void AdvSceneSwitcher::ImportMacros()
RunPostLoadSteps(); RunPostLoadSteps();
ui->macros->Reset(GetMacros(), ui->macros->Reset(GetMacros(),
GetGlobalMacroProperties()._highlightExecuted); GetGlobalMacroSettings()._highlightExecuted);
} }
void AdvSceneSwitcher::on_macroName_editingFinished() void AdvSceneSwitcher::on_macroName_editingFinished()
@@ -742,8 +746,8 @@ void AdvSceneSwitcher::MacroSelectionChanged()
void AdvSceneSwitcher::HighlightOnChange() void AdvSceneSwitcher::HighlightOnChange()
{ {
if (!GetGlobalMacroProperties()._highlightActions && if (!GetGlobalMacroSettings()._highlightActions &&
!GetGlobalMacroProperties()._highlightExecuted) { !GetGlobalMacroSettings()._highlightExecuted) {
return; return;
} }
@@ -753,30 +757,32 @@ void AdvSceneSwitcher::HighlightOnChange()
} }
if (macro->OnChangePreventedActionsRecently()) { if (macro->OnChangePreventedActionsRecently()) {
PulseWidget(ui->runMacroOnChange, Qt::yellow, Qt::transparent, HighlightWidget(ui->runMacroOnChange, Qt::yellow,
true); Qt::transparent, true);
} }
} }
void AdvSceneSwitcher::on_macroProperties_clicked() void AdvSceneSwitcher::on_macroSettings_clicked()
{ {
MacroProperties prop = GetGlobalMacroProperties(); GlobalMacroSettings prop = GetGlobalMacroSettings();
bool accepted = MacroPropertiesDialog::AskForSettings( bool accepted = MacroSettingsDialog::AskForSettings(
this, prop, GetSelectedMacro().get()); this, prop, GetSelectedMacro().get());
if (!accepted) { if (!accepted) {
return; return;
} }
GetGlobalMacroProperties() = prop; GetGlobalMacroSettings() = prop;
emit HighlightMacrosChanged(prop._highlightExecuted); emit HighlightMacrosChanged(prop._highlightExecuted);
} }
static void moveControlsToSplitter(QSplitter *splitter, int idx, static void moveControlsToSplitter(QSplitter *splitter, int idx,
QLayoutItem *item) QLayoutItem *item)
{ {
static int splitterHandleWidth = 38; static int splitterHandleWidth = 32;
auto handle = splitter->handle(idx); auto handle = splitter->handle(idx);
auto layout = item->layout(); auto layout = item->layout();
layout->setContentsMargins(7, 7, 7, 7); int leftMargin, rightMargin;
layout->getContentsMargins(&leftMargin, nullptr, &rightMargin, nullptr);
layout->setContentsMargins(leftMargin, 0, rightMargin, 0);
handle->setLayout(layout); handle->setLayout(layout);
splitter->setHandleWidth(splitterHandleWidth); splitter->setHandleWidth(splitterHandleWidth);
splitter->setStyleSheet("QSplitter::handle {background: transparent;}"); splitter->setStyleSheet("QSplitter::handle {background: transparent;}");
@@ -816,26 +822,57 @@ static void runSegmentHighligtChecks(AdvSceneSwitcher *ss)
return; return;
} }
const auto &properties = GetGlobalMacroProperties(); const auto &settings = GetGlobalMacroSettings();
if (properties._highlightConditions) { if (settings._highlightConditions) {
runSegmentHighligtChecksHelper(ss->ui->conditionsList); runSegmentHighligtChecksHelper(ss->ui->conditionsList);
} }
if (properties._highlightActions) { if (settings._highlightActions) {
runSegmentHighligtChecksHelper(ss->ui->actionsList); runSegmentHighligtChecksHelper(ss->ui->actionsList);
runSegmentHighligtChecksHelper(ss->ui->elseActionsList); runSegmentHighligtChecksHelper(ss->ui->elseActionsList);
} }
} }
static QToolBar *
setupToolBar(const std::initializer_list<std::initializer_list<QWidget *>>
&widgetGroups)
{
auto toolbar = new QToolBar();
toolbar->setIconSize({16, 16});
QAction *lastSeperator = nullptr;
for (const auto &widgetGroup : widgetGroups) {
for (const auto &widget : widgetGroup) {
toolbar->addWidget(widget);
}
lastSeperator = toolbar->addSeparator();
}
if (lastSeperator) {
toolbar->removeAction(lastSeperator);
}
// Prevent "extension" button from showing up
toolbar->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
return toolbar;
}
void AdvSceneSwitcher::SetupMacroTab() void AdvSceneSwitcher::SetupMacroTab()
{ {
ui->macroElseActions->installEventFilter(this); ui->macroElseActions->installEventFilter(this);
ui->macros->installEventFilter(this); ui->macros->installEventFilter(this);
if (GetMacros().size() == 0 && !switcher->disableHints) { if (GetMacros().size() == 0 && !switcher->disableHints) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green)); addPulse = HighlightWidget(ui->macroAdd, QColor(Qt::green));
} }
auto macroControls = setupToolBar({{ui->macroAdd, ui->macroRemove},
{ui->macroUp, ui->macroDown}});
ui->macroControlLayout->addWidget(macroControls);
ui->macros->Reset(GetMacros(), ui->macros->Reset(GetMacros(),
GetGlobalMacroProperties()._highlightExecuted); GetGlobalMacroSettings()._highlightExecuted);
connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this, connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this,
SLOT(MacroSelectionAboutToChange())); SLOT(MacroSelectionAboutToChange()));
connect(ui->macros, SIGNAL(MacroSelectionChanged()), this, connect(ui->macros, SIGNAL(MacroSelectionChanged()), this,
@@ -885,13 +922,7 @@ void AdvSceneSwitcher::SetupMacroTab()
SLOT(HighlightOnChange())); SLOT(HighlightOnChange()));
onChangeHighlightTimer.start(); onChangeHighlightTimer.start();
// Move condition controls into splitter handle layout // Set action and condition toolbars
moveControlsToSplitter(ui->macroActionConditionSplitter, 1,
ui->macroConditionsLayout->takeAt(1));
moveControlsToSplitter(ui->macroElseActionSplitter, 1,
ui->macroActionsLayout->takeAt(1));
// Set action and condition control icons
const std::string pathPrefix = const std::string pathPrefix =
GetDataFilePath("res/images/" + GetThemeTypeName()); GetDataFilePath("res/images/" + GetThemeTypeName());
SetButtonIcon(ui->actionTop, (pathPrefix + "DoubleUp.svg").c_str()); SetButtonIcon(ui->actionTop, (pathPrefix + "DoubleUp.svg").c_str());
@@ -906,6 +937,44 @@ void AdvSceneSwitcher::SetupMacroTab()
SetButtonIcon(ui->toggleElseActions, SetButtonIcon(ui->toggleElseActions,
(pathPrefix + "NotEqual.svg").c_str()); (pathPrefix + "NotEqual.svg").c_str());
auto conditionToolbar =
setupToolBar({{ui->conditionAdd, ui->conditionRemove},
{ui->conditionTop, ui->conditionUp,
ui->conditionDown, ui->conditionBottom}});
auto actionToolbar = setupToolBar({{ui->actionAdd, ui->actionRemove},
{ui->actionTop, ui->actionUp,
ui->actionDown, ui->actionBottom}});
auto elseActionToolbar =
setupToolBar({{ui->elseActionAdd, ui->elseActionRemove},
{ui->elseActionTop, ui->elseActionUp,
ui->elseActionDown, ui->elseActionBottom}});
ui->conditionControlsLayout->addWidget(conditionToolbar);
ui->actionControlsLayout->insertWidget(0, actionToolbar);
ui->elseActionControlsLayout->addWidget(elseActionToolbar);
// Move condition controls into splitter handle layout
moveControlsToSplitter(ui->macroActionConditionSplitter, 1,
ui->macroConditionsLayout->takeAt(1));
moveControlsToSplitter(ui->macroElseActionSplitter, 1,
ui->macroActionsLayout->takeAt(1));
// Override splitter cursor icon when hovering over controls in splitter
SetCursorOnWidgetHover(ui->conditionAdd, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionRemove,
Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionTop, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionUp, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionDown, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionBottom,
Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionAdd, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionRemove, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionTop, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionUp, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionDown, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionBottom, Qt::CursorShape::ArrowCursor);
// Reserve more space for macro edit area than for the macro list // Reserve more space for macro edit area than for the macro list
ui->macroListMacroEditSplitter->setStretchFactor(0, 1); ui->macroListMacroEditSplitter->setStretchFactor(0, 1);
ui->macroListMacroEditSplitter->setStretchFactor(1, 4); ui->macroListMacroEditSplitter->setStretchFactor(1, 4);
@@ -980,8 +1049,30 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
menu.exec(globalPos); menu.exec(globalPos);
} }
static void handleCustomLabelChange(MacroSegmentEdit *segmentEdit, static bool handleCustomLabelRename(MacroSegmentEdit *segmentEdit)
QAction *contextMenuOption) {
std::string label;
auto segment = segmentEdit->Data();
if (!segment) {
return false;
}
bool accepted = NameDialog::AskForName(
GetSettingsWindow(),
obs_module_text(
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
"", label, QString::fromStdString(segment->GetCustomLabel()));
if (!accepted) {
return false;
}
segment->SetCustomLabel(label);
segmentEdit->HeaderInfoChanged("");
return true;
}
static void handleCustomLabelEnableChange(MacroSegmentEdit *segmentEdit,
QAction *contextMenuOption)
{ {
bool enable = contextMenuOption->isChecked(); bool enable = contextMenuOption->isChecked();
auto segment = segmentEdit->Data(); auto segment = segmentEdit->Data();
@@ -992,19 +1083,66 @@ static void handleCustomLabelChange(MacroSegmentEdit *segmentEdit,
return; return;
} }
std::string label; if (!handleCustomLabelRename(segmentEdit)) {
bool accepted = NameDialog::AskForName(
GetSettingsWindow(),
obs_module_text(
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
"", label, QString::fromStdString(segment->GetCustomLabel()));
if (!accepted) {
segment->SetUseCustomLabel(false); segment->SetUseCustomLabel(false);
}
}
static void setupSegmentLabelContextMenuEntries(MacroSegmentEdit *segmentEdit,
QMenu &menu)
{
if (!segmentEdit) {
return; return;
} }
segment->SetCustomLabel(label); auto segment = segmentEdit ? segmentEdit->Data() : nullptr;
segmentEdit->HeaderInfoChanged(""); const bool customLabelIsEnabled = segment &&
segment->GetUseCustomLabel();
auto enableCustomLabel = menu.addAction(obs_module_text(
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
enableCustomLabel->setCheckable(true);
enableCustomLabel->setChecked(customLabelIsEnabled);
QWidget::connect(enableCustomLabel, &QAction::triggered,
[segmentEdit, enableCustomLabel]() {
handleCustomLabelEnableChange(
segmentEdit, enableCustomLabel);
});
if (!customLabelIsEnabled) {
return;
}
auto customLabelRename = menu.addAction(obs_module_text(
"AdvSceneSwitcher.macroTab.segment.customLabelRename"));
QWidget::connect(customLabelRename, &QAction::triggered,
[segmentEdit]() {
handleCustomLabelRename(segmentEdit);
});
}
static void setupCopyPasteContextMenuEnry(AdvSceneSwitcher *ss,
MacroSegmentEdit *segmentEdit,
QMenu &menu)
{
auto copy = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss,
[ss]() { ss->CopyMacroSegment(); });
copy->setEnabled(!!segmentEdit);
const char *pasteText = "AdvSceneSwitcher.macroTab.segment.paste";
if (MacroActionIsInClipboard()) {
if (IsCursorInWidgetArea(ss->ui->macroActions)) {
pasteText =
"AdvSceneSwitcher.macroTab.segment.pasteAction";
} else if (IsCursorInWidgetArea(ss->ui->macroElseActions)) {
pasteText =
"AdvSceneSwitcher.macroTab.segment.pasteElseAction";
}
}
auto paste = menu.addAction(obs_module_text(pasteText), ss,
[ss]() { ss->PasteMacroSegment(); });
paste->setEnabled(MacroSegmentIsInClipboard());
} }
static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos, static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
@@ -1017,43 +1155,19 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
QMenu menu; QMenu menu;
auto segmentEdit = list->WidgetAt(pos); auto segmentEdit = list->WidgetAt(pos);
auto copy = menu.addAction( setupCopyPasteContextMenuEnry(ss, segmentEdit, menu);
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(); menu.addSeparator();
setupSegmentLabelContextMenuEntries(segmentEdit, menu);
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.addSeparator();
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"), menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
ss, [ss, expand]() { expand(ss); }); ss, [ss, expand]() { expand(ss); });
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"),
ss, [ss, collapse]() { collapse(ss); }); ss, [ss, collapse]() { collapse(ss); });
menu.addSeparator(); menu.addSeparator();
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"), menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
ss, [ss, maximize]() { maximize(ss); }); ss, [ss, maximize]() { maximize(ss); });
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"), menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
ss, [ss, minimize]() { minimize(ss); }); ss, [ss, minimize]() { minimize(ss); });
menu.exec(list->mapToGlobal(pos)); menu.exec(list->mapToGlobal(pos));
} }
@@ -1098,7 +1212,7 @@ void AdvSceneSwitcher::CopyMacro()
} }
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
macro->Save(data); macro->Save(data, true);
newMacro->Load(data); newMacro->Load(data);
newMacro->PostLoad(); newMacro->PostLoad();
newMacro->SetName(name); newMacro->SetName(name);
@@ -1106,7 +1220,10 @@ void AdvSceneSwitcher::CopyMacro()
Macro::PrepareMoveToGroup(macro->Parent(), newMacro); Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
ui->macros->Add(newMacro, macro); ui->macros->Add(newMacro, macro);
ui->macroAdd->disconnect(addPulse); if (addPulse) {
addPulse->deleteLater();
addPulse = nullptr;
}
emit MacroAdded(QString::fromStdString(name)); emit MacroAdded(QString::fromStdString(name));
} }
@@ -1398,7 +1515,7 @@ static void fade(QWidget *widget, bool fadeOut)
animation->start(QPropertyAnimation::DeleteWhenStopped); animation->start(QPropertyAnimation::DeleteWhenStopped);
} }
void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut) static void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
{ {
for (const auto &widget : widgets) { for (const auto &widget : widgets) {
fade(widget, fadeOut); fade(widget, fadeOut);

View File

@@ -108,7 +108,7 @@ void MacroTreeItem::HighlightIfExecuted()
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 && if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
_macro->WasExecutedSince(_lastHighlightCheckTime)) { _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(); _lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
} }
@@ -160,7 +160,7 @@ void MacroTreeItem::Update(bool force)
_spacer = nullptr; _spacer = nullptr;
} }
if (_type == Type::Group) { if (_type == Type::Group && _expand) {
_boxLayout->removeWidget(_expand); _boxLayout->removeWidget(_expand);
_expand->deleteLater(); _expand->deleteLater();
_expand = nullptr; _expand = nullptr;

View File

@@ -14,10 +14,66 @@
#include <QAction> #include <QAction>
#include <QMainWindow> #include <QMainWindow>
#include <unordered_map> #include <unordered_map>
#include <util/platform.h>
#if LIBOBS_API_VER < MAKE_SEMANTIC_VERSION(30, 0, 0)
#include <QDockWidget>
namespace {
struct DockMapEntry {
QAction *action = nullptr;
QWidget *dock = nullptr;
};
} // namespace
static std::unordered_map<const char *, DockMapEntry> dockIds;
static std::mutex dockMutex;
static bool obs_frontend_add_dock_by_id(const char *id, const char *title,
QWidget *widget)
{
std::lock_guard<std::mutex> lock(dockMutex);
if (dockIds.count(id) > 0) {
return false;
}
widget->setObjectName(id);
auto dock = new QDockWidget();
dock->setWindowTitle(title);
dock->setWidget(widget);
dock->setFloating(true);
dock->setVisible(false);
dock->setFeatures(QDockWidget::DockWidgetClosable |
QDockWidget::DockWidgetMovable |
QDockWidget::DockWidgetFloatable);
auto action = static_cast<QAction *>(obs_frontend_add_dock(dock));
if (!action) {
return false;
}
dockIds[id] = {action, dock};
return true;
}
static void obs_frontend_remove_dock(const char *id)
{
std::lock_guard<std::mutex> lock(dockMutex);
auto it = dockIds.find(id);
if (it == dockIds.end()) {
return;
}
it->second.action->deleteLater();
it->second.dock->deleteLater();
dockIds.erase(it);
}
#endif
namespace advss { namespace advss {
static constexpr int perfLogThreshold = 300;
static std::deque<std::shared_ptr<Macro>> macros; static std::deque<std::shared_ptr<Macro>> macros;
Macro::Macro(const std::string &name, const bool addHotkey) Macro::Macro(const std::string &name, const bool addHotkey)
@@ -101,90 +157,74 @@ void Macro::PrepareMoveToGroup(std::shared_ptr<Macro> group,
} }
} }
bool Macro::CeckMatch() static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
{
using namespace std::chrono_literals;
static constexpr auto perfLogThreshold = 300ms;
const auto startTime = std::chrono::high_resolution_clock::now();
const bool conditionMatched = condition->CheckCondition();
const auto endTime = std::chrono::high_resolution_clock::now();
const auto timeSpent = endTime - startTime;
if (timeSpent >= perfLogThreshold) {
const long int ms =
std::chrono::duration_cast<std::chrono::milliseconds>(
timeSpent)
.count();
blog(LOG_WARNING,
"spent %ld ms in %s condition check of macro '%s'!", ms,
condition->GetId().c_str(),
condition->GetMacro()->Name().c_str());
}
return conditionMatched;
}
bool Macro::CeckMatch(bool ignorePause)
{ {
if (_isGroup) { if (_isGroup) {
return false; return false;
} }
_matched = false; _matched = false;
for (auto &c : _conditions) { for (auto &condition : _conditions) {
if (_paused) { if (_paused && !ignorePause) {
vblog(LOG_INFO, "Macro %s is paused", _name.c_str()); vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
return false; return false;
} }
auto startTime = std::chrono::high_resolution_clock::now(); bool conditionMatched = checkCondition(condition);
bool cond = c->CheckCondition(); conditionMatched =
auto endTime = std::chrono::high_resolution_clock::now(); condition->CheckDurationModifier(conditionMatched);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
endTime - startTime);
if (ms.count() >= perfLogThreshold) {
blog(LOG_WARNING,
"spent %ld ms in %s condition check of macro '%s'!",
(long int)ms.count(), c->GetId().c_str(),
Name().c_str());
}
c->CheckDurationModifier(cond); const auto logicType = condition->GetLogicType();
if (logicType == Logic::Type::NONE) {
switch (c->GetLogicType()) { vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
case LogicType::NONE: condition->GetId().c_str(), _name.c_str());
vblog(LOG_INFO,
"ignoring condition check 'none' for '%s'",
_name.c_str());
continue; continue;
case LogicType::AND:
_matched = _matched && cond;
if (cond) {
c->EnableHighlight();
}
break;
case LogicType::OR:
_matched = _matched || cond;
if (cond) {
c->EnableHighlight();
}
break;
case LogicType::AND_NOT:
_matched = _matched && !cond;
if (!cond) {
c->EnableHighlight();
}
break;
case LogicType::OR_NOT:
_matched = _matched || !cond;
if (!cond) {
c->EnableHighlight();
}
break;
case LogicType::ROOT_NONE:
_matched = cond;
if (cond) {
c->EnableHighlight();
}
break;
case LogicType::ROOT_NOT:
_matched = !cond;
if (!cond) {
c->EnableHighlight();
}
break;
default:
blog(LOG_WARNING,
"ignoring unknown condition check for '%s'",
_name.c_str());
break;
} }
vblog(LOG_INFO, "condition %s returned %d", c->GetId().c_str(), vblog(LOG_INFO, "condition %s returned %d",
cond); condition->GetId().c_str(), conditionMatched);
const bool isNegativeLogicType =
Logic::IsNegationType(logicType);
if ((conditionMatched && !isNegativeLogicType) ||
(!conditionMatched && isNegativeLogicType)) {
condition->EnableHighlight();
}
_matched = Logic::ApplyConditionLogic(
logicType, _matched, conditionMatched, _name.c_str());
} }
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched); vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
_conditionSateChanged = _lastMatched != _matched; _conditionSateChanged = _lastMatched != _matched;
if (!_conditionSateChanged && _performActionsOnChange) { if (!_conditionSateChanged && _performActionsOnChange) {
_onPreventedActionExecution = true; _onPreventedActionExecution = true;
} }
_lastMatched = _matched; _lastMatched = _matched;
_lastCheckTime = std::chrono::high_resolution_clock::now(); _lastCheckTime = std::chrono::high_resolution_clock::now();
return _matched; return _matched;
@@ -275,9 +315,15 @@ int64_t Macro::MsSinceLastCheck() const
void Macro::SetName(const std::string &name) void Macro::SetName(const std::string &name)
{ {
const bool nameChanged = _name == name;
_name = name; _name = name;
SetHotkeysDesc(); SetHotkeysDesc();
SetDockWidgetName();
if (nameChanged) {
_dockId = GenerateDockId();
}
EnableDock(_registerDock);
} }
void Macro::ResetTimers() void Macro::ResetTimers()
@@ -332,11 +378,6 @@ bool Macro::RunElseActions(bool ignorePause)
return RunActionsHelper(_elseActions, ignorePause); return RunActionsHelper(_elseActions, ignorePause);
} }
bool Macro::DockIsVisible() const
{
return _dock && _dockAction && _dock->isVisible();
}
bool Macro::WasPausedSince( bool Macro::WasPausedSince(
const std::chrono::high_resolution_clock::time_point &time) const const std::chrono::high_resolution_clock::time_point &time) const
{ {
@@ -348,6 +389,11 @@ void Macro::SetMatchOnChange(bool onChange)
_performActionsOnChange = onChange; _performActionsOnChange = onChange;
} }
void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone)
{
_stopActionsIfNotDone = stopActionsIfNotDone;
}
void Macro::SetPaused(bool pause) void Macro::SetPaused(bool pause)
{ {
if (_paused && !pause) { if (_paused && !pause) {
@@ -382,6 +428,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> Macro::GetTempVars(MacroSegment *filter) const
{ {
std::vector<TempVariable> res; std::vector<TempVariable> res;
@@ -552,9 +608,11 @@ std::shared_ptr<Macro> Macro::Parent() const
return _parent.lock(); return _parent.lock();
} }
bool Macro::Save(obs_data_t *obj) const bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
{ {
obs_data_set_string(obj, "name", _name.c_str()); if (!saveForCopy) {
obs_data_set_string(obj, "name", _name.c_str());
}
obs_data_set_bool(obj, "pause", _paused); obs_data_set_bool(obj, "pause", _paused);
obs_data_set_bool(obj, "parallel", _runInParallel); obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "onChange", _performActionsOnChange); obs_data_set_bool(obj, "onChange", _performActionsOnChange);
@@ -570,7 +628,7 @@ bool Macro::Save(obs_data_t *obj) const
return true; return true;
} }
SaveDockSettings(obj); SaveDockSettings(obj, saveForCopy);
SaveSplitterPos(_actionConditionSplitterPosition, obj, SaveSplitterPos(_actionConditionSplitterPosition, obj,
"macroActionConditionSplitterPosition"); "macroActionConditionSplitterPosition");
@@ -610,43 +668,11 @@ bool Macro::Save(obs_data_t *obj) const
} }
obs_data_set_array(obj, "elseActions", elseActions); obs_data_set_array(obj, "elseActions", elseActions);
return true; _inputVariables.Save(obj);
}
bool isValidLogic(LogicType t, bool root)
{
bool isRoot = isRootLogicType(t);
if (!isRoot == root) {
return false;
}
if (isRoot) {
if (t >= LogicType::ROOT_LAST) {
return false;
}
} else if (t >= LogicType::LAST) {
return false;
}
return true; return true;
} }
void setValidLogic(MacroCondition *c, bool root, std::string name)
{
if (isValidLogic(c->GetLogicType(), root)) {
return;
}
if (root) {
c->SetLogicType(LogicType::ROOT_NONE);
blog(LOG_WARNING,
"setting invalid logic selection to 'if' for macro %s",
name.c_str());
} else {
c->SetLogicType(LogicType::NONE);
blog(LOG_WARNING,
"setting invalid logic selection to 'ignore' for macro %s",
name.c_str());
}
}
bool Macro::Load(obs_data_t *obj) bool Macro::Load(obs_data_t *obj)
{ {
_name = obs_data_get_string(obj, "name"); _name = obs_data_get_string(obj, "name");
@@ -702,7 +728,7 @@ bool Macro::Load(obs_data_t *obj)
_conditions.emplace_back(newEntry); _conditions.emplace_back(newEntry);
auto c = _conditions.back().get(); auto c = _conditions.back().get();
c->Load(arrayObj); c->Load(arrayObj);
setValidLogic(c, root, _name); c->ValidateLogicSelection(root, Name().c_str());
} else { } else {
blog(LOG_WARNING, blog(LOG_WARNING,
"discarding condition entry with unknown id (%s) for macro %s", "discarding condition entry with unknown id (%s) for macro %s",
@@ -748,6 +774,8 @@ bool Macro::Load(obs_data_t *obj)
} }
UpdateElseActionIndices(); UpdateElseActionIndices();
_inputVariables.Load(obj);
return true; return true;
} }
@@ -845,14 +873,10 @@ bool Macro::PauseHotkeysEnabled() const
return _registerHotkeys; return _registerHotkeys;
} }
void Macro::SaveDockSettings(obs_data_t *obj) const void Macro::SaveDockSettings(obs_data_t *obj, bool saveForCopy) const
{ {
auto dockSettings = obs_data_create(); auto dockSettings = obs_data_create();
obs_data_set_bool(dockSettings, "register", _registerDock); obs_data_set_bool(dockSettings, "register", _registerDock);
// The object name is used to restore the position of the dock
if (_registerDock) {
SetDockWidgetName();
}
obs_data_set_bool(dockSettings, "hasRunButton", _dockHasRunButton); obs_data_set_bool(dockSettings, "hasRunButton", _dockHasRunButton);
obs_data_set_bool(dockSettings, "hasPauseButton", _dockHasPauseButton); obs_data_set_bool(dockSettings, "hasPauseButton", _dockHasPauseButton);
obs_data_set_bool(dockSettings, "hasStatusLabel", _dockHasStatusLabel); obs_data_set_bool(dockSettings, "hasStatusLabel", _dockHasStatusLabel);
@@ -865,18 +889,14 @@ void Macro::SaveDockSettings(obs_data_t *obj) const
"conditionsTrueStatusText"); "conditionsTrueStatusText");
_conditionsFalseStatusText.Save(dockSettings, _conditionsFalseStatusText.Save(dockSettings,
"conditionsFalseStatusText"); "conditionsFalseStatusText");
if (_dock) { if (saveForCopy) {
auto window = static_cast<QMainWindow *>( auto uuid = GenerateDockId();
obs_frontend_get_main_window()); obs_data_set_string(dockSettings, "dockId", uuid.c_str());
obs_data_set_bool(dockSettings, "isFloating",
_dock->isFloating()); } else {
obs_data_set_bool(dockSettings, "isVisible", DockIsVisible()); obs_data_set_string(dockSettings, "dockId", _dockId.c_str());
obs_data_set_int(dockSettings, "area",
window->dockWidgetArea(_dock));
obs_data_set_string(
dockSettings, "geometry",
_dock->saveGeometry().toBase64().constData());
} }
obs_data_set_int(dockSettings, "version", 1);
obs_data_set_obj(obj, "dockSettings", dockSettings); obs_data_set_obj(obj, "dockSettings", dockSettings);
obs_data_release(dockSettings); obs_data_release(dockSettings);
} }
@@ -893,8 +913,13 @@ void Macro::LoadDockSettings(obs_data_t *obj)
return; return;
} }
if (!obs_data_has_user_value(dockSettings, "version")) {
_dockId = std::string("ADVSS-") + _name;
} else {
_dockId = obs_data_get_string(dockSettings, "dockId");
}
const bool dockEnabled = obs_data_get_bool(dockSettings, "register"); const bool dockEnabled = obs_data_get_bool(dockSettings, "register");
_dockIsVisible = obs_data_get_bool(dockSettings, "isVisible");
// TODO: remove these default settings in a future version // TODO: remove these default settings in a future version
obs_data_set_default_string( obs_data_set_default_string(
@@ -922,15 +947,6 @@ void Macro::LoadDockSettings(obs_data_t *obj)
obs_data_get_bool(dockSettings, "hasStatusLabel"); obs_data_get_bool(dockSettings, "hasStatusLabel");
_dockHighlight = obs_data_get_bool(dockSettings, _dockHighlight = obs_data_get_bool(dockSettings,
"highlightIfConditionsTrue"); "highlightIfConditionsTrue");
_dockIsFloating = obs_data_get_bool(dockSettings, "isFloating");
_dockArea = static_cast<Qt::DockWidgetArea>(
obs_data_get_int(dockSettings, "area"));
auto geometryStr =
obs_data_get_string(dockSettings, "geometry");
if (geometryStr && strlen(geometryStr)) {
_dockGeo =
QByteArray::fromBase64(QByteArray(geometryStr));
}
} }
EnableDock(dockEnabled); EnableDock(dockEnabled);
obs_data_release(dockSettings); obs_data_release(dockSettings);
@@ -938,47 +954,29 @@ void Macro::LoadDockSettings(obs_data_t *obj)
void Macro::EnableDock(bool value) void Macro::EnableDock(bool value)
{ {
if (_registerDock == value) {
return;
}
// Reset dock regardless // Reset dock regardless
RemoveDock(); RemoveDock();
// Unregister dock if (!value) {
if (_registerDock) {
_dockIsFloating = true;
_dockGeo = QByteArray();
_registerDock = value; _registerDock = value;
return; return;
} }
// Create new dock widget _dock = new MacroDock(GetWeakMacroByName(_name.c_str()), _runButtonText,
auto window = _pauseButtonText, _unpauseButtonText,
static_cast<QMainWindow *>(obs_frontend_get_main_window()); _conditionsTrueStatusText,
_dock = new MacroDock(GetWeakMacroByName(_name.c_str()), window,
_runButtonText, _pauseButtonText,
_unpauseButtonText, _conditionsTrueStatusText,
_conditionsFalseStatusText, _dockHighlight); _conditionsFalseStatusText, _dockHighlight);
SetDockWidgetName(); // Used by OBS to restore position
// Register new dock if (!obs_frontend_add_dock_by_id(_dockId.c_str(), _name.c_str(),
_dockAction = static_cast<QAction *>(obs_frontend_add_dock(_dock)); _dock)) {
blog(LOG_INFO, "failed to add macro dock for macro %s",
// Note that OBSBasic::OBSInit() has precedence over the visibility and _name.c_str());
// geometry set here. _dock->deleteLater();
// The function calls here are only intended to attempt to restore the _dock = nullptr;
// dock status when switching scene collections. _registerDock = false;
if (InitialLoadIsComplete()) { return;
_dock->setVisible(_dockIsVisible);
if (window->dockWidgetArea(_dock) != _dockArea) {
window->addDockWidget(_dockArea, _dock);
}
if (_dock->isFloating() != _dockIsFloating) {
_dock->setFloating(_dockIsFloating);
}
_dock->restoreGeometry(_dockGeo);
} }
_registerDock = value; _registerDock = value;
} }
@@ -1075,28 +1073,22 @@ StringVariable Macro::ConditionsFalseStatusText() const
void Macro::RemoveDock() void Macro::RemoveDock()
{ {
if (_dock) { obs_frontend_remove_dock(_dockId.c_str());
_dock->close(); _dock = nullptr;
_dock->deleteLater();
_dockAction->deleteLater();
_dock = nullptr;
_dockAction = nullptr;
}
} }
void Macro::SetDockWidgetName() const std::string Macro::GenerateDockId()
{ {
if (!_dock) { #if LIBOBS_API_VER > MAKE_SEMANTIC_VERSION(30, 0, 0)
return; auto uuid = os_generate_uuid();
} auto id = std::string("advss-macro-dock-") + std::string(uuid);
// Set prefix to avoid dock name conflict bfree(uuid);
_dock->setObjectName("ADVSS-" + QString::fromStdString(_name)); return id;
_dock->SetName(QString::fromStdString(_name));
if (!_dockAction) { #else
return; static std::atomic_int16_t idCounter = 0;
} return std::to_string(++idCounter);
_dockAction->setText(QString::fromStdString(_name)); #endif
} }
static void pauseCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) static void pauseCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)

View File

@@ -2,6 +2,7 @@
#include "macro-action.hpp" #include "macro-action.hpp"
#include "macro-condition.hpp" #include "macro-condition.hpp"
#include "macro-helpers.hpp" #include "macro-helpers.hpp"
#include "macro-input.hpp"
#include "macro-ref.hpp" #include "macro-ref.hpp"
#include "variable-string.hpp" #include "variable-string.hpp"
#include "temp-variable.hpp" #include "temp-variable.hpp"
@@ -25,35 +26,44 @@ public:
Macro(const std::string &name = "", const bool addHotkey = false); Macro(const std::string &name = "", const bool addHotkey = false);
virtual ~Macro(); virtual ~Macro();
bool CeckMatch();
bool 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; } std::string Name() const { return _name; }
void SetName(const std::string &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); void SetPaused(bool pause = true);
bool Paused() const { return _paused; } bool Paused() const { return _paused; }
bool WasPausedSince( bool WasPausedSince(
const std::chrono::high_resolution_clock::time_point &) const; const std::chrono::high_resolution_clock::time_point &) const;
void Stop();
bool GetStop() const { return _stop; }
void ResetTimers();
void SetMatchOnChange(bool onChange); void SetMatchOnChange(bool onChange);
bool MatchOnChange() const { return _performActionsOnChange; } bool MatchOnChange() const { return _performActionsOnChange; }
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; } void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
bool SkipExecOnStart() const { return _skipExecOnStart; } bool SkipExecOnStart() const { return _skipExecOnStart; }
void SetStopActionsIfNotDone(bool stopActionsIfNotDone)
{ void SetStopActionsIfNotDone(bool stopActionsIfNotDone);
_stopActionsIfNotDone = stopActionsIfNotDone;
}
bool StopActionsIfNotDone() const { return _stopActionsIfNotDone; } bool StopActionsIfNotDone() const { return _stopActionsIfNotDone; }
int RunCount() const { return _runCount; }; int RunCount() const { return _runCount; };
void ResetRunCount() { _runCount = 0; }; void ResetRunCount() { _runCount = 0; };
void ResetTimers();
void AddHelperThread(std::thread &&); void AddHelperThread(std::thread &&);
bool GetStop() const { return _stop; } void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
void Stop(); bool RunInParallel() const { return _runInParallel; }
// Input variables
MacroInputVariables GetInputVariables() const;
void SetInputVariables(const MacroInputVariables &);
// Temporary variable helpers // Temporary variable helpers
std::vector<TempVariable> GetTempVars(MacroSegment *filter) const; std::vector<TempVariable> GetTempVars(MacroSegment *filter) const;
@@ -88,7 +98,7 @@ public:
std::shared_ptr<Macro> Parent() const; std::shared_ptr<Macro> Parent() const;
// Saving and loading // Saving and loading
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj, bool saveForCopy = false) const;
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
// Some macros can refer to other macros, which are not yet loaded. // Some macros can refer to other macros, which are not yet loaded.
// Use this function to set these references after loading is complete. // Use this function to set these references after loading is complete.
@@ -98,19 +108,21 @@ public:
bool SwitchesScene() const; bool SwitchesScene() const;
// UI helpers // UI helpers
const QList<int> &GetActionConditionSplitterPosition() const;
void SetActionConditionSplitterPosition(const QList<int>); void SetActionConditionSplitterPosition(const QList<int>);
const QList<int> &GetElseActionSplitterPosition() const; const QList<int> &GetActionConditionSplitterPosition() const;
void SetElseActionSplitterPosition(const QList<int>); void SetElseActionSplitterPosition(const QList<int>);
const QList<int> &GetElseActionSplitterPosition() const;
bool HasValidSplitterPositions() const; bool HasValidSplitterPositions() const;
bool WasExecutedSince( bool WasExecutedSince(
const std::chrono::high_resolution_clock::time_point &) const; const std::chrono::high_resolution_clock::time_point &) const;
bool OnChangePreventedActionsRecently(); bool OnChangePreventedActionsRecently();
void ResetUIHelpers(); void ResetUIHelpers();
// Hotkeys
void EnablePauseHotkeys(bool); void EnablePauseHotkeys(bool);
bool PauseHotkeysEnabled() const; bool PauseHotkeysEnabled() const;
// Docks
void EnableDock(bool); void EnableDock(bool);
bool DockEnabled() const { return _registerDock; } bool DockEnabled() const { return _registerDock; }
void SetDockHasRunButton(bool value); void SetDockHasRunButton(bool value);
@@ -143,11 +155,10 @@ private:
bool RunActions(bool ignorePause); bool RunActions(bool ignorePause);
bool RunElseActions(bool ignorePause); bool RunElseActions(bool ignorePause);
bool DockIsVisible() const; void SaveDockSettings(obs_data_t *obj, bool saveForCopy) const;
void SetDockWidgetName() const;
void SaveDockSettings(obs_data_t *obj) const;
void LoadDockSettings(obs_data_t *obj); void LoadDockSettings(obs_data_t *obj);
void RemoveDock(); void RemoveDock();
static std::string GenerateDockId();
std::string _name = ""; std::string _name = "";
bool _die = false; bool _die = false;
@@ -182,6 +193,8 @@ private:
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
MacroInputVariables _inputVariables;
// UI helpers // UI helpers
bool _onPreventedActionExecution = false; bool _onPreventedActionExecution = false;
@@ -203,12 +216,8 @@ private:
obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.true"); obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.true");
StringVariable _conditionsFalseStatusText = StringVariable _conditionsFalseStatusText =
obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.false"); obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.false");
bool _dockIsFloating = true;
bool _dockIsVisible = false;
Qt::DockWidgetArea _dockArea;
QByteArray _dockGeo;
MacroDock *_dock = nullptr; MacroDock *_dock = nullptr;
QAction *_dockAction = nullptr; std::string _dockId = GenerateDockId();
}; };
void LoadMacros(obs_data_t *obj); void LoadMacros(obs_data_t *obj);

View File

@@ -220,14 +220,15 @@ static void setupTab(QTabWidget *tab)
} }
QWidget::connect(ActionQueueSignalManager::Instance(), QWidget::connect(ActionQueueSignalManager::Instance(),
&ActionQueueSignalManager::Rename, &ActionQueueSignalManager::Rename, tab,
[](const QString &oldName, const QString &newName) { [](const QString &oldName, const QString &newName) {
RenameItemTableRow(tabWidget->Table(), oldName, RenameItemTableRow(tabWidget->Table(), oldName,
newName); newName);
}); });
QWidget::connect( QWidget::connect(
ActionQueueSignalManager::Instance(), ActionQueueSignalManager::Instance(),
&ActionQueueSignalManager::Add, [tab](const QString &name) { &ActionQueueSignalManager::Add, tab,
[tab](const QString &name) {
AddItemTableRow( AddItemTableRow(
tabWidget->Table(), tabWidget->Table(),
getCellLabels(GetWeakActionQueueByQString(name) getCellLabels(GetWeakActionQueueByQString(name)
@@ -238,7 +239,7 @@ static void setupTab(QTabWidget *tab)
setTabVisible(tab, true); setTabVisible(tab, true);
}); });
QWidget::connect(ActionQueueSignalManager::Instance(), QWidget::connect(ActionQueueSignalManager::Instance(),
&ActionQueueSignalManager::Remove, &ActionQueueSignalManager::Remove, tab,
[](const QString &name) { [](const QString &name) {
RemoveItemTableRow(tabWidget->Table(), name); RemoveItemTableRow(tabWidget->Table(), name);
if (tabWidget->Table()->rowCount() == 0) { if (tabWidget->Table()->rowCount() == 0) {

View File

@@ -1,6 +1,7 @@
#include "action-queue.hpp" #include "action-queue.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp" #include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp"
namespace advss { namespace advss {
@@ -379,11 +380,24 @@ static bool queueWithNameExists(const std::string &name)
return !GetWeakActionQueueByName(name).expired(); 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) void ImportQueues(obs_data_t *data)
{ {
OBSDataArrayAutoRelease array = OBSDataArrayAutoRelease array =
obs_data_get_array(data, "actionQueues"); obs_data_get_array(data, "actionQueues");
size_t count = obs_data_array_count(array); 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++) { for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease arrayElement = obs_data_array_item(array, i); OBSDataAutoRelease arrayElement = obs_data_array_item(array, i);
auto queue = ActionQueue::Create(); auto queue = ActionQueue::Create();
@@ -392,7 +406,10 @@ void ImportQueues(obs_data_t *data)
continue; continue;
} }
queues.emplace_back(queue); queues.emplace_back(queue);
importedQueues->emplace_back(queue);
} }
QeueUITask(signalImportedQueues, importedQueues);
} }
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name) std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name)

View File

@@ -16,7 +16,7 @@
#include "switch-video.hpp" #include "switch-video.hpp"
#include "switch-network.hpp" #include "switch-network.hpp"
#include "macro-properties.hpp" #include "macro-settings.hpp"
#include "duration-control.hpp" #include "duration-control.hpp"
#include "priority-helper.hpp" #include "priority-helper.hpp"
#include "plugin-state-helpers.hpp" #include "plugin-state-helpers.hpp"
@@ -131,6 +131,7 @@ public:
RECORINDG_OR_STREAMING RECORINDG_OR_STREAMING
}; };
AutoStart autoStartEvent = AutoStart::NEVER; AutoStart autoStartEvent = AutoStart::NEVER;
bool enableCooldown = false;
Duration cooldown; Duration cooldown;
bool showSystemTrayNotifications = false; bool showSystemTrayNotifications = false;
bool transitionOverrideOverride = false; bool transitionOverrideOverride = false;

View File

@@ -0,0 +1,109 @@
#include "condition-logic.hpp"
#include "obs-module-helper.hpp"
#include "log-helper.hpp"
#include <cassert>
#include <QComboBox>
namespace advss {
const std::map<Logic::Type, const char *> Logic::localeMap = {
{Logic::Type::NONE, {"AdvSceneSwitcher.logic.none"}},
{Logic::Type::AND, {"AdvSceneSwitcher.logic.and"}},
{Logic::Type::OR, {"AdvSceneSwitcher.logic.or"}},
{Logic::Type::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
{Logic::Type::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
{Logic::Type::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
{Logic::Type::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
};
bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
bool conditionMatched, const char *context)
{
if (!context) {
context = "";
}
switch (type) {
case Type::ROOT_NONE:
return conditionMatched;
case Type::ROOT_NOT:
return !conditionMatched;
case Type::ROOT_LAST:
break;
case Type::NONE:
vblog(LOG_INFO, "skipping condition check for '%s'", context);
return currentMatchResult;
case Type::AND:
return currentMatchResult && conditionMatched;
case Type::OR:
return currentMatchResult || conditionMatched;
case Type::AND_NOT:
return currentMatchResult && !conditionMatched;
case Type::OR_NOT:
return currentMatchResult || !conditionMatched;
case Type::LAST:
default:
blog(LOG_WARNING, "ignoring invalid logic check (%s)", context);
return currentMatchResult;
}
return currentMatchResult;
}
void Logic::PopulateLogicTypeSelection(QComboBox *list, bool isRootCondition)
{
auto compare = isRootCondition
? std::function<bool(int)>{[](int typeValue) {
return typeValue < rootOffset;
}}
: std::function<bool(int)>{[](int typeValue) {
return typeValue >= rootOffset;
}};
for (const auto &[type, name] : localeMap) {
const int typeValue = static_cast<int>(type);
if (compare(typeValue)) {
list->addItem(obs_module_text(name), typeValue);
}
}
}
void Logic::Save(obs_data_t *obj, const char *name) const
{
obs_data_set_int(obj, name, static_cast<int>(_type));
}
void Logic::Load(obs_data_t *obj, const char *name)
{
_type = static_cast<Type>(obs_data_get_int(obj, name));
}
bool Logic::IsRootType() const
{
return _type < static_cast<Type>(rootOffset);
}
bool Logic::IsNegationType(Logic::Type type)
{
return type == Type::ROOT_NOT || type == Type::AND_NOT ||
type == Type::OR_NOT;
;
}
bool Logic::IsValidSelection(bool isRootCondition) const
{
if (!IsRootType() == isRootCondition) {
return false;
}
if (IsRootType() &&
(_type < Type::ROOT_NONE || _type >= Type::ROOT_LAST)) {
return false;
}
if (!IsRootType() &&
(_type <= Type::ROOT_LAST || _type >= Type::LAST)) {
return false;
}
return true;
}
} // namespace advss

View File

@@ -0,0 +1,51 @@
#pragma once
#include <map>
#include <obs-data.h>
#include <string>
class QComboBox;
namespace advss {
class Logic {
public:
static constexpr auto rootOffset = 100;
enum class Type {
ROOT_NONE = 0,
ROOT_NOT,
ROOT_LAST,
// leave some space for potential expansion
NONE = rootOffset,
AND,
OR,
AND_NOT,
OR_NOT,
LAST,
};
Logic(Type type) { _type = type; }
void Save(obs_data_t *obj, const char *name) const;
void Load(obs_data_t *obj, const char *name);
Type GetType() const { return _type; }
void SetType(const Type &type) { _type = type; }
bool IsRootType() const;
static bool IsNegationType(Logic::Type);
bool IsValidSelection(bool isRootCondition) const;
static bool ApplyConditionLogic(Type, bool currentMatchResult,
bool conditionMatched,
const char *context);
static void PopulateLogicTypeSelection(QComboBox *list,
bool isRootCondition);
private:
Type _type = Type::NONE;
static const std::map<Type, const char *> localeMap;
};
} // namespace advss

View File

@@ -77,13 +77,12 @@ bool CurlHelper::LoadLib()
{ {
_lib = new QLibrary(curl_library_name, nullptr); _lib = new QLibrary(curl_library_name, nullptr);
if (Resolve()) { if (Resolve()) {
blog(LOG_INFO, "[adv-ss] found curl library"); blog(LOG_INFO, "found curl library");
return true; return true;
} else { } else {
delete _lib; delete _lib;
_lib = nullptr; _lib = nullptr;
blog(LOG_WARNING, blog(LOG_WARNING, "couldn't find the curl library in PATH");
"[adv-ss] couldn't find the curl library in PATH");
} }
QStringList locations; QStringList locations;
locations << QDir::currentPath(); locations << QDir::currentPath();
@@ -94,14 +93,13 @@ bool CurlHelper::LoadLib()
locations << "/usr/local/opt/curl/lib"; locations << "/usr/local/opt/curl/lib";
#endif #endif
for (QString path : locations) { for (QString path : locations) {
blog(LOG_INFO, "[adv-ss] trying '%s'", blog(LOG_INFO, "trying '%s'", path.toUtf8().constData());
path.toUtf8().constData());
QFileInfo libPath( QFileInfo libPath(
QDir(path).absoluteFilePath(curl_library_name)); QDir(path).absoluteFilePath(curl_library_name));
if (libPath.exists() && libPath.isFile()) { if (libPath.exists() && libPath.isFile()) {
QString libFilePath = libPath.absoluteFilePath(); QString libFilePath = libPath.absoluteFilePath();
blog(LOG_INFO, "[adv-ss] found curl library at '%s'", blog(LOG_INFO, "found curl library at '%s'",
libFilePath.toUtf8().constData()); libFilePath.toUtf8().constData());
_lib = new QLibrary(libFilePath, nullptr); _lib = new QLibrary(libFilePath, nullptr);
@@ -113,7 +111,7 @@ bool CurlHelper::LoadLib()
} }
} }
} }
blog(LOG_WARNING, "[adv-ss] can't find the curl library"); blog(LOG_WARNING, "can't find the curl library");
return false; return false;
} }
@@ -128,11 +126,11 @@ bool CurlHelper::Resolve()
if (_init && _setopt && _slistAppend && _perform && _cleanup && if (_init && _setopt && _slistAppend && _perform && _cleanup &&
_error) { _error) {
blog(LOG_INFO, "[adv-ss] curl loaded successfully"); blog(LOG_INFO, "curl loaded successfully");
return true; return true;
} }
blog(LOG_INFO, "[adv-ss] curl symbols not resolved"); blog(LOG_INFO, "curl symbols not resolved");
return false; return false;
} }

View File

@@ -0,0 +1,34 @@
#include "cursor-shape-changer.hpp"
#include <QCursor>
#include <QEvent>
#include <QGuiApplication>
namespace advss {
CursorShapeChanger::CursorShapeChanger(QObject *parent, Qt::CursorShape shape)
: QObject(parent),
_shape(shape)
{
}
bool CursorShapeChanger::eventFilter(QObject *o, QEvent *e)
{
if ((e->type() == QEvent::HoverEnter || e->type() == QEvent::Enter) &&
!_overrideActive) {
_overrideActive = true;
QGuiApplication::setOverrideCursor(QCursor(_shape));
} else if (_overrideActive && e->type() == QEvent::Leave) {
_overrideActive = false;
QGuiApplication::restoreOverrideCursor();
}
return QObject::eventFilter(o, e);
}
void SetCursorOnWidgetHover(QWidget *widget, Qt::CursorShape shape)
{
widget->installEventFilter(new CursorShapeChanger(widget, shape));
}
} // namespace advss

View File

@@ -0,0 +1,20 @@
#pragma once
#include <QWidget>
namespace advss {
class CursorShapeChanger : public QObject {
public:
CursorShapeChanger(QObject *, Qt::CursorShape shape);
protected:
bool eventFilter(QObject *o, QEvent *e) override;
private:
Qt::CursorShape _shape;
std::atomic_bool _overrideActive = {false};
};
void SetCursorOnWidgetHover(QWidget *widget, Qt::CursorShape shape);
} // namespace advss

View File

@@ -0,0 +1,110 @@
#include "duration-modifier.hpp"
namespace advss {
void DurationModifier::Save(obs_data_t *obj, const char *name,
const char *duration) const
{
auto data = obs_data_create();
obs_data_set_int(data, name, static_cast<int>(_type));
_duration.Save(data, duration);
obs_data_set_obj(obj, "durationModifier", data);
obs_data_release(data);
}
void DurationModifier::Load(obs_data_t *obj, const char *name,
const char *duration)
{
obs_data_t *data = nullptr;
if (obs_data_has_user_value(obj, "durationModifier")) {
data = obs_data_get_obj(obj, "durationModifier");
} else {
// For backwards compatibility
obs_data_addref(obj);
data = obj;
}
// For backwards compatibility check if duration value exist without
// time constraint condition - if so assume DurationCondition::MORE
if (!obs_data_has_user_value(data, name) &&
obs_data_has_user_value(data, duration)) {
obs_data_set_int(data, name, static_cast<int>(Type::MORE));
}
_type = static_cast<Type>(obs_data_get_int(data, name));
_duration.Load(data, duration);
// TODO: remove this fallback
if (obs_data_has_user_value(data, "displayUnit")) {
_duration.SetUnit(static_cast<Duration::Unit>(
obs_data_get_int(data, "displayUnit")));
}
obs_data_release(data);
}
void DurationModifier::SetTimeRemaining(double seconds)
{
_duration.SetTimeRemaining(seconds);
}
bool DurationModifier::DurationReached()
{
switch (_type) {
case DurationModifier::Type::NONE:
return true;
case DurationModifier::Type::MORE:
return _duration.DurationReached();
case DurationModifier::Type::EQUAL:
if (_duration.DurationReached() && !_durationWasReached) {
_durationWasReached = true;
return true;
}
break;
case DurationModifier::Type::LESS:
return !_duration.DurationReached();
case DurationModifier::Type::WITHIN:
if (_duration.IsReset()) {
return false;
}
return !_duration.DurationReached();
default:
break;
}
return false;
}
void DurationModifier::ResetDuration()
{
_durationWasReached = false;
_duration.Reset();
}
bool DurationModifier::CheckConditionWithDurationModifier(bool conditionValue)
{
if (_type != DurationModifier::Type::WITHIN && !conditionValue) {
ResetDuration();
}
if (_type == DurationModifier::Type::WITHIN && conditionValue) {
ResetDuration();
}
switch (_type) {
case DurationModifier::Type::NONE:
case DurationModifier::Type::MORE:
case DurationModifier::Type::EQUAL:
case DurationModifier::Type::LESS:
return conditionValue && DurationReached();
case DurationModifier::Type::WITHIN:
if (conditionValue) {
SetTimeRemaining(_duration.Seconds());
}
return conditionValue || DurationReached();
default:
break;
}
return conditionValue;
}
} // namespace advss

View File

@@ -0,0 +1,31 @@
#pragma once
#include "duration.hpp"
namespace advss {
class DurationModifier {
public:
enum class Type { NONE, MORE, EQUAL, LESS, WITHIN };
void Save(obs_data_t *obj, const char *name = "time_constraint",
const char *duration = "seconds") const;
void Load(obs_data_t *obj, const char *name = "time_constraint",
const char *duration = "seconds");
void SetModifier(Type type) { _type = type; }
void SetDuration(const Duration &duration) { _duration = duration; }
Type GetType() const { return _type; }
Duration GetDuration() const { return _duration; }
void ResetDuration();
bool CheckConditionWithDurationModifier(bool conditionValue);
private:
void SetTimeRemaining(double seconds);
bool DurationReached();
Type _type = Type::NONE;
Duration _duration;
bool _durationWasReached = false;
};
} // namespace advss

View File

@@ -101,7 +101,7 @@ double Duration::TimeRemaining() const
void Duration::SetTimeRemaining(double remaining) void Duration::SetTimeRemaining(double remaining)
{ {
long long msPassed = (Seconds() - remaining) * 1000; long long msPassed = (long long)((Seconds() - remaining) * 1000);
_startTime = std::chrono::high_resolution_clock::now() - _startTime = std::chrono::high_resolution_clock::now() -
std::chrono::milliseconds(msPassed); std::chrono::milliseconds(msPassed);
} }

View File

@@ -84,8 +84,7 @@ void FilterComboBox::focusOutEvent(QFocusEvent *event)
// Reset on invalid selection // Reset on invalid selection
if (findText(currentText()) == -1) { if (findText(currentText()) == -1) {
setCurrentIndex(-1); setCurrentIndex(-1);
emit currentIndexChanged(-1); Emit(-1, "");
emit currentTextChanged("");
} }
QComboBox::focusOutEvent(event); QComboBox::focusOutEvent(event);
@@ -121,8 +120,7 @@ void FilterComboBox::CompleterHighlightChanged(const QModelIndex &index)
if (idx == -1) { if (idx == -1) {
return; return;
} }
emit currentIndexChanged(idx); Emit(idx, text);
emit currentTextChanged(text);
} }
void FilterComboBox::TextChanged(const QString &text) void FilterComboBox::TextChanged(const QString &text)
@@ -134,8 +132,18 @@ void FilterComboBox::TextChanged(const QString &text)
if (idx == -1) { if (idx == -1) {
return; return;
} }
emit currentIndexChanged(idx); }
emit currentTextChanged(text);
void FilterComboBox::Emit(int index, const QString &text)
{
if (_lastEmittedIndex != index) {
_lastEmittedIndex = index;
emit currentIndexChanged(index);
}
if (_lastEmittedText != text) {
_lastEmittedText = text;
emit currentTextChanged(text);
}
} }
} // namespace advss } // namespace advss

View File

@@ -25,6 +25,11 @@ private slots:
void TextChanged(const QString &); void TextChanged(const QString &);
private: private:
void Emit(int index, const QString &text);
int _lastEmittedIndex = -1;
QString _lastEmittedText;
int _lastCompleterHighlightRow = -1; int _lastCompleterHighlightRow = -1;
static bool _filteringEnabled; static bool _filteringEnabled;
}; };

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

@@ -81,6 +81,7 @@ public:
"AdvSceneSwitcher.item.nameNotAvailable", "AdvSceneSwitcher.item.nameNotAvailable",
std::string_view configureTooltip = "", QWidget *parent = 0); std::string_view configureTooltip = "", QWidget *parent = 0);
virtual ~ItemSelection() = default; virtual ~ItemSelection() = default;
Item *GetCurrentItem();
void SetItem(const std::string &); void SetItem(const std::string &);
void ShowRenameContextMenu(bool value); void ShowRenameContextMenu(bool value);
@@ -99,8 +100,6 @@ signals:
void ItemRenamed(const QString &oldName, const QString &name); void ItemRenamed(const QString &oldName, const QString &name);
private: private:
Item *GetCurrentItem();
FilterComboBox *_selection; FilterComboBox *_selection;
QPushButton *_modify; QPushButton *_modify;
CreateItemFunc _create; CreateItemFunc _create;

View File

@@ -0,0 +1,44 @@
#include "list-controls.hpp"
#include "obs-module-helper.hpp"
#include "ui-helpers.hpp"
#include <QLayout>
#include <QToolButton>
namespace advss {
ListControls::ListControls(QWidget *parent, bool reorder) : QToolBar(parent)
{
setObjectName("listControls");
setStyleSheet("#listControls { background-color: transparent; }");
setIconSize({16, 16});
AddActionHelper("addIconSmall", "AdvSceneSwitcher.listControls.add",
[this]() { Add(); });
AddActionHelper("removeIconSmall",
"AdvSceneSwitcher.listControls.remove",
[this]() { Remove(); });
if (reorder) {
addSeparator();
AddActionHelper("upArrowIconSmall",
"AdvSceneSwitcher.listControls.up",
[this]() { Up(); });
AddActionHelper("downArrowIconSmall",
"AdvSceneSwitcher.listControls.down",
[this]() { Down(); });
}
}
void ListControls::AddActionHelper(const char *theme, const char *tooltip,
const std::function<void()> &signal)
{
auto button = new QToolButton(this);
button->setToolTip(obs_module_text(tooltip));
button->setProperty("themeID", QVariant(QString(theme)));
(void)addWidget(button);
button->connect(button, &QToolButton::clicked, this, signal);
}
} // namespace advss

View File

@@ -0,0 +1,25 @@
#pragma once
#include "export-symbol-helper.hpp"
#include <QToolBar>
namespace advss {
class ADVSS_EXPORT ListControls final : public QToolBar {
Q_OBJECT
public:
ListControls(QWidget *parent = nullptr, bool reorder = true);
signals:
void Add();
void Remove();
void Up();
void Down();
private:
void AddActionHelper(const char *theme, const char *tooltip,
const std::function<void()> &signal);
};
} // namespace advss

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

@@ -0,0 +1,43 @@
#include "list-editor.hpp"
#include "ui-helpers.hpp"
namespace advss {
ListEditor::ListEditor(QWidget *parent, bool reorder)
: QWidget(parent),
_list(new QListWidget()),
_controls(new ListControls(this, reorder)),
_mainLayout(new QVBoxLayout())
{
QWidget::connect(_controls, SIGNAL(Add()), this, SLOT(Add()));
QWidget::connect(_controls, SIGNAL(Remove()), this, SLOT(Remove()));
QWidget::connect(_controls, SIGNAL(Up()), this, SLOT(Up()));
QWidget::connect(_controls, SIGNAL(Down()), this, SLOT(Down()));
QWidget::connect(_list, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
this, SLOT(Clicked(QListWidgetItem *)));
_mainLayout->setContentsMargins(0, 0, 0, 0);
_mainLayout->addWidget(_list);
_mainLayout->addWidget(_controls);
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

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