mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 04:36:21 -05:00
Compare commits
37 Commits
source-int
...
1.27.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
510f83246e | ||
|
|
c969b21f93 | ||
|
|
6ec40ef8e9 | ||
|
|
27859e83b3 | ||
|
|
263565700a | ||
|
|
5bb10a4aef | ||
|
|
ee72bb192c | ||
|
|
1a1028cbaa | ||
|
|
2f4f8bcc74 | ||
|
|
4113615cd6 | ||
|
|
9522d7c0b4 | ||
|
|
cc3ea79836 | ||
|
|
f72e802d00 | ||
|
|
36201cbfb4 | ||
|
|
685e28d161 | ||
|
|
82d23dcf0e | ||
|
|
ad322d54f0 | ||
|
|
8bac6fe829 | ||
|
|
ac7531decb | ||
|
|
7af4969ec6 | ||
|
|
a088325968 | ||
|
|
57bc58be8e | ||
|
|
f9730b1bc2 | ||
|
|
d4025214e5 | ||
|
|
c431e6c31d | ||
|
|
9941cfe9b5 | ||
|
|
15998012e5 | ||
|
|
bcb043f997 | ||
|
|
74116382b1 | ||
|
|
24f33fb0d2 | ||
|
|
d80df57ef8 | ||
|
|
240c47975c | ||
|
|
bedb3b8dc6 | ||
|
|
8032dde045 | ||
|
|
bfc51b2df5 | ||
|
|
2160da59f3 | ||
|
|
d3164e3ed6 |
7
.github/scripts/.build-deps.zsh
vendored
7
.github/scripts/.build-deps.zsh
vendored
@@ -456,7 +456,12 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
|||||||
popd
|
popd
|
||||||
;;
|
;;
|
||||||
linux)
|
linux)
|
||||||
# Nothing to do for now
|
# Hacky workaround to support libproc2 with Ubuntu 22 build environment
|
||||||
|
local lsb_version=$(lsb_release -r | cut -f 2 || true)
|
||||||
|
if [[ "${lsb_version}" == '22.04' ]] {
|
||||||
|
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
|
||||||
|
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
|
||||||
|
}
|
||||||
;;
|
;;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,6 +7,7 @@
|
|||||||
!/cmake
|
!/cmake
|
||||||
!/data
|
!/data
|
||||||
!/deps
|
!/deps
|
||||||
|
!/scripting
|
||||||
!/forms
|
!/forms
|
||||||
!/lib
|
!/lib
|
||||||
!/module
|
!/module
|
||||||
@@ -35,3 +36,6 @@
|
|||||||
|
|
||||||
# Exclude CMake build number cache
|
# Exclude CMake build number cache
|
||||||
/cmake/.CMakeBuildNumber
|
/cmake/.CMakeBuildNumber
|
||||||
|
|
||||||
|
# Exclude python caches
|
||||||
|
/**/__pycache__
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ target_sources(
|
|||||||
lib/macro/macro-action-macro.hpp
|
lib/macro/macro-action-macro.hpp
|
||||||
lib/macro/macro-action-queue.cpp
|
lib/macro/macro-action-queue.cpp
|
||||||
lib/macro/macro-action-queue.hpp
|
lib/macro/macro-action-queue.hpp
|
||||||
|
lib/macro/macro-action-script.cpp
|
||||||
|
lib/macro/macro-action-script.hpp
|
||||||
lib/macro/macro-action-variable.cpp
|
lib/macro/macro-action-variable.cpp
|
||||||
lib/macro/macro-action-variable.hpp
|
lib/macro/macro-action-variable.hpp
|
||||||
lib/macro/macro-action.cpp
|
lib/macro/macro-action.cpp
|
||||||
@@ -108,6 +110,8 @@ target_sources(
|
|||||||
lib/macro/macro-condition-macro.hpp
|
lib/macro/macro-condition-macro.hpp
|
||||||
lib/macro/macro-condition-queue.cpp
|
lib/macro/macro-condition-queue.cpp
|
||||||
lib/macro/macro-condition-queue.hpp
|
lib/macro/macro-condition-queue.hpp
|
||||||
|
lib/macro/macro-condition-script.cpp
|
||||||
|
lib/macro/macro-condition-script.hpp
|
||||||
lib/macro/macro-condition-tempvar.cpp
|
lib/macro/macro-condition-tempvar.cpp
|
||||||
lib/macro/macro-condition-tempvar.hpp
|
lib/macro/macro-condition-tempvar.hpp
|
||||||
lib/macro/macro-condition-variable.cpp
|
lib/macro/macro-condition-variable.cpp
|
||||||
@@ -128,10 +132,14 @@ target_sources(
|
|||||||
lib/macro/macro-ref.hpp
|
lib/macro/macro-ref.hpp
|
||||||
lib/macro/macro-run-button.cpp
|
lib/macro/macro-run-button.cpp
|
||||||
lib/macro/macro-run-button.hpp
|
lib/macro/macro-run-button.hpp
|
||||||
|
lib/macro/macro-script-handler.cpp
|
||||||
|
lib/macro/macro-script-handler.hpp
|
||||||
lib/macro/macro-segment-copy-paste.cpp
|
lib/macro/macro-segment-copy-paste.cpp
|
||||||
lib/macro/macro-segment-copy-paste.hpp
|
lib/macro/macro-segment-copy-paste.hpp
|
||||||
lib/macro/macro-segment-list.cpp
|
lib/macro/macro-segment-list.cpp
|
||||||
lib/macro/macro-segment-list.hpp
|
lib/macro/macro-segment-list.hpp
|
||||||
|
lib/macro/macro-segment-script.cpp
|
||||||
|
lib/macro/macro-segment-script.hpp
|
||||||
lib/macro/macro-segment-selection.cpp
|
lib/macro/macro-segment-selection.cpp
|
||||||
lib/macro/macro-segment-selection.hpp
|
lib/macro/macro-segment-selection.hpp
|
||||||
lib/macro/macro-segment.cpp
|
lib/macro/macro-segment.cpp
|
||||||
@@ -155,6 +163,8 @@ target_sources(
|
|||||||
lib/queue/action-queue-tab.hpp
|
lib/queue/action-queue-tab.hpp
|
||||||
lib/utils/backup.cpp
|
lib/utils/backup.cpp
|
||||||
lib/utils/backup.hpp
|
lib/utils/backup.hpp
|
||||||
|
lib/utils/condition-logic.cpp
|
||||||
|
lib/utils/condition-logic.hpp
|
||||||
lib/utils/curl-helper.cpp
|
lib/utils/curl-helper.cpp
|
||||||
lib/utils/curl-helper.hpp
|
lib/utils/curl-helper.hpp
|
||||||
lib/utils/cursor-shape-changer.cpp
|
lib/utils/cursor-shape-changer.cpp
|
||||||
@@ -163,6 +173,8 @@ target_sources(
|
|||||||
lib/utils/double-slider.hpp
|
lib/utils/double-slider.hpp
|
||||||
lib/utils/duration-control.cpp
|
lib/utils/duration-control.cpp
|
||||||
lib/utils/duration-control.hpp
|
lib/utils/duration-control.hpp
|
||||||
|
lib/utils/duration-modifier.cpp
|
||||||
|
lib/utils/duration-modifier.hpp
|
||||||
lib/utils/duration.cpp
|
lib/utils/duration.cpp
|
||||||
lib/utils/duration.hpp
|
lib/utils/duration.hpp
|
||||||
lib/utils/export-symbol-helper.hpp
|
lib/utils/export-symbol-helper.hpp
|
||||||
@@ -192,7 +204,6 @@ target_sources(
|
|||||||
lib/utils/name-dialog.hpp
|
lib/utils/name-dialog.hpp
|
||||||
lib/utils/non-modal-dialog.cpp
|
lib/utils/non-modal-dialog.cpp
|
||||||
lib/utils/non-modal-dialog.hpp
|
lib/utils/non-modal-dialog.hpp
|
||||||
lib/utils/obs-dock.hpp
|
|
||||||
lib/utils/obs-module-helper.cpp
|
lib/utils/obs-module-helper.cpp
|
||||||
lib/utils/obs-module-helper.hpp
|
lib/utils/obs-module-helper.hpp
|
||||||
lib/utils/path-helpers.cpp
|
lib/utils/path-helpers.cpp
|
||||||
@@ -201,6 +212,9 @@ target_sources(
|
|||||||
lib/utils/plugin-state-helpers.hpp
|
lib/utils/plugin-state-helpers.hpp
|
||||||
lib/utils/priority-helper.cpp
|
lib/utils/priority-helper.cpp
|
||||||
lib/utils/priority-helper.hpp
|
lib/utils/priority-helper.hpp
|
||||||
|
lib/utils/properties-view.cpp
|
||||||
|
lib/utils/properties-view.hpp
|
||||||
|
lib/utils/properties-view.moc.hpp
|
||||||
lib/utils/regex-config.cpp
|
lib/utils/regex-config.cpp
|
||||||
lib/utils/regex-config.hpp
|
lib/utils/regex-config.hpp
|
||||||
lib/utils/resizing-text-edit.cpp
|
lib/utils/resizing-text-edit.cpp
|
||||||
@@ -264,6 +278,11 @@ target_sources(
|
|||||||
|
|
||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
|
||||||
|
# Subfolder for advanced scene switcher plugins
|
||||||
|
set(ADVSS_PLUGIN_FOLDER "advanced-scene-switcher-plugins")
|
||||||
|
target_compile_definitions(
|
||||||
|
${LIB_NAME} PRIVATE ADVSS_PLUGIN_FOLDER=\"${ADVSS_PLUGIN_FOLDER}\")
|
||||||
|
|
||||||
include(cmake/common/advss_helpers.cmake)
|
include(cmake/common/advss_helpers.cmake)
|
||||||
setup_obs_lib_dependency(${LIB_NAME})
|
setup_obs_lib_dependency(${LIB_NAME})
|
||||||
setup_obs_lib_dependency(${PROJECT_NAME})
|
setup_obs_lib_dependency(${PROJECT_NAME})
|
||||||
|
|||||||
@@ -22,5 +22,6 @@ sudo snap install obs-studio
|
|||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
- If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin.
|
- If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin.
|
||||||
|
- You can add custom conditions and actions at runtime using the API described [here](https://github.com/WarmUpTill/SceneSwitcher/wiki/Scripting).
|
||||||
- You can optionally use [pre-commit](https://pre-commit.com) to automatically handle formatting.
|
- You can optionally use [pre-commit](https://pre-commit.com) to automatically handle formatting.
|
||||||
- If you wish to contribute translations, feel free to submit pull requests for the corresponding files under `data/locale`.
|
- If you wish to contribute translations, feel free to submit pull requests for the corresponding files under `data/locale`.
|
||||||
|
|||||||
BIN
build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
Normal file
BIN
build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
Normal file
Binary file not shown.
BIN
build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
Normal file
BIN
build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
Normal file
Binary file not shown.
@@ -1,33 +1,33 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"obs-studio": {
|
"obs-studio": {
|
||||||
"version": "29.1.2",
|
"version": "30.0.2",
|
||||||
"baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags",
|
"baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags",
|
||||||
"label": "OBS sources",
|
"label": "OBS sources",
|
||||||
"hashes": {
|
"hashes": {
|
||||||
"macos": "215f1fa5772c5dd9f3d6e35b0cb573912b00320149666a77864f9d305525504b",
|
"macos": "be12c3ad0a85713750d8325e4b1db75086223402d7080d0e3c2833d7c5e83c27",
|
||||||
"windows-x64": "46d451f3f42b9d2c59339ec268165849c7b7904cdf1cc2a8d44c015815a9e37d"
|
"windows-x64": "970058c49322cfa9cd6d620abb393fed89743ba7e74bd9dbb6ebe0ea8141d9c7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"prebuilt": {
|
"prebuilt": {
|
||||||
"version": "2023-04-12",
|
"version": "2023-11-03",
|
||||||
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
|
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
|
||||||
"label": "Pre-Built obs-deps",
|
"label": "Pre-Built obs-deps",
|
||||||
"hashes": {
|
"hashes": {
|
||||||
"macos": "9535c6e1ad96f7d49960251e85a245774088d48da1d602bb82f734b10219125a",
|
"macos": "90c2fc069847ec2768dcc867c1c63b112c615ed845a907dc44acab7a97181974",
|
||||||
"windows-x64": "c13a14a1acc4224b21304d97b63da4121de1ed6981297e50496fbc474abc0503"
|
"windows-x64": "d0825a6fb65822c993a3059edfba70d72d2e632ef74893588cf12b1f0d329ce6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"qt6": {
|
"qt6": {
|
||||||
"version": "2023-04-12",
|
"version": "2023-11-03",
|
||||||
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
|
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
|
||||||
"label": "Pre-Built Qt6",
|
"label": "Pre-Built Qt6",
|
||||||
"hashes": {
|
"hashes": {
|
||||||
"macos": "eb7614544ab4f3d2c6052c797635602280ca5b028a6b987523d8484222ce45d1",
|
"macos": "ba4a7152848da0053f63427a2a2cb0a199af3992997c0db08564df6f48c9db98",
|
||||||
"windows-x64": "4d39364b8a8dee5aa24fcebd8440d5c22bb4551c6b440ffeacce7d61f2ed1add"
|
"windows-x64": "bc57dedf76b47119a6dce0435a2f21b35b08c8f2948b1cb34a157320f77732d1"
|
||||||
},
|
},
|
||||||
"debugSymbols": {
|
"debugSymbols": {
|
||||||
"windows-x64": "f34ee5067be19ed370268b15c53684b7b8aaa867dc800b68931df905d679e31f"
|
"windows-x64": "fd8ecd1d8cd2ef049d9f4d7fb5c134f784836d6020758094855dfa98bd025036"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,14 +8,11 @@ if(BUILD_OUT_OF_TREE)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Subfolder for advanced scene switcher plugins
|
|
||||||
set(_PLUGIN_FOLDER "adv-ss-plugins")
|
|
||||||
|
|
||||||
# --- MACOS section ---
|
# --- MACOS section ---
|
||||||
if(OS_MACOS)
|
if(OS_MACOS)
|
||||||
set(ADVSS_BUNDLE_DIR "advanced-scene-switcher.plugin")
|
set(ADVSS_BUNDLE_DIR "advanced-scene-switcher.plugin")
|
||||||
set(ADVSS_BUNDLE_MODULE_DIR "${ADVSS_BUNDLE_DIR}/Contents/MacOS")
|
set(ADVSS_BUNDLE_MODULE_DIR "${ADVSS_BUNDLE_DIR}/Contents/MacOS")
|
||||||
set(ADVSS_BUNDLE_PLUGIN_DIR ${ADVSS_BUNDLE_MODULE_DIR}/${_PLUGIN_FOLDER})
|
set(ADVSS_BUNDLE_PLUGIN_DIR ${ADVSS_BUNDLE_MODULE_DIR}/${ADVSS_PLUGIN_FOLDER})
|
||||||
|
|
||||||
function(install_advss_lib_helper target where)
|
function(install_advss_lib_helper target where)
|
||||||
install(
|
install(
|
||||||
@@ -154,8 +151,8 @@ else()
|
|||||||
|
|
||||||
function(install_advss_plugin target)
|
function(install_advss_plugin target)
|
||||||
plugin_install_helper(
|
plugin_install_helper(
|
||||||
"${target}" "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
"${target}" "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
"${_PLUGIN_FOLDER}")
|
"${ADVSS_PLUGIN_FOLDER}")
|
||||||
if(NOT OS_WINDOWS)
|
if(NOT OS_WINDOWS)
|
||||||
set_target_properties(${target} PROPERTIES INSTALL_RPATH
|
set_target_properties(${target} PROPERTIES INSTALL_RPATH
|
||||||
"$ORIGIN:$ORIGIN/..")
|
"$ORIGIN:$ORIGIN/..")
|
||||||
@@ -169,12 +166,12 @@ else()
|
|||||||
${dep}
|
${dep}
|
||||||
RUNTIME
|
RUNTIME
|
||||||
DESTINATION
|
DESTINATION
|
||||||
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
"${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
COMPONENT
|
COMPONENT
|
||||||
${dep}_Runtime
|
${dep}_Runtime
|
||||||
LIBRARY
|
LIBRARY
|
||||||
DESTINATION
|
DESTINATION
|
||||||
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
"${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
COMPONENT
|
COMPONENT
|
||||||
${dep}_Runtime
|
${dep}_Runtime
|
||||||
NAMELINK_COMPONENT
|
NAMELINK_COMPONENT
|
||||||
@@ -185,13 +182,13 @@ else()
|
|||||||
${dep}
|
${dep}
|
||||||
RUNTIME
|
RUNTIME
|
||||||
DESTINATION
|
DESTINATION
|
||||||
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
"${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
COMPONENT
|
COMPONENT
|
||||||
obs_${dep}
|
obs_${dep}
|
||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
LIBRARY
|
LIBRARY
|
||||||
DESTINATION
|
DESTINATION
|
||||||
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
"${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
COMPONENT
|
COMPONENT
|
||||||
obs_${dep}
|
obs_${dep}
|
||||||
EXCLUDE_FROM_ALL)
|
EXCLUDE_FROM_ALL)
|
||||||
@@ -214,18 +211,18 @@ else()
|
|||||||
|
|
||||||
install(
|
install(
|
||||||
FILES "${dep}"
|
FILES "${dep}"
|
||||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
COMPONENT ${_DEP_NAME}_Runtime
|
COMPONENT ${_DEP_NAME}_Runtime
|
||||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
COMPONENT ${_DEP_NAME}_Runtime
|
COMPONENT ${_DEP_NAME}_Runtime
|
||||||
NAMELINK_COMPONENT ${_DEP_NAME}_Development)
|
NAMELINK_COMPONENT ${_DEP_NAME}_Development)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
FILES "${dep}"
|
FILES "${dep}"
|
||||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
COMPONENT obs_${_DEP_NAME}
|
COMPONENT obs_${_DEP_NAME}
|
||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||||
COMPONENT obs_${_DEP_NAME}
|
COMPONENT obs_${_DEP_NAME}
|
||||||
EXCLUDE_FROM_ALL)
|
EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
@@ -335,7 +332,6 @@ function(install_advss_plugin_dependency)
|
|||||||
if(NOT PARSED_ARGS_TARGET)
|
if(NOT PARSED_ARGS_TARGET)
|
||||||
message(FATAL_ERROR "You must provide a target")
|
message(FATAL_ERROR "You must provide a target")
|
||||||
endif()
|
endif()
|
||||||
set(_PLUGIN_FOLDER "adv-ss-plugins")
|
|
||||||
foreach(_DEPENDENCY ${PARSED_ARGS_DEPENDENCIES})
|
foreach(_DEPENDENCY ${PARSED_ARGS_DEPENDENCIES})
|
||||||
if(EXISTS ${_DEPENDENCY})
|
if(EXISTS ${_DEPENDENCY})
|
||||||
install_advss_plugin_dependency_file(${PARSED_ARGS_TARGET} ${_DEPENDENCY})
|
install_advss_plugin_dependency_file(${PARSED_ARGS_TARGET} ${_DEPENDENCY})
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ AdvSceneSwitcher.condition.media.source="Quelle"
|
|||||||
AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in"
|
AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in"
|
||||||
AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in"
|
AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in"
|
||||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!"
|
AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!"
|
||||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}Status ist{{states}}und{{timeRestrictions}}{{time}}"
|
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}Status ist{{states}}und{{timeRestrictions}}{{time}}"
|
||||||
AdvSceneSwitcher.condition.video="Video"
|
AdvSceneSwitcher.condition.video="Video"
|
||||||
AdvSceneSwitcher.condition.video.condition.match="entspricht genau"
|
AdvSceneSwitcher.condition.video.condition.match="entspricht genau"
|
||||||
AdvSceneSwitcher.condition.video.condition.differ="stimmt nicht überein"
|
AdvSceneSwitcher.condition.video.condition.differ="stimmt nicht überein"
|
||||||
@@ -321,11 +321,8 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Nächster Treffer bei: %1"
|
|||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren"
|
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren"
|
||||||
AdvSceneSwitcher.condition.sceneTransform="Szenenelement transformieren"
|
AdvSceneSwitcher.condition.sceneTransform="Szenenelement transformieren"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Transformation erhalten"
|
AdvSceneSwitcher.condition.sceneTransform.getTransform="Transformation erhalten"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Auf{{scenes}}{{sources}}{{types}}"
|
AdvSceneSwitcher.condition.sceneTransform.condition.match="entspricht Transformation"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="entspricht Transformation"
|
AdvSceneSwitcher.condition.sceneTransform.condition.changed="hat sich geändert"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.changed="hat sich geändert"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
|
||||||
AdvSceneSwitcher.condition.transition="Übergang"
|
AdvSceneSwitcher.condition.transition="Übergang"
|
||||||
AdvSceneSwitcher.condition.transition.type.current="Aktueller Übergangstyp ist"
|
AdvSceneSwitcher.condition.transition.type.current="Aktueller Übergangstyp ist"
|
||||||
AdvSceneSwitcher.condition.transition.type.duration="Aktuelle Übergangsdauer beträgt"
|
AdvSceneSwitcher.condition.transition.type.duration="Aktuelle Übergangsdauer beträgt"
|
||||||
@@ -498,7 +495,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
|
|||||||
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
|
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||||
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
|
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
|
||||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
|
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
|
||||||
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}"
|
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||||
AdvSceneSwitcher.action.file="Datei"
|
AdvSceneSwitcher.action.file="Datei"
|
||||||
AdvSceneSwitcher.action.file.type.write="Schreiben"
|
AdvSceneSwitcher.action.file.type.write="Schreiben"
|
||||||
AdvSceneSwitcher.action.file.type.append="Anhängen"
|
AdvSceneSwitcher.action.file.type.append="Anhängen"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
AdvSceneSwitcher.pluginName="Advanced Scene Switcher"
|
AdvSceneSwitcher.pluginName="Advanced Scene Switcher"
|
||||||
AdvSceneSwitcher.windowTitle="Advanced Scene Switcher"
|
AdvSceneSwitcher.windowTitle="Advanced Scene Switcher"
|
||||||
|
|
||||||
; General Tab
|
# General Tab
|
||||||
AdvSceneSwitcher.generalTab.title="General"
|
AdvSceneSwitcher.generalTab.title="General"
|
||||||
AdvSceneSwitcher.generalTab.status="Status"
|
AdvSceneSwitcher.generalTab.status="Status"
|
||||||
AdvSceneSwitcher.generalTab.status.hotkeytips="Hotkeys can be defined in the OBS settings"
|
AdvSceneSwitcher.generalTab.status.hotkeytips="Hotkeys can be defined in the OBS settings"
|
||||||
@@ -37,6 +37,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.comboBoxFilterDisable="Disable filtering by typing in drop down menus"
|
AdvSceneSwitcher.generalTab.generalBehavior.comboBoxFilterDisable="Disable filtering by typing in drop down menus"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="Display warning if plugins cannot be loaded"
|
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="Display warning if plugins cannot be loaded"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>Loading of the following plugin libraries was unsuccessful, which could result in some Advanced Scene Switcher functions not being available:%1Check the OBS logs for details.<br>This message can be disabled on the General tab.</body></html>"
|
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>Loading of the following plugin libraries was unsuccessful, which could result in some Advanced Scene Switcher functions not being available:%1Check the OBS logs for details.<br>This message can be disabled on the General tab.</body></html>"
|
||||||
|
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
|
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
|
||||||
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
|
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
|
||||||
AdvSceneSwitcher.generalTab.priority="Priority"
|
AdvSceneSwitcher.generalTab.priority="Priority"
|
||||||
@@ -69,7 +70,7 @@ AdvSceneSwitcher.generalTab.transitionOverride="Set transition overrides"
|
|||||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type"
|
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type"
|
||||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
|
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
|
||||||
|
|
||||||
; Variables Tab
|
# Variables Tab
|
||||||
AdvSceneSwitcher.variableTab.title="Variables"
|
AdvSceneSwitcher.variableTab.title="Variables"
|
||||||
AdvSceneSwitcher.variableTab.help="Variables can be used in many places throughout the plugin.\n\nClick on the highlighted plus symbol to add a new variable."
|
AdvSceneSwitcher.variableTab.help="Variables can be used in many places throughout the plugin.\n\nClick on the highlighted plus symbol to add a new variable."
|
||||||
AdvSceneSwitcher.variableTab.variableAddButton.tooltip="Add new variable"
|
AdvSceneSwitcher.variableTab.variableAddButton.tooltip="Add new variable"
|
||||||
@@ -88,7 +89,7 @@ AdvSceneSwitcher.variableTab.lastChanged.text="%1 seconds ago"
|
|||||||
AdvSceneSwitcher.variableTab.lastChanged.text.none="No change since launch"
|
AdvSceneSwitcher.variableTab.lastChanged.text.none="No change since launch"
|
||||||
AdvSceneSwitcher.variableTab.lastChanged.tooltip="Times changed: %1\n\nPrevious value: %2"
|
AdvSceneSwitcher.variableTab.lastChanged.tooltip="Times changed: %1\n\nPrevious value: %2"
|
||||||
|
|
||||||
; Action Queue Tab
|
# Action Queue Tab
|
||||||
AdvSceneSwitcher.actionQueueTab.title="Action Queues"
|
AdvSceneSwitcher.actionQueueTab.title="Action Queues"
|
||||||
AdvSceneSwitcher.actionQueueTab.help="Action queues are executed sequentially but in parallel to the reset of the macro system.\nThe first action added to the queue will be the first one to be processed.\n\nClick on the highlighted plus symbol to add a new queue."
|
AdvSceneSwitcher.actionQueueTab.help="Action queues are executed sequentially but in parallel to the reset of the macro system.\nThe first action added to the queue will be the first one to be processed.\n\nClick on the highlighted plus symbol to add a new queue."
|
||||||
AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="Add new action queue"
|
AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="Add new action queue"
|
||||||
@@ -102,7 +103,7 @@ AdvSceneSwitcher.actionQueueTab.no="No"
|
|||||||
AdvSceneSwitcher.actionQueueTab.removeSingleQueuePopup.text="Are you sure you want to remove \"%1\"?"
|
AdvSceneSwitcher.actionQueueTab.removeSingleQueuePopup.text="Are you sure you want to remove \"%1\"?"
|
||||||
AdvSceneSwitcher.actionQueueTab.removeMultipleQueuesPopup.text="Are you sure you want to remove %1 action queues?"
|
AdvSceneSwitcher.actionQueueTab.removeMultipleQueuesPopup.text="Are you sure you want to remove %1 action queues?"
|
||||||
|
|
||||||
; Websocket Connections Tab
|
# Websocket Connections Tab
|
||||||
AdvSceneSwitcher.websocketConnectionTab.title="Websocket Connections"
|
AdvSceneSwitcher.websocketConnectionTab.title="Websocket Connections"
|
||||||
AdvSceneSwitcher.websocketConnectionTab.help="Websocket connections can be used to communicate with other OBS instances or programs.\n\nClick on the highlighted plus symbol to add a new connection."
|
AdvSceneSwitcher.websocketConnectionTab.help="Websocket connections can be used to communicate with other OBS instances or programs.\n\nClick on the highlighted plus symbol to add a new connection."
|
||||||
AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip="Add new webscoket connection"
|
AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip="Add new webscoket connection"
|
||||||
@@ -116,7 +117,7 @@ AdvSceneSwitcher.websocketConnectionTab.protocol.no="No"
|
|||||||
AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
|
AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
|
||||||
AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 connections?"
|
AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 connections?"
|
||||||
|
|
||||||
; Twitch Connections Tab
|
# Twitch Connections Tab
|
||||||
AdvSceneSwitcher.twitchConnectionTab.title="Twitch Connections"
|
AdvSceneSwitcher.twitchConnectionTab.title="Twitch Connections"
|
||||||
AdvSceneSwitcher.twitchConnectionTab.help="Twitch connections can be used to use Twitch events as triggers to execute actions or perform actions on the linked Twitch account.\n\nClick on the highlighted plus symbol to add a new connection."
|
AdvSceneSwitcher.twitchConnectionTab.help="Twitch connections can be used to use Twitch events as triggers to execute actions or perform actions on the linked Twitch account.\n\nClick on the highlighted plus symbol to add a new connection."
|
||||||
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip.tooltip="Add new Twitch connection"
|
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip.tooltip="Add new Twitch connection"
|
||||||
@@ -129,7 +130,7 @@ AdvSceneSwitcher.twitchConnectionTab.no="No"
|
|||||||
AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
|
AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
|
||||||
AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 Twitch connections?"
|
AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 Twitch connections?"
|
||||||
|
|
||||||
; Macro Tab
|
# Macro Tab
|
||||||
AdvSceneSwitcher.macroTab.title="Macro"
|
AdvSceneSwitcher.macroTab.title="Macro"
|
||||||
AdvSceneSwitcher.macroTab.macros="Macros"
|
AdvSceneSwitcher.macroTab.macros="Macros"
|
||||||
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
|
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
|
||||||
@@ -175,10 +176,13 @@ AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
|
|||||||
AdvSceneSwitcher.macroTab.maximize="Maximize"
|
AdvSceneSwitcher.macroTab.maximize="Maximize"
|
||||||
AdvSceneSwitcher.macroTab.minimize="Minimize"
|
AdvSceneSwitcher.macroTab.minimize="Minimize"
|
||||||
AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label"
|
AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label"
|
||||||
|
AdvSceneSwitcher.macroTab.segment.customLabelRename="Rename custom label"
|
||||||
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label"
|
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label"
|
||||||
AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..."
|
AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..."
|
||||||
AdvSceneSwitcher.macroTab.segment.copy="Copy"
|
AdvSceneSwitcher.macroTab.segment.copy="Copy"
|
||||||
AdvSceneSwitcher.macroTab.segment.paste="Paste"
|
AdvSceneSwitcher.macroTab.segment.paste="Paste"
|
||||||
|
AdvSceneSwitcher.macroTab.segment.pasteAction="Paste as action"
|
||||||
|
AdvSceneSwitcher.macroTab.segment.pasteElseAction="Paste as else-action"
|
||||||
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
|
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
|
||||||
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
|
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
|
||||||
AdvSceneSwitcher.macroTab.generalSettings="General settings"
|
AdvSceneSwitcher.macroTab.generalSettings="General settings"
|
||||||
@@ -238,11 +242,11 @@ AdvSceneSwitcher.macroDock.run="Run"
|
|||||||
AdvSceneSwitcher.macroDock.statusLabel.true="Conditions are true."
|
AdvSceneSwitcher.macroDock.statusLabel.true="Conditions are true."
|
||||||
AdvSceneSwitcher.macroDock.statusLabel.false="Conditions are false."
|
AdvSceneSwitcher.macroDock.statusLabel.false="Conditions are false."
|
||||||
|
|
||||||
; Macro List
|
# Macro List
|
||||||
AdvSceneSwitcher.macroList.deleted="deleted"
|
AdvSceneSwitcher.macroList.deleted="deleted"
|
||||||
AdvSceneSwitcher.macroList.duplicate="\"%1\" is alreay selected!"
|
AdvSceneSwitcher.macroList.duplicate="\"%1\" is alreay selected!"
|
||||||
|
|
||||||
; Macro Logic
|
# Macro Logic
|
||||||
AdvSceneSwitcher.logic.none="Ignore entry"
|
AdvSceneSwitcher.logic.none="Ignore entry"
|
||||||
AdvSceneSwitcher.logic.and="And"
|
AdvSceneSwitcher.logic.and="And"
|
||||||
AdvSceneSwitcher.logic.or="Or"
|
AdvSceneSwitcher.logic.or="Or"
|
||||||
@@ -251,7 +255,8 @@ AdvSceneSwitcher.logic.orNot="Or not"
|
|||||||
AdvSceneSwitcher.logic.rootNone="If"
|
AdvSceneSwitcher.logic.rootNone="If"
|
||||||
AdvSceneSwitcher.logic.not="If not"
|
AdvSceneSwitcher.logic.not="If not"
|
||||||
|
|
||||||
; Macro Conditions
|
# Macro Conditions
|
||||||
|
AdvSceneSwitcher.condition.unknown="Unknown condition"
|
||||||
AdvSceneSwitcher.condition.audio="Audio"
|
AdvSceneSwitcher.condition.audio="Audio"
|
||||||
AdvSceneSwitcher.condition.audio.state.below="below"
|
AdvSceneSwitcher.condition.audio.state.below="below"
|
||||||
AdvSceneSwitcher.condition.audio.state.exact="exactly"
|
AdvSceneSwitcher.condition.audio.state.exact="exactly"
|
||||||
@@ -307,11 +312,14 @@ AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{condition
|
|||||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||||
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
|
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
|
||||||
AdvSceneSwitcher.condition.media="Media"
|
AdvSceneSwitcher.condition.media="Media"
|
||||||
|
AdvSceneSwitcher.condition.media.checkType.state="State matches"
|
||||||
|
AdvSceneSwitcher.condition.media.checkType.time="Time restriction matches"
|
||||||
AdvSceneSwitcher.condition.media.source="Source"
|
AdvSceneSwitcher.condition.media.source="Source"
|
||||||
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
|
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
|
||||||
AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
|
AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
|
||||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
|
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
|
||||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}state is{{states}}and{{timeRestrictions}}{{time}}"
|
AdvSceneSwitcher.condition.media.layout="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}{{states}}{{timeRestrictions}}{{time}}"
|
||||||
|
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}state is{{states}}and{{timeRestrictions}}{{time}}"
|
||||||
AdvSceneSwitcher.condition.video="Video"
|
AdvSceneSwitcher.condition.video="Video"
|
||||||
AdvSceneSwitcher.condition.video.condition.match="exactly matches"
|
AdvSceneSwitcher.condition.video.condition.match="exactly matches"
|
||||||
AdvSceneSwitcher.condition.video.condition.differ="does not match"
|
AdvSceneSwitcher.condition.video.condition.differ="does not match"
|
||||||
@@ -531,11 +539,17 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
|
|||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}On repeat update selected date to repeat date"
|
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}On repeat update selected date to repeat date"
|
||||||
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="On{{scenes}}{{sources}}{{types}}"
|
AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="Get current value"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="matches transform"
|
AdvSceneSwitcher.condition.sceneTransform.settingsType.all="All"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.changed="has changed"
|
AdvSceneSwitcher.condition.sceneTransform.settingsType.single="Single"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
AdvSceneSwitcher.condition.sceneTransform.condition.match="matches"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}}{{getSettings}}"
|
AdvSceneSwitcher.condition.sceneTransform.condition.changed="has changed"
|
||||||
|
AdvSceneSwitcher.condition.sceneTransform.compare.less="less"
|
||||||
|
AdvSceneSwitcher.condition.sceneTransform.compare.equal="equal"
|
||||||
|
AdvSceneSwitcher.condition.sceneTransform.compare.more="larger than"
|
||||||
|
AdvSceneSwitcher.condition.sceneTransform.entry="On{{scenes}}{{settingsType}}transform setting of{{sources}}{{conditions}}"
|
||||||
|
AdvSceneSwitcher.condition.sceneTransform.entry.singleSettingCompare="{{setting}}{{compare}}{{singleSettingValue}}"
|
||||||
|
AdvSceneSwitcher.condition.sceneTransform.entry.options="{{regex}}{{getSettings}}{{getCurrentValue}}"
|
||||||
AdvSceneSwitcher.condition.transition="Transition"
|
AdvSceneSwitcher.condition.transition="Transition"
|
||||||
AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
|
AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
|
||||||
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
|
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
|
||||||
@@ -714,7 +728,8 @@ AdvSceneSwitcher.condition.usb.productName="Product Name:"
|
|||||||
AdvSceneSwitcher.condition.usb.serialNumber="Serial Number:"
|
AdvSceneSwitcher.condition.usb.serialNumber="Serial Number:"
|
||||||
AdvSceneSwitcher.condition.noDevicesFoundWarning="No USB devices detected!\nThe plugin might not have the required permissions to check for USB devices."
|
AdvSceneSwitcher.condition.noDevicesFoundWarning="No USB devices detected!\nThe plugin might not have the required permissions to check for USB devices."
|
||||||
|
|
||||||
; Macro Actions
|
# Macro Actions
|
||||||
|
AdvSceneSwitcher.action.unknown="Unknown action"
|
||||||
AdvSceneSwitcher.action.scene="Switch scene"
|
AdvSceneSwitcher.action.scene="Switch scene"
|
||||||
AdvSceneSwitcher.action.scene.type.program="Program"
|
AdvSceneSwitcher.action.scene.type.program="Program"
|
||||||
AdvSceneSwitcher.action.scene.type.preview="Preview"
|
AdvSceneSwitcher.action.scene.type.preview="Preview"
|
||||||
@@ -904,7 +919,8 @@ AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
|
|||||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
|
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
|
||||||
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
|
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||||
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
|
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
|
||||||
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
|
AdvSceneSwitcher.action.sceneTransform.type.manual="Set transform string"
|
||||||
|
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Set transform setting"
|
||||||
AdvSceneSwitcher.action.sceneTransform.type.reset="Reset transform"
|
AdvSceneSwitcher.action.sceneTransform.type.reset="Reset transform"
|
||||||
AdvSceneSwitcher.action.sceneTransform.type.rotate="Rotate by"
|
AdvSceneSwitcher.action.sceneTransform.type.rotate="Rotate by"
|
||||||
AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="Flip horizontally"
|
AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="Flip horizontally"
|
||||||
@@ -915,7 +931,8 @@ AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="Center to screen"
|
|||||||
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Center vertically"
|
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Center vertically"
|
||||||
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Center horizontally"
|
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Center horizontally"
|
||||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform"
|
AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform"
|
||||||
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}"
|
AdvSceneSwitcher.action.sceneTransform.getCurrentValue="Get current value"
|
||||||
|
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||||
AdvSceneSwitcher.action.file="File"
|
AdvSceneSwitcher.action.file="File"
|
||||||
AdvSceneSwitcher.action.file.type.write="Write"
|
AdvSceneSwitcher.action.file.type.write="Write"
|
||||||
AdvSceneSwitcher.action.file.type.append="Append"
|
AdvSceneSwitcher.action.file.type.append="Append"
|
||||||
@@ -1102,7 +1119,7 @@ AdvSceneSwitcher.action.log="Log"
|
|||||||
AdvSceneSwitcher.action.log.placeholder="My log message!"
|
AdvSceneSwitcher.action.log.placeholder="My log message!"
|
||||||
AdvSceneSwitcher.action.log.entry="Write to OBS log:{{logMessage}}"
|
AdvSceneSwitcher.action.log.entry="Write to OBS log:{{logMessage}}"
|
||||||
|
|
||||||
; Hotkey
|
# Hotkey
|
||||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
|
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
|
||||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher"
|
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher"
|
||||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the Advanced Scene Switcher"
|
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the Advanced Scene Switcher"
|
||||||
@@ -1796,6 +1813,9 @@ AdvSceneSwitcher.tempVar.usb.vendorName="Vendor Name"
|
|||||||
AdvSceneSwitcher.tempVar.usb.productName="Product Name"
|
AdvSceneSwitcher.tempVar.usb.productName="Product Name"
|
||||||
AdvSceneSwitcher.tempVar.usb.serialNumber="Serial Number"
|
AdvSceneSwitcher.tempVar.usb.serialNumber="Serial Number"
|
||||||
|
|
||||||
|
AdvSceneSwitcher.tempVar.transform.setting="Setting value"
|
||||||
|
AdvSceneSwitcher.tempVar.transform.settings="Settings string"
|
||||||
|
|
||||||
AdvSceneSwitcher.selectScene="--select scene--"
|
AdvSceneSwitcher.selectScene="--select scene--"
|
||||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||||
@@ -1840,6 +1860,20 @@ AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
|
|||||||
AdvSceneSwitcher.settings.suffix.type.group=" (Group)"
|
AdvSceneSwitcher.settings.suffix.type.group=" (Group)"
|
||||||
AdvSceneSwitcher.settings.suffix.type.colorAlpha=" (Color alpha)"
|
AdvSceneSwitcher.settings.suffix.type.colorAlpha=" (Color alpha)"
|
||||||
AdvSceneSwitcher.setting.select="--select setting--"
|
AdvSceneSwitcher.setting.select="--select setting--"
|
||||||
|
AdvSceneSwitcher.setting.transform.alignment="Positional Alignment"
|
||||||
|
AdvSceneSwitcher.setting.transform.cropBottom="[Crop]Bottom"
|
||||||
|
AdvSceneSwitcher.setting.transform.cropLeft="[Crop]Left"
|
||||||
|
AdvSceneSwitcher.setting.transform.cropRight="[Crop]Right"
|
||||||
|
AdvSceneSwitcher.setting.transform.cropTop="[Crop]Top"
|
||||||
|
AdvSceneSwitcher.setting.transform.boundingBoxType="Bounding Box Type"
|
||||||
|
AdvSceneSwitcher.setting.transform.boundingBoxAlign="Alignment in Bounding Box"
|
||||||
|
AdvSceneSwitcher.setting.transform.boundingBoxSize="Bounding Box Size"
|
||||||
|
AdvSceneSwitcher.setting.transform.rotation="Rotation"
|
||||||
|
AdvSceneSwitcher.setting.transform.scale="Scale"
|
||||||
|
AdvSceneSwitcher.setting.transform.size="Size"
|
||||||
|
AdvSceneSwitcher.setting.transform.position="Position"
|
||||||
|
AdvSceneSwitcher.setting.transform.height="Height"
|
||||||
|
AdvSceneSwitcher.setting.transform.width="Width"
|
||||||
|
|
||||||
|
|
||||||
AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type"
|
AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type"
|
||||||
@@ -1889,8 +1923,44 @@ AdvSceneSwitcher.noSettingsButtons="No buttons found!"
|
|||||||
|
|
||||||
AdvSceneSwitcher.clearBufferOnMatch="Clear message buffer when matching message was found"
|
AdvSceneSwitcher.clearBufferOnMatch="Clear message buffer when matching message was found"
|
||||||
|
|
||||||
; Legacy tabs below - please don't waste your time adding translations for these :)
|
AdvSceneSwitcher.script.settings="Settings"
|
||||||
; Transition Tab
|
AdvSceneSwitcher.script.timeout="Script timeout:{{timeout}}"
|
||||||
|
|
||||||
|
# This secion is copied from the OBS locale files
|
||||||
|
|
||||||
|
# commonly shared locale
|
||||||
|
Browse="Browse"
|
||||||
|
Show="Show"
|
||||||
|
Hide="Hide"
|
||||||
|
|
||||||
|
|
||||||
|
# properties window
|
||||||
|
Basic.PropertiesWindow="Properties for '%1'"
|
||||||
|
Basic.PropertiesWindow.AutoSelectFormat="%1 (autoselect: %2)"
|
||||||
|
Basic.PropertiesWindow.SelectColor="Select color"
|
||||||
|
Basic.PropertiesWindow.SelectFont="Select font"
|
||||||
|
Basic.PropertiesWindow.SelectFont.WindowTitle="Pick a Font"
|
||||||
|
Basic.PropertiesWindow.ConfirmTitle="Settings Changed"
|
||||||
|
Basic.PropertiesWindow.Confirm="There are unsaved changes. Do you want to keep them?"
|
||||||
|
Basic.PropertiesWindow.NoProperties="No properties available"
|
||||||
|
Basic.PropertiesWindow.AddFiles="Add Files"
|
||||||
|
Basic.PropertiesWindow.AddDir="Add Directory"
|
||||||
|
Basic.PropertiesWindow.AddURL="Add Path/URL"
|
||||||
|
Basic.PropertiesWindow.AddEditableListDir="Add directory to '%1'"
|
||||||
|
Basic.PropertiesWindow.AddEditableListFiles="Add files to '%1'"
|
||||||
|
Basic.PropertiesWindow.AddEditableListEntry="Add entry to '%1'"
|
||||||
|
Basic.PropertiesWindow.EditEditableListEntry="Edit entry from '%1'"
|
||||||
|
|
||||||
|
# properties view
|
||||||
|
Basic.PropertiesView.FPS.Simple="Simple FPS Values"
|
||||||
|
Basic.PropertiesView.FPS.Rational="Rational FPS Values"
|
||||||
|
Basic.PropertiesView.FPS.ValidFPSRanges="Valid FPS Ranges:"
|
||||||
|
Basic.PropertiesView.UrlButton.Text="Open this link in your default web browser?"
|
||||||
|
Basic.PropertiesView.UrlButton.Text.Url="URL: %1"
|
||||||
|
Basic.PropertiesView.UrlButton.OpenUrl="Open URL"
|
||||||
|
|
||||||
|
# Legacy tabs below - please don't waste your time adding translations for these :)
|
||||||
|
# Transition Tab
|
||||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
|
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
|
||||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
|
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
|
||||||
@@ -1901,7 +1971,7 @@ AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Click on the plus symbol
|
|||||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Switch transition{{defTransitionDelay}}after scene change."
|
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Switch transition{{defTransitionDelay}}after scene change."
|
||||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="The delay is used to avoid cancelled scene switches, which can happen if the transition type is changed while a transition is still ongoing."
|
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="The delay is used to avoid cancelled scene switches, which can happen if the transition type is changed while a transition is still ongoing."
|
||||||
|
|
||||||
; Pause Scenes Tab
|
# Pause Scenes Tab
|
||||||
AdvSceneSwitcher.pauseTab.title="Pause"
|
AdvSceneSwitcher.pauseTab.title="Pause"
|
||||||
AdvSceneSwitcher.pauseTab.pauseOnScene="Pause the Scene Switcher on scene"
|
AdvSceneSwitcher.pauseTab.pauseOnScene="Pause the Scene Switcher on scene"
|
||||||
AdvSceneSwitcher.pauseTab.pauseInFocus1="Pause the Scene Switcher when "
|
AdvSceneSwitcher.pauseTab.pauseInFocus1="Pause the Scene Switcher when "
|
||||||
@@ -1912,7 +1982,7 @@ AdvSceneSwitcher.pauseTab.pauseTargetAll="all"
|
|||||||
AdvSceneSwitcher.pauseTab.pauseEntry="Pause{{pauseTargets}}checks when{{pauseTypes}}{{scenes}}{{windows}}"
|
AdvSceneSwitcher.pauseTab.pauseEntry="Pause{{pauseTargets}}checks when{{pauseTypes}}{{scenes}}{{windows}}"
|
||||||
AdvSceneSwitcher.pauseTab.help="On this tab you can configure to pause individual switching methods if a scene is active or window is in focus.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.pauseTab.help="On this tab you can configure to pause individual switching methods if a scene is active or window is in focus.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Window Title Tab
|
# Window Title Tab
|
||||||
AdvSceneSwitcher.windowTitleTab.title="Title"
|
AdvSceneSwitcher.windowTitleTab.title="Title"
|
||||||
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Enter either direct window titles or valid regex. You can check syntax and matches for regular expressions using <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a></p></body></html>"
|
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Enter either direct window titles or valid regex. You can check syntax and matches for regular expressions using <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a></p></body></html>"
|
||||||
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Ignore this window name"
|
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Ignore this window name"
|
||||||
@@ -1924,14 +1994,14 @@ AdvSceneSwitcher.windowTitleTab.entry="{{windows}}{{scenes}}{{transitions}}{{ful
|
|||||||
AdvSceneSwitcher.windowTitleTab.windowsHelp="Switch scenes based on the window title of running applications.\nThe following additional conditions can be selected:\nThe window is Fullscreen\nThe window is maximized\nThe window is focused\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.windowTitleTab.windowsHelp="Switch scenes based on the window title of running applications.\nThe following additional conditions can be selected:\nThe window is Fullscreen\nThe window is maximized\nThe window is focused\n\nClick on the highlighted plus symbol to continue."
|
||||||
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="If a window title is ignored the scene switcher will act as if the previously selected window is still in focus.\nThis will allow you to avoid scene switches, if you frequently switch to a different window, which shall not trigger a scene change.\n\nChoose a window or enter a window title above and click on the plus symbol below to add it to the list."
|
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="If a window title is ignored the scene switcher will act as if the previously selected window is still in focus.\nThis will allow you to avoid scene switches, if you frequently switch to a different window, which shall not trigger a scene change.\n\nChoose a window or enter a window title above and click on the plus symbol below to add it to the list."
|
||||||
|
|
||||||
; Executable Tab
|
# Executable Tab
|
||||||
AdvSceneSwitcher.executableTab.title="Executable"
|
AdvSceneSwitcher.executableTab.title="Executable"
|
||||||
AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven"
|
AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven"
|
||||||
AdvSceneSwitcher.executableTab.requiresFocus="only if focused"
|
AdvSceneSwitcher.executableTab.requiresFocus="only if focused"
|
||||||
AdvSceneSwitcher.executableTab.entry="When{{processes}}is running switch to{{scenes}}using{{transitions}}{{requiresFocus}}"
|
AdvSceneSwitcher.executableTab.entry="When{{processes}}is running switch to{{scenes}}using{{transitions}}{{requiresFocus}}"
|
||||||
AdvSceneSwitcher.executableTab.help="This tab will allow you to automatically switch scenes if a process is running.\nThis can be useful in situations where the window name could change or is not known.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.executableTab.help="This tab will allow you to automatically switch scenes if a process is running.\nThis can be useful in situations where the window name could change or is not known.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Screen Region Tab
|
# Screen Region Tab
|
||||||
AdvSceneSwitcher.screenRegionTab.title="Region"
|
AdvSceneSwitcher.screenRegionTab.title="Region"
|
||||||
AdvSceneSwitcher.screenRegionTab.currentPosition="Cursor is currently at:"
|
AdvSceneSwitcher.screenRegionTab.currentPosition="Cursor is currently at:"
|
||||||
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Show guide frames"
|
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Show guide frames"
|
||||||
@@ -1940,7 +2010,7 @@ AdvSceneSwitcher.screenRegionTab.excludeScenes.None="No selection"
|
|||||||
AdvSceneSwitcher.screenRegionTab.entry="If cursor is in{{minX}}{{minY}}x{{maxX}}{{maxY}}switch to{{scenes}}using{{transitions}}unless in{{excludeScenes}}"
|
AdvSceneSwitcher.screenRegionTab.entry="If cursor is in{{minX}}{{minY}}x{{maxX}}{{maxY}}switch to{{scenes}}using{{transitions}}unless in{{excludeScenes}}"
|
||||||
AdvSceneSwitcher.screenRegionTab.help="This tab will allow you to automatically switch scenes based on the current position of your mouse cursor.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.screenRegionTab.help="This tab will allow you to automatically switch scenes based on the current position of your mouse cursor.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Media Tab
|
# Media Tab
|
||||||
AdvSceneSwitcher.mediaTab.title="Media"
|
AdvSceneSwitcher.mediaTab.title="Media"
|
||||||
AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro"
|
AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro"
|
||||||
AdvSceneSwitcher.mediaTab.states.none="None"
|
AdvSceneSwitcher.mediaTab.states.none="None"
|
||||||
@@ -1961,7 +2031,7 @@ AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Time remaining longer"
|
|||||||
AdvSceneSwitcher.mediaTab.entry="When{{mediaSources}}state is{{states}}and{{timeRestrictions}}{{time}}switch to{{scenes}}using{{transitions}}"
|
AdvSceneSwitcher.mediaTab.entry="When{{mediaSources}}state is{{states}}and{{timeRestrictions}}{{time}}switch to{{scenes}}using{{transitions}}"
|
||||||
AdvSceneSwitcher.mediaTab.help="This tab will allow you to switch scenes based on the states of media sources.\nFor example, you can automatically switch back to the previous scene once the selected media sourced ended its playback.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.mediaTab.help="This tab will allow you to switch scenes based on the states of media sources.\nFor example, you can automatically switch back to the previous scene once the selected media sourced ended its playback.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; File Tab
|
# File Tab
|
||||||
AdvSceneSwitcher.fileTab.title="File"
|
AdvSceneSwitcher.fileTab.title="File"
|
||||||
AdvSceneSwitcher.fileTab.readWriteSceneFile="Read / write scene from / to file"
|
AdvSceneSwitcher.fileTab.readWriteSceneFile="Read / write scene from / to file"
|
||||||
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Write the name of the current scene to this file:"
|
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Write the name of the current scene to this file:"
|
||||||
@@ -1986,13 +2056,13 @@ AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
|
|||||||
AdvSceneSwitcher.fileTab.entry3="{{useRegex}}{{checkModificationDate}}{{checkFileContent}}"
|
AdvSceneSwitcher.fileTab.entry3="{{useRegex}}{{checkModificationDate}}{{checkFileContent}}"
|
||||||
AdvSceneSwitcher.fileTab.help="This tab will allow you to automatically switch scenes based on the content of remote or local files.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.fileTab.help="This tab will allow you to automatically switch scenes based on the content of remote or local files.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Random Tab
|
# Random Tab
|
||||||
AdvSceneSwitcher.randomTab.title="Random"
|
AdvSceneSwitcher.randomTab.title="Random"
|
||||||
AdvSceneSwitcher.randomTab.randomDisabledWarning="Functionality disabled - To activate select \"If no switch condition is met switch to any scene in Random tab\" on General tab"
|
AdvSceneSwitcher.randomTab.randomDisabledWarning="Functionality disabled - To activate select \"If no switch condition is met switch to any scene in Random tab\" on General tab"
|
||||||
AdvSceneSwitcher.randomTab.entry="If no switch condition is met switch to{{scenes}}using{{transitions}}for{{delay}}"
|
AdvSceneSwitcher.randomTab.entry="If no switch condition is met switch to{{scenes}}using{{transitions}}for{{delay}}"
|
||||||
AdvSceneSwitcher.randomTab.help="The scene switcher will randomly choose an entry on this tab to switch to for the configured time.\nNote that the same entry will not be chosen twice in a row.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.randomTab.help="The scene switcher will randomly choose an entry on this tab to switch to for the configured time.\nNote that the same entry will not be chosen twice in a row.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Time Tab
|
# Time Tab
|
||||||
AdvSceneSwitcher.timeTab.title="Time"
|
AdvSceneSwitcher.timeTab.title="Time"
|
||||||
AdvSceneSwitcher.timeTab.anyDay="On any day"
|
AdvSceneSwitcher.timeTab.anyDay="On any day"
|
||||||
AdvSceneSwitcher.timeTab.mondays="Mondays"
|
AdvSceneSwitcher.timeTab.mondays="Mondays"
|
||||||
@@ -2007,14 +2077,14 @@ AdvSceneSwitcher.timeTab.afterstart.tip="The time relative to the start of strea
|
|||||||
AdvSceneSwitcher.timeTab.entry="{{triggers}}at{{time}}switch to{{scenes}}using{{transitions}}"
|
AdvSceneSwitcher.timeTab.entry="{{triggers}}at{{time}}switch to{{scenes}}using{{transitions}}"
|
||||||
AdvSceneSwitcher.timeTab.help="This tab will allow you to automatically switch to a different scene based on the current local time.\n\nNote that the scene switcher will only switch scenes at the exact time you specified.\nMake sure you have configured the priority settings on the General tab to your liking so the selected time point will not be missed due to other switching methods having a higher priority.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.timeTab.help="This tab will allow you to automatically switch to a different scene based on the current local time.\n\nNote that the scene switcher will only switch scenes at the exact time you specified.\nMake sure you have configured the priority settings on the General tab to your liking so the selected time point will not be missed due to other switching methods having a higher priority.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Idle Tab
|
# Idle Tab
|
||||||
AdvSceneSwitcher.idleTab.title="Idle"
|
AdvSceneSwitcher.idleTab.title="Idle"
|
||||||
AdvSceneSwitcher.idleTab.enable="Enable Idle Detection"
|
AdvSceneSwitcher.idleTab.enable="Enable Idle Detection"
|
||||||
AdvSceneSwitcher.idleTab.idleswitch="After{{duration}}of no keyboard or mouse inputs switch to scene{{scenes}}using the{{transitions}}"
|
AdvSceneSwitcher.idleTab.idleswitch="After{{duration}}of no keyboard or mouse inputs switch to scene{{scenes}}using the{{transitions}}"
|
||||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Do not switch if"
|
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Do not switch if"
|
||||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="is in focus"
|
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="is in focus"
|
||||||
|
|
||||||
; Scene Sequence Tab
|
# Scene Sequence Tab
|
||||||
AdvSceneSwitcher.sceneSequenceTab.title="Sequence"
|
AdvSceneSwitcher.sceneSequenceTab.title="Sequence"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.description="A sequence of automatic scene switches can be cancelled by either pausing/stopping the scene switcher or manually switching to a different scene"
|
AdvSceneSwitcher.sceneSequenceTab.description="A sequence of automatic scene switches can be cancelled by either pausing/stopping the scene switcher or manually switching to a different scene"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.save="Save scene sequences to file"
|
AdvSceneSwitcher.sceneSequenceTab.save="Save scene sequences to file"
|
||||||
@@ -2031,7 +2101,7 @@ AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
|
|||||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After{{delay}}switch to{{scenes}}using{{transitions}}"
|
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After{{delay}}switch to{{scenes}}using{{transitions}}"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Audio Tab
|
# Audio Tab
|
||||||
AdvSceneSwitcher.audioTab.title="Audio"
|
AdvSceneSwitcher.audioTab.title="Audio"
|
||||||
AdvSceneSwitcher.audioTab.condition.above="above"
|
AdvSceneSwitcher.audioTab.condition.above="above"
|
||||||
AdvSceneSwitcher.audioTab.condition.below="below"
|
AdvSceneSwitcher.audioTab.condition.below="below"
|
||||||
@@ -2041,7 +2111,7 @@ AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="If multiple entries match
|
|||||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... for{{duration}}seconds switch to{{scenes}}using{{transitions}}"
|
AdvSceneSwitcher.audioTab.multiMatchfallback="... for{{duration}}seconds switch to{{scenes}}using{{transitions}}"
|
||||||
AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Video Tab
|
# Video Tab
|
||||||
AdvSceneSwitcher.videoTab.title="Video"
|
AdvSceneSwitcher.videoTab.title="Video"
|
||||||
AdvSceneSwitcher.videoTab.getScreenshot="Get screenshot for selected entry"
|
AdvSceneSwitcher.videoTab.getScreenshot="Get screenshot for selected entry"
|
||||||
AdvSceneSwitcher.videoTab.getScreenshotHelp="Get Screenshot of the currently selected entry's video source and automatically set it as the target image"
|
AdvSceneSwitcher.videoTab.getScreenshotHelp="Get Screenshot of the currently selected entry's video source and automatically set it as the target image"
|
||||||
@@ -2054,7 +2124,7 @@ AdvSceneSwitcher.videoTab.ignoreInactiveSource="unless source is inactive"
|
|||||||
AdvSceneSwitcher.videoTab.entry="When{{videoSources}}{{condition}}{{filePath}}{{browseButton}}for{{duration}}switch to{{scenes}}using{{transitions}}{{ignoreInactiveSource}}"
|
AdvSceneSwitcher.videoTab.entry="When{{videoSources}}{{condition}}{{filePath}}{{browseButton}}for{{duration}}switch to{{scenes}}using{{transitions}}{{ignoreInactiveSource}}"
|
||||||
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to switch scenes based on the current video output of selected sources.<br/>Make sure to check out <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> for an even better implementation of this functionality.<br/><br/> Click on the highlighted plus symbol to continue.</p></body></html>"
|
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to switch scenes based on the current video output of selected sources.<br/>Make sure to check out <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> for an even better implementation of this functionality.<br/><br/> Click on the highlighted plus symbol to continue.</p></body></html>"
|
||||||
|
|
||||||
; Network Tab
|
# Network Tab
|
||||||
AdvSceneSwitcher.networkTab.title="Network"
|
AdvSceneSwitcher.networkTab.title="Network"
|
||||||
AdvSceneSwitcher.networkTab.description="This tab will allow you to remotely control the active scene of another OBS instance.\nPlease note that the scene names have to match exactly on all OBS instances."
|
AdvSceneSwitcher.networkTab.description="This tab will allow you to remotely control the active scene of another OBS instance.\nPlease note that the scene names have to match exactly on all OBS instances."
|
||||||
AdvSceneSwitcher.networkTab.warning="Running the server outside of a local network will allow third parties to read the active scene."
|
AdvSceneSwitcher.networkTab.warning="Running the server outside of a local network will allow third parties to read the active scene."
|
||||||
@@ -2079,7 +2149,7 @@ AdvSceneSwitcher.networkTab.client.status.connecting="Connecting"
|
|||||||
AdvSceneSwitcher.networkTab.client.status.connected="Connected"
|
AdvSceneSwitcher.networkTab.client.status.connected="Connected"
|
||||||
AdvSceneSwitcher.networkTab.client.reconnect="Force reconnect"
|
AdvSceneSwitcher.networkTab.client.reconnect="Force reconnect"
|
||||||
|
|
||||||
; Scene Group Tab
|
# Scene Group Tab
|
||||||
AdvSceneSwitcher.sceneGroupTab.title="Scene Group"
|
AdvSceneSwitcher.sceneGroupTab.title="Scene Group"
|
||||||
AdvSceneSwitcher.sceneGroupTab.list="Scene Groups"
|
AdvSceneSwitcher.sceneGroupTab.list="Scene Groups"
|
||||||
AdvSceneSwitcher.sceneGroupTab.edit="Edit Scene Groups"
|
AdvSceneSwitcher.sceneGroupTab.edit="Edit Scene Groups"
|
||||||
@@ -2099,7 +2169,7 @@ AdvSceneSwitcher.sceneGroupTab.exists="Scene Group or Scene name exists already"
|
|||||||
AdvSceneSwitcher.sceneGroupTab.help="Scene Groups can be selected as a target just like a regular scene.\n\nAs the name suggests a scene group is a collection of multiple scenes.\nThe scene group will advance through the list of its assigned scenes depending on the configured settings, which can be found on the right side.\n\nYou can configure the scene group to advance to the next scene in the list:\nAfter a number of times the scene group is selected as a target.\nAfter a certain amount of time has passed.\nOr randomly.\n\nFor example, a scene group containing the scenes ...\nScene 1\nScene 2\nScene 3 \n... will activate \"Scene 1\" the first time it is selected as a target.\nThe second time it will activate \"Scene 2\".\nThe remaining times \"Scene 3\" will be activated.\n\nClick the highlighted plus symbol below to add a new scene group."
|
AdvSceneSwitcher.sceneGroupTab.help="Scene Groups can be selected as a target just like a regular scene.\n\nAs the name suggests a scene group is a collection of multiple scenes.\nThe scene group will advance through the list of its assigned scenes depending on the configured settings, which can be found on the right side.\n\nYou can configure the scene group to advance to the next scene in the list:\nAfter a number of times the scene group is selected as a target.\nAfter a certain amount of time has passed.\nOr randomly.\n\nFor example, a scene group containing the scenes ...\nScene 1\nScene 2\nScene 3 \n... will activate \"Scene 1\" the first time it is selected as a target.\nThe second time it will activate \"Scene 2\".\nThe remaining times \"Scene 3\" will be activated.\n\nClick the highlighted plus symbol below to add a new scene group."
|
||||||
AdvSceneSwitcher.sceneGroupTab.scenes.help="Select the scene group you want to modify on the left.\n\nSelect a scene to add to this scene group by selecting the scene above and clicking the plus symbol below.\n\nA scene can be added multiple times to the same scene group."
|
AdvSceneSwitcher.sceneGroupTab.scenes.help="Select the scene group you want to modify on the left.\n\nSelect a scene to add to this scene group by selecting the scene above and clicking the plus symbol below.\n\nA scene can be added multiple times to the same scene group."
|
||||||
|
|
||||||
; Scene Trigger Tab
|
# Scene Trigger Tab
|
||||||
AdvSceneSwitcher.sceneTriggerTab.title="Scene Triggers"
|
AdvSceneSwitcher.sceneTriggerTab.title="Scene Triggers"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--select trigger--"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--select trigger--"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="is active"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="is active"
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ AdvSceneSwitcher.condition.media="Medios"
|
|||||||
AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada"
|
AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada"
|
||||||
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
|
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
|
||||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
|
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
|
||||||
AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}"
|
AdvSceneSwitcher.condition.media.layout.legacy="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}es{{states}}y{{timeRestrictions}}{{time}}"
|
||||||
AdvSceneSwitcher.condition.video="Video"
|
AdvSceneSwitcher.condition.video="Video"
|
||||||
AdvSceneSwitcher.condition.video.condition.match="coincide exactamente"
|
AdvSceneSwitcher.condition.video.condition.match="coincide exactamente"
|
||||||
AdvSceneSwitcher.condition.video.condition.differ="no coincide"
|
AdvSceneSwitcher.condition.video.condition.differ="no coincide"
|
||||||
@@ -269,10 +269,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima coincidencia en: %
|
|||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
|
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
|
||||||
AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena"
|
AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación"
|
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="En{{scenes}}{{sources}}{{types}}"
|
AdvSceneSwitcher.condition.sceneTransform.condition.match="coincide con la transformación"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="coincide con la transformación"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
|
||||||
AdvSceneSwitcher.condition.transition="Transición"
|
AdvSceneSwitcher.condition.transition="Transición"
|
||||||
AdvSceneSwitcher.condition.transition.type.current="El tipo de transición actual es"
|
AdvSceneSwitcher.condition.transition.type.current="El tipo de transición actual es"
|
||||||
AdvSceneSwitcher.condition.transition.type.duration="La duración de la transición actual es"
|
AdvSceneSwitcher.condition.transition.type.duration="La duración de la transición actual es"
|
||||||
@@ -415,7 +412,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
|
|||||||
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
|
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||||
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
|
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
|
||||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
|
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
|
||||||
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}"
|
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||||
AdvSceneSwitcher.action.file="Archivo"
|
AdvSceneSwitcher.action.file="Archivo"
|
||||||
AdvSceneSwitcher.action.file.type.write="Escribir"
|
AdvSceneSwitcher.action.file.type.write="Escribir"
|
||||||
AdvSceneSwitcher.action.file.type.append="Agregar"
|
AdvSceneSwitcher.action.file.type.append="Agregar"
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ AdvSceneSwitcher.condition.media.source="Source"
|
|||||||
AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène"
|
AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène"
|
||||||
AdvSceneSwitcher.condition.media.allOnScene="Toutes les sources média sur la scène"
|
AdvSceneSwitcher.condition.media.allOnScene="Toutes les sources média sur la scène"
|
||||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Malheureusement, toutes les sources média ne se comportent pas de la même manière (par exemple, l'état \"Arrêté\" de la source Media par rapport à la source Vidéo VLC).\nPar conséquent, veuillez expérimenter ce qui fonctionne le mieux pour votre configuration !"
|
AdvSceneSwitcher.condition.media.inconsistencyInfo="Malheureusement, toutes les sources média ne se comportent pas de la même manière (par exemple, l'état \"Arrêté\" de la source Media par rapport à la source Vidéo VLC).\nPar conséquent, veuillez expérimenter ce qui fonctionne le mieux pour votre configuration !"
|
||||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}l'état est{{states}}et{{timeRestrictions}}{{time}}"
|
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}l'état est{{states}}et{{timeRestrictions}}{{time}}"
|
||||||
AdvSceneSwitcher.condition.video="Vidéo"
|
AdvSceneSwitcher.condition.video="Vidéo"
|
||||||
AdvSceneSwitcher.condition.video.condition.match="correspond exactement à"
|
AdvSceneSwitcher.condition.video.condition.match="correspond exactement à"
|
||||||
AdvSceneSwitcher.condition.video.condition.differ="ne correspond pas à"
|
AdvSceneSwitcher.condition.video.condition.differ="ne correspond pas à"
|
||||||
@@ -382,8 +382,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Prochaine correspondance à
|
|||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}À la répétition, mettre à jour la date sélectionnée pour la date de répétition"
|
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}À la répétition, mettre à jour la date sélectionnée pour la date de répétition"
|
||||||
AdvSceneSwitcher.condition.sceneTransform="Transformation de l'élément de la scène"
|
AdvSceneSwitcher.condition.sceneTransform="Transformation de l'élément de la scène"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtenir la transformation"
|
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtenir la transformation"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Sur{{scenes}}{{sources}}{{types}}"
|
AdvSceneSwitcher.condition.sceneTransform.condition.match="correspond à la transformation"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="correspond à la transformation"
|
|
||||||
AdvSceneSwitcher.condition.transition="Transition"
|
AdvSceneSwitcher.condition.transition="Transition"
|
||||||
AdvSceneSwitcher.condition.transition.type.current="Le type de transition actuel est"
|
AdvSceneSwitcher.condition.transition.type.current="Le type de transition actuel est"
|
||||||
AdvSceneSwitcher.condition.transition.type.duration="La durée de la transition actuelle est"
|
AdvSceneSwitcher.condition.transition.type.duration="La durée de la transition actuelle est"
|
||||||
@@ -611,7 +610,7 @@ AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="Centrer à l'écran"
|
|||||||
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Centrer verticalement"
|
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Centrer verticalement"
|
||||||
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Centrer horizontalement"
|
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Centrer horizontalement"
|
||||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtenir la transformation"
|
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtenir la transformation"
|
||||||
AdvSceneSwitcher.action.sceneTransform.entry="Sur{{scenes}}{{action}}{{rotation}}{{sources}}"
|
AdvSceneSwitcher.action.sceneTransform.entry="Sur{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||||
AdvSceneSwitcher.action.file="Fichier"
|
AdvSceneSwitcher.action.file="Fichier"
|
||||||
AdvSceneSwitcher.action.file.type.write="Écrire"
|
AdvSceneSwitcher.action.file.type.write="Écrire"
|
||||||
AdvSceneSwitcher.action.file.type.append="Ajouter"
|
AdvSceneSwitcher.action.file.type.append="Ajouter"
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ AdvSceneSwitcher.condition.media.source="ソース"
|
|||||||
AdvSceneSwitcher.condition.media.anyOnScene="あらゆるメディアソース"
|
AdvSceneSwitcher.condition.media.anyOnScene="あらゆるメディアソース"
|
||||||
AdvSceneSwitcher.condition.media.allOnScene="すべてのメディアソースはこちら"
|
AdvSceneSwitcher.condition.media.allOnScene="すべてのメディアソースはこちら"
|
||||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="残念ながら、すべてのメディア ソース タイプが同じように動作するわけではありません (例: メディア ソースと VLC ビデオ ソースの \"Stopped\" 状態)。\nそのため、ご自身のセットアップに適したものを試してみてください。"
|
AdvSceneSwitcher.condition.media.inconsistencyInfo="残念ながら、すべてのメディア ソース タイプが同じように動作するわけではありません (例: メディア ソースと VLC ビデオ ソースの \"Stopped\" 状態)。\nそのため、ご自身のセットアップに適したものを試してみてください。"
|
||||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}の状態は{{states}}かつ{{timeRestrictions}}{{time}}"
|
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}の状態は{{states}}かつ{{timeRestrictions}}{{time}}"
|
||||||
AdvSceneSwitcher.condition.video="動画"
|
AdvSceneSwitcher.condition.video="動画"
|
||||||
AdvSceneSwitcher.condition.video.condition.match="完全一致"
|
AdvSceneSwitcher.condition.video.condition.match="完全一致"
|
||||||
AdvSceneSwitcher.condition.video.condition.differ="合わない"
|
AdvSceneSwitcher.condition.video.condition.differ="合わない"
|
||||||
@@ -531,11 +531,8 @@ AdvSceneSwitcher.condition.date.entry.pattern="現在の日付「{{currentDate}}
|
|||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}繰り返しの場合、選択した日付を繰り返しの日付に更新します"
|
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}繰り返しの場合、選択した日付を繰り返しの日付に更新します"
|
||||||
; AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
; AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
||||||
; AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
; AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
||||||
; AdvSceneSwitcher.condition.sceneTransform.entry.line1="On{{scenes}}{{sources}}{{types}}"
|
AdvSceneSwitcher.condition.sceneTransform.condition.match="マッチトランスフォーム"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="マッチトランスフォーム"
|
AdvSceneSwitcher.condition.sceneTransform.condition.changed="変わりました"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.changed="変わりました"
|
|
||||||
; AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
|
||||||
; AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}}{{getSettings}}"
|
|
||||||
; AdvSceneSwitcher.condition.transition="Transition"
|
; AdvSceneSwitcher.condition.transition="Transition"
|
||||||
; AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
|
; AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
|
||||||
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
|
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
|
||||||
@@ -903,7 +900,6 @@ AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="画面中央から"
|
|||||||
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="縦方向中央に配置"
|
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="縦方向中央に配置"
|
||||||
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="水平方向に中央揃え"
|
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="水平方向に中央揃え"
|
||||||
AdvSceneSwitcher.action.sceneTransform.getTransform="変形を取得"
|
AdvSceneSwitcher.action.sceneTransform.getTransform="変形を取得"
|
||||||
; AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}"
|
|
||||||
AdvSceneSwitcher.action.file="ファイル"
|
AdvSceneSwitcher.action.file="ファイル"
|
||||||
; AdvSceneSwitcher.action.file.type.write="Write"
|
; AdvSceneSwitcher.action.file.type.write="Write"
|
||||||
; AdvSceneSwitcher.action.file.type.append="Append"
|
; AdvSceneSwitcher.action.file.type.append="Append"
|
||||||
|
|||||||
2139
data/locale/pt-BR.ini
Normal file
2139
data/locale/pt-BR.ini
Normal file
File diff suppressed because it is too large
Load Diff
@@ -117,7 +117,7 @@ AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFil
|
|||||||
AdvSceneSwitcher.condition.media="Medya"
|
AdvSceneSwitcher.condition.media="Medya"
|
||||||
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
|
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
|
||||||
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
|
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
|
||||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
|
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
|
||||||
AdvSceneSwitcher.condition.video="Video"
|
AdvSceneSwitcher.condition.video="Video"
|
||||||
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
|
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
|
||||||
AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme"
|
AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme"
|
||||||
@@ -226,10 +226,7 @@ AdvSceneSwitcher.condition.date.state.before="Önce"
|
|||||||
AdvSceneSwitcher.condition.date.state.between="Arasında"
|
AdvSceneSwitcher.condition.date.state.between="Arasında"
|
||||||
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
|
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
|
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}}{{types}}"
|
AdvSceneSwitcher.condition.sceneTransform.condition.match="dönüşümle eşleşir"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="dönüşümle eşleşir"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
|
||||||
AdvSceneSwitcher.condition.transition="Geçiş"
|
AdvSceneSwitcher.condition.transition="Geçiş"
|
||||||
AdvSceneSwitcher.condition.transition.type.current="Geçerli geçiş türü"
|
AdvSceneSwitcher.condition.transition.type.current="Geçerli geçiş türü"
|
||||||
AdvSceneSwitcher.condition.transition.type.duration="Mevcut geçiş süresi"
|
AdvSceneSwitcher.condition.transition.type.duration="Mevcut geçiş süresi"
|
||||||
@@ -344,7 +341,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
|
|||||||
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
|
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||||
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
|
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
|
||||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
|
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
|
||||||
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}"
|
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||||
AdvSceneSwitcher.action.file="Dosya"
|
AdvSceneSwitcher.action.file="Dosya"
|
||||||
AdvSceneSwitcher.action.file.type.write="Yaz"
|
AdvSceneSwitcher.action.file.type.write="Yaz"
|
||||||
AdvSceneSwitcher.action.file.type.append="Ekle"
|
AdvSceneSwitcher.action.file.type.append="Ekle"
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ AdvSceneSwitcher.condition.media.source="源"
|
|||||||
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源"
|
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源"
|
||||||
AdvSceneSwitcher.condition.media.allOnScene="所有媒体源"
|
AdvSceneSwitcher.condition.media.allOnScene="所有媒体源"
|
||||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是,并非所有媒体源类型的行为都相同(例如,媒体源与VLC视频源“停止”状态)。\n所以,请尝试适合您的设置!"
|
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是,并非所有媒体源类型的行为都相同(例如,媒体源与VLC视频源“停止”状态)。\n所以,请尝试适合您的设置!"
|
||||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}} 的状态是 {{states}} 和 {{timeRestrictions}}{{time}}"
|
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}的状态是{{states}}和{{timeRestrictions}}{{time}}"
|
||||||
AdvSceneSwitcher.condition.video="视频"
|
AdvSceneSwitcher.condition.video="视频"
|
||||||
AdvSceneSwitcher.condition.video.condition.match="完全匹配"
|
AdvSceneSwitcher.condition.video.condition.match="完全匹配"
|
||||||
AdvSceneSwitcher.condition.video.condition.differ="不匹配"
|
AdvSceneSwitcher.condition.video.condition.differ="不匹配"
|
||||||
@@ -365,10 +365,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="下一次匹配 在: %1"
|
|||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 重复时,将选定的日期更新为重复日期"
|
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 重复时,将选定的日期更新为重复日期"
|
||||||
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
|
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
|
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="在 {{scenes}} {{sources}} {{types}}"
|
AdvSceneSwitcher.condition.sceneTransform.condition.match="匹配变换"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="匹配变换"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
|
||||||
AdvSceneSwitcher.condition.transition="转场"
|
AdvSceneSwitcher.condition.transition="转场"
|
||||||
AdvSceneSwitcher.condition.transition.type.current="当前转场特效类型为"
|
AdvSceneSwitcher.condition.transition.type.current="当前转场特效类型为"
|
||||||
AdvSceneSwitcher.condition.transition.type.duration="当前转场特效持续时间为"
|
AdvSceneSwitcher.condition.transition.type.duration="当前转场特效持续时间为"
|
||||||
@@ -555,7 +552,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置"
|
|||||||
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
|
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
|
||||||
AdvSceneSwitcher.action.sceneTransform="场景项目转换"
|
AdvSceneSwitcher.action.sceneTransform="场景项目转换"
|
||||||
AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换"
|
AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换"
|
||||||
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}}{{action}}{{rotation}}{{sources}}"
|
AdvSceneSwitcher.action.sceneTransform.entry="在{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||||
AdvSceneSwitcher.action.file="文件"
|
AdvSceneSwitcher.action.file="文件"
|
||||||
AdvSceneSwitcher.action.file.type.write="写入"
|
AdvSceneSwitcher.action.file.type.write="写入"
|
||||||
AdvSceneSwitcher.action.file.type.append="追加写入"
|
AdvSceneSwitcher.action.file.type.append="追加写入"
|
||||||
|
|||||||
@@ -390,7 +390,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="cooldownLayout">
|
<layout class="QHBoxLayout" name="cooldownLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QCheckBox" name="enableCooldown">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.cooldown</string>
|
<string>AdvSceneSwitcher.generalTab.generalBehavior.cooldown</string>
|
||||||
</property>
|
</property>
|
||||||
|
|||||||
@@ -93,10 +93,24 @@ static void DisplayMissingDataDirWarning()
|
|||||||
DisplayMessage(msg);
|
DisplayMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CanCreateDefaultAction();
|
||||||
|
bool CanCreateDefaultCondition();
|
||||||
|
|
||||||
|
static void DisplayCorruptedInstallWarning()
|
||||||
|
{
|
||||||
|
if (CanCreateDefaultAction() && CanCreateDefaultCondition()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMessage(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage"));
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::LoadUI()
|
void AdvSceneSwitcher::LoadUI()
|
||||||
{
|
{
|
||||||
DisplayMissingDataDirWarning();
|
DisplayMissingDataDirWarning();
|
||||||
DisplayMissingDependencyWarning();
|
DisplayMissingDependencyWarning();
|
||||||
|
DisplayCorruptedInstallWarning();
|
||||||
|
|
||||||
SetupGeneralTab();
|
SetupGeneralTab();
|
||||||
SetupTitleTab();
|
SetupTitleTab();
|
||||||
@@ -241,8 +255,8 @@ void SwitcherData::Thread()
|
|||||||
duration = std::chrono::milliseconds(interval) +
|
duration = std::chrono::milliseconds(interval) +
|
||||||
std::chrono::milliseconds(linger) - runTime;
|
std::chrono::milliseconds(linger) - runTime;
|
||||||
if (duration.count() < 1) {
|
if (duration.count() < 1) {
|
||||||
blog(LOG_INFO,
|
vblog(LOG_INFO,
|
||||||
"detected busy loop - refusing to sleep less than 1ms");
|
"detected busy loop - refusing to sleep less than 1ms");
|
||||||
duration = std::chrono::milliseconds(10);
|
duration = std::chrono::milliseconds(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -708,7 +722,7 @@ static void LoadPlugins()
|
|||||||
{
|
{
|
||||||
QFileInfo libPath(
|
QFileInfo libPath(
|
||||||
QString(obs_get_module_binary_path(obs_current_module())));
|
QString(obs_get_module_binary_path(obs_current_module())));
|
||||||
QString pluginDir(libPath.absolutePath() + "/adv-ss-plugins");
|
QString pluginDir(libPath.absolutePath() + "/" ADVSS_PLUGIN_FOLDER);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
QString libPattern = "*.dll";
|
QString libPattern = "*.dll";
|
||||||
SetDllDirectory(pluginDir.toStdWString().c_str());
|
SetDllDirectory(pluginDir.toStdWString().c_str());
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "macro-segment-list.hpp"
|
#include "macro-segment-list.hpp"
|
||||||
|
#include "condition-logic.hpp"
|
||||||
#include "log-helper.hpp"
|
#include "log-helper.hpp"
|
||||||
|
|
||||||
#include <ui_advanced-scene-switcher.h>
|
#include <ui_advanced-scene-switcher.h>
|
||||||
@@ -12,7 +13,6 @@ class MacroActionEdit;
|
|||||||
class MacroConditionEdit;
|
class MacroConditionEdit;
|
||||||
class Duration;
|
class Duration;
|
||||||
class SequenceWidget;
|
class SequenceWidget;
|
||||||
enum class LogicType;
|
|
||||||
struct SceneGroup;
|
struct SceneGroup;
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -53,6 +53,7 @@ public slots:
|
|||||||
void on_noMatchRandomSwitch_clicked();
|
void on_noMatchRandomSwitch_clicked();
|
||||||
void NoMatchDelayDurationChanged(const Duration &);
|
void NoMatchDelayDurationChanged(const Duration &);
|
||||||
void CooldownDurationChanged(const Duration &);
|
void CooldownDurationChanged(const Duration &);
|
||||||
|
void on_enableCooldown_stateChanged(int state);
|
||||||
void on_startupBehavior_currentIndexChanged(int index);
|
void on_startupBehavior_currentIndexChanged(int index);
|
||||||
void on_logLevel_currentIndexChanged(int index);
|
void on_logLevel_currentIndexChanged(int index);
|
||||||
void on_autoStartEvent_currentIndexChanged(int index);
|
void on_autoStartEvent_currentIndexChanged(int index);
|
||||||
@@ -173,7 +174,7 @@ public slots:
|
|||||||
void MacroConditionReorder(int to, int target);
|
void MacroConditionReorder(int to, int target);
|
||||||
void AddMacroCondition(int idx);
|
void AddMacroCondition(int idx);
|
||||||
void AddMacroCondition(Macro *macro, int idx, const std::string &id,
|
void AddMacroCondition(Macro *macro, int idx, const std::string &id,
|
||||||
obs_data_t *data, LogicType logic);
|
obs_data_t *data, Logic::Type logic);
|
||||||
void RemoveMacroCondition(int idx);
|
void RemoveMacroCondition(int idx);
|
||||||
void MoveMacroConditionUp(int idx);
|
void MoveMacroConditionUp(int idx);
|
||||||
void MoveMacroConditionDown(int idx);
|
void MoveMacroConditionDown(int idx);
|
||||||
|
|||||||
@@ -91,6 +91,17 @@ void AdvSceneSwitcher::CooldownDurationChanged(const Duration &dur)
|
|||||||
switcher->cooldown = dur;
|
switcher->cooldown = dur;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_enableCooldown_stateChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->enableCooldown = state;
|
||||||
|
ui->cooldownTime->setEnabled(state);
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -505,6 +516,7 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
|
|||||||
noMatchDelay.Save(obj, "noMatchDelay");
|
noMatchDelay.Save(obj, "noMatchDelay");
|
||||||
|
|
||||||
cooldown.Save(obj, "cooldown");
|
cooldown.Save(obj, "cooldown");
|
||||||
|
obs_data_set_bool(obj, "enableCooldown", enableCooldown);
|
||||||
|
|
||||||
obs_data_set_bool(obj, "active", sceneColletionStop ? true : !stop);
|
obs_data_set_bool(obj, "active", sceneColletionStop ? true : !stop);
|
||||||
sceneColletionStop = false;
|
sceneColletionStop = false;
|
||||||
@@ -550,6 +562,11 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
|
|||||||
noMatchDelay.Load(obj, "noMatchDelay");
|
noMatchDelay.Load(obj, "noMatchDelay");
|
||||||
|
|
||||||
cooldown.Load(obj, "cooldown");
|
cooldown.Load(obj, "cooldown");
|
||||||
|
if (!obs_data_has_user_value(obj, "enableCooldown")) {
|
||||||
|
enableCooldown = cooldown.Seconds() != 0;
|
||||||
|
} else {
|
||||||
|
enableCooldown = obs_data_get_bool(obj, "enableCooldown");
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_set_default_bool(obj, "active", true);
|
obs_data_set_default_bool(obj, "active", true);
|
||||||
stop = !obs_data_get_bool(obj, "active");
|
stop = !obs_data_get_bool(obj, "active");
|
||||||
@@ -653,7 +670,7 @@ void SwitcherData::CheckNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
|||||||
|
|
||||||
void SwitcherData::checkSwitchCooldown(bool &match)
|
void SwitcherData::checkSwitchCooldown(bool &match)
|
||||||
{
|
{
|
||||||
if (!match) {
|
if (!match || !enableCooldown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,6 +839,8 @@ void AdvSceneSwitcher::SetupGeneralTab()
|
|||||||
|
|
||||||
ui->checkInterval->setValue(switcher->interval);
|
ui->checkInterval->setValue(switcher->interval);
|
||||||
|
|
||||||
|
ui->enableCooldown->setChecked(switcher->enableCooldown);
|
||||||
|
ui->cooldownTime->setEnabled(switcher->enableCooldown);
|
||||||
ui->cooldownTime->SetDuration(switcher->cooldown);
|
ui->cooldownTime->SetDuration(switcher->cooldown);
|
||||||
ui->cooldownTime->setToolTip(obs_module_text(
|
ui->cooldownTime->setToolTip(obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
||||||
|
|||||||
@@ -170,8 +170,10 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
|
|||||||
item->setData(Qt::UserRole, text);
|
item->setData(Qt::UserRole, text);
|
||||||
ui->sceneGroups->setCurrentItem(item);
|
ui->sceneGroups->setCurrentItem(item);
|
||||||
|
|
||||||
addPulse->deleteLater();
|
if (addPulse) {
|
||||||
addPulse = nullptr;
|
addPulse->deleteLater();
|
||||||
|
addPulse = nullptr;
|
||||||
|
}
|
||||||
ui->sceneGroupHelp->setVisible(false);
|
ui->sceneGroupHelp->setVisible(false);
|
||||||
|
|
||||||
emit SceneGroupAdded(QString::fromStdString(name));
|
emit SceneGroupAdded(QString::fromStdString(name));
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_triggerAdd_clicked()
|
|||||||
listAddClicked(ui->sceneTriggers,
|
listAddClicked(ui->sceneTriggers,
|
||||||
new SceneTriggerWidget(this,
|
new SceneTriggerWidget(this,
|
||||||
&switcher->sceneTriggers.back()),
|
&switcher->sceneTriggers.back()),
|
||||||
ui->triggerAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->triggerHelp->setVisible(false);
|
ui->triggerHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_audioAdd_clicked()
|
|||||||
AudioSwitchWidget *sw =
|
AudioSwitchWidget *sw =
|
||||||
new AudioSwitchWidget(this, &switcher->audioSwitches.back());
|
new AudioSwitchWidget(this, &switcher->audioSwitches.back());
|
||||||
|
|
||||||
listAddClicked(ui->audioSwitches, sw, ui->audioAdd, addPulse);
|
listAddClicked(ui->audioSwitches, sw, addPulse);
|
||||||
|
|
||||||
ui->audioHelp->setVisible(false);
|
ui->audioHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_executableAdd_clicked()
|
|||||||
listAddClicked(ui->executables,
|
listAddClicked(ui->executables,
|
||||||
new ExecutableSwitchWidget(
|
new ExecutableSwitchWidget(
|
||||||
this, &switcher->executableSwitches.back()),
|
this, &switcher->executableSwitches.back()),
|
||||||
ui->executableAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->exeHelp->setVisible(false);
|
ui->exeHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ void AdvSceneSwitcher::on_fileAdd_clicked()
|
|||||||
listAddClicked(ui->fileSwitches,
|
listAddClicked(ui->fileSwitches,
|
||||||
new FileSwitchWidget(this,
|
new FileSwitchWidget(this,
|
||||||
&switcher->fileSwitches.back()),
|
&switcher->fileSwitches.back()),
|
||||||
ui->fileAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->fileHelp->setVisible(false);
|
ui->fileHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_mediaAdd_clicked()
|
|||||||
listAddClicked(ui->mediaSwitches,
|
listAddClicked(ui->mediaSwitches,
|
||||||
new MediaSwitchWidget(this,
|
new MediaSwitchWidget(this,
|
||||||
&switcher->mediaSwitches.back()),
|
&switcher->mediaSwitches.back()),
|
||||||
ui->mediaAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->mediaHelp->setVisible(false);
|
ui->mediaHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ void AdvSceneSwitcher::on_pauseAdd_clicked()
|
|||||||
listAddClicked(ui->pauseEntries,
|
listAddClicked(ui->pauseEntries,
|
||||||
new PauseEntryWidget(this,
|
new PauseEntryWidget(this,
|
||||||
&switcher->pauseEntries.back()),
|
&switcher->pauseEntries.back()),
|
||||||
ui->pauseAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->pauseHelp->setVisible(false);
|
ui->pauseHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_randomAdd_clicked()
|
|||||||
listAddClicked(ui->randomSwitches,
|
listAddClicked(ui->randomSwitches,
|
||||||
new RandomSwitchWidget(this,
|
new RandomSwitchWidget(this,
|
||||||
&switcher->randomSwitches.back()),
|
&switcher->randomSwitches.back()),
|
||||||
ui->randomAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->randomHelp->setVisible(false);
|
ui->randomHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ void AdvSceneSwitcher::on_screenRegionAdd_clicked()
|
|||||||
listAddClicked(ui->screenRegionSwitches,
|
listAddClicked(ui->screenRegionSwitches,
|
||||||
new ScreenRegionWidget(
|
new ScreenRegionWidget(
|
||||||
this, &switcher->screenRegionSwitches.back()),
|
this, &switcher->screenRegionSwitches.back()),
|
||||||
ui->screenRegionAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->regionHelp->setVisible(false);
|
ui->regionHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
|
|||||||
listAddClicked(ui->sceneSequenceSwitches,
|
listAddClicked(ui->sceneSequenceSwitches,
|
||||||
new SequenceWidget(
|
new SequenceWidget(
|
||||||
this, &switcher->sceneSequenceSwitches.back()),
|
this, &switcher->sceneSequenceSwitches.back()),
|
||||||
ui->sceneSequenceAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->sequenceHelp->setVisible(false);
|
ui->sequenceHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ void AdvSceneSwitcher::on_timeAdd_clicked()
|
|||||||
listAddClicked(ui->timeSwitches,
|
listAddClicked(ui->timeSwitches,
|
||||||
new TimeSwitchWidget(this,
|
new TimeSwitchWidget(this,
|
||||||
&switcher->timeSwitches.back()),
|
&switcher->timeSwitches.back()),
|
||||||
ui->timeAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->timeHelp->setVisible(false);
|
ui->timeHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ void AdvSceneSwitcher::on_videoAdd_clicked()
|
|||||||
VideoSwitchWidget *sw =
|
VideoSwitchWidget *sw =
|
||||||
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
|
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
|
||||||
|
|
||||||
listAddClicked(ui->videoSwitches, sw, ui->videoAdd, addPulse);
|
listAddClicked(ui->videoSwitches, sw, addPulse);
|
||||||
|
|
||||||
ui->videoHelp->setVisible(false);
|
ui->videoHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ void AdvSceneSwitcher::on_windowAdd_clicked()
|
|||||||
listAddClicked(ui->windowSwitches,
|
listAddClicked(ui->windowSwitches,
|
||||||
new WindowSwitchWidget(this,
|
new WindowSwitchWidget(this,
|
||||||
&switcher->windowSwitches.back()),
|
&switcher->windowSwitches.back()),
|
||||||
ui->windowAdd, addPulse);
|
addPulse);
|
||||||
|
|
||||||
ui->windowHelp->setVisible(false);
|
ui->windowHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,28 @@
|
|||||||
#include "macro-action-factory.hpp"
|
#include "macro-action-factory.hpp"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class MacroActionUnknown : public MacroAction {
|
||||||
|
public:
|
||||||
|
MacroActionUnknown(Macro *m) : MacroAction(m) {}
|
||||||
|
std::shared_ptr<MacroAction> Copy() const
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionUnknown>(GetMacro());
|
||||||
|
}
|
||||||
|
bool PerformAction() { return true; };
|
||||||
|
bool Save(obs_data_t *obj) const { return MacroAction::Save(obj); };
|
||||||
|
bool Load(obs_data_t *obj) { return MacroAction::Load(obj); };
|
||||||
|
std::string GetId() const { return "unknown"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static std::recursive_mutex mutex;
|
||||||
|
|
||||||
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
||||||
{
|
{
|
||||||
static std::map<std::string, MacroActionInfo> _methods;
|
static std::map<std::string, MacroActionInfo> _methods;
|
||||||
@@ -10,6 +31,7 @@ std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
|||||||
|
|
||||||
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
||||||
GetMap()[id] = info;
|
GetMap()[id] = info;
|
||||||
return true;
|
return true;
|
||||||
@@ -17,35 +39,61 @@ bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MacroActionFactory::Deregister(const std::string &id)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
|
if (GetMap().count(id) == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GetMap().erase(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<MacroAction> createUnknownAction(Macro *m)
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionUnknown>(m);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
|
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
|
||||||
Macro *m)
|
Macro *m)
|
||||||
{
|
{
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end())
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._create(m);
|
return it->second._create(m);
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return createUnknownAction(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QWidget *createUnknownActionWidget()
|
||||||
|
{
|
||||||
|
return new QLabel(obs_module_text("AdvSceneSwitcher.action.unknown"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
|
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
std::shared_ptr<MacroAction> action)
|
std::shared_ptr<MacroAction> action)
|
||||||
{
|
{
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end())
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._createWidget(parent, action);
|
return it->second._createWidget(parent, action);
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return createUnknownActionWidget();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MacroActionFactory::GetActionName(const std::string &id)
|
std::string MacroActionFactory::GetActionName(const std::string &id)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._name;
|
return it->second._name;
|
||||||
}
|
}
|
||||||
return "unknown action";
|
return obs_module_text("AdvSceneSwitcher.action.unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MacroActionFactory::GetIdByName(const QString &name)
|
std::string MacroActionFactory::GetIdByName(const QString &name)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
for (auto it : GetMap()) {
|
for (auto it : GetMap()) {
|
||||||
if (name == obs_module_text(it.second._name.c_str())) {
|
if (name == obs_module_text(it.second._name.c_str())) {
|
||||||
return it.first;
|
return it.first;
|
||||||
@@ -54,4 +102,14 @@ std::string MacroActionFactory::GetIdByName(const QString &name)
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CanCreateDefaultAction()
|
||||||
|
{
|
||||||
|
const auto action = MacroActionFactory::Create(
|
||||||
|
MacroAction::GetDefaultID().data(), nullptr);
|
||||||
|
if (!action) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return action->GetId() == MacroAction::GetDefaultID().data();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
struct MacroActionInfo {
|
struct MacroActionInfo {
|
||||||
using CreateAction = std::shared_ptr<MacroAction> (*)(Macro *m);
|
|
||||||
using CreateActionWidget = QWidget *(*)(QWidget *parent,
|
using CreateActionWidget = QWidget *(*)(QWidget *parent,
|
||||||
std::shared_ptr<MacroAction>);
|
std::shared_ptr<MacroAction>);
|
||||||
CreateAction _create = nullptr;
|
|
||||||
|
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
|
||||||
CreateActionWidget _createWidget = nullptr;
|
CreateActionWidget _createWidget = nullptr;
|
||||||
std::string _name;
|
std::string _name;
|
||||||
};
|
};
|
||||||
@@ -19,6 +19,7 @@ public:
|
|||||||
MacroActionFactory() = delete;
|
MacroActionFactory() = delete;
|
||||||
|
|
||||||
EXPORT static bool Register(const std::string &id, MacroActionInfo);
|
EXPORT static bool Register(const std::string &id, MacroActionInfo);
|
||||||
|
static bool Deregister(const std::string &id);
|
||||||
static std::shared_ptr<MacroAction> Create(const std::string &id,
|
static std::shared_ptr<MacroAction> Create(const std::string &id,
|
||||||
Macro *m);
|
Macro *m);
|
||||||
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
|
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
|
||||||
@@ -31,4 +32,6 @@ private:
|
|||||||
static std::map<std::string, MacroActionInfo> &GetMap();
|
static std::map<std::string, MacroActionInfo> &GetMap();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool CanCreateDefaultAction();
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
92
lib/macro/macro-action-script.cpp
Normal file
92
lib/macro/macro-action-script.cpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#include "macro-action-script.hpp"
|
||||||
|
#include "layout-helpers.hpp"
|
||||||
|
#include "macro-helpers.hpp"
|
||||||
|
#include "properties-view.hpp"
|
||||||
|
#include "sync-helpers.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
MacroActionScript::MacroActionScript(Macro *m, const std::string &id,
|
||||||
|
const OBSData &defaultSettings,
|
||||||
|
const std::string &propertiesSignalName,
|
||||||
|
const std::string &triggerSignal,
|
||||||
|
const std::string &completionSignal)
|
||||||
|
: MacroAction(m),
|
||||||
|
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||||
|
triggerSignal, completionSignal),
|
||||||
|
_id(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroActionScript::MacroActionScript(const advss::MacroActionScript &other)
|
||||||
|
: MacroAction(other.GetMacro()),
|
||||||
|
MacroSegmentScript(other),
|
||||||
|
_id(other._id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionScript::PerformAction()
|
||||||
|
{
|
||||||
|
if (!ScriptHandler::ActionIdIsValid(_id)) {
|
||||||
|
blog(LOG_WARNING, "skipping unknown script action \"%s\"",
|
||||||
|
_id.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)SendTriggerSignal();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionScript::LogAction() const
|
||||||
|
{
|
||||||
|
ablog(LOG_INFO, "performing script action \"%s\"", GetId().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionScript::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
MacroAction::Save(obj);
|
||||||
|
MacroSegmentScript::Save(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionScript::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
MacroAction::Load(obj);
|
||||||
|
MacroSegmentScript::Load(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroAction> MacroActionScript::Copy() const
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionScript>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionScript::WaitForCompletion() const
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
start - start);
|
||||||
|
const auto timeoutMs = GetTimeoutSeconds() * 1000.0;
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(*GetMutex());
|
||||||
|
while (!TriggerIsCompleted()) {
|
||||||
|
if (MacroWaitShouldAbort() || MacroIsStopped(GetMacro())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((double)timePassed.count() > timeoutMs) {
|
||||||
|
blog(LOG_INFO, "script action timeout (%s)",
|
||||||
|
_id.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetMacroWaitCV().wait_for(lock, 10ms);
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
timePassed =
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
now - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
29
lib/macro/macro-action-script.hpp
Normal file
29
lib/macro/macro-action-script.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "macro-action-edit.hpp"
|
||||||
|
#include "macro-script-handler.hpp"
|
||||||
|
#include "macro-segment-script.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class MacroActionScript : public MacroAction, public MacroSegmentScript {
|
||||||
|
public:
|
||||||
|
MacroActionScript(Macro *m, const std::string &id,
|
||||||
|
const OBSData &defaultSettings,
|
||||||
|
const std::string &propertiesSignalName,
|
||||||
|
const std::string &triggerSignal,
|
||||||
|
const std::string &signalComplete);
|
||||||
|
MacroActionScript(const advss::MacroActionScript &);
|
||||||
|
bool PerformAction();
|
||||||
|
void LogAction() const;
|
||||||
|
bool Save(obs_data_t *obj) const;
|
||||||
|
bool Load(obs_data_t *obj);
|
||||||
|
std::string GetId() const { return _id; };
|
||||||
|
std::shared_ptr<MacroAction> Copy() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WaitForCompletion() const;
|
||||||
|
|
||||||
|
std::string _id = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -10,26 +10,6 @@
|
|||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
static inline void populateLogicSelection(QComboBox *list, bool root = false)
|
|
||||||
{
|
|
||||||
if (root) {
|
|
||||||
for (const auto &entry : MacroCondition::logicTypes) {
|
|
||||||
if (static_cast<int>(entry.first) < logic_root_offset) {
|
|
||||||
list->addItem(obs_module_text(
|
|
||||||
entry.second._name.c_str()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const auto &entry : MacroCondition::logicTypes) {
|
|
||||||
if (static_cast<int>(entry.first) >=
|
|
||||||
logic_root_offset) {
|
|
||||||
list->addItem(obs_module_text(
|
|
||||||
entry.second._name.c_str()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void populateConditionSelection(QComboBox *list)
|
static inline void populateConditionSelection(QComboBox *list)
|
||||||
{
|
{
|
||||||
for (const auto &[_, condition] :
|
for (const auto &[_, condition] :
|
||||||
@@ -120,13 +100,13 @@ void DurationModifierEdit::Collapse(bool collapse)
|
|||||||
|
|
||||||
MacroConditionEdit::MacroConditionEdit(
|
MacroConditionEdit::MacroConditionEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
||||||
const std::string &id, bool root)
|
const std::string &id, bool isRootCondition)
|
||||||
: MacroSegmentEdit(parent),
|
: MacroSegmentEdit(parent),
|
||||||
_logicSelection(new QComboBox()),
|
_logicSelection(new QComboBox()),
|
||||||
_conditionSelection(new FilterComboBox()),
|
_conditionSelection(new FilterComboBox()),
|
||||||
_dur(new DurationModifierEdit()),
|
_dur(new DurationModifierEdit()),
|
||||||
_entryData(entryData),
|
_entryData(entryData),
|
||||||
_isRoot(root)
|
_isRoot(isRootCondition)
|
||||||
{
|
{
|
||||||
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
|
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
|
||||||
this, SLOT(LogicSelectionChanged(int)));
|
this, SLOT(LogicSelectionChanged(int)));
|
||||||
@@ -139,7 +119,7 @@ MacroConditionEdit::MacroConditionEdit(
|
|||||||
this,
|
this,
|
||||||
SLOT(DurationModifierChanged(DurationModifier::Type)));
|
SLOT(DurationModifierChanged(DurationModifier::Type)));
|
||||||
|
|
||||||
populateLogicSelection(_logicSelection, root);
|
Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition);
|
||||||
populateConditionSelection(_conditionSelection);
|
populateConditionSelection(_conditionSelection);
|
||||||
|
|
||||||
_section->AddHeaderWidget(_logicSelection);
|
_section->AddHeaderWidget(_logicSelection);
|
||||||
@@ -168,15 +148,10 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogicType type;
|
|
||||||
if (IsRootNode()) {
|
|
||||||
type = static_cast<LogicType>(idx);
|
|
||||||
} else {
|
|
||||||
type = static_cast<LogicType>(idx + logic_root_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
auto lock = LockContext();
|
||||||
(*_entryData)->SetLogicType(type);
|
const auto logic = static_cast<Logic::Type>(
|
||||||
|
_logicSelection->itemData(idx).toInt());
|
||||||
|
(*_entryData)->SetLogicType(logic);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionEdit::IsRootNode()
|
bool MacroConditionEdit::IsRootNode()
|
||||||
@@ -186,13 +161,9 @@ bool MacroConditionEdit::IsRootNode()
|
|||||||
|
|
||||||
void MacroConditionEdit::SetLogicSelection()
|
void MacroConditionEdit::SetLogicSelection()
|
||||||
{
|
{
|
||||||
auto logic = (*_entryData)->GetLogicType();
|
const auto logic = (*_entryData)->GetLogicType();
|
||||||
if (IsRootNode()) {
|
_logicSelection->setCurrentIndex(
|
||||||
_logicSelection->setCurrentIndex(static_cast<int>(logic));
|
_logicSelection->findData(static_cast<int>(logic)));
|
||||||
} else {
|
|
||||||
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
|
|
||||||
logic_root_offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionEdit::SetRootNode(bool root)
|
void MacroConditionEdit::SetRootNode(bool root)
|
||||||
@@ -200,7 +171,7 @@ void MacroConditionEdit::SetRootNode(bool root)
|
|||||||
_isRoot = root;
|
_isRoot = root;
|
||||||
const QSignalBlocker blocker(_logicSelection);
|
const QSignalBlocker blocker(_logicSelection);
|
||||||
_logicSelection->clear();
|
_logicSelection->clear();
|
||||||
populateLogicSelection(_logicSelection, root);
|
Logic::PopulateLogicTypeSelection(_logicSelection, root);
|
||||||
SetLogicSelection();
|
SetLogicSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,17 +272,17 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string id;
|
std::string id;
|
||||||
LogicType logic;
|
Logic::Type logic;
|
||||||
if (idx >= 1) {
|
if (idx >= 1) {
|
||||||
id = macro->Conditions().at(idx - 1)->GetId();
|
id = macro->Conditions().at(idx - 1)->GetId();
|
||||||
if (idx == 1) {
|
if (idx == 1) {
|
||||||
logic = LogicType::OR;
|
logic = Logic::Type::OR;
|
||||||
} else {
|
} else {
|
||||||
logic = macro->Conditions().at(idx - 1)->GetLogicType();
|
logic = macro->Conditions().at(idx - 1)->GetLogicType();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
id = MacroCondition::GetDefaultID();
|
id = MacroCondition::GetDefaultID();
|
||||||
logic = LogicType::ROOT_NONE;
|
logic = Logic::Type::ROOT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease data;
|
OBSDataAutoRelease data;
|
||||||
@@ -324,7 +295,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
|||||||
|
|
||||||
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
|
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
|
||||||
const std::string &id,
|
const std::string &id,
|
||||||
obs_data_t *data, LogicType logic)
|
obs_data_t *data, Logic::Type logic)
|
||||||
{
|
{
|
||||||
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
||||||
assert(false);
|
assert(false);
|
||||||
@@ -389,7 +360,7 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
|||||||
macro->UpdateConditionIndices();
|
macro->UpdateConditionIndices();
|
||||||
if (idx == 0 && macro->Conditions().size() > 0) {
|
if (idx == 0 && macro->Conditions().size() > 0) {
|
||||||
auto newRoot = macro->Conditions().at(0);
|
auto newRoot = macro->Conditions().at(0);
|
||||||
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
newRoot->SetLogicType(Logic::Type::ROOT_NONE);
|
||||||
static_cast<MacroConditionEdit *>(
|
static_cast<MacroConditionEdit *>(
|
||||||
ui->conditionsList->WidgetAt(0))
|
ui->conditionsList->WidgetAt(0))
|
||||||
->SetRootNode(true);
|
->SetRootNode(true);
|
||||||
@@ -540,22 +511,23 @@ void AdvSceneSwitcher::MacroConditionReorder(int to, int from)
|
|||||||
auto lock = LockContext();
|
auto lock = LockContext();
|
||||||
auto condition = macro->Conditions().at(from);
|
auto condition = macro->Conditions().at(from);
|
||||||
if (to == 0) {
|
if (to == 0) {
|
||||||
condition->SetLogicType(LogicType::ROOT_NONE);
|
condition->SetLogicType(Logic::Type::ROOT_NONE);
|
||||||
static_cast<MacroConditionEdit *>(
|
static_cast<MacroConditionEdit *>(
|
||||||
ui->conditionsList->WidgetAt(from))
|
ui->conditionsList->WidgetAt(from))
|
||||||
->SetRootNode(true);
|
->SetRootNode(true);
|
||||||
macro->Conditions().at(0)->SetLogicType(LogicType::AND);
|
macro->Conditions().at(0)->SetLogicType(
|
||||||
|
Logic::Type::AND);
|
||||||
static_cast<MacroConditionEdit *>(
|
static_cast<MacroConditionEdit *>(
|
||||||
ui->conditionsList->WidgetAt(0))
|
ui->conditionsList->WidgetAt(0))
|
||||||
->SetRootNode(false);
|
->SetRootNode(false);
|
||||||
}
|
}
|
||||||
if (from == 0) {
|
if (from == 0) {
|
||||||
condition->SetLogicType(LogicType::AND);
|
condition->SetLogicType(Logic::Type::AND);
|
||||||
static_cast<MacroConditionEdit *>(
|
static_cast<MacroConditionEdit *>(
|
||||||
ui->conditionsList->WidgetAt(from))
|
ui->conditionsList->WidgetAt(from))
|
||||||
->SetRootNode(false);
|
->SetRootNode(false);
|
||||||
macro->Conditions().at(1)->SetLogicType(
|
macro->Conditions().at(1)->SetLogicType(
|
||||||
LogicType::ROOT_NONE);
|
Logic::Type::ROOT_NONE);
|
||||||
static_cast<MacroConditionEdit *>(
|
static_cast<MacroConditionEdit *>(
|
||||||
ui->conditionsList->WidgetAt(1))
|
ui->conditionsList->WidgetAt(1))
|
||||||
->SetRootNode(true);
|
->SetRootNode(true);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "macro-condition.hpp"
|
#include "macro-condition.hpp"
|
||||||
#include "macro-condition-factory.hpp"
|
#include "macro-condition-factory.hpp"
|
||||||
#include "filter-combo-box.hpp"
|
#include "filter-combo-box.hpp"
|
||||||
|
#include "duration-control.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,24 @@
|
|||||||
#include "macro-condition-factory.hpp"
|
#include "macro-condition-factory.hpp"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class MacroConditionUnknown : public MacroCondition {
|
||||||
|
public:
|
||||||
|
MacroConditionUnknown(Macro *m) : MacroCondition(m) {}
|
||||||
|
bool CheckCondition() { return false; }
|
||||||
|
bool Save(obs_data_t *obj) const { return MacroCondition::Save(obj); };
|
||||||
|
bool Load(obs_data_t *obj) { return MacroCondition::Load(obj); };
|
||||||
|
std::string GetId() const { return "unknown"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static std::recursive_mutex mutex;
|
||||||
|
|
||||||
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
|
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
|
||||||
{
|
{
|
||||||
static std::map<std::string, MacroConditionInfo> _methods;
|
static std::map<std::string, MacroConditionInfo> _methods;
|
||||||
@@ -11,6 +28,7 @@ std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
|
|||||||
bool MacroConditionFactory::Register(const std::string &id,
|
bool MacroConditionFactory::Register(const std::string &id,
|
||||||
MacroConditionInfo info)
|
MacroConditionInfo info)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
||||||
GetMap()[id] = info;
|
GetMap()[id] = info;
|
||||||
return true;
|
return true;
|
||||||
@@ -18,35 +36,60 @@ bool MacroConditionFactory::Register(const std::string &id,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MacroConditionFactory::Deregister(const std::string &id)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
|
if (GetMap().count(id) == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GetMap().erase(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<MacroCondition> createUnknownCondition(Macro *m)
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroConditionUnknown>(m);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<MacroCondition>
|
std::shared_ptr<MacroCondition>
|
||||||
MacroConditionFactory::Create(const std::string &id, Macro *m)
|
MacroConditionFactory::Create(const std::string &id, Macro *m)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._create(m);
|
return it->second._create(m);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return createUnknownCondition(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QWidget *createUnknownConditionWidget()
|
||||||
|
{
|
||||||
|
return new QLabel(
|
||||||
|
obs_module_text("AdvSceneSwitcher.condition.unknown"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *
|
QWidget *
|
||||||
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
|
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
|
||||||
std::shared_ptr<MacroCondition> cond)
|
std::shared_ptr<MacroCondition> cond)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._createWidget(parent, cond);
|
return it->second._createWidget(parent, cond);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return createUnknownConditionWidget();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MacroConditionFactory::GetConditionName(const std::string &id)
|
std::string MacroConditionFactory::GetConditionName(const std::string &id)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._name;
|
return it->second._name;
|
||||||
}
|
}
|
||||||
return "unknown condition";
|
return obs_module_text("AdvSceneSwitcher.condition.unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MacroConditionFactory::GetIdByName(const QString &name)
|
std::string MacroConditionFactory::GetIdByName(const QString &name)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
for (auto it : GetMap()) {
|
for (auto it : GetMap()) {
|
||||||
if (name == obs_module_text(it.second._name.c_str())) {
|
if (name == obs_module_text(it.second._name.c_str())) {
|
||||||
return it.first;
|
return it.first;
|
||||||
@@ -57,10 +100,21 @@ std::string MacroConditionFactory::GetIdByName(const QString &name)
|
|||||||
|
|
||||||
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
|
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._useDurationModifier;
|
return it->second._useDurationModifier;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CanCreateDefaultCondition()
|
||||||
|
{
|
||||||
|
const auto condition = MacroConditionFactory::Create(
|
||||||
|
MacroCondition::GetDefaultID().data(), nullptr);
|
||||||
|
if (!condition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return condition->GetId() == MacroCondition::GetDefaultID().data();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -6,10 +6,11 @@
|
|||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
struct MacroConditionInfo {
|
struct MacroConditionInfo {
|
||||||
using CreateCondition = std::shared_ptr<MacroCondition> (*)(Macro *m);
|
|
||||||
using CreateConditionWidget =
|
using CreateConditionWidget =
|
||||||
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
|
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
|
||||||
CreateCondition _create = nullptr;
|
|
||||||
|
std::function<std::shared_ptr<MacroCondition>(Macro *m)> _create =
|
||||||
|
nullptr;
|
||||||
CreateConditionWidget _createWidget = nullptr;
|
CreateConditionWidget _createWidget = nullptr;
|
||||||
std::string _name;
|
std::string _name;
|
||||||
bool _useDurationModifier = true;
|
bool _useDurationModifier = true;
|
||||||
@@ -19,6 +20,7 @@ class MacroConditionFactory {
|
|||||||
public:
|
public:
|
||||||
MacroConditionFactory() = delete;
|
MacroConditionFactory() = delete;
|
||||||
EXPORT static bool Register(const std::string &, MacroConditionInfo);
|
EXPORT static bool Register(const std::string &, MacroConditionInfo);
|
||||||
|
static bool Deregister(const std::string &);
|
||||||
static std::shared_ptr<MacroCondition> Create(const std::string &,
|
static std::shared_ptr<MacroCondition> Create(const std::string &,
|
||||||
Macro *m);
|
Macro *m);
|
||||||
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
|
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
|
||||||
@@ -32,4 +34,6 @@ private:
|
|||||||
static std::map<std::string, MacroConditionInfo> &GetMap();
|
static std::map<std::string, MacroConditionInfo> &GetMap();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool CanCreateDefaultCondition();
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
79
lib/macro/macro-condition-script.cpp
Normal file
79
lib/macro/macro-condition-script.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#include "macro-condition-script.hpp"
|
||||||
|
#include "layout-helpers.hpp"
|
||||||
|
#include "macro-helpers.hpp"
|
||||||
|
#include "sync-helpers.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
MacroConditionScript::MacroConditionScript(
|
||||||
|
Macro *m, const std::string &id, const OBSData &defaultSettings,
|
||||||
|
const std::string &propertiesSignalName,
|
||||||
|
const std::string &triggerSignal, const std::string &completionSignal)
|
||||||
|
: MacroCondition(m),
|
||||||
|
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||||
|
triggerSignal, completionSignal),
|
||||||
|
_id(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroConditionScript::MacroConditionScript(
|
||||||
|
const advss::MacroConditionScript &other)
|
||||||
|
: MacroCondition(other.GetMacro()),
|
||||||
|
MacroSegmentScript(other),
|
||||||
|
_id(other._id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionScript::CheckCondition()
|
||||||
|
{
|
||||||
|
if (!ScriptHandler::ConditionIdIsValid(_id)) {
|
||||||
|
blog(LOG_WARNING, "skipping unknown script condition \"%s\"",
|
||||||
|
_id.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SendTriggerSignal();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionScript::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
MacroCondition::Save(obj);
|
||||||
|
MacroSegmentScript::Save(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionScript::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
MacroCondition::Load(obj);
|
||||||
|
MacroSegmentScript::Load(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionScript::WaitForCompletion() const
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
start - start);
|
||||||
|
const auto timeoutMs = GetTimeoutSeconds() * 1000.0;
|
||||||
|
|
||||||
|
while (!TriggerIsCompleted()) {
|
||||||
|
if (MacroWaitShouldAbort() || MacroIsStopped(GetMacro())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((double)timePassed.count() > timeoutMs) {
|
||||||
|
blog(LOG_INFO, "script condition timeout (%s)",
|
||||||
|
_id.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(10ms);
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
timePassed =
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
now - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
27
lib/macro/macro-condition-script.hpp
Normal file
27
lib/macro/macro-condition-script.hpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "macro-condition-edit.hpp"
|
||||||
|
#include "macro-script-handler.hpp"
|
||||||
|
#include "macro-segment-script.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class MacroConditionScript : public MacroCondition, public MacroSegmentScript {
|
||||||
|
public:
|
||||||
|
MacroConditionScript(Macro *m, const std::string &id,
|
||||||
|
const OBSData &defaultSettings,
|
||||||
|
const std::string &propertiesSignalName,
|
||||||
|
const std::string &triggerSignal,
|
||||||
|
const std::string &signalComplete);
|
||||||
|
MacroConditionScript(const advss::MacroConditionScript &);
|
||||||
|
bool CheckCondition();
|
||||||
|
bool Save(obs_data_t *obj) const;
|
||||||
|
bool Load(obs_data_t *obj);
|
||||||
|
std::string GetId() const { return _id; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WaitForCompletion() const;
|
||||||
|
|
||||||
|
std::string _id = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -2,79 +2,6 @@
|
|||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
|
|
||||||
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
|
|
||||||
{LogicType::AND, {"AdvSceneSwitcher.logic.and"}},
|
|
||||||
{LogicType::OR, {"AdvSceneSwitcher.logic.or"}},
|
|
||||||
{LogicType::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
|
|
||||||
{LogicType::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
|
|
||||||
{LogicType::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
|
|
||||||
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
|
|
||||||
};
|
|
||||||
|
|
||||||
void DurationModifier::Save(obs_data_t *obj, const char *condName,
|
|
||||||
const char *duration) const
|
|
||||||
{
|
|
||||||
obs_data_set_int(obj, condName, static_cast<int>(_type));
|
|
||||||
_dur.Save(obj, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DurationModifier::Load(obs_data_t *obj, const char *condName,
|
|
||||||
const char *duration)
|
|
||||||
{
|
|
||||||
// For backwards compatibility check if duration value exist without
|
|
||||||
// time constraint condition - if so assume DurationCondition::MORE
|
|
||||||
if (!obs_data_has_user_value(obj, condName) &&
|
|
||||||
obs_data_has_user_value(obj, duration)) {
|
|
||||||
obs_data_set_int(obj, condName, static_cast<int>(Type::MORE));
|
|
||||||
}
|
|
||||||
|
|
||||||
_type = static_cast<Type>(obs_data_get_int(obj, condName));
|
|
||||||
_dur.Load(obj, duration);
|
|
||||||
|
|
||||||
// TODO: remove this fallback
|
|
||||||
if (obs_data_has_user_value(obj, "displayUnit")) {
|
|
||||||
_dur.SetUnit(static_cast<Duration::Unit>(
|
|
||||||
obs_data_get_int(obj, "displayUnit")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DurationModifier::DurationReached()
|
|
||||||
{
|
|
||||||
switch (_type) {
|
|
||||||
case DurationModifier::Type::NONE:
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case DurationModifier::Type::MORE:
|
|
||||||
return _dur.DurationReached();
|
|
||||||
break;
|
|
||||||
case DurationModifier::Type::EQUAL:
|
|
||||||
if (_dur.DurationReached() && !_timeReached) {
|
|
||||||
_timeReached = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DurationModifier::Type::LESS:
|
|
||||||
return !_dur.DurationReached();
|
|
||||||
break;
|
|
||||||
case DurationModifier::Type::WITHIN:
|
|
||||||
if (_dur.IsReset()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !_dur.DurationReached();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DurationModifier::Reset()
|
|
||||||
{
|
|
||||||
_timeReached = false;
|
|
||||||
_dur.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
MacroCondition::MacroCondition(Macro *m, bool supportsVariableValue)
|
MacroCondition::MacroCondition(Macro *m, bool supportsVariableValue)
|
||||||
: MacroSegment(m, supportsVariableValue)
|
: MacroSegment(m, supportsVariableValue)
|
||||||
{
|
{
|
||||||
@@ -84,73 +11,64 @@ bool MacroCondition::Save(obs_data_t *obj) const
|
|||||||
{
|
{
|
||||||
MacroSegment::Save(obj);
|
MacroSegment::Save(obj);
|
||||||
obs_data_set_string(obj, "id", GetId().c_str());
|
obs_data_set_string(obj, "id", GetId().c_str());
|
||||||
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
|
_logic.Save(obj, "logic");
|
||||||
|
_durationModifier.Save(obj);
|
||||||
// To avoid conflicts with conditions which also use the Duration class
|
|
||||||
// save the duration modifier in a separate obj
|
|
||||||
auto durObj = obs_data_create();
|
|
||||||
_duration.Save(durObj);
|
|
||||||
obs_data_set_obj(obj, "durationModifier", durObj);
|
|
||||||
obs_data_release(durObj);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroCondition::Load(obs_data_t *obj)
|
bool MacroCondition::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroSegment::Load(obj);
|
MacroSegment::Load(obj);
|
||||||
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
|
_logic.Load(obj, "logic");
|
||||||
if (obs_data_has_user_value(obj, "durationModifier")) {
|
_durationModifier.Load(obj);
|
||||||
auto durObj = obs_data_get_obj(obj, "durationModifier");
|
|
||||||
_duration.Load(durObj);
|
|
||||||
obs_data_release(durObj);
|
|
||||||
} else {
|
|
||||||
// For backwards compatibility
|
|
||||||
_duration.Load(obj);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroCondition::ValidateLogicSelection(bool isRootCondition,
|
||||||
|
const char *context)
|
||||||
|
{
|
||||||
|
if (_logic.IsValidSelection(isRootCondition)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_logic.IsRootType()) {
|
||||||
|
_logic.SetType(Logic::Type::ROOT_NONE);
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"setting invalid logic selection to 'if' for macro %s",
|
||||||
|
context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logic.SetType(Logic::Type::NONE);
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"setting invalid logic selection to 'ignore' for macro %s",
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
void MacroCondition::ResetDuration()
|
void MacroCondition::ResetDuration()
|
||||||
{
|
{
|
||||||
_duration.Reset();
|
_durationModifier.ResetDuration();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroCondition::CheckDurationModifier(bool &val)
|
bool MacroCondition::CheckDurationModifier(bool conditionValue)
|
||||||
{
|
{
|
||||||
if (_duration.GetType() != DurationModifier::Type::WITHIN && !val) {
|
return _durationModifier.CheckConditionWithDurationModifier(
|
||||||
_duration.Reset();
|
conditionValue);
|
||||||
}
|
}
|
||||||
if (_duration.GetType() == DurationModifier::Type::WITHIN && val) {
|
|
||||||
_duration.Reset();
|
DurationModifier MacroCondition::GetDurationModifier() const
|
||||||
}
|
{
|
||||||
switch (_duration.GetType()) {
|
return _durationModifier;
|
||||||
case DurationModifier::Type::NONE:
|
|
||||||
case DurationModifier::Type::MORE:
|
|
||||||
case DurationModifier::Type::EQUAL:
|
|
||||||
case DurationModifier::Type::LESS:
|
|
||||||
val = val && _duration.DurationReached();
|
|
||||||
return;
|
|
||||||
case DurationModifier::Type::WITHIN:
|
|
||||||
if (val) {
|
|
||||||
_duration.SetTimeRemaining(
|
|
||||||
_duration.GetDuration().Seconds());
|
|
||||||
}
|
|
||||||
val = val || _duration.DurationReached();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroCondition::SetDurationModifier(DurationModifier::Type m)
|
void MacroCondition::SetDurationModifier(DurationModifier::Type m)
|
||||||
{
|
{
|
||||||
_duration.SetModifier(m);
|
_durationModifier.SetModifier(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroCondition::SetDuration(const Duration &duration)
|
void MacroCondition::SetDuration(const Duration &duration)
|
||||||
{
|
{
|
||||||
_duration.SetValue(duration);
|
_durationModifier.SetDuration(duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view MacroCondition::GetDefaultID()
|
std::string_view MacroCondition::GetDefaultID()
|
||||||
|
|||||||
@@ -1,63 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "macro-segment.hpp"
|
#include "macro-segment.hpp"
|
||||||
|
#include "condition-logic.hpp"
|
||||||
|
#include "duration-modifier.hpp"
|
||||||
#include "macro-ref.hpp"
|
#include "macro-ref.hpp"
|
||||||
#include <duration-control.hpp>
|
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
constexpr auto logic_root_offset = 100;
|
|
||||||
|
|
||||||
enum class LogicType {
|
|
||||||
ROOT_NONE = 0,
|
|
||||||
ROOT_NOT,
|
|
||||||
ROOT_LAST,
|
|
||||||
// leave some space for potential expansion
|
|
||||||
NONE = 100,
|
|
||||||
AND,
|
|
||||||
OR,
|
|
||||||
AND_NOT,
|
|
||||||
OR_NOT,
|
|
||||||
LAST,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline bool isRootLogicType(LogicType l)
|
|
||||||
{
|
|
||||||
return static_cast<int>(l) < logic_root_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LogicTypeInfo {
|
|
||||||
std::string _name;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DurationModifier {
|
|
||||||
public:
|
|
||||||
enum class Type {
|
|
||||||
NONE,
|
|
||||||
MORE,
|
|
||||||
EQUAL,
|
|
||||||
LESS,
|
|
||||||
WITHIN, // Condition will remain true for a set a amount of time
|
|
||||||
// regardless of current state
|
|
||||||
};
|
|
||||||
|
|
||||||
void Save(obs_data_t *obj, const char *condName = "time_constraint",
|
|
||||||
const char *duration = "seconds") const;
|
|
||||||
void Load(obs_data_t *obj, const char *condName = "time_constraint",
|
|
||||||
const char *duration = "seconds");
|
|
||||||
void SetModifier(Type cond) { _type = cond; }
|
|
||||||
void SetTimeRemaining(const double &val) { _dur.SetTimeRemaining(val); }
|
|
||||||
void SetValue(const Duration &value) { _dur = value; }
|
|
||||||
Type GetType() { return _type; }
|
|
||||||
Duration GetDuration() { return _dur; }
|
|
||||||
bool DurationReached();
|
|
||||||
void Reset();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Type _type = Type::NONE;
|
|
||||||
Duration _dur;
|
|
||||||
bool _timeReached = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class EXPORT MacroCondition : public MacroSegment {
|
class EXPORT MacroCondition : public MacroSegment {
|
||||||
public:
|
public:
|
||||||
MacroCondition(Macro *m, bool supportsVariableValue = false);
|
MacroCondition(Macro *m, bool supportsVariableValue = false);
|
||||||
@@ -65,20 +13,24 @@ public:
|
|||||||
virtual bool CheckCondition() = 0;
|
virtual bool CheckCondition() = 0;
|
||||||
virtual bool Save(obs_data_t *obj) const = 0;
|
virtual bool Save(obs_data_t *obj) const = 0;
|
||||||
virtual bool Load(obs_data_t *obj) = 0;
|
virtual bool Load(obs_data_t *obj) = 0;
|
||||||
LogicType GetLogicType() { return _logic; }
|
|
||||||
void SetLogicType(LogicType logic) { _logic = logic; }
|
Logic::Type GetLogicType() const { return _logic.GetType(); }
|
||||||
static const std::map<LogicType, LogicTypeInfo> logicTypes;
|
void SetLogicType(const Logic::Type &logic) { _logic.SetType(logic); }
|
||||||
void ResetDuration();
|
|
||||||
void CheckDurationModifier(bool &val);
|
void ValidateLogicSelection(bool isRootCondition, const char *context);
|
||||||
DurationModifier GetDurationModifier() { return _duration; }
|
|
||||||
|
DurationModifier GetDurationModifier() const;
|
||||||
void SetDurationModifier(DurationModifier::Type m);
|
void SetDurationModifier(DurationModifier::Type m);
|
||||||
void SetDuration(const Duration &duration);
|
void SetDuration(const Duration &duration);
|
||||||
|
|
||||||
|
void ResetDuration();
|
||||||
|
bool CheckDurationModifier(bool conditionValue);
|
||||||
|
|
||||||
static std::string_view GetDefaultID();
|
static std::string_view GetDefaultID();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LogicType _logic = LogicType::ROOT_NONE;
|
Logic _logic = Logic(Logic::Type::ROOT_NONE);
|
||||||
DurationModifier _duration;
|
DurationModifier _durationModifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EXPORT MacroRefCondition : virtual public MacroCondition {
|
class EXPORT MacroRefCondition : virtual public MacroCondition {
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent,
|
MacroDock::MacroDock(std::weak_ptr<Macro> m,
|
||||||
const StringVariable &runButtonText,
|
const StringVariable &runButtonText,
|
||||||
const StringVariable &pauseButtonText,
|
const StringVariable &pauseButtonText,
|
||||||
const StringVariable &unpauseButtonText,
|
const StringVariable &unpauseButtonText,
|
||||||
const StringVariable &conditionsTrueText,
|
const StringVariable &conditionsTrueText,
|
||||||
const StringVariable &conditionsFalseText,
|
const StringVariable &conditionsFalseText,
|
||||||
bool enableHighlight)
|
bool enableHighlight)
|
||||||
: OBSDock(parent),
|
: QFrame(),
|
||||||
_runButtonText(runButtonText),
|
_runButtonText(runButtonText),
|
||||||
_pauseButtonText(pauseButtonText),
|
_pauseButtonText(pauseButtonText),
|
||||||
_unpauseButtonText(unpauseButtonText),
|
_unpauseButtonText(unpauseButtonText),
|
||||||
@@ -25,19 +25,16 @@ MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent,
|
|||||||
_statusText(new QLabel(conditionsFalseText.c_str())),
|
_statusText(new QLabel(conditionsFalseText.c_str())),
|
||||||
_macro(m)
|
_macro(m)
|
||||||
{
|
{
|
||||||
|
setFrameShape(QFrame::StyledPanel);
|
||||||
|
setFrameShadow(QFrame::Sunken);
|
||||||
|
|
||||||
auto macro = _macro.lock();
|
auto macro = _macro.lock();
|
||||||
if (macro) {
|
if (macro) {
|
||||||
setWindowTitle(QString::fromStdString(macro->Name()));
|
|
||||||
_run->setVisible(macro->DockHasRunButton());
|
_run->setVisible(macro->DockHasRunButton());
|
||||||
_pauseToggle->setVisible(macro->DockHasPauseButton());
|
_pauseToggle->setVisible(macro->DockHasPauseButton());
|
||||||
_statusText->setVisible(macro->DockHasStatusLabel());
|
_statusText->setVisible(macro->DockHasStatusLabel());
|
||||||
} else {
|
|
||||||
setWindowTitle("<deleted macro>");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setFeatures(DockWidgetClosable | DockWidgetMovable |
|
|
||||||
DockWidgetFloatable);
|
|
||||||
|
|
||||||
QWidget::connect(_run, SIGNAL(clicked()), this, SLOT(RunClicked()));
|
QWidget::connect(_run, SIGNAL(clicked()), this, SLOT(RunClicked()));
|
||||||
QWidget::connect(_pauseToggle, SIGNAL(clicked()), this,
|
QWidget::connect(_pauseToggle, SIGNAL(clicked()), this,
|
||||||
SLOT(PauseToggleClicked()));
|
SLOT(PauseToggleClicked()));
|
||||||
@@ -52,21 +49,7 @@ MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent,
|
|||||||
QWidget::connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight()));
|
QWidget::connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight()));
|
||||||
_timer.start(500);
|
_timer.start(500);
|
||||||
|
|
||||||
// QFrame wrapper is necessary to avoid dock being partially
|
setLayout(layout);
|
||||||
// transparent
|
|
||||||
QFrame *wrapper = new QFrame;
|
|
||||||
wrapper->setFrameShape(QFrame::StyledPanel);
|
|
||||||
wrapper->setFrameShadow(QFrame::Sunken);
|
|
||||||
wrapper->setLayout(layout);
|
|
||||||
setWidget(wrapper);
|
|
||||||
|
|
||||||
setFloating(true);
|
|
||||||
hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroDock::SetName(const QString &name)
|
|
||||||
{
|
|
||||||
setWindowTitle(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroDock::ShowRunButton(bool value)
|
void MacroDock::ShowRunButton(bool value)
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "obs-dock.hpp"
|
|
||||||
#include "variable-string.hpp"
|
#include "variable-string.hpp"
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QLabel>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
@@ -12,18 +11,16 @@ namespace advss {
|
|||||||
|
|
||||||
class Macro;
|
class Macro;
|
||||||
|
|
||||||
class MacroDock : public OBSDock {
|
class MacroDock : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MacroDock(std::weak_ptr<Macro>, QWidget *parent,
|
MacroDock(std::weak_ptr<Macro>, const StringVariable &runButtonText,
|
||||||
const StringVariable &runButtonText,
|
|
||||||
const StringVariable &pauseButtonText,
|
const StringVariable &pauseButtonText,
|
||||||
const StringVariable &unpauseButtonText,
|
const StringVariable &unpauseButtonText,
|
||||||
const StringVariable &conditionsTrueText,
|
const StringVariable &conditionsTrueText,
|
||||||
const StringVariable &conditionsFalseText,
|
const StringVariable &conditionsFalseText,
|
||||||
bool enableHighlight);
|
bool enableHighlight);
|
||||||
void SetName(const QString &);
|
|
||||||
void ShowRunButton(bool);
|
void ShowRunButton(bool);
|
||||||
void SetRunButtonText(const StringVariable &);
|
void SetRunButtonText(const StringVariable &);
|
||||||
void ShowPauseButton(bool);
|
void ShowPauseButton(bool);
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void MacroInputSelection::SetInputs(const MacroInputVariables &inputs)
|
|||||||
void MacroInputSelection::Add()
|
void MacroInputSelection::Add()
|
||||||
{
|
{
|
||||||
std::string varName;
|
std::string varName;
|
||||||
bool accepted = VariableSelectionDialog::AskForVariable(this, varName);
|
bool accepted = VariableSelectionDialog::AskForVariable(varName);
|
||||||
|
|
||||||
if (!accepted || varName.empty()) {
|
if (!accepted || varName.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -155,7 +155,7 @@ void MacroInputSelection::Down()
|
|||||||
void MacroInputSelection::Clicked(QListWidgetItem *item)
|
void MacroInputSelection::Clicked(QListWidgetItem *item)
|
||||||
{
|
{
|
||||||
std::string varName;
|
std::string varName;
|
||||||
bool accepted = VariableSelectionDialog::AskForVariable(this, varName);
|
bool accepted = VariableSelectionDialog::AskForVariable(varName);
|
||||||
|
|
||||||
if (!accepted || varName.empty()) {
|
if (!accepted || varName.empty()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ void MacroList::MacroRemove(const QString &name)
|
|||||||
void MacroList::Add()
|
void MacroList::Add()
|
||||||
{
|
{
|
||||||
std::string macroName;
|
std::string macroName;
|
||||||
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName);
|
bool accepted = MacroSelectionDialog::AskForMacro(macroName);
|
||||||
|
|
||||||
if (!accepted || macroName.empty()) {
|
if (!accepted || macroName.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -132,7 +132,7 @@ void MacroList::Down()
|
|||||||
void MacroList::Clicked(QListWidgetItem *item)
|
void MacroList::Clicked(QListWidgetItem *item)
|
||||||
{
|
{
|
||||||
std::string macroName;
|
std::string macroName;
|
||||||
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName);
|
bool accepted = MacroSelectionDialog::AskForMacro(macroName);
|
||||||
|
|
||||||
if (!accepted || macroName.empty()) {
|
if (!accepted || macroName.empty()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
447
lib/macro/macro-script-handler.cpp
Normal file
447
lib/macro/macro-script-handler.cpp
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
#include "macro-script-handler.hpp"
|
||||||
|
#include "macro-action-script.hpp"
|
||||||
|
#include "macro-condition-script.hpp"
|
||||||
|
#include "plugin-state-helpers.hpp"
|
||||||
|
#include "log-helper.hpp"
|
||||||
|
#include "variable.hpp"
|
||||||
|
|
||||||
|
#include <obs-properties.h>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
std::mutex ScriptHandler::_mutex = {};
|
||||||
|
std::unordered_map<std::string, ScriptSegmentType> ScriptHandler::_actions = {};
|
||||||
|
std::unordered_map<std::string, ScriptSegmentType> ScriptHandler::_conditions =
|
||||||
|
{};
|
||||||
|
|
||||||
|
/* Procedure handler helpers */
|
||||||
|
|
||||||
|
#define RETURN_STATUS(status) \
|
||||||
|
{ \
|
||||||
|
calldata_set_bool(data, "success", status); \
|
||||||
|
return; \
|
||||||
|
}
|
||||||
|
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
||||||
|
#define RETURN_FAILURE() RETURN_STATUS(false);
|
||||||
|
|
||||||
|
static constexpr std::string_view nameParam = "name";
|
||||||
|
static constexpr std::string_view defaultSettingsParam = "default_settings";
|
||||||
|
static constexpr std::string_view propertiesSignalParam =
|
||||||
|
"properties_signal_name";
|
||||||
|
static constexpr std::string_view triggerSignalParam = "trigger_signal_name";
|
||||||
|
|
||||||
|
static std::string getRegisterScriptSegmentDeclString(const char *funcName)
|
||||||
|
{
|
||||||
|
return std::string("bool ") + funcName + "(in string " +
|
||||||
|
nameParam.data() + ", in ptr " + defaultSettingsParam.data() +
|
||||||
|
", out string " + propertiesSignalParam.data() +
|
||||||
|
", out string " + triggerSignalParam.data() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getDeregisterScriptSegmentDeclString(const char *funcName)
|
||||||
|
{
|
||||||
|
return std::string("bool ") + funcName + "(in string " +
|
||||||
|
nameParam.data() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Script actions */
|
||||||
|
|
||||||
|
static constexpr std::string_view registerActionFuncName =
|
||||||
|
"advss_register_script_action";
|
||||||
|
static constexpr std::string_view deregisterActionFuncName =
|
||||||
|
"advss_deregister_script_action";
|
||||||
|
|
||||||
|
static const std::string registerScriptActionDeclString =
|
||||||
|
getRegisterScriptSegmentDeclString(registerActionFuncName.data());
|
||||||
|
static const std::string deregisterScriptActionDeclString =
|
||||||
|
getDeregisterScriptSegmentDeclString(deregisterActionFuncName.data());
|
||||||
|
|
||||||
|
/* Script conditions */
|
||||||
|
|
||||||
|
static constexpr std::string_view registerConditionFuncName =
|
||||||
|
"advss_register_script_condition";
|
||||||
|
static constexpr std::string_view deregisterConditionFuncName =
|
||||||
|
"advss_deregister_script_condition";
|
||||||
|
|
||||||
|
static const std::string registerScriptConditionDeclString =
|
||||||
|
getRegisterScriptSegmentDeclString(registerConditionFuncName.data());
|
||||||
|
static const std::string deregisterScriptConditionDeclString =
|
||||||
|
getDeregisterScriptSegmentDeclString(
|
||||||
|
deregisterConditionFuncName.data());
|
||||||
|
|
||||||
|
/* Script variables */
|
||||||
|
|
||||||
|
static constexpr std::string_view valueParam = "value";
|
||||||
|
static constexpr std::string_view getVariableValueFuncName =
|
||||||
|
"advss_get_variable_value";
|
||||||
|
static constexpr std::string_view setVariableValueFuncName =
|
||||||
|
"advss_set_variable_value";
|
||||||
|
static const std::string getVariableValueDeclString =
|
||||||
|
std::string("bool ") + getVariableValueFuncName.data() + "(in string " +
|
||||||
|
nameParam.data() + ", out string " + valueParam.data() + ")";
|
||||||
|
static const std::string setVariableValueDeclString =
|
||||||
|
std::string("bool ") + setVariableValueFuncName.data() + "(in string " +
|
||||||
|
nameParam.data() + ", in string " + valueParam.data() + ")";
|
||||||
|
|
||||||
|
static bool setup();
|
||||||
|
static bool setupDone = setup();
|
||||||
|
|
||||||
|
static bool setup()
|
||||||
|
{
|
||||||
|
proc_handler_t *ph = obs_get_proc_handler();
|
||||||
|
assert(ph != NULL);
|
||||||
|
|
||||||
|
proc_handler_add(ph, registerScriptActionDeclString.c_str(),
|
||||||
|
&ScriptHandler::RegisterScriptAction, nullptr);
|
||||||
|
proc_handler_add(ph, deregisterScriptActionDeclString.c_str(),
|
||||||
|
&ScriptHandler::DeregisterScriptAction, nullptr);
|
||||||
|
proc_handler_add(ph, registerScriptConditionDeclString.c_str(),
|
||||||
|
&ScriptHandler::RegisterScriptCondition, nullptr);
|
||||||
|
proc_handler_add(ph, deregisterScriptConditionDeclString.c_str(),
|
||||||
|
&ScriptHandler::DeregisterScriptCondition, nullptr);
|
||||||
|
proc_handler_add(ph, getVariableValueDeclString.c_str(),
|
||||||
|
&ScriptHandler::GetVariableValue, nullptr);
|
||||||
|
proc_handler_add(ph, setVariableValueDeclString.c_str(),
|
||||||
|
&ScriptHandler::SetVariableValue, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void replaceWhitespace(std::string &string)
|
||||||
|
{
|
||||||
|
std::transform(string.begin(), string.end(), string.begin(), [](char c) {
|
||||||
|
return std::isspace(static_cast<unsigned char>(c)) ? '_' : c;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string nameToScriptID(const std::string &name)
|
||||||
|
{
|
||||||
|
return std::string("script_") + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getTriggerSignal(const std::string &name,
|
||||||
|
const bool isAction)
|
||||||
|
{
|
||||||
|
std::string signal = name;
|
||||||
|
replaceWhitespace(signal);
|
||||||
|
signal += "_run";
|
||||||
|
signal += isAction ? "_action" : "_condition";
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getCompletionSignal(const std::string &name,
|
||||||
|
const bool isAction)
|
||||||
|
{
|
||||||
|
auto signal = getTriggerSignal(name, isAction);
|
||||||
|
signal += "_complete";
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getPropertiesSignal(const std::string &name,
|
||||||
|
const bool isAction)
|
||||||
|
{
|
||||||
|
std::string signal = name;
|
||||||
|
replaceWhitespace(signal);
|
||||||
|
signal += isAction ? "_action" : "_condition";
|
||||||
|
signal += "_get_properties";
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
|
||||||
|
{
|
||||||
|
const char *actionName;
|
||||||
|
if (!calldata_get_string(data, nameParam.data(), &actionName) ||
|
||||||
|
strlen(actionName) == 0) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
registerScriptActionDeclString.data(), nameParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
obs_data_t *defaultSettingsPtr = nullptr;
|
||||||
|
if (!calldata_get_ptr(data, defaultSettingsParam.data(),
|
||||||
|
&defaultSettingsPtr)) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
registerActionFuncName.data(),
|
||||||
|
defaultSettingsParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
OBSData defaultSettings(defaultSettingsPtr);
|
||||||
|
obs_data_release(defaultSettingsPtr);
|
||||||
|
|
||||||
|
if (_actions.count(actionName) > 0) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! Action \"%s\" already exists!",
|
||||||
|
registerActionFuncName.data(), actionName);
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string id = nameToScriptID(actionName);
|
||||||
|
auto triggerSignalName = getTriggerSignal(actionName, true);
|
||||||
|
auto completionSignalName = getCompletionSignal(actionName, true);
|
||||||
|
auto propertiesSignalName = getPropertiesSignal(actionName, true);
|
||||||
|
|
||||||
|
const auto createScriptAction =
|
||||||
|
[id, defaultSettings, propertiesSignalName, triggerSignalName,
|
||||||
|
completionSignalName](
|
||||||
|
Macro *m) -> std::shared_ptr<MacroAction> {
|
||||||
|
return std::make_shared<MacroActionScript>(
|
||||||
|
m, id, defaultSettings, propertiesSignalName,
|
||||||
|
triggerSignalName, completionSignalName);
|
||||||
|
};
|
||||||
|
if (!MacroActionFactory::Register(id, {createScriptAction,
|
||||||
|
MacroSegmentScriptEdit::Create,
|
||||||
|
actionName})) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! Action id \"%s\" already exists!",
|
||||||
|
registerActionFuncName.data(), id.c_str());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
blog(LOG_INFO, "[%s] successful for \"%s\"",
|
||||||
|
registerActionFuncName.data(), actionName);
|
||||||
|
|
||||||
|
calldata_set_string(data, triggerSignalParam.data(),
|
||||||
|
triggerSignalName.c_str());
|
||||||
|
calldata_set_string(data, propertiesSignalParam.data(),
|
||||||
|
propertiesSignalName.c_str());
|
||||||
|
_actions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
|
||||||
|
triggerSignalName,
|
||||||
|
completionSignalName));
|
||||||
|
|
||||||
|
RETURN_SUCCESS();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptHandler::DeregisterScriptAction(void *, calldata_t *data)
|
||||||
|
{
|
||||||
|
const char *actionName;
|
||||||
|
if (!calldata_get_string(data, nameParam.data(), &actionName) ||
|
||||||
|
strlen(actionName) == 0) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
deregisterActionFuncName.data(), nameParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string id = nameToScriptID(actionName);
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
|
if (_actions.count(id) == 0) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! Action \"%s\" was never registered!",
|
||||||
|
deregisterActionFuncName.data(), id.c_str());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MacroActionFactory::Deregister(id)) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! Action id \"%s\" does not exist!",
|
||||||
|
deregisterActionFuncName.data(), id.c_str());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = _actions.find(id);
|
||||||
|
if (it != _actions.end()) {
|
||||||
|
_actions.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_SUCCESS();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
|
||||||
|
{
|
||||||
|
const char *conditionName;
|
||||||
|
if (!calldata_get_string(data, nameParam.data(), &conditionName) ||
|
||||||
|
strlen(conditionName) == 0) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
registerScriptConditionDeclString.data(),
|
||||||
|
nameParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_t *defaultSettingsPtr = nullptr;
|
||||||
|
if (!calldata_get_ptr(data, defaultSettingsParam.data(),
|
||||||
|
&defaultSettingsPtr)) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
registerScriptConditionDeclString.data(),
|
||||||
|
defaultSettingsParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
OBSData defaultSettings(defaultSettingsPtr);
|
||||||
|
obs_data_release(defaultSettingsPtr);
|
||||||
|
|
||||||
|
if (_conditions.count(conditionName) > 0) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! Condition \"%s\" already exists!",
|
||||||
|
registerConditionFuncName.data(), conditionName);
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string id = nameToScriptID(conditionName);
|
||||||
|
auto triggerSignalName = getTriggerSignal(conditionName, false);
|
||||||
|
auto completionSignalName = getCompletionSignal(conditionName, false);
|
||||||
|
auto propertiesSignalName = getPropertiesSignal(conditionName, false);
|
||||||
|
|
||||||
|
const auto createScriptCondition =
|
||||||
|
[id, defaultSettings, propertiesSignalName, triggerSignalName,
|
||||||
|
completionSignalName](
|
||||||
|
Macro *m) -> std::shared_ptr<MacroCondition> {
|
||||||
|
return std::make_shared<MacroConditionScript>(
|
||||||
|
m, id, defaultSettings, propertiesSignalName,
|
||||||
|
triggerSignalName, completionSignalName);
|
||||||
|
};
|
||||||
|
if (!MacroConditionFactory::Register(
|
||||||
|
id, {createScriptCondition, MacroSegmentScriptEdit::Create,
|
||||||
|
conditionName})) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! Condition id \"%s\" already exists!",
|
||||||
|
registerConditionFuncName.data(), id.c_str());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
blog(LOG_INFO, "[%s] successful for \"%s\"",
|
||||||
|
registerConditionFuncName.data(), conditionName);
|
||||||
|
|
||||||
|
calldata_set_string(data, triggerSignalParam.data(),
|
||||||
|
triggerSignalName.c_str());
|
||||||
|
calldata_set_string(data, propertiesSignalParam.data(),
|
||||||
|
propertiesSignalName.c_str());
|
||||||
|
_conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
|
||||||
|
triggerSignalName,
|
||||||
|
completionSignalName));
|
||||||
|
|
||||||
|
RETURN_SUCCESS();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptHandler::DeregisterScriptCondition(void *, calldata_t *data)
|
||||||
|
{
|
||||||
|
const char *conditionName;
|
||||||
|
if (!calldata_get_string(data, nameParam.data(), &conditionName) ||
|
||||||
|
strlen(conditionName) == 0) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
deregisterConditionFuncName.data(), nameParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string id = nameToScriptID(conditionName);
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
|
if (_conditions.count(id) == 0) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! Condition \"%s\" was never registered!",
|
||||||
|
deregisterConditionFuncName.data(), id.c_str());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MacroConditionFactory::Deregister(id)) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! Condition id \"%s\" does not exist!",
|
||||||
|
deregisterConditionFuncName.data(), id.c_str());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = _conditions.find(id);
|
||||||
|
if (it != _conditions.end()) {
|
||||||
|
_conditions.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_SUCCESS();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptHandler::GetVariableValue(void *, calldata_t *data)
|
||||||
|
{
|
||||||
|
const char *variableName;
|
||||||
|
if (!calldata_get_string(data, nameParam.data(), &variableName) ||
|
||||||
|
strlen(variableName) == 0) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
getVariableValueFuncName.data(), nameParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto weakVariable = GetWeakVariableByName(variableName);
|
||||||
|
auto variable = weakVariable.lock();
|
||||||
|
if (!variable) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! \"%s\" variable does not exist!",
|
||||||
|
getVariableValueFuncName.data(), nameParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
calldata_set_string(data, valueParam.data(), variable->Value().c_str());
|
||||||
|
RETURN_SUCCESS();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptHandler::SetVariableValue(void *, calldata_t *data)
|
||||||
|
{
|
||||||
|
const char *variableName;
|
||||||
|
if (!calldata_get_string(data, nameParam.data(), &variableName) ||
|
||||||
|
strlen(variableName) == 0) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
getVariableValueFuncName.data(), nameParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
const char *variableValue;
|
||||||
|
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
|
||||||
|
strlen(variableValue) == 0) {
|
||||||
|
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||||
|
getVariableValueFuncName.data(), valueParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto weakVariable = GetWeakVariableByName(variableName);
|
||||||
|
auto variable = weakVariable.lock();
|
||||||
|
if (!variable) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"[%s] failed! \"%s\" variable does not exist!",
|
||||||
|
getVariableValueFuncName.data(), nameParam.data());
|
||||||
|
RETURN_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
variable->SetValue(variableValue);
|
||||||
|
RETURN_SUCCESS();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptHandler::ActionIdIsValid(const std::string &id)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
return _actions.count(id) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptHandler::ConditionIdIsValid(const std::string &id)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
return _conditions.count(id) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string signalNameToTriggerSignalDecl(const std::string &name)
|
||||||
|
{
|
||||||
|
return std::string("void ") + name + "()";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string signalNameToPropertiesSignalDecl(const std::string &name)
|
||||||
|
{
|
||||||
|
return std::string("void ") + name + "(in ptr " +
|
||||||
|
GetPropertiesSignalParamName().data() + ")";
|
||||||
|
}
|
||||||
|
static std::string signalNameToCompletionSignalDecl(const std::string &name)
|
||||||
|
{
|
||||||
|
return std::string("bool ") + name + "(in int " +
|
||||||
|
GeCompletionIdParamName().data() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptSegmentType::ScriptSegmentType(const std::string &id,
|
||||||
|
const std::string &propertiesSignal,
|
||||||
|
const std::string &triggerSignal,
|
||||||
|
const std::string &completionSignal)
|
||||||
|
: _id(id)
|
||||||
|
{
|
||||||
|
signal_handler_add(
|
||||||
|
obs_get_signal_handler(),
|
||||||
|
signalNameToPropertiesSignalDecl(propertiesSignal).c_str());
|
||||||
|
signal_handler_add(
|
||||||
|
obs_get_signal_handler(),
|
||||||
|
signalNameToTriggerSignalDecl(triggerSignal).c_str());
|
||||||
|
signal_handler_add(
|
||||||
|
obs_get_signal_handler(),
|
||||||
|
signalNameToCompletionSignalDecl(completionSignal).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
59
lib/macro/macro-script-handler.hpp
Normal file
59
lib/macro/macro-script-handler.hpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class ScriptSegmentType {
|
||||||
|
public:
|
||||||
|
ScriptSegmentType() = delete;
|
||||||
|
ScriptSegmentType(const std::string &id,
|
||||||
|
const std::string &propertiesSignal,
|
||||||
|
const std::string &triggerSignal,
|
||||||
|
const std::string &completionSignal);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptHandler {
|
||||||
|
public:
|
||||||
|
ScriptHandler() = delete;
|
||||||
|
static void RegisterScriptAction(void *ctx, calldata_t *data);
|
||||||
|
static void DeregisterScriptAction(void *ctx, calldata_t *data);
|
||||||
|
static void RegisterScriptCondition(void *ctx, calldata_t *data);
|
||||||
|
static void DeregisterScriptCondition(void *ctx, calldata_t *data);
|
||||||
|
static void GetVariableValue(void *ctx, calldata_t *data);
|
||||||
|
static void SetVariableValue(void *ctx, calldata_t *data);
|
||||||
|
static bool ActionIdIsValid(const std::string &id);
|
||||||
|
static bool ConditionIdIsValid(const std::string &id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::mutex _mutex;
|
||||||
|
static std::unordered_map<std::string, ScriptSegmentType> _actions;
|
||||||
|
static std::unordered_map<std::string, ScriptSegmentType> _conditions;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::string_view GetPropertiesSignalParamName()
|
||||||
|
{
|
||||||
|
return "properties";
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::string_view GetActionCompletionSignalParamName()
|
||||||
|
{
|
||||||
|
return "completion_signal_name";
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::string_view GeCompletionIdParamName()
|
||||||
|
{
|
||||||
|
return "completion_id";
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::string_view GeResultSignalParamName()
|
||||||
|
{
|
||||||
|
return "result";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
#include "macro-segment-copy-paste.hpp"
|
||||||
#include "advanced-scene-switcher.hpp"
|
#include "advanced-scene-switcher.hpp"
|
||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
|
#include "ui-helpers.hpp"
|
||||||
|
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
|
|
||||||
@@ -61,23 +63,35 @@ void AdvSceneSwitcher::PasteMacroSegment()
|
|||||||
const auto condition = std::static_pointer_cast<MacroCondition>(
|
const auto condition = std::static_pointer_cast<MacroCondition>(
|
||||||
copyInfo.segment);
|
copyInfo.segment);
|
||||||
auto logic = condition->GetLogicType();
|
auto logic = condition->GetLogicType();
|
||||||
if (logic > LogicType::ROOT_LAST &&
|
if (logic > Logic::Type::ROOT_LAST &&
|
||||||
macro->Conditions().empty()) {
|
macro->Conditions().empty()) {
|
||||||
logic = LogicType::ROOT_NONE;
|
logic = Logic::Type::ROOT_NONE;
|
||||||
}
|
}
|
||||||
if (logic < LogicType::ROOT_LAST &&
|
if (logic < Logic::Type::ROOT_LAST &&
|
||||||
!macro->Conditions().empty()) {
|
!macro->Conditions().empty()) {
|
||||||
logic = LogicType::OR;
|
logic = Logic::Type::OR;
|
||||||
}
|
}
|
||||||
AddMacroCondition(macro.get(), macro->Conditions().size(),
|
AddMacroCondition(macro.get(), macro->Conditions().size(),
|
||||||
copyInfo.segment->GetId(), data.Get(), logic);
|
copyInfo.segment->GetId(), data.Get(), logic);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MacroSegmentCopyInfo::Type::ACTION:
|
case MacroSegmentCopyInfo::Type::ACTION:
|
||||||
|
if (IsCursorInWidgetArea(ui->macroElseActions)) {
|
||||||
|
AddMacroElseAction(macro.get(),
|
||||||
|
macro->ElseActions().size(),
|
||||||
|
copyInfo.segment->GetId(),
|
||||||
|
data.Get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
AddMacroAction(macro.get(), macro->Actions().size(),
|
AddMacroAction(macro.get(), macro->Actions().size(),
|
||||||
copyInfo.segment->GetId(), data.Get());
|
copyInfo.segment->GetId(), data.Get());
|
||||||
break;
|
break;
|
||||||
case MacroSegmentCopyInfo::Type::ELSE:
|
case MacroSegmentCopyInfo::Type::ELSE:
|
||||||
|
if (IsCursorInWidgetArea(ui->macroActions)) {
|
||||||
|
AddMacroAction(macro.get(), macro->Actions().size(),
|
||||||
|
copyInfo.segment->GetId(), data.Get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
AddMacroElseAction(macro.get(), macro->ElseActions().size(),
|
AddMacroElseAction(macro.get(), macro->ElseActions().size(),
|
||||||
copyInfo.segment->GetId(), data.Get());
|
copyInfo.segment->GetId(), data.Get());
|
||||||
break;
|
break;
|
||||||
@@ -91,6 +105,12 @@ bool MacroSegmentIsInClipboard()
|
|||||||
return copyInfo.type != MacroSegmentCopyInfo::Type::NONE;
|
return copyInfo.type != MacroSegmentCopyInfo::Type::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MacroActionIsInClipboard()
|
||||||
|
{
|
||||||
|
return copyInfo.type == MacroSegmentCopyInfo::Type::ACTION ||
|
||||||
|
copyInfo.type == MacroSegmentCopyInfo::Type::ELSE;
|
||||||
|
}
|
||||||
|
|
||||||
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window)
|
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window)
|
||||||
{
|
{
|
||||||
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window);
|
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ namespace advss {
|
|||||||
class AdvSceneSwitcher;
|
class AdvSceneSwitcher;
|
||||||
|
|
||||||
bool MacroSegmentIsInClipboard();
|
bool MacroSegmentIsInClipboard();
|
||||||
|
bool MacroActionIsInClipboard();
|
||||||
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
|
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
219
lib/macro/macro-segment-script.cpp
Normal file
219
lib/macro/macro-segment-script.cpp
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
#include "macro-segment-script.hpp"
|
||||||
|
#include "layout-helpers.hpp"
|
||||||
|
#include "macro-action.hpp"
|
||||||
|
#include "macro-condition.hpp"
|
||||||
|
#include "macro-helpers.hpp"
|
||||||
|
#include "macro-script-handler.hpp"
|
||||||
|
#include "obs-module-helper.hpp"
|
||||||
|
#include "properties-view.hpp"
|
||||||
|
#include "sync-helpers.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
static std::atomic_int completionIdCounter = 0;
|
||||||
|
|
||||||
|
MacroSegmentScript::MacroSegmentScript(obs_data_t *defaultSettings,
|
||||||
|
const std::string &propertiesSignalName,
|
||||||
|
const std::string &triggerSignal,
|
||||||
|
const std::string &completionSignal)
|
||||||
|
: _settings(obs_data_get_defaults(defaultSettings)),
|
||||||
|
_propertiesSignal(propertiesSignalName),
|
||||||
|
_triggerSignal(triggerSignal),
|
||||||
|
_completionSignal(completionSignal)
|
||||||
|
{
|
||||||
|
signal_handler_connect(obs_get_signal_handler(),
|
||||||
|
completionSignal.c_str(),
|
||||||
|
&MacroSegmentScript::CompletionSignalReceived,
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroSegmentScript::MacroSegmentScript(const MacroSegmentScript &other)
|
||||||
|
: _settings(obs_data_create()),
|
||||||
|
_propertiesSignal(other._propertiesSignal),
|
||||||
|
_triggerSignal(other._triggerSignal),
|
||||||
|
_completionSignal(other._completionSignal)
|
||||||
|
{
|
||||||
|
signal_handler_connect(obs_get_signal_handler(),
|
||||||
|
_completionSignal.c_str(),
|
||||||
|
&MacroSegmentScript::CompletionSignalReceived,
|
||||||
|
this);
|
||||||
|
obs_data_apply(_settings.Get(), other._settings.Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroSegmentScript::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
obs_data_set_obj(obj, "settings", _settings.Get());
|
||||||
|
_timeout.Save(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroSegmentScript::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
OBSDataAutoRelease settings = obs_data_get_obj(obj, "settings");
|
||||||
|
obs_data_apply(_settings.Get(), settings);
|
||||||
|
_timeout.Load(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_properties_t *MacroSegmentScript::GetProperties() const
|
||||||
|
{
|
||||||
|
auto data = calldata_create();
|
||||||
|
signal_handler_signal(obs_get_signal_handler(),
|
||||||
|
_propertiesSignal.c_str(), data);
|
||||||
|
obs_properties_t *properties = nullptr;
|
||||||
|
if (!calldata_get_ptr(data, GetPropertiesSignalParamName().data(),
|
||||||
|
&properties)) {
|
||||||
|
calldata_destroy(data);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
calldata_destroy(data);
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScript::UpdateSettings(obs_data_t *newSettings) const
|
||||||
|
{
|
||||||
|
obs_data_clear(_settings.Get());
|
||||||
|
obs_data_apply(_settings.Get(), newSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroSegmentScript::SendTriggerSignal()
|
||||||
|
{
|
||||||
|
_completionId = ++completionIdCounter;
|
||||||
|
_triggerIsComplete = false;
|
||||||
|
_triggerResult = false;
|
||||||
|
|
||||||
|
auto data = calldata_create();
|
||||||
|
calldata_set_string(data, GetActionCompletionSignalParamName().data(),
|
||||||
|
_completionSignal.c_str());
|
||||||
|
calldata_set_int(data, GeCompletionIdParamName().data(), _completionId);
|
||||||
|
calldata_set_string(data, "settings", obs_data_get_json(GetSettings()));
|
||||||
|
signal_handler_signal(obs_get_signal_handler(), _triggerSignal.c_str(),
|
||||||
|
data);
|
||||||
|
calldata_destroy(data);
|
||||||
|
|
||||||
|
SetMacroAbortWait(false);
|
||||||
|
WaitForCompletion();
|
||||||
|
|
||||||
|
return _triggerResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
|
||||||
|
{
|
||||||
|
auto segment = static_cast<MacroSegmentScript *>(param);
|
||||||
|
long long int id;
|
||||||
|
if (!calldata_get_int(data, GeCompletionIdParamName().data(), &id)) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"received completion signal without \"%s\" parameter",
|
||||||
|
GeCompletionIdParamName().data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool result;
|
||||||
|
if (!calldata_get_bool(data, GeResultSignalParamName().data(),
|
||||||
|
&result)) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"received completion signal without \"%s\" parameter",
|
||||||
|
GeResultSignalParamName().data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id != segment->_completionId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
segment->_triggerIsComplete = true;
|
||||||
|
segment->_triggerResult = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_properties_t *MacroSegmentScriptEdit::GetProperties(void *obj)
|
||||||
|
{
|
||||||
|
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
|
||||||
|
if (!segmentEdit) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return segmentEdit->_entryData->GetProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptEdit::UpdateSettings(void *obj, obs_data_t *settings)
|
||||||
|
{
|
||||||
|
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
|
||||||
|
if (!segmentEdit || segmentEdit->_loading || !segmentEdit->_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto lock = LockContext();
|
||||||
|
segmentEdit->_entryData->UpdateSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroSegmentScriptEdit::MacroSegmentScriptEdit(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroSegmentScript> entryData)
|
||||||
|
: QWidget(parent),
|
||||||
|
_timeout(new DurationSelection(this))
|
||||||
|
{
|
||||||
|
QWidget::connect(_timeout, &DurationSelection::DurationChanged, this,
|
||||||
|
&MacroSegmentScriptEdit::TimeoutChanged);
|
||||||
|
|
||||||
|
auto timeoutLayout = new QHBoxLayout();
|
||||||
|
PlaceWidgets(obs_module_text("AdvSceneSwitcher.script.timeout"),
|
||||||
|
timeoutLayout, {{"{{timeout}}", _timeout}});
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout();
|
||||||
|
|
||||||
|
auto properties = entryData->GetProperties();
|
||||||
|
if (!!properties) {
|
||||||
|
obs_properties_destroy(properties);
|
||||||
|
|
||||||
|
// We need a separate OBSData object here as we can't risk
|
||||||
|
// entryData->_settings being modified while it is currently used
|
||||||
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
|
obs_data_apply(data, entryData->GetSettings());
|
||||||
|
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(30, 0, 0)
|
||||||
|
auto propertiesView =
|
||||||
|
new OBSPropertiesView(data.Get(), this, GetProperties,
|
||||||
|
nullptr, UpdateSettings);
|
||||||
|
layout->addWidget(propertiesView);
|
||||||
|
connect(propertiesView, &OBSPropertiesView::PropertiesResized,
|
||||||
|
this, [this]() {
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
});
|
||||||
|
#else
|
||||||
|
layout->addWidget(new QLabel(
|
||||||
|
"Displaying script properties not supported when compiled for OBS 29!"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addLayout(timeoutLayout);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
_entryData = entryData;
|
||||||
|
UpdateEntryData();
|
||||||
|
_loading = false;
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptEdit::UpdateEntryData()
|
||||||
|
{
|
||||||
|
_timeout->SetDuration(_entryData->_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *MacroSegmentScriptEdit::Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroAction> segment)
|
||||||
|
{
|
||||||
|
return new MacroSegmentScriptEdit(
|
||||||
|
parent, std::dynamic_pointer_cast<MacroSegmentScript>(segment));
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *MacroSegmentScriptEdit::Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroCondition> segment)
|
||||||
|
{
|
||||||
|
return new MacroSegmentScriptEdit(
|
||||||
|
parent, std::dynamic_pointer_cast<MacroSegmentScript>(segment));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptEdit::TimeoutChanged(const Duration &timeout)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
78
lib/macro/macro-segment-script.hpp
Normal file
78
lib/macro/macro-segment-script.hpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "duration-control.hpp"
|
||||||
|
#include "macro-script-handler.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <obs-data.h>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class Macro;
|
||||||
|
class MacroAction;
|
||||||
|
class MacroCondition;
|
||||||
|
|
||||||
|
class MacroSegmentScript {
|
||||||
|
public:
|
||||||
|
MacroSegmentScript(obs_data_t *defaultSettings,
|
||||||
|
const std::string &propertiesSignalName,
|
||||||
|
const std::string &triggerSignal,
|
||||||
|
const std::string &completionSignal);
|
||||||
|
MacroSegmentScript(const advss::MacroSegmentScript &);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool Save(obs_data_t *obj) const;
|
||||||
|
bool Load(obs_data_t *obj);
|
||||||
|
|
||||||
|
obs_properties_t *GetProperties() const;
|
||||||
|
OBSData GetSettings() const { return _settings.Get(); }
|
||||||
|
void UpdateSettings(obs_data_t *newSettings) const;
|
||||||
|
|
||||||
|
bool SendTriggerSignal();
|
||||||
|
double GetTimeoutSeconds() const { return _timeout.Seconds(); };
|
||||||
|
bool TriggerIsCompleted() const { return _triggerIsComplete; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void WaitForCompletion() const = 0;
|
||||||
|
static void CompletionSignalReceived(void *param, calldata_t *data);
|
||||||
|
|
||||||
|
OBSDataAutoRelease _settings;
|
||||||
|
std::string _propertiesSignal = "";
|
||||||
|
|
||||||
|
std::string _triggerSignal = "";
|
||||||
|
std::string _completionSignal = "";
|
||||||
|
std::atomic_bool _triggerIsComplete = {false};
|
||||||
|
bool _triggerResult = false;
|
||||||
|
int64_t _completionId = 0;
|
||||||
|
|
||||||
|
Duration _timeout = Duration(10.0);
|
||||||
|
|
||||||
|
friend class MacroSegmentScriptEdit;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroSegmentScriptEdit : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MacroSegmentScriptEdit(
|
||||||
|
QWidget *parent,
|
||||||
|
std::shared_ptr<MacroSegmentScript> entryData = nullptr);
|
||||||
|
void UpdateEntryData();
|
||||||
|
static QWidget *Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroAction> segment);
|
||||||
|
static QWidget *Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroCondition> segment);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void TimeoutChanged(const Duration &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static obs_properties_t *GetProperties(void *obj);
|
||||||
|
static void UpdateSettings(void *obj, obs_data_t *settings);
|
||||||
|
|
||||||
|
DurationSelection *_timeout;
|
||||||
|
|
||||||
|
std::shared_ptr<MacroSegmentScript> _entryData;
|
||||||
|
bool _loading = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -70,7 +70,8 @@ void MacroSegmentSelection::MacroSegmentOrderChanged()
|
|||||||
SetupDescription();
|
SetupDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GetMacroSegmentDescription(Macro *macro, int idx, bool isCondition)
|
static QString GetMacroSegmentDescription(Macro *macro, int idx,
|
||||||
|
bool isCondition)
|
||||||
{
|
{
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void MacroSelection::HideSelectedMacro()
|
|||||||
void MacroSelection::ShowAllMacros()
|
void MacroSelection::ShowAllMacros()
|
||||||
{
|
{
|
||||||
auto v = qobject_cast<QListView *>(view());
|
auto v = qobject_cast<QListView *>(view());
|
||||||
for (int i = count(); i > 0; i--) {
|
for (int i = count() - 1; i >= 0; i--) {
|
||||||
v->setRowHidden(i, false);
|
v->setRowHidden(i, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ MacroSelectionDialog::MacroSelectionDialog(QWidget *parent)
|
|||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroSelectionDialog::AskForMacro(QWidget *parent, std::string ¯oName)
|
bool MacroSelectionDialog::AskForMacro(std::string ¯oName)
|
||||||
{
|
{
|
||||||
MacroSelectionDialog dialog(GetSettingsWindow());
|
MacroSelectionDialog dialog(GetSettingsWindow());
|
||||||
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class MacroSelectionDialog : public QDialog {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
MacroSelectionDialog(QWidget *parent);
|
MacroSelectionDialog(QWidget *parent);
|
||||||
static bool AskForMacro(QWidget *parent, std::string ¯oName);
|
static bool AskForMacro(std::string ¯oName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MacroSelection *_macroSelection;
|
MacroSelection *_macroSelection;
|
||||||
|
|||||||
@@ -100,8 +100,10 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui->macros->Add(newMacro);
|
ui->macros->Add(newMacro);
|
||||||
addPulse->deleteLater();
|
if (addPulse) {
|
||||||
addPulse = nullptr;
|
addPulse->deleteLater();
|
||||||
|
addPulse = nullptr;
|
||||||
|
}
|
||||||
emit MacroAdded(QString::fromStdString(name));
|
emit MacroAdded(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1047,8 +1049,30 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
|
|||||||
menu.exec(globalPos);
|
menu.exec(globalPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleCustomLabelChange(MacroSegmentEdit *segmentEdit,
|
static bool handleCustomLabelRename(MacroSegmentEdit *segmentEdit)
|
||||||
QAction *contextMenuOption)
|
{
|
||||||
|
std::string label;
|
||||||
|
auto segment = segmentEdit->Data();
|
||||||
|
if (!segment) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accepted = NameDialog::AskForName(
|
||||||
|
GetSettingsWindow(),
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
|
||||||
|
"", label, QString::fromStdString(segment->GetCustomLabel()));
|
||||||
|
if (!accepted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
segment->SetCustomLabel(label);
|
||||||
|
segmentEdit->HeaderInfoChanged("");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleCustomLabelEnableChange(MacroSegmentEdit *segmentEdit,
|
||||||
|
QAction *contextMenuOption)
|
||||||
{
|
{
|
||||||
bool enable = contextMenuOption->isChecked();
|
bool enable = contextMenuOption->isChecked();
|
||||||
auto segment = segmentEdit->Data();
|
auto segment = segmentEdit->Data();
|
||||||
@@ -1059,19 +1083,66 @@ static void handleCustomLabelChange(MacroSegmentEdit *segmentEdit,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string label;
|
if (!handleCustomLabelRename(segmentEdit)) {
|
||||||
bool accepted = NameDialog::AskForName(
|
|
||||||
GetSettingsWindow(),
|
|
||||||
obs_module_text(
|
|
||||||
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
|
|
||||||
"", label, QString::fromStdString(segment->GetCustomLabel()));
|
|
||||||
if (!accepted) {
|
|
||||||
segment->SetUseCustomLabel(false);
|
segment->SetUseCustomLabel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setupSegmentLabelContextMenuEntries(MacroSegmentEdit *segmentEdit,
|
||||||
|
QMenu &menu)
|
||||||
|
{
|
||||||
|
if (!segmentEdit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
segment->SetCustomLabel(label);
|
auto segment = segmentEdit ? segmentEdit->Data() : nullptr;
|
||||||
segmentEdit->HeaderInfoChanged("");
|
const bool customLabelIsEnabled = segment &&
|
||||||
|
segment->GetUseCustomLabel();
|
||||||
|
|
||||||
|
auto enableCustomLabel = menu.addAction(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
|
||||||
|
enableCustomLabel->setCheckable(true);
|
||||||
|
enableCustomLabel->setChecked(customLabelIsEnabled);
|
||||||
|
QWidget::connect(enableCustomLabel, &QAction::triggered,
|
||||||
|
[segmentEdit, enableCustomLabel]() {
|
||||||
|
handleCustomLabelEnableChange(
|
||||||
|
segmentEdit, enableCustomLabel);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!customLabelIsEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto customLabelRename = menu.addAction(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.segment.customLabelRename"));
|
||||||
|
QWidget::connect(customLabelRename, &QAction::triggered,
|
||||||
|
[segmentEdit]() {
|
||||||
|
handleCustomLabelRename(segmentEdit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setupCopyPasteContextMenuEnry(AdvSceneSwitcher *ss,
|
||||||
|
MacroSegmentEdit *segmentEdit,
|
||||||
|
QMenu &menu)
|
||||||
|
{
|
||||||
|
auto copy = menu.addAction(
|
||||||
|
obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss,
|
||||||
|
[ss]() { ss->CopyMacroSegment(); });
|
||||||
|
copy->setEnabled(!!segmentEdit);
|
||||||
|
|
||||||
|
const char *pasteText = "AdvSceneSwitcher.macroTab.segment.paste";
|
||||||
|
if (MacroActionIsInClipboard()) {
|
||||||
|
if (IsCursorInWidgetArea(ss->ui->macroActions)) {
|
||||||
|
pasteText =
|
||||||
|
"AdvSceneSwitcher.macroTab.segment.pasteAction";
|
||||||
|
} else if (IsCursorInWidgetArea(ss->ui->macroElseActions)) {
|
||||||
|
pasteText =
|
||||||
|
"AdvSceneSwitcher.macroTab.segment.pasteElseAction";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto paste = menu.addAction(obs_module_text(pasteText), ss,
|
||||||
|
[ss]() { ss->PasteMacroSegment(); });
|
||||||
|
paste->setEnabled(MacroSegmentIsInClipboard());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
||||||
@@ -1084,43 +1155,19 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
|||||||
QMenu menu;
|
QMenu menu;
|
||||||
auto segmentEdit = list->WidgetAt(pos);
|
auto segmentEdit = list->WidgetAt(pos);
|
||||||
|
|
||||||
auto copy = menu.addAction(
|
setupCopyPasteContextMenuEnry(ss, segmentEdit, menu);
|
||||||
obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss,
|
|
||||||
[ss]() { ss->CopyMacroSegment(); });
|
|
||||||
copy->setEnabled(!!segmentEdit);
|
|
||||||
auto paste = menu.addAction(
|
|
||||||
obs_module_text("AdvSceneSwitcher.macroTab.segment.paste"), ss,
|
|
||||||
[ss]() { ss->PasteMacroSegment(); });
|
|
||||||
paste->setEnabled(MacroSegmentIsInClipboard());
|
|
||||||
|
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
|
setupSegmentLabelContextMenuEntries(segmentEdit, menu);
|
||||||
if (segmentEdit) {
|
|
||||||
auto customLabel = menu.addAction(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
|
|
||||||
customLabel->setCheckable(true);
|
|
||||||
auto segment = segmentEdit ? segmentEdit->Data() : nullptr;
|
|
||||||
customLabel->setChecked(segment &&
|
|
||||||
segment->GetUseCustomLabel());
|
|
||||||
QWidget::connect(customLabel, &QAction::triggered,
|
|
||||||
std::bind(handleCustomLabelChange, segmentEdit,
|
|
||||||
customLabel));
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
|
|
||||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
|
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
|
||||||
ss, [ss, expand]() { expand(ss); });
|
ss, [ss, expand]() { expand(ss); });
|
||||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"),
|
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"),
|
||||||
ss, [ss, collapse]() { collapse(ss); });
|
ss, [ss, collapse]() { collapse(ss); });
|
||||||
|
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
|
|
||||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
|
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
|
||||||
ss, [ss, maximize]() { maximize(ss); });
|
ss, [ss, maximize]() { maximize(ss); });
|
||||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
|
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
|
||||||
ss, [ss, minimize]() { minimize(ss); });
|
ss, [ss, minimize]() { minimize(ss); });
|
||||||
|
|
||||||
menu.exec(list->mapToGlobal(pos));
|
menu.exec(list->mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1165,7 +1212,7 @@ void AdvSceneSwitcher::CopyMacro()
|
|||||||
}
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
macro->Save(data);
|
macro->Save(data, true);
|
||||||
newMacro->Load(data);
|
newMacro->Load(data);
|
||||||
newMacro->PostLoad();
|
newMacro->PostLoad();
|
||||||
newMacro->SetName(name);
|
newMacro->SetName(name);
|
||||||
@@ -1173,8 +1220,10 @@ void AdvSceneSwitcher::CopyMacro()
|
|||||||
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
|
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
|
||||||
|
|
||||||
ui->macros->Add(newMacro, macro);
|
ui->macros->Add(newMacro, macro);
|
||||||
addPulse->deleteLater();
|
if (addPulse) {
|
||||||
addPulse = nullptr;
|
addPulse->deleteLater();
|
||||||
|
addPulse = nullptr;
|
||||||
|
}
|
||||||
emit MacroAdded(QString::fromStdString(name));
|
emit MacroAdded(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ void MacroTreeItem::Update(bool force)
|
|||||||
_spacer = nullptr;
|
_spacer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_type == Type::Group) {
|
if (_type == Type::Group && _expand) {
|
||||||
_boxLayout->removeWidget(_expand);
|
_boxLayout->removeWidget(_expand);
|
||||||
_expand->deleteLater();
|
_expand->deleteLater();
|
||||||
_expand = nullptr;
|
_expand = nullptr;
|
||||||
|
|||||||
@@ -14,10 +14,66 @@
|
|||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <util/platform.h>
|
||||||
|
|
||||||
|
#if LIBOBS_API_VER < MAKE_SEMANTIC_VERSION(30, 0, 0)
|
||||||
|
#include <QDockWidget>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct DockMapEntry {
|
||||||
|
QAction *action = nullptr;
|
||||||
|
QWidget *dock = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static std::unordered_map<const char *, DockMapEntry> dockIds;
|
||||||
|
static std::mutex dockMutex;
|
||||||
|
|
||||||
|
static bool obs_frontend_add_dock_by_id(const char *id, const char *title,
|
||||||
|
QWidget *widget)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(dockMutex);
|
||||||
|
if (dockIds.count(id) > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget->setObjectName(id);
|
||||||
|
|
||||||
|
auto dock = new QDockWidget();
|
||||||
|
dock->setWindowTitle(title);
|
||||||
|
dock->setWidget(widget);
|
||||||
|
dock->setFloating(true);
|
||||||
|
dock->setVisible(false);
|
||||||
|
dock->setFeatures(QDockWidget::DockWidgetClosable |
|
||||||
|
QDockWidget::DockWidgetMovable |
|
||||||
|
QDockWidget::DockWidgetFloatable);
|
||||||
|
|
||||||
|
auto action = static_cast<QAction *>(obs_frontend_add_dock(dock));
|
||||||
|
if (!action) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dockIds[id] = {action, dock};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void obs_frontend_remove_dock(const char *id)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(dockMutex);
|
||||||
|
auto it = dockIds.find(id);
|
||||||
|
if (it == dockIds.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second.action->deleteLater();
|
||||||
|
it->second.dock->deleteLater();
|
||||||
|
dockIds.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
static constexpr int perfLogThreshold = 300;
|
|
||||||
static std::deque<std::shared_ptr<Macro>> macros;
|
static std::deque<std::shared_ptr<Macro>> macros;
|
||||||
|
|
||||||
Macro::Macro(const std::string &name, const bool addHotkey)
|
Macro::Macro(const std::string &name, const bool addHotkey)
|
||||||
@@ -101,6 +157,30 @@ void Macro::PrepareMoveToGroup(std::shared_ptr<Macro> group,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
static constexpr auto perfLogThreshold = 300ms;
|
||||||
|
|
||||||
|
const auto startTime = std::chrono::high_resolution_clock::now();
|
||||||
|
const bool conditionMatched = condition->CheckCondition();
|
||||||
|
const auto endTime = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto timeSpent = endTime - startTime;
|
||||||
|
|
||||||
|
if (timeSpent >= perfLogThreshold) {
|
||||||
|
const long int ms =
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
timeSpent)
|
||||||
|
.count();
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"spent %ld ms in %s condition check of macro '%s'!", ms,
|
||||||
|
condition->GetId().c_str(),
|
||||||
|
condition->GetMacro()->Name().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return conditionMatched;
|
||||||
|
}
|
||||||
|
|
||||||
bool Macro::CeckMatch(bool ignorePause)
|
bool Macro::CeckMatch(bool ignorePause)
|
||||||
{
|
{
|
||||||
if (_isGroup) {
|
if (_isGroup) {
|
||||||
@@ -108,77 +188,36 @@ bool Macro::CeckMatch(bool ignorePause)
|
|||||||
}
|
}
|
||||||
|
|
||||||
_matched = false;
|
_matched = false;
|
||||||
for (auto &c : _conditions) {
|
for (auto &condition : _conditions) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto startTime = std::chrono::high_resolution_clock::now();
|
bool conditionMatched = checkCondition(condition);
|
||||||
bool cond = c->CheckCondition();
|
conditionMatched =
|
||||||
auto endTime = std::chrono::high_resolution_clock::now();
|
condition->CheckDurationModifier(conditionMatched);
|
||||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
endTime - startTime);
|
|
||||||
if (ms.count() >= perfLogThreshold) {
|
|
||||||
blog(LOG_WARNING,
|
|
||||||
"spent %ld ms in %s condition check of macro '%s'!",
|
|
||||||
(long int)ms.count(), c->GetId().c_str(),
|
|
||||||
Name().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
c->CheckDurationModifier(cond);
|
const auto logicType = condition->GetLogicType();
|
||||||
|
if (logicType == Logic::Type::NONE) {
|
||||||
switch (c->GetLogicType()) {
|
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
|
||||||
case LogicType::NONE:
|
condition->GetId().c_str(), _name.c_str());
|
||||||
vblog(LOG_INFO,
|
|
||||||
"ignoring condition check 'none' for '%s'",
|
|
||||||
_name.c_str());
|
|
||||||
continue;
|
continue;
|
||||||
case LogicType::AND:
|
|
||||||
_matched = _matched && cond;
|
|
||||||
if (cond) {
|
|
||||||
c->EnableHighlight();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LogicType::OR:
|
|
||||||
_matched = _matched || cond;
|
|
||||||
if (cond) {
|
|
||||||
c->EnableHighlight();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LogicType::AND_NOT:
|
|
||||||
_matched = _matched && !cond;
|
|
||||||
if (!cond) {
|
|
||||||
c->EnableHighlight();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LogicType::OR_NOT:
|
|
||||||
_matched = _matched || !cond;
|
|
||||||
if (!cond) {
|
|
||||||
c->EnableHighlight();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LogicType::ROOT_NONE:
|
|
||||||
_matched = cond;
|
|
||||||
if (cond) {
|
|
||||||
c->EnableHighlight();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LogicType::ROOT_NOT:
|
|
||||||
_matched = !cond;
|
|
||||||
if (!cond) {
|
|
||||||
c->EnableHighlight();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
blog(LOG_WARNING,
|
|
||||||
"ignoring unknown condition check for '%s'",
|
|
||||||
_name.c_str());
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
vblog(LOG_INFO, "condition %s returned %d", c->GetId().c_str(),
|
vblog(LOG_INFO, "condition %s returned %d",
|
||||||
cond);
|
condition->GetId().c_str(), conditionMatched);
|
||||||
|
|
||||||
|
const bool isNegativeLogicType =
|
||||||
|
Logic::IsNegationType(logicType);
|
||||||
|
if ((conditionMatched && !isNegativeLogicType) ||
|
||||||
|
(!conditionMatched && isNegativeLogicType)) {
|
||||||
|
condition->EnableHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
_matched = Logic::ApplyConditionLogic(
|
||||||
|
logicType, _matched, conditionMatched, _name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
||||||
|
|
||||||
_conditionSateChanged = _lastMatched != _matched;
|
_conditionSateChanged = _lastMatched != _matched;
|
||||||
@@ -276,9 +315,15 @@ int64_t Macro::MsSinceLastCheck() const
|
|||||||
|
|
||||||
void Macro::SetName(const std::string &name)
|
void Macro::SetName(const std::string &name)
|
||||||
{
|
{
|
||||||
|
const bool nameChanged = _name == name;
|
||||||
_name = name;
|
_name = name;
|
||||||
|
|
||||||
SetHotkeysDesc();
|
SetHotkeysDesc();
|
||||||
SetDockWidgetName();
|
|
||||||
|
if (nameChanged) {
|
||||||
|
_dockId = GenerateDockId();
|
||||||
|
}
|
||||||
|
EnableDock(_registerDock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Macro::ResetTimers()
|
void Macro::ResetTimers()
|
||||||
@@ -333,11 +378,6 @@ bool Macro::RunElseActions(bool ignorePause)
|
|||||||
return RunActionsHelper(_elseActions, ignorePause);
|
return RunActionsHelper(_elseActions, ignorePause);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Macro::DockIsVisible() const
|
|
||||||
{
|
|
||||||
return _dock && _dockAction && _dock->isVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Macro::WasPausedSince(
|
bool Macro::WasPausedSince(
|
||||||
const std::chrono::high_resolution_clock::time_point &time) const
|
const std::chrono::high_resolution_clock::time_point &time) const
|
||||||
{
|
{
|
||||||
@@ -568,9 +608,11 @@ std::shared_ptr<Macro> Macro::Parent() const
|
|||||||
return _parent.lock();
|
return _parent.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Macro::Save(obs_data_t *obj) const
|
bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
|
||||||
{
|
{
|
||||||
obs_data_set_string(obj, "name", _name.c_str());
|
if (!saveForCopy) {
|
||||||
|
obs_data_set_string(obj, "name", _name.c_str());
|
||||||
|
}
|
||||||
obs_data_set_bool(obj, "pause", _paused);
|
obs_data_set_bool(obj, "pause", _paused);
|
||||||
obs_data_set_bool(obj, "parallel", _runInParallel);
|
obs_data_set_bool(obj, "parallel", _runInParallel);
|
||||||
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
|
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
|
||||||
@@ -586,7 +628,7 @@ bool Macro::Save(obs_data_t *obj) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveDockSettings(obj);
|
SaveDockSettings(obj, saveForCopy);
|
||||||
|
|
||||||
SaveSplitterPos(_actionConditionSplitterPosition, obj,
|
SaveSplitterPos(_actionConditionSplitterPosition, obj,
|
||||||
"macroActionConditionSplitterPosition");
|
"macroActionConditionSplitterPosition");
|
||||||
@@ -631,40 +673,6 @@ bool Macro::Save(obs_data_t *obj) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValidLogic(LogicType t, bool root)
|
|
||||||
{
|
|
||||||
bool isRoot = isRootLogicType(t);
|
|
||||||
if (!isRoot == root) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isRoot) {
|
|
||||||
if (t >= LogicType::ROOT_LAST) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (t >= LogicType::LAST) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setValidLogic(MacroCondition *c, bool root, std::string name)
|
|
||||||
{
|
|
||||||
if (isValidLogic(c->GetLogicType(), root)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (root) {
|
|
||||||
c->SetLogicType(LogicType::ROOT_NONE);
|
|
||||||
blog(LOG_WARNING,
|
|
||||||
"setting invalid logic selection to 'if' for macro %s",
|
|
||||||
name.c_str());
|
|
||||||
} else {
|
|
||||||
c->SetLogicType(LogicType::NONE);
|
|
||||||
blog(LOG_WARNING,
|
|
||||||
"setting invalid logic selection to 'ignore' for macro %s",
|
|
||||||
name.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Macro::Load(obs_data_t *obj)
|
bool Macro::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
_name = obs_data_get_string(obj, "name");
|
_name = obs_data_get_string(obj, "name");
|
||||||
@@ -720,7 +728,7 @@ bool Macro::Load(obs_data_t *obj)
|
|||||||
_conditions.emplace_back(newEntry);
|
_conditions.emplace_back(newEntry);
|
||||||
auto c = _conditions.back().get();
|
auto c = _conditions.back().get();
|
||||||
c->Load(arrayObj);
|
c->Load(arrayObj);
|
||||||
setValidLogic(c, root, _name);
|
c->ValidateLogicSelection(root, Name().c_str());
|
||||||
} else {
|
} else {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING,
|
||||||
"discarding condition entry with unknown id (%s) for macro %s",
|
"discarding condition entry with unknown id (%s) for macro %s",
|
||||||
@@ -865,14 +873,10 @@ bool Macro::PauseHotkeysEnabled() const
|
|||||||
return _registerHotkeys;
|
return _registerHotkeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Macro::SaveDockSettings(obs_data_t *obj) const
|
void Macro::SaveDockSettings(obs_data_t *obj, bool saveForCopy) const
|
||||||
{
|
{
|
||||||
auto dockSettings = obs_data_create();
|
auto dockSettings = obs_data_create();
|
||||||
obs_data_set_bool(dockSettings, "register", _registerDock);
|
obs_data_set_bool(dockSettings, "register", _registerDock);
|
||||||
// The object name is used to restore the position of the dock
|
|
||||||
if (_registerDock) {
|
|
||||||
SetDockWidgetName();
|
|
||||||
}
|
|
||||||
obs_data_set_bool(dockSettings, "hasRunButton", _dockHasRunButton);
|
obs_data_set_bool(dockSettings, "hasRunButton", _dockHasRunButton);
|
||||||
obs_data_set_bool(dockSettings, "hasPauseButton", _dockHasPauseButton);
|
obs_data_set_bool(dockSettings, "hasPauseButton", _dockHasPauseButton);
|
||||||
obs_data_set_bool(dockSettings, "hasStatusLabel", _dockHasStatusLabel);
|
obs_data_set_bool(dockSettings, "hasStatusLabel", _dockHasStatusLabel);
|
||||||
@@ -885,18 +889,14 @@ void Macro::SaveDockSettings(obs_data_t *obj) const
|
|||||||
"conditionsTrueStatusText");
|
"conditionsTrueStatusText");
|
||||||
_conditionsFalseStatusText.Save(dockSettings,
|
_conditionsFalseStatusText.Save(dockSettings,
|
||||||
"conditionsFalseStatusText");
|
"conditionsFalseStatusText");
|
||||||
if (_dock) {
|
if (saveForCopy) {
|
||||||
auto window = static_cast<QMainWindow *>(
|
auto uuid = GenerateDockId();
|
||||||
obs_frontend_get_main_window());
|
obs_data_set_string(dockSettings, "dockId", uuid.c_str());
|
||||||
obs_data_set_bool(dockSettings, "isFloating",
|
|
||||||
_dock->isFloating());
|
} else {
|
||||||
obs_data_set_bool(dockSettings, "isVisible", DockIsVisible());
|
obs_data_set_string(dockSettings, "dockId", _dockId.c_str());
|
||||||
obs_data_set_int(dockSettings, "area",
|
|
||||||
window->dockWidgetArea(_dock));
|
|
||||||
obs_data_set_string(
|
|
||||||
dockSettings, "geometry",
|
|
||||||
_dock->saveGeometry().toBase64().constData());
|
|
||||||
}
|
}
|
||||||
|
obs_data_set_int(dockSettings, "version", 1);
|
||||||
obs_data_set_obj(obj, "dockSettings", dockSettings);
|
obs_data_set_obj(obj, "dockSettings", dockSettings);
|
||||||
obs_data_release(dockSettings);
|
obs_data_release(dockSettings);
|
||||||
}
|
}
|
||||||
@@ -913,8 +913,13 @@ void Macro::LoadDockSettings(obs_data_t *obj)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!obs_data_has_user_value(dockSettings, "version")) {
|
||||||
|
_dockId = std::string("ADVSS-") + _name;
|
||||||
|
} else {
|
||||||
|
_dockId = obs_data_get_string(dockSettings, "dockId");
|
||||||
|
}
|
||||||
|
|
||||||
const bool dockEnabled = obs_data_get_bool(dockSettings, "register");
|
const bool dockEnabled = obs_data_get_bool(dockSettings, "register");
|
||||||
_dockIsVisible = obs_data_get_bool(dockSettings, "isVisible");
|
|
||||||
|
|
||||||
// TODO: remove these default settings in a future version
|
// TODO: remove these default settings in a future version
|
||||||
obs_data_set_default_string(
|
obs_data_set_default_string(
|
||||||
@@ -942,15 +947,6 @@ void Macro::LoadDockSettings(obs_data_t *obj)
|
|||||||
obs_data_get_bool(dockSettings, "hasStatusLabel");
|
obs_data_get_bool(dockSettings, "hasStatusLabel");
|
||||||
_dockHighlight = obs_data_get_bool(dockSettings,
|
_dockHighlight = obs_data_get_bool(dockSettings,
|
||||||
"highlightIfConditionsTrue");
|
"highlightIfConditionsTrue");
|
||||||
_dockIsFloating = obs_data_get_bool(dockSettings, "isFloating");
|
|
||||||
_dockArea = static_cast<Qt::DockWidgetArea>(
|
|
||||||
obs_data_get_int(dockSettings, "area"));
|
|
||||||
auto geometryStr =
|
|
||||||
obs_data_get_string(dockSettings, "geometry");
|
|
||||||
if (geometryStr && strlen(geometryStr)) {
|
|
||||||
_dockGeo =
|
|
||||||
QByteArray::fromBase64(QByteArray(geometryStr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EnableDock(dockEnabled);
|
EnableDock(dockEnabled);
|
||||||
obs_data_release(dockSettings);
|
obs_data_release(dockSettings);
|
||||||
@@ -958,47 +954,29 @@ void Macro::LoadDockSettings(obs_data_t *obj)
|
|||||||
|
|
||||||
void Macro::EnableDock(bool value)
|
void Macro::EnableDock(bool value)
|
||||||
{
|
{
|
||||||
if (_registerDock == value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset dock regardless
|
// Reset dock regardless
|
||||||
RemoveDock();
|
RemoveDock();
|
||||||
|
|
||||||
// Unregister dock
|
if (!value) {
|
||||||
if (_registerDock) {
|
|
||||||
_dockIsFloating = true;
|
|
||||||
_dockGeo = QByteArray();
|
|
||||||
_registerDock = value;
|
_registerDock = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new dock widget
|
_dock = new MacroDock(GetWeakMacroByName(_name.c_str()), _runButtonText,
|
||||||
auto window =
|
_pauseButtonText, _unpauseButtonText,
|
||||||
static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
_conditionsTrueStatusText,
|
||||||
_dock = new MacroDock(GetWeakMacroByName(_name.c_str()), window,
|
|
||||||
_runButtonText, _pauseButtonText,
|
|
||||||
_unpauseButtonText, _conditionsTrueStatusText,
|
|
||||||
_conditionsFalseStatusText, _dockHighlight);
|
_conditionsFalseStatusText, _dockHighlight);
|
||||||
SetDockWidgetName(); // Used by OBS to restore position
|
|
||||||
|
|
||||||
// Register new dock
|
if (!obs_frontend_add_dock_by_id(_dockId.c_str(), _name.c_str(),
|
||||||
_dockAction = static_cast<QAction *>(obs_frontend_add_dock(_dock));
|
_dock)) {
|
||||||
|
blog(LOG_INFO, "failed to add macro dock for macro %s",
|
||||||
// Note that OBSBasic::OBSInit() has precedence over the visibility and
|
_name.c_str());
|
||||||
// geometry set here.
|
_dock->deleteLater();
|
||||||
// The function calls here are only intended to attempt to restore the
|
_dock = nullptr;
|
||||||
// dock status when switching scene collections.
|
_registerDock = false;
|
||||||
if (InitialLoadIsComplete()) {
|
return;
|
||||||
_dock->setVisible(_dockIsVisible);
|
|
||||||
if (window->dockWidgetArea(_dock) != _dockArea) {
|
|
||||||
window->addDockWidget(_dockArea, _dock);
|
|
||||||
}
|
|
||||||
if (_dock->isFloating() != _dockIsFloating) {
|
|
||||||
_dock->setFloating(_dockIsFloating);
|
|
||||||
}
|
|
||||||
_dock->restoreGeometry(_dockGeo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerDock = value;
|
_registerDock = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1095,28 +1073,22 @@ StringVariable Macro::ConditionsFalseStatusText() const
|
|||||||
|
|
||||||
void Macro::RemoveDock()
|
void Macro::RemoveDock()
|
||||||
{
|
{
|
||||||
if (_dock) {
|
obs_frontend_remove_dock(_dockId.c_str());
|
||||||
_dock->close();
|
_dock = nullptr;
|
||||||
_dock->deleteLater();
|
|
||||||
_dockAction->deleteLater();
|
|
||||||
_dock = nullptr;
|
|
||||||
_dockAction = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Macro::SetDockWidgetName() const
|
std::string Macro::GenerateDockId()
|
||||||
{
|
{
|
||||||
if (!_dock) {
|
#if LIBOBS_API_VER > MAKE_SEMANTIC_VERSION(30, 0, 0)
|
||||||
return;
|
auto uuid = os_generate_uuid();
|
||||||
}
|
auto id = std::string("advss-macro-dock-") + std::string(uuid);
|
||||||
// Set prefix to avoid dock name conflict
|
bfree(uuid);
|
||||||
_dock->setObjectName("ADVSS-" + QString::fromStdString(_name));
|
return id;
|
||||||
_dock->SetName(QString::fromStdString(_name));
|
|
||||||
|
|
||||||
if (!_dockAction) {
|
#else
|
||||||
return;
|
static std::atomic_int16_t idCounter = 0;
|
||||||
}
|
return std::to_string(++idCounter);
|
||||||
_dockAction->setText(QString::fromStdString(_name));
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pauseCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
static void pauseCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public:
|
|||||||
std::shared_ptr<Macro> Parent() const;
|
std::shared_ptr<Macro> Parent() const;
|
||||||
|
|
||||||
// Saving and loading
|
// Saving and loading
|
||||||
bool Save(obs_data_t *obj) const;
|
bool Save(obs_data_t *obj, bool saveForCopy = false) const;
|
||||||
bool Load(obs_data_t *obj);
|
bool Load(obs_data_t *obj);
|
||||||
// Some macros can refer to other macros, which are not yet loaded.
|
// Some macros can refer to other macros, which are not yet loaded.
|
||||||
// Use this function to set these references after loading is complete.
|
// Use this function to set these references after loading is complete.
|
||||||
@@ -155,11 +155,10 @@ private:
|
|||||||
bool RunActions(bool ignorePause);
|
bool RunActions(bool ignorePause);
|
||||||
bool RunElseActions(bool ignorePause);
|
bool RunElseActions(bool ignorePause);
|
||||||
|
|
||||||
bool DockIsVisible() const;
|
void SaveDockSettings(obs_data_t *obj, bool saveForCopy) const;
|
||||||
void SetDockWidgetName() const;
|
|
||||||
void SaveDockSettings(obs_data_t *obj) const;
|
|
||||||
void LoadDockSettings(obs_data_t *obj);
|
void LoadDockSettings(obs_data_t *obj);
|
||||||
void RemoveDock();
|
void RemoveDock();
|
||||||
|
static std::string GenerateDockId();
|
||||||
|
|
||||||
std::string _name = "";
|
std::string _name = "";
|
||||||
bool _die = false;
|
bool _die = false;
|
||||||
@@ -217,12 +216,8 @@ private:
|
|||||||
obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.true");
|
obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.true");
|
||||||
StringVariable _conditionsFalseStatusText =
|
StringVariable _conditionsFalseStatusText =
|
||||||
obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.false");
|
obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.false");
|
||||||
bool _dockIsFloating = true;
|
|
||||||
bool _dockIsVisible = false;
|
|
||||||
Qt::DockWidgetArea _dockArea;
|
|
||||||
QByteArray _dockGeo;
|
|
||||||
MacroDock *_dock = nullptr;
|
MacroDock *_dock = nullptr;
|
||||||
QAction *_dockAction = nullptr;
|
std::string _dockId = GenerateDockId();
|
||||||
};
|
};
|
||||||
|
|
||||||
void LoadMacros(obs_data_t *obj);
|
void LoadMacros(obs_data_t *obj);
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ public:
|
|||||||
RECORINDG_OR_STREAMING
|
RECORINDG_OR_STREAMING
|
||||||
};
|
};
|
||||||
AutoStart autoStartEvent = AutoStart::NEVER;
|
AutoStart autoStartEvent = AutoStart::NEVER;
|
||||||
|
bool enableCooldown = false;
|
||||||
Duration cooldown;
|
Duration cooldown;
|
||||||
bool showSystemTrayNotifications = false;
|
bool showSystemTrayNotifications = false;
|
||||||
bool transitionOverrideOverride = false;
|
bool transitionOverrideOverride = false;
|
||||||
|
|||||||
109
lib/utils/condition-logic.cpp
Normal file
109
lib/utils/condition-logic.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include "condition-logic.hpp"
|
||||||
|
#include "obs-module-helper.hpp"
|
||||||
|
#include "log-helper.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <QComboBox>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
const std::map<Logic::Type, const char *> Logic::localeMap = {
|
||||||
|
{Logic::Type::NONE, {"AdvSceneSwitcher.logic.none"}},
|
||||||
|
{Logic::Type::AND, {"AdvSceneSwitcher.logic.and"}},
|
||||||
|
{Logic::Type::OR, {"AdvSceneSwitcher.logic.or"}},
|
||||||
|
{Logic::Type::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
|
||||||
|
{Logic::Type::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
|
||||||
|
{Logic::Type::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
|
||||||
|
{Logic::Type::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
|
||||||
|
bool conditionMatched, const char *context)
|
||||||
|
{
|
||||||
|
if (!context) {
|
||||||
|
context = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case Type::ROOT_NONE:
|
||||||
|
return conditionMatched;
|
||||||
|
case Type::ROOT_NOT:
|
||||||
|
return !conditionMatched;
|
||||||
|
case Type::ROOT_LAST:
|
||||||
|
break;
|
||||||
|
case Type::NONE:
|
||||||
|
vblog(LOG_INFO, "skipping condition check for '%s'", context);
|
||||||
|
return currentMatchResult;
|
||||||
|
case Type::AND:
|
||||||
|
return currentMatchResult && conditionMatched;
|
||||||
|
case Type::OR:
|
||||||
|
return currentMatchResult || conditionMatched;
|
||||||
|
case Type::AND_NOT:
|
||||||
|
return currentMatchResult && !conditionMatched;
|
||||||
|
case Type::OR_NOT:
|
||||||
|
return currentMatchResult || !conditionMatched;
|
||||||
|
case Type::LAST:
|
||||||
|
default:
|
||||||
|
blog(LOG_WARNING, "ignoring invalid logic check (%s)", context);
|
||||||
|
return currentMatchResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentMatchResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logic::PopulateLogicTypeSelection(QComboBox *list, bool isRootCondition)
|
||||||
|
{
|
||||||
|
auto compare = isRootCondition
|
||||||
|
? std::function<bool(int)>{[](int typeValue) {
|
||||||
|
return typeValue < rootOffset;
|
||||||
|
}}
|
||||||
|
: std::function<bool(int)>{[](int typeValue) {
|
||||||
|
return typeValue >= rootOffset;
|
||||||
|
}};
|
||||||
|
for (const auto &[type, name] : localeMap) {
|
||||||
|
const int typeValue = static_cast<int>(type);
|
||||||
|
if (compare(typeValue)) {
|
||||||
|
list->addItem(obs_module_text(name), typeValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logic::Save(obs_data_t *obj, const char *name) const
|
||||||
|
{
|
||||||
|
obs_data_set_int(obj, name, static_cast<int>(_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logic::Load(obs_data_t *obj, const char *name)
|
||||||
|
{
|
||||||
|
_type = static_cast<Type>(obs_data_get_int(obj, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Logic::IsRootType() const
|
||||||
|
{
|
||||||
|
return _type < static_cast<Type>(rootOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Logic::IsNegationType(Logic::Type type)
|
||||||
|
{
|
||||||
|
return type == Type::ROOT_NOT || type == Type::AND_NOT ||
|
||||||
|
type == Type::OR_NOT;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Logic::IsValidSelection(bool isRootCondition) const
|
||||||
|
{
|
||||||
|
if (!IsRootType() == isRootCondition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (IsRootType() &&
|
||||||
|
(_type < Type::ROOT_NONE || _type >= Type::ROOT_LAST)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!IsRootType() &&
|
||||||
|
(_type <= Type::ROOT_LAST || _type >= Type::LAST)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
51
lib/utils/condition-logic.hpp
Normal file
51
lib/utils/condition-logic.hpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <map>
|
||||||
|
#include <obs-data.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class Logic {
|
||||||
|
public:
|
||||||
|
static constexpr auto rootOffset = 100;
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
ROOT_NONE = 0,
|
||||||
|
ROOT_NOT,
|
||||||
|
ROOT_LAST,
|
||||||
|
|
||||||
|
// leave some space for potential expansion
|
||||||
|
NONE = rootOffset,
|
||||||
|
AND,
|
||||||
|
OR,
|
||||||
|
AND_NOT,
|
||||||
|
OR_NOT,
|
||||||
|
LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
Logic(Type type) { _type = type; }
|
||||||
|
|
||||||
|
void Save(obs_data_t *obj, const char *name) const;
|
||||||
|
void Load(obs_data_t *obj, const char *name);
|
||||||
|
|
||||||
|
Type GetType() const { return _type; }
|
||||||
|
void SetType(const Type &type) { _type = type; }
|
||||||
|
bool IsRootType() const;
|
||||||
|
static bool IsNegationType(Logic::Type);
|
||||||
|
bool IsValidSelection(bool isRootCondition) const;
|
||||||
|
|
||||||
|
static bool ApplyConditionLogic(Type, bool currentMatchResult,
|
||||||
|
bool conditionMatched,
|
||||||
|
const char *context);
|
||||||
|
|
||||||
|
static void PopulateLogicTypeSelection(QComboBox *list,
|
||||||
|
bool isRootCondition);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type _type = Type::NONE;
|
||||||
|
static const std::map<Type, const char *> localeMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -77,13 +77,12 @@ bool CurlHelper::LoadLib()
|
|||||||
{
|
{
|
||||||
_lib = new QLibrary(curl_library_name, nullptr);
|
_lib = new QLibrary(curl_library_name, nullptr);
|
||||||
if (Resolve()) {
|
if (Resolve()) {
|
||||||
blog(LOG_INFO, "[adv-ss] found curl library");
|
blog(LOG_INFO, "found curl library");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
delete _lib;
|
delete _lib;
|
||||||
_lib = nullptr;
|
_lib = nullptr;
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING, "couldn't find the curl library in PATH");
|
||||||
"[adv-ss] couldn't find the curl library in PATH");
|
|
||||||
}
|
}
|
||||||
QStringList locations;
|
QStringList locations;
|
||||||
locations << QDir::currentPath();
|
locations << QDir::currentPath();
|
||||||
@@ -94,14 +93,13 @@ bool CurlHelper::LoadLib()
|
|||||||
locations << "/usr/local/opt/curl/lib";
|
locations << "/usr/local/opt/curl/lib";
|
||||||
#endif
|
#endif
|
||||||
for (QString path : locations) {
|
for (QString path : locations) {
|
||||||
blog(LOG_INFO, "[adv-ss] trying '%s'",
|
blog(LOG_INFO, "trying '%s'", path.toUtf8().constData());
|
||||||
path.toUtf8().constData());
|
|
||||||
QFileInfo libPath(
|
QFileInfo libPath(
|
||||||
QDir(path).absoluteFilePath(curl_library_name));
|
QDir(path).absoluteFilePath(curl_library_name));
|
||||||
|
|
||||||
if (libPath.exists() && libPath.isFile()) {
|
if (libPath.exists() && libPath.isFile()) {
|
||||||
QString libFilePath = libPath.absoluteFilePath();
|
QString libFilePath = libPath.absoluteFilePath();
|
||||||
blog(LOG_INFO, "[adv-ss] found curl library at '%s'",
|
blog(LOG_INFO, "found curl library at '%s'",
|
||||||
libFilePath.toUtf8().constData());
|
libFilePath.toUtf8().constData());
|
||||||
|
|
||||||
_lib = new QLibrary(libFilePath, nullptr);
|
_lib = new QLibrary(libFilePath, nullptr);
|
||||||
@@ -113,7 +111,7 @@ bool CurlHelper::LoadLib()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
blog(LOG_WARNING, "[adv-ss] can't find the curl library");
|
blog(LOG_WARNING, "can't find the curl library");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,11 +126,11 @@ bool CurlHelper::Resolve()
|
|||||||
|
|
||||||
if (_init && _setopt && _slistAppend && _perform && _cleanup &&
|
if (_init && _setopt && _slistAppend && _perform && _cleanup &&
|
||||||
_error) {
|
_error) {
|
||||||
blog(LOG_INFO, "[adv-ss] curl loaded successfully");
|
blog(LOG_INFO, "curl loaded successfully");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
blog(LOG_INFO, "[adv-ss] curl symbols not resolved");
|
blog(LOG_INFO, "curl symbols not resolved");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
110
lib/utils/duration-modifier.cpp
Normal file
110
lib/utils/duration-modifier.cpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#include "duration-modifier.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
void DurationModifier::Save(obs_data_t *obj, const char *name,
|
||||||
|
const char *duration) const
|
||||||
|
{
|
||||||
|
auto data = obs_data_create();
|
||||||
|
obs_data_set_int(data, name, static_cast<int>(_type));
|
||||||
|
_duration.Save(data, duration);
|
||||||
|
obs_data_set_obj(obj, "durationModifier", data);
|
||||||
|
obs_data_release(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DurationModifier::Load(obs_data_t *obj, const char *name,
|
||||||
|
const char *duration)
|
||||||
|
{
|
||||||
|
obs_data_t *data = nullptr;
|
||||||
|
if (obs_data_has_user_value(obj, "durationModifier")) {
|
||||||
|
data = obs_data_get_obj(obj, "durationModifier");
|
||||||
|
} else {
|
||||||
|
// For backwards compatibility
|
||||||
|
obs_data_addref(obj);
|
||||||
|
data = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For backwards compatibility check if duration value exist without
|
||||||
|
// time constraint condition - if so assume DurationCondition::MORE
|
||||||
|
if (!obs_data_has_user_value(data, name) &&
|
||||||
|
obs_data_has_user_value(data, duration)) {
|
||||||
|
obs_data_set_int(data, name, static_cast<int>(Type::MORE));
|
||||||
|
}
|
||||||
|
|
||||||
|
_type = static_cast<Type>(obs_data_get_int(data, name));
|
||||||
|
_duration.Load(data, duration);
|
||||||
|
|
||||||
|
// TODO: remove this fallback
|
||||||
|
if (obs_data_has_user_value(data, "displayUnit")) {
|
||||||
|
_duration.SetUnit(static_cast<Duration::Unit>(
|
||||||
|
obs_data_get_int(data, "displayUnit")));
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DurationModifier::SetTimeRemaining(double seconds)
|
||||||
|
{
|
||||||
|
_duration.SetTimeRemaining(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DurationModifier::DurationReached()
|
||||||
|
{
|
||||||
|
switch (_type) {
|
||||||
|
case DurationModifier::Type::NONE:
|
||||||
|
return true;
|
||||||
|
case DurationModifier::Type::MORE:
|
||||||
|
return _duration.DurationReached();
|
||||||
|
case DurationModifier::Type::EQUAL:
|
||||||
|
if (_duration.DurationReached() && !_durationWasReached) {
|
||||||
|
_durationWasReached = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DurationModifier::Type::LESS:
|
||||||
|
return !_duration.DurationReached();
|
||||||
|
case DurationModifier::Type::WITHIN:
|
||||||
|
if (_duration.IsReset()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !_duration.DurationReached();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DurationModifier::ResetDuration()
|
||||||
|
{
|
||||||
|
_durationWasReached = false;
|
||||||
|
_duration.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DurationModifier::CheckConditionWithDurationModifier(bool conditionValue)
|
||||||
|
{
|
||||||
|
if (_type != DurationModifier::Type::WITHIN && !conditionValue) {
|
||||||
|
ResetDuration();
|
||||||
|
}
|
||||||
|
if (_type == DurationModifier::Type::WITHIN && conditionValue) {
|
||||||
|
ResetDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_type) {
|
||||||
|
case DurationModifier::Type::NONE:
|
||||||
|
case DurationModifier::Type::MORE:
|
||||||
|
case DurationModifier::Type::EQUAL:
|
||||||
|
case DurationModifier::Type::LESS:
|
||||||
|
return conditionValue && DurationReached();
|
||||||
|
case DurationModifier::Type::WITHIN:
|
||||||
|
if (conditionValue) {
|
||||||
|
SetTimeRemaining(_duration.Seconds());
|
||||||
|
}
|
||||||
|
return conditionValue || DurationReached();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conditionValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
31
lib/utils/duration-modifier.hpp
Normal file
31
lib/utils/duration-modifier.hpp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "duration.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class DurationModifier {
|
||||||
|
public:
|
||||||
|
enum class Type { NONE, MORE, EQUAL, LESS, WITHIN };
|
||||||
|
|
||||||
|
void Save(obs_data_t *obj, const char *name = "time_constraint",
|
||||||
|
const char *duration = "seconds") const;
|
||||||
|
void Load(obs_data_t *obj, const char *name = "time_constraint",
|
||||||
|
const char *duration = "seconds");
|
||||||
|
void SetModifier(Type type) { _type = type; }
|
||||||
|
void SetDuration(const Duration &duration) { _duration = duration; }
|
||||||
|
Type GetType() const { return _type; }
|
||||||
|
Duration GetDuration() const { return _duration; }
|
||||||
|
void ResetDuration();
|
||||||
|
|
||||||
|
bool CheckConditionWithDurationModifier(bool conditionValue);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetTimeRemaining(double seconds);
|
||||||
|
bool DurationReached();
|
||||||
|
|
||||||
|
Type _type = Type::NONE;
|
||||||
|
Duration _duration;
|
||||||
|
bool _durationWasReached = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -101,7 +101,7 @@ double Duration::TimeRemaining() const
|
|||||||
|
|
||||||
void Duration::SetTimeRemaining(double remaining)
|
void Duration::SetTimeRemaining(double remaining)
|
||||||
{
|
{
|
||||||
long long msPassed = (Seconds() - remaining) * 1000;
|
long long msPassed = (long long)((Seconds() - remaining) * 1000);
|
||||||
_startTime = std::chrono::high_resolution_clock::now() -
|
_startTime = std::chrono::high_resolution_clock::now() -
|
||||||
std::chrono::milliseconds(msPassed);
|
std::chrono::milliseconds(msPassed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,8 +84,7 @@ void FilterComboBox::focusOutEvent(QFocusEvent *event)
|
|||||||
// Reset on invalid selection
|
// Reset on invalid selection
|
||||||
if (findText(currentText()) == -1) {
|
if (findText(currentText()) == -1) {
|
||||||
setCurrentIndex(-1);
|
setCurrentIndex(-1);
|
||||||
emit currentIndexChanged(-1);
|
Emit(-1, "");
|
||||||
emit currentTextChanged("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QComboBox::focusOutEvent(event);
|
QComboBox::focusOutEvent(event);
|
||||||
@@ -121,8 +120,7 @@ void FilterComboBox::CompleterHighlightChanged(const QModelIndex &index)
|
|||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit currentIndexChanged(idx);
|
Emit(idx, text);
|
||||||
emit currentTextChanged(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterComboBox::TextChanged(const QString &text)
|
void FilterComboBox::TextChanged(const QString &text)
|
||||||
@@ -134,8 +132,18 @@ void FilterComboBox::TextChanged(const QString &text)
|
|||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit currentIndexChanged(idx);
|
}
|
||||||
emit currentTextChanged(text);
|
|
||||||
|
void FilterComboBox::Emit(int index, const QString &text)
|
||||||
|
{
|
||||||
|
if (_lastEmittedIndex != index) {
|
||||||
|
_lastEmittedIndex = index;
|
||||||
|
emit currentIndexChanged(index);
|
||||||
|
}
|
||||||
|
if (_lastEmittedText != text) {
|
||||||
|
_lastEmittedText = text;
|
||||||
|
emit currentTextChanged(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ private slots:
|
|||||||
void TextChanged(const QString &);
|
void TextChanged(const QString &);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Emit(int index, const QString &text);
|
||||||
|
|
||||||
|
int _lastEmittedIndex = -1;
|
||||||
|
QString _lastEmittedText;
|
||||||
|
|
||||||
int _lastCompleterHighlightRow = -1;
|
int _lastCompleterHighlightRow = -1;
|
||||||
static bool _filteringEnabled;
|
static bool _filteringEnabled;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#ifndef UNIT_TEST
|
||||||
#include <util/base.h>
|
#include <util/base.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
|
#ifdef UNIT_TEST
|
||||||
|
#define blog(level, msg, ...)
|
||||||
|
#define vblog(level, msg, ...)
|
||||||
|
#define ablog(level, msg, ...)
|
||||||
|
#else
|
||||||
|
|
||||||
// Print log with "[adv-ss] " prefix
|
// Print log with "[adv-ss] " prefix
|
||||||
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
||||||
// Print log with "[adv-ss] " if log level is set to "verbose"
|
// Print log with "[adv-ss] " if log level is set to "verbose"
|
||||||
@@ -20,6 +28,8 @@ namespace advss {
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// Returns true if log level is set to "verbose"
|
// Returns true if log level is set to "verbose"
|
||||||
EXPORT bool VerboseLoggingEnabled();
|
EXPORT bool VerboseLoggingEnabled();
|
||||||
// Returns true if log level is set to "action" or "verbose"
|
// Returns true if log level is set to "action" or "verbose"
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <QDockWidget>
|
|
||||||
|
|
||||||
// QDockWidget wrapper enable applying "OBSDock" stylesheet
|
|
||||||
class OBSDock : public QDockWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline OBSDock(QWidget *parent = nullptr) : QDockWidget(parent) {}
|
|
||||||
};
|
|
||||||
2540
lib/utils/properties-view.cpp
Normal file
2540
lib/utils/properties-view.cpp
Normal file
File diff suppressed because it is too large
Load Diff
264
lib/utils/properties-view.hpp
Normal file
264
lib/utils/properties-view.hpp
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <obs-data.h>
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <qtimer.h>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(30, 0, 0)
|
||||||
|
|
||||||
|
class QFormLayout;
|
||||||
|
class QLabel;
|
||||||
|
class QResizeEvent;
|
||||||
|
|
||||||
|
typedef obs_properties_t *(*PropertiesReloadCallback)(void *obj);
|
||||||
|
typedef void (*PropertiesUpdateCallback)(void *obj, obs_data_t *old_settings,
|
||||||
|
obs_data_t *new_settings);
|
||||||
|
typedef void (*PropertiesVisualUpdateCb)(void *obj, obs_data_t *settings);
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class OBSPropertiesView;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
class VScrollArea : public QScrollArea {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
VScrollArea(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void resizeEvent(QResizeEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
class SpinBoxIgnoreScroll : public QSpinBox {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SpinBoxIgnoreScroll(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void wheelEvent(QWheelEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
class OBSPlainTextEdit : public QPlainTextEdit {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit OBSPlainTextEdit(QWidget *parent = nullptr,
|
||||||
|
bool monospace = true);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
class WidgetInfo : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class OBSPropertiesView;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OBSPropertiesView *view;
|
||||||
|
obs_property_t *property;
|
||||||
|
QWidget *widget;
|
||||||
|
QPointer<QTimer> update_timer;
|
||||||
|
bool recently_updated = false;
|
||||||
|
OBSData old_settings_cache;
|
||||||
|
|
||||||
|
void BoolChanged(const char *setting);
|
||||||
|
void IntChanged(const char *setting);
|
||||||
|
void FloatChanged(const char *setting);
|
||||||
|
void TextChanged(const char *setting);
|
||||||
|
bool PathChanged(const char *setting);
|
||||||
|
void ListChanged(const char *setting);
|
||||||
|
bool ColorChangedInternal(const char *setting, bool supportAlpha);
|
||||||
|
bool ColorChanged(const char *setting);
|
||||||
|
bool ColorAlphaChanged(const char *setting);
|
||||||
|
bool FontChanged(const char *setting);
|
||||||
|
void GroupChanged(const char *setting);
|
||||||
|
void EditableListChanged();
|
||||||
|
void ButtonClicked();
|
||||||
|
|
||||||
|
void TogglePasswordText(bool checked);
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline WidgetInfo(OBSPropertiesView *view_, obs_property_t *prop,
|
||||||
|
QWidget *widget_)
|
||||||
|
: view(view_),
|
||||||
|
property(prop),
|
||||||
|
widget(widget_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~WidgetInfo()
|
||||||
|
{
|
||||||
|
if (update_timer) {
|
||||||
|
update_timer->stop();
|
||||||
|
QMetaObject::invokeMethod(update_timer, "timeout");
|
||||||
|
update_timer->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void ControlChanged();
|
||||||
|
|
||||||
|
/* editable list */
|
||||||
|
void EditListAdd();
|
||||||
|
void EditListAddText();
|
||||||
|
void EditListAddFiles();
|
||||||
|
void EditListAddDir();
|
||||||
|
void EditListRemove();
|
||||||
|
void EditListEdit();
|
||||||
|
void EditListUp();
|
||||||
|
void EditListDown();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
class OBSPropertiesView : public VScrollArea {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class WidgetInfo;
|
||||||
|
|
||||||
|
using properties_delete_t = decltype(&obs_properties_destroy);
|
||||||
|
using properties_t =
|
||||||
|
std::unique_ptr<obs_properties_t, properties_delete_t>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWidget *widget = nullptr;
|
||||||
|
properties_t properties;
|
||||||
|
OBSData settings;
|
||||||
|
OBSWeakObjectAutoRelease weakObj;
|
||||||
|
void *rawObj = nullptr;
|
||||||
|
std::string type;
|
||||||
|
PropertiesReloadCallback reloadCallback;
|
||||||
|
PropertiesUpdateCallback callback = nullptr;
|
||||||
|
PropertiesVisualUpdateCb visUpdateCb = nullptr;
|
||||||
|
int minSize;
|
||||||
|
std::vector<std::unique_ptr<WidgetInfo>> children;
|
||||||
|
std::string lastFocused;
|
||||||
|
QWidget *lastWidget = nullptr;
|
||||||
|
bool deferUpdate;
|
||||||
|
bool enableDefer = true;
|
||||||
|
|
||||||
|
template<typename Sender, typename SenderParent, typename... Args>
|
||||||
|
QWidget *NewWidget(obs_property_t *prop, Sender *widget,
|
||||||
|
void (SenderParent::*signal)(Args...));
|
||||||
|
|
||||||
|
QWidget *AddCheckbox(obs_property_t *prop);
|
||||||
|
QWidget *AddText(obs_property_t *prop, QFormLayout *layout,
|
||||||
|
QLabel *&label);
|
||||||
|
void AddPath(obs_property_t *prop, QFormLayout *layout, QLabel **label);
|
||||||
|
void AddInt(obs_property_t *prop, QFormLayout *layout, QLabel **label);
|
||||||
|
void AddFloat(obs_property_t *prop, QFormLayout *layout,
|
||||||
|
QLabel **label);
|
||||||
|
QWidget *AddList(obs_property_t *prop, bool &warning);
|
||||||
|
void AddEditableList(obs_property_t *prop, QFormLayout *layout,
|
||||||
|
QLabel *&label);
|
||||||
|
QWidget *AddButton(obs_property_t *prop);
|
||||||
|
void AddColorInternal(obs_property_t *prop, QFormLayout *layout,
|
||||||
|
QLabel *&label, bool supportAlpha);
|
||||||
|
void AddColor(obs_property_t *prop, QFormLayout *layout,
|
||||||
|
QLabel *&label);
|
||||||
|
void AddColorAlpha(obs_property_t *prop, QFormLayout *layout,
|
||||||
|
QLabel *&label);
|
||||||
|
void AddFont(obs_property_t *prop, QFormLayout *layout, QLabel *&label);
|
||||||
|
void AddFrameRate(obs_property_t *prop, bool &warning,
|
||||||
|
QFormLayout *layout, QLabel *&label);
|
||||||
|
|
||||||
|
void AddGroup(obs_property_t *prop, QFormLayout *layout);
|
||||||
|
|
||||||
|
void AddProperty(obs_property_t *property, QFormLayout *layout);
|
||||||
|
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
|
void GetScrollPos(int &h, int &v, int &hend, int &vend);
|
||||||
|
void SetScrollPos(int h, int v, int old_hend, int old_vend);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void RefreshProperties();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void ReloadProperties();
|
||||||
|
void SignalChanged();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void PropertiesResized();
|
||||||
|
void Changed();
|
||||||
|
void PropertiesRefreshed();
|
||||||
|
|
||||||
|
public:
|
||||||
|
OBSPropertiesView(OBSData settings, obs_object_t *obj,
|
||||||
|
PropertiesReloadCallback reloadCallback,
|
||||||
|
PropertiesUpdateCallback callback,
|
||||||
|
PropertiesVisualUpdateCb cb = nullptr,
|
||||||
|
int minSize = 0);
|
||||||
|
OBSPropertiesView(OBSData settings, void *obj,
|
||||||
|
PropertiesReloadCallback reloadCallback,
|
||||||
|
PropertiesUpdateCallback callback,
|
||||||
|
PropertiesVisualUpdateCb cb = nullptr,
|
||||||
|
int minSize = 0);
|
||||||
|
OBSPropertiesView(OBSData settings, const char *type,
|
||||||
|
PropertiesReloadCallback reloadCallback,
|
||||||
|
int minSize = 0);
|
||||||
|
|
||||||
|
#define obj_constructor(type) \
|
||||||
|
inline OBSPropertiesView(OBSData settings, obs_##type##_t *type, \
|
||||||
|
PropertiesReloadCallback reloadCallback, \
|
||||||
|
PropertiesUpdateCallback callback, \
|
||||||
|
PropertiesVisualUpdateCb cb = nullptr, \
|
||||||
|
int minSize = 0) \
|
||||||
|
: OBSPropertiesView(settings, (obs_object_t *)type, \
|
||||||
|
reloadCallback, callback, cb, minSize) \
|
||||||
|
{ \
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_constructor(source);
|
||||||
|
obj_constructor(output);
|
||||||
|
obj_constructor(encoder);
|
||||||
|
obj_constructor(service);
|
||||||
|
#undef obj_constructor
|
||||||
|
|
||||||
|
inline obs_data_t *GetSettings() const { return settings; }
|
||||||
|
|
||||||
|
inline void UpdateSettings()
|
||||||
|
{
|
||||||
|
if (callback)
|
||||||
|
callback(OBSGetStrongRef(weakObj), nullptr, settings);
|
||||||
|
else if (visUpdateCb)
|
||||||
|
visUpdateCb(OBSGetStrongRef(weakObj), settings);
|
||||||
|
}
|
||||||
|
inline bool DeferUpdate() const { return deferUpdate; }
|
||||||
|
inline void SetDeferrable(bool deferrable) { enableDefer = deferrable; }
|
||||||
|
|
||||||
|
inline OBSObject GetObject() const { return OBSGetStrongRef(weakObj); }
|
||||||
|
|
||||||
|
#define Def_IsObject(type) \
|
||||||
|
inline bool IsObject(obs_##type##_t *type) const \
|
||||||
|
{ \
|
||||||
|
OBSObject obj = OBSGetStrongRef(weakObj); \
|
||||||
|
return obj.Get() == (obs_object_t *)type; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
Def_IsObject(source)
|
||||||
|
Def_IsObject(output)
|
||||||
|
Def_IsObject(encoder)
|
||||||
|
Def_IsObject(service)
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
#undef Def_IsObject
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
|
|
||||||
|
#endif
|
||||||
63
lib/utils/properties-view.moc.hpp
Normal file
63
lib/utils/properties-view.moc.hpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <obs.h>
|
||||||
|
#include <media-io/frame-rate.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4505)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool operator!=(const media_frames_per_second &a,
|
||||||
|
const media_frames_per_second &b)
|
||||||
|
{
|
||||||
|
return a.numerator != b.numerator || a.denominator != b.denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool operator==(const media_frames_per_second &a,
|
||||||
|
const media_frames_per_second &b)
|
||||||
|
{
|
||||||
|
return !(a != b);
|
||||||
|
}
|
||||||
|
|
||||||
|
using frame_rate_range_t =
|
||||||
|
std::pair<media_frames_per_second, media_frames_per_second>;
|
||||||
|
using frame_rate_ranges_t = std::vector<frame_rate_range_t>;
|
||||||
|
|
||||||
|
class OBSFrameRatePropertyWidget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
frame_rate_ranges_t fps_ranges;
|
||||||
|
|
||||||
|
QComboBox *modeSelect = nullptr;
|
||||||
|
QStackedWidget *modeDisplay = nullptr;
|
||||||
|
|
||||||
|
QWidget *labels = nullptr;
|
||||||
|
QLabel *currentFPS = nullptr;
|
||||||
|
QLabel *timePerFrame = nullptr;
|
||||||
|
QLabel *minLabel = nullptr;
|
||||||
|
QLabel *maxLabel = nullptr;
|
||||||
|
|
||||||
|
QComboBox *simpleFPS = nullptr;
|
||||||
|
|
||||||
|
QComboBox *fpsRange = nullptr;
|
||||||
|
QSpinBox *numEdit = nullptr;
|
||||||
|
QSpinBox *denEdit = nullptr;
|
||||||
|
|
||||||
|
bool updating = false;
|
||||||
|
|
||||||
|
const char *name = nullptr;
|
||||||
|
obs_data_t *settings = nullptr;
|
||||||
|
|
||||||
|
QLabel *warningLabel = nullptr;
|
||||||
|
|
||||||
|
OBSFrameRatePropertyWidget() = default;
|
||||||
|
};
|
||||||
@@ -133,7 +133,9 @@ bool Section::eventFilter(QObject *obj, QEvent *event)
|
|||||||
|
|
||||||
void Section::SetupAnimations()
|
void Section::SetupAnimations()
|
||||||
{
|
{
|
||||||
delete _toggleAnimation;
|
if (_toggleAnimation) {
|
||||||
|
_toggleAnimation->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
_toggleAnimation = new QParallelAnimationGroup(this);
|
_toggleAnimation = new QParallelAnimationGroup(this);
|
||||||
_toggleAnimation->addAnimation(
|
_toggleAnimation->addAnimation(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "status-control.hpp"
|
#include "status-control.hpp"
|
||||||
|
#include "log-helper.hpp"
|
||||||
#include "obs-module-helper.hpp"
|
#include "obs-module-helper.hpp"
|
||||||
#include "path-helpers.hpp"
|
#include "path-helpers.hpp"
|
||||||
#include "plugin-state-helpers.hpp"
|
#include "plugin-state-helpers.hpp"
|
||||||
@@ -11,10 +12,30 @@
|
|||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
|
|
||||||
|
#if LIBOBS_API_VER < MAKE_SEMANTIC_VERSION(30, 0, 0)
|
||||||
|
#include <QDockWidget>
|
||||||
|
|
||||||
|
static bool obs_frontend_add_dock_by_id(const char *id, const char *title,
|
||||||
|
QWidget *widget)
|
||||||
|
{
|
||||||
|
widget->setObjectName(id);
|
||||||
|
|
||||||
|
auto dock = new QDockWidget();
|
||||||
|
dock->setWindowTitle(title);
|
||||||
|
dock->setWidget(widget);
|
||||||
|
dock->setFloating(true);
|
||||||
|
dock->setVisible(false);
|
||||||
|
dock->setFeatures(QDockWidget::DockWidgetClosable |
|
||||||
|
QDockWidget::DockWidgetMovable |
|
||||||
|
QDockWidget::DockWidgetFloatable);
|
||||||
|
|
||||||
|
return !!obs_frontend_add_dock(dock);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
void OpenSettingsWindow();
|
void OpenSettingsWindow();
|
||||||
StatusDock *dock = nullptr;
|
|
||||||
|
|
||||||
static QString colorToString(const QColor &color)
|
static QString colorToString(const QColor &color)
|
||||||
{
|
{
|
||||||
@@ -133,6 +154,7 @@ void StatusControl::SetStarted()
|
|||||||
_status->setText(obs_module_text("AdvSceneSwitcher.status.active"));
|
_status->setText(obs_module_text("AdvSceneSwitcher.status.active"));
|
||||||
if (_pulse) {
|
if (_pulse) {
|
||||||
_pulse->deleteLater();
|
_pulse->deleteLater();
|
||||||
|
_pulse = nullptr;
|
||||||
}
|
}
|
||||||
SetStatusStyleSheet(false);
|
SetStatusStyleSheet(false);
|
||||||
_setToStopped = false;
|
_setToStopped = false;
|
||||||
@@ -168,16 +190,12 @@ void StatusControl::SetStatusStyleSheet(bool stopped) const
|
|||||||
_status->setStyleSheet(style);
|
_status->setStyleSheet(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusDock::StatusDock(QWidget *parent) : OBSDock(parent)
|
StatusDockWidget::StatusDockWidget(QWidget *parent) : QFrame(parent)
|
||||||
{
|
{
|
||||||
setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
setFrameShape(QFrame::StyledPanel);
|
||||||
setFeatures(DockWidgetClosable | DockWidgetMovable |
|
setFrameShadow(QFrame::Sunken);
|
||||||
DockWidgetFloatable);
|
|
||||||
// Setting a fixed object name is crucial for OBS to be able to restore
|
|
||||||
// the docks position, if the dock is not floating
|
|
||||||
setObjectName("Adv-ss-dock");
|
|
||||||
|
|
||||||
QAction *action = new QAction;
|
auto action = new QAction;
|
||||||
action->setProperty("themeID", QVariant(QString::fromUtf8("cogsIcon")));
|
action->setProperty("themeID", QVariant(QString::fromUtf8("cogsIcon")));
|
||||||
action->connect(action, &QAction::triggered, OpenSettingsWindow);
|
action->connect(action, &QAction::triggered, OpenSettingsWindow);
|
||||||
const auto path = QString::fromStdString(GetDataFilePath(
|
const auto path = QString::fromStdString(GetDataFilePath(
|
||||||
@@ -195,29 +213,21 @@ StatusDock::StatusDock(QWidget *parent) : OBSDock(parent)
|
|||||||
statusControl->ButtonLayout()->setStretchFactor(statusControl->Button(),
|
statusControl->ButtonLayout()->setStretchFactor(statusControl->Button(),
|
||||||
100);
|
100);
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout;
|
auto layout = new QVBoxLayout;
|
||||||
layout->addWidget(statusControl);
|
layout->addWidget(statusControl);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
setLayout(layout);
|
||||||
// QFrame wrapper is necessary to avoid dock being partially
|
|
||||||
// transparent
|
|
||||||
QFrame *wrapper = new QFrame;
|
|
||||||
wrapper->setFrameShape(QFrame::StyledPanel);
|
|
||||||
wrapper->setFrameShadow(QFrame::Sunken);
|
|
||||||
wrapper->setLayout(layout);
|
|
||||||
setWidget(wrapper);
|
|
||||||
|
|
||||||
setFloating(true);
|
|
||||||
hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupDock()
|
void SetupDock()
|
||||||
{
|
{
|
||||||
dock = new StatusDock(
|
auto dock = new StatusDockWidget();
|
||||||
static_cast<QMainWindow *>(obs_frontend_get_main_window()));
|
if (!obs_frontend_add_dock_by_id(
|
||||||
// Added for cosmetic reasons to avoid brief flash of dock window on startup
|
"advss-status-dock",
|
||||||
dock->setVisible(false);
|
obs_module_text("AdvSceneSwitcher.windowTitle"), dock)) {
|
||||||
obs_frontend_add_dock(dock);
|
blog(LOG_INFO, "failed to register status dock!");
|
||||||
|
dock->deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "obs-dock.hpp"
|
|
||||||
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@@ -38,11 +36,11 @@ private:
|
|||||||
bool _setToStopped = true;
|
bool _setToStopped = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatusDock : public OBSDock {
|
class StatusDockWidget : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StatusDock(QWidget *parent = 0);
|
StatusDockWidget(QWidget *parent = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
void SetupDock();
|
void SetupDock();
|
||||||
|
|||||||
@@ -160,4 +160,12 @@ void QeueUITask(void (*task)(void *param), void *param)
|
|||||||
obs_queue_task(OBS_TASK_UI, task, param, false);
|
obs_queue_task(OBS_TASK_UI, task, param, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsCursorInWidgetArea(QWidget *widget)
|
||||||
|
{
|
||||||
|
const auto cursorPos = QCursor::pos();
|
||||||
|
const auto widgetPos = widget->mapFromGlobal(cursorPos);
|
||||||
|
const auto widgetRect = widget->rect();
|
||||||
|
return widgetRect.contains(widgetPos);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -33,4 +33,6 @@ EXPORT std::string GetThemeTypeName();
|
|||||||
|
|
||||||
void QeueUITask(void (*task)(void *param), void *param);
|
void QeueUITask(void (*task)(void *param), void *param);
|
||||||
|
|
||||||
|
bool IsCursorInWidgetArea(QWidget *widget);
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ std::string ToString(double value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
||||||
QPushButton *addButton, QObject *addHighlight)
|
QObject *addHighlight)
|
||||||
{
|
{
|
||||||
if (!list || !newWidget) {
|
if (!list || !newWidget) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ std::string ToString(double value);
|
|||||||
/* Legacy helpers */
|
/* Legacy helpers */
|
||||||
|
|
||||||
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
||||||
QPushButton *addButton = nullptr,
|
|
||||||
QObject *addHighlight = nullptr);
|
QObject *addHighlight = nullptr);
|
||||||
bool listMoveUp(QListWidget *list);
|
bool listMoveUp(QListWidget *list);
|
||||||
bool listMoveDown(QListWidget *list);
|
bool listMoveDown(QListWidget *list);
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ void Variable::Save(obs_data_t *obj) const
|
|||||||
|
|
||||||
std::string Variable::Value(bool updateLastUsed) const
|
std::string Variable::Value(bool updateLastUsed) const
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
if (updateLastUsed) {
|
if (updateLastUsed) {
|
||||||
UpdateLastUsed();
|
UpdateLastUsed();
|
||||||
}
|
}
|
||||||
@@ -73,6 +74,7 @@ std::optional<int> Variable::IntValue() const
|
|||||||
|
|
||||||
void Variable::SetValue(const std::string &value)
|
void Variable::SetValue(const std::string &value)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
_previousValue = _value;
|
_previousValue = _value;
|
||||||
_value = value;
|
_value = value;
|
||||||
|
|
||||||
@@ -298,8 +300,7 @@ VariableSelectionDialog::VariableSelectionDialog(QWidget *parent)
|
|||||||
|
|
||||||
QWidget *GetSettingsWindow();
|
QWidget *GetSettingsWindow();
|
||||||
|
|
||||||
bool VariableSelectionDialog::AskForVariable(QWidget *parent,
|
bool VariableSelectionDialog::AskForVariable(std::string &varName)
|
||||||
std::string &varName)
|
|
||||||
{
|
{
|
||||||
VariableSelectionDialog dialog(GetSettingsWindow());
|
VariableSelectionDialog dialog(GetSettingsWindow());
|
||||||
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
#include "item-selection-helpers.hpp"
|
#include "item-selection-helpers.hpp"
|
||||||
#include "resizing-text-edit.hpp"
|
#include "resizing-text-edit.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <mutex>
|
||||||
#include <optional>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <obs-data.h>
|
#include <obs-data.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ private:
|
|||||||
int _valueChangeCount = 0;
|
int _valueChangeCount = 0;
|
||||||
mutable std::chrono::high_resolution_clock::time_point _lastUsed;
|
mutable std::chrono::high_resolution_clock::time_point _lastUsed;
|
||||||
mutable std::chrono::high_resolution_clock::time_point _lastChanged;
|
mutable std::chrono::high_resolution_clock::time_point _lastChanged;
|
||||||
|
mutable std::mutex _mutex;
|
||||||
|
|
||||||
friend VariableSelection;
|
friend VariableSelection;
|
||||||
friend VariableSettingsDialog;
|
friend VariableSettingsDialog;
|
||||||
@@ -87,7 +89,7 @@ class VariableSelectionDialog : public QDialog {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
VariableSelectionDialog(QWidget *parent);
|
VariableSelectionDialog(QWidget *parent);
|
||||||
static bool AskForVariable(QWidget *parent, std::string &variableName);
|
static bool AskForVariable(std::string &variableName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VariableSelection *_variableSelection;
|
VariableSelection *_variableSelection;
|
||||||
|
|||||||
@@ -172,6 +172,8 @@ target_sources(
|
|||||||
utils/striped-frame.hpp
|
utils/striped-frame.hpp
|
||||||
utils/text-helpers.cpp
|
utils/text-helpers.cpp
|
||||||
utils/text-helpers.hpp
|
utils/text-helpers.hpp
|
||||||
|
utils/transform-setting.cpp
|
||||||
|
utils/transform-setting.hpp
|
||||||
utils/transition-selection.cpp
|
utils/transition-selection.cpp
|
||||||
utils/transition-selection.hpp
|
utils/transition-selection.hpp
|
||||||
utils/websocket-helpers.cpp
|
utils/websocket-helpers.cpp
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ bool MacroActionSceneTransform::_registered = MacroActionFactory::Register(
|
|||||||
const static std::map<MacroActionSceneTransform::Action, std::string> actions = {
|
const static std::map<MacroActionSceneTransform::Action, std::string> actions = {
|
||||||
{MacroActionSceneTransform::Action::MANUAL_TRANSFORM,
|
{MacroActionSceneTransform::Action::MANUAL_TRANSFORM,
|
||||||
"AdvSceneSwitcher.action.sceneTransform.type.manual"},
|
"AdvSceneSwitcher.action.sceneTransform.type.manual"},
|
||||||
|
{MacroActionSceneTransform::Action::SINGLE_TRANSFORM_SETTING,
|
||||||
|
"AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting"},
|
||||||
{MacroActionSceneTransform::Action::RESET,
|
{MacroActionSceneTransform::Action::RESET,
|
||||||
"AdvSceneSwitcher.action.sceneTransform.type.reset"},
|
"AdvSceneSwitcher.action.sceneTransform.type.reset"},
|
||||||
{MacroActionSceneTransform::Action::ROTATE,
|
{MacroActionSceneTransform::Action::ROTATE,
|
||||||
@@ -200,49 +202,52 @@ void MacroActionSceneTransform::Transform(obs_scene_item *item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (_action) {
|
switch (_action) {
|
||||||
case MacroActionSceneTransform::Action::RESET:
|
case Action::RESET:
|
||||||
obs_sceneitem_defer_update_begin(item);
|
obs_sceneitem_defer_update_begin(item);
|
||||||
reset(item);
|
reset(item);
|
||||||
obs_sceneitem_defer_update_end(item);
|
obs_sceneitem_defer_update_end(item);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case MacroActionSceneTransform::Action::ROTATE:
|
case Action::ROTATE:
|
||||||
rotate(item, _rotation);
|
rotate(item, _rotation);
|
||||||
break;
|
break;
|
||||||
case MacroActionSceneTransform::Action::FLIP_HORIZONTAL: {
|
case Action::FLIP_HORIZONTAL: {
|
||||||
vec2 scale;
|
vec2 scale;
|
||||||
vec2_set(&scale, -1.0f, 1.0f);
|
vec2_set(&scale, -1.0f, 1.0f);
|
||||||
flip(item, scale);
|
flip(item, scale);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MacroActionSceneTransform::Action::FLIP_VERTICAL: {
|
case Action::FLIP_VERTICAL: {
|
||||||
vec2 scale;
|
vec2 scale;
|
||||||
vec2_set(&scale, 1.0f, -1.0f);
|
vec2_set(&scale, 1.0f, -1.0f);
|
||||||
flip(item, scale);
|
flip(item, scale);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MacroActionSceneTransform::Action::FIT_TO_SCREEN:
|
case Action::FIT_TO_SCREEN:
|
||||||
centerAlign(item, OBS_BOUNDS_SCALE_INNER);
|
centerAlign(item, OBS_BOUNDS_SCALE_INNER);
|
||||||
break;
|
break;
|
||||||
case MacroActionSceneTransform::Action::STRETCH_TO_SCREEN:
|
case Action::STRETCH_TO_SCREEN:
|
||||||
centerAlign(item, OBS_BOUNDS_STRETCH);
|
centerAlign(item, OBS_BOUNDS_STRETCH);
|
||||||
break;
|
break;
|
||||||
case MacroActionSceneTransform::Action::CENTER_TO_SCREEN:
|
case Action::CENTER_TO_SCREEN:
|
||||||
center(item, CenterType::SCREEN);
|
center(item, CenterType::SCREEN);
|
||||||
break;
|
break;
|
||||||
case MacroActionSceneTransform::Action::CENTER_VERTICALLY:
|
case Action::CENTER_VERTICALLY:
|
||||||
center(item, CenterType::VERTICAL);
|
center(item, CenterType::VERTICAL);
|
||||||
break;
|
break;
|
||||||
case MacroActionSceneTransform::Action::CENTER_HORIZONTALLY:
|
case Action::CENTER_HORIZONTALLY:
|
||||||
center(item, CenterType::HORIZONTAL);
|
center(item, CenterType::HORIZONTAL);
|
||||||
break;
|
break;
|
||||||
case MacroActionSceneTransform::Action::MANUAL_TRANSFORM:
|
case Action::MANUAL_TRANSFORM:
|
||||||
ApplySettings(_settings); // Resolves variables
|
ApplySettings(_transformString); // Resolves variables
|
||||||
obs_sceneitem_defer_update_begin(item);
|
obs_sceneitem_defer_update_begin(item);
|
||||||
obs_sceneitem_set_info(item, &_info);
|
obs_sceneitem_set_info(item, &_info);
|
||||||
obs_sceneitem_set_crop(item, &_crop);
|
obs_sceneitem_set_crop(item, &_crop);
|
||||||
obs_sceneitem_defer_update_end(item);
|
obs_sceneitem_defer_update_end(item);
|
||||||
break;
|
break;
|
||||||
|
case Action::SINGLE_TRANSFORM_SETTING:
|
||||||
|
SetTransformSetting(item, _settingSelection,
|
||||||
|
_singleSettingsValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +275,9 @@ bool MacroActionSceneTransform::Save(obs_data_t *obj) const
|
|||||||
_scene.Save(obj);
|
_scene.Save(obj);
|
||||||
_source.Save(obj);
|
_source.Save(obj);
|
||||||
_rotation.Save(obj, "rotation");
|
_rotation.Save(obj, "rotation");
|
||||||
_settings.Save(obj, "settings");
|
_transformString.Save(obj, "settings");
|
||||||
|
_singleSettingsValue.Save(obj, "singleSettingsValue");
|
||||||
|
_settingSelection.Save(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,13 +300,15 @@ bool MacroActionSceneTransform::Load(obs_data_t *obj)
|
|||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_source.Load(obj);
|
_source.Load(obj);
|
||||||
_rotation.Load(obj, "rotation");
|
_rotation.Load(obj, "rotation");
|
||||||
_settings.Load(obj, "settings");
|
_transformString.Load(obj, "settings");
|
||||||
|
_singleSettingsValue.Load(obj, "singleSettingsValue");
|
||||||
|
_settingSelection.Load(obj);
|
||||||
|
|
||||||
// Convert old data format
|
// Convert old data format
|
||||||
// TODO: Remove in future version
|
// TODO: Remove in future version
|
||||||
if (!obs_data_has_user_value(obj, "settings")) {
|
if (!obs_data_has_user_value(obj, "settings")) {
|
||||||
LoadTransformState(obj, _info, _crop);
|
LoadTransformState(obj, _info, _crop);
|
||||||
_settings = ConvertSettings();
|
_transformString = ConvertSettings();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -326,8 +335,9 @@ void MacroActionSceneTransform::ResolveVariablesToFixedValues()
|
|||||||
{
|
{
|
||||||
_scene.ResolveVariables();
|
_scene.ResolveVariables();
|
||||||
_source.ResolveVariables();
|
_source.ResolveVariables();
|
||||||
_settings.ResolveVariables();
|
_transformString.ResolveVariables();
|
||||||
_rotation.ResolveVariables();
|
_rotation.ResolveVariables();
|
||||||
|
_singleSettingsValue.ResolveVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MacroActionSceneTransform::ConvertSettings()
|
std::string MacroActionSceneTransform::ConvertSettings()
|
||||||
@@ -381,9 +391,13 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
|
|||||||
_sources(new SceneItemSelectionWidget(parent)),
|
_sources(new SceneItemSelectionWidget(parent)),
|
||||||
_action(new QComboBox()),
|
_action(new QComboBox()),
|
||||||
_rotation(new VariableDoubleSpinBox()),
|
_rotation(new VariableDoubleSpinBox()),
|
||||||
_getSettings(new QPushButton(obs_module_text(
|
_getTransform(new QPushButton(obs_module_text(
|
||||||
"AdvSceneSwitcher.action.sceneTransform.getTransform"))),
|
"AdvSceneSwitcher.action.sceneTransform.getTransform"))),
|
||||||
_settings(new VariableTextEdit(this)),
|
_getCurrentValue(new QPushButton(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.sceneTransform.getCurrentValue"))),
|
||||||
|
_transformString(new VariableTextEdit(this)),
|
||||||
|
_settingSelection(new TransformSettingSelection(this)),
|
||||||
|
_singleSettingValue(new VariableLineEdit(this)),
|
||||||
_buttonLayout(new QHBoxLayout())
|
_buttonLayout(new QHBoxLayout())
|
||||||
{
|
{
|
||||||
_rotation->setMinimum(-360);
|
_rotation->setMinimum(-360);
|
||||||
@@ -405,28 +419,40 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
|
|||||||
_rotation,
|
_rotation,
|
||||||
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
||||||
this, SLOT(RotationChanged(const NumberVariable<double> &)));
|
this, SLOT(RotationChanged(const NumberVariable<double> &)));
|
||||||
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
QWidget::connect(_getTransform, SIGNAL(clicked()), this,
|
||||||
SLOT(GetSettingsClicked()));
|
SLOT(GetSettingsClicked()));
|
||||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
QWidget::connect(_getCurrentValue, SIGNAL(clicked()), this,
|
||||||
SLOT(SettingsChanged()));
|
SLOT(GetCurrentValueClicked()));
|
||||||
|
QWidget::connect(_transformString, SIGNAL(textChanged()), this,
|
||||||
|
SLOT(TransformStringChanged()));
|
||||||
|
QWidget::connect(_singleSettingValue, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(SettingValueChanged()));
|
||||||
|
QWidget::connect(
|
||||||
|
_settingSelection,
|
||||||
|
SIGNAL(SelectionChanged(const TransformSetting &)), this,
|
||||||
|
SLOT(SettingSelectionChanged(const TransformSetting &)));
|
||||||
|
|
||||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||||
|
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{scenes}}", _scenes}, {"{{rotation}}", _rotation},
|
{"{{scenes}}", _scenes},
|
||||||
{"{{sources}}", _sources}, {"{{action}}", _action},
|
{"{{rotation}}", _rotation},
|
||||||
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
|
{"{{sources}}", _sources},
|
||||||
|
{"{{action}}", _action},
|
||||||
|
{"{{settingSelection}}", _settingSelection},
|
||||||
|
{"{{singleSettingValue}}", _singleSettingValue},
|
||||||
};
|
};
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text("AdvSceneSwitcher.action.sceneTransform.entry"),
|
obs_module_text("AdvSceneSwitcher.action.sceneTransform.entry"),
|
||||||
entryLayout, widgetPlaceholders);
|
entryLayout, widgetPlaceholders);
|
||||||
|
|
||||||
_buttonLayout->addWidget(_getSettings);
|
_buttonLayout->addWidget(_getTransform);
|
||||||
|
_buttonLayout->addWidget(_getCurrentValue);
|
||||||
_buttonLayout->addStretch();
|
_buttonLayout->addStretch();
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
auto mainLayout = new QVBoxLayout;
|
||||||
mainLayout->addLayout(entryLayout);
|
mainLayout->addLayout(entryLayout);
|
||||||
mainLayout->addWidget(_settings);
|
mainLayout->addWidget(_transformString);
|
||||||
mainLayout->addLayout(_buttonLayout);
|
mainLayout->addLayout(_buttonLayout);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
@@ -446,28 +472,31 @@ void MacroActionSceneTransformEdit::UpdateEntryData()
|
|||||||
_action->setCurrentIndex(
|
_action->setCurrentIndex(
|
||||||
_action->findData(static_cast<int>(_entryData->_action)));
|
_action->findData(static_cast<int>(_entryData->_action)));
|
||||||
_rotation->SetValue(_entryData->_rotation);
|
_rotation->SetValue(_entryData->_rotation);
|
||||||
_settings->setPlainText(_entryData->_settings);
|
_transformString->setPlainText(_entryData->_transformString);
|
||||||
|
UpdateSettingSelection();
|
||||||
|
_settingSelection->SetSetting(_entryData->_settingSelection);
|
||||||
|
_singleSettingValue->setText(_entryData->_singleSettingsValue);
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
void MacroActionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
{
|
||||||
return;
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_scene = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock = LockContext();
|
UpdateSettingSelection();
|
||||||
_entryData->_scene = s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionSceneTransformEdit::SourceChanged(const SceneItemSelection &item)
|
void MacroActionSceneTransformEdit::SourceChanged(const SceneItemSelection &item)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
{
|
||||||
return;
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_source = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock = LockContext();
|
UpdateSettingSelection();
|
||||||
_entryData->_source = item;
|
|
||||||
emit HeaderInfoChanged(
|
emit HeaderInfoChanged(
|
||||||
QString::fromStdString(_entryData->GetShortDesc()));
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
adjustSize();
|
adjustSize();
|
||||||
@@ -476,11 +505,7 @@ void MacroActionSceneTransformEdit::SourceChanged(const SceneItemSelection &item
|
|||||||
|
|
||||||
void MacroActionSceneTransformEdit::ActionChanged(int idx)
|
void MacroActionSceneTransformEdit::ActionChanged(int idx)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_action = static_cast<MacroActionSceneTransform::Action>(
|
_entryData->_action = static_cast<MacroActionSceneTransform::Action>(
|
||||||
_action->itemData(idx).toInt());
|
_action->itemData(idx).toInt());
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
@@ -488,11 +513,7 @@ void MacroActionSceneTransformEdit::ActionChanged(int idx)
|
|||||||
|
|
||||||
void MacroActionSceneTransformEdit::RotationChanged(const DoubleVariable &value)
|
void MacroActionSceneTransformEdit::RotationChanged(const DoubleVariable &value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_rotation = value;
|
_entryData->_rotation = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,17 +529,52 @@ void MacroActionSceneTransformEdit::GetSettingsClicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto settings = GetSceneItemTransform(items[0]);
|
auto settings = GetSceneItemTransform(items[0]);
|
||||||
_settings->setPlainText(FormatJsonString(settings));
|
_transformString->setPlainText(FormatJsonString(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionSceneTransformEdit::SettingsChanged()
|
void MacroActionSceneTransformEdit::GetCurrentValueClicked()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock = LockContext();
|
auto items = _entryData->_source.GetSceneItems(_entryData->_scene);
|
||||||
_entryData->_settings = _settings->toPlainText().toStdString();
|
if (items.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto value = GetTransformSettingValue(items.at(0),
|
||||||
|
_entryData->_settingSelection);
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_singleSettingValue->setText(QString::fromStdString(*value));
|
||||||
|
SettingValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneTransformEdit::TransformStringChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_transformString =
|
||||||
|
_transformString->toPlainText().toStdString();
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneTransformEdit::SettingSelectionChanged(
|
||||||
|
const TransformSetting &setting)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_settingSelection = setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneTransformEdit::SettingValueChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_singleSettingsValue =
|
||||||
|
_singleSettingValue->text().toStdString();
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
@@ -530,13 +586,39 @@ void MacroActionSceneTransformEdit::SetWidgetVisibility()
|
|||||||
_buttonLayout,
|
_buttonLayout,
|
||||||
_entryData->_action ==
|
_entryData->_action ==
|
||||||
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
|
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
|
||||||
_settings->setVisible(
|
_transformString->setVisible(
|
||||||
_entryData->_action ==
|
_entryData->_action ==
|
||||||
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
|
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
|
||||||
_rotation->setVisible(_entryData->_action ==
|
_rotation->setVisible(_entryData->_action ==
|
||||||
MacroActionSceneTransform::Action::ROTATE);
|
MacroActionSceneTransform::Action::ROTATE);
|
||||||
|
_getTransform->setVisible(
|
||||||
|
_entryData->_action ==
|
||||||
|
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
|
||||||
|
_getCurrentValue->setVisible(
|
||||||
|
_entryData->_action ==
|
||||||
|
MacroActionSceneTransform::Action::SINGLE_TRANSFORM_SETTING);
|
||||||
|
_settingSelection->setVisible(
|
||||||
|
_entryData->_action ==
|
||||||
|
MacroActionSceneTransform::Action::SINGLE_TRANSFORM_SETTING);
|
||||||
|
_singleSettingValue->setVisible(
|
||||||
|
_entryData->_action ==
|
||||||
|
MacroActionSceneTransform::Action::SINGLE_TRANSFORM_SETTING);
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneTransformEdit::UpdateSettingSelection() const
|
||||||
|
{
|
||||||
|
if (!_entryData) {
|
||||||
|
_settingSelection->SetSource(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto items = _entryData->_source.GetSceneItems(_entryData->_scene);
|
||||||
|
if (items.empty()) {
|
||||||
|
_settingSelection->SetSource(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_settingSelection->SetSource(items.at(0));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "macro-action-edit.hpp"
|
#include "macro-action-edit.hpp"
|
||||||
#include "scene-selection.hpp"
|
#include "scene-selection.hpp"
|
||||||
#include "scene-item-selection.hpp"
|
#include "scene-item-selection.hpp"
|
||||||
|
#include "transform-setting.hpp"
|
||||||
#include "variable-text-edit.hpp"
|
#include "variable-text-edit.hpp"
|
||||||
#include "variable-spinbox.hpp"
|
#include "variable-spinbox.hpp"
|
||||||
|
|
||||||
@@ -33,13 +34,16 @@ public:
|
|||||||
CENTER_VERTICALLY,
|
CENTER_VERTICALLY,
|
||||||
CENTER_HORIZONTALLY,
|
CENTER_HORIZONTALLY,
|
||||||
MANUAL_TRANSFORM = 100,
|
MANUAL_TRANSFORM = 100,
|
||||||
|
SINGLE_TRANSFORM_SETTING,
|
||||||
};
|
};
|
||||||
|
|
||||||
Action _action = Action::RESET;
|
Action _action = Action::RESET;
|
||||||
SceneSelection _scene;
|
SceneSelection _scene;
|
||||||
SceneItemSelection _source;
|
SceneItemSelection _source;
|
||||||
StringVariable _settings = "";
|
StringVariable _transformString = "";
|
||||||
DoubleVariable _rotation = 90.0;
|
DoubleVariable _rotation = 90.0;
|
||||||
|
StringVariable _singleSettingsValue = "";
|
||||||
|
TransformSetting _settingSelection;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Transform(obs_scene_item *);
|
void Transform(obs_scene_item *);
|
||||||
@@ -76,19 +80,26 @@ private slots:
|
|||||||
void ActionChanged(int);
|
void ActionChanged(int);
|
||||||
void RotationChanged(const NumberVariable<double> &value);
|
void RotationChanged(const NumberVariable<double> &value);
|
||||||
void GetSettingsClicked();
|
void GetSettingsClicked();
|
||||||
void SettingsChanged();
|
void GetCurrentValueClicked();
|
||||||
|
void TransformStringChanged();
|
||||||
|
void SettingSelectionChanged(const TransformSetting &);
|
||||||
|
void SettingValueChanged();
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetWidgetVisibility();
|
void SetWidgetVisibility();
|
||||||
|
void UpdateSettingSelection() const;
|
||||||
|
|
||||||
SceneSelectionWidget *_scenes;
|
SceneSelectionWidget *_scenes;
|
||||||
SceneItemSelectionWidget *_sources;
|
SceneItemSelectionWidget *_sources;
|
||||||
QComboBox *_action;
|
QComboBox *_action;
|
||||||
VariableDoubleSpinBox *_rotation;
|
VariableDoubleSpinBox *_rotation;
|
||||||
QPushButton *_getSettings;
|
QPushButton *_getTransform;
|
||||||
VariableTextEdit *_settings;
|
QPushButton *_getCurrentValue;
|
||||||
|
VariableTextEdit *_transformString;
|
||||||
|
TransformSettingSelection *_settingSelection;
|
||||||
|
VariableLineEdit *_singleSettingValue;
|
||||||
QHBoxLayout *_buttonLayout;
|
QHBoxLayout *_buttonLayout;
|
||||||
|
|
||||||
std::shared_ptr<MacroActionSceneTransform> _entryData;
|
std::shared_ptr<MacroActionSceneTransform> _entryData;
|
||||||
|
|||||||
@@ -52,16 +52,6 @@ static const std::map<MacroConditionMedia::State, std::string> mediaStates = {
|
|||||||
"AdvSceneSwitcher.mediaTab.states.any"},
|
"AdvSceneSwitcher.mediaTab.states.any"},
|
||||||
};
|
};
|
||||||
|
|
||||||
MacroConditionMedia::~MacroConditionMedia()
|
|
||||||
{
|
|
||||||
OBSSourceAutoRelease mediasource =
|
|
||||||
obs_weak_source_get_source(_source.GetSource());
|
|
||||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
|
||||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
|
||||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
|
||||||
signal_handler_disconnect(sh, "media_next", MediaNext, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MacroConditionMedia::CheckTime()
|
bool MacroConditionMedia::CheckTime()
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease s =
|
OBSSourceAutoRelease s =
|
||||||
@@ -71,7 +61,7 @@ bool MacroConditionMedia::CheckTime()
|
|||||||
|
|
||||||
bool match = false;
|
bool match = false;
|
||||||
|
|
||||||
switch (_restriction) {
|
switch (_timeRestriction) {
|
||||||
case Time::TIME_RESTRICTION_NONE:
|
case Time::TIME_RESTRICTION_NONE:
|
||||||
match = true;
|
match = true;
|
||||||
break;
|
break;
|
||||||
@@ -152,7 +142,21 @@ bool MacroConditionMedia::CheckMediaMatch()
|
|||||||
if (!_source.GetSource()) {
|
if (!_source.GetSource()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool matched = CheckState() && CheckTime();
|
|
||||||
|
bool matched = false;
|
||||||
|
switch (_checkType) {
|
||||||
|
case CheckType::STATE:
|
||||||
|
matched = CheckState();
|
||||||
|
break;
|
||||||
|
case CheckType::TIME:
|
||||||
|
matched = CheckTime();
|
||||||
|
break;
|
||||||
|
case CheckType::LEGACY:
|
||||||
|
matched = CheckState() && CheckTime();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// reset for next check
|
// reset for next check
|
||||||
_stopped = false;
|
_stopped = false;
|
||||||
@@ -168,16 +172,50 @@ void MacroConditionMedia::HandleSceneChange()
|
|||||||
_lastConfigureScene = GetCurrentScene();
|
_lastConfigureScene = GetCurrentScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MacroConditionMedia::MacroConditionMedia(const MacroConditionMedia &other)
|
||||||
|
: MacroCondition(other.GetMacro()),
|
||||||
|
_sourceType(other._sourceType),
|
||||||
|
_checkType(other._checkType),
|
||||||
|
_state(other._state),
|
||||||
|
_timeRestriction(other._timeRestriction),
|
||||||
|
_scene(other._scene),
|
||||||
|
_source(other._source),
|
||||||
|
_rawSource(other._rawSource),
|
||||||
|
_sourceGroup(),
|
||||||
|
_time(other._time),
|
||||||
|
_lastConfigureScene(other._lastConfigureScene)
|
||||||
|
{
|
||||||
|
ResetSignalHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroConditionMedia &
|
||||||
|
MacroConditionMedia::operator=(const MacroConditionMedia &other)
|
||||||
|
{
|
||||||
|
_sourceType = other._sourceType;
|
||||||
|
_checkType = other._checkType;
|
||||||
|
_state = other._state;
|
||||||
|
_timeRestriction = other._timeRestriction;
|
||||||
|
_scene = other._scene;
|
||||||
|
_source = other._source;
|
||||||
|
_rawSource = other._rawSource;
|
||||||
|
_time = other._time;
|
||||||
|
_lastConfigureScene = other._lastConfigureScene;
|
||||||
|
|
||||||
|
ResetSignalHandler();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
bool MacroConditionMedia::CheckCondition()
|
bool MacroConditionMedia::CheckCondition()
|
||||||
{
|
{
|
||||||
bool match = false;
|
bool match = false;
|
||||||
switch (_sourceType) {
|
switch (_sourceType) {
|
||||||
case Type::ANY:
|
case SourceType::ANY:
|
||||||
for (auto &source : _sourceGroup) {
|
for (auto &source : _sourceGroup) {
|
||||||
match = match || source.CheckCondition();
|
match = match || source.CheckCondition();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::ALL: {
|
case SourceType::ALL: {
|
||||||
bool res = true;
|
bool res = true;
|
||||||
for (auto &source : _sourceGroup) {
|
for (auto &source : _sourceGroup) {
|
||||||
res = res && source.CheckCondition();
|
res = res && source.CheckCondition();
|
||||||
@@ -185,7 +223,7 @@ bool MacroConditionMedia::CheckCondition()
|
|||||||
match = res;
|
match = res;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::SOURCE:
|
case SourceType::SOURCE:
|
||||||
match = CheckMediaMatch();
|
match = CheckMediaMatch();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -205,10 +243,12 @@ bool MacroConditionMedia::Save(obs_data_t *obj) const
|
|||||||
_source.Save(obj);
|
_source.Save(obj);
|
||||||
_scene.Save(obj);
|
_scene.Save(obj);
|
||||||
obs_data_set_int(obj, "sourceType", static_cast<int>(_sourceType));
|
obs_data_set_int(obj, "sourceType", static_cast<int>(_sourceType));
|
||||||
|
obs_data_set_int(obj, "checkType", static_cast<int>(_checkType));
|
||||||
obs_data_set_int(obj, "state", static_cast<int>(_state));
|
obs_data_set_int(obj, "state", static_cast<int>(_state));
|
||||||
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
|
obs_data_set_int(obj, "restriction",
|
||||||
|
static_cast<int>(_timeRestriction));
|
||||||
_time.Save(obj);
|
_time.Save(obj);
|
||||||
obs_data_set_int(obj, "version", 0);
|
obs_data_set_int(obj, "version", 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +285,7 @@ void MacroConditionMedia::UpdateMediaSourcesOfSceneList()
|
|||||||
|
|
||||||
for (auto &source : mediaSources) {
|
for (auto &source : mediaSources) {
|
||||||
MacroConditionMedia cond(*this);
|
MacroConditionMedia cond(*this);
|
||||||
cond._sourceType = Type::SOURCE;
|
cond._sourceType = SourceType::SOURCE;
|
||||||
cond._source.SetSource(source);
|
cond._source.SetSource(source);
|
||||||
_sourceGroup.push_back(cond);
|
_sourceGroup.push_back(cond);
|
||||||
}
|
}
|
||||||
@@ -256,28 +296,31 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
|
|||||||
MacroCondition::Load(obj);
|
MacroCondition::Load(obj);
|
||||||
_source.Load(obj);
|
_source.Load(obj);
|
||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_sourceType = static_cast<Type>(obs_data_get_int(obj, "sourceType"));
|
_sourceType =
|
||||||
_state = static_cast<MacroConditionMedia::State>(
|
static_cast<SourceType>(obs_data_get_int(obj, "sourceType"));
|
||||||
obs_data_get_int(obj, "state"));
|
_state = static_cast<State>(obs_data_get_int(obj, "state"));
|
||||||
_restriction = static_cast<MacroConditionMedia::Time>(
|
_checkType = static_cast<CheckType>(obs_data_get_int(obj, "checkType"));
|
||||||
obs_data_get_int(obj, "restriction"));
|
_timeRestriction =
|
||||||
|
static_cast<Time>(obs_data_get_int(obj, "restriction"));
|
||||||
_time.Load(obj);
|
_time.Load(obj);
|
||||||
|
|
||||||
if (_sourceType == Type::SOURCE) {
|
if (_sourceType == SourceType::SOURCE) {
|
||||||
OBSSourceAutoRelease mediasource =
|
ResetSignalHandler();
|
||||||
obs_weak_source_get_source(_source.GetSource());
|
|
||||||
signal_handler_t *sh =
|
|
||||||
obs_source_get_signal_handler(mediasource);
|
|
||||||
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
|
|
||||||
signal_handler_connect(sh, "media_ended", MediaEnded, this);
|
|
||||||
signal_handler_connect(sh, "media_next", MediaNext, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateMediaSourcesOfSceneList();
|
UpdateMediaSourcesOfSceneList();
|
||||||
if (!obs_data_has_user_value(obj, "version")) {
|
if (!obs_data_has_user_value(obj, "version")) {
|
||||||
if (_state ==
|
if (_state == State::OBS_MEDIA_STATE_ENDED) {
|
||||||
MacroConditionMedia::State::OBS_MEDIA_STATE_ENDED) {
|
_state = State::PLAYLIST_ENDED;
|
||||||
_state = MacroConditionMedia::State::PLAYLIST_ENDED;
|
}
|
||||||
|
}
|
||||||
|
if (obs_data_get_int(obj, "version") < 1) {
|
||||||
|
if (IsUsingLegacyCheck()) {
|
||||||
|
_checkType = CheckType::LEGACY;
|
||||||
|
} else if (_state == State::ANY) {
|
||||||
|
_checkType = CheckType::TIME;
|
||||||
|
} else {
|
||||||
|
_checkType = CheckType::STATE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -286,16 +329,16 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
|
|||||||
std::string MacroConditionMedia::GetShortDesc() const
|
std::string MacroConditionMedia::GetShortDesc() const
|
||||||
{
|
{
|
||||||
switch (_sourceType) {
|
switch (_sourceType) {
|
||||||
case Type::SOURCE:
|
case SourceType::SOURCE:
|
||||||
return _source.ToString();
|
return _source.ToString();
|
||||||
case Type::ANY:
|
case SourceType::ANY:
|
||||||
if (_scene.GetScene(false)) {
|
if (_scene.GetScene(false)) {
|
||||||
return obs_module_text(
|
return obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.media.anyOnScene") +
|
"AdvSceneSwitcher.condition.media.anyOnScene") +
|
||||||
std::string(" ") + _scene.ToString();
|
std::string(" ") + _scene.ToString();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::ALL:
|
case SourceType::ALL:
|
||||||
if (_scene.GetScene(false)) {
|
if (_scene.GetScene(false)) {
|
||||||
return obs_module_text(
|
return obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.media.allOnScene") +
|
"AdvSceneSwitcher.condition.media.allOnScene") +
|
||||||
@@ -308,27 +351,21 @@ std::string MacroConditionMedia::GetShortDesc() const
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionMedia::ClearSignalHandler()
|
bool MacroConditionMedia::IsUsingLegacyCheck() const
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease mediasource =
|
return _state != State::ANY &&
|
||||||
obs_weak_source_get_source(_source.GetSource());
|
_timeRestriction != Time::TIME_RESTRICTION_NONE;
|
||||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
|
||||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
|
||||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
|
||||||
signal_handler_disconnect(sh, "media_next", MediaNext, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionMedia::ResetSignalHandler()
|
void MacroConditionMedia::ResetSignalHandler()
|
||||||
{
|
{
|
||||||
|
_signals.clear();
|
||||||
OBSSourceAutoRelease mediasource =
|
OBSSourceAutoRelease mediasource =
|
||||||
obs_weak_source_get_source(_source.GetSource());
|
obs_weak_source_get_source(_source.GetSource());
|
||||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
_signals.emplace_back(sh, "media_stopped", MediaStopped, this);
|
||||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
_signals.emplace_back(sh, "media_ended", MediaEnded, this);
|
||||||
signal_handler_disconnect(sh, "media_next", MediaNext, this);
|
_signals.emplace_back(sh, "media_next", MediaNext, this);
|
||||||
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
|
|
||||||
signal_handler_connect(sh, "media_ended", MediaEnded, this);
|
|
||||||
signal_handler_connect(sh, "media_next", MediaNext, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionMedia::MediaStopped(void *data, calldata_t *)
|
void MacroConditionMedia::MediaStopped(void *data, calldata_t *)
|
||||||
@@ -361,17 +398,28 @@ void MacroConditionMedia::MediaNext(void *data, calldata_t *)
|
|||||||
media->_next = true;
|
media->_next = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void populateMediaTimes(QComboBox *list)
|
static void populateSateSelection(QComboBox *list, bool addLegacyEntries)
|
||||||
{
|
{
|
||||||
for (auto entry : mediaTimeRestrictions) {
|
for (const auto &[value, name] : mediaStates) {
|
||||||
list->addItem(obs_module_text(entry.second.c_str()));
|
if (value == MacroConditionMedia::State::ANY &&
|
||||||
|
!addLegacyEntries) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list->addItem(obs_module_text(name.c_str()),
|
||||||
|
static_cast<int>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void populateMediaStates(QComboBox *list)
|
static void populateTimeRestrictionSelection(QComboBox *list,
|
||||||
|
bool addLegacyEntries)
|
||||||
{
|
{
|
||||||
for (auto entry : mediaStates) {
|
for (const auto &[value, name] : mediaTimeRestrictions) {
|
||||||
list->addItem(obs_module_text(entry.second.c_str()));
|
if (value == MacroConditionMedia::Time::TIME_RESTRICTION_NONE &&
|
||||||
|
!addLegacyEntries) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list->addItem(obs_module_text(name.c_str()),
|
||||||
|
static_cast<int>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,19 +428,33 @@ static void populateSourceTypes(QComboBox *list)
|
|||||||
list->clear();
|
list->clear();
|
||||||
list->addItem(
|
list->addItem(
|
||||||
obs_module_text("AdvSceneSwitcher.condition.media.source"),
|
obs_module_text("AdvSceneSwitcher.condition.media.source"),
|
||||||
static_cast<int>(MacroConditionMedia::Type::SOURCE));
|
static_cast<int>(MacroConditionMedia::SourceType::SOURCE));
|
||||||
list->addItem(
|
list->addItem(
|
||||||
obs_module_text("AdvSceneSwitcher.condition.media.anyOnScene"),
|
obs_module_text("AdvSceneSwitcher.condition.media.anyOnScene"),
|
||||||
static_cast<int>(MacroConditionMedia::Type::ANY));
|
static_cast<int>(MacroConditionMedia::SourceType::ANY));
|
||||||
list->addItem(
|
list->addItem(
|
||||||
obs_module_text("AdvSceneSwitcher.condition.media.allOnScene"),
|
obs_module_text("AdvSceneSwitcher.condition.media.allOnScene"),
|
||||||
static_cast<int>(MacroConditionMedia::Type::ALL));
|
static_cast<int>(MacroConditionMedia::SourceType::ALL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void populateCheckTypes(QComboBox *list)
|
||||||
|
{
|
||||||
|
list->clear();
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.media.checkType.state"),
|
||||||
|
static_cast<int>(MacroConditionMedia::CheckType::STATE));
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.media.checkType.time"),
|
||||||
|
static_cast<int>(MacroConditionMedia::CheckType::TIME));
|
||||||
}
|
}
|
||||||
|
|
||||||
MacroConditionMediaEdit::MacroConditionMediaEdit(
|
MacroConditionMediaEdit::MacroConditionMediaEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData)
|
QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
_sourceTypes(new QComboBox()),
|
_sourceTypes(new QComboBox()),
|
||||||
|
_checkTypes(new QComboBox()),
|
||||||
_scenes(new SceneSelectionWidget(window(), true, true, true, true,
|
_scenes(new SceneSelectionWidget(window(), true, true, true, true,
|
||||||
true)),
|
true)),
|
||||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||||
@@ -409,6 +471,8 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
|
|||||||
|
|
||||||
QWidget::connect(_sourceTypes, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_sourceTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(SourceTypeChanged(int)));
|
SLOT(SourceTypeChanged(int)));
|
||||||
|
QWidget::connect(_checkTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(CheckTypeChanged(int)));
|
||||||
QWidget::connect(_sources,
|
QWidget::connect(_sources,
|
||||||
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
||||||
SLOT(SourceChanged(const SourceSelection &)));
|
SLOT(SourceChanged(const SourceSelection &)));
|
||||||
@@ -422,20 +486,28 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
|
|||||||
SLOT(TimeChanged(const Duration &)));
|
SLOT(TimeChanged(const Duration &)));
|
||||||
|
|
||||||
populateSourceTypes(_sourceTypes);
|
populateSourceTypes(_sourceTypes);
|
||||||
populateMediaStates(_states);
|
populateCheckTypes(_checkTypes);
|
||||||
populateMediaTimes(_timeRestrictions);
|
const bool isUsingLegacyCheck = entryData->_checkType ==
|
||||||
|
MacroConditionMedia::CheckType::LEGACY;
|
||||||
|
populateSateSelection(_states, isUsingLegacyCheck);
|
||||||
|
populateTimeRestrictionSelection(_timeRestrictions, isUsingLegacyCheck);
|
||||||
|
|
||||||
auto layout = new QHBoxLayout;
|
auto layout = new QHBoxLayout;
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{sourceTypes}}", _sourceTypes},
|
{"{{sourceTypes}}", _sourceTypes},
|
||||||
|
{"{{checkTypes}}", _checkTypes},
|
||||||
{"{{mediaSources}}", _sources},
|
{"{{mediaSources}}", _sources},
|
||||||
{"{{scenes}}", _scenes},
|
{"{{scenes}}", _scenes},
|
||||||
{"{{states}}", _states},
|
{"{{states}}", _states},
|
||||||
{"{{timeRestrictions}}", _timeRestrictions},
|
{"{{timeRestrictions}}", _timeRestrictions},
|
||||||
{"{{time}}", _time},
|
{"{{time}}", _time},
|
||||||
};
|
};
|
||||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.media.entry"),
|
PlaceWidgets(
|
||||||
layout, widgetPlaceholders);
|
obs_module_text(
|
||||||
|
isUsingLegacyCheck
|
||||||
|
? "AdvSceneSwitcher.condition.media.layout.legacy"
|
||||||
|
: "AdvSceneSwitcher.condition.media.layout"),
|
||||||
|
layout, widgetPlaceholders);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
@@ -445,15 +517,12 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
|
|||||||
|
|
||||||
void MacroConditionMediaEdit::SourceTypeChanged(int idx)
|
void MacroConditionMediaEdit::SourceTypeChanged(int idx)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
_entryData->_sourceType = static_cast<MacroConditionMedia::SourceType>(
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_sourceType = static_cast<MacroConditionMedia::Type>(
|
|
||||||
_sourceTypes->itemData(idx).toInt());
|
_sourceTypes->itemData(idx).toInt());
|
||||||
|
|
||||||
if (_entryData->_sourceType == MacroConditionMedia::Type::SOURCE) {
|
if (_entryData->_sourceType ==
|
||||||
|
MacroConditionMedia::SourceType::SOURCE) {
|
||||||
_entryData->_sourceGroup.clear();
|
_entryData->_sourceGroup.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,16 +533,19 @@ void MacroConditionMediaEdit::SourceTypeChanged(int idx)
|
|||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionMediaEdit::CheckTypeChanged(int idx)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_checkType = static_cast<MacroConditionMedia::CheckType>(
|
||||||
|
_checkTypes->itemData(idx).toInt());
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
void MacroConditionMediaEdit::SourceChanged(const SourceSelection &source)
|
void MacroConditionMediaEdit::SourceChanged(const SourceSelection &source)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_sourceGroup.clear();
|
_entryData->_sourceGroup.clear();
|
||||||
_entryData->_sourceType = MacroConditionMedia::Type::SOURCE;
|
_entryData->_sourceType = MacroConditionMedia::SourceType::SOURCE;
|
||||||
_entryData->ClearSignalHandler();
|
|
||||||
_entryData->_source = source;
|
_entryData->_source = source;
|
||||||
_entryData->ResetSignalHandler();
|
_entryData->ResetSignalHandler();
|
||||||
emit HeaderInfoChanged(
|
emit HeaderInfoChanged(
|
||||||
@@ -483,40 +555,20 @@ void MacroConditionMediaEdit::SourceChanged(const SourceSelection &source)
|
|||||||
|
|
||||||
void MacroConditionMediaEdit::SceneChanged(const SceneSelection &s)
|
void MacroConditionMediaEdit::SceneChanged(const SceneSelection &s)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_scene = s;
|
_entryData->_scene = s;
|
||||||
_entryData->UpdateMediaSourcesOfSceneList();
|
_entryData->UpdateMediaSourcesOfSceneList();
|
||||||
emit HeaderInfoChanged(
|
emit HeaderInfoChanged(
|
||||||
QString::fromStdString(_entryData->GetShortDesc()));
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
MacroConditionMedia::State getMediaStateFromIdx(int idx)
|
|
||||||
{
|
|
||||||
if (idx < static_cast<int>(
|
|
||||||
MacroConditionMedia::State::LAST_OBS_MEDIA_STATE)) {
|
|
||||||
return static_cast<MacroConditionMedia::State>(idx);
|
|
||||||
} else {
|
|
||||||
return static_cast<MacroConditionMedia::State>(
|
|
||||||
idx -
|
|
||||||
static_cast<int>(
|
|
||||||
MacroConditionMedia::State::LAST_OBS_MEDIA_STATE) +
|
|
||||||
custom_media_states_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroConditionMediaEdit::StateChanged(int index)
|
void MacroConditionMediaEdit::StateChanged(int index)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
_entryData->_state = static_cast<MacroConditionMedia::State>(
|
||||||
}
|
_states->itemData(index).toInt());
|
||||||
|
if (_entryData->_sourceType !=
|
||||||
auto lock = LockContext();
|
MacroConditionMedia::SourceType::SOURCE) {
|
||||||
_entryData->_state = getMediaStateFromIdx(index);
|
|
||||||
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
|
|
||||||
_entryData->UpdateMediaSourcesOfSceneList();
|
_entryData->UpdateMediaSourcesOfSceneList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -527,7 +579,9 @@ void MacroConditionMediaEdit::TimeRestrictionChanged(int index)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<MacroConditionMedia::Time>(index) ==
|
const auto timeRestriction = static_cast<MacroConditionMedia::Time>(
|
||||||
|
_timeRestrictions->itemData(index).toInt());
|
||||||
|
if (timeRestriction ==
|
||||||
MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
|
MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
|
||||||
_time->setDisabled(true);
|
_time->setDisabled(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -535,9 +589,9 @@ void MacroConditionMediaEdit::TimeRestrictionChanged(int index)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto lock = LockContext();
|
auto lock = LockContext();
|
||||||
_entryData->_restriction =
|
_entryData->_timeRestriction = timeRestriction;
|
||||||
static_cast<MacroConditionMedia::Time>(index);
|
if (_entryData->_sourceType !=
|
||||||
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
|
MacroConditionMedia::SourceType::SOURCE) {
|
||||||
_entryData->UpdateMediaSourcesOfSceneList();
|
_entryData->UpdateMediaSourcesOfSceneList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -550,7 +604,8 @@ void MacroConditionMediaEdit::TimeChanged(const Duration &dur)
|
|||||||
|
|
||||||
auto lock = LockContext();
|
auto lock = LockContext();
|
||||||
_entryData->_time = dur;
|
_entryData->_time = dur;
|
||||||
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
|
if (_entryData->_sourceType !=
|
||||||
|
MacroConditionMedia::SourceType::SOURCE) {
|
||||||
_entryData->UpdateMediaSourcesOfSceneList();
|
_entryData->UpdateMediaSourcesOfSceneList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -558,20 +613,25 @@ void MacroConditionMediaEdit::TimeChanged(const Duration &dur)
|
|||||||
void MacroConditionMediaEdit::SetWidgetVisibility()
|
void MacroConditionMediaEdit::SetWidgetVisibility()
|
||||||
{
|
{
|
||||||
_sources->setVisible(_entryData->_sourceType ==
|
_sources->setVisible(_entryData->_sourceType ==
|
||||||
MacroConditionMedia::Type::SOURCE);
|
MacroConditionMedia::SourceType::SOURCE);
|
||||||
_scenes->setVisible(_entryData->_sourceType !=
|
_scenes->setVisible(_entryData->_sourceType !=
|
||||||
MacroConditionMedia::Type::SOURCE);
|
MacroConditionMedia::SourceType::SOURCE);
|
||||||
}
|
|
||||||
|
|
||||||
static int getIdxFromMediaState(MacroConditionMedia::State state)
|
if (_entryData->_checkType == MacroConditionMedia::CheckType::LEGACY) {
|
||||||
{
|
_checkTypes->hide();
|
||||||
if (state < MacroConditionMedia::State::LAST_OBS_MEDIA_STATE) {
|
return;
|
||||||
return static_cast<int>(state);
|
|
||||||
} else {
|
|
||||||
return static_cast<int>(state) - custom_media_states_offset +
|
|
||||||
static_cast<int>(
|
|
||||||
MacroConditionMedia::State::LAST_OBS_MEDIA_STATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_states->setVisible(_entryData->_checkType ==
|
||||||
|
MacroConditionMedia::CheckType::STATE);
|
||||||
|
|
||||||
|
_timeRestrictions->setVisible(_entryData->_checkType ==
|
||||||
|
MacroConditionMedia::CheckType::TIME);
|
||||||
|
_time->setVisible(_entryData->_checkType ==
|
||||||
|
MacroConditionMedia::CheckType::TIME);
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionMediaEdit::UpdateEntryData()
|
void MacroConditionMediaEdit::UpdateEntryData()
|
||||||
@@ -580,15 +640,18 @@ void MacroConditionMediaEdit::UpdateEntryData()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_checkTypes->setCurrentIndex(_checkTypes->findData(
|
||||||
|
static_cast<int>(_entryData->_checkType)));
|
||||||
_sourceTypes->setCurrentIndex(_sourceTypes->findData(
|
_sourceTypes->setCurrentIndex(_sourceTypes->findData(
|
||||||
static_cast<int>(_entryData->_sourceType)));
|
static_cast<int>(_entryData->_sourceType)));
|
||||||
_sources->SetSource(_entryData->_source);
|
_sources->SetSource(_entryData->_source);
|
||||||
_scenes->SetScene(_entryData->_scene);
|
_scenes->SetScene(_entryData->_scene);
|
||||||
_states->setCurrentIndex(getIdxFromMediaState(_entryData->_state));
|
_states->setCurrentIndex(
|
||||||
_timeRestrictions->setCurrentIndex(
|
_states->findData(static_cast<int>(_entryData->_state)));
|
||||||
static_cast<int>(_entryData->_restriction));
|
_timeRestrictions->setCurrentIndex(_timeRestrictions->findData(
|
||||||
|
static_cast<int>(_entryData->_timeRestriction)));
|
||||||
_time->SetDuration(_entryData->_time);
|
_time->SetDuration(_entryData->_time);
|
||||||
if (_entryData->_restriction ==
|
if (_entryData->_timeRestriction ==
|
||||||
MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
|
MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
|
||||||
_time->setDisabled(true);
|
_time->setDisabled(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,18 @@
|
|||||||
#include "source-selection.hpp"
|
#include "source-selection.hpp"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <obs.hpp>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
constexpr auto custom_media_states_offset = 100;
|
|
||||||
|
|
||||||
class MacroConditionMedia : public MacroCondition {
|
class MacroConditionMedia : public MacroCondition {
|
||||||
public:
|
public:
|
||||||
MacroConditionMedia(Macro *m) : MacroCondition(m) {}
|
MacroConditionMedia(Macro *m) : MacroCondition(m) {}
|
||||||
~MacroConditionMedia();
|
MacroConditionMedia(const MacroConditionMedia &);
|
||||||
|
MacroConditionMedia &operator=(const MacroConditionMedia &);
|
||||||
bool CheckCondition();
|
bool CheckCondition();
|
||||||
bool Save(obs_data_t *obj) const;
|
bool Save(obs_data_t *obj) const;
|
||||||
bool Load(obs_data_t *obj);
|
bool Load(obs_data_t *obj);
|
||||||
@@ -26,19 +26,17 @@ public:
|
|||||||
{
|
{
|
||||||
return std::make_shared<MacroConditionMedia>(m);
|
return std::make_shared<MacroConditionMedia>(m);
|
||||||
}
|
}
|
||||||
void ClearSignalHandler();
|
|
||||||
void ResetSignalHandler();
|
void ResetSignalHandler();
|
||||||
void UpdateMediaSourcesOfSceneList();
|
void UpdateMediaSourcesOfSceneList();
|
||||||
static void MediaStopped(void *data, calldata_t *);
|
static void MediaStopped(void *data, calldata_t *);
|
||||||
static void MediaEnded(void *data, calldata_t *);
|
static void MediaEnded(void *data, calldata_t *);
|
||||||
static void MediaNext(void *data, calldata_t *);
|
static void MediaNext(void *data, calldata_t *);
|
||||||
|
|
||||||
enum class Type {
|
enum class SourceType { SOURCE, ANY, ALL };
|
||||||
SOURCE,
|
SourceType _sourceType = SourceType::SOURCE;
|
||||||
ANY,
|
|
||||||
ALL,
|
enum class CheckType { STATE, TIME, LEGACY = 1000 };
|
||||||
};
|
CheckType _checkType = CheckType::STATE;
|
||||||
Type _sourceType = Type::SOURCE;
|
|
||||||
|
|
||||||
enum class State {
|
enum class State {
|
||||||
// OBS's internal states
|
// OBS's internal states
|
||||||
@@ -53,19 +51,19 @@ public:
|
|||||||
// Just a marker
|
// Just a marker
|
||||||
LAST_OBS_MEDIA_STATE,
|
LAST_OBS_MEDIA_STATE,
|
||||||
// states added for use in the plugin
|
// states added for use in the plugin
|
||||||
PLAYLIST_ENDED = custom_media_states_offset,
|
PLAYLIST_ENDED = 100,
|
||||||
ANY,
|
ANY
|
||||||
};
|
};
|
||||||
State _state = State::OBS_MEDIA_STATE_NONE;
|
State _state = State::OBS_MEDIA_STATE_PLAYING;
|
||||||
|
|
||||||
enum class Time {
|
enum class Time {
|
||||||
TIME_RESTRICTION_NONE,
|
TIME_RESTRICTION_NONE,
|
||||||
TIME_RESTRICTION_SHORTER,
|
TIME_RESTRICTION_SHORTER,
|
||||||
TIME_RESTRICTION_LONGER,
|
TIME_RESTRICTION_LONGER,
|
||||||
TIME_RESTRICTION_REMAINING_SHORTER,
|
TIME_RESTRICTION_REMAINING_SHORTER,
|
||||||
TIME_RESTRICTION_REMAINING_LONGER,
|
TIME_RESTRICTION_REMAINING_LONGER
|
||||||
};
|
};
|
||||||
Time _restriction = Time::TIME_RESTRICTION_NONE;
|
Time _timeRestriction = Time::TIME_RESTRICTION_REMAINING_SHORTER;
|
||||||
|
|
||||||
SceneSelection _scene;
|
SceneSelection _scene;
|
||||||
SourceSelection _source;
|
SourceSelection _source;
|
||||||
@@ -74,12 +72,15 @@ public:
|
|||||||
Duration _time;
|
Duration _time;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool IsUsingLegacyCheck() const;
|
||||||
bool CheckTime();
|
bool CheckTime();
|
||||||
bool CheckState();
|
bool CheckState();
|
||||||
bool CheckPlaylistEnd(const obs_media_state);
|
bool CheckPlaylistEnd(const obs_media_state);
|
||||||
bool CheckMediaMatch();
|
bool CheckMediaMatch();
|
||||||
void HandleSceneChange();
|
void HandleSceneChange();
|
||||||
|
|
||||||
|
std::vector<OBSSignal> _signals;
|
||||||
|
|
||||||
bool _stopped = false;
|
bool _stopped = false;
|
||||||
bool _ended = false;
|
bool _ended = false;
|
||||||
bool _next = false;
|
bool _next = false;
|
||||||
@@ -110,6 +111,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void CheckTypeChanged(int);
|
||||||
void SourceTypeChanged(int);
|
void SourceTypeChanged(int);
|
||||||
void SourceChanged(const SourceSelection &);
|
void SourceChanged(const SourceSelection &);
|
||||||
void SceneChanged(const SceneSelection &);
|
void SceneChanged(const SceneSelection &);
|
||||||
@@ -121,6 +123,7 @@ signals:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
QComboBox *_sourceTypes;
|
QComboBox *_sourceTypes;
|
||||||
|
QComboBox *_checkTypes;
|
||||||
SceneSelectionWidget *_scenes;
|
SceneSelectionWidget *_scenes;
|
||||||
SourceSelectionWidget *_sources;
|
SourceSelectionWidget *_sources;
|
||||||
QComboBox *_states;
|
QComboBox *_states;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public:
|
|||||||
ProcessConfig _procConfig;
|
ProcessConfig _procConfig;
|
||||||
bool _checkExitCode = true;
|
bool _checkExitCode = true;
|
||||||
int _exitCodeToCheck = 0;
|
int _exitCodeToCheck = 0;
|
||||||
Duration _timeout = Duration(0.1);
|
Duration _timeout = Duration(1.0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RunProcess();
|
void RunProcess();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "macro-condition-scene-transform.hpp"
|
#include "macro-condition-scene-transform.hpp"
|
||||||
#include "layout-helpers.hpp"
|
#include "layout-helpers.hpp"
|
||||||
#include "json-helpers.hpp"
|
#include "json-helpers.hpp"
|
||||||
|
#include "math-helpers.hpp"
|
||||||
#include "text-helpers.hpp"
|
#include "text-helpers.hpp"
|
||||||
#include "scene-item-transform-helpers.hpp"
|
#include "scene-item-transform-helpers.hpp"
|
||||||
|
|
||||||
@@ -15,12 +16,28 @@ bool MacroConditionSceneTransform::_registered =
|
|||||||
MacroConditionSceneTransformEdit::Create,
|
MacroConditionSceneTransformEdit::Create,
|
||||||
"AdvSceneSwitcher.condition.sceneTransform"});
|
"AdvSceneSwitcher.condition.sceneTransform"});
|
||||||
|
|
||||||
const static std::map<MacroConditionSceneTransform::CondType, std::string>
|
static const std::map<MacroConditionSceneTransform::SettingsType, std::string>
|
||||||
|
settingsTypes = {
|
||||||
|
{MacroConditionSceneTransform::SettingsType::ALL,
|
||||||
|
"AdvSceneSwitcher.condition.sceneTransform.settingsType.all"},
|
||||||
|
{MacroConditionSceneTransform::SettingsType::SINGLE,
|
||||||
|
"AdvSceneSwitcher.condition.sceneTransform.settingsType.single"},
|
||||||
|
};
|
||||||
|
static const std::map<MacroConditionSceneTransform::Compare, std::string>
|
||||||
|
compareTypes = {
|
||||||
|
{MacroConditionSceneTransform::Compare::LESS,
|
||||||
|
"AdvSceneSwitcher.condition.sceneTransform.compare.less"},
|
||||||
|
{MacroConditionSceneTransform::Compare::EQUAL,
|
||||||
|
"AdvSceneSwitcher.condition.sceneTransform.compare.equal"},
|
||||||
|
{MacroConditionSceneTransform::Compare::MORE,
|
||||||
|
"AdvSceneSwitcher.condition.sceneTransform.compare.more"},
|
||||||
|
};
|
||||||
|
static const std::map<MacroConditionSceneTransform::Condition, std::string>
|
||||||
conditionTypes = {
|
conditionTypes = {
|
||||||
{MacroConditionSceneTransform::CondType::MATCHES,
|
{MacroConditionSceneTransform::Condition::MATCHES,
|
||||||
"AdvSceneSwitcher.condition.sceneTransform.entry.type.matches"},
|
"AdvSceneSwitcher.condition.sceneTransform.condition.match"},
|
||||||
{MacroConditionSceneTransform::CondType::CHANGED,
|
{MacroConditionSceneTransform::Condition::CHANGED,
|
||||||
"AdvSceneSwitcher.condition.sceneTransform.entry.type.changed"},
|
"AdvSceneSwitcher.condition.sceneTransform.condition.changed"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool doesTransformOfAnySceneItemMatch(
|
static bool doesTransformOfAnySceneItemMatch(
|
||||||
@@ -64,21 +81,17 @@ didTransformOfAnySceneItemChange(const std::vector<OBSSceneItem> &items,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionSceneTransform::CheckCondition()
|
bool MacroConditionSceneTransform::CheckAllSettings(
|
||||||
|
const std::vector<OBSSceneItem> &items)
|
||||||
{
|
{
|
||||||
auto items = _source.GetSceneItems(_scene);
|
|
||||||
if (items.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string newVariable = "";
|
std::string newVariable = "";
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
switch (_type) {
|
switch (_condition) {
|
||||||
case CondType::MATCHES:
|
case Condition::MATCHES:
|
||||||
ret = doesTransformOfAnySceneItemMatch(items, _settings, _regex,
|
ret = doesTransformOfAnySceneItemMatch(items, _transformString,
|
||||||
newVariable);
|
_regex, newVariable);
|
||||||
break;
|
break;
|
||||||
case CondType::CHANGED:
|
case Condition::CHANGED:
|
||||||
ret = didTransformOfAnySceneItemChange(
|
ret = didTransformOfAnySceneItemChange(
|
||||||
items, _previousTransform, newVariable);
|
items, _previousTransform, newVariable);
|
||||||
break;
|
break;
|
||||||
@@ -88,17 +101,143 @@ bool MacroConditionSceneTransform::CheckCondition()
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetVariableValue(newVariable);
|
SetVariableValue(newVariable);
|
||||||
|
SetTempVarValue("settings", newVariable);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MacroConditionSceneTransform::AnySceneItemTransformSettingChanged(
|
||||||
|
const std::vector<OBSSceneItem> &items)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (_previousSettingValues.size() < items.size()) {
|
||||||
|
_previousSettingValues.resize(items.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> varValues;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < items.size(); i++) {
|
||||||
|
const auto &item = items[i];
|
||||||
|
auto &previousValue = _previousSettingValues[i];
|
||||||
|
|
||||||
|
const auto currentValue =
|
||||||
|
GetTransformSettingValue(item, _setting);
|
||||||
|
if (!currentValue) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
varValues.emplace_back(*currentValue);
|
||||||
|
|
||||||
|
if (*currentValue == previousValue) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
previousValue = *currentValue;
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTempVarHelper(varValues);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool compareValue(MacroConditionSceneTransform::Compare compareMethod,
|
||||||
|
double value1, double value2)
|
||||||
|
{
|
||||||
|
switch (compareMethod) {
|
||||||
|
case MacroConditionSceneTransform::Compare::LESS:
|
||||||
|
return value1 < value2;
|
||||||
|
case MacroConditionSceneTransform::Compare::EQUAL:
|
||||||
|
return DoubleEquals(value1, value2, 0.00001);
|
||||||
|
case MacroConditionSceneTransform::Compare::MORE:
|
||||||
|
return value1 > value2;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionSceneTransform::AnySceneItemTransformSettingMatches(
|
||||||
|
const std::vector<OBSSceneItem> &items)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
std::vector<std::string> varValues;
|
||||||
|
|
||||||
|
for (const auto &item : items) {
|
||||||
|
const auto currentValue =
|
||||||
|
GetTransformSettingValue(item, _setting);
|
||||||
|
if (!currentValue) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (compareValue(_compare, std::stod(*currentValue),
|
||||||
|
std::stod(_singleSetting))) {
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
} catch (std::invalid_argument &) {
|
||||||
|
} catch (std::out_of_range &) {
|
||||||
|
}
|
||||||
|
|
||||||
|
varValues.emplace_back(*currentValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTempVarHelper(varValues);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransform::SetTempVarHelper(
|
||||||
|
const std::vector<std::string> &values)
|
||||||
|
{
|
||||||
|
std::string varValue;
|
||||||
|
bool addSeperator = false;
|
||||||
|
for (const auto &value : values) {
|
||||||
|
if (addSeperator) {
|
||||||
|
varValue += ";";
|
||||||
|
}
|
||||||
|
varValue += value;
|
||||||
|
addSeperator = true;
|
||||||
|
}
|
||||||
|
SetTempVarValue("setting", varValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionSceneTransform::CheckSingleSetting(
|
||||||
|
const std::vector<OBSSceneItem> &items)
|
||||||
|
{
|
||||||
|
if (_condition == Condition::CHANGED) {
|
||||||
|
return AnySceneItemTransformSettingChanged(items);
|
||||||
|
}
|
||||||
|
return AnySceneItemTransformSettingMatches(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionSceneTransform::CheckCondition()
|
||||||
|
{
|
||||||
|
auto items = _source.GetSceneItems(_scene);
|
||||||
|
if (items.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_settingsType) {
|
||||||
|
case SettingsType::ALL:
|
||||||
|
return CheckAllSettings(items);
|
||||||
|
case SettingsType::SINGLE:
|
||||||
|
return CheckSingleSetting(items);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool MacroConditionSceneTransform::Save(obs_data_t *obj) const
|
bool MacroConditionSceneTransform::Save(obs_data_t *obj) const
|
||||||
{
|
{
|
||||||
MacroCondition::Save(obj);
|
MacroCondition::Save(obj);
|
||||||
_scene.Save(obj);
|
_scene.Save(obj);
|
||||||
_source.Save(obj);
|
_source.Save(obj);
|
||||||
_settings.Save(obj, "settings");
|
_transformString.Save(obj, "transformString");
|
||||||
|
_singleSetting.Save(obj, "singleSetting");
|
||||||
_regex.Save(obj);
|
_regex.Save(obj);
|
||||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
_setting.Save(obj);
|
||||||
|
obs_data_set_int(obj, "settingsType", static_cast<int>(_settingsType));
|
||||||
|
obs_data_set_int(obj, "compare", static_cast<int>(_compare));
|
||||||
|
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||||
|
obs_data_set_int(obj, "version", 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,14 +253,32 @@ bool MacroConditionSceneTransform::Load(obs_data_t *obj)
|
|||||||
MacroCondition::Load(obj);
|
MacroCondition::Load(obj);
|
||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_source.Load(obj);
|
_source.Load(obj);
|
||||||
_settings.Load(obj, "settings");
|
_transformString.Load(obj, "transformString");
|
||||||
|
_singleSetting.Load(obj, "singleSetting");
|
||||||
_regex.Load(obj);
|
_regex.Load(obj);
|
||||||
_type = static_cast<CondType>(obs_data_get_int(obj, "type"));
|
_setting.Load(obj);
|
||||||
|
SetSettingsType(static_cast<SettingsType>(
|
||||||
|
obs_data_get_int(obj, "settingsType")));
|
||||||
|
_compare = static_cast<Compare>(obs_data_get_int(obj, "compare"));
|
||||||
|
SetCondition(
|
||||||
|
static_cast<Condition>(obs_data_get_int(obj, "condition")));
|
||||||
|
|
||||||
// TOOD: remove in future version
|
// TOOD: remove in future version
|
||||||
if (obs_data_has_user_value(obj, "regex")) {
|
if (obs_data_has_user_value(obj, "regex")) {
|
||||||
_regex.CreateBackwardsCompatibleRegex(
|
_regex.CreateBackwardsCompatibleRegex(
|
||||||
obs_data_get_bool(obj, "regex"));
|
obs_data_get_bool(obj, "regex"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!obs_data_has_user_value(obj, "version")) {
|
||||||
|
SetCondition(
|
||||||
|
static_cast<Condition>(obs_data_get_int(obj, "type")));
|
||||||
|
_transformString.Load(obj, "settings");
|
||||||
|
SetSettingsType(SettingsType::ALL);
|
||||||
|
_compare = Compare::EQUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupTempVars();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,10 +291,41 @@ std::string MacroConditionSceneTransform::GetShortDesc() const
|
|||||||
return _scene.ToString() + " - " + _source.ToString();
|
return _scene.ToString() + " - " + _source.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void populateTypesSelection(QComboBox *list)
|
void MacroConditionSceneTransform::SetSettingsType(SettingsType type)
|
||||||
{
|
{
|
||||||
for (const auto &entry : conditionTypes) {
|
_settingsType = type;
|
||||||
list->addItem(obs_module_text(entry.second.c_str()));
|
SetupTempVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransform::SetCondition(Condition condition)
|
||||||
|
{
|
||||||
|
_condition = condition;
|
||||||
|
_previousTransform.clear();
|
||||||
|
_previousSettingValues.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransform::SetupTempVars()
|
||||||
|
{
|
||||||
|
MacroCondition::SetupTempVars();
|
||||||
|
if (_settingsType == SettingsType::ALL) {
|
||||||
|
AddTempvar(
|
||||||
|
"settings",
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.transform.settings"));
|
||||||
|
} else {
|
||||||
|
AddTempvar(
|
||||||
|
"setting",
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.transform.setting"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static inline void populateSelection(QComboBox *list,
|
||||||
|
const std::map<T, std::string> &map)
|
||||||
|
{
|
||||||
|
for (const auto &[_, name] : map) {
|
||||||
|
list->addItem(obs_module_text(name.c_str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,13 +336,22 @@ MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
|||||||
_scenes(new SceneSelectionWidget(window(), true, false, false, true)),
|
_scenes(new SceneSelectionWidget(window(), true, false, false, true)),
|
||||||
_sources(new SceneItemSelectionWidget(
|
_sources(new SceneItemSelectionWidget(
|
||||||
parent, true, SceneItemSelectionWidget::Placeholder::ANY)),
|
parent, true, SceneItemSelectionWidget::Placeholder::ANY)),
|
||||||
_types(new QComboBox()),
|
_settingsType(new QComboBox()),
|
||||||
|
_compare(new QComboBox()),
|
||||||
|
_conditions(new QComboBox()),
|
||||||
_getSettings(new QPushButton(obs_module_text(
|
_getSettings(new QPushButton(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.sceneTransform.getTransform"))),
|
"AdvSceneSwitcher.condition.sceneTransform.getTransform"))),
|
||||||
_settings(new VariableTextEdit(this)),
|
_getCurrentValue(new QPushButton(obs_module_text(
|
||||||
_regex(new RegexConfigWidget(parent))
|
"AdvSceneSwitcher.condition.sceneTransform.getCurrentValue"))),
|
||||||
|
_transformString(new VariableTextEdit(this)),
|
||||||
|
_singleSettingValue(new VariableLineEdit(this)),
|
||||||
|
_regex(new RegexConfigWidget(parent)),
|
||||||
|
_settingSelection(new TransformSettingSelection(this)),
|
||||||
|
_compareLayout(new QHBoxLayout())
|
||||||
{
|
{
|
||||||
populateTypesSelection(_types);
|
populateSelection(_settingsType, settingsTypes);
|
||||||
|
populateSelection(_compare, compareTypes);
|
||||||
|
populateSelection(_conditions, conditionTypes);
|
||||||
|
|
||||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||||
@@ -163,45 +360,62 @@ MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
|||||||
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(_types, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_settingsType, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(TypeChanged(int)));
|
SLOT(SettingsTypeChanged(int)));
|
||||||
|
QWidget::connect(_compare, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(CompareChanged(int)));
|
||||||
|
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(ConditionChanged(int)));
|
||||||
|
QWidget::connect(
|
||||||
|
_settingSelection,
|
||||||
|
SIGNAL(SelectionChanged(const TransformSetting &)), this,
|
||||||
|
SLOT(SettingSelectionChanged(const TransformSetting &)));
|
||||||
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
||||||
SLOT(GetSettingsClicked()));
|
SLOT(GetSettingsClicked()));
|
||||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
QWidget::connect(_getCurrentValue, SIGNAL(clicked()), this,
|
||||||
SLOT(SettingsChanged()));
|
SLOT(GetCurrentValueClicked()));
|
||||||
|
QWidget::connect(_transformString, SIGNAL(textChanged()), this,
|
||||||
|
SLOT(TransformChanged()));
|
||||||
|
QWidget::connect(_singleSettingValue, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(SettingValueChanged()));
|
||||||
QWidget::connect(_regex,
|
QWidget::connect(_regex,
|
||||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||||
SLOT(RegexChanged(const RegexConfig &)));
|
SLOT(RegexChanged(const RegexConfig &)));
|
||||||
|
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{scenes}}", _scenes},
|
{"{{scenes}}", _scenes},
|
||||||
{"{{sources}}", _sources},
|
{"{{sources}}", _sources},
|
||||||
{"{{types}}", _types},
|
{"{{compare}}", _compare},
|
||||||
{"{{settings}}", _settings},
|
{"{{setting}}", _settingSelection},
|
||||||
|
{"{{settingsType}}", _settingsType},
|
||||||
|
{"{{conditions}}", _conditions},
|
||||||
|
{"{{transformString}}", _transformString},
|
||||||
|
{"{{singleSettingValue}}", _singleSettingValue},
|
||||||
|
{"{{settings}}", _singleSettingValue},
|
||||||
{"{{getSettings}}", _getSettings},
|
{"{{getSettings}}", _getSettings},
|
||||||
|
{"{{getCurrentValue}}", _getCurrentValue},
|
||||||
{"{{regex}}", _regex},
|
{"{{regex}}", _regex},
|
||||||
};
|
};
|
||||||
|
|
||||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
auto entryLayout = new QHBoxLayout;
|
||||||
|
PlaceWidgets(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.sceneTransform.entry"),
|
||||||
|
entryLayout, widgetPlaceholders);
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.sceneTransform.entry.line1"),
|
"AdvSceneSwitcher.condition.sceneTransform.entry.singleSettingCompare"),
|
||||||
line1Layout, widgetPlaceholders);
|
_compareLayout, widgetPlaceholders, false);
|
||||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
auto optionsLayout = new QHBoxLayout;
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.sceneTransform.entry.line2"),
|
"AdvSceneSwitcher.condition.sceneTransform.entry.options"),
|
||||||
line2Layout, widgetPlaceholders, false);
|
optionsLayout, widgetPlaceholders);
|
||||||
QHBoxLayout *line3Layout = new QHBoxLayout;
|
|
||||||
PlaceWidgets(
|
|
||||||
obs_module_text(
|
|
||||||
"AdvSceneSwitcher.condition.sceneTransform.entry.line3"),
|
|
||||||
line3Layout, widgetPlaceholders);
|
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
mainLayout->addLayout(line1Layout);
|
mainLayout->addLayout(entryLayout);
|
||||||
mainLayout->addLayout(line2Layout);
|
mainLayout->addLayout(_compareLayout);
|
||||||
mainLayout->addLayout(line3Layout);
|
mainLayout->addWidget(_transformString);
|
||||||
|
mainLayout->addLayout(optionsLayout);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
@@ -217,52 +431,75 @@ void MacroConditionSceneTransformEdit::UpdateEntryData()
|
|||||||
|
|
||||||
_scenes->SetScene(_entryData->_scene);
|
_scenes->SetScene(_entryData->_scene);
|
||||||
_sources->SetSceneItem(_entryData->_source);
|
_sources->SetSceneItem(_entryData->_source);
|
||||||
_types->setCurrentIndex(static_cast<int>(_entryData->_type));
|
_settingsType->setCurrentIndex(
|
||||||
|
static_cast<int>(_entryData->GetSettingsType()));
|
||||||
|
_compare->setCurrentIndex(static_cast<int>(_entryData->_compare));
|
||||||
|
_conditions->setCurrentIndex(
|
||||||
|
static_cast<int>(_entryData->GetCondition()));
|
||||||
_regex->SetRegexConfig(_entryData->_regex);
|
_regex->SetRegexConfig(_entryData->_regex);
|
||||||
_settings->setPlainText(_entryData->_settings);
|
UpdateSettingSelection();
|
||||||
|
_settingSelection->SetSetting(_entryData->_setting);
|
||||||
|
_transformString->setPlainText(_entryData->_transformString);
|
||||||
|
_singleSettingValue->setText(_entryData->_singleSetting);
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
void MacroConditionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
{
|
||||||
return;
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_scene = s;
|
||||||
}
|
}
|
||||||
|
UpdateSettingSelection();
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_scene = s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionSceneTransformEdit::SourceChanged(
|
void MacroConditionSceneTransformEdit::SourceChanged(
|
||||||
const SceneItemSelection &item)
|
const SceneItemSelection &item)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
{
|
||||||
return;
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_source = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock = LockContext();
|
UpdateSettingSelection();
|
||||||
_entryData->_source = item;
|
|
||||||
emit HeaderInfoChanged(
|
emit HeaderInfoChanged(
|
||||||
QString::fromStdString(_entryData->GetShortDesc()));
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionSceneTransformEdit::TypeChanged(int index)
|
void MacroConditionSceneTransformEdit::SettingsTypeChanged(int type)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
_entryData->SetSettingsType(
|
||||||
}
|
static_cast<MacroConditionSceneTransform::SettingsType>(type));
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_type =
|
|
||||||
static_cast<MacroConditionSceneTransform::CondType>(index);
|
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransformEdit::CompareChanged(int type)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_compare =
|
||||||
|
static_cast<MacroConditionSceneTransform::Compare>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransformEdit::ConditionChanged(int index)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->SetCondition(
|
||||||
|
static_cast<MacroConditionSceneTransform::Condition>(index));
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransformEdit::SettingSelectionChanged(
|
||||||
|
const TransformSetting &setting)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_setting = setting;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) {
|
if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -276,17 +513,47 @@ void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
|||||||
if (_entryData->_regex.Enabled()) {
|
if (_entryData->_regex.Enabled()) {
|
||||||
settings = EscapeForRegex(settings);
|
settings = EscapeForRegex(settings);
|
||||||
}
|
}
|
||||||
_settings->setPlainText(settings);
|
_transformString->setPlainText(settings);
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionSceneTransformEdit::SettingsChanged()
|
void MacroConditionSceneTransformEdit::GetCurrentValueClicked()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock = LockContext();
|
auto items = _entryData->_source.GetSceneItems(_entryData->_scene);
|
||||||
_entryData->_settings = _settings->toPlainText().toStdString();
|
if (items.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto value =
|
||||||
|
GetTransformSettingValue(items.at(0), _entryData->_setting);
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_singleSettingValue->setText(QString::fromStdString(*value));
|
||||||
|
SettingValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransformEdit::TransformChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_transformString =
|
||||||
|
_transformString->toPlainText().toStdString();
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransformEdit::SettingValueChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_singleSetting = _singleSettingValue->text().toStdString();
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
@@ -294,11 +561,7 @@ void MacroConditionSceneTransformEdit::SettingsChanged()
|
|||||||
|
|
||||||
void MacroConditionSceneTransformEdit::RegexChanged(const RegexConfig &conf)
|
void MacroConditionSceneTransformEdit::RegexChanged(const RegexConfig &conf)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_regex = conf;
|
_entryData->_regex = conf;
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
@@ -307,14 +570,61 @@ void MacroConditionSceneTransformEdit::RegexChanged(const RegexConfig &conf)
|
|||||||
|
|
||||||
void MacroConditionSceneTransformEdit::SetWidgetVisibility()
|
void MacroConditionSceneTransformEdit::SetWidgetVisibility()
|
||||||
{
|
{
|
||||||
const bool showSettingsAndRegex =
|
const auto condition = _entryData->GetCondition();
|
||||||
_entryData->_type ==
|
const auto settingsType = _entryData->GetSettingsType();
|
||||||
MacroConditionSceneTransform::CondType::MATCHES;
|
_transformString->setVisible(
|
||||||
_settings->setVisible(showSettingsAndRegex);
|
condition == MacroConditionSceneTransform::Condition::MATCHES);
|
||||||
_getSettings->setVisible(showSettingsAndRegex);
|
_regex->setVisible(
|
||||||
_regex->setVisible(showSettingsAndRegex);
|
condition == MacroConditionSceneTransform::Condition::MATCHES &&
|
||||||
|
settingsType ==
|
||||||
|
MacroConditionSceneTransform::SettingsType::ALL);
|
||||||
|
_getSettings->setVisible(
|
||||||
|
condition == MacroConditionSceneTransform::Condition::MATCHES &&
|
||||||
|
settingsType ==
|
||||||
|
MacroConditionSceneTransform::SettingsType::ALL);
|
||||||
|
_getCurrentValue->setVisible(
|
||||||
|
condition == MacroConditionSceneTransform::Condition::MATCHES &&
|
||||||
|
settingsType ==
|
||||||
|
MacroConditionSceneTransform::SettingsType::SINGLE);
|
||||||
|
SetLayoutVisible(
|
||||||
|
_compareLayout,
|
||||||
|
condition == MacroConditionSceneTransform::Condition::MATCHES &&
|
||||||
|
settingsType == MacroConditionSceneTransform::
|
||||||
|
SettingsType::SINGLE);
|
||||||
|
_settingSelection->setVisible(
|
||||||
|
settingsType ==
|
||||||
|
MacroConditionSceneTransform::SettingsType::SINGLE);
|
||||||
|
if (condition == MacroConditionSceneTransform::Condition::MATCHES &&
|
||||||
|
settingsType ==
|
||||||
|
MacroConditionSceneTransform::SettingsType::SINGLE) {
|
||||||
|
RemoveStretchIfPresent(_compareLayout);
|
||||||
|
} else if (condition ==
|
||||||
|
MacroConditionSceneTransform::Condition::CHANGED &&
|
||||||
|
settingsType ==
|
||||||
|
MacroConditionSceneTransform::SettingsType::SINGLE) {
|
||||||
|
AddStretchIfNecessary(_compareLayout);
|
||||||
|
}
|
||||||
|
_transformString->setVisible(
|
||||||
|
condition == MacroConditionSceneTransform::Condition::MATCHES &&
|
||||||
|
settingsType ==
|
||||||
|
MacroConditionSceneTransform::SettingsType::ALL);
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneTransformEdit::UpdateSettingSelection() const
|
||||||
|
{
|
||||||
|
if (!_entryData) {
|
||||||
|
_settingSelection->SetSource(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto items = _entryData->_source.GetSceneItems(_entryData->_scene);
|
||||||
|
if (items.empty()) {
|
||||||
|
_settingSelection->SetSource(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_settingSelection->SetSource(items.at(0));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "macro-condition-edit.hpp"
|
#include "macro-condition-edit.hpp"
|
||||||
#include "scene-selection.hpp"
|
|
||||||
#include "scene-item-selection.hpp"
|
|
||||||
#include "variable-text-edit.hpp"
|
|
||||||
#include "regex-config.hpp"
|
#include "regex-config.hpp"
|
||||||
|
#include "scene-item-selection.hpp"
|
||||||
|
#include "scene-selection.hpp"
|
||||||
|
#include "transform-setting.hpp"
|
||||||
|
#include "variable-text-edit.hpp"
|
||||||
|
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
@@ -23,19 +24,38 @@ public:
|
|||||||
return std::make_shared<MacroConditionSceneTransform>(m);
|
return std::make_shared<MacroConditionSceneTransform>(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CondType {
|
enum class SettingsType { ALL, SINGLE };
|
||||||
MATCHES,
|
enum class Condition { MATCHES, CHANGED };
|
||||||
CHANGED,
|
enum class Compare { LESS, EQUAL, MORE };
|
||||||
};
|
|
||||||
|
void SetSettingsType(SettingsType type);
|
||||||
|
SettingsType GetSettingsType() const { return _settingsType; }
|
||||||
|
void SetCondition(Condition condition);
|
||||||
|
Condition GetCondition() const { return _condition; }
|
||||||
|
|
||||||
SceneSelection _scene;
|
SceneSelection _scene;
|
||||||
SceneItemSelection _source;
|
SceneItemSelection _source;
|
||||||
CondType _type = CondType::MATCHES;
|
Compare _compare = Compare::EQUAL;
|
||||||
RegexConfig _regex;
|
RegexConfig _regex;
|
||||||
StringVariable _settings = "";
|
StringVariable _transformString = "";
|
||||||
|
StringVariable _singleSetting = "";
|
||||||
|
TransformSetting _setting;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetupTempVars();
|
||||||
|
bool CheckAllSettings(const std::vector<OBSSceneItem> &);
|
||||||
|
bool CheckSingleSetting(const std::vector<OBSSceneItem> &);
|
||||||
|
bool
|
||||||
|
AnySceneItemTransformSettingChanged(const std::vector<OBSSceneItem> &);
|
||||||
|
bool
|
||||||
|
AnySceneItemTransformSettingMatches(const std::vector<OBSSceneItem> &);
|
||||||
|
void SetTempVarHelper(const std::vector<std::string> &values);
|
||||||
|
|
||||||
|
SettingsType _settingsType = SettingsType::SINGLE;
|
||||||
|
Condition _condition = Condition::MATCHES;
|
||||||
|
|
||||||
std::vector<std::string> _previousTransform;
|
std::vector<std::string> _previousTransform;
|
||||||
|
std::vector<std::string> _previousSettingValues;
|
||||||
|
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
static const std::string id;
|
static const std::string id;
|
||||||
@@ -62,25 +82,36 @@ public:
|
|||||||
private slots:
|
private slots:
|
||||||
void SceneChanged(const SceneSelection &);
|
void SceneChanged(const SceneSelection &);
|
||||||
void SourceChanged(const SceneItemSelection &);
|
void SourceChanged(const SceneItemSelection &);
|
||||||
void TypeChanged(int type);
|
void SettingsTypeChanged(int type);
|
||||||
|
void CompareChanged(int type);
|
||||||
|
void ConditionChanged(int type);
|
||||||
|
void SettingSelectionChanged(const TransformSetting &);
|
||||||
void GetSettingsClicked();
|
void GetSettingsClicked();
|
||||||
void SettingsChanged();
|
void GetCurrentValueClicked();
|
||||||
|
void TransformChanged();
|
||||||
|
void SettingValueChanged();
|
||||||
void RegexChanged(const RegexConfig &);
|
void RegexChanged(const RegexConfig &);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
protected:
|
|
||||||
SceneSelectionWidget *_scenes;
|
|
||||||
SceneItemSelectionWidget *_sources;
|
|
||||||
QComboBox *_types;
|
|
||||||
QPushButton *_getSettings;
|
|
||||||
VariableTextEdit *_settings;
|
|
||||||
RegexConfigWidget *_regex;
|
|
||||||
|
|
||||||
std::shared_ptr<MacroConditionSceneTransform> _entryData;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetWidgetVisibility();
|
void SetWidgetVisibility();
|
||||||
|
void UpdateSettingSelection() const;
|
||||||
|
|
||||||
|
SceneSelectionWidget *_scenes;
|
||||||
|
SceneItemSelectionWidget *_sources;
|
||||||
|
QComboBox *_settingsType;
|
||||||
|
QComboBox *_compare;
|
||||||
|
QComboBox *_conditions;
|
||||||
|
QPushButton *_getSettings;
|
||||||
|
QPushButton *_getCurrentValue;
|
||||||
|
VariableTextEdit *_transformString;
|
||||||
|
VariableLineEdit *_singleSettingValue;
|
||||||
|
RegexConfigWidget *_regex;
|
||||||
|
TransformSettingSelection *_settingSelection;
|
||||||
|
QHBoxLayout *_compareLayout;
|
||||||
|
|
||||||
|
std::shared_ptr<MacroConditionSceneTransform> _entryData;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
373
plugins/base/utils/transform-setting.cpp
Normal file
373
plugins/base/utils/transform-setting.cpp
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
#include "transform-setting.hpp"
|
||||||
|
#include "obs-module-helper.hpp"
|
||||||
|
#include "math-helpers.hpp"
|
||||||
|
#include "scene-item-transform-helpers.hpp"
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <QLayout>
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(advss::TransformSetting);
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
TransformSetting::TransformSetting(const std::string &id,
|
||||||
|
const std::string &nestedId)
|
||||||
|
: _id(id),
|
||||||
|
_nestedId(nestedId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformSetting::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
|
obs_data_set_string(data, "id", _id.c_str());
|
||||||
|
obs_data_set_string(data, "nestedId", _nestedId.c_str());
|
||||||
|
obs_data_set_obj(obj, "transformSetting", data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformSetting::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
OBSDataAutoRelease data = obs_data_get_obj(obj, "transformSetting");
|
||||||
|
_id = obs_data_get_string(data, "id");
|
||||||
|
_nestedId = obs_data_get_string(data, "nestedId");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformSetting::operator==(const TransformSetting &other) const
|
||||||
|
{
|
||||||
|
return _id == other._id && _nestedId == other._nestedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
getSoruceTransformSettingsHelper(std::vector<TransformSetting> &settings,
|
||||||
|
const std::string &jsonString,
|
||||||
|
const std::string &nestedId = "")
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
auto transform = nlohmann::json::parse(jsonString);
|
||||||
|
for (const auto &[key, value] : transform.items()) {
|
||||||
|
if (value.is_object() && nestedId.empty()) {
|
||||||
|
getSoruceTransformSettingsHelper(
|
||||||
|
settings, value.dump(), key);
|
||||||
|
} else {
|
||||||
|
settings.emplace_back(key, nestedId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const nlohmann::json::exception &) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<TransformSetting> GetSoruceTransformSettings(obs_scene_item *source)
|
||||||
|
{
|
||||||
|
std::vector<TransformSetting> settings;
|
||||||
|
auto jsonString = GetSceneItemTransform(source);
|
||||||
|
getSoruceTransformSettingsHelper(settings, jsonString);
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string>
|
||||||
|
GetTransformSettingValue(obs_scene_item *source,
|
||||||
|
const TransformSetting &setting)
|
||||||
|
{
|
||||||
|
auto jsonString = GetSceneItemTransform(source);
|
||||||
|
if (setting.GetNestedID().empty()) {
|
||||||
|
return GetJsonField(jsonString, setting.GetID());
|
||||||
|
}
|
||||||
|
auto nestedJsonString = GetJsonField(jsonString, setting.GetNestedID());
|
||||||
|
if (nestedJsonString.has_value()) {
|
||||||
|
return GetJsonField(*nestedJsonString, setting.GetID());
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static void logConversionError(T, const char *func, const char *target,
|
||||||
|
const char *value)
|
||||||
|
{
|
||||||
|
if (std::is_same<T, std::invalid_argument>::value) {
|
||||||
|
blog(LOG_WARNING, "%s invalid %s value (%s)", func, target,
|
||||||
|
value);
|
||||||
|
} else {
|
||||||
|
blog(LOG_WARNING, "%s value out of range for %s (%s)", func,
|
||||||
|
target, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleCrop(struct obs_sceneitem_crop &crop, const std::string &id,
|
||||||
|
const std::string &value)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
try {
|
||||||
|
val = std::stoi(value);
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
logConversionError(e, __func__, id.c_str(), value.c_str());
|
||||||
|
return;
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
logConversionError(e, __func__, id.c_str(), value.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == "left") {
|
||||||
|
crop.left = val;
|
||||||
|
} else if (id == "top") {
|
||||||
|
crop.top = val;
|
||||||
|
} else if (id == "right") {
|
||||||
|
crop.right = val;
|
||||||
|
} else if (id == "bottom") {
|
||||||
|
crop.bottom = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handlePosOrScaleOrBounds(struct obs_transform_info &info,
|
||||||
|
const TransformSetting &setting,
|
||||||
|
const std::string &value)
|
||||||
|
{
|
||||||
|
float val = 0;
|
||||||
|
try {
|
||||||
|
val = std::stof(value);
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
logConversionError(
|
||||||
|
e, __func__,
|
||||||
|
(setting.GetID() + "-" + setting.GetNestedID()).c_str(),
|
||||||
|
value.c_str());
|
||||||
|
return;
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
logConversionError(
|
||||||
|
e, __func__,
|
||||||
|
(setting.GetID() + "-" + setting.GetNestedID()).c_str(),
|
||||||
|
value.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vec2 *target = nullptr;
|
||||||
|
if (setting.GetNestedID() == "scale") {
|
||||||
|
target = &info.scale;
|
||||||
|
} else if (setting.GetNestedID() == "bounds") {
|
||||||
|
target = &info.bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setting.GetID() == "x") {
|
||||||
|
target->x = val;
|
||||||
|
} else if (setting.GetID() == "y") {
|
||||||
|
target->y = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleRot(struct obs_transform_info &info, const std::string &value)
|
||||||
|
{
|
||||||
|
float val = 0;
|
||||||
|
try {
|
||||||
|
val = std::stof(value);
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
logConversionError(e, __func__, "rot", value.c_str());
|
||||||
|
return;
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
logConversionError(e, __func__, "rot", value.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.rot = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTransformSetting(obs_scene_item *source,
|
||||||
|
const TransformSetting &setting,
|
||||||
|
const std::string &value)
|
||||||
|
{
|
||||||
|
auto id = setting.GetID();
|
||||||
|
|
||||||
|
struct obs_transform_info info = {};
|
||||||
|
struct obs_sceneitem_crop crop = {};
|
||||||
|
|
||||||
|
obs_sceneitem_get_info(source, &info);
|
||||||
|
obs_sceneitem_get_crop(source, &crop);
|
||||||
|
|
||||||
|
if (id == "alignment") {
|
||||||
|
try {
|
||||||
|
auto val = std::stoul(value);
|
||||||
|
info.alignment = val;
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
logConversionError(e, __func__, id.c_str(),
|
||||||
|
value.c_str());
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
logConversionError(e, __func__, id.c_str(),
|
||||||
|
value.c_str());
|
||||||
|
}
|
||||||
|
} else if (id == "left" || id == "top" || id == "right" ||
|
||||||
|
id == "bottom") {
|
||||||
|
handleCrop(crop, id, value);
|
||||||
|
} else if (id == "bounds_alignment") {
|
||||||
|
uint32_t val = 0;
|
||||||
|
try {
|
||||||
|
val = std::stoul(value);
|
||||||
|
info.bounds_alignment = val;
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
logConversionError(e, __func__, id.c_str(),
|
||||||
|
value.c_str());
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
logConversionError(e, __func__, id.c_str(),
|
||||||
|
value.c_str());
|
||||||
|
}
|
||||||
|
} else if (id == "bounds_type") {
|
||||||
|
try {
|
||||||
|
obs_bounds_type val =
|
||||||
|
static_cast<obs_bounds_type>(std::stoul(value));
|
||||||
|
info.bounds_type = val;
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
logConversionError(e, __func__, id.c_str(),
|
||||||
|
value.c_str());
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
logConversionError(e, __func__, id.c_str(),
|
||||||
|
value.c_str());
|
||||||
|
}
|
||||||
|
} else if (id == "x" || id == "y") {
|
||||||
|
handlePosOrScaleOrBounds(info, setting, value);
|
||||||
|
} else if (id == "width" || id == "height") {
|
||||||
|
float val = 0;
|
||||||
|
try {
|
||||||
|
val = std::stof(value);
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
logConversionError(
|
||||||
|
e, __func__,
|
||||||
|
(setting.GetID() + "-" + setting.GetNestedID())
|
||||||
|
.c_str(),
|
||||||
|
value.c_str());
|
||||||
|
return;
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
logConversionError(
|
||||||
|
e, __func__,
|
||||||
|
(setting.GetID() + "-" + setting.GetNestedID())
|
||||||
|
.c_str(),
|
||||||
|
value.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto source2 = obs_sceneitem_get_source(source);
|
||||||
|
if (setting.GetID() == "width") {
|
||||||
|
val = val / obs_source_get_width(source2);
|
||||||
|
handlePosOrScaleOrBounds(info,
|
||||||
|
TransformSetting("x", "scale"),
|
||||||
|
std::to_string(val));
|
||||||
|
} else if (setting.GetID() == "height") {
|
||||||
|
val = val / obs_source_get_height(source2);
|
||||||
|
handlePosOrScaleOrBounds(info,
|
||||||
|
TransformSetting("y", "scale"),
|
||||||
|
std::to_string(val));
|
||||||
|
}
|
||||||
|
} else if (id == "rot") {
|
||||||
|
handleRot(info, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_defer_update_begin(source);
|
||||||
|
obs_sceneitem_set_info(source, &info);
|
||||||
|
obs_sceneitem_set_crop(source, &crop);
|
||||||
|
obs_sceneitem_defer_update_end(source);
|
||||||
|
obs_sceneitem_force_update_transform(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformSettingSelection::TransformSettingSelection(QWidget *parent)
|
||||||
|
: QWidget(parent),
|
||||||
|
_settings(new FilterComboBox(
|
||||||
|
this, obs_module_text("AdvSceneSwitcher.setting.select")))
|
||||||
|
{
|
||||||
|
_settings->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||||
|
_settings->setMaximumWidth(350);
|
||||||
|
|
||||||
|
QWidget::connect(_settings, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(SelectionIdxChanged(int)));
|
||||||
|
|
||||||
|
auto layout = new QHBoxLayout();
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->addWidget(_settings);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformSettingSelection::SetSource(obs_scene_item *source)
|
||||||
|
{
|
||||||
|
_settings->clear();
|
||||||
|
Populate(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformSettingSelection::SetSetting(const TransformSetting &setting)
|
||||||
|
{
|
||||||
|
QVariant variant;
|
||||||
|
variant.setValue(setting);
|
||||||
|
_settings->setCurrentIndex(_settings->findData(variant));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformSettingSelection::SelectionIdxChanged(int idx)
|
||||||
|
{
|
||||||
|
if (idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto setting = _settings->itemData(idx).value<TransformSetting>();
|
||||||
|
emit SelectionChanged(setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString mapIdToLocale(const TransformSetting &setting)
|
||||||
|
{
|
||||||
|
static const std::unordered_map<std::string_view, std::string_view>
|
||||||
|
idMap = {
|
||||||
|
{"alignment",
|
||||||
|
"AdvSceneSwitcher.setting.transform.alignment"},
|
||||||
|
{"bottom",
|
||||||
|
"AdvSceneSwitcher.setting.transform.cropBottom"},
|
||||||
|
{"left", "AdvSceneSwitcher.setting.transform.cropLeft"},
|
||||||
|
{"right",
|
||||||
|
"AdvSceneSwitcher.setting.transform.cropRight"},
|
||||||
|
{"top", "AdvSceneSwitcher.setting.transform.cropTop"},
|
||||||
|
{"bounds_alignment",
|
||||||
|
"AdvSceneSwitcher.setting.transform.boundingBoxAlign"},
|
||||||
|
{"bounds_type",
|
||||||
|
"AdvSceneSwitcher.setting.transform.boundingBoxType"},
|
||||||
|
{"rot", "AdvSceneSwitcher.setting.transform.rotation"},
|
||||||
|
{"height", "AdvSceneSwitcher.setting.transform.height"},
|
||||||
|
{"width", "AdvSceneSwitcher.setting.transform.width"},
|
||||||
|
{"x", "x"},
|
||||||
|
{"y", "y"},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::unordered_map<std::string_view, std::string_view>
|
||||||
|
nestedIdMap = {
|
||||||
|
{"bounds",
|
||||||
|
"AdvSceneSwitcher.setting.transform.boundingBoxSize"},
|
||||||
|
{"pos", "AdvSceneSwitcher.setting.transform.position"},
|
||||||
|
{"scale", "AdvSceneSwitcher.setting.transform.scale"},
|
||||||
|
{"size", "AdvSceneSwitcher.setting.transform.size"},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto idNameIt = idMap.find(setting.GetID());
|
||||||
|
QString name = idNameIt == idMap.end()
|
||||||
|
? QString::fromStdString(setting.GetID())
|
||||||
|
: obs_module_text(idNameIt->second.data());
|
||||||
|
|
||||||
|
if (!setting.GetNestedID().empty()) {
|
||||||
|
auto idNameIt = nestedIdMap.find(setting.GetNestedID());
|
||||||
|
const auto nestedName =
|
||||||
|
idNameIt == nestedIdMap.end()
|
||||||
|
? QString::fromStdString(setting.GetID())
|
||||||
|
: obs_module_text(idNameIt->second.data());
|
||||||
|
name = "[" + nestedName + "] " + name;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformSettingSelection::Populate(obs_scene_item *source)
|
||||||
|
{
|
||||||
|
auto settings = GetSoruceTransformSettings(source);
|
||||||
|
for (const auto &setting : settings) {
|
||||||
|
QVariant variant;
|
||||||
|
variant.setValue(setting);
|
||||||
|
auto name = mapIdToLocale(setting);
|
||||||
|
_settings->addItem(name, variant);
|
||||||
|
}
|
||||||
|
_settings->setCurrentIndex(-1);
|
||||||
|
TransformSetting emptySetting;
|
||||||
|
emit SelectionChanged(emptySetting);
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
58
plugins/base/utils/transform-setting.hpp
Normal file
58
plugins/base/utils/transform-setting.hpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "filter-combo-box.hpp"
|
||||||
|
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class TransformSetting {
|
||||||
|
public:
|
||||||
|
TransformSetting() = default;
|
||||||
|
TransformSetting(const std::string &id, const std::string &nestedId);
|
||||||
|
bool Save(obs_data_t *obj) const;
|
||||||
|
bool Load(obs_data_t *obj);
|
||||||
|
std::string GetID() const { return _id; }
|
||||||
|
std::string GetNestedID() const { return _nestedId; }
|
||||||
|
EXPORT bool operator==(const TransformSetting &other) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _id = "";
|
||||||
|
std::string _nestedId = "";
|
||||||
|
friend class TransformSettingSelection;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<TransformSetting>
|
||||||
|
GetSoruceTransformSettings(obs_scene_item *source);
|
||||||
|
std::optional<std::string>
|
||||||
|
GetTransformSettingValue(obs_scene_item *source,
|
||||||
|
const TransformSetting &setting);
|
||||||
|
void SetTransformSetting(obs_scene_item *source,
|
||||||
|
const TransformSetting &setting,
|
||||||
|
const std::string &value);
|
||||||
|
|
||||||
|
class TransformSettingSelection : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TransformSettingSelection(QWidget *parent = nullptr);
|
||||||
|
void SetSource(obs_scene_item *);
|
||||||
|
void SetSetting(const TransformSetting &);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void SelectionIdxChanged(int);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void SelectionChanged(const TransformSetting &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Populate(obs_scene_item *);
|
||||||
|
|
||||||
|
FilterComboBox *_settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -70,6 +70,9 @@ static bool setupDeviceObservers()
|
|||||||
} catch (const libremidi::driver_error &error) {
|
} catch (const libremidi::driver_error &error) {
|
||||||
blog(LOG_WARNING, "Failed to setup midi device observers: %s",
|
blog(LOG_WARNING, "Failed to setup midi device observers: %s",
|
||||||
error.what());
|
error.what());
|
||||||
|
} catch (const std::exception &error) {
|
||||||
|
blog(LOG_WARNING, "Failed to setup midi device observers: %s",
|
||||||
|
error.what());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ setTempVarsHelper(obs_data_t *data,
|
|||||||
setTempVarsHelper(jsonStr, setVar);
|
setTempVarsHelper(jsonStr, setVar);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionTwitch::CheckChannelGenericEvents(TwitchToken &token)
|
bool MacroConditionTwitch::CheckChannelGenericEvents()
|
||||||
{
|
{
|
||||||
if (!_eventBuffer) {
|
if (!_eventBuffer) {
|
||||||
return false;
|
return false;
|
||||||
@@ -315,7 +315,7 @@ bool MacroConditionTwitch::CheckChannelGenericEvents(TwitchToken &token)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionTwitch::CheckChannelLiveEvents(TwitchToken &token)
|
bool MacroConditionTwitch::CheckChannelLiveEvents()
|
||||||
{
|
{
|
||||||
if (!_eventBuffer) {
|
if (!_eventBuffer) {
|
||||||
return false;
|
return false;
|
||||||
@@ -541,13 +541,13 @@ bool MacroConditionTwitch::CheckCondition()
|
|||||||
case Condition::USER_UNBAN_EVENT:
|
case Condition::USER_UNBAN_EVENT:
|
||||||
case Condition::USER_MODERATOR_ADDITION_EVENT:
|
case Condition::USER_MODERATOR_ADDITION_EVENT:
|
||||||
case Condition::USER_MODERATOR_DELETION_EVENT:
|
case Condition::USER_MODERATOR_DELETION_EVENT:
|
||||||
return CheckChannelGenericEvents(*token);
|
return CheckChannelGenericEvents();
|
||||||
case Condition::STREAM_ONLINE_LIVE_EVENT:
|
case Condition::STREAM_ONLINE_LIVE_EVENT:
|
||||||
case Condition::STREAM_ONLINE_PLAYLIST_EVENT:
|
case Condition::STREAM_ONLINE_PLAYLIST_EVENT:
|
||||||
case Condition::STREAM_ONLINE_WATCHPARTY_EVENT:
|
case Condition::STREAM_ONLINE_WATCHPARTY_EVENT:
|
||||||
case Condition::STREAM_ONLINE_PREMIERE_EVENT:
|
case Condition::STREAM_ONLINE_PREMIERE_EVENT:
|
||||||
case Condition::STREAM_ONLINE_RERUN_EVENT:
|
case Condition::STREAM_ONLINE_RERUN_EVENT:
|
||||||
return CheckChannelLiveEvents(*token);
|
return CheckChannelLiveEvents();
|
||||||
case Condition::LIVE_POLLING: {
|
case Condition::LIVE_POLLING: {
|
||||||
auto info = _channel.GetLiveInfo(*token);
|
auto info = _channel.GetLiveInfo(*token);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ public:
|
|||||||
bool _clearBufferOnMatch = false;
|
bool _clearBufferOnMatch = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool CheckChannelGenericEvents(TwitchToken &token);
|
bool CheckChannelGenericEvents();
|
||||||
bool CheckChannelLiveEvents(TwitchToken &token);
|
bool CheckChannelLiveEvents();
|
||||||
bool CheckChatMessages(TwitchToken &token);
|
bool CheckChatMessages(TwitchToken &token);
|
||||||
|
|
||||||
void RegisterEventSubscription();
|
void RegisterEventSubscription();
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ static std::vector<USBDeviceInfo> pollUSBDevices()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hotplugCallback(struct libusb_context *ctx,
|
static int hotplugCallback(struct libusb_context *,
|
||||||
struct libusb_device *device,
|
struct libusb_device *device,
|
||||||
libusb_hotplug_event event, void *user_data)
|
libusb_hotplug_event event, void *user_data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,6 +122,12 @@ bool MacroConditionVideo::CheckShouldBeSkipped()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MacroConditionVideo::MacroConditionVideo(Macro *m)
|
||||||
|
: QObject(),
|
||||||
|
MacroCondition(m, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool MacroConditionVideo::CheckCondition()
|
bool MacroConditionVideo::CheckCondition()
|
||||||
{
|
{
|
||||||
if (!_video.ValidSelection()) {
|
if (!_video.ValidSelection()) {
|
||||||
@@ -133,6 +139,10 @@ bool MacroConditionVideo::CheckCondition()
|
|||||||
return _lastMatchResult;
|
return _lastMatchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!FileInputIsUpToDate()) {
|
||||||
|
LoadImageFromFile();
|
||||||
|
}
|
||||||
|
|
||||||
if (_blockUntilScreenshotDone) {
|
if (_blockUntilScreenshotDone) {
|
||||||
GetScreenshot(true);
|
GetScreenshot(true);
|
||||||
}
|
}
|
||||||
@@ -231,7 +241,10 @@ void MacroConditionVideo::GetScreenshot(bool blocking)
|
|||||||
|
|
||||||
bool MacroConditionVideo::LoadImageFromFile()
|
bool MacroConditionVideo::LoadImageFromFile()
|
||||||
{
|
{
|
||||||
if (!_matchImage.load(QString::fromStdString(_file))) {
|
const QFileInfo info(QString::fromStdString(_file));
|
||||||
|
_loadedFileLastModified = info.lastModified();
|
||||||
|
_loadedFile = _file;
|
||||||
|
if (!_matchImage.load(info.absoluteFilePath())) {
|
||||||
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
||||||
_file.c_str());
|
_file.c_str());
|
||||||
(&_matchImage)->~QImage();
|
(&_matchImage)->~QImage();
|
||||||
@@ -244,6 +257,8 @@ bool MacroConditionVideo::LoadImageFromFile()
|
|||||||
_matchImage.convertToFormat(QImage::Format::Format_RGBA8888);
|
_matchImage.convertToFormat(QImage::Format::Format_RGBA8888);
|
||||||
_patternMatchParameters.image = _matchImage;
|
_patternMatchParameters.image = _matchImage;
|
||||||
_patternImageData = CreatePatternData(_matchImage);
|
_patternImageData = CreatePatternData(_matchImage);
|
||||||
|
|
||||||
|
emit InputFileChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,6 +306,16 @@ bool MacroConditionVideo::ScreenshotContainsPattern()
|
|||||||
return count > 0;
|
return count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MacroConditionVideo::FileInputIsUpToDate() const
|
||||||
|
{
|
||||||
|
if (!requiresFileInput(_condition)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const QFileInfo info(QString::fromStdString(_file));
|
||||||
|
return (_loadedFileLastModified == info.lastModified()) &&
|
||||||
|
(_file == _loadedFile);
|
||||||
|
}
|
||||||
|
|
||||||
bool MacroConditionVideo::OutputChanged()
|
bool MacroConditionVideo::OutputChanged()
|
||||||
{
|
{
|
||||||
if (!_patternMatchParameters.useForChangedCheck) {
|
if (!_patternMatchParameters.useForChangedCheck) {
|
||||||
@@ -1165,6 +1190,13 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)),
|
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)),
|
||||||
&_previewDialog, SLOT(ConditionChanged(int)));
|
&_previewDialog, SLOT(ConditionChanged(int)));
|
||||||
QWidget::connect(_area, SIGNAL(Resized()), this, SLOT(Resize()));
|
QWidget::connect(_area, SIGNAL(Resized()), this, SLOT(Resize()));
|
||||||
|
QWidget::connect(entryData.get(),
|
||||||
|
&MacroConditionVideo::InputFileChanged, this,
|
||||||
|
[this]() {
|
||||||
|
UpdatePreviewTooltip();
|
||||||
|
_previewDialog.PatternMatchParametersChanged(
|
||||||
|
_entryData->_patternMatchParameters);
|
||||||
|
});
|
||||||
|
|
||||||
populateVideoInputSelection(_videoInputTypes);
|
populateVideoInputSelection(_videoInputTypes);
|
||||||
populateConditionSelection(_condition);
|
populateConditionSelection(_condition);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user