Compare commits

...

84 Commits

Author SHA1 Message Date
WarmUpTill
aa87911b71 Use cpp-httplib based HTTP action type
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The goal is to remove the older, more limited version of the HTTP action
at some point in the future.
2025-03-30 14:06:19 +02:00
WarmUpTill
1b05019acc Add option to hide entries from action selection
Intended to be used to hide legacy version of actions types (e.g. HTTP)
2025-03-30 14:01:41 +02:00
WarmUpTill
78a5a2629d Hide the "remote" file check option
This option will be removed at some point in the future.
The http action should be used instead.
2025-03-30 14:01:41 +02:00
WarmUpTill
634270a978 Cleanup includes 2025-03-30 14:01:41 +02:00
WarmUpTill
78ba22e1e4 Hide "get settings" button when setting macro property value 2025-03-30 14:01:41 +02:00
WarmUpTill
aba5737a60 Update and clean up locale (qwe1154323937) 2025-03-30 14:01:41 +02:00
WarmUpTill
4315f7f621 Exclude unwanted files from sources archive 2025-03-30 14:01:41 +02:00
WarmUpTill
53c535962f Use tab key to switch to dialog controls and set default focus to input 2025-03-30 14:01:41 +02:00
WarmUpTill
1e718b78c7 Fall back to project version if git tag cannot be queried 2025-03-30 14:01:41 +02:00
WarmUpTill
b1d2156228 Update libremidi to v4.3.0 2025-03-30 14:01:41 +02:00
WarmUpTill
9c3c953c6b Ignore deprecation warnings for Qt 6.7 and above
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-22 18:09:51 +01:00
WarmUpTill
ae74f68db7 Add option to close projector windows 2025-03-22 18:09:51 +01:00
WarmUpTill
213f1bba36 Add "Any" entry of the reward selection only for Twitch condition 2025-03-22 18:09:51 +01:00
WarmUpTill
5a2cb0bd68 Add more scripting signals / procedures 2025-03-22 18:09:51 +01:00
WarmUpTill
23b461828b Add start start / stop callbacks 2025-03-22 18:09:51 +01:00
WarmUpTill
9944a1b03b Move interval reset handling 2025-03-22 18:09:51 +01:00
WarmUpTill
eaad4d1bbd Fix Twitch helper caches misbehaving
API calls with different sets of arguments could map to the same key
value pair, which resulted in unexpected behavior for functions using
those caches
2025-03-22 18:09:51 +01:00
WarmUpTill
17d9b73b9a Fix thread flood when Twitch event sub connection is lost
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-15 12:31:13 +01:00
WarmUpTill
d6185a6099 Add handling for Twitch API throttling 2025-03-15 12:31:13 +01:00
WarmUpTill
41c06b9edb Refactor Twitch condition event handling 2025-03-15 12:31:13 +01:00
WarmUpTill
d8807077da Add option to query reward information 2025-03-15 12:31:13 +01:00
WarmUpTill
34e125f82d Rework chache handling
* No longer provide multiple HTTP helper functions with the same name
* Default to cache not being used
* Explicitly use the cache for certain Twitch actions and conditions
* Clear caches on shutdown to avoid leaks being reported
2025-03-15 12:31:13 +01:00
WarmUpTill
d670b6d07e Move JSON temp var heper functions 2025-03-15 12:31:13 +01:00
WarmUpTill
7b0f985f18 Add macro properties for 'reward' object in redemption events 2025-03-15 12:31:13 +01:00
WarmUpTill
212be923f6 Add option to query user information 2025-03-15 12:31:13 +01:00
WarmUpTill
88fcb57e9f Cleanup 2025-03-15 12:31:13 +01:00
WarmUpTill
9b609c118f Fix disabling chat emote only mode attempting to send chat messages 2025-03-15 12:31:13 +01:00
WarmUpTill
37c398d37a Indicate that "Set to condition/action value" should be avoided 2025-03-15 12:31:13 +01:00
WarmUpTill
a51b7f6b13 Fix UI theming issues
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-11 22:12:05 +01:00
WarmUpTill
808fd84b83 Remove Qt5 support 2025-03-11 22:12:05 +01:00
WarmUpTill
57bcea15f5 cmake: Replace find_qt with find_package 2025-03-10 19:48:01 +01:00
WarmUpTill
4e51b56b9b Add game capture condition 2025-03-10 18:49:07 +01:00
WarmUpTill
ef5cf41d34 Add OBS 31 support 2025-03-10 18:49:07 +01:00
WarmUpTill
911e79ea6a Fix OCR controls showing in place of color controls
This happened when OCR support was disabled at build time
2025-03-10 18:33:13 +01:00
WarmUpTill
d586177de0 Add screenshot condition
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-03-09 20:57:51 +01:00
WarmUpTill
202c36646c Fix UI issues when opening ItemSettingsDialogs
Some widgets were displayed incorrectly if the parent of the dialog
window is not the main settings window
2025-03-09 20:05:07 +01:00
WarmUpTill
ab5102f5ca Fix typo 2025-03-09 20:05:07 +01:00
WarmUpTill
e612fb99f6 Cleanup
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
* Reorder action type selection
* Refactor
2025-03-06 21:30:03 +01:00
WarmUpTill
691e77a69a Add option to generate random number 2025-03-06 21:30:03 +01:00
Kaito Udagawa
930118c61f Call Setup() after Load()
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Tesseract API depends on the languageCode and this must be reinitialized after Load
2025-03-06 08:43:54 +01:00
Kaito Udagawa
1e05f2f2ce Fix languageCode
LanguageCode is not respected by OCRParameters and settings properly.
2025-03-06 08:43:54 +01:00
WarmUpTill
ae25b4d023 Improve setting selection handling
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Will now try to keep the current selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
af055a12a1 Cleanup 2025-03-05 14:16:04 +01:00
WarmUpTill
7de28eedd3 Improve settings button selection handling
Will now try to keep the current button selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
8dfe81f522 Display failure if Twitch token account name cannot be fetched
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-27 19:52:41 +01:00
WarmUpTill
a1d8ae291d Add option to hide empty name warning in ItemSettingsDialog 2025-02-27 19:52:41 +01:00
WarmUpTill
eb6989527d Add support for libproc2 version 4.0.5
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-10 18:12:08 +01:00
WarmUpTill
2986a5dd96 Fix crash when actions were copied into a action queue
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The steps added via AddPostLoadStep() from the copy were never executed.
Thus they might be invalid when RunPostLoadSteps() is next called.

This could result in a crash when a new action or condition is added
after e.g. a "Filter" action's TempVariableRef was copied, which adds a
PostLoadStep to resolve the macro segment reference.
2025-02-04 08:26:04 +01:00
WarmUpTill
816ee9b244 Fix crash when changing Twitch condition type 2025-02-04 08:26:04 +01:00
WarmUpTill
51b53bf948 Fix script actions / conditions not working after stopping macro
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-30 09:39:50 +01:00
WarmUpTill
2c5121ee94 Prevent crash when macro contains invalid action or condition 2025-01-30 09:39:50 +01:00
WarmUpTill
6a58684854 Fix import of incomplete settings not resetting duration to zero
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-23 19:00:51 +01:00
WarmUpTill
0b622fdbed Fix crash when importing settings 2025-01-23 19:00:51 +01:00
WarmUpTill
3bce8e075f CI: Switch to and adapt to Ubuntu 24 runner
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-15 21:36:54 +01:00
WarmUpTill
f598d84ea1 Fix freeze due to ResizingPlainTextEdit constantly resizing incorrectly 2025-01-15 21:36:35 +01:00
WarmUpTill
d7a244e80e Fix transition condition not considering transition overrides
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-12-31 12:47:20 +01:00
WarmUpTill
408002e96a Add support for OBS 31 in-tree builds
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-12-12 21:41:21 +01:00
WarmUpTill
50da3d3210 Fix "Media" condition showing the incorrect macro property selection
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-11-30 12:11:14 +01:00
WarmUpTill
9e13067769 Fix typo
Some checks failed
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-11-23 17:11:05 +01:00
WarmUpTill
821e768e76 Reduce screenshot selection area to area to be checked
Some checks failed
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
debian-build / build (push) Has been cancelled
2024-11-22 20:52:15 +01:00
WarmUpTill
4d2179448b Rename files 2024-11-22 20:52:15 +01:00
WarmUpTill
40d9002470 Update scripting examples
* Add more comments
* Add example showcasing how to register and set temp vars
2024-11-22 20:36:36 +01:00
WarmUpTill
7f1e310ca6 Add support to register, deregister, and set temp vars from scripts 2024-11-22 20:36:36 +01:00
WarmUpTill
66d02f4683 Fix typos 2024-11-22 20:36:36 +01:00
Arash Partow
0aae1b9978 Update ExprTk to 0.0.3
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-11-22 19:56:02 +01:00
WarmUpTill
db50822b05 Prepare to fix deprecation warning when using OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The following log message will be present in OBS 31 when
CheckIfTrayIsDisabled() is called:

DEPRECATION: obs_frontend_get_global_config is deprecated. Read from
global or user configuration explicitly instead.

It will be resolved when the minimum OBS version is bumped to OBS 31.
2024-11-02 08:28:52 +01:00
WarmUpTill
63a545b293 Fix "Date" condition returning true unexpectedly
This could happen if the plugin was stopped on the General tab and
restarted or the first time the plugin was started.
2024-11-02 08:28:52 +01:00
WarmUpTill
bc0497d2c9 Add MacroWasCheckedSinceLastStart() 2024-11-02 08:28:52 +01:00
WarmUpTill
37226a3a4c Fix macro group icon not being displayed correctly in OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-31 19:39:38 +01:00
WarmUpTill
9f15fbe47c Recursively search for source button in property groups
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-30 18:29:23 +01:00
WarmUpTill
0ee6feb529 Add option to check the current stream key
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-26 23:42:59 +02:00
WarmUpTill
3b82b55090 Fix Queue condition not displaying the correct size selection 2024-10-26 23:42:59 +02:00
WarmUpTill
071c3309c2 Add option to check current streaming service
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-26 11:09:06 +02:00
WarmUpTill
ee7ee7a846 Move AutoUpdateTooltipLabel definition 2024-10-26 11:09:06 +02:00
WarmUpTill
d10300bd4b Fix typo 2024-10-26 11:09:06 +02:00
WarmUpTill
e262672876 Add "Last empty" row to Queue tab 2024-10-26 11:08:51 +02:00
WarmUpTill
392e775c7b Add temp var support for size and running state 2024-10-26 11:08:51 +02:00
WarmUpTill
f641c20564 Add SetTempVarValue() overload to support boolean values 2024-10-26 11:08:51 +02:00
WarmUpTill
39327be88e Add option to move scene item above / below specified scene item
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-21 20:46:27 +02:00
WarmUpTill
ed0963799c Fix "Screenshot" action variable selection not being loaded
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-18 18:30:49 +02:00
WarmUpTill
f9e73a2b8f Fix crash when clearing Twitch chat buffer 2024-10-18 18:29:46 +02:00
Przemek Pawlas
dfaa704b71 Remove some unused includes 2024-10-12 17:09:33 +02:00
Przemek Pawlas
d54edddde4 Enable use of variables as input for "Variable" action 2024-10-12 17:09:33 +02:00
Przemek Pawlas
37cc289204 Rename function 2024-10-12 17:09:33 +02:00
166 changed files with 15884 additions and 6043 deletions

View File

@@ -6,15 +6,6 @@
"autosort": true "autosort": true
}, },
"additional_commands": { "additional_commands": {
"find_qt": {
"flags": [],
"kwargs": {
"COMPONENTS": "+",
"COMPONENTS_WIN": "+",
"COMPONENTS_MACOS": "+",
"COMPONENTS_LINUX": "+"
}
},
"set_target_properties_obs": { "set_target_properties_obs": {
"pargs": 1, "pargs": 1,
"flags": [], "flags": [],

View File

@@ -20,6 +20,7 @@ body:
- macOS 13 - macOS 13
- macOS 12 - macOS 12
- macOS 11 - macOS 11
- Ubuntu 24.04
- Ubuntu 23.10 - Ubuntu 23.10
- Ubuntu 22.04 - Ubuntu 22.04
- Other - Other

View File

@@ -9,5 +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 'libproc2-dev'
package 'libusb-1.0-0-dev' package 'libusb-1.0-0-dev'

View File

@@ -480,12 +480,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
popd popd
;; ;;
linux) linux)
# Hacky workaround to support libproc2 with Ubuntu 22 build environment # Nothing to do for now
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
}
;; ;;
} }
} }

0
.github/test vendored
View File

View File

@@ -43,7 +43,7 @@ jobs:
# devscripts and libobs-dev are needed but they were already installed # devscripts and libobs-dev are needed but they were already installed
# from check_libobs_revision and install_frontend_header sections. # from check_libobs_revision and install_frontend_header sections.
sudo apt update sudo apt update
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev libprocps-dev sudo apt install build-essential cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qt6-base-dev libopencv-dev libproc2-dev
- name: build - name: build
run: | run: |
debuild --no-lintian --no-sign debuild --no-lintian --no-sign

View File

@@ -10,7 +10,7 @@ env:
jobs: jobs:
check-event: check-event:
name: Check GitHub Event Data 🔎 name: Check GitHub Event Data 🔎
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
defaults: defaults:
run: run:
shell: bash shell: bash
@@ -177,7 +177,7 @@ jobs:
ubuntu-build: ubuntu-build:
name: Build for Ubuntu 🐧 name: Build for Ubuntu 🐧
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
needs: check-event needs: check-event
defaults: defaults:
run: run:
@@ -241,14 +241,14 @@ jobs:
- name: Upload Artifacts 📡 - name: Upload Artifacts 📡
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }} name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*.* path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*.*
- name: Upload debug symbol artifacts 🪲 - name: Upload debug symbol artifacts 🪲
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: ${{ fromJSON(needs.check-event.outputs.package) }} if: ${{ fromJSON(needs.check-event.outputs.package) }}
with: with:
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*-dbgsym.ddeb path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*-dbgsym.ddeb
windows-build: windows-build:

View File

@@ -3,7 +3,7 @@ on:
workflow_call: workflow_call:
jobs: jobs:
clang-format: clang-format:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -15,7 +15,7 @@ jobs:
failCondition: error failCondition: error
cmake-format: cmake-format:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:

View File

@@ -31,7 +31,7 @@ jobs:
create-release: create-release:
name: Create Release 🛫 name: Create Release 🛫
if: github.ref_type == 'tag' if: github.ref_type == 'tag'
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
needs: build-project needs: build-project
defaults: defaults:
run: run:
@@ -77,7 +77,7 @@ jobs:
variants=( variants=(
'windows-x64;zip|exe' 'windows-x64;zip|exe'
'macos-universal;tar.xz|pkg' 'macos-universal;tar.xz|pkg'
'ubuntu-22.04-x86_64;tar.xz|deb|ddeb' 'ubuntu-24.04-x86_64;tar.xz|deb|ddeb'
'sources;tar.xz' 'sources;tar.xz'
) )

View File

@@ -20,6 +20,12 @@ if(BUILD_OUT_OF_TREE)
include(helpers) include(helpers)
endif() endif()
# OBS 31 no longer defines find_qt so check if we need to include it for in-tree
# builds
if(NOT COMMAND find_qt)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/helpers_common.cmake")
endif()
set(LIB_NAME "${PROJECT_NAME}-lib") set(LIB_NAME "${PROJECT_NAME}-lib")
add_library(${PROJECT_NAME} MODULE) add_library(${PROJECT_NAME} MODULE)
add_library(${LIB_NAME} SHARED) add_library(${LIB_NAME} SHARED)
@@ -27,6 +33,11 @@ add_library(${LIB_NAME} SHARED)
include(cmake/common/get_git_revision_description.cmake) include(cmake/common/get_git_revision_description.cmake)
get_git_head_revision(GIT_REFSPEC GIT_SHA1) get_git_head_revision(GIT_REFSPEC GIT_SHA1)
git_describe(GIT_TAG) git_describe(GIT_TAG)
if(${GIT_TAG} STREQUAL "GIT-NOTFOUND")
set(GIT_TAG ${PROJECT_VERSION})
endif()
message(STATUS "${PROJECT_NAME} version: ${GIT_TAG}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/version.cpp.in" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/version.cpp.in"
"${CMAKE_CURRENT_BINARY_DIR}/lib/version.cpp" @ONLY) "${CMAKE_CURRENT_BINARY_DIR}/lib/version.cpp" @ONLY)
@@ -157,6 +168,8 @@ target_sources(
lib/queue/action-queue.hpp lib/queue/action-queue.hpp
lib/queue/action-queue-tab.cpp lib/queue/action-queue-tab.cpp
lib/queue/action-queue-tab.hpp lib/queue/action-queue-tab.hpp
lib/utils/auto-update-tooltip-label.cpp
lib/utils/auto-update-tooltip-label.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.cpp
@@ -287,9 +300,16 @@ 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})
find_qt(COMPONENTS Widgets Core) find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets) target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
target_link_libraries(${LIB_NAME} PRIVATE Qt::Core Qt::Widgets) target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported Qt
# version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
if(Qt6_VERSION VERSION_GREATER "6.0.0")
target_compile_definitions(${LIB_NAME} PRIVATE QT_NO_DEPRECATED_WARNINGS)
endif()
target_compile_options( target_compile_options(
${PROJECT_NAME} ${PROJECT_NAME}
PRIVATE PRIVATE
@@ -405,7 +425,12 @@ else()
set(PROCESS_CONDITION_SUPPORTED 1) set(PROCESS_CONDITION_SUPPORTED 1)
endif() endif()
if(PROCPS2_INCLUDE_DIR) if(PROCPS2_INCLUDE_DIR)
message(STATUS "${PROJECT_NAME} using libproc2") find_package(PkgConfig REQUIRED)
pkg_check_modules(libproc2 IMPORTED_TARGET libproc2<4.0.5 QUIET)
if(libproc2_FOUND)
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_USE_INFO)
endif()
pkg_check_modules(libproc2 REQUIRED IMPORTED_TARGET libproc2)
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}") set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE) target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
set(PROCESS_CONDITION_SUPPORTED 1) set(PROCESS_CONDITION_SUPPORTED 1)
@@ -418,6 +443,9 @@ else()
endif() endif()
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}") target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp) target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp)
# Don't include irrelevant folders into sources archive
list(APPEND CPACK_SOURCE_IGNORE_FILES "\\.deps/.*")
endif() endif()
if(NOT OS_WINDOWS) if(NOT OS_WINDOWS)

View File

@@ -7,7 +7,7 @@ Build-Depends: cmake,
libcurl4-openssl-dev, libcurl4-openssl-dev,
libobs-dev, libobs-dev,
libxtst-dev, libxtst-dev,
qtbase5-dev, qt6-base-dev,
libxss-dev, libxss-dev,
libopencv-dev libopencv-dev
Standards-Version: 4.6.0 Standards-Version: 4.6.0

View File

@@ -3,7 +3,7 @@
export DEB_BUILD_MAINT_OPTIONS = hardening=+all export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export QT_SELECT = qt5 export QT_SELECT = qt6
%: %:
dh $@ dh $@

View File

@@ -283,8 +283,14 @@ endfunction()
function(setup_advss_plugin target) function(setup_advss_plugin target)
setup_obs_lib_dependency(${target}) setup_obs_lib_dependency(${target})
find_qt(COMPONENTS Widgets Core) find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets) target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported
# Qt version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
if(Qt6_VERSION VERSION_GREATER "6.7.0")
target_compile_definitions(${target} PRIVATE QT_NO_DEPRECATED_WARNINGS)
endif()
set_target_properties( set_target_properties(
${target} ${target}

View File

@@ -17,85 +17,6 @@ if(NOT QT_VERSION)
set_property(CACHE QT_VERSION PROPERTY STRINGS AUTO 5 6) set_property(CACHE QT_VERSION PROPERTY STRINGS AUTO 5 6)
endif() endif()
# find_qt: Macro to find best possible Qt version for use with the project:
macro(find_qt)
set(multiValueArgs COMPONENTS COMPONENTS_WIN COMPONENTS_MAC COMPONENTS_LINUX)
cmake_parse_arguments(find_qt "" "${oneValueArgs}" "${multiValueArgs}"
${ARGN})
# Do not use versionless targets in the first step to avoid Qt::Core being
# clobbered by later opportunistic find_package runs
set(QT_NO_CREATE_VERSIONLESS_TARGETS TRUE)
message(DEBUG "Start Qt version discovery...")
# Loop until _QT_VERSION is set or FATAL_ERROR aborts script execution early
while(NOT _QT_VERSION)
message(DEBUG "QT_VERSION set to ${QT_VERSION}")
if(QT_VERSION STREQUAL AUTO AND NOT qt_test_version)
set(qt_test_version 6)
elseif(NOT QT_VERSION STREQUAL AUTO)
set(qt_test_version ${QT_VERSION})
endif()
message(DEBUG "Attempting to find Qt${qt_test_version}")
find_package(
Qt${qt_test_version}
COMPONENTS Core
QUIET)
if(TARGET Qt${qt_test_version}::Core)
set(_QT_VERSION
${qt_test_version}
CACHE INTERNAL "")
message(STATUS "Qt version found: ${_QT_VERSION}")
unset(qt_test_version)
break()
elseif(QT_VERSION STREQUAL AUTO)
if(qt_test_version EQUAL 6)
message(WARNING "Qt6 was not found, falling back to Qt5")
set(qt_test_version 5)
continue()
endif()
endif()
message(FATAL_ERROR "Neither Qt6 nor Qt5 found.")
endwhile()
# Enable versionless targets for the remaining Qt components
set(QT_NO_CREATE_VERSIONLESS_TARGETS FALSE)
set(qt_components ${find_qt_COMPONENTS})
if(OS_WINDOWS)
list(APPEND qt_components ${find_qt_COMPONENTS_WIN})
elseif(OS_MACOS)
list(APPEND qt_components ${find_qt_COMPONENTS_MAC})
else()
list(APPEND qt_components ${find_qt_COMPONENTS_LINUX})
endif()
message(DEBUG "Trying to find Qt components ${qt_components}...")
find_package(Qt${_QT_VERSION} REQUIRED ${qt_components})
list(APPEND qt_components Core)
if("Gui" IN_LIST find_qt_COMPONENTS_LINUX)
list(APPEND qt_components "GuiPrivate")
endif()
# Check for versionless targets of each requested component and create if
# necessary
foreach(component IN LISTS qt_components)
message(DEBUG "Checking for target Qt::${component}")
if(NOT TARGET Qt::${component} AND TARGET Qt${_QT_VERSION}::${component})
add_library(Qt::${component} INTERFACE IMPORTED)
set_target_properties(
Qt::${component} PROPERTIES INTERFACE_LINK_LIBRARIES
Qt${_QT_VERSION}::${component})
endif()
set_property(TARGET Qt::${component} PROPERTY INTERFACE_COMPILE_FEATURES "")
endforeach()
endmacro()
# check_uuid: Helper function to check for valid UUID # check_uuid: Helper function to check for valid UUID
function(check_uuid uuid_string return_value) function(check_uuid uuid_string return_value)
set(valid_uuid TRUE) set(valid_uuid TRUE)

View File

@@ -212,7 +212,6 @@ AdvSceneSwitcher.condition.stream.state.start="Stream läuft"
AdvSceneSwitcher.condition.stream.state.stop="Stream gestoppt" AdvSceneSwitcher.condition.stream.state.stop="Stream gestoppt"
AdvSceneSwitcher.condition.stream.state.starting="Stream wird gestartet" AdvSceneSwitcher.condition.stream.state.starting="Stream wird gestartet"
AdvSceneSwitcher.condition.stream.state.stopping="Stream wird gestoppt" AdvSceneSwitcher.condition.stream.state.stopping="Stream wird gestoppt"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Aufnahme" AdvSceneSwitcher.condition.record="Aufnahme"
AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft" AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft"
AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert" AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert"
@@ -490,7 +489,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Nach unten verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="An erste Stelle verschieben" AdvSceneSwitcher.action.sceneOrder.type.moveTop="An erste Stelle verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="An letzte Stelle verschieben" AdvSceneSwitcher.action.sceneOrder.type.moveBottom="An letzte Stelle verschieben"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben" 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}}{{sources2}}{{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}}{{settingSelection}}{{singleSettingValue}}" AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -527,7 +526,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Standard"
AdvSceneSwitcher.action.screenshot.save.custom="Benutzerdefiniert" AdvSceneSwitcher.action.screenshot.save.custom="Benutzerdefiniert"
AdvSceneSwitcher.action.screenshot.type.source="Quelle" AdvSceneSwitcher.action.screenshot.type.source="Quelle"
AdvSceneSwitcher.action.screenshot.type.scene="Szene" AdvSceneSwitcher.action.screenshot.type.scene="Szene"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's Haupt-Ausgabe"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Quellen oder Szenen, die nicht immer gerendert werden, können dazu führen, dass einige Teile der Screenshots leer bleiben." AdvSceneSwitcher.action.screenshot.blackscreenNote="Quellen oder Szenen, die nicht immer gerendert werden, können dazu führen, dass einige Teile der Screenshots leer bleiben."
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}{{variables}}Pfad" AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}{{variables}}Pfad"
AdvSceneSwitcher.action.profile="Profil" AdvSceneSwitcher.action.profile="Profil"

View File

@@ -97,6 +97,7 @@ AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="Add new action queue"
AdvSceneSwitcher.actionQueueTab.queueRemoveButton.tooltip="Remove selected action queues" AdvSceneSwitcher.actionQueueTab.queueRemoveButton.tooltip="Remove selected action queues"
AdvSceneSwitcher.actionQueueTab.name.header="Name" AdvSceneSwitcher.actionQueueTab.name.header="Name"
AdvSceneSwitcher.actionQueueTab.size.header="Size" AdvSceneSwitcher.actionQueueTab.size.header="Size"
AdvSceneSwitcher.actionQueueTab.lastEmpty.header="Last empty"
AdvSceneSwitcher.actionQueueTab.isRunning.header="Is running" 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"
@@ -418,7 +419,10 @@ AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
AdvSceneSwitcher.condition.stream.state.starting="Stream starting" AdvSceneSwitcher.condition.stream.state.starting="Stream starting"
AdvSceneSwitcher.condition.stream.state.stopping="Stream stopping" AdvSceneSwitcher.condition.stream.state.stopping="Stream stopping"
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Keyframe interval equals" AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Keyframe interval equals"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}" AdvSceneSwitcher.condition.stream.state.streamKey="Stream key is"
AdvSceneSwitcher.condition.stream.state.service="Service name matches"
AdvSceneSwitcher.condition.stream.service.tooltip="Current service name: %1"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}{{streamKey}}{{serviceName}}{{regex}}{{currentService}}"
AdvSceneSwitcher.condition.record="Recording" AdvSceneSwitcher.condition.record="Recording"
AdvSceneSwitcher.condition.record.state.start="Recording running" AdvSceneSwitcher.condition.record.state.start="Recording running"
AdvSceneSwitcher.condition.record.state.pause="Recording paused" AdvSceneSwitcher.condition.record.state.pause="Recording paused"
@@ -765,6 +769,11 @@ AdvSceneSwitcher.condition.streamDeck.checkData="Check data field"
AdvSceneSwitcher.condition.streamDeck.startListen="Start listening" AdvSceneSwitcher.condition.streamDeck.startListen="Start listening"
AdvSceneSwitcher.condition.streamDeck.stopListen="Stop listening" AdvSceneSwitcher.condition.streamDeck.stopListen="Stop listening"
AdvSceneSwitcher.condition.streamDeck.pluginDownload="<html><head/><body><p>The Stream Deck plugin can be found <a href=\"https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin/releases\"><span style=\" text-decoration: underline; color:#268bd2;\">here on GitHub</span></a>.</p></body></html>" AdvSceneSwitcher.condition.streamDeck.pluginDownload="<html><head/><body><p>The Stream Deck plugin can be found <a href=\"https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin/releases\"><span style=\" text-decoration: underline; color:#268bd2;\">here on GitHub</span></a>.</p></body></html>"
AdvSceneSwitcher.condition.gameCapture="Game capture"
AdvSceneSwitcher.condition.gameCapture.entry="{{sources}}hooked a game."
AdvSceneSwitcher.condition.screenshot="Screenshot"
AdvSceneSwitcher.condition.screenshot.entry="A screenshot was taken"
# Macro Actions # Macro Actions
AdvSceneSwitcher.action.unknown="Unknown action" AdvSceneSwitcher.action.unknown="Unknown action"
@@ -956,7 +965,9 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Move to top" AdvSceneSwitcher.action.sceneOrder.type.moveTop="Move to top"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom" 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.type.above="Move above"
AdvSceneSwitcher.action.sceneOrder.type.below="Move below"
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}{{sources2}}"
AdvSceneSwitcher.action.sceneTransform="Scene item transform" AdvSceneSwitcher.action.sceneTransform="Scene item transform"
AdvSceneSwitcher.action.sceneTransform.type.manual="Set transform string" AdvSceneSwitcher.action.sceneTransform.type.manual="Set transform string"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Set transform setting" AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Set transform setting"
@@ -1011,7 +1022,6 @@ AdvSceneSwitcher.action.screenshot.save.custom="Custom file path"
AdvSceneSwitcher.action.screenshot.save.variable="Variable (base64 encoded PNG)" AdvSceneSwitcher.action.screenshot.save.variable="Variable (base64 encoded PNG)"
AdvSceneSwitcher.action.screenshot.type.source="Source" AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="Scene" AdvSceneSwitcher.action.screenshot.type.scene="Scene"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's main output"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Sources or scenes, which are not always rendered, may result in some parts of screenshots to remain blank." AdvSceneSwitcher.action.screenshot.blackscreenNote="Sources or scenes, which are not always rendered, may result in some parts of screenshots to remain blank."
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}and save to{{saveType}}{{variables}}" AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}and save to{{saveType}}{{variables}}"
AdvSceneSwitcher.action.profile="Profile" AdvSceneSwitcher.action.profile="Profile"
@@ -1040,21 +1050,35 @@ AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request="Send{{api}}of typ
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients" AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients"
AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}" AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}"
AdvSceneSwitcher.action.http="HTTP" AdvSceneSwitcher.action.http="HTTP"
AdvSceneSwitcher.action.http.legacy="HTTP (legacy)"
AdvSceneSwitcher.action.http.setHeaders="Set headers" AdvSceneSwitcher.action.http.setHeaders="Set headers"
AdvSceneSwitcher.action.http.headers="Headers:" AdvSceneSwitcher.action.http.headers="Headers:"
AdvSceneSwitcher.action.http.addHeader="Add header" AdvSceneSwitcher.action.http.addHeader="Add header"
AdvSceneSwitcher.action.http.addHeader.name="Header name"
AdvSceneSwitcher.action.http.addHeader.value="Header value"
AdvSceneSwitcher.action.http.setParams="Set parameters"
AdvSceneSwitcher.action.http.params="Parameters:"
AdvSceneSwitcher.action.http.addParam.name="Parameter name"
AdvSceneSwitcher.action.http.addParam.value="Parameter value"
AdvSceneSwitcher.action.http.body="Message body:"
AdvSceneSwitcher.action.http.type.get="GET" AdvSceneSwitcher.action.http.type.get="GET"
AdvSceneSwitcher.action.http.type.post="POST" AdvSceneSwitcher.action.http.type.post="POST"
AdvSceneSwitcher.action.http.type.put="PUT"
AdvSceneSwitcher.action.http.type.patch="PATCH"
AdvSceneSwitcher.action.http.type.delete="DELETE"
AdvSceneSwitcher.action.http.layout.method="Send{{method}}to URL{{url}}at path{{path}}"
AdvSceneSwitcher.action.http.layout.contentType="Content type:{{contentType}}"
AdvSceneSwitcher.action.http.layout.timeout="Timeout:{{timeout}}seconds"
AdvSceneSwitcher.action.http.entry.line1="Send{{method}}to{{url}}" AdvSceneSwitcher.action.http.entry.line1="Send{{method}}to{{url}}"
AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds" AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds"
AdvSceneSwitcher.action.variable="Variable" AdvSceneSwitcher.action.variable="Variable"
AdvSceneSwitcher.action.variable.type.set="Set to fixed value" AdvSceneSwitcher.action.variable.type.set="Set to value"
AdvSceneSwitcher.action.variable.type.append="Append" AdvSceneSwitcher.action.variable.type.append="Append"
AdvSceneSwitcher.action.variable.type.appendVar="Append variable" AdvSceneSwitcher.action.variable.type.appendVar="Append variable"
AdvSceneSwitcher.action.variable.type.increment="Increment" AdvSceneSwitcher.action.variable.type.increment="Increment"
AdvSceneSwitcher.action.variable.type.decrement="Decrement" AdvSceneSwitcher.action.variable.type.decrement="Decrement"
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value" AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value (legacy)"
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value" AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value (legacy)"
AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer" AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer"
AdvSceneSwitcher.action.variable.type.subString="Set to substring of current value" AdvSceneSwitcher.action.variable.type.subString="Set to substring of current value"
AdvSceneSwitcher.action.variable.type.findAndReplace="Find and replace in current value" AdvSceneSwitcher.action.variable.type.findAndReplace="Find and replace in current value"
@@ -1072,6 +1096,7 @@ AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values" AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values"
AdvSceneSwitcher.action.variable.type.trim="Trim whitespace" AdvSceneSwitcher.action.variable.type.trim="Trim whitespace"
AdvSceneSwitcher.action.variable.type.changeCase="Change case" AdvSceneSwitcher.action.variable.type.changeCase="Change case"
AdvSceneSwitcher.action.variable.type.randomNumber="Generate random number"
AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase" AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase"
AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE" AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE"
AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized" AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized"
@@ -1086,6 +1111,7 @@ AdvSceneSwitcher.action.variable.findAndReplace.replace="Text to replace with"
AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip="Using \1, \2, etc. to represent first, second, etc. capture groups is supported." AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip="Using \1, \2, etc. to represent first, second, etc. capture groups is supported."
AdvSceneSwitcher.action.variable.subString.begin="Beginning" AdvSceneSwitcher.action.variable.subString.begin="Beginning"
AdvSceneSwitcher.action.variable.subString.all="All" AdvSceneSwitcher.action.variable.subString.all="All"
AdvSceneSwitcher.action.variable.generateInteger="Only generate integers"
AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!" AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!" AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
@@ -1098,7 +1124,10 @@ AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMat
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}" AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}" AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}" AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.entry.randomNumber="Generate random number in range from{{randomNumberStart}}to{{randomNumberEnd}}"
AdvSceneSwitcher.action.projector="Projector" AdvSceneSwitcher.action.projector="Projector"
AdvSceneSwitcher.action.projector.action.open="Open"
AdvSceneSwitcher.action.projector.action.close="Close"
AdvSceneSwitcher.action.projector.type.source="Source" AdvSceneSwitcher.action.projector.type.source="Source"
AdvSceneSwitcher.action.projector.type.scene="Scene" AdvSceneSwitcher.action.projector.type.scene="Scene"
AdvSceneSwitcher.action.projector.type.preview="Preview" AdvSceneSwitcher.action.projector.type.preview="Preview"
@@ -1107,8 +1136,9 @@ AdvSceneSwitcher.action.projector.type.multiview="Multiview"
AdvSceneSwitcher.action.projector.display="Display" AdvSceneSwitcher.action.projector.display="Display"
AdvSceneSwitcher.action.projector.windowed="Windowed" AdvSceneSwitcher.action.projector.windowed="Windowed"
AdvSceneSwitcher.action.projector.fullscreen="Fullscreen" AdvSceneSwitcher.action.projector.fullscreen="Fullscreen"
AdvSceneSwitcher.action.projector.entry="Open{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}" AdvSceneSwitcher.action.projector.entry.close="{{actions}}projector with name{{projectorWindowName}}{{regex}}"
AdvSceneSwitcher.action.projector.entry.monitor="on{{monitors}}" AdvSceneSwitcher.action.projector.entry.open.windowed="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.open.fullscreen="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}on{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI" AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Send message to{{device}}:" AdvSceneSwitcher.action.midi.entry="Send message to{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on{{listenDevices}}:{{listenButton}}" AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on{{listenDevices}}:{{listenButton}}"
@@ -1128,10 +1158,15 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Create stream clip"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Send chat announcement" AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Send chat announcement"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Enable chat's emote-only mode" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Enable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote-only mode" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Send chat message" AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}" AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}" AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="Enter title" AdvSceneSwitcher.action.twitch.title.title="Enter title"
AdvSceneSwitcher.action.twitch.marker.description="Describe marker" AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip" AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
@@ -1141,6 +1176,8 @@ AdvSceneSwitcher.action.twitch.announcement.blue="Blue"
AdvSceneSwitcher.action.twitch.announcement.green="Green" AdvSceneSwitcher.action.twitch.announcement.green="Green"
AdvSceneSwitcher.action.twitch.announcement.orange="Orange" AdvSceneSwitcher.action.twitch.announcement.orange="Orange"
AdvSceneSwitcher.action.twitch.announcement.purple="Purple" AdvSceneSwitcher.action.twitch.announcement.purple="Purple"
AdvSceneSwitcher.action.twitch.user.getInfo.queryType.id="User Id"
AdvSceneSwitcher.action.twitch.user.getInfo.queryType.login="User login"
AdvSceneSwitcher.action.clipboard="Clipboard" AdvSceneSwitcher.action.clipboard="Clipboard"
AdvSceneSwitcher.action.clipboard.type.copy.text="Copy text" AdvSceneSwitcher.action.clipboard.type.copy.text="Copy text"
AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image" AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image"
@@ -1205,7 +1242,7 @@ AdvSceneSwitcher.validation.text.maxLength="Only %1 characters are allowed in th
AdvSceneSwitcher.variable.select="--select variable--" AdvSceneSwitcher.variable.select="--select variable--"
AdvSceneSwitcher.variable.add="Add new variable" AdvSceneSwitcher.variable.add="Add new variable"
AdvSceneSwitcher.variable.configure="Configure variable settings" AdvSceneSwitcher.variable.configure="Configure variable settings"
AdvSceneSwitcher.variable.invalid="Invalid varialbe selection" AdvSceneSwitcher.variable.invalid="Invalid variable selection"
AdvSceneSwitcher.variable.name="Name:" AdvSceneSwitcher.variable.name="Name:"
AdvSceneSwitcher.variable.value="Current value:" AdvSceneSwitcher.variable.value="Current value:"
AdvSceneSwitcher.variable.save="Save / load behavior" AdvSceneSwitcher.variable.save="Save / load behavior"
@@ -1584,12 +1621,21 @@ AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived="Shoutout time"
AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout." AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout."
AdvSceneSwitcher.tempVar.twitch.id.reward="Reward ID" AdvSceneSwitcher.tempVar.twitch.id.reward="Reward ID"
AdvSceneSwitcher.tempVar.twitch.id.redemption="Redemption ID"
AdvSceneSwitcher.tempVar.twitch.user_id.reward="User ID redeeming the reward" AdvSceneSwitcher.tempVar.twitch.user_id.reward="User ID redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming the reward" AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_name.reward="User name redeeming the reward" AdvSceneSwitcher.tempVar.twitch.user_name.reward="User name redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward" AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward"
AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information" AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information (JSON)"
AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed, at the time it was redeemed." AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed in JSON form, at the time it was redeemed."
AdvSceneSwitcher.tempVar.twitch.reward.id.reward="Reward ID"
AdvSceneSwitcher.tempVar.twitch.reward.id.reward.description="The ID that uniquely identifies the reward."
AdvSceneSwitcher.tempVar.twitch.reward.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.reward.title.reward.description="The rewards title."
AdvSceneSwitcher.tempVar.twitch.reward.prompt.reward="Reward prompt"
AdvSceneSwitcher.tempVar.twitch.reward.prompt.reward.description="The prompt displayed to the viewer if user input is required."
AdvSceneSwitcher.tempVar.twitch.reward.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.reward.cost.reward.description="The rewards cost, in Channel Points."
AdvSceneSwitcher.tempVar.twitch.status.reward="Reward status" AdvSceneSwitcher.tempVar.twitch.status.reward="Reward status"
AdvSceneSwitcher.tempVar.twitch.status.reward.description="Defaults to unfulfilled. Possible values are unknown, unfulfilled, fulfilled, and canceled." AdvSceneSwitcher.tempVar.twitch.status.reward.description="Defaults to unfulfilled. Possible values are unknown, unfulfilled, fulfilled, and canceled."
AdvSceneSwitcher.tempVar.twitch.redeemed_at.reward="Redemption time" AdvSceneSwitcher.tempVar.twitch.redeemed_at.reward="Redemption time"
@@ -1604,22 +1650,37 @@ AdvSceneSwitcher.tempVar.twitch.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.cost.reward="Reward cost" AdvSceneSwitcher.tempVar.twitch.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.prompt.reward="Reward description" AdvSceneSwitcher.tempVar.twitch.prompt.reward="Reward description"
AdvSceneSwitcher.tempVar.twitch.is_user_input_required.reward="Reward requires user input" AdvSceneSwitcher.tempVar.twitch.is_user_input_required.reward="Reward requires user input"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Reward maximum per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream"
AdvSceneSwitcher.tempVar.twitch.background_color.reward="Reward background color" AdvSceneSwitcher.tempVar.twitch.background_color.reward="Reward background color"
AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image" AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image (JSON)"
AdvSceneSwitcher.tempVar.twitch.image.reward.description="Set of custom images of 1x, 2x and 4x sizes for the reward. Can be null if no images have been uploaded." AdvSceneSwitcher.tempVar.twitch.image.reward.description="Set of custom images of 1x, 2x and 4x sizes for the reward. Can be null if no images have been uploaded."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image" AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward="Reward image URL"
AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward.description="The URL to the custom image for the reward. This field is null if the broadcaster didnt upload images."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image (JSON)"
AdvSceneSwitcher.tempVar.twitch.default_image.reward.description="Set of default images of 1x, 2x and 4x sizes for the reward." AdvSceneSwitcher.tempVar.twitch.default_image.reward.description="Set of default images of 1x, 2x and 4x sizes for the reward."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Reward cooldown" AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward="Reward default image URL"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds." AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward.description="The URL to the default image for the reward."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward="Redemption count" AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward="Redemption count"
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled." AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled." AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Maximum redemptions per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward="Maximum redemptions per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward="Maximum redemptions per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward.description="The maximum number of redemptions allowed per live stream."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward="Maximum redemptions per user per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per user per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward="Maximum redemptions per user per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward.description="The settings used to determine whether to apply a maximum to the number of redemptions allowed per user per live stream."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Global cooldown (JSON)"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward="Global cooldown enabled"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward.description="A Boolean value that determines whether to apply a cooldown period. Is true if a cooldown period is enabled."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward="Global cooldown"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward.description="The cooldown period, in seconds."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.charity_name="Charity name" AdvSceneSwitcher.tempVar.twitch.charity_name="Charity name"
AdvSceneSwitcher.tempVar.twitch.charity_description="Charity description" AdvSceneSwitcher.tempVar.twitch.charity_description="Charity description"
@@ -1788,6 +1849,24 @@ AdvSceneSwitcher.tempVar.twitch.to_broadcaster_user_name.raid.description="The b
AdvSceneSwitcher.tempVar.twitch.viewers.raid="Raid participants" AdvSceneSwitcher.tempVar.twitch.viewers.raid="Raid participants"
AdvSceneSwitcher.tempVar.twitch.viewers.raid.description="The number of viewers in the raid." AdvSceneSwitcher.tempVar.twitch.viewers.raid.description="The number of viewers in the raid."
AdvSceneSwitcher.tempVar.twitch.id.user.getInfo="User ID"
AdvSceneSwitcher.tempVar.twitch.id.user.getInfo.description="The user ID of the queried account."
AdvSceneSwitcher.tempVar.twitch.login.user.getInfo="User login"
AdvSceneSwitcher.tempVar.twitch.login.user.getInfo.description="The user login of the queried account."
AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo="User name"
AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo.description="The display name of the queried account."
AdvSceneSwitcher.tempVar.twitch.type.user.getInfo="User type"
AdvSceneSwitcher.tempVar.twitch.type.user.getInfo.description="The type of user. Possible values are:\n admin — Twitch administrator\n global_mod\n staff — Twitch staff\n \"\" — Normal user"
AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo="Broadcaster type"
AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo.description="The type of broadcaster. Possible values are:\n affiliate — An affiliate broadcaster\n partner — A partner broadcaster\n \"\" — A normal broadcaster"
AdvSceneSwitcher.tempVar.twitch.description.user.getInfo="Description"
AdvSceneSwitcher.tempVar.twitch.description.user.getInfo.description="The user's description of their channel."
AdvSceneSwitcher.tempVar.twitch.profile_image_url.user.getInfo="Profile image URL"
AdvSceneSwitcher.tempVar.twitch.offline_image_url.user.getInfo="Offline image URL"
AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo="Account creation date"
AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo.description="The UTC date and time that the user's account was created. The timestamp is in RFC3339 format."
AdvSceneSwitcher.tempVar.audio.output_volume="Output volume" AdvSceneSwitcher.tempVar.audio.output_volume="Output volume"
AdvSceneSwitcher.tempVar.audio.output_volume.description="The volume the audio source is outputting." AdvSceneSwitcher.tempVar.audio.output_volume.description="The volume the audio source is outputting."
AdvSceneSwitcher.tempVar.audio.configured_volume="Configured volume" AdvSceneSwitcher.tempVar.audio.configured_volume="Configured volume"
@@ -1877,8 +1956,11 @@ AdvSceneSwitcher.tempVar.display.height.description="The height of the display w
AdvSceneSwitcher.tempVar.streaming.keyframeInterval="Stream keyframe interval" AdvSceneSwitcher.tempVar.streaming.keyframeInterval="Stream keyframe interval"
AdvSceneSwitcher.tempVar.streaming.keyframeInterval.description="Stream keyframe interval configured in the OBS settings." AdvSceneSwitcher.tempVar.streaming.keyframeInterval.description="Stream keyframe interval configured in the OBS settings."
AdvSceneSwitcher.tempVar.streaming.streamKey="Stream key"
AdvSceneSwitcher.tempVar.streaming.durationSeconds="Stream duration" AdvSceneSwitcher.tempVar.streaming.durationSeconds="Stream duration"
AdvSceneSwitcher.tempVar.streaming.durationSeconds.description="Seconds passed since the stream was started.\nThis value will be zero if the stream is stopped." AdvSceneSwitcher.tempVar.streaming.durationSeconds.description="Seconds passed since the stream was started.\nThis value will be zero if the stream is stopped."
AdvSceneSwitcher.tempVar.streaming.serviceName="Service"
AdvSceneSwitcher.tempVar.streaming.serviceName.description="The name of the streaming service being streamed to (e.g. Twitch)"
AdvSceneSwitcher.tempVar.clipboard.text="Clipboard text" AdvSceneSwitcher.tempVar.clipboard.text="Clipboard text"
AdvSceneSwitcher.tempVar.clipboard.text.description="The text contained in the clipboard.\nWill be empty if the clipboard does not contain text." AdvSceneSwitcher.tempVar.clipboard.text.description="The text contained in the clipboard.\nWill be empty if the clipboard does not contain text."
@@ -1958,6 +2040,25 @@ AdvSceneSwitcher.tempVar.streamDeck.column="Key column"
AdvSceneSwitcher.tempVar.streamDeck.data="Data" AdvSceneSwitcher.tempVar.streamDeck.data="Data"
AdvSceneSwitcher.tempVar.streamDeck.data.description="The value of the \"Data\" field configured in the action on the Stream Deck side." AdvSceneSwitcher.tempVar.streamDeck.data.description="The value of the \"Data\" field configured in the action on the Stream Deck side."
AdvSceneSwitcher.tempVar.queue.size="Size"
AdvSceneSwitcher.tempVar.queue.running="Is running"
AdvSceneSwitcher.tempVar.queue.running.description="Returns \"true\" if the queue is started and \"false\" if it is stopped."
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath="Last screenshot path"
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath.description="The filesystem path to the last screenshot taken."
AdvSceneSwitcher.tempVar.gameCapture.title="Title"
AdvSceneSwitcher.tempVar.gameCapture.title.description="Window title of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.class="Class"
AdvSceneSwitcher.tempVar.gameCapture.class.description="Window class of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
AdvSceneSwitcher.tempVar.http.status="Status code"
AdvSceneSwitcher.tempVar.http.body="Message body"
AdvSceneSwitcher.tempVar.http.error="Error"
AdvSceneSwitcher.tempVar.http.error.description="Empty when no error occurred.\nOther possible values:\n\n * Could not establish connection\n * Failed to bind IP address\n * Failed to read connection\n * Failed to write connection\n * Maximum redirect count exceeded\n * Connection handling canceled\n * SSL connection failed\n * SSL certificate loading failed\n * SSL server verification failed\n * Unsupported HTTP multipart boundary characters\n * Compression failed\n * Connection timed out\n * Proxy connection failed\n * Unknown"
AdvSceneSwitcher.selectScene="--select scene--" AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene" AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene" AdvSceneSwitcher.selectCurrentScene="Current Scene"
@@ -1996,7 +2097,6 @@ AdvSceneSwitcher.settings.suffix.type.list=" (List)"
AdvSceneSwitcher.settings.suffix.type.color=" (Color)" AdvSceneSwitcher.settings.suffix.type.color=" (Color)"
AdvSceneSwitcher.settings.suffix.type.button=" (Button)" AdvSceneSwitcher.settings.suffix.type.button=" (Button)"
AdvSceneSwitcher.settings.suffix.type.font=" (Font)" AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
AdvSceneSwitcher.settings.suffix.type.editableList=" (Editable list)" AdvSceneSwitcher.settings.suffix.type.editableList=" (Editable list)"
AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)" AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
AdvSceneSwitcher.settings.suffix.type.group=" (Group)" AdvSceneSwitcher.settings.suffix.type.group=" (Group)"

View File

@@ -172,7 +172,6 @@ AdvSceneSwitcher.condition.stream.state.start="Transmisión en ejecución"
AdvSceneSwitcher.condition.stream.state.stop="Transmisión detenida" AdvSceneSwitcher.condition.stream.state.stop="Transmisión detenida"
AdvSceneSwitcher.condition.stream.state.starting="Inicio de transmisión" AdvSceneSwitcher.condition.stream.state.starting="Inicio de transmisión"
AdvSceneSwitcher.condition.stream.state.stopping="Detener transmisión" AdvSceneSwitcher.condition.stream.state.stopping="Detener transmisión"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Grabación" AdvSceneSwitcher.condition.record="Grabación"
AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución" AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución"
AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa" AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa"
@@ -407,7 +406,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover hacia abajo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover al principio" AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover al principio"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover al final" AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover al final"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición" 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}}{{sources2}}{{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}}{{settingSelection}}{{singleSettingValue}}" AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -439,7 +438,6 @@ AdvSceneSwitcher.action.random="Aleatorio"
AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)" AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)"
AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema" AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
AdvSceneSwitcher.action.screenshot="Captura de pantalla" AdvSceneSwitcher.action.screenshot="Captura de pantalla"
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
AdvSceneSwitcher.action.profile="Perfil" AdvSceneSwitcher.action.profile="Perfil"
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}" AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Colección de escenas" AdvSceneSwitcher.action.sceneCollection="Colección de escenas"

View File

@@ -597,7 +597,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Déplacer vers le bas"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Déplacer en haut" AdvSceneSwitcher.action.sceneOrder.type.moveTop="Déplacer en haut"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Déplacer en bas" AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Déplacer en bas"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Déplacer à la position" AdvSceneSwitcher.action.sceneOrder.type.movePosition="Déplacer à la position"
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{position}}" AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformation de l'élément de la scène" AdvSceneSwitcher.action.sceneTransform="Transformation de l'élément de la scène"
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform" AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
AdvSceneSwitcher.action.sceneTransform.type.reset="Réinitialiser la transformation" AdvSceneSwitcher.action.sceneTransform.type.reset="Réinitialiser la transformation"
@@ -647,7 +647,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Par défaut"
AdvSceneSwitcher.action.screenshot.save.custom="Personnalisé" AdvSceneSwitcher.action.screenshot.save.custom="Personnalisé"
AdvSceneSwitcher.action.screenshot.type.source="Source" AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="Scène" AdvSceneSwitcher.action.screenshot.type.scene="Scène"
AdvSceneSwitcher.action.screenshot.mainOutput="Sortie principale d'OBS"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Les sources ou les scènes qui ne sont pas toujours rendues peuvent entraîner des parties de captures d'écran vides." AdvSceneSwitcher.action.screenshot.blackscreenNote="Les sources ou les scènes qui ne sont pas toujours rendues peuvent entraîner des parties de captures d'écran vides."
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}{{variables}}" AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}{{variables}}"
AdvSceneSwitcher.action.profile="Profil" AdvSceneSwitcher.action.profile="Profil"
@@ -718,8 +717,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivue"
AdvSceneSwitcher.action.projector.display="Affichage" AdvSceneSwitcher.action.projector.display="Affichage"
AdvSceneSwitcher.action.projector.windowed="Fenêtré" AdvSceneSwitcher.action.projector.windowed="Fenêtré"
AdvSceneSwitcher.action.projector.fullscreen="Plein écran" AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
AdvSceneSwitcher.action.projector.entry="Ouvrir le projecteur{{windowTypes}}de{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="sur{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI" AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:" AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}" AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"

View File

@@ -925,7 +925,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="下へ移動"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="トップへ移動" AdvSceneSwitcher.action.sceneOrder.type.moveTop="トップへ移動"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="一番下へ移動" AdvSceneSwitcher.action.sceneOrder.type.moveBottom="一番下へ移動"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="ポジションへの移動" AdvSceneSwitcher.action.sceneOrder.type.movePosition="ポジションへの移動"
AdvSceneSwitcher.action.sceneOrder.entry="オン{{scenes}}{{actions}}{{sources}}{{position}}" AdvSceneSwitcher.action.sceneOrder.entry="オン{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="シーンアイテムの変形" AdvSceneSwitcher.action.sceneTransform="シーンアイテムの変形"
AdvSceneSwitcher.action.sceneTransform.type.manual="変形" AdvSceneSwitcher.action.sceneTransform.type.manual="変形"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="トランスフォーム設定を行う" AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="トランスフォーム設定を行う"
@@ -979,7 +979,6 @@ AdvSceneSwitcher.action.screenshot.save.default="デフォルト"
AdvSceneSwitcher.action.screenshot.save.custom="カスタム" AdvSceneSwitcher.action.screenshot.save.custom="カスタム"
; AdvSceneSwitcher.action.screenshot.type.source="Source" ; AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="シーン" AdvSceneSwitcher.action.screenshot.type.scene="シーン"
AdvSceneSwitcher.action.screenshot.mainOutput="OBSの主な出力"
AdvSceneSwitcher.action.screenshot.blackscreenNote="常にレンダリングされるわけではないソースやシーンにより、スクリーンショットの一部が空白のままになる場合があります。" AdvSceneSwitcher.action.screenshot.blackscreenNote="常にレンダリングされるわけではないソースやシーンにより、スクリーンショットの一部が空白のままになる場合があります。"
AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}{{variables}}の場所に保存します" AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}{{variables}}の場所に保存します"
AdvSceneSwitcher.action.profile="プロファイル" AdvSceneSwitcher.action.profile="プロファイル"
@@ -1068,8 +1067,6 @@ AdvSceneSwitcher.action.projector.type.multiview="マルチビュー"
AdvSceneSwitcher.action.projector.display="ディスプレイ" AdvSceneSwitcher.action.projector.display="ディスプレイ"
AdvSceneSwitcher.action.projector.windowed="ウィンドウ" AdvSceneSwitcher.action.projector.windowed="ウィンドウ"
AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン" AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン"
AdvSceneSwitcher.action.projector.entry="{{types}}{{scenes}}{{sources}}の{{windowTypes}}プロジェクターを開きます"
AdvSceneSwitcher.action.projector.entry.monitor="{{monitors}}上で"
; AdvSceneSwitcher.action.midi="MIDI" ; AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:" AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:"
AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}" AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}"
@@ -1089,10 +1086,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="ストリームクリップの
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="チャット通知の送信" AdvSceneSwitcher.action.twitch.type.chat.announcement.send="チャット通知の送信"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="チャットのエモート専用モードを有効にする" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="チャットのエモート専用モードを有効にする"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="チャットのエモート専用モードを無効にする" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="チャットのエモート専用モードを無効にする"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="チャットメッセージを送る" AdvSceneSwitcher.action.twitch.type.chat.sendMessage="チャットメッセージを送る"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません"
; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}" ; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}} でアカウント{{account}}{{actions}}を使用しています" AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力してください" AdvSceneSwitcher.action.twitch.title.title="タイトルを入力してください"
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明" AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します" AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"

View File

@@ -395,7 +395,6 @@ AdvSceneSwitcher.condition.stream.state.stop="Streaming parado"
AdvSceneSwitcher.condition.stream.state.starting="Iniciando o streaming" AdvSceneSwitcher.condition.stream.state.starting="Iniciando o streaming"
AdvSceneSwitcher.condition.stream.state.stopping="Parando o streaming" AdvSceneSwitcher.condition.stream.state.stopping="Parando o streaming"
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Intervalo de keyframe igual a" AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Intervalo de keyframe igual a"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Gravação" AdvSceneSwitcher.condition.record="Gravação"
AdvSceneSwitcher.condition.record.state.start="Gravação em execução" AdvSceneSwitcher.condition.record.state.start="Gravação em execução"
AdvSceneSwitcher.condition.record.state.pause="Gravação pausada" AdvSceneSwitcher.condition.record.state.pause="Gravação pausada"
@@ -907,7 +906,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover para baixo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover para o topo" AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover para o topo"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover para o fundo" AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover para o fundo"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover para posição" AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover para posição"
AdvSceneSwitcher.action.sceneOrder.entry="Em{{scenes}}{{actions}}{{sources}}{{position}}" AdvSceneSwitcher.action.sceneOrder.entry="Em{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformação de item de cena" AdvSceneSwitcher.action.sceneTransform="Transformação de item de cena"
AdvSceneSwitcher.action.sceneTransform.type.manual="Definir string de transformação" AdvSceneSwitcher.action.sceneTransform.type.manual="Definir string de transformação"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Definir configuração de transformação" AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Definir configuração de transformação"
@@ -961,7 +960,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Padrão"
AdvSceneSwitcher.action.screenshot.save.custom="Personalizado" AdvSceneSwitcher.action.screenshot.save.custom="Personalizado"
AdvSceneSwitcher.action.screenshot.type.source="Fonte" AdvSceneSwitcher.action.screenshot.type.source="Fonte"
AdvSceneSwitcher.action.screenshot.type.scene="Cena" AdvSceneSwitcher.action.screenshot.type.scene="Cena"
AdvSceneSwitcher.action.screenshot.mainOutput="Saída principal do OBS"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Fontes ou cenas, que nem sempre são renderizadas, podem resultar em algumas partes das capturas de tela permanecendo em branco." AdvSceneSwitcher.action.screenshot.blackscreenNote="Fontes ou cenas, que nem sempre são renderizadas, podem resultar em algumas partes das capturas de tela permanecendo em branco."
AdvSceneSwitcher.action.screenshot.entry="Captura de tela{{targetType}}{{sources}}{{scenes}}e salvar em{{saveType}}{{variables}}localização" AdvSceneSwitcher.action.screenshot.entry="Captura de tela{{targetType}}{{sources}}{{scenes}}e salvar em{{saveType}}{{variables}}localização"
AdvSceneSwitcher.action.profile="Perfil" AdvSceneSwitcher.action.profile="Perfil"
@@ -1050,8 +1048,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivisão"
AdvSceneSwitcher.action.projector.display="Exibição" AdvSceneSwitcher.action.projector.display="Exibição"
AdvSceneSwitcher.action.projector.windowed="Janela" AdvSceneSwitcher.action.projector.windowed="Janela"
AdvSceneSwitcher.action.projector.fullscreen="Tela cheia" AdvSceneSwitcher.action.projector.fullscreen="Tela cheia"
AdvSceneSwitcher.action.projector.entry="Abrir{{windowTypes}}projetor de{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="em{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI" AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:" AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}" AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}"
@@ -1071,10 +1067,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Criar clipe da transmissão"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Enviar anúncio de chat" AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Enviar anúncio de chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas emotes no chat" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Enviar mensagem de chat" AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}" AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}" AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título" AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador" AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe" AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"

View File

@@ -149,7 +149,6 @@ AdvSceneSwitcher.condition.stream.state.start="Yayın çalışıyor"
AdvSceneSwitcher.condition.stream.state.stop="Yayın durdu" AdvSceneSwitcher.condition.stream.state.stop="Yayın durdu"
AdvSceneSwitcher.condition.stream.state.starting="Yayın başlıyor" AdvSceneSwitcher.condition.stream.state.starting="Yayın başlıyor"
AdvSceneSwitcher.condition.stream.state.stopping="Yayın duruyor" AdvSceneSwitcher.condition.stream.state.stopping="Yayın duruyor"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Kayıt" AdvSceneSwitcher.condition.record="Kayıt"
AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor" AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı" AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
@@ -336,7 +335,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Aşağı indir"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="En üste taşı" AdvSceneSwitcher.action.sceneOrder.type.moveTop="En üste taşı"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Aşağıya taşı" AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Aşağıya taşı"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı" 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}}{{sources2}}{{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}}{{settingSelection}}{{singleSettingValue}}" AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -357,7 +356,6 @@ AdvSceneSwitcher.action.random="Rastgele"
AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)" AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)"
AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi" AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü" AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
AdvSceneSwitcher.action.profile="Profil" AdvSceneSwitcher.action.profile="Profil"
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}" AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu" AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"

File diff suppressed because it is too large Load Diff

13156
deps/exprtk/exprtk.hpp vendored

File diff suppressed because it is too large Load Diff

2
deps/libremidi vendored

View File

@@ -1,6 +1,5 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "backup.hpp" #include "backup.hpp"
#include "curl-helper.hpp"
#include "log-helper.hpp" #include "log-helper.hpp"
#include "macro-helpers.hpp" #include "macro-helpers.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
@@ -19,10 +18,15 @@
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <QAction> #include <QAction>
#include <QDirIterator> #include <QDirIterator>
#include <QLibrary>
#include <QMainWindow> #include <QMainWindow>
#include <QTextStream> #include <QTextStream>
#include <regex> #include <regex>
#ifdef _WIN32
#include <Windows.h>
#endif
namespace advss { namespace advss {
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr; AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
@@ -307,7 +311,7 @@ void SwitcherData::Thread()
} }
} }
ResetForNextInterval(); RunIntervalResetSteps();
if (match) { if (match) {
if (macroMatch) { if (macroMatch) {
@@ -357,14 +361,6 @@ void SwitcherData::SetPreconditions()
InvalidateMacroTempVarValues(); InvalidateMacroTempVarValues();
} }
void SwitcherData::ResetForNextInterval()
{
// Plugin reset functions
for (const auto &func : resetIntervalSteps) {
func();
}
}
bool SwitcherData::CheckForMatch(OBSWeakSource &scene, bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
OBSWeakSource &transition, int &linger, OBSWeakSource &transition, int &linger,
bool &setPrevSceneAfterLinger, bool &setPrevSceneAfterLinger,
@@ -445,7 +441,7 @@ void AutoStartActionQueues();
void SwitcherData::Start() void SwitcherData::Start()
{ {
if (!(th && th->isRunning())) { if (!(th && th->isRunning())) {
ResetForNextInterval(); RunIntervalResetSteps();
ResetMacros(); ResetMacros();
AutoStartActionQueues(); AutoStartActionQueues();
@@ -453,10 +449,7 @@ void SwitcherData::Start()
th = new SwitcherThread(); th = new SwitcherThread();
th->start((QThread::Priority)threadPriority); th->start((QThread::Priority)threadPriority);
// Will be overwritten quickly but might be useful RunStartSteps();
writeToStatusFile("Advanced Scene Switcher running");
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
nullptr);
} }
if (showSystemTrayNotifications) { if (showSystemTrayNotifications) {
@@ -483,11 +476,7 @@ void SwitcherData::Stop()
th->wait(); th->wait();
delete th; delete th;
th = nullptr; th = nullptr;
writeToStatusFile("Advanced Scene Switcher stopped"); RunStopSteps();
if (!obsIsShuttingDown) {
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
nullptr);
}
} }
if (showSystemTrayNotifications) { if (showSystemTrayNotifications) {
@@ -735,7 +724,7 @@ void OpenSettingsWindow()
} }
} }
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable) void AdvSceneSwitcher::HighlightMacroSettingsButton(bool enable)
{ {
static QObject *highlight = nullptr; static QObject *highlight = nullptr;
if ((highlight && enable) || (!highlight && !enable)) { if ((highlight && enable) || (!highlight && !enable)) {
@@ -755,13 +744,13 @@ void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
highlight = HighlightWidget(ui->macroSettings, Qt::green); highlight = HighlightWidget(ui->macroSettings, Qt::green);
} }
void HighligthMacroSettingsButton(bool enable) void HighlightMacroSettingsButton(bool enable)
{ {
auto window = GetSettingsWindow(); auto window = GetSettingsWindow();
if (!window) { if (!window) {
return; return;
} }
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton( static_cast<AdvSceneSwitcher *>(window)->HighlightMacroSettingsButton(
enable); enable);
} }

View File

@@ -97,7 +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); void HighlightMacroSettingsButton(bool enable = true);
public slots: public slots:
void on_macroAdd_clicked(); void on_macroAdd_clicked();
@@ -386,6 +386,6 @@ private:
}; };
void OpenSettingsWindow(); void OpenSettingsWindow();
void HighligthMacroSettingsButton(bool enable); void HighlightMacroSettingsButton(bool enable);
} // namespace advss } // namespace advss

View File

@@ -344,6 +344,10 @@ void AdvSceneSwitcher::on_importSettings_clicked()
return; return;
} }
// We have to make sure to that no macro is currently being edited while
// the new settings are loaded
ui->macros->clearSelection();
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
switcher->LoadSettings(obj); switcher->LoadSettings(obj);
switcher->lastImportPath = path.toStdString(); switcher->lastImportPath = path.toStdString();

View File

@@ -21,6 +21,29 @@ bool FileSwitch::pause = false;
static QObject *addPulse = nullptr; static QObject *addPulse = nullptr;
static std::hash<std::string> strHash; static std::hash<std::string> strHash;
static void writeToStatusFile(const QString &msg)
{
if (!GetSwitcher() || !GetSwitcher()->fileIO.writeEnabled ||
GetSwitcher()->fileIO.writePath.empty()) {
return;
}
QFile file(QString::fromStdString(GetSwitcher()->fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << msg << Qt::endl;
}
file.close();
}
static bool _ = []() {
AddStartStep(
[]() { writeToStatusFile("Advanced Scene Switcher running"); });
AddStopStep(
[]() { writeToStatusFile("Advanced Scene Switcher stopped"); });
return true;
}();
void AdvSceneSwitcher::on_browseButton_clicked() void AdvSceneSwitcher::on_browseButton_clicked()
{ {
QString path = QFileDialog::getOpenFileName( QString path = QFileDialog::getOpenFileName(
@@ -113,24 +136,6 @@ void SwitcherData::writeSceneInfoToFile()
} }
} }
void SwitcherData::writeToStatusFile(const QString &msg)
{
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
return;
}
QFile file(QString::fromStdString(fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream << msg << "\n";
#else
stream << msg << Qt::endl;
#endif
}
file.close();
}
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene, bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
OBSWeakSource &transition) OBSWeakSource &transition)
{ {

View File

@@ -384,7 +384,11 @@ static void getProcessListProcps2(QStringList &processes)
return; return;
} }
while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) { while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) {
#ifdef PROCPS2_USE_INFO
auto cmd = PIDS_VAL(0, str, stack, info); auto cmd = PIDS_VAL(0, str, stack, info);
#else
auto cmd = PIDS_VAL(0, str, stack);
#endif
QString procName(cmd); QString procName(cmd);
if (!procName.isEmpty() && !processes.contains(procName)) { if (!procName.isEmpty() && !processes.contains(procName)) {
processes << procName; processes << procName;

View File

@@ -17,6 +17,9 @@ static inline void populateActionSelection(QComboBox *list)
QString entry(obs_module_text(action._name.c_str())); QString entry(obs_module_text(action._name.c_str()));
if (list->findText(entry) == -1) { if (list->findText(entry) == -1) {
list->addItem(entry); list->addItem(entry);
qobject_cast<QListView *>(list->view())
->setRowHidden(list->count() - 1,
action._hidden);
} else { } else {
blog(LOG_WARNING, blog(LOG_WARNING,
"did not insert duplicate action entry with name \"%s\"", "did not insert duplicate action entry with name \"%s\"",

View File

@@ -12,6 +12,7 @@ struct MacroActionInfo {
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr; std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
CreateActionWidget _createWidget = nullptr; CreateActionWidget _createWidget = nullptr;
std::string _name; std::string _name;
bool _hidden = false;
}; };
class MacroActionFactory { class MacroActionFactory {

View File

@@ -309,11 +309,11 @@ MacroActionMacroEdit::MacroActionMacroEdit(
_loading = false; _loading = false;
} }
void HighligthMacroSettingsButton(bool enable); void HighlightMacroSettingsButton(bool enable);
MacroActionMacroEdit::~MacroActionMacroEdit() MacroActionMacroEdit::~MacroActionMacroEdit()
{ {
HighligthMacroSettingsButton(false); HighlightMacroSettingsButton(false);
} }
void MacroActionMacroEdit::UpdateEntryData() void MacroActionMacroEdit::UpdateEntryData()
@@ -480,7 +480,7 @@ void MacroActionMacroEdit::SetWidgetVisibility()
_inputs->setVisible(_entryData->_action == _inputs->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN && MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs); _entryData->_runOptions.setInputs);
HighligthMacroSettingsButton(_entryData->_action == HighlightMacroSettingsButton(_entryData->_action ==
MacroActionMacro::Action::RUN && MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs && _entryData->_runOptions.setInputs &&
!_inputs->HasInputsToSet()); !_inputs->HasInputsToSet());

View File

@@ -6,14 +6,17 @@
namespace advss { namespace advss {
MacroActionScript::MacroActionScript(Macro *m, const std::string &id, MacroActionScript::MacroActionScript(
const OBSData &defaultSettings, Macro *m, const std::string &id, const OBSData &defaultSettings,
const std::string &propertiesSignalName, const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &triggerSignalName,
const std::string &completionSignal) const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: MacroAction(m), : MacroAction(m),
MacroSegmentScript(defaultSettings, propertiesSignalName, MacroSegmentScript(defaultSettings, propertiesSignalName,
triggerSignal, completionSignal), triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName),
_id(id) _id(id)
{ {
} }
@@ -89,4 +92,29 @@ void MacroActionScript::WaitForCompletion() const
} }
} }
void MacroActionScript::RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText)
{
AddTempvar(variableId, name, helpText);
}
void MacroActionScript::DeregisterAllTempVarsHelper()
{
MacroSegment::SetupTempVars();
}
void MacroActionScript::SetTempVarValueHelper(const std::string &variableId,
const std::string &value)
{
MacroAction::SetTempVarValue(variableId, value);
}
void MacroActionScript::SetupTempVars()
{
// This just exists so MacroSegment::SetupTempVars() is not called.
// We want the ScriptHandler to handle the registration and clearing of
// the temp vars.
}
} // namespace advss } // namespace advss

View File

@@ -10,8 +10,10 @@ public:
MacroActionScript(Macro *m, const std::string &id, MacroActionScript(Macro *m, const std::string &id,
const OBSData &defaultSettings, const OBSData &defaultSettings,
const std::string &propertiesSignalName, const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &triggerSignalName,
const std::string &signalComplete); const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName);
MacroActionScript(const advss::MacroActionScript &); MacroActionScript(const advss::MacroActionScript &);
bool PerformAction(); bool PerformAction();
void LogAction() const; void LogAction() const;
@@ -22,6 +24,13 @@ public:
private: private:
void WaitForCompletion() const; void WaitForCompletion() const;
void RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText);
void DeregisterAllTempVarsHelper();
void SetTempVarValueHelper(const std::string &variableId,
const std::string &value);
void SetupTempVars();
std::string _id = ""; std::string _id = "";
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -28,8 +28,8 @@ public:
int GetSegmentIndexValue() const; int GetSegmentIndexValue() const;
void ResolveVariablesToFixedValues(); void ResolveVariablesToFixedValues();
enum class Type { enum class Action {
SET_FIXED_VALUE, SET_VALUE,
APPEND, APPEND,
APPEND_VAR, APPEND_VAR,
INCREMENT, INCREMENT,
@@ -52,18 +52,19 @@ public:
SWAP_VALUES, SWAP_VALUES,
TRIM, TRIM,
CHANGE_CASE, CHANGE_CASE,
RANDOM_NUMBER,
}; };
Type _type = Type::SET_FIXED_VALUE; Action _action = Action::SET_VALUE;
std::weak_ptr<Variable> _variable; std::weak_ptr<Variable> _variable;
std::weak_ptr<Variable> _variable2; std::weak_ptr<Variable> _variable2;
StringVariable _strValue = ""; StringVariable _strValue = "";
double _numValue = 0; DoubleVariable _numValue = 0;
int _subStringStart = 0; IntVariable _subStringStart = 0;
int _subStringSize = 0; IntVariable _subStringSize = 0;
RegexConfig _subStringRegex = RegexConfig::PartialMatchRegexConfig(); RegexConfig _subStringRegex = RegexConfig::PartialMatchRegexConfig();
std::string _regexPattern = ".*"; std::string _regexPattern = ".*";
int _regexMatchIdx = 0; IntVariable _regexMatchIdx = 0;
RegexConfig _findRegex; RegexConfig _findRegex;
StringVariable _findStr = obs_module_text( StringVariable _findStr = obs_module_text(
"AdvSceneSwitcher.action.variable.findAndReplace.find"); "AdvSceneSwitcher.action.variable.findAndReplace.find");
@@ -99,6 +100,9 @@ public:
}; };
CaseType _caseType = CaseType::LOWER_CASE; CaseType _caseType = CaseType::LOWER_CASE;
DoubleVariable _randomNumberStart = 0;
DoubleVariable _randomNumberEnd = 100;
bool _generateInteger = true;
private: private:
void DecrementCurrentSegmentVariableRef(); void DecrementCurrentSegmentVariableRef();
@@ -108,6 +112,7 @@ private:
void HandleMathExpression(Variable *); void HandleMathExpression(Variable *);
void HandleCaseChange(Variable *); void HandleCaseChange(Variable *);
void SetToSceneItemName(Variable *); void SetToSceneItemName(Variable *);
void GenerateRandomNumber(Variable *);
std::weak_ptr<MacroSegment> _macroSegment; std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1; int _segmentIdxLoadValue = -1;
@@ -136,15 +141,15 @@ private slots:
void Variable2Changed(const QString &); void Variable2Changed(const QString &);
void ActionChanged(int); void ActionChanged(int);
void StrValueChanged(); void StrValueChanged();
void NumValueChanged(double); void NumValueChanged(const NumberVariable<double> &value);
void SegmentIndexChanged(const IntVariable &); void SegmentIndexChanged(const IntVariable &);
void UpdateSegmentVariableValue(); void UpdateSegmentVariableValue();
void MacroSegmentOrderChanged(); void MacroSegmentOrderChanged();
void SubStringStartChanged(int val); void SubStringStartChanged(const NumberVariable<int> &start);
void SubStringSizeChanged(int val); void SubStringSizeChanged(const NumberVariable<int> &size);
void SubStringRegexChanged(const RegexConfig &conf); void SubStringRegexChanged(const RegexConfig &conf);
void RegexPatternChanged(); void RegexPatternChanged();
void RegexMatchIdxChanged(int val); void RegexMatchIdxChanged(const NumberVariable<int> &index);
void FindStrValueChanged(); void FindStrValueChanged();
void FindRegexChanged(const RegexConfig &conf); void FindRegexChanged(const RegexConfig &conf);
void ReplaceStrValueChanged(); void ReplaceStrValueChanged();
@@ -161,6 +166,9 @@ private slots:
void StringLengthChanged(const NumberVariable<int> &); void StringLengthChanged(const NumberVariable<int> &);
void CharSelectionChanged(const QString &); void CharSelectionChanged(const QString &);
void CaseTypeChanged(int index); void CaseTypeChanged(int index);
void RandomNumberStartChanged(const NumberVariable<double> &);
void RandomNumberEndChanged(const NumberVariable<double> &);
void GenerateIntegerChanged(int);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -173,18 +181,18 @@ private:
VariableSelection *_variables2; VariableSelection *_variables2;
FilterComboBox *_actions; FilterComboBox *_actions;
VariableTextEdit *_strValue; VariableTextEdit *_strValue;
QDoubleSpinBox *_numValue; VariableDoubleSpinBox *_numValue;
MacroSegmentSelection *_segmentIdx; MacroSegmentSelection *_segmentIdx;
QLabel *_segmentValueStatus; QLabel *_segmentValueStatus;
ResizingPlainTextEdit *_segmentValue; ResizingPlainTextEdit *_segmentValue;
QVBoxLayout *_substringLayout; QVBoxLayout *_substringLayout;
QHBoxLayout *_subStringIndexEntryLayout; QHBoxLayout *_subStringIndexEntryLayout;
QHBoxLayout *_subStringRegexEntryLayout; QHBoxLayout *_subStringRegexEntryLayout;
QSpinBox *_subStringStart; VariableSpinBox *_subStringStart;
QSpinBox *_subStringSize; VariableSpinBox *_subStringSize;
RegexConfigWidget *_substringRegex; RegexConfigWidget *_substringRegex;
ResizingPlainTextEdit *_regexPattern; ResizingPlainTextEdit *_regexPattern;
QSpinBox *_regexMatchIdx; VariableSpinBox *_regexMatchIdx;
QHBoxLayout *_findReplaceLayout; QHBoxLayout *_findReplaceLayout;
RegexConfigWidget *_findRegex; RegexConfigWidget *_findRegex;
VariableTextEdit *_findStr; VariableTextEdit *_findStr;
@@ -206,6 +214,10 @@ private:
VariableSpinBox *_stringLength; VariableSpinBox *_stringLength;
SingleCharSelection *_paddingCharSelection; SingleCharSelection *_paddingCharSelection;
FilterComboBox *_caseType; FilterComboBox *_caseType;
VariableDoubleSpinBox *_randomNumberStart;
VariableDoubleSpinBox *_randomNumberEnd;
QCheckBox *_generateInteger;
QVBoxLayout *_randomLayout;
QHBoxLayout *_entryLayout; QHBoxLayout *_entryLayout;
std::shared_ptr<MacroActionVariable> _entryData; std::shared_ptr<MacroActionVariable> _entryData;

View File

@@ -9,7 +9,6 @@
#include <QPushButton> #include <QPushButton>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QTimer> #include <QTimer>
#include <QListWidget>
namespace advss { namespace advss {

View File

@@ -27,6 +27,9 @@ bool MacroConditionQueue::CheckCondition()
return false; return false;
} }
SetTempVarValue("size", std::to_string(queue->Size()));
SetTempVarValue("running", queue->IsRunning());
switch (_condition) { switch (_condition) {
case Condition::STARTED: case Condition::STARTED:
return queue->IsRunning(); return queue->IsRunning();
@@ -63,6 +66,18 @@ std::string MacroConditionQueue::GetShortDesc() const
return GetActionQueueName(_queue); return GetActionQueueName(_queue);
} }
void MacroConditionQueue::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar("size",
obs_module_text("AdvSceneSwitcher.tempVar.queue.size"));
AddTempvar(
"running",
obs_module_text("AdvSceneSwitcher.tempVar.queue.running"),
obs_module_text(
"AdvSceneSwitcher.tempVar.queue.running.description"));
}
static inline void populateQueueTypeSelection(QComboBox *list) static inline void populateQueueTypeSelection(QComboBox *list)
{ {
for (const auto &[_, name] : conditionTypes) { for (const auto &[_, name] : conditionTypes) {
@@ -141,6 +156,7 @@ void MacroConditionQueueEdit::UpdateEntryData()
} }
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition)); _conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_queues->SetActionQueue(_entryData->_queue); _queues->SetActionQueue(_entryData->_queue);
_size->SetValue(_entryData->_size);
SetWidgetVisibility(); SetWidgetVisibility();
} }

View File

@@ -32,6 +32,8 @@ public:
IntVariable _size = 1; IntVariable _size = 1;
private: private:
void SetupTempVars();
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };

View File

@@ -8,10 +8,14 @@ namespace advss {
MacroConditionScript::MacroConditionScript( MacroConditionScript::MacroConditionScript(
Macro *m, const std::string &id, const OBSData &defaultSettings, Macro *m, const std::string &id, const OBSData &defaultSettings,
const std::string &propertiesSignalName, const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &completionSignal) const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: MacroCondition(m), : MacroCondition(m),
MacroSegmentScript(defaultSettings, propertiesSignalName, MacroSegmentScript(defaultSettings, propertiesSignalName,
triggerSignal, completionSignal), triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName),
_id(id) _id(id)
{ {
} }
@@ -76,4 +80,29 @@ void MacroConditionScript::WaitForCompletion() const
} }
} }
void MacroConditionScript::RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText)
{
AddTempvar(variableId, name, helpText);
}
void MacroConditionScript::DeregisterAllTempVarsHelper()
{
MacroSegment::SetupTempVars();
}
void MacroConditionScript::SetTempVarValueHelper(const std::string &variableId,
const std::string &value)
{
MacroCondition::SetTempVarValue(variableId, value);
}
void MacroConditionScript::SetupTempVars()
{
// This just exists so MacroSegment::SetupTempVars() is not called.
// We want the ScriptHandler to handle the registration and clearing of
// the temp vars.
}
} // namespace advss } // namespace advss

View File

@@ -10,8 +10,10 @@ public:
MacroConditionScript(Macro *m, const std::string &id, MacroConditionScript(Macro *m, const std::string &id,
const OBSData &defaultSettings, const OBSData &defaultSettings,
const std::string &propertiesSignalName, const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &triggerSignalName,
const std::string &signalComplete); const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName);
MacroConditionScript(const advss::MacroConditionScript &); MacroConditionScript(const advss::MacroConditionScript &);
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
@@ -20,6 +22,13 @@ public:
private: private:
void WaitForCompletion() const; void WaitForCompletion() const;
void RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText);
void DeregisterAllTempVarsHelper();
void SetTempVarValueHelper(const std::string &variableId,
const std::string &value);
void SetupTempVars();
std::string _id = ""; std::string _id = "";
}; };

View File

@@ -67,9 +67,7 @@ void SetMacroSwitchedScene(bool value)
{ {
static bool setupDone = false; static bool setupDone = false;
if (!setupDone) { if (!setupDone) {
// Will always be called with switcher lock already held AddIntervalResetStep([]() { macroSceneSwitched = false; });
AddIntervalResetStep([]() { macroSceneSwitched = false; },
false);
setupDone = true; setupDone = true;
} }
macroSceneSwitched = value; macroSceneSwitched = value;
@@ -109,6 +107,14 @@ bool MacroWasPausedSince(
return macro ? macro->WasPausedSince(time) : false; return macro ? macro->WasPausedSince(time) : false;
} }
bool MacroWasCheckedSinceLastStart(Macro *macro)
{
if (!macro) {
return false;
}
return macro->LastConditionCheckTime().time_since_epoch().count() != 0;
}
void AddMacroHelperThread(Macro *macro, std::thread &&newThread) void AddMacroHelperThread(Macro *macro, std::thread &&newThread)
{ {
if (!macro) { if (!macro) {

View File

@@ -51,6 +51,7 @@ EXPORT bool MacroIsPaused(Macro *);
EXPORT bool EXPORT bool
MacroWasPausedSince(Macro *, MacroWasPausedSince(Macro *,
const std::chrono::high_resolution_clock::time_point &); const std::chrono::high_resolution_clock::time_point &);
EXPORT bool MacroWasCheckedSinceLastStart(Macro *);
EXPORT void AddMacroHelperThread(Macro *, std::thread &&); EXPORT void AddMacroHelperThread(Macro *, std::thread &&);

View File

@@ -181,15 +181,7 @@ void MacroInputEdit::SetInputVariablesAndValues(
_variables = inputs; _variables = inputs;
_values = values; _values = values;
if ((size_t)_values.size() < _variables._inputVariables.size()) { 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()); _values.resize(_variables._inputVariables.size());
#endif
} }
SetupWidgets(); SetupWidgets();
} }
@@ -199,7 +191,7 @@ bool MacroInputEdit::HasInputsToSet() const
return !_variables._inputVariables.empty(); return !_variables._inputVariables.empty();
} }
void HighligthMacroSettingsButton(bool enable); void HighlightMacroSettingsButton(bool enable);
void MacroInputEdit::SetupWidgets() void MacroInputEdit::SetupWidgets()
{ {

View File

@@ -29,6 +29,10 @@ static constexpr std::string_view defaultSettingsParam = "default_settings";
static constexpr std::string_view propertiesSignalParam = static constexpr std::string_view propertiesSignalParam =
"properties_signal_name"; "properties_signal_name";
static constexpr std::string_view triggerSignalParam = "trigger_signal_name"; static constexpr std::string_view triggerSignalParam = "trigger_signal_name";
static constexpr std::string_view newInstanceSignalNameParam =
"new_instance_signal_name";
static constexpr std::string_view deletedInstanceSignalNameParam =
"deleted_instance_signal_name";
static std::string getRegisterScriptSegmentDeclString(const char *funcName) static std::string getRegisterScriptSegmentDeclString(const char *funcName)
{ {
@@ -72,16 +76,55 @@ static const std::string deregisterScriptConditionDeclString =
/* Script variables */ /* Script variables */
static constexpr std::string_view valueParam = "value"; static constexpr std::string_view valueParam = "value";
static constexpr std::string_view tempVarIdParam = "temp_var_id";
static constexpr std::string_view tempVarNameParam = "temp_var_name";
static constexpr std::string_view tempVarHelpParam = "temp_var_help";
static constexpr std::string_view getVariableValueFuncName = static constexpr std::string_view getVariableValueFuncName =
"advss_get_variable_value"; "advss_get_variable_value";
static constexpr std::string_view setVariableValueFuncName = static constexpr std::string_view setVariableValueFuncName =
"advss_set_variable_value"; "advss_set_variable_value";
static constexpr std::string_view registerTempVarFuncName =
"advss_register_temp_var";
static constexpr std::string_view deregisterAllTempVarsFuncName =
"advss_deregister_temp_vars";
static constexpr std::string_view setTempVarValueFuncName =
"advss_set_temp_var_value";
static const std::string getVariableValueDeclString = static const std::string getVariableValueDeclString =
std::string("bool ") + getVariableValueFuncName.data() + "(in string " + std::string("bool ") + getVariableValueFuncName.data() + "(in string " +
nameParam.data() + ", out string " + valueParam.data() + ")"; nameParam.data() + ", out string " + valueParam.data() + ")";
static const std::string setVariableValueDeclString = static const std::string setVariableValueDeclString =
std::string("bool ") + setVariableValueFuncName.data() + "(in string " + std::string("bool ") + setVariableValueFuncName.data() + "(in string " +
nameParam.data() + ", in string " + valueParam.data() + ")"; nameParam.data() + ", in string " + valueParam.data() + ")";
static const std::string registerTempVarDeclString =
std::string("bool ") + registerTempVarFuncName.data() + "(in string " +
tempVarIdParam.data() + ", in string " + tempVarNameParam.data() +
", in string " + tempVarHelpParam.data() + ", in int " +
GetInstanceIdParamName().data() + ")";
static const std::string deregisterAllTempVarsDeclString =
std::string("bool ") + deregisterAllTempVarsFuncName.data() +
"(in int " + GetInstanceIdParamName().data() + ")";
static const std::string setTempVarValueDeclString =
std::string("bool ") + setTempVarValueFuncName.data() + "(in string " +
tempVarIdParam.data() + ", in string " + valueParam.data() +
", in int " + GetInstanceIdParamName().data() + ")";
/* Plugin status */
static constexpr std::string_view stopSignalName = "advss_plugin_stopped";
static constexpr std::string_view startSignalName = "advss_plugin_started";
static constexpr std::string_view getRunningStatusFuncName =
"advss_plugin_running";
static constexpr std::string_view resetIntervalSignalName =
"advss_interval_reset";
static const std::string stopSignalDeclString =
std::string("void ") + stopSignalName.data() + "()";
static const std::string startSignalDeclString =
std::string("void ") + startSignalName.data() + "()";
static const std::string resetIntervalDeclString =
std::string("void ") + resetIntervalSignalName.data() + "()";
static const std::string getRunningStatusDeclString =
std::string("bool ") + getRunningStatusFuncName.data() + "()";
static bool setup(); static bool setup();
static bool setupDone = setup(); static bool setupDone = setup();
@@ -103,13 +146,42 @@ static bool setup()
&ScriptHandler::GetVariableValue, nullptr); &ScriptHandler::GetVariableValue, nullptr);
proc_handler_add(ph, setVariableValueDeclString.c_str(), proc_handler_add(ph, setVariableValueDeclString.c_str(),
&ScriptHandler::SetVariableValue, nullptr); &ScriptHandler::SetVariableValue, nullptr);
proc_handler_add(ph, registerTempVarDeclString.c_str(),
&ScriptHandler::RegisterTempVar, nullptr);
proc_handler_add(ph, deregisterAllTempVarsDeclString.c_str(),
&ScriptHandler::DeregisterAllTempVars, nullptr);
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
&ScriptHandler::SetTempVarValue, nullptr);
proc_handler_add(ph, getRunningStatusDeclString.c_str(),
&ScriptHandler::GetRunningStatus, nullptr);
auto sh = obs_get_signal_handler();
signal_handler_add(sh, stopSignalDeclString.c_str());
signal_handler_add(sh, startSignalDeclString.c_str());
signal_handler_add(sh, resetIntervalDeclString.c_str());
static constexpr auto triggerSignal = [](const std::string_view &name) {
auto sh = obs_get_signal_handler();
struct calldata data;
calldata_init(&data);
signal_handler_signal(sh, name.data(), &data);
calldata_free(&data);
};
AddIntervalResetStep([]() { triggerSignal(resetIntervalSignalName); });
AddStartStep([]() { triggerSignal(startSignalName); });
AddStopStep([]() { triggerSignal(stopSignalName); });
return true; return true;
} }
static void replaceWhitespace(std::string &string) static void replaceProblematicChars(std::string &string)
{ {
std::transform(string.begin(), string.end(), string.begin(), [](char c) { std::transform(string.begin(), string.end(), string.begin(), [](char c) {
return std::isspace(static_cast<unsigned char>(c)) ? '_' : c; if (std::isspace(static_cast<unsigned char>(c)) || c == '(' ||
c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
return '_';
}
return c;
}); });
} }
@@ -122,7 +194,7 @@ static std::string getTriggerSignal(const std::string &name,
const bool isAction) const bool isAction)
{ {
std::string signal = name; std::string signal = name;
replaceWhitespace(signal); replaceProblematicChars(signal);
signal += "_run"; signal += "_run";
signal += isAction ? "_action" : "_condition"; signal += isAction ? "_action" : "_condition";
return signal; return signal;
@@ -140,12 +212,32 @@ static std::string getPropertiesSignal(const std::string &name,
const bool isAction) const bool isAction)
{ {
std::string signal = name; std::string signal = name;
replaceWhitespace(signal); replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition"; signal += isAction ? "_action" : "_condition";
signal += "_get_properties"; signal += "_get_properties";
return signal; return signal;
} }
static std::string getNewInstanceSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition";
signal += "_new_instance";
return signal;
}
static std::string getDeletedInstanceSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition";
signal += "_deleted_instance";
return signal;
}
void ScriptHandler::RegisterScriptAction(void *, calldata_t *data) void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
{ {
const char *actionName; const char *actionName;
@@ -177,14 +269,19 @@ void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
auto triggerSignalName = getTriggerSignal(actionName, true); auto triggerSignalName = getTriggerSignal(actionName, true);
auto completionSignalName = getCompletionSignal(actionName, true); auto completionSignalName = getCompletionSignal(actionName, true);
auto propertiesSignalName = getPropertiesSignal(actionName, true); auto propertiesSignalName = getPropertiesSignal(actionName, true);
auto newInstanceSignalName = getNewInstanceSignal(actionName, true);
auto deletedInstanceSignalName =
getDeletedInstanceSignal(actionName, true);
const auto createScriptAction = const auto createScriptAction =
[id, defaultSettings, propertiesSignalName, triggerSignalName, [id, defaultSettings, propertiesSignalName, triggerSignalName,
completionSignalName]( completionSignalName, newInstanceSignalName,
deletedInstanceSignalName](
Macro *m) -> std::shared_ptr<MacroAction> { Macro *m) -> std::shared_ptr<MacroAction> {
return std::make_shared<MacroActionScript>( return std::make_shared<MacroActionScript>(
m, id, defaultSettings, propertiesSignalName, m, id, defaultSettings, propertiesSignalName,
triggerSignalName, completionSignalName); triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName);
}; };
if (!MacroActionFactory::Register(id, {createScriptAction, if (!MacroActionFactory::Register(id, {createScriptAction,
MacroSegmentScriptEdit::Create, MacroSegmentScriptEdit::Create,
@@ -202,9 +299,15 @@ void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
triggerSignalName.c_str()); triggerSignalName.c_str());
calldata_set_string(data, propertiesSignalParam.data(), calldata_set_string(data, propertiesSignalParam.data(),
propertiesSignalName.c_str()); propertiesSignalName.c_str());
calldata_set_string(data, newInstanceSignalNameParam.data(),
newInstanceSignalName.c_str());
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
deletedInstanceSignalName.c_str());
_actions.emplace(id, ScriptSegmentType(id, propertiesSignalName, _actions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
triggerSignalName, triggerSignalName,
completionSignalName)); completionSignalName,
newInstanceSignalName,
deletedInstanceSignalName));
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@@ -279,14 +382,19 @@ void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
auto triggerSignalName = getTriggerSignal(conditionName, false); auto triggerSignalName = getTriggerSignal(conditionName, false);
auto completionSignalName = getCompletionSignal(conditionName, false); auto completionSignalName = getCompletionSignal(conditionName, false);
auto propertiesSignalName = getPropertiesSignal(conditionName, false); auto propertiesSignalName = getPropertiesSignal(conditionName, false);
auto newInstanceSignalName = getNewInstanceSignal(conditionName, false);
auto deletedInstanceSignalName =
getDeletedInstanceSignal(conditionName, false);
const auto createScriptCondition = const auto createScriptCondition =
[id, defaultSettings, propertiesSignalName, triggerSignalName, [id, defaultSettings, propertiesSignalName, triggerSignalName,
completionSignalName]( completionSignalName, newInstanceSignalName,
deletedInstanceSignalName](
Macro *m) -> std::shared_ptr<MacroCondition> { Macro *m) -> std::shared_ptr<MacroCondition> {
return std::make_shared<MacroConditionScript>( return std::make_shared<MacroConditionScript>(
m, id, defaultSettings, propertiesSignalName, m, id, defaultSettings, propertiesSignalName,
triggerSignalName, completionSignalName); triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName);
}; };
if (!MacroConditionFactory::Register( if (!MacroConditionFactory::Register(
id, {createScriptCondition, MacroSegmentScriptEdit::Create, id, {createScriptCondition, MacroSegmentScriptEdit::Create,
@@ -304,9 +412,15 @@ void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
triggerSignalName.c_str()); triggerSignalName.c_str());
calldata_set_string(data, propertiesSignalParam.data(), calldata_set_string(data, propertiesSignalParam.data(),
propertiesSignalName.c_str()); propertiesSignalName.c_str());
calldata_set_string(data, newInstanceSignalNameParam.data(),
newInstanceSignalName.c_str());
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
deletedInstanceSignalName.c_str());
_conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName, _conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
triggerSignalName, triggerSignalName,
completionSignalName)); completionSignalName,
newInstanceSignalName,
deletedInstanceSignalName));
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@@ -399,6 +513,97 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
{
calldata_set_bool(data, "is_running", PluginIsRunning());
}
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
{
const char *variableId;
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
strlen(variableId) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarIdParam.data());
RETURN_FAILURE();
}
const char *name;
if (!calldata_get_string(data, tempVarNameParam.data(), &name) ||
strlen(name) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarNameParam.data());
RETURN_FAILURE();
}
const char *helpText;
if (!calldata_get_string(data, tempVarHelpParam.data(), &helpText)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarHelpParam.data());
RETURN_FAILURE();
}
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::RegisterTempVar(variableId, name, helpText,
instanceId);
RETURN_SUCCESS();
}
void ScriptHandler::DeregisterAllTempVars(void *, calldata_t *data)
{
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
deregisterAllTempVarsFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::DeregisterAllTempVars(instanceId);
RETURN_SUCCESS();
}
void ScriptHandler::SetTempVarValue(void *, calldata_t *data)
{
const char *variableId;
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
strlen(variableId) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
setTempVarValueFuncName.data(), tempVarIdParam.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!",
setTempVarValueFuncName.data(), valueParam.data());
RETURN_FAILURE();
}
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
setTempVarValueFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::SetTempVarValue(variableId, variableValue,
instanceId);
RETURN_SUCCESS();
}
bool ScriptHandler::ActionIdIsValid(const std::string &id) bool ScriptHandler::ActionIdIsValid(const std::string &id)
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
@@ -424,24 +629,39 @@ static std::string signalNameToPropertiesSignalDecl(const std::string &name)
static std::string signalNameToCompletionSignalDecl(const std::string &name) static std::string signalNameToCompletionSignalDecl(const std::string &name)
{ {
return std::string("bool ") + name + "(in int " + return std::string("bool ") + name + "(in int " +
GeCompletionIdParamName().data() + ")"; GetCompletionIdParamName().data() + ")";
} }
ScriptSegmentType::ScriptSegmentType(const std::string &id, static std::string signalNameToInstanceSignalDecl(const std::string &name)
const std::string &propertiesSignal, {
const std::string &triggerSignal, return std::string("void ") + name + "(out int " +
const std::string &completionSignal) GetInstanceIdParamName().data() + ")";
}
ScriptSegmentType::ScriptSegmentType(
const std::string &id, const std::string &propertiesSignalName,
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: _id(id) : _id(id)
{ {
auto sh = obs_get_signal_handler();
signal_handler_add( signal_handler_add(
obs_get_signal_handler(), sh,
signalNameToPropertiesSignalDecl(propertiesSignal).c_str()); signalNameToPropertiesSignalDecl(propertiesSignalName).c_str());
signal_handler_add( signal_handler_add(
obs_get_signal_handler(), sh, signalNameToTriggerSignalDecl(triggerSignalName).c_str());
signalNameToTriggerSignalDecl(triggerSignal).c_str());
signal_handler_add( signal_handler_add(
obs_get_signal_handler(), sh,
signalNameToCompletionSignalDecl(completionSignal).c_str()); signalNameToCompletionSignalDecl(completionSignalName).c_str());
signal_handler_add(
sh,
signalNameToInstanceSignalDecl(newInstanceSignalName).c_str());
signal_handler_add(
sh, signalNameToInstanceSignalDecl(deletedInstanceSignalName)
.c_str());
} }
} // namespace advss } // namespace advss

View File

@@ -10,9 +10,11 @@ class ScriptSegmentType {
public: public:
ScriptSegmentType() = delete; ScriptSegmentType() = delete;
ScriptSegmentType(const std::string &id, ScriptSegmentType(const std::string &id,
const std::string &propertiesSignal, const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &triggerSignalName,
const std::string &completionSignal); const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName);
private: private:
std::string _id; std::string _id;
@@ -26,7 +28,11 @@ public:
static void RegisterScriptCondition(void *ctx, calldata_t *data); static void RegisterScriptCondition(void *ctx, calldata_t *data);
static void DeregisterScriptCondition(void *ctx, calldata_t *data); static void DeregisterScriptCondition(void *ctx, calldata_t *data);
static void GetVariableValue(void *ctx, calldata_t *data); static void GetVariableValue(void *ctx, calldata_t *data);
static void RegisterTempVar(void *ctx, calldata_t *data);
static void DeregisterAllTempVars(void *ctx, calldata_t *data);
static void SetTempVarValue(void *ctx, calldata_t *data);
static void SetVariableValue(void *ctx, calldata_t *data); static void SetVariableValue(void *ctx, calldata_t *data);
static void GetRunningStatus(void *ctx, calldata_t *data);
static bool ActionIdIsValid(const std::string &id); static bool ActionIdIsValid(const std::string &id);
static bool ConditionIdIsValid(const std::string &id); static bool ConditionIdIsValid(const std::string &id);
@@ -46,14 +52,19 @@ static constexpr std::string_view GetActionCompletionSignalParamName()
return "completion_signal_name"; return "completion_signal_name";
} }
static constexpr std::string_view GeCompletionIdParamName() static constexpr std::string_view GetCompletionIdParamName()
{ {
return "completion_id"; return "completion_id";
} }
static constexpr std::string_view GeResultSignalParamName() static constexpr std::string_view GetResultSignalParamName()
{ {
return "result"; return "result";
} }
static constexpr std::string_view GetInstanceIdParamName()
{
return "instance_id";
}
} // namespace advss } // namespace advss

View File

@@ -178,11 +178,7 @@ void MacroSegmentList::mousePressEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton || if (event->button() == Qt::LeftButton ||
event->button() == Qt::RightButton) { event->button() == Qt::RightButton) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragPosition = GetDragIndex(event->globalPos());
#else
_dragPosition = GetDragIndex(event->globalPosition().toPoint()); _dragPosition = GetDragIndex(event->globalPosition().toPoint());
#endif
emit SelectionChanged(_dragPosition); emit SelectionChanged(_dragPosition);
} else { } else {
_dragPosition = -1; _dragPosition = -1;
@@ -300,11 +296,7 @@ void MacroSegmentList::dragMoveEvent(QDragMoveEvent *event)
return; return;
} }
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragCursorPos = (mapToGlobal(event->pos()));
#else
_dragCursorPos = (mapToGlobal(event->position().toPoint())); _dragCursorPos = (mapToGlobal(event->position().toPoint()));
#endif
CheckDropLine(_dragCursorPos); CheckDropLine(_dragCursorPos);
} }
@@ -450,16 +442,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
{ {
HideLastDropLine(); HideLastDropLine();
auto widget = qobject_cast<QWidget *>(event->source()); auto widget = qobject_cast<QWidget *>(event->source());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (widget && !widget->geometry().contains(event->pos()) &&
widgetIsInLayout(widget, _contentLayout)) {
int dropPosition = GetDropIndex(mapToGlobal(event->pos()));
if (dropPosition == -1) {
return;
}
emit Reorder(dropPosition, _dragPosition);
}
#else
if (widget && if (widget &&
!widget->geometry().contains(event->position().toPoint()) && !widget->geometry().contains(event->position().toPoint()) &&
widgetIsInLayout(widget, _contentLayout)) { widgetIsInLayout(widget, _contentLayout)) {
@@ -470,7 +452,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
} }
emit Reorder(dropPosition, _dragPosition); emit Reorder(dropPosition, _dragPosition);
} }
#endif
_dragPosition = -1; _dragPosition = -1;
} }

View File

@@ -11,33 +11,106 @@
namespace advss { namespace advss {
static std::atomic_int completionIdCounter = 0; static std::atomic_int completionIdCounter = 0;
static std::atomic_int instanceIdCounter = 0;
static std::mutex instanceMtx;
static std::vector<MacroSegmentScript *> instances{};
MacroSegmentScript::MacroSegmentScript(obs_data_t *defaultSettings, MacroSegmentScript::MacroSegmentScript(
const std::string &propertiesSignalName, obs_data_t *defaultSettings, const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &triggerSignalName,
const std::string &completionSignal) const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: _settings(obs_data_get_defaults(defaultSettings)), : _settings(obs_data_get_defaults(defaultSettings)),
_propertiesSignal(propertiesSignalName), _propertiesSignal(propertiesSignalName),
_triggerSignal(triggerSignal), _triggerSignal(triggerSignalName),
_completionSignal(completionSignal) _completionSignal(completionSignalName),
_newInstanceSignal(newInstanceSignalName),
_deletedInstanceSignal(deletedInstanceSignalName),
_instanceId(++instanceIdCounter)
{ {
signal_handler_connect(obs_get_signal_handler(), signal_handler_connect(obs_get_signal_handler(),
completionSignal.c_str(), completionSignalName.c_str(),
&MacroSegmentScript::CompletionSignalReceived, &MacroSegmentScript::CompletionSignalReceived,
this); this);
std::lock_guard<std::mutex> lock(instanceMtx);
instances.emplace_back(this);
SignalNewInstance();
} }
MacroSegmentScript::MacroSegmentScript(const MacroSegmentScript &other) MacroSegmentScript::MacroSegmentScript(const MacroSegmentScript &other)
: _settings(obs_data_create()), : _settings(obs_data_create()),
_propertiesSignal(other._propertiesSignal), _propertiesSignal(other._propertiesSignal),
_triggerSignal(other._triggerSignal), _triggerSignal(other._triggerSignal),
_completionSignal(other._completionSignal) _completionSignal(other._completionSignal),
_newInstanceSignal(other._newInstanceSignal),
_deletedInstanceSignal(other._deletedInstanceSignal),
_instanceId(++instanceIdCounter)
{ {
signal_handler_connect(obs_get_signal_handler(), signal_handler_connect(obs_get_signal_handler(),
_completionSignal.c_str(), _completionSignal.c_str(),
&MacroSegmentScript::CompletionSignalReceived, &MacroSegmentScript::CompletionSignalReceived,
this); this);
obs_data_apply(_settings.Get(), other._settings.Get()); obs_data_apply(_settings.Get(), other._settings.Get());
std::lock_guard<std::mutex> lock(instanceMtx);
instances.emplace_back(this);
SignalNewInstance();
}
MacroSegmentScript::~MacroSegmentScript()
{
auto data = calldata_create();
calldata_set_int(data, GetInstanceIdParamName().data(), _instanceId);
signal_handler_signal(obs_get_signal_handler(),
_deletedInstanceSignal.c_str(), data);
calldata_destroy(data);
std::lock_guard<std::mutex> lock(instanceMtx);
instances.erase(std::remove(instances.begin(), instances.end(), this),
instances.end());
}
void MacroSegmentScript::RegisterTempVar(const std::string &variableId,
const std::string &name,
const std::string &helpText,
int instanceId)
{
std::lock_guard<std::mutex> lock(instanceMtx);
for (auto instance : instances) {
if (instance->_instanceId != instanceId) {
continue;
}
instance->RegisterTempVarHelper(variableId, name, helpText);
break;
}
}
void MacroSegmentScript::DeregisterAllTempVars(int instanceId)
{
std::lock_guard<std::mutex> lock(instanceMtx);
for (auto instance : instances) {
if (instance->_instanceId != instanceId) {
continue;
}
instance->DeregisterAllTempVarsHelper();
}
}
void MacroSegmentScript::SetTempVarValue(const std::string &variableId,
const std::string &value,
int instanceId)
{
std::lock_guard<std::mutex> lock(instanceMtx);
for (auto instance : instances) {
if (instance->_instanceId != instanceId) {
continue;
}
instance->SetTempVarValueHelper(variableId, value);
}
} }
bool MacroSegmentScript::Save(obs_data_t *obj) const bool MacroSegmentScript::Save(obs_data_t *obj) const
@@ -85,8 +158,10 @@ bool MacroSegmentScript::SendTriggerSignal()
auto data = calldata_create(); auto data = calldata_create();
calldata_set_string(data, GetActionCompletionSignalParamName().data(), calldata_set_string(data, GetActionCompletionSignalParamName().data(),
_completionSignal.c_str()); _completionSignal.c_str());
calldata_set_int(data, GeCompletionIdParamName().data(), _completionId); calldata_set_int(data, GetCompletionIdParamName().data(),
_completionId);
calldata_set_string(data, "settings", obs_data_get_json(GetSettings())); calldata_set_string(data, "settings", obs_data_get_json(GetSettings()));
calldata_set_int(data, GetInstanceIdParamName().data(), _instanceId);
signal_handler_signal(obs_get_signal_handler(), _triggerSignal.c_str(), signal_handler_signal(obs_get_signal_handler(), _triggerSignal.c_str(),
data); data);
calldata_destroy(data); calldata_destroy(data);
@@ -101,18 +176,18 @@ void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
{ {
auto segment = static_cast<MacroSegmentScript *>(param); auto segment = static_cast<MacroSegmentScript *>(param);
long long int id; long long int id;
if (!calldata_get_int(data, GeCompletionIdParamName().data(), &id)) { if (!calldata_get_int(data, GetCompletionIdParamName().data(), &id)) {
blog(LOG_WARNING, blog(LOG_WARNING,
"received completion signal without \"%s\" parameter", "received completion signal without \"%s\" parameter",
GeCompletionIdParamName().data()); GetCompletionIdParamName().data());
return; return;
} }
bool result; bool result;
if (!calldata_get_bool(data, GeResultSignalParamName().data(), if (!calldata_get_bool(data, GetResultSignalParamName().data(),
&result)) { &result)) {
blog(LOG_WARNING, blog(LOG_WARNING,
"received completion signal without \"%s\" parameter", "received completion signal without \"%s\" parameter",
GeResultSignalParamName().data()); GetResultSignalParamName().data());
return; return;
} }
if (id != segment->_completionId) { if (id != segment->_completionId) {
@@ -122,6 +197,19 @@ void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
segment->_triggerResult = result; segment->_triggerResult = result;
} }
void MacroSegmentScript::SignalNewInstance() const
{
std::thread signalNewInstanceThread([this]() {
auto data = calldata_create();
calldata_set_int(data, GetInstanceIdParamName().data(),
_instanceId);
signal_handler_signal(obs_get_signal_handler(),
_newInstanceSignal.c_str(), data);
calldata_destroy(data);
});
signalNewInstanceThread.detach();
}
obs_properties_t *MacroSegmentScriptEdit::GetProperties(void *obj) obs_properties_t *MacroSegmentScriptEdit::GetProperties(void *obj)
{ {
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj); auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);

View File

@@ -15,9 +15,20 @@ class MacroSegmentScript {
public: public:
MacroSegmentScript(obs_data_t *defaultSettings, MacroSegmentScript(obs_data_t *defaultSettings,
const std::string &propertiesSignalName, const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &triggerSignalName,
const std::string &completionSignal); const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName);
MacroSegmentScript(const advss::MacroSegmentScript &); MacroSegmentScript(const advss::MacroSegmentScript &);
virtual ~MacroSegmentScript();
static void RegisterTempVar(const std::string &variableId,
const std::string &name,
const std::string &helpText,
int instanceId);
static void DeregisterAllTempVars(int instanceId);
static void SetTempVarValue(const std::string &variableId,
const std::string &value, int instanceId);
protected: protected:
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
@@ -31,15 +42,27 @@ protected:
double GetTimeoutSeconds() const { return _timeout.Seconds(); }; double GetTimeoutSeconds() const { return _timeout.Seconds(); };
bool TriggerIsCompleted() const { return _triggerIsComplete; } bool TriggerIsCompleted() const { return _triggerIsComplete; }
virtual void RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText) = 0;
virtual void DeregisterAllTempVarsHelper() = 0;
virtual void SetTempVarValueHelper(const std::string &variableId,
const std::string &value) = 0;
const int64_t _instanceId;
private: private:
virtual void WaitForCompletion() const = 0; virtual void WaitForCompletion() const = 0;
static void CompletionSignalReceived(void *param, calldata_t *data); static void CompletionSignalReceived(void *param, calldata_t *data);
void SignalNewInstance() const;
OBSDataAutoRelease _settings; OBSDataAutoRelease _settings;
std::string _propertiesSignal = ""; std::string _propertiesSignal = "";
std::string _triggerSignal = ""; std::string _triggerSignal = "";
std::string _completionSignal = ""; std::string _completionSignal = "";
std::string _newInstanceSignal = "";
std::string _deletedInstanceSignal = "";
std::atomic_bool _triggerIsComplete = {false}; std::atomic_bool _triggerIsComplete = {false};
bool _triggerResult = false; bool _triggerResult = false;
int64_t _completionId = 0; int64_t _completionId = 0;

View File

@@ -21,7 +21,7 @@ MacroSegmentSelection::MacroSegmentSelection(QWidget *parent, Type type,
{ {
_index->setMinimum(0); _index->setMinimum(0);
_index->setMaximum(99); _index->setMaximum(99);
_index->specialValueText("-"); _index->setSpecialValueText("-");
if (!allowVariables) { if (!allowVariables) {
_index->DisableVariableSelection(); _index->DisableVariableSelection();
} }

View File

@@ -294,13 +294,8 @@ bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
QMouseEvent *newEvent = new QMouseEvent( QMouseEvent *newEvent = new QMouseEvent(
mouseEvent->type(), mouseEvent->type(),
_headerInfo->mapTo(this, mouseEvent->pos()), _headerInfo->mapTo(this, mouseEvent->pos()),
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) mouseEvent->globalPosition(), mouseEvent->button(),
mouseEvent->globalPos(), mouseEvent->buttons(), mouseEvent->modifiers());
#else
mouseEvent->globalPosition(),
#endif
mouseEvent->button(), mouseEvent->buttons(),
mouseEvent->modifiers());
QApplication::sendEvent(parentWidget(), newEvent); QApplication::sendEvent(parentWidget(), newEvent);
} }
return QWidget::eventFilter(obj, ev); return QWidget::eventFilter(obj, ev);

View File

@@ -51,8 +51,17 @@ protected:
virtual void SetupTempVars(); virtual void SetupTempVars();
void AddTempvar(const std::string &id, const std::string &name, void AddTempvar(const std::string &id, const std::string &name,
const std::string &description = ""); const std::string &description = "");
void SetTempVarValue(const std::string &id, const std::string &value); void SetTempVarValue(const std::string &id, const std::string &value);
template<typename T, typename = std::enable_if_t<
std::is_same<std::decay_t<T>, bool>::value>>
void SetTempVarValue(const std::string &id, T value)
{
SetTempVarValue(id, value ? std::string("true")
: std::string("false"));
}
private: private:
void ClearAvailableTempvars(); void ClearAvailableTempvars();
std::optional<const TempVariable> std::optional<const TempVariable>

View File

@@ -178,6 +178,7 @@ void MacroTreeItem::Update(bool force)
QSizePolicy::Maximum); QSizePolicy::Maximum);
_expand->setMaximumSize(10, 16); _expand->setMaximumSize(10, 16);
_expand->setMinimumSize(10, 0); _expand->setMinimumSize(10, 0);
_expand->setProperty("class", "checkbox-icon indicator-expand");
#ifdef __APPLE__ #ifdef __APPLE__
_expand->setAttribute(Qt::WA_LayoutUsesWidgetRect); _expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
#endif #endif
@@ -888,14 +889,7 @@ void MacroTree::dropEvent(QDropEvent *event)
QModelIndexList indices = selectedIndexes(); QModelIndexList indices = selectedIndexes();
DropIndicatorPosition indicator = dropIndicatorPosition(); DropIndicatorPosition indicator = dropIndicatorPosition();
int row = indexAt( int row = indexAt(event->position().toPoint()).row();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
event->position().toPoint()
#else
event->pos()
#endif
)
.row();
bool emptyDrop = row == -1; bool emptyDrop = row == -1;
if (emptyDrop) { if (emptyDrop) {

View File

@@ -244,8 +244,13 @@ bool Macro::CheckConditions(bool ignorePause)
return false; return false;
} }
_stop = false;
_matched = false; _matched = false;
for (auto &condition : _conditions) { for (auto &condition : _conditions) {
if (!condition) {
continue;
}
if (_paused && !ignorePause) { 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;
@@ -383,6 +388,9 @@ bool Macro::RunActionsHelper(
bool actionsExecutedSuccessfully = true; bool actionsExecutedSuccessfully = true;
for (auto &action : actions) { for (auto &action : actions) {
if (!action) {
continue;
}
if (action->Enabled()) { if (action->Enabled()) {
action->LogAction(); action->LogAction();
actionsExecutedSuccessfully = actionsExecutedSuccessfully =

View File

@@ -5,6 +5,7 @@
#include "plugin-state-helpers.hpp" #include "plugin-state-helpers.hpp"
#include "sync-helpers.hpp" #include "sync-helpers.hpp"
#include "tab-helpers.hpp" #include "tab-helpers.hpp"
#include "time-helpers.hpp"
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include <QTimer> #include <QTimer>
@@ -39,8 +40,8 @@ ActionQueueTable *ActionQueueTable::Create()
void ActionQueueTable::Add() void ActionQueueTable::Add()
{ {
auto newQueue = std::make_shared<ActionQueue>(); auto newQueue = std::make_shared<ActionQueue>();
auto accepted = auto accepted = ActionQueueSettingsDialog::AskForSettings(
ActionQueueSettingsDialog::AskForSettings(this, *newQueue); GetSettingsWindow(), *newQueue);
if (!accepted) { if (!accepted) {
return; return;
} }
@@ -55,6 +56,13 @@ void ActionQueueTable::Add()
QString::fromStdString(newQueue->Name())); QString::fromStdString(newQueue->Name()));
} }
static double getSecondsSinceLastEmpty(ActionQueue *queue)
{
const auto time = std::chrono::high_resolution_clock::now() -
queue->GetLastEmptyTime();
return std::chrono::duration_cast<std::chrono::seconds>(time).count();
}
static QStringList getCellLabels(ActionQueue *queue, bool addName = true) static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
{ {
assert(queue); assert(queue);
@@ -64,6 +72,7 @@ static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
result << QString::fromStdString(queue->Name()); result << QString::fromStdString(queue->Name());
} }
result << QString::number(queue->Size()) result << QString::number(queue->Size())
<< FormatRelativeTime(getSecondsSinceLastEmpty(queue))
<< QString::fromStdString(obs_module_text( << QString::fromStdString(obs_module_text(
queue->IsRunning() queue->IsRunning()
? "AdvSceneSwitcher.actionQueueTab.yes" ? "AdvSceneSwitcher.actionQueueTab.yes"
@@ -115,7 +124,7 @@ static void openSettingsDialog()
auto oldName = queue->Name(); auto oldName = queue->Name();
bool accepted = ActionQueueSettingsDialog::AskForSettings( bool accepted = ActionQueueSettingsDialog::AskForSettings(
tabWidget->Table(), *queue.get()); GetSettingsWindow(), *queue.get());
if (accepted && oldName != queue->Name()) { if (accepted && oldName != queue->Name()) {
ActionQueueSignalManager::Instance()->Rename( ActionQueueSignalManager::Instance()->Rename(
QString::fromStdString(oldName), QString::fromStdString(oldName),
@@ -199,6 +208,8 @@ ActionQueueTable::ActionQueueTable(QTabWidget *parent)
"AdvSceneSwitcher.actionQueueTab.name.header") "AdvSceneSwitcher.actionQueueTab.name.header")
<< obs_module_text( << obs_module_text(
"AdvSceneSwitcher.actionQueueTab.size.header") "AdvSceneSwitcher.actionQueueTab.size.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.lastEmpty.header")
<< obs_module_text( << obs_module_text(
"AdvSceneSwitcher.actionQueueTab.isRunning.header") "AdvSceneSwitcher.actionQueueTab.isRunning.header")
<< obs_module_text( << obs_module_text(

View File

@@ -26,7 +26,10 @@ void SetupActionQueues()
done = true; done = true;
} }
ActionQueue::ActionQueue() : Item() {} ActionQueue::ActionQueue() : Item()
{
_lastEmpty = std::chrono::high_resolution_clock::now();
}
ActionQueue::~ActionQueue() ActionQueue::~ActionQueue()
{ {
@@ -110,6 +113,7 @@ void ActionQueue::Add(const std::shared_ptr<MacroAction> &action)
action->Save(data); action->Save(data);
copy->Load(data); copy->Load(data);
copy->PostLoad(); copy->PostLoad();
RunPostLoadSteps();
copy->ResolveVariablesToFixedValues(); copy->ResolveVariablesToFixedValues();
_actions.emplace_back(copy); _actions.emplace_back(copy);
} else { } else {
@@ -124,6 +128,14 @@ bool ActionQueue::IsEmpty()
return _actions.empty(); return _actions.empty();
} }
ActionQueue::TimePoint ActionQueue::GetLastEmptyTime()
{
if (IsEmpty()) {
_lastEmpty = std::chrono::high_resolution_clock::now();
}
return _lastEmpty;
}
size_t ActionQueue::Size() size_t ActionQueue::Size()
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
@@ -137,6 +149,8 @@ void ActionQueue::RunActions()
{ // Grab next action to run { // Grab next action to run
std::unique_lock<std::mutex> lock(_mutex); std::unique_lock<std::mutex> lock(_mutex);
while (_actions.empty() && !_stop) { while (_actions.empty() && !_stop) {
_lastEmpty =
std::chrono::high_resolution_clock::now();
_cv.wait(lock); _cv.wait(lock);
} }
@@ -166,7 +180,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
"AdvSceneSwitcher.actionQueues.select", "AdvSceneSwitcher.actionQueues.select",
"AdvSceneSwitcher.actionQueues.add", "AdvSceneSwitcher.actionQueues.add",
"AdvSceneSwitcher.actionQueues.nameNotAvailable", "AdvSceneSwitcher.actionQueues.nameNotAvailable",
parent), true, parent),
_queueRunStatus(new QLabel()), _queueRunStatus(new QLabel()),
_startStopToggle(new QPushButton()), _startStopToggle(new QPushButton()),
_queueSize(new QLabel()), _queueSize(new QLabel()),

View File

@@ -2,11 +2,12 @@
#include "item-selection-helpers.hpp" #include "item-selection-helpers.hpp"
#include "macro-action.hpp" #include "macro-action.hpp"
#include <chrono>
#include <condition_variable>
#include <deque> #include <deque>
#include <obs-data.h> #include <obs-data.h>
#include <QCheckBox> #include <QCheckBox>
#include <thread> #include <thread>
#include <condition_variable>
namespace advss { namespace advss {
@@ -14,6 +15,8 @@ class ActionQueueSelection;
class ActionQueueSettingsDialog; class ActionQueueSettingsDialog;
class ActionQueue : public Item { class ActionQueue : public Item {
using TimePoint = std::chrono::high_resolution_clock::time_point;
public: public:
ActionQueue(); ActionQueue();
~ActionQueue(); ~ActionQueue();
@@ -30,6 +33,7 @@ public:
void Clear(); void Clear();
bool IsEmpty(); bool IsEmpty();
TimePoint GetLastEmptyTime();
void Add(const std::shared_ptr<MacroAction> &); void Add(const std::shared_ptr<MacroAction> &);
size_t Size(); size_t Size();
@@ -44,6 +48,7 @@ private:
std::condition_variable _cv; std::condition_variable _cv;
std::thread _thread; std::thread _thread;
std::deque<std::shared_ptr<MacroAction>> _actions; std::deque<std::shared_ptr<MacroAction>> _actions;
TimePoint _lastEmpty;
friend ActionQueueSelection; friend ActionQueueSelection;
friend ActionQueueSettingsDialog; friend ActionQueueSettingsDialog;

View File

@@ -168,17 +168,6 @@ void SwitcherData::SaveVersion(obs_data_t *obj,
obs_data_set_string(obj, "version", currentVersion.c_str()); obs_data_set_string(obj, "version", currentVersion.c_str());
} }
void SwitcherData::AddIntervalResetStep(std::function<void()> function,
bool tryLock)
{
if (!tryLock) {
resetIntervalSteps.emplace_back(function);
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
resetIntervalSteps.emplace_back(function);
}
void SwitcherData::RunPostLoadSteps() void SwitcherData::RunPostLoadSteps()
{ {
for (const auto &func : postLoadSteps) { for (const auto &func : postLoadSteps) {

View File

@@ -57,11 +57,9 @@ public:
bool AnySceneTransitionStarted(); bool AnySceneTransitionStarted();
void SetPreconditions(); void SetPreconditions();
void ResetForNextInterval();
void AddSaveStep(std::function<void(obs_data_t *)>); void AddSaveStep(std::function<void(obs_data_t *)>);
void AddLoadStep(std::function<void(obs_data_t *)>); void AddLoadStep(std::function<void(obs_data_t *)>);
void AddPostLoadStep(std::function<void()>); void AddPostLoadStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>, bool lock = true);
void RunPostLoadSteps(); void RunPostLoadSteps();
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition, bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPreviousSceneAsMatch, int &linger, bool &setPreviousSceneAsMatch,
@@ -100,7 +98,6 @@ public:
std::vector<std::function<void(obs_data_t *)>> saveSteps; std::vector<std::function<void(obs_data_t *)>> saveSteps;
std::vector<std::function<void(obs_data_t *)>> loadSteps; std::vector<std::function<void(obs_data_t *)>> loadSteps;
std::vector<std::function<void()>> postLoadSteps; std::vector<std::function<void()>> postLoadSteps;
std::vector<std::function<void()>> resetIntervalSteps;
bool firstBoot = true; bool firstBoot = true;
bool transitionActive = false; bool transitionActive = false;
@@ -233,7 +230,6 @@ public:
bool checkPause(); bool checkPause();
void checkDefaultSceneTransitions(); void checkDefaultSceneTransitions();
void writeSceneInfoToFile(); void writeSceneInfoToFile();
void writeToStatusFile(const QString &msg);
void checkSwitchCooldown(bool &match); void checkSwitchCooldown(bool &match);
std::deque<WindowSwitch> windowSwitches; std::deque<WindowSwitch> windowSwitches;

View File

@@ -0,0 +1,34 @@
#include "auto-update-tooltip-label.hpp"
namespace advss {
AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
QWidget *parent, const std::function<QString()> &updateTooltipCallback,
int updateIntervalMs)
: QLabel(parent),
_callback(updateTooltipCallback),
_timer(new QTimer(this)),
_updateIntervalMs(updateIntervalMs)
{
connect(_timer, &QTimer::timeout, this,
&AutoUpdateTooltipLabel::UpdateTooltip);
}
void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
{
_timer->start(_updateIntervalMs);
QLabel::enterEvent(event);
}
void AutoUpdateTooltipLabel::leaveEvent(QEvent *event)
{
_timer->stop();
QLabel::leaveEvent(event);
}
void AutoUpdateTooltipLabel::UpdateTooltip()
{
setToolTip(_callback());
}
} // namespace advss

View File

@@ -0,0 +1,32 @@
#pragma once
#include "export-symbol-helper.hpp"
#include <functional>
#include <QLabel>
#include <QTimer>
namespace advss {
class ADVSS_EXPORT AutoUpdateTooltipLabel : public QLabel {
Q_OBJECT
public:
AutoUpdateTooltipLabel(
QWidget *parent,
const std::function<QString()> &updateTooltipCallback,
int updateIntervalMs = 300);
protected:
void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
private slots:
void UpdateTooltip();
private:
const std::function<QString()> _callback;
QTimer *_timer;
const int _updateIntervalMs;
};
} // namespace advss

View File

@@ -29,6 +29,7 @@ void Duration::Load(obs_data_t *obj, const char *name)
// TODO: remove this fallback // TODO: remove this fallback
if (!data || !obs_data_has_user_value(data, "version") || if (!data || !obs_data_has_user_value(data, "version") ||
obs_data_get_int(data, "version") != 1) { obs_data_get_int(data, "version") != 1) {
_value = 0.;
bool usingDefaultArgs = strcmp("duration", name) == 0; bool usingDefaultArgs = strcmp("duration", name) == 0;
if (usingDefaultArgs) { if (usingDefaultArgs) {
_value = obs_data_get_double(obj, "seconds"); _value = obs_data_get_double(obj, "seconds");

View File

@@ -59,7 +59,10 @@ ItemSelection::ItemSelection(std::deque<std::shared_ptr<Item>> &items,
_conflictStr(conflict) _conflictStr(conflict)
{ {
_modify->setMaximumWidth(22); _modify->setMaximumWidth(22);
SetButtonIcon(_modify, ":/settings/images/settings/general.svg"); SetButtonIcon(_modify,
GetThemeTypeName() == "Light"
? ":/settings/images/settings/general.svg"
: "theme:Dark/settings/general.svg");
_modify->setFlat(true); _modify->setFlat(true);
if (!configureTooltip.empty()) { if (!configureTooltip.empty()) {
_modify->setToolTip(obs_module_text(configureTooltip.data())); _modify->setToolTip(obs_module_text(configureTooltip.data()));
@@ -265,6 +268,7 @@ ItemSettingsDialog::ItemSettingsDialog(const Item &settings,
std::string_view select, std::string_view select,
std::string_view add, std::string_view add,
std::string_view nameConflict, std::string_view nameConflict,
bool showNameEmptyWarning,
QWidget *parent) QWidget *parent)
: QDialog(parent), : QDialog(parent),
_name(new QLineEdit()), _name(new QLineEdit()),
@@ -274,7 +278,8 @@ ItemSettingsDialog::ItemSettingsDialog(const Item &settings,
_items(items), _items(items),
_selectStr(select), _selectStr(select),
_addStr(add), _addStr(add),
_conflictStr(nameConflict) _conflictStr(nameConflict),
_showNameEmptyWarning(showNameEmptyWarning)
{ {
setModal(true); setModal(true);
setWindowModality(Qt::WindowModality::WindowModal); setWindowModality(Qt::WindowModality::WindowModal);

View File

@@ -45,7 +45,7 @@ public:
std::string_view addString = "AdvSceneSwitcher.item.add", std::string_view addString = "AdvSceneSwitcher.item.add",
std::string_view conflictString = std::string_view conflictString =
"AdvSceneSwitcher.item.nameNotAvailable", "AdvSceneSwitcher.item.nameNotAvailable",
QWidget *parent = 0); bool showEmptyNameWarning = true, QWidget *parent = 0);
virtual ~ItemSettingsDialog() = default; virtual ~ItemSettingsDialog() = default;
private slots: private slots:

View File

@@ -32,7 +32,9 @@ void PreventMouseWheelAdjustWithoutFocus(QWidget *w)
QString(w->metaObject()->className()) == QString(w->metaObject()->className()) ==
"advss::OSCMessageElementEdit" || "advss::OSCMessageElementEdit" ||
QString(w->metaObject()->className()) == QString(w->metaObject()->className()) ==
"advss::ChatMessagePropertyEdit") { "advss::ChatMessagePropertyEdit" ||
QString(w->metaObject()->className()) ==
"advss::KeyValueListContainerWidget") {
return; return;
} }
w->setFocusPolicy(Qt::StrongFocus); w->setFocusPolicy(Qt::StrongFocus);

View File

@@ -67,6 +67,8 @@ NonModalMessageDialog::NonModalMessageDialog(const QString &message, Type type,
} }
case Type::INPUT: { case Type::INPUT: {
_inputEdit = new ResizingPlainTextEdit(this); _inputEdit = new ResizingPlainTextEdit(this);
_inputEdit->setTabChangesFocus(true);
_inputEdit->setFocus();
connect(_inputEdit, &ResizingPlainTextEdit::textChanged, this, connect(_inputEdit, &ResizingPlainTextEdit::textChanged, this,
&NonModalMessageDialog::InputChanged); &NonModalMessageDialog::InputChanged);
layout->addWidget(_inputEdit); layout->addWidget(_inputEdit);

View File

@@ -21,6 +21,24 @@ static std::vector<std::function<void()>> &getPluginCleanupSteps()
return steps; return steps;
} }
static std::vector<std::function<void()>> &getResetIntervalSteps()
{
static std::vector<std::function<void()>> steps;
return steps;
}
static std::vector<std::function<void()>> &getStartSteps()
{
static std::vector<std::function<void()>> steps;
return steps;
}
static std::vector<std::function<void()>> &getStopSteps()
{
static std::vector<std::function<void()>> steps;
return steps;
}
static std::mutex mutex; static std::mutex mutex;
void SavePluginSettings(obs_data_t *obj) void SavePluginSettings(obs_data_t *obj)
@@ -48,9 +66,10 @@ void AddPostLoadStep(std::function<void()> step)
GetSwitcher()->AddPostLoadStep(step); GetSwitcher()->AddPostLoadStep(step);
} }
void AddIntervalResetStep(std::function<void()> step, bool lock) void AddIntervalResetStep(std::function<void()> step)
{ {
GetSwitcher()->AddIntervalResetStep(step, lock); std::lock_guard<std::mutex> lock(mutex);
getResetIntervalSteps().emplace_back(step);
} }
void RunPostLoadSteps() void RunPostLoadSteps()
@@ -100,6 +119,42 @@ void RunPluginCleanupSteps()
} }
} }
void RunIntervalResetSteps()
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto &step : getResetIntervalSteps()) {
step();
}
}
void AddStartStep(std::function<void()> step)
{
std::lock_guard<std::mutex> lock(mutex);
getStartSteps().emplace_back(step);
}
void AddStopStep(std::function<void()> step)
{
std::lock_guard<std::mutex> lock(mutex);
getStopSteps().emplace_back(step);
}
void RunStartSteps()
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto &step : getStartSteps()) {
step();
}
}
void RunStopSteps()
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto &step : getStopSteps()) {
step();
}
}
void StopPlugin() void StopPlugin()
{ {
GetSwitcher()->Stop(); GetSwitcher()->Stop();

View File

@@ -11,7 +11,7 @@ EXPORT void LoadPluginSettings(obs_data_t *);
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>); EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);
EXPORT void AddLoadStep(std::function<void(obs_data_t *)>); EXPORT void AddLoadStep(std::function<void(obs_data_t *)>);
EXPORT void AddPostLoadStep(std::function<void()>); EXPORT void AddPostLoadStep(std::function<void()>);
EXPORT void AddIntervalResetStep(std::function<void()>, bool lock = true); EXPORT void AddIntervalResetStep(std::function<void()>);
EXPORT void RunPostLoadSteps(); EXPORT void RunPostLoadSteps();
EXPORT void AddPluginInitStep(std::function<void()>); EXPORT void AddPluginInitStep(std::function<void()>);
@@ -25,6 +25,11 @@ EXPORT void StopPlugin();
EXPORT void StartPlugin(); EXPORT void StartPlugin();
EXPORT bool PluginIsRunning(); EXPORT bool PluginIsRunning();
EXPORT int GetIntervalValue(); EXPORT int GetIntervalValue();
void AddStartStep(std::function<void()>);
void AddStopStep(std::function<void()>);
void RunStartSteps();
void RunStopSteps();
void RunIntervalResetSteps();
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 }; enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior); EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior);

View File

@@ -92,7 +92,10 @@ RegexConfigWidget::RegexConfigWidget(QWidget *parent, bool showEnable)
_openSettings(new QToolButton()), _openSettings(new QToolButton()),
_enable(new QPushButton()) _enable(new QPushButton())
{ {
SetButtonIcon(_openSettings, ":/settings/images/settings/general.svg"); SetButtonIcon(_openSettings,
GetThemeTypeName() == "Light"
? ":/settings/images/settings/general.svg"
: "theme:Dark/settings/general.svg");
_openSettings->setToolTip( _openSettings->setToolTip(
obs_module_text("AdvSceneSwitcher.regex.configure")); obs_module_text("AdvSceneSwitcher.regex.configure"));

View File

@@ -94,7 +94,7 @@ void ResizingPlainTextEdit::AddHeightForScrollBar(bool addHeight)
return; return;
} }
_hScrollBarAddedHeight = verticalScrollBar()->height(); _hScrollBarAddedHeight = horizontalScrollBar()->height();
setFixedHeight(height() + _hScrollBarAddedHeight); setFixedHeight(height() + _hScrollBarAddedHeight);
} }

View File

@@ -455,35 +455,4 @@ void NotifyUIAboutTempVarChange()
nullptr, false); nullptr, false);
} }
AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
QWidget *parent, std::function<QString()> updateFunc)
: QLabel(parent),
_updateFunc(updateFunc),
_timer(new QTimer(this))
{
connect(_timer, &QTimer::timeout, this,
&AutoUpdateTooltipLabel::UpdateTooltip);
}
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
#else
void AutoUpdateTooltipLabel::enterEvent(QEvent *event)
#endif
{
_timer->start(300);
QLabel::enterEvent(event);
}
void AutoUpdateTooltipLabel::leaveEvent(QEvent *event)
{
_timer->stop();
QLabel::leaveEvent(event);
}
void AutoUpdateTooltipLabel::UpdateTooltip()
{
setToolTip(_updateFunc());
}
} // namespace advss } // namespace advss

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#include "auto-update-tooltip-label.hpp"
#include "filter-combo-box.hpp" #include "filter-combo-box.hpp"
#include <obs-data.h> #include <obs-data.h>
@@ -6,8 +7,6 @@
#include <optional> #include <optional>
#include <QEnterEvent> #include <QEnterEvent>
#include <QEvent> #include <QEvent>
#include <QLabel>
#include <QTimer>
#include <QStringList> #include <QStringList>
#include <string> #include <string>
@@ -79,29 +78,6 @@ private:
friend TempVariableSelection; friend TempVariableSelection;
}; };
class AutoUpdateTooltipLabel : public QLabel {
Q_OBJECT
public:
AutoUpdateTooltipLabel(QWidget *parent,
std::function<QString()> updateFunc);
protected:
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent *event) override;
#else
void enterEvent(QEvent *event) override;
#endif
void leaveEvent(QEvent *event) override;
private slots:
void UpdateTooltip();
private:
std::function<QString()> _updateFunc;
QTimer *_timer;
};
class ADVSS_EXPORT TempVariableSelection : public QWidget { class ADVSS_EXPORT TempVariableSelection : public QWidget {
Q_OBJECT Q_OBJECT

View File

@@ -23,6 +23,14 @@ static bool setupDone = setup();
bool setup() bool setup()
{ {
AddPluginPostLoadStep(registerWebsocketVendor); AddPluginPostLoadStep(registerWebsocketVendor);
AddStartStep([]() {
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
nullptr);
});
AddStopStep([]() {
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
nullptr);
});
return true; return true;
} }
@@ -109,6 +117,9 @@ void RegisterWebsocketRequest(
void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data) void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data)
{ {
if (OBSIsShuttingDown()) {
return;
}
obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data); obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data);
} }

View File

@@ -29,7 +29,7 @@ private:
T _value = {}; T _value = {};
std::weak_ptr<Variable> _variable; std::weak_ptr<Variable> _variable;
friend class GenericVaraiableSpinbox; friend class GenericVariableSpinbox;
friend class VariableSpinBox; friend class VariableSpinBox;
friend class VariableDoubleSpinBox; friend class VariableDoubleSpinBox;
}; };

View File

@@ -82,9 +82,10 @@ template<typename T> bool NumberVariable<T>::HasValidValue() const
auto value = var->IntValue(); auto value = var->IntValue();
return value.has_value(); return value.has_value();
} else if constexpr (std::is_same<T, double>::value) { } else if constexpr (std::is_same<T, double>::value) {
auto value = var->IntValue(); auto value = var->DoubleValue();
return value.has_value(); return value.has_value();
} }
assert(false); assert(false);
return false; return false;
} }

View File

@@ -5,8 +5,8 @@
namespace advss { namespace advss {
GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent, GenericVariableSpinbox::GenericVariableSpinbox(QWidget *parent,
bool wholeNumber) bool wholeNumber)
: QWidget(parent), : QWidget(parent),
_fixedValueInt(new QSpinBox()), _fixedValueInt(new QSpinBox()),
_fixedValueDouble(new QDoubleSpinBox()), _fixedValueDouble(new QDoubleSpinBox()),
@@ -16,7 +16,9 @@ GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent,
{ {
_toggleType->setCheckable(true); _toggleType->setCheckable(true);
_toggleType->setMaximumWidth(11); _toggleType->setMaximumWidth(11);
SetButtonIcon(_toggleType, ":/res/images/dots-vert.svg"); SetButtonIcon(_toggleType, GetThemeTypeName() == "Light"
? ":/res/images/dots-vert.svg"
: "theme:Dark/dots-vert.svg");
QWidget::connect(_fixedValueInt, SIGNAL(valueChanged(int)), this, QWidget::connect(_fixedValueInt, SIGNAL(valueChanged(int)), this,
SLOT(SetFixedValue(int))); SLOT(SetFixedValue(int)));
@@ -48,7 +50,7 @@ GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent,
SetVisibility(); SetVisibility();
} }
void GenericVaraiableSpinbox::SetValue(const NumberVariable<int> &number) void GenericVariableSpinbox::SetValue(const NumberVariable<int> &number)
{ {
_numberInt = number; _numberInt = number;
const QSignalBlocker b1(_toggleType); const QSignalBlocker b1(_toggleType);
@@ -59,7 +61,7 @@ void GenericVaraiableSpinbox::SetValue(const NumberVariable<int> &number)
SetVisibility(); SetVisibility();
} }
void GenericVaraiableSpinbox::SetValue(const NumberVariable<double> &number) void GenericVariableSpinbox::SetValue(const NumberVariable<double> &number)
{ {
_numberDouble = number; _numberDouble = number;
const QSignalBlocker b1(_toggleType); const QSignalBlocker b1(_toggleType);
@@ -70,44 +72,44 @@ void GenericVaraiableSpinbox::SetValue(const NumberVariable<double> &number)
SetVisibility(); SetVisibility();
} }
void GenericVaraiableSpinbox::DisableVariableSelection() void GenericVariableSpinbox::DisableVariableSelection()
{ {
_hideTypeToggle = true; _hideTypeToggle = true;
_toggleType->hide(); _toggleType->hide();
ToggleTypeClicked(false); ToggleTypeClicked(false);
} }
void GenericVaraiableSpinbox::setMinimum(double value) void GenericVariableSpinbox::setMinimum(double value)
{ {
_fixedValueInt->setMinimum(value); _fixedValueInt->setMinimum(value);
_fixedValueDouble->setMinimum(value); _fixedValueDouble->setMinimum(value);
} }
void GenericVaraiableSpinbox::setMaximum(double value) void GenericVariableSpinbox::setMaximum(double value)
{ {
_fixedValueInt->setMaximum(value); _fixedValueInt->setMaximum(value);
_fixedValueDouble->setMaximum(value); _fixedValueDouble->setMaximum(value);
} }
void GenericVaraiableSpinbox::setPrefix(const QString &prefix) void GenericVariableSpinbox::setPrefix(const QString &prefix)
{ {
_fixedValueInt->setPrefix(prefix); _fixedValueInt->setPrefix(prefix);
_fixedValueDouble->setPrefix(prefix); _fixedValueDouble->setPrefix(prefix);
} }
void GenericVaraiableSpinbox::setSuffix(const QString &suffix) void GenericVariableSpinbox::setSuffix(const QString &suffix)
{ {
_fixedValueInt->setSuffix(suffix); _fixedValueInt->setSuffix(suffix);
_fixedValueDouble->setSuffix(suffix); _fixedValueDouble->setSuffix(suffix);
} }
void GenericVaraiableSpinbox::specialValueText(const QString &text) void GenericVariableSpinbox::setSpecialValueText(const QString &text)
{ {
_fixedValueInt->setSpecialValueText(text); _fixedValueInt->setSpecialValueText(text);
_fixedValueDouble->setSpecialValueText(text); _fixedValueDouble->setSpecialValueText(text);
} }
void GenericVaraiableSpinbox::SetFixedValue(int value) void GenericVariableSpinbox::SetFixedValue(int value)
{ {
_numberInt._value = value; _numberInt._value = value;
const QSignalBlocker b(_fixedValueInt); const QSignalBlocker b(_fixedValueInt);
@@ -115,7 +117,7 @@ void GenericVaraiableSpinbox::SetFixedValue(int value)
EmitSignals(); EmitSignals();
} }
void GenericVaraiableSpinbox::SetFixedValue(double value) void GenericVariableSpinbox::SetFixedValue(double value)
{ {
_numberDouble._value = value; _numberDouble._value = value;
const QSignalBlocker b(_fixedValueDouble); const QSignalBlocker b(_fixedValueDouble);
@@ -123,14 +125,14 @@ void GenericVaraiableSpinbox::SetFixedValue(double value)
EmitSignals(); EmitSignals();
} }
void GenericVaraiableSpinbox::VariableChanged(const QString &name) void GenericVariableSpinbox::VariableChanged(const QString &name)
{ {
_numberInt._variable = GetWeakVariableByQString(name); _numberInt._variable = GetWeakVariableByQString(name);
_numberDouble._variable = GetWeakVariableByQString(name); _numberDouble._variable = GetWeakVariableByQString(name);
EmitSignals(); EmitSignals();
} }
void GenericVaraiableSpinbox::ToggleTypeClicked(bool useVariable) void GenericVariableSpinbox::ToggleTypeClicked(bool useVariable)
{ {
_numberInt._type = useVariable ? NumberVariable<int>::Type::VARIABLE _numberInt._type = useVariable ? NumberVariable<int>::Type::VARIABLE
: NumberVariable<int>::Type::FIXED_VALUE; : NumberVariable<int>::Type::FIXED_VALUE;
@@ -142,7 +144,7 @@ void GenericVaraiableSpinbox::ToggleTypeClicked(bool useVariable)
EmitSignals(); EmitSignals();
} }
void GenericVaraiableSpinbox::EmitSignals() void GenericVariableSpinbox::EmitSignals()
{ {
if (_wholeNumber) { if (_wholeNumber) {
emit FixedValueChanged(_numberInt); emit FixedValueChanged(_numberInt);
@@ -153,7 +155,7 @@ void GenericVaraiableSpinbox::EmitSignals()
} }
} }
void GenericVaraiableSpinbox::SetVisibility() void GenericVariableSpinbox::SetVisibility()
{ {
if (_wholeNumber) { if (_wholeNumber) {
_fixedValueDouble->hide(); _fixedValueDouble->hide();
@@ -164,7 +166,7 @@ void GenericVaraiableSpinbox::SetVisibility()
} }
} }
void GenericVaraiableSpinbox::SetVisibilityInt() void GenericVariableSpinbox::SetVisibilityInt()
{ {
if (_numberInt.IsFixedType()) { if (_numberInt.IsFixedType()) {
_fixedValueInt->show(); _fixedValueInt->show();
@@ -180,7 +182,7 @@ void GenericVaraiableSpinbox::SetVisibilityInt()
updateGeometry(); updateGeometry();
} }
void GenericVaraiableSpinbox::SetVisibilityDouble() void GenericVariableSpinbox::SetVisibilityDouble()
{ {
if (_numberDouble.IsFixedType()) { if (_numberDouble.IsFixedType()) {
_fixedValueDouble->show(); _fixedValueDouble->show();
@@ -197,12 +199,12 @@ void GenericVaraiableSpinbox::SetVisibilityDouble()
} }
VariableSpinBox::VariableSpinBox(QWidget *parent) VariableSpinBox::VariableSpinBox(QWidget *parent)
: GenericVaraiableSpinbox(parent, true) : GenericVariableSpinbox(parent, true)
{ {
} }
VariableDoubleSpinBox::VariableDoubleSpinBox(QWidget *parent) VariableDoubleSpinBox::VariableDoubleSpinBox(QWidget *parent)
: GenericVaraiableSpinbox(parent, false) : GenericVariableSpinbox(parent, false)
{ {
} }

View File

@@ -9,10 +9,10 @@
namespace advss { namespace advss {
class ADVSS_EXPORT GenericVaraiableSpinbox : public QWidget { class ADVSS_EXPORT GenericVariableSpinbox : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
GenericVaraiableSpinbox(QWidget *parent, bool wholeNumber); GenericVariableSpinbox(QWidget *parent, bool wholeNumber);
void SetValue(const NumberVariable<int> &); void SetValue(const NumberVariable<int> &);
void SetValue(const NumberVariable<double> &); void SetValue(const NumberVariable<double> &);
void DisableVariableSelection(); void DisableVariableSelection();
@@ -23,7 +23,7 @@ public:
void setPrefix(const QString &prefix); void setPrefix(const QString &prefix);
void setSuffix(const QString &suffix); void setSuffix(const QString &suffix);
void specialValueText(const QString &text); void setSpecialValueText(const QString &text);
public slots: public slots:
void SetFixedValue(int); void SetFixedValue(int);
@@ -59,7 +59,7 @@ private:
bool _hideTypeToggle = false; bool _hideTypeToggle = false;
}; };
class ADVSS_EXPORT VariableSpinBox : public GenericVaraiableSpinbox { class ADVSS_EXPORT VariableSpinBox : public GenericVariableSpinbox {
Q_OBJECT Q_OBJECT
public: public:
@@ -68,7 +68,7 @@ public:
QSpinBox *SpinBox() { return _fixedValueInt; } QSpinBox *SpinBox() { return _fixedValueInt; }
}; };
class ADVSS_EXPORT VariableDoubleSpinBox : public GenericVaraiableSpinbox { class ADVSS_EXPORT VariableDoubleSpinBox : public GenericVariableSpinbox {
Q_OBJECT Q_OBJECT
public: public:

View File

@@ -43,8 +43,8 @@ VariableTable *VariableTable::Create()
void VariableTable::Add() void VariableTable::Add()
{ {
auto newVariable = std::make_shared<Variable>(); auto newVariable = std::make_shared<Variable>();
auto accepted = auto accepted = VariableSettingsDialog::AskForSettings(
VariableSettingsDialog::AskForSettings(this, *newVariable); GetSettingsWindow(), *newVariable);
if (!accepted) { if (!accepted) {
return; return;
} }
@@ -184,7 +184,7 @@ static void openSettingsDialog()
auto oldName = variable->Name(); auto oldName = variable->Name();
bool accepted = VariableSettingsDialog::AskForSettings( bool accepted = VariableSettingsDialog::AskForSettings(
tabWidget->Table(), *variable.get()); GetSettingsWindow(), *variable.get());
if (accepted && oldName != variable->Name()) { if (accepted && oldName != variable->Name()) {
VariableSignalManager::Instance()->Rename( VariableSignalManager::Instance()->Rename(
QString::fromStdString(oldName), QString::fromStdString(oldName),

View File

@@ -137,7 +137,8 @@ VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
: ItemSettingsDialog(settings, variables, : ItemSettingsDialog(settings, variables,
"AdvSceneSwitcher.variable.select", "AdvSceneSwitcher.variable.select",
"AdvSceneSwitcher.variable.add", "AdvSceneSwitcher.variable.add",
"AdvSceneSwitcher.item.nameNotAvailable", parent), "AdvSceneSwitcher.item.nameNotAvailable", true,
parent),
_value(new ResizingPlainTextEdit(this)), _value(new ResizingPlainTextEdit(this)),
_defaultValue(new ResizingPlainTextEdit(this)), _defaultValue(new ResizingPlainTextEdit(this)),
_save(new QComboBox()) _save(new QComboBox())

View File

@@ -28,6 +28,7 @@ install_advss_plugin_dependency(...)
... to install the plugin and its dependencies. ... to install the plugin and its dependencies.
#]] #]]
add_plugin(http)
add_plugin(midi) add_plugin(midi)
add_plugin(openvr) add_plugin(openvr)
add_plugin(stream-deck) add_plugin(stream-deck)

View File

@@ -15,8 +15,8 @@ target_sources(
macro-action-filter.hpp macro-action-filter.hpp
macro-action-hotkey.cpp macro-action-hotkey.cpp
macro-action-hotkey.hpp macro-action-hotkey.hpp
macro-action-http.cpp macro-action-http-legacy.cpp
macro-action-http.hpp macro-action-http-legacy.hpp
macro-action-log.cpp macro-action-log.cpp
macro-action-log.hpp macro-action-log.hpp
macro-action-media.cpp macro-action-media.cpp
@@ -89,6 +89,8 @@ target_sources(
macro-condition-filter.hpp macro-condition-filter.hpp
macro-condition-folder.cpp macro-condition-folder.cpp
macro-condition-folder.hpp macro-condition-folder.hpp
macro-condition-game-capture.cpp
macro-condition-game-capture.hpp
macro-condition-hotkey.cpp macro-condition-hotkey.cpp
macro-condition-hotkey.hpp macro-condition-hotkey.hpp
macro-condition-idle.cpp macro-condition-idle.cpp
@@ -117,6 +119,8 @@ target_sources(
macro-condition-scene-visibility.hpp macro-condition-scene-visibility.hpp
macro-condition-scene.cpp macro-condition-scene.cpp
macro-condition-scene.hpp macro-condition-scene.hpp
macro-condition-screenshot.cpp
macro-condition-screenshot.hpp
macro-condition-slideshow.cpp macro-condition-slideshow.cpp
macro-condition-slideshow.hpp macro-condition-slideshow.hpp
macro-condition-source.cpp macro-condition-source.cpp

View File

@@ -461,7 +461,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
_volumeDB->setMinimum(-100); _volumeDB->setMinimum(-100);
_volumeDB->setMaximum(0); _volumeDB->setMaximum(0);
_volumeDB->setSuffix("dB"); _volumeDB->setSuffix("dB");
_volumeDB->specialValueText("-inf"); _volumeDB->setSpecialValueText("-inf");
_rate->setMinimum(0.01); _rate->setMinimum(0.01);
_rate->setMaximum(999.); _rate->setMaximum(999.);

View File

@@ -4,8 +4,6 @@
#include "slider-spinbox.hpp" #include "slider-spinbox.hpp"
#include "source-selection.hpp" #include "source-selection.hpp"
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox> #include <QCheckBox>
#include <QHBoxLayout> #include <QHBoxLayout>

View File

@@ -3,8 +3,6 @@
#include "file-selection.hpp" #include "file-selection.hpp"
#include "variable-text-edit.hpp" #include "variable-text-edit.hpp"
#include <QSpinBox>
namespace advss { namespace advss {
class MacroActionFile : public MacroAction { class MacroActionFile : public MacroAction {

View File

@@ -215,7 +215,7 @@ MacroActionFilterEdit::MacroActionFilterEdit(
_settingsString(new VariableTextEdit(this)), _settingsString(new VariableTextEdit(this)),
_refreshSettingSelection(new QPushButton( _refreshSettingSelection(new QPushButton(
obs_module_text("AdvSceneSwitcher.action.filter.refresh"))), obs_module_text("AdvSceneSwitcher.action.filter.refresh"))),
_settingsButtons(new QComboBox()) _settingsButtons(new SourceSettingsButtonSelection(this))
{ {
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents); _filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
@@ -253,8 +253,10 @@ MacroActionFilterEdit::MacroActionFilterEdit(
SLOT(SelectionChanged(const SourceSetting &))); SLOT(SelectionChanged(const SourceSetting &)));
QWidget::connect(_refreshSettingSelection, SIGNAL(clicked()), this, QWidget::connect(_refreshSettingSelection, SIGNAL(clicked()), this,
SLOT(RefreshVariableSourceSelectionValue())); SLOT(RefreshVariableSourceSelectionValue()));
QWidget::connect(_settingsButtons, SIGNAL(currentIndexChanged(int)), QWidget::connect(_settingsButtons,
this, SLOT(ButtonChanged(int))); SIGNAL(SelectionChanged(const SourceSettingButton &)),
this,
SLOT(ButtonChanged(const SourceSettingButton &)));
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = { const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources}, {"{{sources}}", _sources},
@@ -305,25 +307,18 @@ void MacroActionFilterEdit::UpdateEntryData()
const auto filters = const auto filters =
_entryData->_filter.GetFilters(_entryData->_source); _entryData->_filter.GetFilters(_entryData->_source);
OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0); OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0);
_filterSettings->SetSource(firstFilter); _filterSettings->SetSelection(firstFilter, _entryData->_setting);
_filterSettings->SetSetting(_entryData->_setting);
_settingsInputMethods->setCurrentIndex(_settingsInputMethods->findData( _settingsInputMethods->setCurrentIndex(_settingsInputMethods->findData(
static_cast<int>(_entryData->_settingsInputMethod))); static_cast<int>(_entryData->_settingsInputMethod)));
_tempVars->SetVariable(_entryData->_tempVar); _tempVars->SetVariable(_entryData->_tempVar);
_manualSettingValue->setPlainText(_entryData->_manualSettingValue); _manualSettingValue->setPlainText(_entryData->_manualSettingValue);
PopulateSourceButtonSelection(_settingsButtons, firstFilter); _settingsButtons->SetSelection(firstFilter, _entryData->_button);
_settingsButtons->setCurrentText(
QString::fromStdString(_entryData->_button.ToString()));
SetWidgetVisibility(); SetWidgetVisibility();
} }
void MacroActionFilterEdit::SourceChanged(const SourceSelection &source) void MacroActionFilterEdit::SourceChanged(const SourceSelection &source)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_source = source; _entryData->_source = source;
} }
@@ -337,11 +332,13 @@ void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
auto lock = LockContext(); auto lock = LockContext();
_entryData->_filter = filter; _entryData->_filter = filter;
} }
const auto filters = const auto filters =
_entryData->_filter.GetFilters(_entryData->_source); _entryData->_filter.GetFilters(_entryData->_source);
OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0); OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0);
_filterSettings->SetSource(firstFilter); _filterSettings->SetSource(firstFilter);
PopulateSourceButtonSelection(_settingsButtons, firstFilter); _settingsButtons->SetSource(firstFilter);
SetWidgetVisibility(); SetWidgetVisibility();
emit HeaderInfoChanged( emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc())); QString::fromStdString(_entryData->GetShortDesc()));
@@ -349,11 +346,7 @@ void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
void MacroActionFilterEdit::ActionChanged(int value) void MacroActionFilterEdit::ActionChanged(int value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionFilter::Action>(value); _entryData->_action = static_cast<MacroActionFilter::Action>(value);
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -391,11 +384,7 @@ void MacroActionFilterEdit::GetSettingsClicked()
void MacroActionFilterEdit::SettingsStringChanged() void MacroActionFilterEdit::SettingsStringChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_settingsString = _entryData->_settingsString =
_settingsString->toPlainText().toStdString(); _settingsString->toPlainText().toStdString();
@@ -405,11 +394,7 @@ void MacroActionFilterEdit::SettingsStringChanged()
void MacroActionFilterEdit::SettingsInputMethodChanged(int idx) void MacroActionFilterEdit::SettingsInputMethodChanged(int idx)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_settingsInputMethod = _entryData->_settingsInputMethod =
static_cast<MacroActionFilter::SettingsInputMethod>( static_cast<MacroActionFilter::SettingsInputMethod>(
_settingsInputMethods->itemData(idx).toInt()); _settingsInputMethods->itemData(idx).toInt());
@@ -418,31 +403,19 @@ void MacroActionFilterEdit::SettingsInputMethodChanged(int idx)
void MacroActionFilterEdit::SelectionChanged(const TempVariableRef &var) void MacroActionFilterEdit::SelectionChanged(const TempVariableRef &var)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_tempVar = var; _entryData->_tempVar = var;
} }
void MacroActionFilterEdit::SelectionChanged(const SourceSetting &setting) void MacroActionFilterEdit::SelectionChanged(const SourceSetting &setting)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_setting = setting; _entryData->_setting = setting;
} }
void MacroActionFilterEdit::ManualSettingsValueChanged() void MacroActionFilterEdit::ManualSettingsValueChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_manualSettingValue = _entryData->_manualSettingValue =
_manualSettingValue->toPlainText().toStdString(); _manualSettingValue->toPlainText().toStdString();
@@ -457,15 +430,10 @@ void MacroActionFilterEdit::RefreshVariableSourceSelectionValue()
_filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0)); _filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0));
} }
void MacroActionFilterEdit::ButtonChanged(int idx) void MacroActionFilterEdit::ButtonChanged(const SourceSettingButton &button)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return; _entryData->_button = button;
}
auto lock = LockContext();
_entryData->_button = qvariant_cast<SourceSettingButton>(
_settingsButtons->itemData(idx));
} }
void MacroActionFilterEdit::SetWidgetVisibility() void MacroActionFilterEdit::SetWidgetVisibility()
@@ -481,8 +449,11 @@ void MacroActionFilterEdit::SetWidgetVisibility()
_entryData->_action == MacroActionFilter::Action::SETTINGS && _entryData->_action == MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod == _entryData->_settingsInputMethod ==
MacroActionFilter::SettingsInputMethod::JSON_STRING); MacroActionFilter::SettingsInputMethod::JSON_STRING);
_getSettings->setVisible(_entryData->_action == _getSettings->setVisible(
MacroActionFilter::Action::SETTINGS); _entryData->_action == MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod !=
MacroActionFilter::SettingsInputMethod::
INDIVIDUAL_TEMPVAR);
_tempVars->setVisible(_entryData->_action == _tempVars->setVisible(_entryData->_action ==
MacroActionFilter::Action::SETTINGS && MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod == _entryData->_settingsInputMethod ==

View File

@@ -7,7 +7,6 @@
#include "source-setting.hpp" #include "source-setting.hpp"
#include <QComboBox> #include <QComboBox>
#include <QSpinBox>
#include <QPushButton> #include <QPushButton>
namespace advss { namespace advss {
@@ -82,7 +81,7 @@ private slots:
void SelectionChanged(const SourceSetting &); void SelectionChanged(const SourceSetting &);
void ManualSettingsValueChanged(); void ManualSettingsValueChanged();
void RefreshVariableSourceSelectionValue(); void RefreshVariableSourceSelectionValue();
void ButtonChanged(int idx); void ButtonChanged(const SourceSettingButton &);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -101,7 +100,7 @@ private:
SourceSettingSelection *_filterSettings; SourceSettingSelection *_filterSettings;
VariableTextEdit *_settingsString; VariableTextEdit *_settingsString;
QPushButton *_refreshSettingSelection; QPushButton *_refreshSettingSelection;
QComboBox *_settingsButtons; SourceSettingsButtonSelection *_settingsButtons;
std::shared_ptr<MacroActionFilter> _entryData; std::shared_ptr<MacroActionFilter> _entryData;
bool _loading = true; bool _loading = true;

View File

@@ -5,7 +5,6 @@
#include <QComboBox> #include <QComboBox>
#include <QCheckBox> #include <QCheckBox>
#include <QSpinBox>
#include <QHBoxLayout> #include <QHBoxLayout>
namespace advss { namespace advss {

View File

@@ -1,4 +1,4 @@
#include "macro-action-http.hpp" #include "macro-action-http-legacy.hpp"
#include "curl-helper.hpp" #include "curl-helper.hpp"
#include "layout-helpers.hpp" #include "layout-helpers.hpp"
@@ -9,7 +9,7 @@ const std::string MacroActionHttp::id = "http";
bool MacroActionHttp::_registered = MacroActionFactory::Register( bool MacroActionHttp::_registered = MacroActionFactory::Register(
MacroActionHttp::id, MacroActionHttp::id,
{MacroActionHttp::Create, MacroActionHttpEdit::Create, {MacroActionHttp::Create, MacroActionHttpEdit::Create,
"AdvSceneSwitcher.action.http"}); "AdvSceneSwitcher.action.http.legacy", true});
const static std::map<MacroActionHttp::Method, std::string> methods = { const static std::map<MacroActionHttp::Method, std::string> methods = {
{MacroActionHttp::Method::GET, "AdvSceneSwitcher.action.http.type.get"}, {MacroActionHttp::Method::GET, "AdvSceneSwitcher.action.http.type.get"},

View File

@@ -34,11 +34,11 @@ const static std::map<PluginStateAction, std::string> actionTypes = {
const static std::map<NoMatchBehavior, std::string> noMatchValues = { const static std::map<NoMatchBehavior, std::string> noMatchValues = {
{NoMatchBehavior::NO_SWITCH, {NoMatchBehavior::NO_SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch"}, "AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch"},
{NoMatchBehavior::SWITCH, {NoMatchBehavior::SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo"}, "AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo"},
{NoMatchBehavior::RANDOM_SWITCH, {NoMatchBehavior::RANDOM_SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom"}, "AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom"},
}; };
static void stopPlugin() static void stopPlugin()

View File

@@ -3,7 +3,6 @@
#include "file-selection.hpp" #include "file-selection.hpp"
#include <obs.hpp> #include <obs.hpp>
#include <QDoubleSpinBox>
#include <QLabel> #include <QLabel>
namespace advss { namespace advss {

View File

@@ -5,6 +5,8 @@
#include "source-helpers.hpp" #include "source-helpers.hpp"
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <QApplication>
#include <QWindow>
namespace advss { namespace advss {
@@ -28,8 +30,34 @@ const static std::map<MacroActionProjector::Type, std::string> selectionTypes =
"AdvSceneSwitcher.action.projector.type.multiview"}, "AdvSceneSwitcher.action.projector.type.multiview"},
}; };
static void closeOBSProjectorWindows(const std::string expectedWindowTitle,
const RegexConfig &regex)
{
for (QWindow *widget : QApplication::allWindows()) {
if (!widget->property("isOBSProjectorWindow").toBool()) {
continue;
}
auto const windowTitle = widget->title().toStdString();
if (!regex.Enabled() && expectedWindowTitle != windowTitle) {
continue;
}
if (!regex.Matches(windowTitle, expectedWindowTitle)) {
continue;
}
widget->close();
}
}
bool MacroActionProjector::PerformAction() bool MacroActionProjector::PerformAction()
{ {
if (_action == Action::CLOSE) {
closeOBSProjectorWindows(_projectorWindowName, _regex);
return true;
}
std::string name = ""; std::string name = "";
const char *type = ""; const char *type = "";
@@ -78,10 +106,16 @@ bool MacroActionProjector::PerformAction()
void MacroActionProjector::LogAction() const void MacroActionProjector::LogAction() const
{ {
if (_action == Action::CLOSE) {
ablog(LOG_INFO, "closing projector window \"%s\"",
_projectorWindowName.c_str());
return;
}
auto it = selectionTypes.find(_type); auto it = selectionTypes.find(_type);
if (it != selectionTypes.end()) { if (it != selectionTypes.end()) {
ablog(LOG_INFO, ablog(LOG_INFO,
"performed projector action \"%s\" with" "open projector \"%s\" with"
"source \"%s\"," "source \"%s\","
"scene \"%s\"," "scene \"%s\","
"monitor %d", "monitor %d",
@@ -96,24 +130,30 @@ void MacroActionProjector::LogAction() const
bool MacroActionProjector::Save(obs_data_t *obj) const bool MacroActionProjector::Save(obs_data_t *obj) const
{ {
MacroAction::Save(obj); MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_int(obj, "type", static_cast<int>(_type)); obs_data_set_int(obj, "type", static_cast<int>(_type));
obs_data_set_int(obj, "monitor", _monitor); obs_data_set_int(obj, "monitor", _monitor);
obs_data_set_string(obj, "monitorName", _monitorName.c_str()); obs_data_set_string(obj, "monitorName", _monitorName.c_str());
obs_data_set_bool(obj, "fullscreen", _fullscreen); obs_data_set_bool(obj, "fullscreen", _fullscreen);
_scene.Save(obj); _scene.Save(obj);
_source.Save(obj); _source.Save(obj);
_projectorWindowName.Save(obj, "projectorWindowName");
_regex.Save(obj);
return true; return true;
} }
bool MacroActionProjector::Load(obs_data_t *obj) bool MacroActionProjector::Load(obs_data_t *obj)
{ {
MacroAction::Load(obj); MacroAction::Load(obj);
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_type = static_cast<Type>(obs_data_get_int(obj, "type")); _type = static_cast<Type>(obs_data_get_int(obj, "type"));
_monitor = obs_data_get_int(obj, "monitor"); _monitor = obs_data_get_int(obj, "monitor");
_monitorName = obs_data_get_string(obj, "monitorName"); _monitorName = obs_data_get_string(obj, "monitorName");
_fullscreen = obs_data_get_bool(obj, "fullscreen"); _fullscreen = obs_data_get_bool(obj, "fullscreen");
_scene.Load(obj); _scene.Load(obj);
_source.Load(obj); _source.Load(obj);
_projectorWindowName.Load(obj, "projectorWindowName");
_regex.Load(obj);
return true; return true;
} }
@@ -131,6 +171,7 @@ void MacroActionProjector::ResolveVariablesToFixedValues()
{ {
_source.ResolveVariables(); _source.ResolveVariables();
_scene.ResolveVariables(); _scene.ResolveVariables();
_projectorWindowName.ResolveVariables();
} }
void MacroActionProjector::SetMonitor(int idx) void MacroActionProjector::SetMonitor(int idx)
@@ -166,14 +207,22 @@ bool MacroActionProjector::MonitorSetupChanged() const
QString::fromStdString(_monitorName); QString::fromStdString(_monitorName);
} }
static inline void populateSelectionTypes(QComboBox *list) static void populateActionSelection(QComboBox *list)
{ {
for (auto entry : selectionTypes) { list->addItem(obs_module_text(
list->addItem(obs_module_text(entry.second.c_str())); "AdvSceneSwitcher.action.projector.action.open"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.projector.action.close"));
}
static void populateSelectionTypes(QComboBox *list)
{
for (const auto &[_, name] : selectionTypes) {
list->addItem(obs_module_text(name.c_str()));
} }
} }
static inline void populateWindowTypes(QComboBox *list) static void populateWindowTypes(QComboBox *list)
{ {
list->addItem( list->addItem(
obs_module_text("AdvSceneSwitcher.action.projector.windowed")); obs_module_text("AdvSceneSwitcher.action.projector.windowed"));
@@ -184,14 +233,18 @@ static inline void populateWindowTypes(QComboBox *list)
MacroActionProjectorEdit::MacroActionProjectorEdit( MacroActionProjectorEdit::MacroActionProjectorEdit(
QWidget *parent, std::shared_ptr<MacroActionProjector> entryData) QWidget *parent, std::shared_ptr<MacroActionProjector> entryData)
: QWidget(parent), : QWidget(parent),
_windowTypes(new QComboBox()), _actions(new QComboBox()),
_types(new QComboBox()), _types(new QComboBox()),
_windowTypes(new QComboBox()),
_scenes(new SceneSelectionWidget(window(), true, false, true, true, _scenes(new SceneSelectionWidget(window(), true, false, true, true,
true)), true)),
_sources(new SourceSelectionWidget(window(), QStringList(), true)), _sources(new SourceSelectionWidget(window(), QStringList(), true)),
_monitorSelection(new QHBoxLayout()), _monitors(new QComboBox()),
_monitors(new QComboBox()) _projectorWindowName(new VariableLineEdit(this)),
_regex(new RegexConfigWidget(this)),
_layout(new QHBoxLayout(this))
{ {
populateActionSelection(_actions);
populateWindowTypes(_windowTypes); populateWindowTypes(_windowTypes);
populateSelectionTypes(_types); populateSelectionTypes(_types);
auto sources = GetSourceNames(); auto sources = GetSourceNames();
@@ -201,6 +254,8 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
_monitors->setPlaceholderText( _monitors->setPlaceholderText(
obs_module_text("AdvSceneSwitcher.selectDisplay")); obs_module_text("AdvSceneSwitcher.selectDisplay"));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(WindowTypeChanged(int))); SLOT(WindowTypeChanged(int)));
QWidget::connect(_types, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_types, SIGNAL(currentIndexChanged(int)), this,
@@ -212,24 +267,15 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
SLOT(SourceChanged(const SourceSelection &))); SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_monitors, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_monitors, SIGNAL(currentIndexChanged(int)), this,
SLOT(MonitorChanged(int))); SLOT(MonitorChanged(int)));
QWidget::connect(_projectorWindowName, SIGNAL(editingFinished()), this,
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { SLOT(ProjectorWindowNameChanged()));
{"{{windowTypes}}", _windowTypes}, {"{{types}}", _types}, QWidget::connect(_regex,
{"{{scenes}}", _scenes}, {"{{sources}}", _sources}, SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
{"{{monitors}}", _monitors}, SLOT(RegexChanged(const RegexConfig &)));
};
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.projector.entry.monitor"),
_monitorSelection, widgetPlaceholders);
QHBoxLayout *mainLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.projector.entry"),
mainLayout, widgetPlaceholders);
mainLayout->insertLayout(mainLayout->count() - 1, _monitorSelection);
setLayout(mainLayout);
_entryData = entryData; _entryData = entryData;
SetWidgetLayout();
setLayout(_layout);
UpdateEntryData(); UpdateEntryData();
_loading = false; _loading = false;
} }
@@ -239,11 +285,15 @@ void MacroActionProjectorEdit::UpdateEntryData()
if (!_entryData) { if (!_entryData) {
return; return;
} }
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_windowTypes->setCurrentIndex(_entryData->_fullscreen ? 1 : 0); _windowTypes->setCurrentIndex(_entryData->_fullscreen ? 1 : 0);
_types->setCurrentIndex(static_cast<int>(_entryData->_type)); _types->setCurrentIndex(static_cast<int>(_entryData->_type));
_scenes->SetScene(_entryData->_scene); _scenes->SetScene(_entryData->_scene);
_sources->SetSource(_entryData->_source); _sources->SetSource(_entryData->_source);
_monitors->setCurrentIndex(_entryData->GetMonitor()); _monitors->setCurrentIndex(_entryData->GetMonitor());
_projectorWindowName->setText(_entryData->_projectorWindowName);
_regex->SetRegexConfig(_entryData->_regex);
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -265,12 +315,26 @@ void MacroActionProjectorEdit::MonitorChanged(int value)
_entryData->SetMonitor(value); _entryData->SetMonitor(value);
} }
void MacroActionProjectorEdit::ProjectorWindowNameChanged()
{
GUARD_LOADING_AND_LOCK();
_entryData->_projectorWindowName =
_projectorWindowName->text().toStdString();
}
void MacroActionProjectorEdit::RegexChanged(const RegexConfig &regex)
{
GUARD_LOADING_AND_LOCK();
_entryData->_regex = regex;
}
void MacroActionProjectorEdit::WindowTypeChanged(int) void MacroActionProjectorEdit::WindowTypeChanged(int)
{ {
GUARD_LOADING_AND_LOCK(); GUARD_LOADING_AND_LOCK();
_entryData->_fullscreen = _entryData->_fullscreen =
_windowTypes->currentText() == _windowTypes->currentText() ==
obs_module_text("AdvSceneSwitcher.action.projector.fullscreen"); obs_module_text("AdvSceneSwitcher.action.projector.fullscreen");
SetWidgetLayout();
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -281,17 +345,69 @@ void MacroActionProjectorEdit::TypeChanged(int value)
SetWidgetVisibility(); SetWidgetVisibility();
} }
void MacroActionProjectorEdit::ActionChanged(int idx)
{
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionProjector::Action>(idx);
SetWidgetLayout();
SetWidgetVisibility();
}
void MacroActionProjectorEdit::SetWidgetLayout()
{
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{windowTypes}}", _windowTypes},
{"{{types}}", _types},
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{monitors}}", _monitors},
{"{{projectorWindowName}}", _projectorWindowName},
{"{{regex}}", _regex},
};
for (const auto &[_, widget] : widgetPlaceholders) {
_layout->removeWidget(widget);
}
ClearLayout(_layout);
const char *layoutText;
if (_entryData->_action == MacroActionProjector::Action::CLOSE) {
layoutText = "AdvSceneSwitcher.action.projector.entry.close";
} else if (_entryData->_fullscreen) {
layoutText =
"AdvSceneSwitcher.action.projector.entry.open.fullscreen";
} else {
layoutText =
"AdvSceneSwitcher.action.projector.entry.open.windowed";
}
PlaceWidgets(obs_module_text(layoutText), _layout, widgetPlaceholders);
}
void MacroActionProjectorEdit::SetWidgetVisibility() void MacroActionProjectorEdit::SetWidgetVisibility()
{ {
if (!_entryData) { if (!_entryData) {
return; return;
} }
_scenes->setVisible(_entryData->_type == _projectorWindowName->setVisible(_entryData->_action ==
MacroActionProjector::Type::SCENE); MacroActionProjector::Action::CLOSE);
_sources->setVisible(_entryData->_type == _regex->setVisible(_entryData->_action ==
MacroActionProjector::Type::SOURCE); MacroActionProjector::Action::CLOSE);
SetLayoutVisible(_monitorSelection, _entryData->_fullscreen); _types->setVisible(_entryData->_action ==
MacroActionProjector::Action::OPEN);
_windowTypes->setVisible(_entryData->_action ==
MacroActionProjector::Action::OPEN);
_scenes->setVisible(
_entryData->_action == MacroActionProjector::Action::OPEN &&
_entryData->_type == MacroActionProjector::Type::SCENE);
_sources->setVisible(
_entryData->_action == MacroActionProjector::Action::OPEN &&
_entryData->_type == MacroActionProjector::Type::SOURCE);
_monitors->setVisible(_entryData->_action ==
MacroActionProjector::Action::OPEN &&
_entryData->_fullscreen);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();

View File

@@ -1,7 +1,9 @@
#pragma once #pragma once
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "regex-config.hpp"
#include "scene-selection.hpp" #include "scene-selection.hpp"
#include "source-selection.hpp" #include "source-selection.hpp"
#include "variable-line-edit.hpp"
namespace advss { namespace advss {
@@ -19,6 +21,11 @@ public:
void SetMonitor(int); void SetMonitor(int);
int GetMonitor() const; int GetMonitor() const;
enum class Action {
OPEN,
CLOSE,
};
enum class Type { enum class Type {
SOURCE, SOURCE,
SCENE, SCENE,
@@ -27,10 +34,13 @@ public:
MULTIVIEW, MULTIVIEW,
}; };
Action _action = Action::OPEN;
Type _type = Type::SCENE; Type _type = Type::SCENE;
SourceSelection _source; SourceSelection _source;
SceneSelection _scene; SceneSelection _scene;
bool _fullscreen = true; bool _fullscreen = true;
StringVariable _projectorWindowName = "Windowed Projector";
RegexConfig _regex = RegexConfig::PartialMatchRegexConfig(true);
private: private:
bool MonitorSetupChanged() const; bool MonitorSetupChanged() const;
@@ -60,21 +70,28 @@ public:
} }
private slots: private slots:
void ActionChanged(int value);
void WindowTypeChanged(int value); void WindowTypeChanged(int value);
void TypeChanged(int value); void TypeChanged(int value);
void SceneChanged(const SceneSelection &); void SceneChanged(const SceneSelection &);
void SourceChanged(const SourceSelection &); void SourceChanged(const SourceSelection &);
void MonitorChanged(int value); void MonitorChanged(int value);
void ProjectorWindowNameChanged();
void RegexChanged(const RegexConfig &);
private: private:
void SetWidgetLayout();
void SetWidgetVisibility(); void SetWidgetVisibility();
QComboBox *_windowTypes; QComboBox *_actions;
QComboBox *_types; QComboBox *_types;
QComboBox *_windowTypes;
SceneSelectionWidget *_scenes; SceneSelectionWidget *_scenes;
SourceSelectionWidget *_sources; SourceSelectionWidget *_sources;
QHBoxLayout *_monitorSelection;
QComboBox *_monitors; QComboBox *_monitors;
VariableLineEdit *_projectorWindowName;
RegexConfigWidget *_regex;
QHBoxLayout *_layout;
std::shared_ptr<MacroActionProjector> _entryData; std::shared_ptr<MacroActionProjector> _entryData;
bool _loading = true; bool _loading = true;

View File

@@ -142,10 +142,10 @@ void MacroActionReplayBufferEdit::UpdateEntryData()
} }
_actions->setCurrentIndex(static_cast<int>(_entryData->_action)); _actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_duration->SetDuration(_entryData->_duration); _duration->SetDuration(_entryData->_duration);
SetWidgetVisiblity(); SetWidgetVisibility();
} }
void MacroActionReplayBufferEdit::SetWidgetVisiblity() void MacroActionReplayBufferEdit::SetWidgetVisibility()
{ {
_warning->setVisible(_entryData->_action == _warning->setVisible(_entryData->_action ==
MacroActionReplayBuffer::Action::SAVE); MacroActionReplayBuffer::Action::SAVE);
@@ -169,7 +169,7 @@ void MacroActionReplayBufferEdit::ActionChanged(int value)
_entryData->_action = _entryData->_action =
static_cast<MacroActionReplayBuffer::Action>(value); static_cast<MacroActionReplayBuffer::Action>(value);
SetWidgetVisiblity(); SetWidgetVisibility();
} }
void MacroActionReplayBufferEdit::DurationChanged(const Duration &duration) void MacroActionReplayBufferEdit::DurationChanged(const Duration &duration)

View File

@@ -53,7 +53,7 @@ private slots:
void DurationChanged(const Duration &); void DurationChanged(const Duration &);
private: private:
void SetWidgetVisiblity(); void SetWidgetVisibility();
QComboBox *_actions; QComboBox *_actions;
QLabel *_warning; QLabel *_warning;

View File

@@ -10,17 +10,21 @@ bool MacroActionSceneOrder::_registered = MacroActionFactory::Register(
{MacroActionSceneOrder::Create, MacroActionSceneOrderEdit::Create, {MacroActionSceneOrder::Create, MacroActionSceneOrderEdit::Create,
"AdvSceneSwitcher.action.sceneOrder"}); "AdvSceneSwitcher.action.sceneOrder"});
const static std::map<SceneOrderAction, std::string> actionTypes = { const static std::map<MacroActionSceneOrder::Action, std::string> actionTypes = {
{SceneOrderAction::MOVE_UP, {MacroActionSceneOrder::Action::MOVE_UP,
"AdvSceneSwitcher.action.sceneOrder.type.moveUp"}, "AdvSceneSwitcher.action.sceneOrder.type.moveUp"},
{SceneOrderAction::MOVE_DOWN, {MacroActionSceneOrder::Action::MOVE_DOWN,
"AdvSceneSwitcher.action.sceneOrder.type.moveDown"}, "AdvSceneSwitcher.action.sceneOrder.type.moveDown"},
{SceneOrderAction::MOVE_TOP, {MacroActionSceneOrder::Action::MOVE_TOP,
"AdvSceneSwitcher.action.sceneOrder.type.moveTop"}, "AdvSceneSwitcher.action.sceneOrder.type.moveTop"},
{SceneOrderAction::MOVE_BOTTOM, {MacroActionSceneOrder::Action::MOVE_BOTTOM,
"AdvSceneSwitcher.action.sceneOrder.type.moveBottom"}, "AdvSceneSwitcher.action.sceneOrder.type.moveBottom"},
{SceneOrderAction::POSITION, {MacroActionSceneOrder::Action::POSITION,
"AdvSceneSwitcher.action.sceneOrder.type.movePosition"}, "AdvSceneSwitcher.action.sceneOrder.type.movePosition"},
{MacroActionSceneOrder::Action::ABOVE,
"AdvSceneSwitcher.action.sceneOrder.type.above"},
{MacroActionSceneOrder::Action::BELOW,
"AdvSceneSwitcher.action.sceneOrder.type.below"},
}; };
static void moveSceneItemsUp(std::vector<OBSSceneItem> &items) static void moveSceneItemsUp(std::vector<OBSSceneItem> &items)
@@ -66,26 +70,128 @@ static void moveSceneItemsPos(std::vector<OBSSceneItem> &items, int pos)
} }
} }
namespace {
struct PositionData {
obs_scene_item *item = nullptr;
bool found = false;
int position = 0;
};
} // namespace
static bool getSceneItemPositionHelper(obs_scene_t *, obs_sceneitem_t *item,
void *data)
{
auto positionData = reinterpret_cast<PositionData *>(data);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, getSceneItemPositionHelper, data);
}
if (positionData->item == item) {
positionData->found = true;
return false;
}
positionData->position += 1;
return true;
}
static std::optional<int> getSceneItemPosition(const OBSSceneItem &item,
const SceneSelection &scene)
{
auto sceneSource = OBSGetStrongRef(scene.GetScene());
auto obsScene = obs_scene_from_source(sceneSource);
PositionData data{item};
obs_scene_enum_items(obsScene, getSceneItemPositionHelper, &data);
if (!data.found) {
return {};
}
return data.position;
}
static void moveItemFromToHelper(MacroActionSceneOrder::Action action,
const OBSSceneItem &itemToMove,
int currentPosition, int targetPosition)
{
if (action == MacroActionSceneOrder::Action::ABOVE) {
if (currentPosition > targetPosition) {
obs_sceneitem_set_order_position(itemToMove,
targetPosition + 1);
} else {
obs_sceneitem_set_order_position(itemToMove,
targetPosition);
}
} else if (action == MacroActionSceneOrder::Action::BELOW) {
if (currentPosition > targetPosition) {
obs_sceneitem_set_order_position(itemToMove,
targetPosition);
} else {
obs_sceneitem_set_order_position(itemToMove,
targetPosition - 1);
}
}
}
static void moveItemToItemHelper(MacroActionSceneOrder::Action action,
const std::vector<OBSSceneItem> &itemsToMove,
const SceneItemSelection &target,
const SceneSelection &scene)
{
auto targetItems = target.GetSceneItems(scene);
if (targetItems.empty()) {
return;
}
auto targetItem = targetItems.at(0);
for (const auto &item : itemsToMove) {
if (item == targetItem) {
continue;
}
auto targetPosition = getSceneItemPosition(targetItem, scene);
if (!targetPosition) {
continue;
}
auto currentPosition = getSceneItemPosition(item, scene);
if (!currentPosition) {
continue;
}
moveItemFromToHelper(action, item, *currentPosition,
*targetPosition);
}
}
bool MacroActionSceneOrder::PerformAction() bool MacroActionSceneOrder::PerformAction()
{ {
auto items = _source.GetSceneItems(_scene); auto items = _source.GetSceneItems(_scene);
switch (_action) { switch (_action) {
case SceneOrderAction::MOVE_UP: case Action::MOVE_UP:
moveSceneItemsUp(items); moveSceneItemsUp(items);
break; break;
case SceneOrderAction::MOVE_DOWN: case Action::MOVE_DOWN:
moveSceneItemsDown(items); moveSceneItemsDown(items);
break; break;
case SceneOrderAction::MOVE_TOP: case Action::MOVE_TOP:
moveSceneItemsTop(items); moveSceneItemsTop(items);
break; break;
case SceneOrderAction::MOVE_BOTTOM: case Action::MOVE_BOTTOM:
moveSceneItemsBottom(items); moveSceneItemsBottom(items);
break; break;
case SceneOrderAction::POSITION: case Action::POSITION:
moveSceneItemsPos(items, _position); moveSceneItemsPos(items, _position);
break; break;
case Action::ABOVE:
case Action::BELOW: {
moveItemToItemHelper(_action, items, _source2, _scene);
break;
}
default: default:
break; break;
} }
@@ -111,6 +217,7 @@ bool MacroActionSceneOrder::Save(obs_data_t *obj) const
MacroAction::Save(obj); MacroAction::Save(obj);
_scene.Save(obj); _scene.Save(obj);
_source.Save(obj); _source.Save(obj);
_source2.Save(obj, "sceneItemSelection2");
obs_data_set_int(obj, "action", static_cast<int>(_action)); obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_int(obj, "position", _position); obs_data_set_int(obj, "position", _position);
return true; return true;
@@ -128,8 +235,8 @@ bool MacroActionSceneOrder::Load(obs_data_t *obj)
MacroAction::Load(obj); MacroAction::Load(obj);
_scene.Load(obj); _scene.Load(obj);
_source.Load(obj); _source.Load(obj);
_action = _source2.Load(obj, "sceneItemSelection2");
static_cast<SceneOrderAction>(obs_data_get_int(obj, "action")); _action = static_cast<Action>(obs_data_get_int(obj, "action"));
_position = obs_data_get_int(obj, "position"); _position = obs_data_get_int(obj, "position");
return true; return true;
} }
@@ -167,13 +274,13 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionSceneOrderEdit::MacroActionSceneOrderEdit( MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneOrder> entryData) QWidget *parent, std::shared_ptr<MacroActionSceneOrder> entryData)
: QWidget(parent) : QWidget(parent),
_scenes(new SceneSelectionWidget(this, true, false, false, true)),
_sources(new SceneItemSelectionWidget(this)),
_sources2(new SceneItemSelectionWidget(this)),
_actions(new QComboBox(this)),
_position(new QSpinBox(this))
{ {
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
_sources = new SceneItemSelectionWidget(parent);
_actions = new QComboBox();
_position = new QSpinBox();
populateActionSelection(_actions); populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
@@ -182,23 +289,28 @@ MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
this, SLOT(SceneChanged(const SceneSelection &))); this, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
_sources, SLOT(SceneChanged(const SceneSelection &))); _sources, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
_sources2, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_sources, QWidget::connect(_sources,
SIGNAL(SceneItemChanged(const SceneItemSelection &)), SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this, SLOT(SourceChanged(const SceneItemSelection &))); this, SLOT(SourceChanged(const SceneItemSelection &)));
QWidget::connect(_sources2,
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this,
SLOT(Source2Changed(const SceneItemSelection &)));
QWidget::connect(_position, SIGNAL(valueChanged(int)), this, QWidget::connect(_position, SIGNAL(valueChanged(int)), this,
SLOT(PositionChanged(int))); SLOT(PositionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout; auto layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{actions}}", _actions},
{"{{position}}", _position},
};
PlaceWidgets( PlaceWidgets(
obs_module_text("AdvSceneSwitcher.action.sceneOrder.entry"), obs_module_text("AdvSceneSwitcher.action.sceneOrder.entry"),
mainLayout, widgetPlaceholders); layout,
setLayout(mainLayout); {{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{sources2}}", _sources2},
{"{{actions}}", _actions},
{"{{position}}", _position}});
setLayout(layout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
@@ -214,28 +326,20 @@ void MacroActionSceneOrderEdit::UpdateEntryData()
_actions->setCurrentIndex(static_cast<int>(_entryData->_action)); _actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_scenes->SetScene(_entryData->_scene); _scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem(_entryData->_source); _sources->SetSceneItem(_entryData->_source);
_sources2->SetSceneItem(_entryData->_source2);
_position->setValue(_entryData->_position); _position->setValue(_entryData->_position);
_position->setVisible(_entryData->_action == SetWidgetVisibility();
SceneOrderAction::POSITION);
} }
void MacroActionSceneOrderEdit::SceneChanged(const SceneSelection &s) void MacroActionSceneOrderEdit::SceneChanged(const SceneSelection &s)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_scene = s; _entryData->_scene = s;
} }
void MacroActionSceneOrderEdit::SourceChanged(const SceneItemSelection &item) void MacroActionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_source = item; _entryData->_source = item;
emit HeaderInfoChanged( emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc())); QString::fromStdString(_entryData->GetShortDesc()));
@@ -243,26 +347,36 @@ void MacroActionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
updateGeometry(); updateGeometry();
} }
void MacroActionSceneOrderEdit::Source2Changed(const SceneItemSelection &item)
{
GUARD_LOADING_AND_LOCK();
_entryData->_source2 = item;
adjustSize();
updateGeometry();
}
void MacroActionSceneOrderEdit::ActionChanged(int value) void MacroActionSceneOrderEdit::ActionChanged(int value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return; _entryData->_action = static_cast<MacroActionSceneOrder::Action>(value);
} SetWidgetVisibility();
auto lock = LockContext();
_entryData->_action = static_cast<SceneOrderAction>(value);
_position->setVisible(_entryData->_action ==
SceneOrderAction::POSITION);
} }
void MacroActionSceneOrderEdit::PositionChanged(int value) void MacroActionSceneOrderEdit::PositionChanged(int value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_position = value; _entryData->_position = value;
} }
void MacroActionSceneOrderEdit::SetWidgetVisibility()
{
_position->setVisible(_entryData->_action ==
MacroActionSceneOrder::Action::POSITION);
_sources2->setVisible(
_entryData->_action == MacroActionSceneOrder::Action::ABOVE ||
_entryData->_action == MacroActionSceneOrder::Action::BELOW);
adjustSize();
updateGeometry();
}
} // namespace advss } // namespace advss

View File

@@ -7,14 +7,6 @@
namespace advss { namespace advss {
enum class SceneOrderAction {
MOVE_UP,
MOVE_DOWN,
MOVE_TOP,
MOVE_BOTTOM,
POSITION,
};
class MacroActionSceneOrder : public MacroAction { class MacroActionSceneOrder : public MacroAction {
public: public:
MacroActionSceneOrder(Macro *m) : MacroAction(m) {} MacroActionSceneOrder(Macro *m) : MacroAction(m) {}
@@ -30,7 +22,17 @@ public:
SceneSelection _scene; SceneSelection _scene;
SceneItemSelection _source; SceneItemSelection _source;
SceneOrderAction _action = SceneOrderAction::MOVE_UP; SceneItemSelection _source2;
enum class Action {
MOVE_UP,
MOVE_DOWN,
MOVE_TOP,
MOVE_BOTTOM,
POSITION,
ABOVE,
BELOW,
};
Action _action = Action::MOVE_UP;
int _position = 0; int _position = 0;
private: private:
@@ -58,20 +60,22 @@ public:
private slots: private slots:
void SceneChanged(const SceneSelection &); void SceneChanged(const SceneSelection &);
void SourceChanged(const SceneItemSelection &); void SourceChanged(const SceneItemSelection &);
void Source2Changed(const SceneItemSelection &);
void ActionChanged(int value); void ActionChanged(int value);
void PositionChanged(int value); void PositionChanged(int value);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected: private:
void SetWidgetVisibility();
SceneSelectionWidget *_scenes; SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources; SceneItemSelectionWidget *_sources;
SceneItemSelectionWidget *_sources2;
QComboBox *_actions; QComboBox *_actions;
QSpinBox *_position; QSpinBox *_position;
std::shared_ptr<MacroActionSceneOrder> _entryData;
private: std::shared_ptr<MacroActionSceneOrder> _entryData;
QHBoxLayout *_mainLayout;
bool _loading = true; bool _loading = true;
}; };

View File

@@ -6,8 +6,6 @@
#include "variable-text-edit.hpp" #include "variable-text-edit.hpp"
#include "variable-spinbox.hpp" #include "variable-spinbox.hpp"
#include <QSpinBox>
namespace advss { namespace advss {
class MacroActionSceneTransform : public MacroAction { class MacroActionSceneTransform : public MacroAction {

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