mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-09 10:06:04 -05:00
Compare commits
48 Commits
1.29.0
...
1.30.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f13beaead2 | ||
|
|
13dba6527d | ||
|
|
b78a6510a9 | ||
|
|
6c22f438b6 | ||
|
|
347abe6c84 | ||
|
|
230863adda | ||
|
|
f59478fac7 | ||
|
|
8adac79cd8 | ||
|
|
ea93c44db7 | ||
|
|
9e20b341d8 | ||
|
|
232cbb06f6 | ||
|
|
e9d57a0f4a | ||
|
|
9633a61a65 | ||
|
|
0643b250e1 | ||
|
|
b52738881f | ||
|
|
470d5ba3d7 | ||
|
|
8326e72047 | ||
|
|
12ab4d8cf9 | ||
|
|
295ec9eb81 | ||
|
|
4c5dbd4b7c | ||
|
|
5568f92ad0 | ||
|
|
7c4c0056ce | ||
|
|
d2b4b1cc07 | ||
|
|
a3ca22d238 | ||
|
|
c73542a3cc | ||
|
|
70bbc7cdac | ||
|
|
d892298995 | ||
|
|
0fe31432be | ||
|
|
aaa0113ccb | ||
|
|
b908954b46 | ||
|
|
b0eede8a85 | ||
|
|
aa87911b71 | ||
|
|
1b05019acc | ||
|
|
78a5a2629d | ||
|
|
634270a978 | ||
|
|
78ba22e1e4 | ||
|
|
aba5737a60 | ||
|
|
4315f7f621 | ||
|
|
53c535962f | ||
|
|
1e718b78c7 | ||
|
|
b1d2156228 | ||
|
|
9c3c953c6b | ||
|
|
ae74f68db7 | ||
|
|
213f1bba36 | ||
|
|
5a2cb0bd68 | ||
|
|
23b461828b | ||
|
|
9944a1b03b | ||
|
|
eaad4d1bbd |
5
.github/workflows/build-project.yaml
vendored
5
.github/workflows/build-project.yaml
vendored
@@ -210,6 +210,11 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-ccache-x86_64-
|
${{ runner.os }}-ccache-x86_64-
|
||||||
|
|
||||||
|
- name: Set up CMake 🏗️
|
||||||
|
uses: jwlawson/actions-setup-cmake@v1.13
|
||||||
|
with:
|
||||||
|
cmake-version: '3.24.x'
|
||||||
|
|
||||||
- name: Set up Homebrew 🍺
|
- name: Set up Homebrew 🍺
|
||||||
uses: Homebrew/actions/setup-homebrew@master
|
uses: Homebrew/actions/setup-homebrew@master
|
||||||
|
|
||||||
|
|||||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -28,3 +28,9 @@
|
|||||||
[submodule "deps/libusb"]
|
[submodule "deps/libusb"]
|
||||||
path = deps/libusb
|
path = deps/libusb
|
||||||
url = https://github.com/libusb/libusb.git
|
url = https://github.com/libusb/libusb.git
|
||||||
|
[submodule "deps/date"]
|
||||||
|
path = deps/date
|
||||||
|
url = https://github.com/HowardHinnant/date.git
|
||||||
|
[submodule "deps/jsoncons"]
|
||||||
|
path = deps/jsoncons
|
||||||
|
url = https://github.com/danielaparker/jsoncons.git
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ add_library(${LIB_NAME} SHARED)
|
|||||||
include(cmake/common/get_git_revision_description.cmake)
|
include(cmake/common/get_git_revision_description.cmake)
|
||||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||||
git_describe(GIT_TAG)
|
git_describe(GIT_TAG)
|
||||||
|
|
||||||
|
if(${GIT_TAG} STREQUAL "GIT-NOTFOUND")
|
||||||
|
set(GIT_TAG ${PROJECT_VERSION})
|
||||||
|
endif()
|
||||||
|
message(STATUS "${PROJECT_NAME} version: ${GIT_TAG}")
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/version.cpp.in"
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/version.cpp.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/lib/version.cpp" @ONLY)
|
"${CMAKE_CURRENT_BINARY_DIR}/lib/version.cpp" @ONLY)
|
||||||
|
|
||||||
@@ -190,6 +195,8 @@ target_sources(
|
|||||||
lib/utils/help-icon.cpp
|
lib/utils/help-icon.cpp
|
||||||
lib/utils/item-selection-helpers.cpp
|
lib/utils/item-selection-helpers.cpp
|
||||||
lib/utils/item-selection-helpers.hpp
|
lib/utils/item-selection-helpers.hpp
|
||||||
|
lib/utils/json-helpers.cpp
|
||||||
|
lib/utils/json-helpers.hpp
|
||||||
lib/utils/layout-helpers.cpp
|
lib/utils/layout-helpers.cpp
|
||||||
lib/utils/layout-helpers.hpp
|
lib/utils/layout-helpers.hpp
|
||||||
lib/utils/list-controls.cpp
|
lib/utils/list-controls.cpp
|
||||||
@@ -298,6 +305,13 @@ setup_obs_lib_dependency(${PROJECT_NAME})
|
|||||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
||||||
target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
||||||
|
|
||||||
|
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported Qt
|
||||||
|
# version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
|
||||||
|
if(Qt6_VERSION VERSION_GREATER "6.0.0")
|
||||||
|
target_compile_definitions(${LIB_NAME} PRIVATE QT_NO_DEPRECATED_WARNINGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_options(
|
target_compile_options(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
@@ -346,6 +360,21 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
target_link_libraries(${LIB_NAME} PUBLIC nlohmann_json::nlohmann_json)
|
target_link_libraries(${LIB_NAME} PUBLIC nlohmann_json::nlohmann_json)
|
||||||
|
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/deps/jsoncons/CMakeLists.txt")
|
||||||
|
# Don't build jsoncons unit tests as they are causing compilation issues and
|
||||||
|
# won't be executed either way
|
||||||
|
if(OS_MACOS)
|
||||||
|
cmake_policy(SET CMP0077 NEW)
|
||||||
|
endif()
|
||||||
|
set(JSONCONS_BUILD_TESTS
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
add_subdirectory(deps/jsoncons EXCLUDE_FROM_ALL)
|
||||||
|
target_link_libraries(${LIB_NAME} PRIVATE jsoncons)
|
||||||
|
target_compile_definitions(${LIB_NAME} PRIVATE JSONPATH_SUPPORT=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(CURL QUIET)
|
find_package(CURL QUIET)
|
||||||
find_package(Libcurl QUIET)
|
find_package(Libcurl QUIET)
|
||||||
if(CURL_FOUND)
|
if(CURL_FOUND)
|
||||||
@@ -431,6 +460,9 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
|
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
|
||||||
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp)
|
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp)
|
||||||
|
|
||||||
|
# Don't include irrelevant folders into sources archive
|
||||||
|
list(APPEND CPACK_SOURCE_IGNORE_FILES "\\.deps/.*")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT OS_WINDOWS)
|
if(NOT OS_WINDOWS)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ The **Snap** package manager offers an OBS Studio installation which is bundled
|
|||||||
sudo snap install obs-studio
|
sudo snap install obs-studio
|
||||||
```
|
```
|
||||||
|
|
||||||
|
More information can be found [here](https://github.com/WarmUpTill/SceneSwitcher/wiki/Installation).
|
||||||
|
|
||||||
## 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.
|
||||||
|
|||||||
@@ -286,6 +286,12 @@ function(setup_advss_plugin target)
|
|||||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||||
target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
|
target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
|
||||||
|
|
||||||
|
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported
|
||||||
|
# Qt version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
|
||||||
|
if(Qt6_VERSION VERSION_GREATER "6.7.0")
|
||||||
|
target_compile_definitions(${target} PRIVATE QT_NO_DEPRECATED_WARNINGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
${target}
|
${target}
|
||||||
PROPERTIES AUTOMOC ON
|
PROPERTIES AUTOMOC ON
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Wechsle zu
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Wechsle zu:"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Wechsle zu:"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach Ausführen von Aktionen überspringe das Ausführen von Aktionen für"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach Ausführen von Aktionen überspringe das Ausführen von Aktionen für"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="In diesem Zeitraum werden potentielle erfüllte Bedingungen ignoriert!"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="In diesem Zeitraum werden potentielle erfüllte Bedingungen ignoriert!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ausführliches Logging"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Fensterposition und -größe speichern"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Fensterposition und -größe speichern"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Benachrichtigungen im System-Tray anzeigen"
|
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Benachrichtigungen im System-Tray anzeigen"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deaktiviere UI Tipps"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deaktiviere UI Tipps"
|
||||||
@@ -526,7 +525,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Standard"
|
|||||||
AdvSceneSwitcher.action.screenshot.save.custom="Benutzerdefiniert"
|
AdvSceneSwitcher.action.screenshot.save.custom="Benutzerdefiniert"
|
||||||
AdvSceneSwitcher.action.screenshot.type.source="Quelle"
|
AdvSceneSwitcher.action.screenshot.type.source="Quelle"
|
||||||
AdvSceneSwitcher.action.screenshot.type.scene="Szene"
|
AdvSceneSwitcher.action.screenshot.type.scene="Szene"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's Haupt-Ausgabe"
|
|
||||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="Quellen oder Szenen, die nicht immer gerendert werden, können dazu führen, dass einige Teile der Screenshots leer bleiben."
|
AdvSceneSwitcher.action.screenshot.blackscreenNote="Quellen oder Szenen, die nicht immer gerendert werden, können dazu führen, dass einige Teile der Screenshots leer bleiben."
|
||||||
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}{{variables}}Pfad"
|
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}{{variables}}Pfad"
|
||||||
AdvSceneSwitcher.action.profile="Profil"
|
AdvSceneSwitcher.action.profile="Profil"
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Switch to:"
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After performing actions skip performing actions for"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After performing actions skip performing actions for"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel="Log level:"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel="Log level:"
|
||||||
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.disable="Disable logging"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="Default"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="Default"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logMacro="Log executed macros"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logMacro="Log executed macros"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="Log performed actions"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="Log performed actions"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="Verbose logging"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="Verbose logging"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
|
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
||||||
@@ -199,8 +199,11 @@ AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of curre
|
|||||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
|
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
|
||||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
|
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
|
||||||
AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for new macros"
|
AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for new macros"
|
||||||
AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange="Save settings when selecting a new macro"
|
AdvSceneSwitcher.macroTab.newMacroCheckInParallel="Evaluate conditions of new macros in parallel to other macros"
|
||||||
|
AdvSceneSwitcher.macroTab.checkInParallel.tooltip="If this option is not enabled, this macro's conditions will be evaluated after the preceeding macro's condition check has completed.\nSo, if there are three macros - Macro A, Macro B, and Macro C - in the macro list the order the conditions in are evaluated is:\n • Macro A's conditions\n • Macro B's conditions\n • Macro C's conditions\nIf this option is enabled, the order might differ.\nEnabling this option can be useful for scenarios in which you have multiple long running condition checks, which you don't want to result in \"blocking\" other macros."
|
||||||
|
AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange="Save settings when switching between macros"
|
||||||
AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange.tooltip="Saving the settings can be an expensive operation if you are working with a very large scene collection.\nIn this case, it might make sense to disable this option."
|
AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange.tooltip="Saving the settings can be an expensive operation if you are working with a very large scene collection.\nIn this case, it might make sense to disable this option."
|
||||||
|
AdvSceneSwitcher.macroTab.currentCheckInParallel="Evaluate conditions of selected macro in parallel to other macros"
|
||||||
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="Register hotkeys to control the pause state of selected macro"
|
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="Register hotkeys to control the pause state of selected macro"
|
||||||
AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for currently selected macro"
|
AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for currently selected macro"
|
||||||
AdvSceneSwitcher.macroTab.shortCircuit.tooltip="Enabling short circuit evaluation might improve the performance, as some condition checks are skipped, if the overall macro cannot be evaluated to \"true\" anymore.\nHowever, please note that condition checks, which are skipped over, will also not update their duration modifier checks."
|
AdvSceneSwitcher.macroTab.shortCircuit.tooltip="Enabling short circuit evaluation might improve the performance, as some condition checks are skipped, if the overall macro cannot be evaluated to \"true\" anymore.\nHowever, please note that condition checks, which are skipped over, will also not update their duration modifier checks."
|
||||||
@@ -1022,7 +1025,6 @@ AdvSceneSwitcher.action.screenshot.save.custom="Custom file path"
|
|||||||
AdvSceneSwitcher.action.screenshot.save.variable="Variable (base64 encoded PNG)"
|
AdvSceneSwitcher.action.screenshot.save.variable="Variable (base64 encoded PNG)"
|
||||||
AdvSceneSwitcher.action.screenshot.type.source="Source"
|
AdvSceneSwitcher.action.screenshot.type.source="Source"
|
||||||
AdvSceneSwitcher.action.screenshot.type.scene="Scene"
|
AdvSceneSwitcher.action.screenshot.type.scene="Scene"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's main output"
|
|
||||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="Sources or scenes, which are not always rendered, may result in some parts of screenshots to remain blank."
|
AdvSceneSwitcher.action.screenshot.blackscreenNote="Sources or scenes, which are not always rendered, may result in some parts of screenshots to remain blank."
|
||||||
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}and save to{{saveType}}{{variables}}"
|
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}and save to{{saveType}}{{variables}}"
|
||||||
AdvSceneSwitcher.action.profile="Profile"
|
AdvSceneSwitcher.action.profile="Profile"
|
||||||
@@ -1051,11 +1053,25 @@ AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request="Send{{api}}of typ
|
|||||||
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients"
|
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients"
|
||||||
AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}"
|
AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}"
|
||||||
AdvSceneSwitcher.action.http="HTTP"
|
AdvSceneSwitcher.action.http="HTTP"
|
||||||
|
AdvSceneSwitcher.action.http.legacy="HTTP (legacy)"
|
||||||
AdvSceneSwitcher.action.http.setHeaders="Set headers"
|
AdvSceneSwitcher.action.http.setHeaders="Set headers"
|
||||||
AdvSceneSwitcher.action.http.headers="Headers:"
|
AdvSceneSwitcher.action.http.headers="Headers:"
|
||||||
AdvSceneSwitcher.action.http.addHeader="Add header"
|
AdvSceneSwitcher.action.http.addHeader="Add header"
|
||||||
|
AdvSceneSwitcher.action.http.addHeader.name="Header name"
|
||||||
|
AdvSceneSwitcher.action.http.addHeader.value="Header value"
|
||||||
|
AdvSceneSwitcher.action.http.setParams="Set parameters"
|
||||||
|
AdvSceneSwitcher.action.http.params="Parameters:"
|
||||||
|
AdvSceneSwitcher.action.http.addParam.name="Parameter name"
|
||||||
|
AdvSceneSwitcher.action.http.addParam.value="Parameter value"
|
||||||
|
AdvSceneSwitcher.action.http.body="Message body:"
|
||||||
AdvSceneSwitcher.action.http.type.get="GET"
|
AdvSceneSwitcher.action.http.type.get="GET"
|
||||||
AdvSceneSwitcher.action.http.type.post="POST"
|
AdvSceneSwitcher.action.http.type.post="POST"
|
||||||
|
AdvSceneSwitcher.action.http.type.put="PUT"
|
||||||
|
AdvSceneSwitcher.action.http.type.patch="PATCH"
|
||||||
|
AdvSceneSwitcher.action.http.type.delete="DELETE"
|
||||||
|
AdvSceneSwitcher.action.http.layout.method="Send{{method}}to URL{{url}}at path{{path}}"
|
||||||
|
AdvSceneSwitcher.action.http.layout.contentType="Content type:{{contentType}}"
|
||||||
|
AdvSceneSwitcher.action.http.layout.timeout="Timeout:{{timeout}}seconds"
|
||||||
AdvSceneSwitcher.action.http.entry.line1="Send{{method}}to{{url}}"
|
AdvSceneSwitcher.action.http.entry.line1="Send{{method}}to{{url}}"
|
||||||
AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds"
|
AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds"
|
||||||
AdvSceneSwitcher.action.variable="Variable"
|
AdvSceneSwitcher.action.variable="Variable"
|
||||||
@@ -1074,7 +1090,10 @@ AdvSceneSwitcher.action.variable.type.askForValue="Get user input"
|
|||||||
AdvSceneSwitcher.action.variable.type.environmentVariable="Set to environment variable value"
|
AdvSceneSwitcher.action.variable.type.environmentVariable="Set to environment variable value"
|
||||||
AdvSceneSwitcher.action.variable.type.sceneItemCount="Set to scene item count of scene"
|
AdvSceneSwitcher.action.variable.type.sceneItemCount="Set to scene item count of scene"
|
||||||
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
|
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
|
||||||
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
|
AdvSceneSwitcher.action.variable.type.extractJsonField="Extract JSON field with name"
|
||||||
|
AdvSceneSwitcher.action.variable.type.queryJson="Query JSON"
|
||||||
|
AdvSceneSwitcher.action.variable.type.queryJson.info="You can use \"JSONPath\" (RFC 9535) syntax here.\nSo, for example:\n\n • $['books'][0]['category']\n • $.books[0].category\n\nIf the query or the JSON should be invalid, the variable value will not be changed.\nIf the input is valid, the result of the query will always be a JSON array."
|
||||||
|
AdvSceneSwitcher.action.variable.type.accessJsonArray="Access JSON array at index"
|
||||||
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
|
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
|
||||||
AdvSceneSwitcher.action.variable.type.setToTempvar.help="This action type will allow you to extract values out of segments of the current macro and assign those values to the selected variable.\nFor example, you can get the current scene name from a scene condition and assign this name to a variable."
|
AdvSceneSwitcher.action.variable.type.setToTempvar.help="This action type will allow you to extract values out of segments of the current macro and assign those values to the selected variable.\nFor example, you can get the current scene name from a scene condition and assign this name to a variable."
|
||||||
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
|
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
|
||||||
@@ -1103,16 +1122,18 @@ AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
|
|||||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
|
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
|
||||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
|
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
|
||||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
|
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
|
||||||
AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}"
|
AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
|
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
|
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
|
AdvSceneSwitcher.action.variable.layout.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
|
||||||
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.randomNumber="Generate random number in range from{{randomNumberStart}}to{{randomNumberEnd}}"
|
AdvSceneSwitcher.action.variable.layout.randomNumber="Generate random number in range from{{randomNumberStart}}to{{randomNumberEnd}}"
|
||||||
AdvSceneSwitcher.action.projector="Projector"
|
AdvSceneSwitcher.action.projector="Projector"
|
||||||
|
AdvSceneSwitcher.action.projector.action.open="Open"
|
||||||
|
AdvSceneSwitcher.action.projector.action.close="Close"
|
||||||
AdvSceneSwitcher.action.projector.type.source="Source"
|
AdvSceneSwitcher.action.projector.type.source="Source"
|
||||||
AdvSceneSwitcher.action.projector.type.scene="Scene"
|
AdvSceneSwitcher.action.projector.type.scene="Scene"
|
||||||
AdvSceneSwitcher.action.projector.type.preview="Preview"
|
AdvSceneSwitcher.action.projector.type.preview="Preview"
|
||||||
@@ -1121,8 +1142,9 @@ AdvSceneSwitcher.action.projector.type.multiview="Multiview"
|
|||||||
AdvSceneSwitcher.action.projector.display="Display"
|
AdvSceneSwitcher.action.projector.display="Display"
|
||||||
AdvSceneSwitcher.action.projector.windowed="Windowed"
|
AdvSceneSwitcher.action.projector.windowed="Windowed"
|
||||||
AdvSceneSwitcher.action.projector.fullscreen="Fullscreen"
|
AdvSceneSwitcher.action.projector.fullscreen="Fullscreen"
|
||||||
AdvSceneSwitcher.action.projector.entry="Open{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
|
AdvSceneSwitcher.action.projector.entry.close="{{actions}}projector with name{{projectorWindowName}}{{regex}}"
|
||||||
AdvSceneSwitcher.action.projector.entry.monitor="on{{monitors}}"
|
AdvSceneSwitcher.action.projector.entry.open.windowed="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
|
||||||
|
AdvSceneSwitcher.action.projector.entry.open.fullscreen="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}on{{monitors}}"
|
||||||
AdvSceneSwitcher.action.midi="MIDI"
|
AdvSceneSwitcher.action.midi="MIDI"
|
||||||
AdvSceneSwitcher.action.midi.entry="Send message to{{device}}:"
|
AdvSceneSwitcher.action.midi.entry="Send message to{{device}}:"
|
||||||
AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on{{listenDevices}}:{{listenButton}}"
|
AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on{{listenDevices}}:{{listenButton}}"
|
||||||
@@ -1393,6 +1415,7 @@ AdvSceneSwitcher.twitchToken.moderator.chat.settings.manage="Manage channel's ch
|
|||||||
AdvSceneSwitcher.twitchToken.user.whispers.manage="Manage user's whispers."
|
AdvSceneSwitcher.twitchToken.user.whispers.manage="Manage user's whispers."
|
||||||
AdvSceneSwitcher.twitchToken.chat.read="View live stream chat messages."
|
AdvSceneSwitcher.twitchToken.chat.read="View live stream chat messages."
|
||||||
AdvSceneSwitcher.twitchToken.chat.edit="Send live stream chat messages."
|
AdvSceneSwitcher.twitchToken.chat.edit="Send live stream chat messages."
|
||||||
|
AdvSceneSwitcher.twitchToken.validateTimestamps="Validate timestamps of received Twitch event messages. (Recommended)"
|
||||||
|
|
||||||
AdvSceneSwitcher.twitch.selection.channel.open="Open channel"
|
AdvSceneSwitcher.twitch.selection.channel.open="Open channel"
|
||||||
AdvSceneSwitcher.twitch.selection.channel.open.tooltip.details="Open channel in external application handling the HTTPS protocol."
|
AdvSceneSwitcher.twitch.selection.channel.open.tooltip.details="Open channel in external application handling the HTTPS protocol."
|
||||||
@@ -2038,6 +2061,11 @@ AdvSceneSwitcher.tempVar.gameCapture.class.description="Window class of the appl
|
|||||||
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
|
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
|
||||||
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
|
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
|
||||||
|
|
||||||
|
AdvSceneSwitcher.tempVar.http.status="Status code"
|
||||||
|
AdvSceneSwitcher.tempVar.http.body="Message body"
|
||||||
|
AdvSceneSwitcher.tempVar.http.error="Error"
|
||||||
|
AdvSceneSwitcher.tempVar.http.error.description="Empty when no error occurred.\nOther possible values:\n\n * Could not establish connection\n * Failed to bind IP address\n * Failed to read connection\n * Failed to write connection\n * Maximum redirect count exceeded\n * Connection handling canceled\n * SSL connection failed\n * SSL certificate loading failed\n * SSL server verification failed\n * Unsupported HTTP multipart boundary characters\n * Compression failed\n * Connection timed out\n * Proxy connection failed\n * Unknown"
|
||||||
|
|
||||||
AdvSceneSwitcher.selectScene="--select scene--"
|
AdvSceneSwitcher.selectScene="--select scene--"
|
||||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||||
@@ -2076,7 +2104,6 @@ AdvSceneSwitcher.settings.suffix.type.list=" (List)"
|
|||||||
AdvSceneSwitcher.settings.suffix.type.color=" (Color)"
|
AdvSceneSwitcher.settings.suffix.type.color=" (Color)"
|
||||||
AdvSceneSwitcher.settings.suffix.type.button=" (Button)"
|
AdvSceneSwitcher.settings.suffix.type.button=" (Button)"
|
||||||
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
|
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
|
||||||
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
|
|
||||||
AdvSceneSwitcher.settings.suffix.type.editableList=" (Editable list)"
|
AdvSceneSwitcher.settings.suffix.type.editableList=" (Editable list)"
|
||||||
AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
|
AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
|
||||||
AdvSceneSwitcher.settings.suffix.type.group=" (Group)"
|
AdvSceneSwitcher.settings.suffix.type.group=" (Group)"
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="No cambiar"
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Cambiar a una escena aleatoria"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Cambiar a una escena aleatoria"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Cambiar a:"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Cambiar a:"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="¡Durante este tiempo, se ignorarán las posibles coincidencias!"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="¡Durante este tiempo, se ignorarán las posibles coincidencias!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Habilitar el registro detallado"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Guardar la posición y el tamaño de la ventana"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Guardar la posición y el tamaño de la ventana"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificaciones del sistema"
|
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificaciones del sistema"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deshabilitar sugerencias de interfaz de usuario"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deshabilitar sugerencias de interfaz de usuario"
|
||||||
@@ -438,7 +437,6 @@ AdvSceneSwitcher.action.random="Aleatorio"
|
|||||||
AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)"
|
AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)"
|
||||||
AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
|
AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
|
||||||
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
|
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
|
|
||||||
AdvSceneSwitcher.action.profile="Perfil"
|
AdvSceneSwitcher.action.profile="Perfil"
|
||||||
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
|
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
|
||||||
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
|
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Basculer v
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Basculer vers :"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Basculer vers :"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Après avoir effectué des actions, sauter l'exécution d'actions pendant"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Après avoir effectué des actions, sauter l'exécution d'actions pendant"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Pendant cette période, les correspondances potentielles seront ignorées !"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Pendant cette période, les correspondances potentielles seront ignorées !"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Activer les journaux détaillés"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Enregistrer la position et la taille de la fenêtre"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Enregistrer la position et la taille de la fenêtre"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Afficher les notifications dans la zone de notification système"
|
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Afficher les notifications dans la zone de notification système"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Désactiver les indications de l'interface utilisateur"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Désactiver les indications de l'interface utilisateur"
|
||||||
@@ -647,7 +646,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Par défaut"
|
|||||||
AdvSceneSwitcher.action.screenshot.save.custom="Personnalisé"
|
AdvSceneSwitcher.action.screenshot.save.custom="Personnalisé"
|
||||||
AdvSceneSwitcher.action.screenshot.type.source="Source"
|
AdvSceneSwitcher.action.screenshot.type.source="Source"
|
||||||
AdvSceneSwitcher.action.screenshot.type.scene="Scène"
|
AdvSceneSwitcher.action.screenshot.type.scene="Scène"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="Sortie principale d'OBS"
|
|
||||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="Les sources ou les scènes qui ne sont pas toujours rendues peuvent entraîner des parties de captures d'écran vides."
|
AdvSceneSwitcher.action.screenshot.blackscreenNote="Les sources ou les scènes qui ne sont pas toujours rendues peuvent entraîner des parties de captures d'écran vides."
|
||||||
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}{{variables}}"
|
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}{{variables}}"
|
||||||
AdvSceneSwitcher.action.profile="Profil"
|
AdvSceneSwitcher.action.profile="Profil"
|
||||||
@@ -705,10 +703,10 @@ AdvSceneSwitcher.action.variable.invalidSelection="Sélection invalide !"
|
|||||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !"
|
AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !"
|
||||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !"
|
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !"
|
||||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :"
|
AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
|
AdvSceneSwitcher.action.variable.layout.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière :"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière :"
|
||||||
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
|
||||||
AdvSceneSwitcher.action.projector="Projecteur"
|
AdvSceneSwitcher.action.projector="Projecteur"
|
||||||
AdvSceneSwitcher.action.projector.type.source="Source"
|
AdvSceneSwitcher.action.projector.type.source="Source"
|
||||||
AdvSceneSwitcher.action.projector.type.scene="Scène"
|
AdvSceneSwitcher.action.projector.type.scene="Scène"
|
||||||
@@ -718,8 +716,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivue"
|
|||||||
AdvSceneSwitcher.action.projector.display="Affichage"
|
AdvSceneSwitcher.action.projector.display="Affichage"
|
||||||
AdvSceneSwitcher.action.projector.windowed="Fenêtré"
|
AdvSceneSwitcher.action.projector.windowed="Fenêtré"
|
||||||
AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
|
AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
|
||||||
AdvSceneSwitcher.action.projector.entry="Ouvrir le projecteur{{windowTypes}}de{{types}}{{scenes}}{{sources}}"
|
|
||||||
AdvSceneSwitcher.action.projector.entry.monitor="sur{{monitors}}"
|
|
||||||
AdvSceneSwitcher.action.midi="MIDI"
|
AdvSceneSwitcher.action.midi="MIDI"
|
||||||
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
|
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
|
||||||
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"
|
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.logLevel="ログレベル:"
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="デフォルト"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="デフォルト"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="実行されたアクションのログ"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="実行されたアクションのログ"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="詳細ログ(Verbose)"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="詳細ログ(Verbose)"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="詳細ログを有効にする(生成されるログファイルのサイズが増加します)"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="ウィンドウのサイズと位置を記憶する"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="ウィンドウのサイズと位置を記憶する"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="システムトレイ通知を表示"
|
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="システムトレイ通知を表示"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="UIヒントを無効にする"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="UIヒントを無効にする"
|
||||||
@@ -979,7 +978,6 @@ AdvSceneSwitcher.action.screenshot.save.default="デフォルト"
|
|||||||
AdvSceneSwitcher.action.screenshot.save.custom="カスタム"
|
AdvSceneSwitcher.action.screenshot.save.custom="カスタム"
|
||||||
; AdvSceneSwitcher.action.screenshot.type.source="Source"
|
; AdvSceneSwitcher.action.screenshot.type.source="Source"
|
||||||
AdvSceneSwitcher.action.screenshot.type.scene="シーン"
|
AdvSceneSwitcher.action.screenshot.type.scene="シーン"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBSの主な出力"
|
|
||||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="常にレンダリングされるわけではないソースやシーンにより、スクリーンショットの一部が空白のままになる場合があります。"
|
AdvSceneSwitcher.action.screenshot.blackscreenNote="常にレンダリングされるわけではないソースやシーンにより、スクリーンショットの一部が空白のままになる場合があります。"
|
||||||
AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}{{variables}}の場所に保存します"
|
AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}{{variables}}の場所に保存します"
|
||||||
AdvSceneSwitcher.action.profile="プロファイル"
|
AdvSceneSwitcher.action.profile="プロファイル"
|
||||||
@@ -1031,7 +1029,7 @@ AdvSceneSwitcher.action.variable.type.askForValue="入力ダイアログを表
|
|||||||
AdvSceneSwitcher.action.variable.type.environmentVariable="特定の環境変数の値に設定します"
|
AdvSceneSwitcher.action.variable.type.environmentVariable="特定の環境変数の値に設定します"
|
||||||
AdvSceneSwitcher.action.variable.type.sceneItemCount="特定のシーンのシーン項目の数の値にします"
|
AdvSceneSwitcher.action.variable.type.sceneItemCount="特定のシーンのシーン項目の数の値にします"
|
||||||
AdvSceneSwitcher.action.variable.type.stringLength="変数値を指定された入力文字列の長さに設定"
|
AdvSceneSwitcher.action.variable.type.stringLength="変数値を指定された入力文字列の長さに設定"
|
||||||
AdvSceneSwitcher.action.variable.type.extractJson="変数から json フィールドを抽出する"
|
AdvSceneSwitcher.action.variable.type.extractJsonField="変数から json フィールドを抽出する"
|
||||||
AdvSceneSwitcher.action.variable.type.setToTempvar="マクロプロパティに設定"
|
AdvSceneSwitcher.action.variable.type.setToTempvar="マクロプロパティに設定"
|
||||||
AdvSceneSwitcher.action.variable.type.setToTempvar.help="このアクション タイプを使用すると、現在のマクロのセグメントから値を抽出し、それらの値を選択した変数に割り当てることができます。\nたとえば、シーン条件から現在のシーン名を取得し、この名前を変数に割り当てることができます。"
|
AdvSceneSwitcher.action.variable.type.setToTempvar.help="このアクション タイプを使用すると、現在のマクロのセグメントから値を抽出し、それらの値を選択した変数に割り当てることができます。\nたとえば、シーン条件から現在のシーン名を取得し、この名前を変数に割り当てることができます。"
|
||||||
AdvSceneSwitcher.action.variable.type.sceneItemName="インデックスのシーン項目名に設定"
|
AdvSceneSwitcher.action.variable.type.sceneItemName="インデックスのシーン項目名に設定"
|
||||||
@@ -1051,14 +1049,14 @@ AdvSceneSwitcher.action.variable.invalidSelection="選択が無効です!"
|
|||||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="%1 アクションからの変数値の取得はサポートされていません!"
|
AdvSceneSwitcher.action.variable.actionNoVariableSupport="%1 アクションからの変数値の取得はサポートされていません!"
|
||||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="%1 条件からの変数値の取得はサポートされていません!"
|
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="%1 条件からの変数値の取得はサポートされていません!"
|
||||||
AdvSceneSwitcher.action.variable.currentSegmentValue="現在値:"
|
AdvSceneSwitcher.action.variable.currentSegmentValue="現在値:"
|
||||||
; AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
; AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。"
|
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。"
|
||||||
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。"
|
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}"
|
AdvSceneSwitcher.action.variable.layout.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:"
|
||||||
; AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
; AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}"
|
||||||
AdvSceneSwitcher.action.projector="プロジェクター"
|
AdvSceneSwitcher.action.projector="プロジェクター"
|
||||||
AdvSceneSwitcher.action.projector.type.source="ソース"
|
AdvSceneSwitcher.action.projector.type.source="ソース"
|
||||||
AdvSceneSwitcher.action.projector.type.scene="シーン"
|
AdvSceneSwitcher.action.projector.type.scene="シーン"
|
||||||
@@ -1068,8 +1066,6 @@ AdvSceneSwitcher.action.projector.type.multiview="マルチビュー"
|
|||||||
AdvSceneSwitcher.action.projector.display="ディスプレイ"
|
AdvSceneSwitcher.action.projector.display="ディスプレイ"
|
||||||
AdvSceneSwitcher.action.projector.windowed="ウィンドウ"
|
AdvSceneSwitcher.action.projector.windowed="ウィンドウ"
|
||||||
AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン"
|
AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン"
|
||||||
AdvSceneSwitcher.action.projector.entry="{{types}}{{scenes}}{{sources}}の{{windowTypes}}プロジェクターを開きます"
|
|
||||||
AdvSceneSwitcher.action.projector.entry.monitor="{{monitors}}上で"
|
|
||||||
; AdvSceneSwitcher.action.midi="MIDI"
|
; AdvSceneSwitcher.action.midi="MIDI"
|
||||||
AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:"
|
AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:"
|
||||||
AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}"
|
AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}"
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.logLevel="Nível de log:"
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="Padrão"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="Padrão"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="Registrar ações executadas"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="Registrar ações executadas"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="Registro detalhado"
|
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="Registro detalhado"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ativar registro detalhado"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Salvar posição e tamanho da janela"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Salvar posição e tamanho da janela"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificações na área de notificação"
|
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificações na área de notificação"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Desativar dicas de interface"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Desativar dicas de interface"
|
||||||
@@ -960,7 +959,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Padrão"
|
|||||||
AdvSceneSwitcher.action.screenshot.save.custom="Personalizado"
|
AdvSceneSwitcher.action.screenshot.save.custom="Personalizado"
|
||||||
AdvSceneSwitcher.action.screenshot.type.source="Fonte"
|
AdvSceneSwitcher.action.screenshot.type.source="Fonte"
|
||||||
AdvSceneSwitcher.action.screenshot.type.scene="Cena"
|
AdvSceneSwitcher.action.screenshot.type.scene="Cena"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="Saída principal do OBS"
|
|
||||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="Fontes ou cenas, que nem sempre são renderizadas, podem resultar em algumas partes das capturas de tela permanecendo em branco."
|
AdvSceneSwitcher.action.screenshot.blackscreenNote="Fontes ou cenas, que nem sempre são renderizadas, podem resultar em algumas partes das capturas de tela permanecendo em branco."
|
||||||
AdvSceneSwitcher.action.screenshot.entry="Captura de tela{{targetType}}{{sources}}{{scenes}}e salvar em{{saveType}}{{variables}}localização"
|
AdvSceneSwitcher.action.screenshot.entry="Captura de tela{{targetType}}{{sources}}{{scenes}}e salvar em{{saveType}}{{variables}}localização"
|
||||||
AdvSceneSwitcher.action.profile="Perfil"
|
AdvSceneSwitcher.action.profile="Perfil"
|
||||||
@@ -1013,7 +1011,7 @@ AdvSceneSwitcher.action.variable.type.askForValue="Obter entrada do usuário"
|
|||||||
AdvSceneSwitcher.action.variable.type.environmentVariable="Definir valor da variável de ambiente"
|
AdvSceneSwitcher.action.variable.type.environmentVariable="Definir valor da variável de ambiente"
|
||||||
AdvSceneSwitcher.action.variable.type.sceneItemCount="Definir para contagem de itens de cena"
|
AdvSceneSwitcher.action.variable.type.sceneItemCount="Definir para contagem de itens de cena"
|
||||||
AdvSceneSwitcher.action.variable.type.stringLength="Definir comprimento da string"
|
AdvSceneSwitcher.action.variable.type.stringLength="Definir comprimento da string"
|
||||||
AdvSceneSwitcher.action.variable.type.extractJson="Extrair campo json com nome"
|
AdvSceneSwitcher.action.variable.type.extractJsonField="Extrair campo json com nome"
|
||||||
AdvSceneSwitcher.action.variable.type.setToTempvar="Definir para propriedade da macro"
|
AdvSceneSwitcher.action.variable.type.setToTempvar="Definir para propriedade da macro"
|
||||||
AdvSceneSwitcher.action.variable.type.setToTempvar.help="Este tipo de ação permite extrair valores de segmentos da macro atual e atribuir esses valores à variável selecionada.\nPor exemplo, você pode obter o nome da cena atual de uma condição de cena e atribuir esse nome a uma variável."
|
AdvSceneSwitcher.action.variable.type.setToTempvar.help="Este tipo de ação permite extrair valores de segmentos da macro atual e atribuir esses valores à variável selecionada.\nPor exemplo, você pode obter o nome da cena atual de uma condição de cena e atribuir esse nome a uma variável."
|
||||||
AdvSceneSwitcher.action.variable.type.sceneItemName="Definir para nome do item de cena no índice"
|
AdvSceneSwitcher.action.variable.type.sceneItemName="Definir para nome do item de cena no índice"
|
||||||
@@ -1033,13 +1031,13 @@ AdvSceneSwitcher.action.variable.invalidSelection="Seleção inválida!"
|
|||||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Obter valores de variáveis das ações %1 não é suportado!"
|
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Obter valores de variáveis das ações %1 não é suportado!"
|
||||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Obter valores de variáveis das condições %1 não é suportado!"
|
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Obter valores de variáveis das condições %1 não é suportado!"
|
||||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:"
|
AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:"
|
||||||
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
|
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
|
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
|
AdvSceneSwitcher.action.variable.layout.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:"
|
||||||
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
|
||||||
AdvSceneSwitcher.action.projector="Projetor"
|
AdvSceneSwitcher.action.projector="Projetor"
|
||||||
AdvSceneSwitcher.action.projector.type.source="Fonte"
|
AdvSceneSwitcher.action.projector.type.source="Fonte"
|
||||||
AdvSceneSwitcher.action.projector.type.scene="Cena"
|
AdvSceneSwitcher.action.projector.type.scene="Cena"
|
||||||
@@ -1049,8 +1047,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivisão"
|
|||||||
AdvSceneSwitcher.action.projector.display="Exibição"
|
AdvSceneSwitcher.action.projector.display="Exibição"
|
||||||
AdvSceneSwitcher.action.projector.windowed="Janela"
|
AdvSceneSwitcher.action.projector.windowed="Janela"
|
||||||
AdvSceneSwitcher.action.projector.fullscreen="Tela cheia"
|
AdvSceneSwitcher.action.projector.fullscreen="Tela cheia"
|
||||||
AdvSceneSwitcher.action.projector.entry="Abrir{{windowTypes}}projetor de{{types}}{{scenes}}{{sources}}"
|
|
||||||
AdvSceneSwitcher.action.projector.entry.monitor="em{{monitors}}"
|
|
||||||
AdvSceneSwitcher.action.midi="MIDI"
|
AdvSceneSwitcher.action.midi="MIDI"
|
||||||
AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:"
|
AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:"
|
||||||
AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}"
|
AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}"
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="Не пере
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Переключиться на любую сцену на вкладке Random"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Переключиться на любую сцену на вкладке Random"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Переключиться на:"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Переключиться на:"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Включить ведение подробного журнала"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Отключить подсказки пользовательского интерфейса"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Отключить подсказки пользовательского интерфейса"
|
||||||
AdvSceneSwitcher.generalTab.priority="Приоритет"
|
AdvSceneSwitcher.generalTab.priority="Приоритет"
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Rastgele s
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Değiştirmek:"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Değiştirmek:"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Bir eşleşmeden sonra aşağıdakiler için işlem yapmayın:"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Bir eşleşmeden sonra aşağıdakiler için işlem yapmayın:"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Bu süre zarfında olası eşleşmeler göz ardı edilecektir!"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Bu süre zarfında olası eşleşmeler göz ardı edilecektir!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ayrıntılı günlük kaydını etkinleştir"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Pencere konumunu ve boyutunu kaydet"
|
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Pencere konumunu ve boyutunu kaydet"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Sistem tepsisi bildirimlerini göster"
|
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Sistem tepsisi bildirimlerini göster"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="UI ipuçlarını devre dışı bırak"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="UI ipuçlarını devre dışı bırak"
|
||||||
@@ -356,7 +355,6 @@ AdvSceneSwitcher.action.random="Rastgele"
|
|||||||
AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)"
|
AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)"
|
||||||
AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
|
AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
|
||||||
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
|
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
|
|
||||||
AdvSceneSwitcher.action.profile="Profil"
|
AdvSceneSwitcher.action.profile="Profil"
|
||||||
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
|
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
|
||||||
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
|
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1
deps/date
vendored
Submodule
1
deps/date
vendored
Submodule
Submodule deps/date added at 5bdb7e6f31
1
deps/jsoncons
vendored
Submodule
1
deps/jsoncons
vendored
Submodule
Submodule deps/jsoncons added at 64b9da1e9f
2
deps/libremidi
vendored
2
deps/libremidi
vendored
Submodule deps/libremidi updated: d6d6557b5b...73e6ea40de
@@ -182,26 +182,6 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="logLevel">
|
<widget class="QComboBox" name="logLevel">
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logMacro</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
#include "advanced-scene-switcher.hpp"
|
||||||
#include "backup.hpp"
|
#include "backup.hpp"
|
||||||
#include "curl-helper.hpp"
|
|
||||||
#include "log-helper.hpp"
|
#include "log-helper.hpp"
|
||||||
#include "macro-helpers.hpp"
|
#include "macro-helpers.hpp"
|
||||||
#include "obs-module-helper.hpp"
|
#include "obs-module-helper.hpp"
|
||||||
@@ -19,10 +18,15 @@
|
|||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
|
#include <QLibrary>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
|
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
|
||||||
@@ -307,7 +311,7 @@ void SwitcherData::Thread()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetForNextInterval();
|
RunIntervalResetSteps();
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
if (macroMatch) {
|
if (macroMatch) {
|
||||||
@@ -357,14 +361,6 @@ void SwitcherData::SetPreconditions()
|
|||||||
InvalidateMacroTempVarValues();
|
InvalidateMacroTempVarValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::ResetForNextInterval()
|
|
||||||
{
|
|
||||||
// Plugin reset functions
|
|
||||||
for (const auto &func : resetIntervalSteps) {
|
|
||||||
func();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
|
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition, int &linger,
|
OBSWeakSource &transition, int &linger,
|
||||||
bool &setPrevSceneAfterLinger,
|
bool &setPrevSceneAfterLinger,
|
||||||
@@ -445,7 +441,7 @@ void AutoStartActionQueues();
|
|||||||
void SwitcherData::Start()
|
void SwitcherData::Start()
|
||||||
{
|
{
|
||||||
if (!(th && th->isRunning())) {
|
if (!(th && th->isRunning())) {
|
||||||
ResetForNextInterval();
|
RunIntervalResetSteps();
|
||||||
ResetMacros();
|
ResetMacros();
|
||||||
AutoStartActionQueues();
|
AutoStartActionQueues();
|
||||||
|
|
||||||
@@ -453,10 +449,7 @@ void SwitcherData::Start()
|
|||||||
th = new SwitcherThread();
|
th = new SwitcherThread();
|
||||||
th->start((QThread::Priority)threadPriority);
|
th->start((QThread::Priority)threadPriority);
|
||||||
|
|
||||||
// Will be overwritten quickly but might be useful
|
RunStartSteps();
|
||||||
writeToStatusFile("Advanced Scene Switcher running");
|
|
||||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
|
|
||||||
nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSystemTrayNotifications) {
|
if (showSystemTrayNotifications) {
|
||||||
@@ -483,11 +476,7 @@ void SwitcherData::Stop()
|
|||||||
th->wait();
|
th->wait();
|
||||||
delete th;
|
delete th;
|
||||||
th = nullptr;
|
th = nullptr;
|
||||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
RunStopSteps();
|
||||||
if (!obsIsShuttingDown) {
|
|
||||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSystemTrayNotifications) {
|
if (showSystemTrayNotifications) {
|
||||||
|
|||||||
@@ -82,19 +82,21 @@ public:
|
|||||||
void RemoveMacro(std::shared_ptr<Macro> &);
|
void RemoveMacro(std::shared_ptr<Macro> &);
|
||||||
void RemoveSelectedMacros();
|
void RemoveSelectedMacros();
|
||||||
void RenameMacro(std::shared_ptr<Macro> &, const QString &name);
|
void RenameMacro(std::shared_ptr<Macro> &, const QString &name);
|
||||||
std::shared_ptr<Macro> GetSelectedMacro();
|
std::shared_ptr<Macro> GetSelectedMacro() const;
|
||||||
std::vector<std::shared_ptr<Macro>> GetSelectedMacros();
|
std::vector<std::shared_ptr<Macro>> GetSelectedMacros() const;
|
||||||
void SetEditMacro(Macro &m);
|
void SetEditMacro(Macro &m);
|
||||||
void SetMacroEditAreaDisabled(bool);
|
void SetMacroEditAreaDisabled(bool) const;
|
||||||
void HighlightAction(int idx, QColor color = QColor(Qt::green));
|
void HighlightAction(int idx, QColor color = QColor(Qt::green)) const;
|
||||||
void HighlightElseAction(int idx, QColor color = QColor(Qt::green));
|
void HighlightElseAction(int idx,
|
||||||
void HighlightCondition(int idx, QColor color = QColor(Qt::green));
|
QColor color = QColor(Qt::green)) const;
|
||||||
|
void HighlightCondition(int idx,
|
||||||
|
QColor color = QColor(Qt::green)) const;
|
||||||
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
|
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
|
||||||
void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0);
|
void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0);
|
||||||
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
|
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
|
||||||
void SetActionData(Macro &m);
|
void SetActionData(Macro &m) const;
|
||||||
void SetElseActionData(Macro &m);
|
void SetElseActionData(Macro &m) const;
|
||||||
void SetConditionData(Macro &m);
|
void SetConditionData(Macro &m) const;
|
||||||
void SwapActions(Macro *m, int pos1, int pos2);
|
void SwapActions(Macro *m, int pos1, int pos2);
|
||||||
void SwapConditions(Macro *m, int pos1, int pos2);
|
void SwapConditions(Macro *m, int pos1, int pos2);
|
||||||
void HighlightMacroSettingsButton(bool enable = true);
|
void HighlightMacroSettingsButton(bool enable = true);
|
||||||
@@ -102,11 +104,11 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void on_macroAdd_clicked();
|
void on_macroAdd_clicked();
|
||||||
void on_macroRemove_clicked();
|
void on_macroRemove_clicked();
|
||||||
void on_macroUp_clicked();
|
void on_macroUp_clicked() const;
|
||||||
void on_macroDown_clicked();
|
void on_macroDown_clicked() const;
|
||||||
void on_macroName_editingFinished();
|
void on_macroName_editingFinished();
|
||||||
void on_runMacroInParallel_stateChanged(int value);
|
void on_runMacroInParallel_stateChanged(int value) const;
|
||||||
void on_runMacroOnChange_stateChanged(int value);
|
void on_runMacroOnChange_stateChanged(int value) const;
|
||||||
void on_conditionAdd_clicked();
|
void on_conditionAdd_clicked();
|
||||||
void on_conditionRemove_clicked();
|
void on_conditionRemove_clicked();
|
||||||
void on_conditionTop_clicked();
|
void on_conditionTop_clicked();
|
||||||
@@ -119,14 +121,14 @@ public slots:
|
|||||||
void on_actionUp_clicked();
|
void on_actionUp_clicked();
|
||||||
void on_actionDown_clicked();
|
void on_actionDown_clicked();
|
||||||
void on_actionBottom_clicked();
|
void on_actionBottom_clicked();
|
||||||
void on_toggleElseActions_clicked();
|
void on_toggleElseActions_clicked() const;
|
||||||
void on_elseActionAdd_clicked();
|
void on_elseActionAdd_clicked();
|
||||||
void on_elseActionRemove_clicked();
|
void on_elseActionRemove_clicked();
|
||||||
void on_elseActionTop_clicked();
|
void on_elseActionTop_clicked();
|
||||||
void on_elseActionUp_clicked();
|
void on_elseActionUp_clicked();
|
||||||
void on_elseActionDown_clicked();
|
void on_elseActionDown_clicked();
|
||||||
void on_elseActionBottom_clicked();
|
void on_elseActionBottom_clicked();
|
||||||
void MacroSelectionAboutToChange();
|
void MacroSelectionAboutToChange() const;
|
||||||
void MacroSelectionChanged();
|
void MacroSelectionChanged();
|
||||||
void UpMacroSegementHotkey();
|
void UpMacroSegementHotkey();
|
||||||
void DownMacroSegementHotkey();
|
void DownMacroSegementHotkey();
|
||||||
@@ -137,22 +139,22 @@ public slots:
|
|||||||
void ShowMacroConditionsContextMenu(const QPoint &);
|
void ShowMacroConditionsContextMenu(const QPoint &);
|
||||||
void CopyMacro();
|
void CopyMacro();
|
||||||
void RenameSelectedMacro();
|
void RenameSelectedMacro();
|
||||||
void ExportMacros();
|
void ExportMacros() const;
|
||||||
void ImportMacros();
|
void ImportMacros();
|
||||||
void ExpandAllActions();
|
void ExpandAllActions() const;
|
||||||
void ExpandAllElseActions();
|
void ExpandAllElseActions() const;
|
||||||
void ExpandAllConditions();
|
void ExpandAllConditions() const;
|
||||||
void CollapseAllActions();
|
void CollapseAllActions() const;
|
||||||
void CollapseAllElseActions();
|
void CollapseAllElseActions() const;
|
||||||
void CollapseAllConditions();
|
void CollapseAllConditions() const;
|
||||||
void MinimizeActions();
|
void MinimizeActions() const;
|
||||||
void MaximizeActions();
|
void MaximizeActions() const;
|
||||||
void MinimizeElseActions();
|
void MinimizeElseActions() const;
|
||||||
void MaximizeElseActions();
|
void MaximizeElseActions() const;
|
||||||
void MinimizeConditions();
|
void MinimizeConditions() const;
|
||||||
void MaximizeConditions();
|
void MaximizeConditions() const;
|
||||||
void SetElseActionsStateToHidden();
|
void SetElseActionsStateToHidden() const;
|
||||||
void SetElseActionsStateToVisible();
|
void SetElseActionsStateToVisible() const;
|
||||||
void MacroActionSelectionChanged(int idx);
|
void MacroActionSelectionChanged(int idx);
|
||||||
void MacroActionReorder(int to, int target);
|
void MacroActionReorder(int to, int target);
|
||||||
void AddMacroAction(Macro *macro, int idx, const std::string &id,
|
void AddMacroAction(Macro *macro, int idx, const std::string &id,
|
||||||
@@ -178,8 +180,8 @@ public slots:
|
|||||||
void RemoveMacroCondition(int idx);
|
void RemoveMacroCondition(int idx);
|
||||||
void MoveMacroConditionUp(int idx);
|
void MoveMacroConditionUp(int idx);
|
||||||
void MoveMacroConditionDown(int idx);
|
void MoveMacroConditionDown(int idx);
|
||||||
void HighlightControls();
|
void HighlightControls() const;
|
||||||
void HighlightOnChange();
|
void HighlightOnChange() const;
|
||||||
void on_macroSettings_clicked();
|
void on_macroSettings_clicked();
|
||||||
void CopyMacroSegment();
|
void CopyMacroSegment();
|
||||||
void PasteMacroSegment();
|
void PasteMacroSegment();
|
||||||
|
|||||||
@@ -113,12 +113,12 @@ void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
|||||||
static_cast<SwitcherData::StartupBehavior>(index);
|
static_cast<SwitcherData::StartupBehavior>(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_logLevel_currentIndexChanged(int value)
|
void AdvSceneSwitcher::on_logLevel_currentIndexChanged(int idx)
|
||||||
{
|
{
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switcher->logLevel = static_cast<SwitcherData::LogLevel>(value);
|
SetLogLevel(static_cast<LogLevel>(ui->logLevel->itemData(idx).toInt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
|
void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
|
||||||
@@ -383,6 +383,7 @@ void AdvSceneSwitcher::CheckFirstTimeSetup()
|
|||||||
switcher->firstBoot = false;
|
switcher->firstBoot = false;
|
||||||
DisplayMessage(
|
DisplayMessage(
|
||||||
obs_module_text("AdvSceneSwitcher.firstBootMessage"));
|
obs_module_text("AdvSceneSwitcher.firstBootMessage"));
|
||||||
|
switcher->Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,8 +530,8 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
|
|||||||
obs_data_set_int(obj, "autoStartEvent",
|
obs_data_set_int(obj, "autoStartEvent",
|
||||||
static_cast<int>(autoStartEvent));
|
static_cast<int>(autoStartEvent));
|
||||||
|
|
||||||
obs_data_set_int(obj, "logLevel", static_cast<int>(logLevel));
|
SaveLogLevel(obj);
|
||||||
obs_data_set_int(obj, "logLevelVersion", 1);
|
|
||||||
obs_data_set_bool(obj, "showSystemTrayNotifications",
|
obs_data_set_bool(obj, "showSystemTrayNotifications",
|
||||||
showSystemTrayNotifications);
|
showSystemTrayNotifications);
|
||||||
obs_data_set_bool(obj, "disableHints", disableHints);
|
obs_data_set_bool(obj, "disableHints", disableHints);
|
||||||
@@ -586,25 +587,7 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
|
|||||||
autoStartEvent =
|
autoStartEvent =
|
||||||
static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent"));
|
static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent"));
|
||||||
|
|
||||||
logLevel = static_cast<LogLevel>(obs_data_get_int(obj, "logLevel"));
|
LoadLogLevel(obj);
|
||||||
if (obs_data_get_int(obj, "logLevelVersion") < 1) {
|
|
||||||
enum OldLogLevel { DEFAULT, LOG_ACTION, VERBOSE };
|
|
||||||
OldLogLevel oldLogLevel = static_cast<OldLogLevel>(
|
|
||||||
obs_data_get_int(obj, "logLevel"));
|
|
||||||
switch (oldLogLevel) {
|
|
||||||
case DEFAULT:
|
|
||||||
logLevel = LogLevel::DEFAULT;
|
|
||||||
break;
|
|
||||||
case LOG_ACTION:
|
|
||||||
logLevel = LogLevel::LOG_ACTION;
|
|
||||||
break;
|
|
||||||
case VERBOSE:
|
|
||||||
logLevel = LogLevel::VERBOSE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showSystemTrayNotifications =
|
showSystemTrayNotifications =
|
||||||
obs_data_get_bool(obj, "showSystemTrayNotifications");
|
obs_data_get_bool(obj, "showSystemTrayNotifications");
|
||||||
@@ -894,7 +877,9 @@ void AdvSceneSwitcher::SetupGeneralTab()
|
|||||||
SIGNAL(DurationChanged(const Duration &)), this,
|
SIGNAL(DurationChanged(const Duration &)), this,
|
||||||
SLOT(CooldownDurationChanged(const Duration &)));
|
SLOT(CooldownDurationChanged(const Duration &)));
|
||||||
|
|
||||||
ui->logLevel->setCurrentIndex(static_cast<int>(switcher->logLevel));
|
PopulateLogLevelSelection(ui->logLevel);
|
||||||
|
ui->logLevel->setCurrentIndex(
|
||||||
|
ui->logLevel->findData(static_cast<int>(GetLogLevel())));
|
||||||
|
|
||||||
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
||||||
ui->showTrayNotifications->setChecked(
|
ui->showTrayNotifications->setChecked(
|
||||||
|
|||||||
@@ -21,6 +21,29 @@ bool FileSwitch::pause = false;
|
|||||||
static QObject *addPulse = nullptr;
|
static QObject *addPulse = nullptr;
|
||||||
static std::hash<std::string> strHash;
|
static std::hash<std::string> strHash;
|
||||||
|
|
||||||
|
static void writeToStatusFile(const QString &msg)
|
||||||
|
{
|
||||||
|
if (!GetSwitcher() || !GetSwitcher()->fileIO.writeEnabled ||
|
||||||
|
GetSwitcher()->fileIO.writePath.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(QString::fromStdString(GetSwitcher()->fileIO.writePath));
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << msg << Qt::endl;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _ = []() {
|
||||||
|
AddStartStep(
|
||||||
|
[]() { writeToStatusFile("Advanced Scene Switcher running"); });
|
||||||
|
AddStopStep(
|
||||||
|
[]() { writeToStatusFile("Advanced Scene Switcher stopped"); });
|
||||||
|
return true;
|
||||||
|
}();
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_browseButton_clicked()
|
void AdvSceneSwitcher::on_browseButton_clicked()
|
||||||
{
|
{
|
||||||
QString path = QFileDialog::getOpenFileName(
|
QString path = QFileDialog::getOpenFileName(
|
||||||
@@ -113,20 +136,6 @@ void SwitcherData::writeSceneInfoToFile()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::writeToStatusFile(const QString &msg)
|
|
||||||
{
|
|
||||||
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile file(QString::fromStdString(fileIO.writePath));
|
|
||||||
if (file.open(QIODevice::ReadWrite)) {
|
|
||||||
QTextStream stream(&file);
|
|
||||||
stream << msg << Qt::endl;
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
|
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -550,6 +550,11 @@ static void initProc2()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ignoreXerror(Display *d, XErrorEvent *e)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void PlatformInit()
|
void PlatformInit()
|
||||||
{
|
{
|
||||||
auto display = disp();
|
auto display = disp();
|
||||||
@@ -560,6 +565,7 @@ void PlatformInit()
|
|||||||
initXss();
|
initXss();
|
||||||
initProcps();
|
initProcps();
|
||||||
initProc2();
|
initProc2();
|
||||||
|
XSetErrorHandler(ignoreXerror);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanupHelper(QLibrary *lib)
|
static void cleanupHelper(QLibrary *lib)
|
||||||
@@ -576,6 +582,7 @@ void PlatformCleanup()
|
|||||||
cleanupHelper(libprocps);
|
cleanupHelper(libprocps);
|
||||||
cleanupHelper(libproc2);
|
cleanupHelper(libproc2);
|
||||||
cleanupDisplay();
|
cleanupDisplay();
|
||||||
|
XSetErrorHandler(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#include "section.hpp"
|
#include "section.hpp"
|
||||||
#include "switch-button.hpp"
|
#include "switch-button.hpp"
|
||||||
|
|
||||||
#include <QGraphicsOpacityEffect>
|
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
static inline void populateActionSelection(QComboBox *list)
|
static inline void populateActionSelection(QComboBox *list)
|
||||||
@@ -17,6 +15,9 @@ static inline void populateActionSelection(QComboBox *list)
|
|||||||
QString entry(obs_module_text(action._name.c_str()));
|
QString entry(obs_module_text(action._name.c_str()));
|
||||||
if (list->findText(entry) == -1) {
|
if (list->findText(entry) == -1) {
|
||||||
list->addItem(entry);
|
list->addItem(entry);
|
||||||
|
qobject_cast<QListView *>(list->view())
|
||||||
|
->setRowHidden(list->count() - 1,
|
||||||
|
action._hidden);
|
||||||
} else {
|
} else {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING,
|
||||||
"did not insert duplicate action entry with name \"%s\"",
|
"did not insert duplicate action entry with name \"%s\"",
|
||||||
@@ -60,12 +61,40 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
|||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
UpdateEntryData(id);
|
SetupWidgets(true);
|
||||||
|
|
||||||
_actionStateTimer.start(300);
|
_actionStateTimer.start(300);
|
||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionEdit::SetupWidgets(bool basicSetup)
|
||||||
|
{
|
||||||
|
if (_allWidgetsAreSetup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto id = (*_entryData)->GetId();
|
||||||
|
_actionSelection->setCurrentText(
|
||||||
|
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
|
||||||
|
const bool enabled = (*_entryData)->Enabled();
|
||||||
|
_enable->setChecked(enabled);
|
||||||
|
SetDisableEffect(!enabled);
|
||||||
|
HeaderInfoChanged(
|
||||||
|
QString::fromStdString((*_entryData)->GetShortDesc()));
|
||||||
|
|
||||||
|
if (basicSetup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
|
||||||
|
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||||
|
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||||
|
_section->SetContent(widget, (*_entryData)->GetCollapsed());
|
||||||
|
SetFocusPolicyOfWidgets();
|
||||||
|
|
||||||
|
_allWidgetsAreSetup = true;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -95,38 +124,11 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
|||||||
SetFocusPolicyOfWidgets();
|
SetFocusPolicyOfWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionEdit::UpdateEntryData(const std::string &id)
|
|
||||||
{
|
|
||||||
_actionSelection->setCurrentText(
|
|
||||||
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
|
|
||||||
const bool enabled = (*_entryData)->Enabled();
|
|
||||||
_enable->setChecked(enabled);
|
|
||||||
SetDisableEffect(!enabled);
|
|
||||||
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
|
|
||||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
|
||||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
|
||||||
HeaderInfoChanged(
|
|
||||||
QString::fromStdString((*_entryData)->GetShortDesc()));
|
|
||||||
_section->SetContent(widget, (*_entryData)->GetCollapsed());
|
|
||||||
SetFocusPolicyOfWidgets();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
|
void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
|
||||||
{
|
{
|
||||||
_entryData = data;
|
_entryData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionEdit::SetDisableEffect(bool value)
|
|
||||||
{
|
|
||||||
if (value) {
|
|
||||||
auto effect = new QGraphicsOpacityEffect(this);
|
|
||||||
effect->setOpacity(0.5);
|
|
||||||
_section->setGraphicsEffect(effect);
|
|
||||||
} else {
|
|
||||||
_section->setGraphicsEffect(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroActionEdit::ActionEnableChanged(bool value)
|
void MacroActionEdit::ActionEnableChanged(bool value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -144,13 +146,9 @@ void MacroActionEdit::UpdateActionState()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetEnableAppearance((*_entryData)->Enabled());
|
const bool enabled = (*_entryData)->Enabled();
|
||||||
}
|
SetEnableAppearance(enabled);
|
||||||
|
_enable->setChecked(enabled);
|
||||||
void MacroActionEdit::SetEnableAppearance(bool value)
|
|
||||||
{
|
|
||||||
_enable->setChecked(value);
|
|
||||||
SetDisableEffect(!value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
|
std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public:
|
|||||||
QWidget *parent = nullptr,
|
QWidget *parent = nullptr,
|
||||||
std::shared_ptr<MacroAction> * = nullptr,
|
std::shared_ptr<MacroAction> * = nullptr,
|
||||||
const std::string &id = MacroAction::GetDefaultID().data());
|
const std::string &id = MacroAction::GetDefaultID().data());
|
||||||
void UpdateEntryData(const std::string &id);
|
void SetupWidgets(bool basicSetup = false);
|
||||||
void SetEntryData(std::shared_ptr<MacroAction> *);
|
void SetEntryData(std::shared_ptr<MacroAction> *);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@@ -27,8 +27,6 @@ private slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<MacroSegment> Data() const;
|
std::shared_ptr<MacroSegment> Data() const;
|
||||||
void SetDisableEffect(bool);
|
|
||||||
void SetEnableAppearance(bool);
|
|
||||||
|
|
||||||
FilterComboBox *_actionSelection;
|
FilterComboBox *_actionSelection;
|
||||||
SwitchButton *_enable;
|
SwitchButton *_enable;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ struct MacroActionInfo {
|
|||||||
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
|
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
|
||||||
CreateActionWidget _createWidget = nullptr;
|
CreateActionWidget _createWidget = nullptr;
|
||||||
std::string _name;
|
std::string _name;
|
||||||
|
bool _hidden = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MacroActionFactory {
|
class MacroActionFactory {
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#include "macro-action-variable.hpp"
|
#include "macro-action-variable.hpp"
|
||||||
#include "advanced-scene-switcher.hpp"
|
#include "advanced-scene-switcher.hpp"
|
||||||
|
#include "json-helpers.hpp"
|
||||||
#include "layout-helpers.hpp"
|
#include "layout-helpers.hpp"
|
||||||
#include "math-helpers.hpp"
|
#include "math-helpers.hpp"
|
||||||
#include "macro-condition-edit.hpp"
|
#include "macro-condition-edit.hpp"
|
||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
#include "non-modal-dialog.hpp"
|
#include "non-modal-dialog.hpp"
|
||||||
#include "source-helpers.hpp"
|
#include "source-helpers.hpp"
|
||||||
|
#include "ui-helpers.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
@@ -385,6 +387,22 @@ bool MacroActionVariable::PerformAction()
|
|||||||
var->SetValue(*value);
|
var->SetValue(*value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case Action::QUERY_JSON: {
|
||||||
|
auto value = QueryJson(var->Value(), _jsonQuery);
|
||||||
|
if (!value.has_value()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var->SetValue(*value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Action::ARRAY_JSON: {
|
||||||
|
auto value = AccessJsonArrayIndex(var->Value(), _jsonIndex);
|
||||||
|
if (!value.has_value()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var->SetValue(*value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case Action::SET_TO_TEMPVAR: {
|
case Action::SET_TO_TEMPVAR: {
|
||||||
auto tempVar = _tempVar.GetTempVariable(GetMacro());
|
auto tempVar = _tempVar.GetTempVariable(GetMacro());
|
||||||
if (!tempVar) {
|
if (!tempVar) {
|
||||||
@@ -471,6 +489,8 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
|
|||||||
_randomNumberStart.Save(obj, "randomNumberStart");
|
_randomNumberStart.Save(obj, "randomNumberStart");
|
||||||
_randomNumberEnd.Save(obj, "randomNumberEnd");
|
_randomNumberEnd.Save(obj, "randomNumberEnd");
|
||||||
obs_data_set_bool(obj, "generateInteger", _generateInteger);
|
obs_data_set_bool(obj, "generateInteger", _generateInteger);
|
||||||
|
_jsonQuery.Save(obj, "jsonQuery");
|
||||||
|
_jsonIndex.Save(obj, "jsonIndex");
|
||||||
|
|
||||||
obs_data_set_int(obj, "version", 1);
|
obs_data_set_int(obj, "version", 1);
|
||||||
|
|
||||||
@@ -527,6 +547,8 @@ bool MacroActionVariable::Load(obs_data_t *obj)
|
|||||||
_randomNumberStart.Load(obj, "randomNumberStart");
|
_randomNumberStart.Load(obj, "randomNumberStart");
|
||||||
_randomNumberEnd.Load(obj, "randomNumberEnd");
|
_randomNumberEnd.Load(obj, "randomNumberEnd");
|
||||||
_generateInteger = obs_data_get_bool(obj, "generateInteger");
|
_generateInteger = obs_data_get_bool(obj, "generateInteger");
|
||||||
|
_jsonQuery.Load(obj, "jsonQuery");
|
||||||
|
_jsonIndex.Load(obj, "jsonIndex");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -626,6 +648,8 @@ void MacroActionVariable::ResolveVariablesToFixedValues()
|
|||||||
_envVariableName.ResolveVariables();
|
_envVariableName.ResolveVariables();
|
||||||
_scene.ResolveVariables();
|
_scene.ResolveVariables();
|
||||||
_sceneItemIndex.ResolveVariables();
|
_sceneItemIndex.ResolveVariables();
|
||||||
|
_jsonQuery.ResolveVariables();
|
||||||
|
_jsonIndex.ResolveVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariable::DecrementCurrentSegmentVariableRef()
|
void MacroActionVariable::DecrementCurrentSegmentVariableRef()
|
||||||
@@ -655,8 +679,6 @@ static inline void populateActionSelection(QComboBox *list)
|
|||||||
"AdvSceneSwitcher.action.variable.type.subString"},
|
"AdvSceneSwitcher.action.variable.type.subString"},
|
||||||
{MacroActionVariable::Action::FIND_AND_REPLACE,
|
{MacroActionVariable::Action::FIND_AND_REPLACE,
|
||||||
"AdvSceneSwitcher.action.variable.type.findAndReplace"},
|
"AdvSceneSwitcher.action.variable.type.findAndReplace"},
|
||||||
{MacroActionVariable::Action::EXTRACT_JSON,
|
|
||||||
"AdvSceneSwitcher.action.variable.type.extractJson"},
|
|
||||||
{MacroActionVariable::Action::STRING_LENGTH,
|
{MacroActionVariable::Action::STRING_LENGTH,
|
||||||
"AdvSceneSwitcher.action.variable.type.stringLength"},
|
"AdvSceneSwitcher.action.variable.type.stringLength"},
|
||||||
{MacroActionVariable::Action::TRIM,
|
{MacroActionVariable::Action::TRIM,
|
||||||
@@ -665,6 +687,14 @@ static inline void populateActionSelection(QComboBox *list)
|
|||||||
"AdvSceneSwitcher.action.variable.type.changeCase"},
|
"AdvSceneSwitcher.action.variable.type.changeCase"},
|
||||||
{true, ""}, // Separator
|
{true, ""}, // Separator
|
||||||
|
|
||||||
|
{MacroActionVariable::Action::EXTRACT_JSON,
|
||||||
|
"AdvSceneSwitcher.action.variable.type.extractJsonField"},
|
||||||
|
{MacroActionVariable::Action::QUERY_JSON,
|
||||||
|
"AdvSceneSwitcher.action.variable.type.queryJson"},
|
||||||
|
{MacroActionVariable::Action::ARRAY_JSON,
|
||||||
|
"AdvSceneSwitcher.action.variable.type.accessJsonArray"},
|
||||||
|
{true, ""}, // Separator
|
||||||
|
|
||||||
{MacroActionVariable::Action::SET_TO_TEMPVAR,
|
{MacroActionVariable::Action::SET_TO_TEMPVAR,
|
||||||
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
|
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
|
||||||
{MacroActionVariable::Action::SET_CONDITION_VALUE,
|
{MacroActionVariable::Action::SET_CONDITION_VALUE,
|
||||||
@@ -797,6 +827,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
"AdvSceneSwitcher.action.variable.generateInteger"),
|
"AdvSceneSwitcher.action.variable.generateInteger"),
|
||||||
this)),
|
this)),
|
||||||
_randomLayout(new QVBoxLayout()),
|
_randomLayout(new QVBoxLayout()),
|
||||||
|
_jsonQuery(new VariableLineEdit(this)),
|
||||||
|
_jsonQueryHelp(new QLabel(this)),
|
||||||
|
_jsonIndex(new VariableSpinBox(this)),
|
||||||
_entryLayout(new QHBoxLayout())
|
_entryLayout(new QHBoxLayout())
|
||||||
{
|
{
|
||||||
_numValue->setMinimum(-9999999999);
|
_numValue->setMinimum(-9999999999);
|
||||||
@@ -826,6 +859,15 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
_randomNumberStart->setMaximum(9999999999);
|
_randomNumberStart->setMaximum(9999999999);
|
||||||
_randomNumberEnd->setMinimum(-9999999999);
|
_randomNumberEnd->setMinimum(-9999999999);
|
||||||
_randomNumberEnd->setMaximum(9999999999);
|
_randomNumberEnd->setMaximum(9999999999);
|
||||||
|
const QString path = GetThemeTypeName() == "Light"
|
||||||
|
? ":/res/images/help.svg"
|
||||||
|
: ":/res/images/help_light.svg";
|
||||||
|
const QIcon icon(path);
|
||||||
|
const QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||||
|
_jsonQueryHelp->setPixmap(pixmap);
|
||||||
|
_jsonQueryHelp->setToolTip(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.variable.type.queryJson.info"));
|
||||||
|
_jsonIndex->setMaximum(999);
|
||||||
|
|
||||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||||
this, SLOT(VariableChanged(const QString &)));
|
this, SLOT(VariableChanged(const QString &)));
|
||||||
@@ -912,6 +954,12 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
SLOT(RandomNumberEndChanged(const NumberVariable<double> &)));
|
SLOT(RandomNumberEndChanged(const NumberVariable<double> &)));
|
||||||
QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this,
|
||||||
SLOT(GenerateIntegerChanged(int)));
|
SLOT(GenerateIntegerChanged(int)));
|
||||||
|
QWidget::connect(_jsonQuery, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(JsonQueryChanged()));
|
||||||
|
QWidget::connect(
|
||||||
|
_jsonIndex,
|
||||||
|
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||||
|
this, SLOT(JsonIndexChanged(const NumberVariable<int> &)));
|
||||||
|
|
||||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{variables}}", _variables},
|
{"{{variables}}", _variables},
|
||||||
@@ -943,39 +991,42 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
{"{{randomNumberStart}}", _randomNumberStart},
|
{"{{randomNumberStart}}", _randomNumberStart},
|
||||||
{"{{randomNumberEnd}}", _randomNumberEnd},
|
{"{{randomNumberEnd}}", _randomNumberEnd},
|
||||||
{"{{generateInteger}}", _generateInteger},
|
{"{{generateInteger}}", _generateInteger},
|
||||||
|
{"{{jsonQuery}}", _jsonQuery},
|
||||||
|
{"{{jsonQueryHelp}}", _jsonQueryHelp},
|
||||||
|
{"{{jsonIndex}}", _jsonIndex},
|
||||||
};
|
};
|
||||||
PlaceWidgets(
|
PlaceWidgets(obs_module_text(
|
||||||
obs_module_text("AdvSceneSwitcher.action.variable.entry.other"),
|
"AdvSceneSwitcher.action.variable.layout.other"),
|
||||||
_entryLayout, widgetPlaceholders);
|
_entryLayout, widgetPlaceholders);
|
||||||
|
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.substringIndex"),
|
"AdvSceneSwitcher.action.variable.layout.substringIndex"),
|
||||||
_subStringIndexEntryLayout, widgetPlaceholders);
|
_subStringIndexEntryLayout, widgetPlaceholders);
|
||||||
|
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.substringRegex"),
|
"AdvSceneSwitcher.action.variable.layout.substringRegex"),
|
||||||
_subStringRegexEntryLayout, widgetPlaceholders);
|
_subStringRegexEntryLayout, widgetPlaceholders);
|
||||||
|
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.findAndReplace"),
|
"AdvSceneSwitcher.action.variable.layout.findAndReplace"),
|
||||||
_findReplaceLayout, widgetPlaceholders, false);
|
_findReplaceLayout, widgetPlaceholders, false);
|
||||||
|
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.userInput.customPrompt"),
|
"AdvSceneSwitcher.action.variable.layout.userInput.customPrompt"),
|
||||||
_promptLayout, widgetPlaceholders);
|
_promptLayout, widgetPlaceholders);
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.userInput.placeholder"),
|
"AdvSceneSwitcher.action.variable.layout.userInput.placeholder"),
|
||||||
_placeholderLayout, widgetPlaceholders);
|
_placeholderLayout, widgetPlaceholders);
|
||||||
|
|
||||||
auto randomLayout = new QHBoxLayout();
|
auto randomLayout = new QHBoxLayout();
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.randomNumber"),
|
"AdvSceneSwitcher.action.variable.layout.randomNumber"),
|
||||||
randomLayout, widgetPlaceholders);
|
randomLayout, widgetPlaceholders);
|
||||||
_randomLayout->addLayout(randomLayout);
|
_randomLayout->addLayout(randomLayout);
|
||||||
_randomLayout->addWidget(_generateInteger);
|
_randomLayout->addWidget(_generateInteger);
|
||||||
@@ -1057,17 +1108,15 @@ void MacroActionVariableEdit::UpdateEntryData()
|
|||||||
_randomNumberStart->SetValue(_entryData->_randomNumberStart);
|
_randomNumberStart->SetValue(_entryData->_randomNumberStart);
|
||||||
_randomNumberEnd->SetValue(_entryData->_randomNumberEnd);
|
_randomNumberEnd->SetValue(_entryData->_randomNumberEnd);
|
||||||
_generateInteger->setChecked(_entryData->_generateInteger);
|
_generateInteger->setChecked(_entryData->_generateInteger);
|
||||||
|
_jsonQuery->setText(_entryData->_jsonQuery);
|
||||||
|
_jsonIndex->SetValue(_entryData->_jsonIndex);
|
||||||
|
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::VariableChanged(const QString &text)
|
void MacroActionVariableEdit::VariableChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_variable = GetWeakVariableByQString(text);
|
_entryData->_variable = GetWeakVariableByQString(text);
|
||||||
emit HeaderInfoChanged(
|
emit HeaderInfoChanged(
|
||||||
QString::fromStdString(_entryData->GetShortDesc()));
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
@@ -1075,11 +1124,7 @@ void MacroActionVariableEdit::VariableChanged(const QString &text)
|
|||||||
|
|
||||||
void MacroActionVariableEdit::Variable2Changed(const QString &text)
|
void MacroActionVariableEdit::Variable2Changed(const QString &text)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_variable2 = GetWeakVariableByQString(text);
|
_entryData->_variable2 = GetWeakVariableByQString(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1105,11 +1150,7 @@ void MacroActionVariableEdit::ActionChanged(int idx)
|
|||||||
|
|
||||||
void MacroActionVariableEdit::StrValueChanged()
|
void MacroActionVariableEdit::StrValueChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_strValue = _strValue->toPlainText().toStdString();
|
_entryData->_strValue = _strValue->toPlainText().toStdString();
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
@@ -1118,21 +1159,13 @@ void MacroActionVariableEdit::StrValueChanged()
|
|||||||
void MacroActionVariableEdit::NumValueChanged(
|
void MacroActionVariableEdit::NumValueChanged(
|
||||||
const NumberVariable<double> &value)
|
const NumberVariable<double> &value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_numValue = value;
|
_entryData->_numValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::SegmentIndexChanged(const IntVariable &val)
|
void MacroActionVariableEdit::SegmentIndexChanged(const IntVariable &val)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->SetSegmentIndexValue(val - 1);
|
_entryData->SetSegmentIndexValue(val - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1238,32 +1271,20 @@ void MacroActionVariableEdit::MacroSegmentOrderChanged()
|
|||||||
void MacroActionVariableEdit::SubStringStartChanged(
|
void MacroActionVariableEdit::SubStringStartChanged(
|
||||||
const NumberVariable<int> &start)
|
const NumberVariable<int> &start)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_subStringStart = start;
|
_entryData->_subStringStart = start;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::SubStringSizeChanged(
|
void MacroActionVariableEdit::SubStringSizeChanged(
|
||||||
const NumberVariable<int> &size)
|
const NumberVariable<int> &size)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_subStringSize = size;
|
_entryData->_subStringSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::SubStringRegexChanged(const RegexConfig &conf)
|
void MacroActionVariableEdit::SubStringRegexChanged(const RegexConfig &conf)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_subStringRegex = conf;
|
_entryData->_subStringRegex = conf;
|
||||||
|
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
@@ -1271,11 +1292,7 @@ void MacroActionVariableEdit::SubStringRegexChanged(const RegexConfig &conf)
|
|||||||
|
|
||||||
void MacroActionVariableEdit::RegexPatternChanged()
|
void MacroActionVariableEdit::RegexPatternChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_regexPattern = _regexPattern->toPlainText().toStdString();
|
_entryData->_regexPattern = _regexPattern->toPlainText().toStdString();
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
@@ -1284,21 +1301,13 @@ void MacroActionVariableEdit::RegexPatternChanged()
|
|||||||
void MacroActionVariableEdit::RegexMatchIdxChanged(
|
void MacroActionVariableEdit::RegexMatchIdxChanged(
|
||||||
const NumberVariable<int> &index)
|
const NumberVariable<int> &index)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_regexMatchIdx = index;
|
_entryData->_regexMatchIdx = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::FindStrValueChanged()
|
void MacroActionVariableEdit::FindStrValueChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_findStr = _findStr->toPlainText().toStdString();
|
_entryData->_findStr = _findStr->toPlainText().toStdString();
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
@@ -1306,11 +1315,7 @@ void MacroActionVariableEdit::FindStrValueChanged()
|
|||||||
|
|
||||||
void MacroActionVariableEdit::FindRegexChanged(const RegexConfig &conf)
|
void MacroActionVariableEdit::FindRegexChanged(const RegexConfig &conf)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_findRegex = conf;
|
_entryData->_findRegex = conf;
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
@@ -1318,11 +1323,7 @@ void MacroActionVariableEdit::FindRegexChanged(const RegexConfig &conf)
|
|||||||
|
|
||||||
void MacroActionVariableEdit::ReplaceStrValueChanged()
|
void MacroActionVariableEdit::ReplaceStrValueChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_replaceStr = _replaceStr->toPlainText().toStdString();
|
_entryData->_replaceStr = _replaceStr->toPlainText().toStdString();
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
@@ -1330,11 +1331,7 @@ void MacroActionVariableEdit::ReplaceStrValueChanged()
|
|||||||
|
|
||||||
void MacroActionVariableEdit::MathExpressionChanged()
|
void MacroActionVariableEdit::MathExpressionChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_mathExpression = _mathExpression->text().toStdString();
|
_entryData->_mathExpression = _mathExpression->text().toStdString();
|
||||||
|
|
||||||
// In case of invalid expression display an error
|
// In case of invalid expression display an error
|
||||||
@@ -1352,73 +1349,45 @@ void MacroActionVariableEdit::MathExpressionChanged()
|
|||||||
|
|
||||||
void MacroActionVariableEdit::UseCustomPromptChanged(int value)
|
void MacroActionVariableEdit::UseCustomPromptChanged(int value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_useCustomPrompt = value;
|
_entryData->_useCustomPrompt = value;
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::InputPromptChanged()
|
void MacroActionVariableEdit::InputPromptChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_inputPrompt = _inputPrompt->text().toStdString();
|
_entryData->_inputPrompt = _inputPrompt->text().toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::UseInputPlaceholderChanged(int value)
|
void MacroActionVariableEdit::UseInputPlaceholderChanged(int value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_useInputPlaceholder = value;
|
_entryData->_useInputPlaceholder = value;
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::InputPlaceholderChanged()
|
void MacroActionVariableEdit::InputPlaceholderChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_inputPlaceholder = _inputPlaceholder->text().toStdString();
|
_entryData->_inputPlaceholder = _inputPlaceholder->text().toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::EnvVariableChanged()
|
void MacroActionVariableEdit::EnvVariableChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_envVariableName = _envVariable->text().toStdString();
|
_entryData->_envVariableName = _envVariable->text().toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::SceneChanged(const SceneSelection &scene)
|
void MacroActionVariableEdit::SceneChanged(const SceneSelection &scene)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_scene = scene;
|
_entryData->_scene = scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
|
void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_tempVar = var;
|
_entryData->_tempVar = var;
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
@@ -1426,21 +1395,13 @@ void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
|
|||||||
void MacroActionVariableEdit::SceneItemIndexChanged(
|
void MacroActionVariableEdit::SceneItemIndexChanged(
|
||||||
const NumberVariable<int> &value)
|
const NumberVariable<int> &value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_sceneItemIndex = value;
|
_entryData->_sceneItemIndex = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::DirectionChanged(int value)
|
void MacroActionVariableEdit::DirectionChanged(int value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_direction =
|
_entryData->_direction =
|
||||||
static_cast<MacroActionVariable::Direction>(value);
|
static_cast<MacroActionVariable::Direction>(value);
|
||||||
}
|
}
|
||||||
@@ -1448,21 +1409,13 @@ void MacroActionVariableEdit::DirectionChanged(int value)
|
|||||||
void MacroActionVariableEdit::StringLengthChanged(
|
void MacroActionVariableEdit::StringLengthChanged(
|
||||||
const NumberVariable<int> &value)
|
const NumberVariable<int> &value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_stringLength = value;
|
_entryData->_stringLength = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::CharSelectionChanged(const QString &character)
|
void MacroActionVariableEdit::CharSelectionChanged(const QString &character)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
if (character.isEmpty()) {
|
if (character.isEmpty()) {
|
||||||
_entryData->_paddingChar = ' ';
|
_entryData->_paddingChar = ' ';
|
||||||
} else {
|
} else {
|
||||||
@@ -1472,11 +1425,7 @@ void MacroActionVariableEdit::CharSelectionChanged(const QString &character)
|
|||||||
|
|
||||||
void MacroActionVariableEdit::CaseTypeChanged(int index)
|
void MacroActionVariableEdit::CaseTypeChanged(int index)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
GUARD_LOADING_AND_LOCK();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = LockContext();
|
|
||||||
_entryData->_caseType = static_cast<MacroActionVariable::CaseType>(
|
_entryData->_caseType = static_cast<MacroActionVariable::CaseType>(
|
||||||
_caseType->itemData(index).toInt());
|
_caseType->itemData(index).toInt());
|
||||||
}
|
}
|
||||||
@@ -1501,6 +1450,18 @@ void MacroActionVariableEdit::GenerateIntegerChanged(int value)
|
|||||||
_entryData->_generateInteger = value;
|
_entryData->_generateInteger = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionVariableEdit::JsonQueryChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_jsonQuery = _jsonQuery->text().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionVariableEdit::JsonIndexChanged(const NumberVariable<int> &value)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_jsonIndex = value;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::SetWidgetVisibility()
|
void MacroActionVariableEdit::SetWidgetVisibility()
|
||||||
{
|
{
|
||||||
if (!_entryData) {
|
if (!_entryData) {
|
||||||
@@ -1524,19 +1485,22 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
{"{{stringLength}}", _stringLength},
|
{"{{stringLength}}", _stringLength},
|
||||||
{"{{paddingCharSelection}}", _paddingCharSelection},
|
{"{{paddingCharSelection}}", _paddingCharSelection},
|
||||||
{"{{caseType}}", _caseType},
|
{"{{caseType}}", _caseType},
|
||||||
|
{"{{jsonQuery}}", _jsonQuery},
|
||||||
|
{"{{jsonQueryHelp}}", _jsonQueryHelp},
|
||||||
|
{"{{jsonIndex}}", _jsonIndex},
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *layoutString = "";
|
const char *layoutString = "";
|
||||||
if (_entryData->_action == MacroActionVariable::Action::PAD) {
|
if (_entryData->_action == MacroActionVariable::Action::PAD) {
|
||||||
layoutString = obs_module_text(
|
layoutString = obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.pad");
|
"AdvSceneSwitcher.action.variable.layout.pad");
|
||||||
} else if (_entryData->_action ==
|
} else if (_entryData->_action ==
|
||||||
MacroActionVariable::Action::TRUNCATE) {
|
MacroActionVariable::Action::TRUNCATE) {
|
||||||
layoutString = obs_module_text(
|
layoutString = obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.truncate");
|
"AdvSceneSwitcher.action.variable.layout.truncate");
|
||||||
} else {
|
} else {
|
||||||
layoutString = obs_module_text(
|
layoutString = obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.entry.other");
|
"AdvSceneSwitcher.action.variable.layout.other");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &[_, widget] : widgetPlaceholders) {
|
for (const auto &[_, widget] : widgetPlaceholders) {
|
||||||
@@ -1552,7 +1516,8 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
MacroActionVariable::Action::MATH_EXPRESSION ||
|
MacroActionVariable::Action::MATH_EXPRESSION ||
|
||||||
_entryData->_action == MacroActionVariable::Action::ENV_VARIABLE ||
|
_entryData->_action == MacroActionVariable::Action::ENV_VARIABLE ||
|
||||||
_entryData->_action == MacroActionVariable::Action::STRING_LENGTH ||
|
_entryData->_action == MacroActionVariable::Action::STRING_LENGTH ||
|
||||||
_entryData->_action == MacroActionVariable::Action::EXTRACT_JSON) {
|
_entryData->_action == MacroActionVariable::Action::EXTRACT_JSON ||
|
||||||
|
_entryData->_action == MacroActionVariable::Action::QUERY_JSON) {
|
||||||
RemoveStretchIfPresent(_entryLayout);
|
RemoveStretchIfPresent(_entryLayout);
|
||||||
} else {
|
} else {
|
||||||
AddStretchIfNecessary(_entryLayout);
|
AddStretchIfNecessary(_entryLayout);
|
||||||
@@ -1664,6 +1629,12 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
SetLayoutVisible(_randomLayout,
|
SetLayoutVisible(_randomLayout,
|
||||||
_entryData->_action ==
|
_entryData->_action ==
|
||||||
MacroActionVariable::Action::RANDOM_NUMBER);
|
MacroActionVariable::Action::RANDOM_NUMBER);
|
||||||
|
_jsonQuery->setVisible(_entryData->_action ==
|
||||||
|
MacroActionVariable::Action::QUERY_JSON);
|
||||||
|
_jsonQueryHelp->setVisible(_entryData->_action ==
|
||||||
|
MacroActionVariable::Action::QUERY_JSON);
|
||||||
|
_jsonIndex->setVisible(_entryData->_action ==
|
||||||
|
MacroActionVariable::Action::ARRAY_JSON);
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ public:
|
|||||||
TRIM,
|
TRIM,
|
||||||
CHANGE_CASE,
|
CHANGE_CASE,
|
||||||
RANDOM_NUMBER,
|
RANDOM_NUMBER,
|
||||||
|
QUERY_JSON,
|
||||||
|
ARRAY_JSON,
|
||||||
};
|
};
|
||||||
|
|
||||||
Action _action = Action::SET_VALUE;
|
Action _action = Action::SET_VALUE;
|
||||||
@@ -104,6 +106,9 @@ public:
|
|||||||
DoubleVariable _randomNumberEnd = 100;
|
DoubleVariable _randomNumberEnd = 100;
|
||||||
bool _generateInteger = true;
|
bool _generateInteger = true;
|
||||||
|
|
||||||
|
StringVariable _jsonQuery = "$.some.nested.value";
|
||||||
|
IntVariable _jsonIndex = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DecrementCurrentSegmentVariableRef();
|
void DecrementCurrentSegmentVariableRef();
|
||||||
void HandleIndexSubString(Variable *);
|
void HandleIndexSubString(Variable *);
|
||||||
@@ -169,6 +174,8 @@ private slots:
|
|||||||
void RandomNumberStartChanged(const NumberVariable<double> &);
|
void RandomNumberStartChanged(const NumberVariable<double> &);
|
||||||
void RandomNumberEndChanged(const NumberVariable<double> &);
|
void RandomNumberEndChanged(const NumberVariable<double> &);
|
||||||
void GenerateIntegerChanged(int);
|
void GenerateIntegerChanged(int);
|
||||||
|
void JsonQueryChanged();
|
||||||
|
void JsonIndexChanged(const NumberVariable<int> &);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
@@ -218,6 +225,9 @@ private:
|
|||||||
VariableDoubleSpinBox *_randomNumberEnd;
|
VariableDoubleSpinBox *_randomNumberEnd;
|
||||||
QCheckBox *_generateInteger;
|
QCheckBox *_generateInteger;
|
||||||
QVBoxLayout *_randomLayout;
|
QVBoxLayout *_randomLayout;
|
||||||
|
VariableLineEdit *_jsonQuery;
|
||||||
|
QLabel *_jsonQueryHelp;
|
||||||
|
VariableSpinBox *_jsonIndex;
|
||||||
QHBoxLayout *_entryLayout;
|
QHBoxLayout *_entryLayout;
|
||||||
|
|
||||||
std::shared_ptr<MacroActionVariable> _entryData;
|
std::shared_ptr<MacroActionVariable> _entryData;
|
||||||
|
|||||||
@@ -11,15 +11,12 @@ bool MacroAction::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_bool(obj, "enabled", _enabled);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroAction::Load(obs_data_t *obj)
|
bool MacroAction::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroSegment::Load(obj);
|
MacroSegment::Load(obj);
|
||||||
obs_data_set_default_bool(obj, "enabled", true);
|
|
||||||
_enabled = obs_data_get_bool(obj, "enabled");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,16 +25,6 @@ void MacroAction::LogAction() const
|
|||||||
ablog(LOG_INFO, "performed action %s", GetId().c_str());
|
ablog(LOG_INFO, "performed action %s", GetId().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroAction::SetEnabled(bool value)
|
|
||||||
{
|
|
||||||
_enabled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MacroAction::Enabled() const
|
|
||||||
{
|
|
||||||
return _enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroAction::ResolveVariablesToFixedValues() {}
|
void MacroAction::ResolveVariablesToFixedValues() {}
|
||||||
|
|
||||||
std::string_view MacroAction::GetDefaultID()
|
std::string_view MacroAction::GetDefaultID()
|
||||||
|
|||||||
@@ -19,13 +19,9 @@ public:
|
|||||||
// Used to resolve variables before actions are added to action queues
|
// Used to resolve variables before actions are added to action queues
|
||||||
virtual void ResolveVariablesToFixedValues();
|
virtual void ResolveVariablesToFixedValues();
|
||||||
|
|
||||||
void SetEnabled(bool);
|
|
||||||
bool Enabled() const;
|
|
||||||
|
|
||||||
static std::string_view GetDefaultID();
|
static std::string_view GetDefaultID();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _enabled = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class EXPORT MacroRefAction : virtual public MacroAction {
|
class EXPORT MacroRefAction : virtual public MacroAction {
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ MacroConditionEdit::MacroConditionEdit(
|
|||||||
mainLayout->addWidget(_frame);
|
mainLayout->addWidget(_frame);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
UpdateEntryData(id);
|
SetupWidgets(true);
|
||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,9 +152,11 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
|
|||||||
const auto logic = static_cast<Logic::Type>(
|
const auto logic = static_cast<Logic::Type>(
|
||||||
_logicSelection->itemData(idx).toInt());
|
_logicSelection->itemData(idx).toInt());
|
||||||
(*_entryData)->SetLogicType(logic);
|
(*_entryData)->SetLogicType(logic);
|
||||||
|
|
||||||
|
SetEnableAppearance(logic != Logic::Type::NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionEdit::IsRootNode()
|
bool MacroConditionEdit::IsRootNode() const
|
||||||
{
|
{
|
||||||
return _isRoot;
|
return _isRoot;
|
||||||
}
|
}
|
||||||
@@ -164,6 +166,7 @@ void MacroConditionEdit::SetLogicSelection()
|
|||||||
const auto logic = (*_entryData)->GetLogicType();
|
const auto logic = (*_entryData)->GetLogicType();
|
||||||
_logicSelection->setCurrentIndex(
|
_logicSelection->setCurrentIndex(
|
||||||
_logicSelection->findData(static_cast<int>(logic)));
|
_logicSelection->findData(static_cast<int>(logic)));
|
||||||
|
SetEnableAppearance(logic != Logic::Type::NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionEdit::SetRootNode(bool root)
|
void MacroConditionEdit::SetRootNode(bool root)
|
||||||
@@ -175,23 +178,36 @@ void MacroConditionEdit::SetRootNode(bool root)
|
|||||||
SetLogicSelection();
|
SetLogicSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionEdit::UpdateEntryData(const std::string &id)
|
void MacroConditionEdit::SetupWidgets(bool basicSetup)
|
||||||
{
|
{
|
||||||
|
if (_allWidgetsAreSetup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto id = (*_entryData)->GetId();
|
||||||
_conditionSelection->setCurrentText(obs_module_text(
|
_conditionSelection->setCurrentText(obs_module_text(
|
||||||
MacroConditionFactory::GetConditionName(id).c_str()));
|
MacroConditionFactory::GetConditionName(id).c_str()));
|
||||||
auto widget =
|
|
||||||
MacroConditionFactory::CreateWidget(id, this, *_entryData);
|
|
||||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
|
||||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
|
||||||
HeaderInfoChanged(
|
HeaderInfoChanged(
|
||||||
QString::fromStdString((*_entryData)->GetShortDesc()));
|
QString::fromStdString((*_entryData)->GetShortDesc()));
|
||||||
SetLogicSelection();
|
SetLogicSelection();
|
||||||
_section->SetContent(widget, (*_entryData)->GetCollapsed());
|
|
||||||
|
|
||||||
_dur->setVisible(MacroConditionFactory::UsesDurationModifier(id));
|
_dur->setVisible(MacroConditionFactory::UsesDurationModifier(id));
|
||||||
auto modifier = (*_entryData)->GetDurationModifier();
|
auto modifier = (*_entryData)->GetDurationModifier();
|
||||||
_dur->SetValue(modifier);
|
_dur->SetValue(modifier);
|
||||||
|
|
||||||
|
if (basicSetup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto widget =
|
||||||
|
MacroConditionFactory::CreateWidget(id, this, *_entryData);
|
||||||
|
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||||
|
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||||
|
_section->SetContent(widget, (*_entryData)->GetCollapsed());
|
||||||
SetFocusPolicyOfWidgets();
|
SetFocusPolicyOfWidgets();
|
||||||
|
|
||||||
|
_allWidgetsAreSetup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionEdit::SetEntryData(std::shared_ptr<MacroCondition> *data)
|
void MacroConditionEdit::SetEntryData(std::shared_ptr<MacroCondition> *data)
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ public:
|
|||||||
std::shared_ptr<MacroCondition> * = nullptr,
|
std::shared_ptr<MacroCondition> * = nullptr,
|
||||||
const std::string &id = MacroCondition::GetDefaultID().data(),
|
const std::string &id = MacroCondition::GetDefaultID().data(),
|
||||||
bool root = true);
|
bool root = true);
|
||||||
bool IsRootNode();
|
bool IsRootNode() const;
|
||||||
void SetRootNode(bool);
|
void SetRootNode(bool);
|
||||||
void UpdateEntryData(const std::string &id);
|
void SetupWidgets(bool basicSetup = false);
|
||||||
void SetEntryData(std::shared_ptr<MacroCondition> *);
|
void SetEntryData(std::shared_ptr<MacroCondition> *);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QListWidget>
|
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ void SetMacroSwitchedScene(bool value)
|
|||||||
{
|
{
|
||||||
static bool setupDone = false;
|
static bool setupDone = false;
|
||||||
if (!setupDone) {
|
if (!setupDone) {
|
||||||
// Will always be called with switcher lock already held
|
AddIntervalResetStep([]() { macroSceneSwitched = false; });
|
||||||
AddIntervalResetStep([]() { macroSceneSwitched = false; },
|
|
||||||
false);
|
|
||||||
setupDone = true;
|
setupDone = true;
|
||||||
}
|
}
|
||||||
macroSceneSwitched = value;
|
macroSceneSwitched = value;
|
||||||
|
|||||||
@@ -108,6 +108,24 @@ static const std::string setTempVarValueDeclString =
|
|||||||
tempVarIdParam.data() + ", in string " + valueParam.data() +
|
tempVarIdParam.data() + ", in string " + valueParam.data() +
|
||||||
", in int " + GetInstanceIdParamName().data() + ")";
|
", in int " + GetInstanceIdParamName().data() + ")";
|
||||||
|
|
||||||
|
/* Plugin status */
|
||||||
|
|
||||||
|
static constexpr std::string_view stopSignalName = "advss_plugin_stopped";
|
||||||
|
static constexpr std::string_view startSignalName = "advss_plugin_started";
|
||||||
|
static constexpr std::string_view getRunningStatusFuncName =
|
||||||
|
"advss_plugin_running";
|
||||||
|
static constexpr std::string_view resetIntervalSignalName =
|
||||||
|
"advss_interval_reset";
|
||||||
|
|
||||||
|
static const std::string stopSignalDeclString =
|
||||||
|
std::string("void ") + stopSignalName.data() + "()";
|
||||||
|
static const std::string startSignalDeclString =
|
||||||
|
std::string("void ") + startSignalName.data() + "()";
|
||||||
|
static const std::string resetIntervalDeclString =
|
||||||
|
std::string("void ") + resetIntervalSignalName.data() + "()";
|
||||||
|
static const std::string getRunningStatusDeclString =
|
||||||
|
std::string("bool ") + getRunningStatusFuncName.data() + "()";
|
||||||
|
|
||||||
static bool setup();
|
static bool setup();
|
||||||
static bool setupDone = setup();
|
static bool setupDone = setup();
|
||||||
|
|
||||||
@@ -134,6 +152,25 @@ static bool setup()
|
|||||||
&ScriptHandler::DeregisterAllTempVars, nullptr);
|
&ScriptHandler::DeregisterAllTempVars, nullptr);
|
||||||
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
|
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
|
||||||
&ScriptHandler::SetTempVarValue, nullptr);
|
&ScriptHandler::SetTempVarValue, nullptr);
|
||||||
|
proc_handler_add(ph, getRunningStatusDeclString.c_str(),
|
||||||
|
&ScriptHandler::GetRunningStatus, nullptr);
|
||||||
|
|
||||||
|
auto sh = obs_get_signal_handler();
|
||||||
|
signal_handler_add(sh, stopSignalDeclString.c_str());
|
||||||
|
signal_handler_add(sh, startSignalDeclString.c_str());
|
||||||
|
signal_handler_add(sh, resetIntervalDeclString.c_str());
|
||||||
|
|
||||||
|
static constexpr auto triggerSignal = [](const std::string_view &name) {
|
||||||
|
auto sh = obs_get_signal_handler();
|
||||||
|
struct calldata data;
|
||||||
|
calldata_init(&data);
|
||||||
|
signal_handler_signal(sh, name.data(), &data);
|
||||||
|
calldata_free(&data);
|
||||||
|
};
|
||||||
|
|
||||||
|
AddIntervalResetStep([]() { triggerSignal(resetIntervalSignalName); });
|
||||||
|
AddStartStep([]() { triggerSignal(startSignalName); });
|
||||||
|
AddStopStep([]() { triggerSignal(stopSignalName); });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,6 +513,11 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
|
|||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
|
||||||
|
{
|
||||||
|
calldata_set_bool(data, "is_running", PluginIsRunning());
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
|
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
|
||||||
{
|
{
|
||||||
const char *variableId;
|
const char *variableId;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
static void DeregisterAllTempVars(void *ctx, calldata_t *data);
|
static void DeregisterAllTempVars(void *ctx, calldata_t *data);
|
||||||
static void SetTempVarValue(void *ctx, calldata_t *data);
|
static void SetTempVarValue(void *ctx, calldata_t *data);
|
||||||
static void SetVariableValue(void *ctx, calldata_t *data);
|
static void SetVariableValue(void *ctx, calldata_t *data);
|
||||||
|
static void GetRunningStatus(void *ctx, calldata_t *data);
|
||||||
static bool ActionIdIsValid(const std::string &id);
|
static bool ActionIdIsValid(const std::string &id);
|
||||||
static bool ConditionIdIsValid(const std::string &id);
|
static bool ConditionIdIsValid(const std::string &id);
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ MacroSegmentList::MacroSegmentList(QWidget *parent)
|
|||||||
setWidget(wrapper);
|
setWidget(wrapper);
|
||||||
setWidgetResizable(true);
|
setWidgetResizable(true);
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
|
|
||||||
|
connect(verticalScrollBar(), &QScrollBar::valueChanged, this,
|
||||||
|
[this]() { SetupVisibleMacroSegmentWidgets(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
MacroSegmentList::~MacroSegmentList()
|
MacroSegmentList::~MacroSegmentList()
|
||||||
@@ -98,16 +101,18 @@ void MacroSegmentList::SetHelpMsgVisible(bool visible) const
|
|||||||
_helpMsg->setVisible(visible);
|
_helpMsg->setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroSegmentList::Insert(int idx, MacroSegmentEdit *widget)
|
void MacroSegmentList::Insert(int idx, QWidget *widget)
|
||||||
{
|
{
|
||||||
widget->installEventFilter(this);
|
widget->installEventFilter(this);
|
||||||
_contentLayout->insertWidget(idx, widget);
|
_contentLayout->insertWidget(idx, widget);
|
||||||
|
SetupVisibleMacroSegmentWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroSegmentList::Add(QWidget *widget)
|
void MacroSegmentList::Add(QWidget *widget)
|
||||||
{
|
{
|
||||||
widget->installEventFilter(this);
|
widget->installEventFilter(this);
|
||||||
_contentLayout->addWidget(widget);
|
_contentLayout->addWidget(widget);
|
||||||
|
SetupVisibleMacroSegmentWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroSegmentList::Remove(int idx) const
|
void MacroSegmentList::Remove(int idx) const
|
||||||
@@ -322,6 +327,36 @@ QRect MacroSegmentList::GetContentItemRectWithPadding(int idx) const
|
|||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroSegmentList::SetVisibilityCheckEnable(bool enable)
|
||||||
|
{
|
||||||
|
_checkVisibility = enable;
|
||||||
|
|
||||||
|
if (_checkVisibility) {
|
||||||
|
QTimer::singleShot(0, this, [this]() {
|
||||||
|
SetupVisibleMacroSegmentWidgets();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentList::SetupVisibleMacroSegmentWidgets()
|
||||||
|
{
|
||||||
|
if (!_checkVisibility) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto viewportRect = viewport()->rect();
|
||||||
|
|
||||||
|
for (auto segment : widget()->findChildren<MacroSegmentEdit *>()) {
|
||||||
|
const auto pos = segment->mapTo(viewport(), QPoint(0, 0));
|
||||||
|
const QRect rect(pos, segment->size());
|
||||||
|
if (!viewportRect.intersects(rect)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
segment->SetupWidgets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos) const
|
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos) const
|
||||||
{
|
{
|
||||||
int idx = -1;
|
int idx = -1;
|
||||||
@@ -455,4 +490,10 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
|
|||||||
_dragPosition = -1;
|
_dragPosition = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroSegmentList::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
SetupVisibleMacroSegmentWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public:
|
|||||||
MacroSegmentEdit *WidgetAt(int idx) const;
|
MacroSegmentEdit *WidgetAt(int idx) const;
|
||||||
MacroSegmentEdit *WidgetAt(const QPoint &) const;
|
MacroSegmentEdit *WidgetAt(const QPoint &) const;
|
||||||
int IndexAt(const QPoint &) const;
|
int IndexAt(const QPoint &) const;
|
||||||
void Insert(int idx, MacroSegmentEdit *widget);
|
void Insert(int idx, QWidget *widget);
|
||||||
void Add(QWidget *widget);
|
void Add(QWidget *widget);
|
||||||
void Remove(int idx) const;
|
void Remove(int idx) const;
|
||||||
void Clear(int idx = 0) const; // Clear all elements >= idx
|
void Clear(int idx = 0) const; // Clear all elements >= idx
|
||||||
@@ -28,6 +28,7 @@ public:
|
|||||||
void SetCollapsed(bool) const;
|
void SetCollapsed(bool) const;
|
||||||
void SetSelection(int idx) const;
|
void SetSelection(int idx) const;
|
||||||
QVBoxLayout *ContentLayout() const { return _contentLayout; }
|
QVBoxLayout *ContentLayout() const { return _contentLayout; }
|
||||||
|
void SetVisibilityCheckEnable(bool enable);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void SelectionChanged(int idx);
|
void SelectionChanged(int idx);
|
||||||
@@ -42,8 +43,10 @@ protected:
|
|||||||
void dragEnterEvent(QDragEnterEvent *event);
|
void dragEnterEvent(QDragEnterEvent *event);
|
||||||
void dragMoveEvent(QDragMoveEvent *event);
|
void dragMoveEvent(QDragMoveEvent *event);
|
||||||
void dropEvent(QDropEvent *event);
|
void dropEvent(QDropEvent *event);
|
||||||
|
void resizeEvent(QResizeEvent *event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetupVisibleMacroSegmentWidgets();
|
||||||
int GetSegmentIndexFromPos(const QPoint &) const;
|
int GetSegmentIndexFromPos(const QPoint &) const;
|
||||||
int GetDragIndex(const QPoint &) const;
|
int GetDragIndex(const QPoint &) const;
|
||||||
int GetDropIndex(const QPoint &) const;
|
int GetDropIndex(const QPoint &) const;
|
||||||
@@ -62,6 +65,8 @@ private:
|
|||||||
QVBoxLayout *_layout;
|
QVBoxLayout *_layout;
|
||||||
QVBoxLayout *_contentLayout;
|
QVBoxLayout *_contentLayout;
|
||||||
QLabel *_helpMsg;
|
QLabel *_helpMsg;
|
||||||
|
|
||||||
|
bool _checkVisibility = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
|
#include <QGraphicsOpacityEffect>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
@@ -24,24 +25,24 @@ bool MacroSegment::Save(obs_data_t *obj) const
|
|||||||
obs_data_set_bool(data, "collapsed", _collapsed);
|
obs_data_set_bool(data, "collapsed", _collapsed);
|
||||||
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
|
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
|
||||||
obs_data_set_string(data, "customLabel", _customLabel.c_str());
|
obs_data_set_string(data, "customLabel", _customLabel.c_str());
|
||||||
|
obs_data_set_bool(data, "enabled", _enabled);
|
||||||
|
obs_data_set_int(data, "version", 1);
|
||||||
obs_data_set_obj(obj, "segmentSettings", data);
|
obs_data_set_obj(obj, "segmentSettings", data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroSegment::Load(obs_data_t *obj)
|
bool MacroSegment::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
|
OBSDataAutoRelease data = obs_data_get_obj(obj, "segmentSettings");
|
||||||
|
_collapsed = obs_data_get_bool(data, "collapsed");
|
||||||
|
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
|
||||||
|
_customLabel = obs_data_get_string(data, "customLabel");
|
||||||
|
obs_data_set_default_bool(data, "enabled", true);
|
||||||
|
_enabled = obs_data_get_bool(data, "enabled");
|
||||||
|
|
||||||
// TODO: remove this fallback at some point
|
// TODO: remove this fallback at some point
|
||||||
if (obs_data_has_user_value(obj, "segmentSettings")) {
|
if (!obs_data_has_user_value(data, "version")) {
|
||||||
OBSDataAutoRelease data =
|
_enabled = obs_data_get_bool(obj, "enabled");
|
||||||
obs_data_get_obj(obj, "segmentSettings");
|
|
||||||
_collapsed = obs_data_get_bool(data, "collapsed");
|
|
||||||
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
|
|
||||||
_customLabel = obs_data_get_string(data, "customLabel");
|
|
||||||
} else {
|
|
||||||
_collapsed = obs_data_get_bool(obj, "collapsed");
|
|
||||||
_useCustomLabel = false;
|
|
||||||
_customLabel = obs_module_text(
|
|
||||||
"AdvSceneSwitcher.macroTab.segment.defaultCustomLabel");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearAvailableTempvars();
|
ClearAvailableTempvars();
|
||||||
@@ -73,6 +74,16 @@ bool MacroSegment::GetHighlightAndReset()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroSegment::SetEnabled(bool value)
|
||||||
|
{
|
||||||
|
_enabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroSegment::Enabled() const
|
||||||
|
{
|
||||||
|
return _enabled;
|
||||||
|
}
|
||||||
|
|
||||||
std::string MacroSegment::GetVariableValue() const
|
std::string MacroSegment::GetVariableValue() const
|
||||||
{
|
{
|
||||||
if (_supportsVariableValue) {
|
if (_supportsVariableValue) {
|
||||||
@@ -313,13 +324,29 @@ void MacroSegmentEdit::HeaderInfoChanged(const QString &text)
|
|||||||
_headerInfo->setText(text);
|
_headerInfo->setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroSegmentEdit::Collapsed(bool collapsed)
|
void MacroSegmentEdit::Collapsed(bool collapsed) const
|
||||||
{
|
{
|
||||||
if (Data()) {
|
if (Data()) {
|
||||||
Data()->SetCollapsed(collapsed);
|
Data()->SetCollapsed(collapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroSegmentEdit::SetDisableEffect(bool value)
|
||||||
|
{
|
||||||
|
if (value) {
|
||||||
|
auto effect = new QGraphicsOpacityEffect(this);
|
||||||
|
effect->setOpacity(0.5);
|
||||||
|
_section->setGraphicsEffect(effect);
|
||||||
|
} else {
|
||||||
|
_section->setGraphicsEffect(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentEdit::SetEnableAppearance(bool value)
|
||||||
|
{
|
||||||
|
SetDisableEffect(!value);
|
||||||
|
}
|
||||||
|
|
||||||
void MacroSegmentEdit::SetFocusPolicyOfWidgets()
|
void MacroSegmentEdit::SetFocusPolicyOfWidgets()
|
||||||
{
|
{
|
||||||
QList<QWidget *> widgets = this->findChildren<QWidget *>();
|
QList<QWidget *> widgets = this->findChildren<QWidget *>();
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ public:
|
|||||||
virtual std::string GetId() const = 0;
|
virtual std::string GetId() const = 0;
|
||||||
void EnableHighlight();
|
void EnableHighlight();
|
||||||
bool GetHighlightAndReset();
|
bool GetHighlightAndReset();
|
||||||
|
void SetEnabled(bool);
|
||||||
|
bool Enabled() const;
|
||||||
virtual std::string GetVariableValue() const;
|
virtual std::string GetVariableValue() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -46,7 +48,7 @@ protected:
|
|||||||
friend void IncrementVariableRef(MacroSegment *);
|
friend void IncrementVariableRef(MacroSegment *);
|
||||||
friend void DecrementVariableRef(MacroSegment *);
|
friend void DecrementVariableRef(MacroSegment *);
|
||||||
void SetVariableValue(const std::string &value);
|
void SetVariableValue(const std::string &value);
|
||||||
bool IsReferencedInVars() { return _variableRefs != 0; }
|
bool IsReferencedInVars() const { return _variableRefs != 0; }
|
||||||
|
|
||||||
virtual void SetupTempVars();
|
virtual void SetupTempVars();
|
||||||
void AddTempvar(const std::string &id, const std::string &name,
|
void AddTempvar(const std::string &id, const std::string &name,
|
||||||
@@ -75,6 +77,7 @@ private:
|
|||||||
// UI helper
|
// UI helper
|
||||||
bool _highlight = false;
|
bool _highlight = false;
|
||||||
bool _collapsed = false;
|
bool _collapsed = false;
|
||||||
|
bool _enabled = true;
|
||||||
|
|
||||||
// Custom header labels
|
// Custom header labels
|
||||||
bool _useCustomLabel = false;
|
bool _useCustomLabel = false;
|
||||||
@@ -103,12 +106,13 @@ public:
|
|||||||
void SetCollapsed(bool collapsed);
|
void SetCollapsed(bool collapsed);
|
||||||
void SetSelected(bool);
|
void SetSelected(bool);
|
||||||
virtual std::shared_ptr<MacroSegment> Data() const = 0;
|
virtual std::shared_ptr<MacroSegment> Data() const = 0;
|
||||||
|
virtual void SetupWidgets(bool basicSetup = false) = 0;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void Collapsed(bool);
|
void Collapsed(bool) const;
|
||||||
signals:
|
signals:
|
||||||
void MacroAdded(const QString &name);
|
void MacroAdded(const QString &name);
|
||||||
void MacroRemoved(const QString &name);
|
void MacroRemoved(const QString &name);
|
||||||
@@ -118,12 +122,16 @@ signals:
|
|||||||
void SceneGroupRenamed(const QString &oldName, const QString newName);
|
void SceneGroupRenamed(const QString &oldName, const QString newName);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void SetDisableEffect(bool);
|
||||||
|
void SetEnableAppearance(bool);
|
||||||
|
|
||||||
bool eventFilter(QObject *obj, QEvent *ev) override;
|
bool eventFilter(QObject *obj, QEvent *ev) override;
|
||||||
|
|
||||||
Section *_section;
|
Section *_section;
|
||||||
QLabel *_headerInfo;
|
QLabel *_headerInfo;
|
||||||
QWidget *_frame;
|
QWidget *_frame;
|
||||||
QVBoxLayout *_contentLayout;
|
QVBoxLayout *_contentLayout;
|
||||||
|
bool _allWidgetsAreSetup = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class DropLineState {
|
enum class DropLineState {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ void GlobalMacroSettings::Save(obs_data_t *obj) const
|
|||||||
obs_data_set_bool(data, "highlightExecuted", _highlightExecuted);
|
obs_data_set_bool(data, "highlightExecuted", _highlightExecuted);
|
||||||
obs_data_set_bool(data, "highlightConditions", _highlightConditions);
|
obs_data_set_bool(data, "highlightConditions", _highlightConditions);
|
||||||
obs_data_set_bool(data, "highlightActions", _highlightActions);
|
obs_data_set_bool(data, "highlightActions", _highlightActions);
|
||||||
|
obs_data_set_bool(data, "newMacroCheckInParallel",
|
||||||
|
_newMacroCheckInParallel);
|
||||||
obs_data_set_bool(data, "newMacroRegisterHotkey",
|
obs_data_set_bool(data, "newMacroRegisterHotkey",
|
||||||
_newMacroRegisterHotkeys);
|
_newMacroRegisterHotkeys);
|
||||||
obs_data_set_bool(data, "newMacroUseShortCircuitEvaluation",
|
obs_data_set_bool(data, "newMacroUseShortCircuitEvaluation",
|
||||||
@@ -41,6 +43,8 @@ void GlobalMacroSettings::Load(obs_data_t *obj)
|
|||||||
_highlightExecuted = obs_data_get_bool(data, "highlightExecuted");
|
_highlightExecuted = obs_data_get_bool(data, "highlightExecuted");
|
||||||
_highlightConditions = obs_data_get_bool(data, "highlightConditions");
|
_highlightConditions = obs_data_get_bool(data, "highlightConditions");
|
||||||
_highlightActions = obs_data_get_bool(data, "highlightActions");
|
_highlightActions = obs_data_get_bool(data, "highlightActions");
|
||||||
|
_newMacroCheckInParallel =
|
||||||
|
obs_data_get_bool(data, "newMacroCheckInParallel");
|
||||||
_newMacroRegisterHotkeys =
|
_newMacroRegisterHotkeys =
|
||||||
obs_data_get_bool(data, "newMacroRegisterHotkey");
|
obs_data_get_bool(data, "newMacroRegisterHotkey");
|
||||||
_newMacroUseShortCircuitEvaluation =
|
_newMacroUseShortCircuitEvaluation =
|
||||||
@@ -62,12 +66,16 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||||||
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
|
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
|
||||||
_highlightActions(new QCheckBox(obs_module_text(
|
_highlightActions(new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
|
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
|
||||||
|
_newMacroCheckInParallel(new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.newMacroCheckInParallel"))),
|
||||||
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
|
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
|
||||||
_newMacroUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
|
_newMacroUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation"))),
|
"AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation"))),
|
||||||
_saveSettingsOnMacroChange(new QCheckBox(obs_module_text(
|
_saveSettingsOnMacroChange(new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange"))),
|
"AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange"))),
|
||||||
|
_currentCheckInParallel(new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.currentCheckInParallel"))),
|
||||||
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.currentRegisterHotkeys"))),
|
"AdvSceneSwitcher.macroTab.currentRegisterHotkeys"))),
|
||||||
_currentUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
|
_currentUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
|
||||||
@@ -107,6 +115,11 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||||||
setWindowModality(Qt::WindowModality::WindowModal);
|
setWindowModality(Qt::WindowModality::WindowModal);
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
|
||||||
|
_newMacroCheckInParallel->setToolTip(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.checkInParallel.tooltip"));
|
||||||
|
_currentCheckInParallel->setToolTip(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.checkInParallel.tooltip"));
|
||||||
|
|
||||||
_newMacroUseShortCircuitEvaluation->setToolTip(obs_module_text(
|
_newMacroUseShortCircuitEvaluation->setToolTip(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
|
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
|
||||||
_saveSettingsOnMacroChange->setToolTip(obs_module_text(
|
_saveSettingsOnMacroChange->setToolTip(obs_module_text(
|
||||||
@@ -146,6 +159,8 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||||||
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
|
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
|
||||||
auto generalLayout = new QVBoxLayout;
|
auto generalLayout = new QVBoxLayout;
|
||||||
generalLayout->addWidget(_saveSettingsOnMacroChange);
|
generalLayout->addWidget(_saveSettingsOnMacroChange);
|
||||||
|
generalLayout->addWidget(_currentCheckInParallel);
|
||||||
|
generalLayout->addWidget(_newMacroCheckInParallel);
|
||||||
generalLayout->addWidget(_currentSkipOnStartup);
|
generalLayout->addWidget(_currentSkipOnStartup);
|
||||||
generalLayout->addWidget(_currentStopActionsIfNotDone);
|
generalLayout->addWidget(_currentStopActionsIfNotDone);
|
||||||
generalLayout->addWidget(_currentUseShortCircuitEvaluation);
|
generalLayout->addWidget(_currentUseShortCircuitEvaluation);
|
||||||
@@ -273,6 +288,7 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||||||
_highlightExecutedMacros->setChecked(settings._highlightExecuted);
|
_highlightExecutedMacros->setChecked(settings._highlightExecuted);
|
||||||
_highlightConditions->setChecked(settings._highlightConditions);
|
_highlightConditions->setChecked(settings._highlightConditions);
|
||||||
_highlightActions->setChecked(settings._highlightActions);
|
_highlightActions->setChecked(settings._highlightActions);
|
||||||
|
_newMacroCheckInParallel->setChecked(settings._newMacroCheckInParallel);
|
||||||
_newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
|
_newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
|
||||||
_newMacroUseShortCircuitEvaluation->setChecked(
|
_newMacroUseShortCircuitEvaluation->setChecked(
|
||||||
settings._newMacroUseShortCircuitEvaluation);
|
settings._newMacroUseShortCircuitEvaluation);
|
||||||
@@ -280,20 +296,23 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||||||
settings._saveSettingsOnMacroChange);
|
settings._saveSettingsOnMacroChange);
|
||||||
|
|
||||||
if (!macro || macro->IsGroup()) {
|
if (!macro || macro->IsGroup()) {
|
||||||
hotkeyOptions->hide();
|
|
||||||
|
|
||||||
// General group
|
// General group
|
||||||
_currentSkipOnStartup->hide();
|
_currentSkipOnStartup->hide();
|
||||||
_currentStopActionsIfNotDone->hide();
|
_currentStopActionsIfNotDone->hide();
|
||||||
_currentUseShortCircuitEvaluation->hide();
|
_currentUseShortCircuitEvaluation->hide();
|
||||||
|
_currentCheckInParallel->hide();
|
||||||
SetLayoutVisible(customConditionIntervalLayout, false);
|
SetLayoutVisible(customConditionIntervalLayout, false);
|
||||||
SetLayoutVisible(pauseStateSaveBehavorLayout, false);
|
SetLayoutVisible(pauseStateSaveBehavorLayout, false);
|
||||||
|
|
||||||
|
// Hotkey group
|
||||||
|
_currentMacroRegisterHotkeys->hide();
|
||||||
|
|
||||||
inputOptions->hide();
|
inputOptions->hide();
|
||||||
_dockOptions->hide();
|
_dockOptions->hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_currentCheckInParallel->setChecked(macro->CheckInParallel());
|
||||||
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
|
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
|
||||||
_currentUseShortCircuitEvaluation->setChecked(
|
_currentUseShortCircuitEvaluation->setChecked(
|
||||||
macro->ShortCircuitEvaluationEnabled());
|
macro->ShortCircuitEvaluationEnabled());
|
||||||
@@ -431,6 +450,8 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
|
|||||||
userInput._highlightConditions =
|
userInput._highlightConditions =
|
||||||
dialog._highlightConditions->isChecked();
|
dialog._highlightConditions->isChecked();
|
||||||
userInput._highlightActions = dialog._highlightActions->isChecked();
|
userInput._highlightActions = dialog._highlightActions->isChecked();
|
||||||
|
userInput._newMacroCheckInParallel =
|
||||||
|
dialog._newMacroCheckInParallel->isChecked();
|
||||||
userInput._newMacroRegisterHotkeys =
|
userInput._newMacroRegisterHotkeys =
|
||||||
dialog._newMacroRegisterHotkeys->isChecked();
|
dialog._newMacroRegisterHotkeys->isChecked();
|
||||||
userInput._newMacroUseShortCircuitEvaluation =
|
userInput._newMacroUseShortCircuitEvaluation =
|
||||||
@@ -441,6 +462,7 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro->SetCheckInParallel(dialog._currentCheckInParallel->isChecked());
|
||||||
macro->EnablePauseHotkeys(
|
macro->EnablePauseHotkeys(
|
||||||
dialog._currentMacroRegisterHotkeys->isChecked());
|
dialog._currentMacroRegisterHotkeys->isChecked());
|
||||||
macro->SetShortCircuitEvaluation(
|
macro->SetShortCircuitEvaluation(
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ public:
|
|||||||
void Save(obs_data_t *obj) const;
|
void Save(obs_data_t *obj) const;
|
||||||
void Load(obs_data_t *obj);
|
void Load(obs_data_t *obj);
|
||||||
|
|
||||||
bool _highlightExecuted = false;
|
bool _highlightExecuted = true;
|
||||||
bool _highlightConditions = false;
|
bool _highlightConditions = true;
|
||||||
bool _highlightActions = false;
|
bool _highlightActions = true;
|
||||||
|
bool _newMacroCheckInParallel = false;
|
||||||
bool _newMacroRegisterHotkeys = true;
|
bool _newMacroRegisterHotkeys = true;
|
||||||
bool _newMacroUseShortCircuitEvaluation = false;
|
bool _newMacroUseShortCircuitEvaluation = false;
|
||||||
bool _saveSettingsOnMacroChange = true;
|
bool _saveSettingsOnMacroChange = true;
|
||||||
@@ -52,10 +53,12 @@ private:
|
|||||||
QCheckBox *_highlightExecutedMacros;
|
QCheckBox *_highlightExecutedMacros;
|
||||||
QCheckBox *_highlightConditions;
|
QCheckBox *_highlightConditions;
|
||||||
QCheckBox *_highlightActions;
|
QCheckBox *_highlightActions;
|
||||||
|
QCheckBox *_newMacroCheckInParallel;
|
||||||
QCheckBox *_newMacroRegisterHotkeys;
|
QCheckBox *_newMacroRegisterHotkeys;
|
||||||
QCheckBox *_newMacroUseShortCircuitEvaluation;
|
QCheckBox *_newMacroUseShortCircuitEvaluation;
|
||||||
QCheckBox *_saveSettingsOnMacroChange;
|
QCheckBox *_saveSettingsOnMacroChange;
|
||||||
// Current macro specific settings
|
// Current macro specific settings
|
||||||
|
QCheckBox *_currentCheckInParallel;
|
||||||
QCheckBox *_currentMacroRegisterHotkeys;
|
QCheckBox *_currentMacroRegisterHotkeys;
|
||||||
QCheckBox *_currentUseShortCircuitEvaluation;
|
QCheckBox *_currentUseShortCircuitEvaluation;
|
||||||
QCheckBox *_currentUseCustomConditionCheckInterval;
|
QCheckBox *_currentUseCustomConditionCheckInterval;
|
||||||
|
|||||||
@@ -27,6 +27,14 @@ namespace advss {
|
|||||||
static QObject *addPulse = nullptr;
|
static QObject *addPulse = nullptr;
|
||||||
static QTimer onChangeHighlightTimer;
|
static QTimer onChangeHighlightTimer;
|
||||||
|
|
||||||
|
static void disableAddButtonHighlight()
|
||||||
|
{
|
||||||
|
if (addPulse) {
|
||||||
|
addPulse->deleteLater();
|
||||||
|
addPulse = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool macroNameExists(const std::string &name)
|
static bool macroNameExists(const std::string &name)
|
||||||
{
|
{
|
||||||
return !!GetMacroByName(name.c_str());
|
return !!GetMacroByName(name.c_str());
|
||||||
@@ -86,9 +94,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = std::make_shared<Macro>(
|
res = std::make_shared<Macro>(name, GetGlobalMacroSettings());
|
||||||
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys,
|
|
||||||
GetGlobalMacroSettings()._newMacroUseShortCircuitEvaluation);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,10 +106,7 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addPulse) {
|
disableAddButtonHighlight();
|
||||||
addPulse->deleteLater();
|
|
||||||
addPulse = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto selectedMacro = GetSelectedMacro();
|
auto selectedMacro = GetSelectedMacro();
|
||||||
if (!selectedMacro) {
|
if (!selectedMacro) {
|
||||||
@@ -160,7 +163,7 @@ void AdvSceneSwitcher::RemoveSelectedMacros()
|
|||||||
if (macroCount == 1) {
|
if (macroCount == 1) {
|
||||||
QString deleteWarning = obs_module_text(
|
QString deleteWarning = obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text");
|
"AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text");
|
||||||
auto macro = macros.at(0);
|
auto ¯o = macros.at(0);
|
||||||
deleteWarning = deleteWarning.arg(
|
deleteWarning = deleteWarning.arg(
|
||||||
QString::fromStdString(macro->Name()));
|
QString::fromStdString(macro->Name()));
|
||||||
|
|
||||||
@@ -200,7 +203,7 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
|
|||||||
RemoveSelectedMacros();
|
RemoveSelectedMacros();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_macroUp_clicked()
|
void AdvSceneSwitcher::on_macroUp_clicked() const
|
||||||
{
|
{
|
||||||
auto macro = GetSelectedMacro();
|
auto macro = GetSelectedMacro();
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -209,7 +212,7 @@ void AdvSceneSwitcher::on_macroUp_clicked()
|
|||||||
ui->macros->Up(macro);
|
ui->macros->Up(macro);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_macroDown_clicked()
|
void AdvSceneSwitcher::on_macroDown_clicked() const
|
||||||
{
|
{
|
||||||
auto macro = GetSelectedMacro();
|
auto macro = GetSelectedMacro();
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -279,7 +282,7 @@ static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> ¯os,
|
|||||||
macros.insert(it, subitems.begin(), subitems.end());
|
macros.insert(it, subitems.begin(), subitems.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::ExportMacros()
|
void AdvSceneSwitcher::ExportMacros() const
|
||||||
{
|
{
|
||||||
auto selectedMacros = GetSelectedMacros();
|
auto selectedMacros = GetSelectedMacros();
|
||||||
auto macros = selectedMacros;
|
auto macros = selectedMacros;
|
||||||
@@ -490,6 +493,7 @@ void AdvSceneSwitcher::ImportMacros()
|
|||||||
|
|
||||||
ui->macros->Reset(GetMacros(),
|
ui->macros->Reset(GetMacros(),
|
||||||
GetGlobalMacroSettings()._highlightExecuted);
|
GetGlobalMacroSettings()._highlightExecuted);
|
||||||
|
disableAddButtonHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_macroName_editingFinished()
|
void AdvSceneSwitcher::on_macroName_editingFinished()
|
||||||
@@ -510,7 +514,7 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
|
|||||||
RenameMacro(macro, newName);
|
RenameMacro(macro, newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value)
|
void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value) const
|
||||||
{
|
{
|
||||||
auto macro = GetSelectedMacro();
|
auto macro = GetSelectedMacro();
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -520,7 +524,7 @@ void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value)
|
|||||||
macro->SetRunInParallel(value);
|
macro->SetRunInParallel(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value)
|
void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value) const
|
||||||
{
|
{
|
||||||
auto macro = GetSelectedMacro();
|
auto macro = GetSelectedMacro();
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -533,29 +537,56 @@ void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value)
|
|||||||
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx)
|
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx)
|
||||||
{
|
{
|
||||||
auto &actions = m.Actions();
|
auto &actions = m.Actions();
|
||||||
|
|
||||||
|
// The layout system has not completed geometry propagation yet, so we
|
||||||
|
// can skip those checks for now
|
||||||
|
ui->actionsList->SetVisibilityCheckEnable(false);
|
||||||
|
|
||||||
for (; afterIdx < actions.size(); afterIdx++) {
|
for (; afterIdx < actions.size(); afterIdx++) {
|
||||||
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
|
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
|
||||||
actions[afterIdx]->GetId());
|
actions[afterIdx]->GetId());
|
||||||
ui->actionsList->Add(newEntry);
|
ui->actionsList->Add(newEntry);
|
||||||
}
|
}
|
||||||
ui->actionsList->SetHelpMsgVisible(actions.size() == 0);
|
ui->actionsList->SetHelpMsgVisible(actions.size() == 0);
|
||||||
|
|
||||||
|
// Give the layout system time before enabling the visibility checks and
|
||||||
|
// fully constructing the visible macro segments
|
||||||
|
QTimer::singleShot(0, this, [this]() {
|
||||||
|
ui->actionsList->SetVisibilityCheckEnable(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::PopulateMacroElseActions(Macro &m, uint32_t afterIdx)
|
void AdvSceneSwitcher::PopulateMacroElseActions(Macro &m, uint32_t afterIdx)
|
||||||
{
|
{
|
||||||
auto &actions = m.ElseActions();
|
auto &actions = m.ElseActions();
|
||||||
|
|
||||||
|
// The layout system has not completed geometry propagation yet, so we
|
||||||
|
// can skip those checks for now
|
||||||
|
ui->elseActionsList->SetVisibilityCheckEnable(false);
|
||||||
|
|
||||||
for (; afterIdx < actions.size(); afterIdx++) {
|
for (; afterIdx < actions.size(); afterIdx++) {
|
||||||
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
|
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
|
||||||
actions[afterIdx]->GetId());
|
actions[afterIdx]->GetId());
|
||||||
ui->elseActionsList->Add(newEntry);
|
ui->elseActionsList->Add(newEntry);
|
||||||
}
|
}
|
||||||
ui->elseActionsList->SetHelpMsgVisible(actions.size() == 0);
|
ui->elseActionsList->SetHelpMsgVisible(actions.size() == 0);
|
||||||
|
|
||||||
|
// Give the layout system time before enabling the visibility checks and
|
||||||
|
// fully constructing the visible macro segments
|
||||||
|
QTimer::singleShot(0, this, [this]() {
|
||||||
|
ui->elseActionsList->SetVisibilityCheckEnable(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
|
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
|
||||||
{
|
{
|
||||||
bool root = afterIdx == 0;
|
bool root = afterIdx == 0;
|
||||||
auto &conditions = m.Conditions();
|
auto &conditions = m.Conditions();
|
||||||
|
|
||||||
|
// The layout system has not completed geometry propagation yet, so we
|
||||||
|
// can skip those checks for now
|
||||||
|
ui->conditionsList->SetVisibilityCheckEnable(false);
|
||||||
|
|
||||||
for (; afterIdx < conditions.size(); afterIdx++) {
|
for (; afterIdx < conditions.size(); afterIdx++) {
|
||||||
auto newEntry = new MacroConditionEdit(
|
auto newEntry = new MacroConditionEdit(
|
||||||
this, &conditions[afterIdx],
|
this, &conditions[afterIdx],
|
||||||
@@ -564,9 +595,15 @@ void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
|
|||||||
root = false;
|
root = false;
|
||||||
}
|
}
|
||||||
ui->conditionsList->SetHelpMsgVisible(conditions.size() == 0);
|
ui->conditionsList->SetHelpMsgVisible(conditions.size() == 0);
|
||||||
|
|
||||||
|
// Give the layout system time before enabling the visibility checks and
|
||||||
|
// fully constructing the visible macro segments
|
||||||
|
QTimer::singleShot(0, this, [this]() {
|
||||||
|
ui->conditionsList->SetVisibilityCheckEnable(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetActionData(Macro &m)
|
void AdvSceneSwitcher::SetActionData(Macro &m) const
|
||||||
{
|
{
|
||||||
auto &actions = m.Actions();
|
auto &actions = m.Actions();
|
||||||
for (int idx = 0; idx < ui->actionsList->ContentLayout()->count();
|
for (int idx = 0; idx < ui->actionsList->ContentLayout()->count();
|
||||||
@@ -583,7 +620,7 @@ void AdvSceneSwitcher::SetActionData(Macro &m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetElseActionData(Macro &m)
|
void AdvSceneSwitcher::SetElseActionData(Macro &m) const
|
||||||
{
|
{
|
||||||
auto &actions = m.ElseActions();
|
auto &actions = m.ElseActions();
|
||||||
for (int idx = 0; idx < ui->elseActionsList->ContentLayout()->count();
|
for (int idx = 0; idx < ui->elseActionsList->ContentLayout()->count();
|
||||||
@@ -600,7 +637,7 @@ void AdvSceneSwitcher::SetElseActionData(Macro &m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetConditionData(Macro &m)
|
void AdvSceneSwitcher::SetConditionData(Macro &m) const
|
||||||
{
|
{
|
||||||
auto &conditions = m.Conditions();
|
auto &conditions = m.Conditions();
|
||||||
for (int idx = 0; idx < ui->conditionsList->ContentLayout()->count();
|
for (int idx = 0; idx < ui->conditionsList->ContentLayout()->count();
|
||||||
@@ -678,7 +715,7 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m)
|
|||||||
m.GetElseActionSplitterPosition());
|
m.GetElseActionSplitterPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable)
|
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
|
||||||
{
|
{
|
||||||
ui->macroName->setDisabled(disable);
|
ui->macroName->setDisabled(disable);
|
||||||
ui->runMacro->setDisabled(disable);
|
ui->runMacro->setDisabled(disable);
|
||||||
@@ -689,32 +726,32 @@ void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable)
|
|||||||
ui->macroActionConditionSplitter->setDisabled(disable);
|
ui->macroActionConditionSplitter->setDisabled(disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::HighlightAction(int idx, QColor color)
|
void AdvSceneSwitcher::HighlightAction(int idx, QColor color) const
|
||||||
{
|
{
|
||||||
ui->actionsList->Highlight(idx, color);
|
ui->actionsList->Highlight(idx, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::HighlightElseAction(int idx, QColor color)
|
void AdvSceneSwitcher::HighlightElseAction(int idx, QColor color) const
|
||||||
{
|
{
|
||||||
ui->elseActionsList->Highlight(idx, color);
|
ui->elseActionsList->Highlight(idx, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::HighlightCondition(int idx, QColor color)
|
void AdvSceneSwitcher::HighlightCondition(int idx, QColor color) const
|
||||||
{
|
{
|
||||||
ui->conditionsList->Highlight(idx, color);
|
ui->conditionsList->Highlight(idx, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Macro> AdvSceneSwitcher::GetSelectedMacro()
|
std::shared_ptr<Macro> AdvSceneSwitcher::GetSelectedMacro() const
|
||||||
{
|
{
|
||||||
return ui->macros->GetCurrentMacro();
|
return ui->macros->GetCurrentMacro();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Macro>> AdvSceneSwitcher::GetSelectedMacros()
|
std::vector<std::shared_ptr<Macro>> AdvSceneSwitcher::GetSelectedMacros() const
|
||||||
{
|
{
|
||||||
return ui->macros->GetCurrentMacros();
|
return ui->macros->GetCurrentMacros();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::MacroSelectionAboutToChange()
|
void AdvSceneSwitcher::MacroSelectionAboutToChange() const
|
||||||
{
|
{
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
@@ -770,7 +807,7 @@ void AdvSceneSwitcher::MacroSelectionChanged()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::HighlightOnChange()
|
void AdvSceneSwitcher::HighlightOnChange() const
|
||||||
{
|
{
|
||||||
if (!GetGlobalMacroSettings()._highlightActions &&
|
if (!GetGlobalMacroSettings()._highlightActions &&
|
||||||
!GetGlobalMacroSettings()._highlightExecuted) {
|
!GetGlobalMacroSettings()._highlightExecuted) {
|
||||||
@@ -1299,15 +1336,12 @@ void AdvSceneSwitcher::CopyMacro()
|
|||||||
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
|
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
|
||||||
|
|
||||||
ui->macros->Add(newMacro, macro);
|
ui->macros->Add(newMacro, macro);
|
||||||
if (addPulse) {
|
disableAddButtonHighlight();
|
||||||
addPulse->deleteLater();
|
|
||||||
addPulse = nullptr;
|
|
||||||
}
|
|
||||||
emit MacroAdded(QString::fromStdString(name));
|
emit MacroAdded(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCollapsedHelper(const std::shared_ptr<Macro> &m, MacroSegmentList *list,
|
static void setCollapsedHelper(const std::shared_ptr<Macro> &m,
|
||||||
bool collapsed)
|
MacroSegmentList *list, bool collapsed)
|
||||||
{
|
{
|
||||||
if (!m) {
|
if (!m) {
|
||||||
return;
|
return;
|
||||||
@@ -1315,32 +1349,32 @@ void setCollapsedHelper(const std::shared_ptr<Macro> &m, MacroSegmentList *list,
|
|||||||
list->SetCollapsed(collapsed);
|
list->SetCollapsed(collapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::ExpandAllActions()
|
void AdvSceneSwitcher::ExpandAllActions() const
|
||||||
{
|
{
|
||||||
setCollapsedHelper(GetSelectedMacro(), ui->actionsList, false);
|
setCollapsedHelper(GetSelectedMacro(), ui->actionsList, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::ExpandAllElseActions()
|
void AdvSceneSwitcher::ExpandAllElseActions() const
|
||||||
{
|
{
|
||||||
setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, false);
|
setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::ExpandAllConditions()
|
void AdvSceneSwitcher::ExpandAllConditions() const
|
||||||
{
|
{
|
||||||
setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, false);
|
setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::CollapseAllActions()
|
void AdvSceneSwitcher::CollapseAllActions() const
|
||||||
{
|
{
|
||||||
setCollapsedHelper(GetSelectedMacro(), ui->actionsList, true);
|
setCollapsedHelper(GetSelectedMacro(), ui->actionsList, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::CollapseAllElseActions()
|
void AdvSceneSwitcher::CollapseAllElseActions() const
|
||||||
{
|
{
|
||||||
setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, true);
|
setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::CollapseAllConditions()
|
void AdvSceneSwitcher::CollapseAllConditions() const
|
||||||
{
|
{
|
||||||
setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, true);
|
setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, true);
|
||||||
}
|
}
|
||||||
@@ -1355,7 +1389,7 @@ static void reduceSizeOfSplitterIdx(QSplitter *splitter, int idx)
|
|||||||
splitter->setSizes(sizes);
|
splitter->setSizes(sizes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::MinimizeActions()
|
void AdvSceneSwitcher::MinimizeActions() const
|
||||||
{
|
{
|
||||||
auto macro = GetSelectedMacro();
|
auto macro = GetSelectedMacro();
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -1369,13 +1403,13 @@ void AdvSceneSwitcher::MinimizeActions()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::MaximizeActions()
|
void AdvSceneSwitcher::MaximizeActions() const
|
||||||
{
|
{
|
||||||
MinimizeElseActions();
|
MinimizeElseActions();
|
||||||
MinimizeConditions();
|
MinimizeConditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::MinimizeElseActions()
|
void AdvSceneSwitcher::MinimizeElseActions() const
|
||||||
{
|
{
|
||||||
auto macro = GetSelectedMacro();
|
auto macro = GetSelectedMacro();
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -1388,24 +1422,24 @@ void AdvSceneSwitcher::MinimizeElseActions()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::MaximizeElseActions()
|
void AdvSceneSwitcher::MaximizeElseActions() const
|
||||||
{
|
{
|
||||||
MinimizeConditions();
|
MinimizeConditions();
|
||||||
reduceSizeOfSplitterIdx(ui->macroElseActionSplitter, 0);
|
reduceSizeOfSplitterIdx(ui->macroElseActionSplitter, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::MinimizeConditions()
|
void AdvSceneSwitcher::MinimizeConditions() const
|
||||||
{
|
{
|
||||||
reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 0);
|
reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::MaximizeConditions()
|
void AdvSceneSwitcher::MaximizeConditions() const
|
||||||
{
|
{
|
||||||
MinimizeElseActions();
|
MinimizeElseActions();
|
||||||
MinimizeActions();
|
MinimizeActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_toggleElseActions_clicked()
|
void AdvSceneSwitcher::on_toggleElseActions_clicked() const
|
||||||
{
|
{
|
||||||
auto elsePosition = ui->macroElseActionSplitter->sizes();
|
auto elsePosition = ui->macroElseActionSplitter->sizes();
|
||||||
if (elsePosition[1] == 0) {
|
if (elsePosition[1] == 0) {
|
||||||
@@ -1416,14 +1450,14 @@ void AdvSceneSwitcher::on_toggleElseActions_clicked()
|
|||||||
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
|
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetElseActionsStateToHidden()
|
void AdvSceneSwitcher::SetElseActionsStateToHidden() const
|
||||||
{
|
{
|
||||||
ui->toggleElseActions->setToolTip(obs_module_text(
|
ui->toggleElseActions->setToolTip(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.toggleElseActions.show.tooltip"));
|
"AdvSceneSwitcher.macroTab.toggleElseActions.show.tooltip"));
|
||||||
ui->toggleElseActions->setChecked(false);
|
ui->toggleElseActions->setChecked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetElseActionsStateToVisible()
|
void AdvSceneSwitcher::SetElseActionsStateToVisible() const
|
||||||
{
|
{
|
||||||
ui->toggleElseActions->setToolTip(obs_module_text(
|
ui->toggleElseActions->setToolTip(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.toggleElseActions.hide.tooltip"));
|
"AdvSceneSwitcher.macroTab.toggleElseActions.hide.tooltip"));
|
||||||
@@ -1601,7 +1635,7 @@ static void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::HighlightControls()
|
void AdvSceneSwitcher::HighlightControls() const
|
||||||
{
|
{
|
||||||
const std::vector<QWidget *> conditionControls{
|
const std::vector<QWidget *> conditionControls{
|
||||||
ui->conditionAdd, ui->conditionRemove, ui->conditionTop,
|
ui->conditionAdd, ui->conditionRemove, ui->conditionTop,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "macro-condition-factory.hpp"
|
#include "macro-condition-factory.hpp"
|
||||||
#include "macro-dock.hpp"
|
#include "macro-dock.hpp"
|
||||||
#include "macro-helpers.hpp"
|
#include "macro-helpers.hpp"
|
||||||
|
#include "macro-settings.hpp"
|
||||||
#include "plugin-state-helpers.hpp"
|
#include "plugin-state-helpers.hpp"
|
||||||
#include "splitter-helpers.hpp"
|
#include "splitter-helpers.hpp"
|
||||||
#include "sync-helpers.hpp"
|
#include "sync-helpers.hpp"
|
||||||
@@ -76,15 +77,21 @@ namespace advss {
|
|||||||
|
|
||||||
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 shortCircuitEvaluation)
|
|
||||||
{
|
{
|
||||||
SetName(name);
|
SetName(name);
|
||||||
if (addHotkey) {
|
}
|
||||||
|
|
||||||
|
Macro::Macro(const std::string &name, const GlobalMacroSettings &settings)
|
||||||
|
: Macro(name)
|
||||||
|
{
|
||||||
|
if (settings._newMacroRegisterHotkeys) {
|
||||||
SetupHotkeys();
|
SetupHotkeys();
|
||||||
}
|
}
|
||||||
_registerHotkeys = addHotkey;
|
_registerHotkeys = settings._newMacroRegisterHotkeys;
|
||||||
_useShortCircuitEvaluation = shortCircuitEvaluation;
|
_checkInParallel = settings._newMacroCheckInParallel;
|
||||||
|
_useShortCircuitEvaluation =
|
||||||
|
settings._newMacroUseShortCircuitEvaluation;
|
||||||
}
|
}
|
||||||
|
|
||||||
Macro::~Macro()
|
Macro::~Macro()
|
||||||
@@ -104,7 +111,8 @@ std::shared_ptr<Macro>
|
|||||||
Macro::CreateGroup(const std::string &name,
|
Macro::CreateGroup(const std::string &name,
|
||||||
std::vector<std::shared_ptr<Macro>> &children)
|
std::vector<std::shared_ptr<Macro>> &children)
|
||||||
{
|
{
|
||||||
auto group = std::make_shared<Macro>(name, false);
|
auto group = std::make_shared<Macro>(name);
|
||||||
|
group->_registerHotkeys = false;
|
||||||
for (auto &c : children) {
|
for (auto &c : children) {
|
||||||
c->SetParent(group);
|
c->SetParent(group);
|
||||||
}
|
}
|
||||||
@@ -244,19 +252,52 @@ bool Macro::CheckConditions(bool ignorePause)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stop = false;
|
const auto checkConditionsTask =
|
||||||
_matched = false;
|
[this,
|
||||||
for (auto &condition : _conditions) {
|
ignorePause](const std::deque<std::shared_ptr<MacroCondition>>
|
||||||
if (!condition) {
|
&conditions) {
|
||||||
continue;
|
for (auto &condition : conditions) {
|
||||||
}
|
if (!condition) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (_paused && !ignorePause) {
|
if (_paused && !ignorePause) {
|
||||||
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
|
vblog(LOG_INFO, "Macro %s is paused",
|
||||||
|
_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_matched = CheckConditionHelper(condition);
|
||||||
|
}
|
||||||
|
return _matched;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (CheckInParallel()) {
|
||||||
|
if (!_conditionCheckFuture.valid()) {
|
||||||
|
_stop = false;
|
||||||
|
_matched = false;
|
||||||
|
_conditionCheckFuture = std::async(
|
||||||
|
std::launch::async,
|
||||||
|
[this, checkConditionsTask]() {
|
||||||
|
// Copy to avoid settings modifications
|
||||||
|
// causing issues
|
||||||
|
const auto conditionsCopy = _conditions;
|
||||||
|
checkConditionsTask(conditionsCopy);
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (_conditionCheckFuture.wait_for(std::chrono::seconds(0)) !=
|
||||||
_matched = CheckConditionHelper(condition);
|
std::future_status::ready) {
|
||||||
|
vblog(LOG_INFO,
|
||||||
|
"Macro %s still waiting for condition check result",
|
||||||
|
_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_conditionCheckFuture.get();
|
||||||
|
} else {
|
||||||
|
_stop = false;
|
||||||
|
_matched = false;
|
||||||
|
_matched = checkConditionsTask(_conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
||||||
@@ -273,7 +314,9 @@ bool Macro::CheckConditions(bool ignorePause)
|
|||||||
|
|
||||||
bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
|
bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
|
||||||
{
|
{
|
||||||
if (!_done) {
|
if (_actionRunFuture.valid() &&
|
||||||
|
_actionRunFuture.wait_for(std::chrono::seconds(0)) !=
|
||||||
|
std::future_status::ready) {
|
||||||
vblog(LOG_INFO, "Macro %s already running", _name.c_str());
|
vblog(LOG_INFO, "Macro %s already running", _name.c_str());
|
||||||
|
|
||||||
if (!_stopActionsIfNotDone) {
|
if (!_stopActionsIfNotDone) {
|
||||||
@@ -291,14 +334,15 @@ bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
|
|||||||
: std::bind(&Macro::RunElseActions, this,
|
: std::bind(&Macro::RunElseActions, this,
|
||||||
std::placeholders::_1);
|
std::placeholders::_1);
|
||||||
_stop = false;
|
_stop = false;
|
||||||
_done = false;
|
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
if (_runInParallel || forceParallel) {
|
if (_runInParallel || forceParallel) {
|
||||||
if (_backgroundThread.joinable()) {
|
if (_actionRunFuture.valid()) {
|
||||||
_backgroundThread.join();
|
_actionRunFuture.get();
|
||||||
}
|
}
|
||||||
_backgroundThread = std::thread(
|
_actionRunFuture = std::async(std::launch::async,
|
||||||
[this, runFunc, ignorePause] { runFunc(ignorePause); });
|
[this, runFunc, ignorePause] {
|
||||||
|
runFunc(ignorePause);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ret = runFunc(ignorePause);
|
ret = runFunc(ignorePause);
|
||||||
}
|
}
|
||||||
@@ -336,6 +380,13 @@ bool Macro::ConditionsShouldBeChecked() const
|
|||||||
|
|
||||||
bool Macro::ShouldRunActions() const
|
bool Macro::ShouldRunActions() const
|
||||||
{
|
{
|
||||||
|
if (CheckInParallel() && _conditionCheckFuture.valid()) {
|
||||||
|
vblog(LOG_INFO,
|
||||||
|
"%s not ready to perform actions as condition check is still running",
|
||||||
|
_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const bool hasActionsToExecute =
|
const bool hasActionsToExecute =
|
||||||
!_paused && (_matched || _elseActions.size() > 0) &&
|
!_paused && (_matched || _elseActions.size() > 0) &&
|
||||||
(!_performActionsOnChange || _conditionSateChanged);
|
(!_performActionsOnChange || _conditionSateChanged);
|
||||||
@@ -408,7 +459,6 @@ bool Macro::RunActionsHelper(
|
|||||||
action->EnableHighlight();
|
action->EnableHighlight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_done = true;
|
|
||||||
return actionsExecutedSuccessfully;
|
return actionsExecutedSuccessfully;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,9 +558,35 @@ void Macro::Stop()
|
|||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_backgroundThread.joinable()) {
|
if (_actionRunFuture.valid()) {
|
||||||
_backgroundThread.join();
|
_actionRunFuture.get();
|
||||||
}
|
}
|
||||||
|
if (_conditionCheckFuture.valid()) {
|
||||||
|
_conditionCheckFuture.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Macro::SetCheckInParallel(bool parallel)
|
||||||
|
{
|
||||||
|
_checkInParallel = parallel;
|
||||||
|
_conditionCheckFuture = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Macro::ParallelTasksCompleted() const
|
||||||
|
{
|
||||||
|
// A parallel action run might be triggered by RunInParallel() or the
|
||||||
|
// "Run Macro" button, so checking just for RunInParallel() will not
|
||||||
|
// suffice
|
||||||
|
if (!CheckInParallel() && !_actionRunFuture.valid()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_actionRunFuture.valid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (CheckInParallel() && _conditionCheckFuture.valid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MacroInputVariables Macro::GetInputVariables() const
|
MacroInputVariables Macro::GetInputVariables() const
|
||||||
@@ -712,6 +788,7 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
|
|||||||
static_cast<int>(_pauseSaveBehavior));
|
static_cast<int>(_pauseSaveBehavior));
|
||||||
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, "checkConditionsInParallel", _checkInParallel);
|
||||||
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
|
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
|
||||||
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
|
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
|
||||||
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
|
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
|
||||||
@@ -797,6 +874,7 @@ bool Macro::Load(obs_data_t *obj)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_runInParallel = obs_data_get_bool(obj, "parallel");
|
_runInParallel = obs_data_get_bool(obj, "parallel");
|
||||||
|
_checkInParallel = obs_data_get_bool(obj, "checkConditionsInParallel");
|
||||||
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
|
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
|
||||||
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
|
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
|
||||||
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
|
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
|
||||||
@@ -1468,6 +1546,14 @@ std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
|
|||||||
void InvalidateMacroTempVarValues()
|
void InvalidateMacroTempVarValues()
|
||||||
{
|
{
|
||||||
for (const auto &m : macros) {
|
for (const auto &m : macros) {
|
||||||
|
// Do not invalidate the temp vars set during condition checks
|
||||||
|
// or action executions running in parallel to the "main" macro
|
||||||
|
// loop, as otherwise access to the information stored in those
|
||||||
|
// variables might get lost while those checks or actions are
|
||||||
|
// still ongoing.
|
||||||
|
if (!m->ParallelTasksCompleted()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
m->InvalidateTempVarValues();
|
m->InvalidateTempVarValues();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "variable-string.hpp"
|
#include "variable-string.hpp"
|
||||||
#include "temp-variable.hpp"
|
#include "temp-variable.hpp"
|
||||||
|
|
||||||
|
#include <future>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
class MacroDock;
|
class MacroDock;
|
||||||
|
class GlobalMacroSettings;
|
||||||
|
|
||||||
class Macro {
|
class Macro {
|
||||||
using TimePoint = std::chrono::high_resolution_clock::time_point;
|
using TimePoint = std::chrono::high_resolution_clock::time_point;
|
||||||
@@ -27,8 +29,8 @@ class Macro {
|
|||||||
public:
|
public:
|
||||||
enum class PauseStateSaveBehavior { PERSIST, PAUSE, UNPAUSE };
|
enum class PauseStateSaveBehavior { PERSIST, PAUSE, UNPAUSE };
|
||||||
|
|
||||||
Macro(const std::string &name = "", const bool addHotkey = false,
|
Macro(const std::string &name = "");
|
||||||
const bool shortCircuitEvaluation = false);
|
Macro(const std::string &name, const GlobalMacroSettings &settings);
|
||||||
~Macro();
|
~Macro();
|
||||||
|
|
||||||
std::string Name() const { return _name; }
|
std::string Name() const { return _name; }
|
||||||
@@ -77,6 +79,9 @@ public:
|
|||||||
void AddHelperThread(std::thread &&);
|
void AddHelperThread(std::thread &&);
|
||||||
void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
|
void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
|
||||||
bool RunInParallel() const { return _runInParallel; }
|
bool RunInParallel() const { return _runInParallel; }
|
||||||
|
bool CheckInParallel() const { return _checkInParallel; }
|
||||||
|
void SetCheckInParallel(bool parallel);
|
||||||
|
bool ParallelTasksCompleted() const;
|
||||||
|
|
||||||
// Input variables
|
// Input variables
|
||||||
MacroInputVariables GetInputVariables() const;
|
MacroInputVariables GetInputVariables() const;
|
||||||
@@ -182,11 +187,10 @@ private:
|
|||||||
std::string _name = "";
|
std::string _name = "";
|
||||||
bool _die = false;
|
bool _die = false;
|
||||||
bool _stop = false;
|
bool _stop = false;
|
||||||
bool _done = true;
|
std::future<void> _actionRunFuture;
|
||||||
TimePoint _lastCheckTime{};
|
TimePoint _lastCheckTime{};
|
||||||
TimePoint _lastUnpauseTime{};
|
TimePoint _lastUnpauseTime{};
|
||||||
TimePoint _lastExecutionTime{};
|
TimePoint _lastExecutionTime{};
|
||||||
std::thread _backgroundThread;
|
|
||||||
std::vector<std::thread> _helperThreads;
|
std::vector<std::thread> _helperThreads;
|
||||||
|
|
||||||
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
||||||
@@ -204,7 +208,9 @@ private:
|
|||||||
bool _conditionSateChanged = false;
|
bool _conditionSateChanged = false;
|
||||||
|
|
||||||
bool _runInParallel = false;
|
bool _runInParallel = false;
|
||||||
|
bool _checkInParallel = false;
|
||||||
bool _matched = false;
|
bool _matched = false;
|
||||||
|
std::future<void> _conditionCheckFuture;
|
||||||
bool _lastMatched = false;
|
bool _lastMatched = false;
|
||||||
bool _performActionsOnChange = true;
|
bool _performActionsOnChange = true;
|
||||||
bool _skipExecOnStart = false;
|
bool _skipExecOnStart = false;
|
||||||
|
|||||||
@@ -168,17 +168,6 @@ void SwitcherData::SaveVersion(obs_data_t *obj,
|
|||||||
obs_data_set_string(obj, "version", currentVersion.c_str());
|
obs_data_set_string(obj, "version", currentVersion.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::AddIntervalResetStep(std::function<void()> function,
|
|
||||||
bool tryLock)
|
|
||||||
{
|
|
||||||
if (!tryLock) {
|
|
||||||
resetIntervalSteps.emplace_back(function);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
|
||||||
resetIntervalSteps.emplace_back(function);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitcherData::RunPostLoadSteps()
|
void SwitcherData::RunPostLoadSteps()
|
||||||
{
|
{
|
||||||
for (const auto &func : postLoadSteps) {
|
for (const auto &func : postLoadSteps) {
|
||||||
|
|||||||
@@ -57,11 +57,9 @@ public:
|
|||||||
bool AnySceneTransitionStarted();
|
bool AnySceneTransitionStarted();
|
||||||
|
|
||||||
void SetPreconditions();
|
void SetPreconditions();
|
||||||
void ResetForNextInterval();
|
|
||||||
void AddSaveStep(std::function<void(obs_data_t *)>);
|
void AddSaveStep(std::function<void(obs_data_t *)>);
|
||||||
void AddLoadStep(std::function<void(obs_data_t *)>);
|
void AddLoadStep(std::function<void(obs_data_t *)>);
|
||||||
void AddPostLoadStep(std::function<void()>);
|
void AddPostLoadStep(std::function<void()>);
|
||||||
void AddIntervalResetStep(std::function<void()>, bool lock = true);
|
|
||||||
void RunPostLoadSteps();
|
void RunPostLoadSteps();
|
||||||
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||||
int &linger, bool &setPreviousSceneAsMatch,
|
int &linger, bool &setPreviousSceneAsMatch,
|
||||||
@@ -100,7 +98,6 @@ public:
|
|||||||
std::vector<std::function<void(obs_data_t *)>> saveSteps;
|
std::vector<std::function<void(obs_data_t *)>> saveSteps;
|
||||||
std::vector<std::function<void(obs_data_t *)>> loadSteps;
|
std::vector<std::function<void(obs_data_t *)>> loadSteps;
|
||||||
std::vector<std::function<void()>> postLoadSteps;
|
std::vector<std::function<void()>> postLoadSteps;
|
||||||
std::vector<std::function<void()>> resetIntervalSteps;
|
|
||||||
|
|
||||||
bool firstBoot = true;
|
bool firstBoot = true;
|
||||||
bool transitionActive = false;
|
bool transitionActive = false;
|
||||||
@@ -135,9 +132,6 @@ public:
|
|||||||
bool transitionOverrideOverride = false;
|
bool transitionOverrideOverride = false;
|
||||||
bool adjustActiveTransitionType = true;
|
bool adjustActiveTransitionType = true;
|
||||||
|
|
||||||
enum class LogLevel { DEFAULT, LOG_MACRO, LOG_ACTION, VERBOSE };
|
|
||||||
LogLevel logLevel = LogLevel::DEFAULT;
|
|
||||||
|
|
||||||
/* --- End of General tab section --- */
|
/* --- End of General tab section --- */
|
||||||
|
|
||||||
std::string lastTitle;
|
std::string lastTitle;
|
||||||
@@ -233,7 +227,6 @@ public:
|
|||||||
bool checkPause();
|
bool checkPause();
|
||||||
void checkDefaultSceneTransitions();
|
void checkDefaultSceneTransitions();
|
||||||
void writeSceneInfoToFile();
|
void writeSceneInfoToFile();
|
||||||
void writeToStatusFile(const QString &msg);
|
|
||||||
void checkSwitchCooldown(bool &match);
|
void checkSwitchCooldown(bool &match);
|
||||||
|
|
||||||
std::deque<WindowSwitch> windowSwitches;
|
std::deque<WindowSwitch> windowSwitches;
|
||||||
|
|||||||
102
lib/utils/json-helpers.cpp
Normal file
102
lib/utils/json-helpers.cpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#include "json-helpers.hpp"
|
||||||
|
|
||||||
|
#ifdef JSONPATH_SUPPORT
|
||||||
|
#include <jsoncons/json.hpp>
|
||||||
|
#include <jsoncons_ext/jsonpath/jsonpath.hpp>
|
||||||
|
#endif
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
QString FormatJsonString(std::string s)
|
||||||
|
{
|
||||||
|
return FormatJsonString(QString::fromStdString(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FormatJsonString(QString json)
|
||||||
|
{
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
||||||
|
if (doc.isNull()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return QString::fromUtf8(doc.toJson(QJsonDocument::Indented));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MatchJson(const std::string &json1, const std::string &json2,
|
||||||
|
const RegexConfig ®ex)
|
||||||
|
{
|
||||||
|
auto j1 = FormatJsonString(json1).toStdString();
|
||||||
|
auto j2 = FormatJsonString(json2).toStdString();
|
||||||
|
|
||||||
|
if (j1.empty()) {
|
||||||
|
j1 = json1;
|
||||||
|
}
|
||||||
|
if (j2.empty()) {
|
||||||
|
j2 = json2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regex.Enabled()) {
|
||||||
|
return regex.Matches(j1, j2);
|
||||||
|
}
|
||||||
|
return j1 == j2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> GetJsonField(const std::string &jsonStr,
|
||||||
|
const std::string &fieldToExtract)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
nlohmann::json json = nlohmann::json::parse(jsonStr);
|
||||||
|
auto it = json.find(fieldToExtract);
|
||||||
|
if (it == json.end()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (it->is_string()) {
|
||||||
|
return it->get<std::string>();
|
||||||
|
}
|
||||||
|
return it->dump();
|
||||||
|
} catch (const nlohmann::json::exception &) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> QueryJson(const std::string &jsonStr,
|
||||||
|
const std::string &query)
|
||||||
|
{
|
||||||
|
#ifdef JSONPATH_SUPPORT
|
||||||
|
try {
|
||||||
|
auto json = jsoncons::json::parse(jsonStr);
|
||||||
|
auto result = jsoncons::jsonpath::json_query(json, query);
|
||||||
|
return result.as_string();
|
||||||
|
} catch (const jsoncons::json_exception &) {
|
||||||
|
return {};
|
||||||
|
} catch (const jsoncons::json_errc &) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return {};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> AccessJsonArrayIndex(const std::string &jsonStr,
|
||||||
|
const int index)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
nlohmann::json json = nlohmann::json::parse(jsonStr);
|
||||||
|
if (!json.is_array() || index >= (int)json.size() ||
|
||||||
|
index < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto result = json.at(index);
|
||||||
|
if (result.is_string()) {
|
||||||
|
return result.get<std::string>();
|
||||||
|
}
|
||||||
|
return result.dump();
|
||||||
|
} catch (const nlohmann::json::exception &) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
21
lib/utils/json-helpers.hpp
Normal file
21
lib/utils/json-helpers.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "export-symbol-helper.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
EXPORT QString FormatJsonString(std::string);
|
||||||
|
EXPORT QString FormatJsonString(QString);
|
||||||
|
EXPORT bool MatchJson(const std::string &json1, const std::string &json2,
|
||||||
|
const RegexConfig ®ex);
|
||||||
|
EXPORT std::optional<std::string> GetJsonField(const std::string &json,
|
||||||
|
const std::string &id);
|
||||||
|
EXPORT std::optional<std::string> QueryJson(const std::string &json,
|
||||||
|
const std::string &query);
|
||||||
|
EXPORT std::optional<std::string> AccessJsonArrayIndex(const std::string &json,
|
||||||
|
const int index);
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -1,26 +1,97 @@
|
|||||||
#include "log-helper.hpp"
|
#include "log-helper.hpp"
|
||||||
#include "switcher-data.hpp"
|
#include "obs-module-helper.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <obs-data.h>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
|
static LogLevel logLevel = LogLevel::DEFAULT;
|
||||||
|
|
||||||
bool VerboseLoggingEnabled()
|
bool VerboseLoggingEnabled()
|
||||||
{
|
{
|
||||||
return GetSwitcher() &&
|
return logLevel == LogLevel::VERBOSE;
|
||||||
GetSwitcher()->logLevel == SwitcherData::LogLevel::VERBOSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ActionLoggingEnabled()
|
bool ActionLoggingEnabled()
|
||||||
{
|
{
|
||||||
return GetSwitcher() &&
|
return logLevel == LogLevel::LOG_ACTION || VerboseLoggingEnabled();
|
||||||
(GetSwitcher()->logLevel == SwitcherData::LogLevel::LOG_ACTION ||
|
|
||||||
VerboseLoggingEnabled());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroLoggingEnabled()
|
bool MacroLoggingEnabled()
|
||||||
{
|
{
|
||||||
return GetSwitcher() &&
|
return logLevel == LogLevel::LOG_MACRO || ActionLoggingEnabled();
|
||||||
(GetSwitcher()->logLevel == SwitcherData::LogLevel::LOG_MACRO ||
|
}
|
||||||
ActionLoggingEnabled());
|
|
||||||
|
bool LoggingEnabled()
|
||||||
|
{
|
||||||
|
return logLevel != LogLevel::DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLogLevel(LogLevel newLogLevel)
|
||||||
|
{
|
||||||
|
logLevel = newLogLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogLevel GetLogLevel()
|
||||||
|
{
|
||||||
|
return logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveLogLevel(void *data)
|
||||||
|
{
|
||||||
|
auto obj = static_cast<obs_data_t *>(data);
|
||||||
|
obs_data_set_int(obj, "logLevel", static_cast<int>(logLevel));
|
||||||
|
obs_data_set_int(obj, "logLevelVersion", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadLogLevel(void *data)
|
||||||
|
{
|
||||||
|
auto obj = static_cast<obs_data_t *>(data);
|
||||||
|
logLevel = static_cast<LogLevel>(obs_data_get_int(obj, "logLevel"));
|
||||||
|
if (obs_data_get_int(obj, "logLevelVersion") < 1) {
|
||||||
|
enum OldLogLevel { DEFAULT, LOG_ACTION, VERBOSE };
|
||||||
|
OldLogLevel oldLogLevel = static_cast<OldLogLevel>(
|
||||||
|
obs_data_get_int(obj, "logLevel"));
|
||||||
|
switch (oldLogLevel) {
|
||||||
|
case DEFAULT:
|
||||||
|
logLevel = LogLevel::DEFAULT;
|
||||||
|
break;
|
||||||
|
case LOG_ACTION:
|
||||||
|
logLevel = LogLevel::LOG_ACTION;
|
||||||
|
break;
|
||||||
|
case VERBOSE:
|
||||||
|
logLevel = LogLevel::VERBOSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopulateLogLevelSelection(void *sel)
|
||||||
|
{
|
||||||
|
auto selection = static_cast<QComboBox *>(sel);
|
||||||
|
static const std::map<LogLevel, std::string> logLevels = {
|
||||||
|
{LogLevel::DISABLE,
|
||||||
|
"AdvSceneSwitcher.generalTab.generalBehavior.logLevel.disable"},
|
||||||
|
{LogLevel::DEFAULT,
|
||||||
|
"AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default"},
|
||||||
|
{LogLevel::LOG_MACRO,
|
||||||
|
"AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logMacro"},
|
||||||
|
{LogLevel::LOG_ACTION,
|
||||||
|
"AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction"},
|
||||||
|
{LogLevel::VERBOSE,
|
||||||
|
"AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose"},
|
||||||
|
};
|
||||||
|
|
||||||
|
selection->clear();
|
||||||
|
for (const auto &[value, name] : logLevels) {
|
||||||
|
selection->addItem(obs_module_text(name.c_str()),
|
||||||
|
static_cast<int>(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -12,7 +12,13 @@ namespace advss {
|
|||||||
#else
|
#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, ...) \
|
||||||
|
do { \
|
||||||
|
if (LoggingEnabled()) { \
|
||||||
|
blog(level, "[adv-ss] " msg, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
// Print log with "[adv-ss] " if log level is set to "verbose"
|
// Print log with "[adv-ss] " if log level is set to "verbose"
|
||||||
#define vblog(level, msg, ...) \
|
#define vblog(level, msg, ...) \
|
||||||
do { \
|
do { \
|
||||||
@@ -43,5 +49,15 @@ EXPORT bool VerboseLoggingEnabled();
|
|||||||
EXPORT bool ActionLoggingEnabled();
|
EXPORT bool ActionLoggingEnabled();
|
||||||
// Returns true if log level is set to "macro", "action" or "verbose"
|
// Returns true if log level is set to "macro", "action" or "verbose"
|
||||||
EXPORT bool MacroLoggingEnabled();
|
EXPORT bool MacroLoggingEnabled();
|
||||||
|
// Returns true if logging is enabled and false otherwise
|
||||||
|
EXPORT bool LoggingEnabled();
|
||||||
|
|
||||||
|
enum class LogLevel { DISABLE = -1, DEFAULT, LOG_MACRO, LOG_ACTION, VERBOSE };
|
||||||
|
|
||||||
|
void SetLogLevel(LogLevel);
|
||||||
|
LogLevel GetLogLevel();
|
||||||
|
void SaveLogLevel(void *data);
|
||||||
|
void LoadLogLevel(void *data);
|
||||||
|
void PopulateLogLevelSelection(void *);
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ void PreventMouseWheelAdjustWithoutFocus(QWidget *w)
|
|||||||
QString(w->metaObject()->className()) ==
|
QString(w->metaObject()->className()) ==
|
||||||
"advss::OSCMessageElementEdit" ||
|
"advss::OSCMessageElementEdit" ||
|
||||||
QString(w->metaObject()->className()) ==
|
QString(w->metaObject()->className()) ==
|
||||||
"advss::ChatMessagePropertyEdit") {
|
"advss::ChatMessagePropertyEdit" ||
|
||||||
|
QString(w->metaObject()->className()) ==
|
||||||
|
"advss::KeyValueListContainerWidget") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
w->setFocusPolicy(Qt::StrongFocus);
|
w->setFocusPolicy(Qt::StrongFocus);
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ NonModalMessageDialog::NonModalMessageDialog(const QString &message, Type type,
|
|||||||
}
|
}
|
||||||
case Type::INPUT: {
|
case Type::INPUT: {
|
||||||
_inputEdit = new ResizingPlainTextEdit(this);
|
_inputEdit = new ResizingPlainTextEdit(this);
|
||||||
|
_inputEdit->setTabChangesFocus(true);
|
||||||
|
_inputEdit->setFocus();
|
||||||
connect(_inputEdit, &ResizingPlainTextEdit::textChanged, this,
|
connect(_inputEdit, &ResizingPlainTextEdit::textChanged, this,
|
||||||
&NonModalMessageDialog::InputChanged);
|
&NonModalMessageDialog::InputChanged);
|
||||||
layout->addWidget(_inputEdit);
|
layout->addWidget(_inputEdit);
|
||||||
|
|||||||
@@ -21,6 +21,24 @@ static std::vector<std::function<void()>> &getPluginCleanupSteps()
|
|||||||
return steps;
|
return steps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::function<void()>> &getResetIntervalSteps()
|
||||||
|
{
|
||||||
|
static std::vector<std::function<void()>> steps;
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::function<void()>> &getStartSteps()
|
||||||
|
{
|
||||||
|
static std::vector<std::function<void()>> steps;
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::function<void()>> &getStopSteps()
|
||||||
|
{
|
||||||
|
static std::vector<std::function<void()>> steps;
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
static std::mutex mutex;
|
static std::mutex mutex;
|
||||||
|
|
||||||
void SavePluginSettings(obs_data_t *obj)
|
void SavePluginSettings(obs_data_t *obj)
|
||||||
@@ -48,9 +66,10 @@ void AddPostLoadStep(std::function<void()> step)
|
|||||||
GetSwitcher()->AddPostLoadStep(step);
|
GetSwitcher()->AddPostLoadStep(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddIntervalResetStep(std::function<void()> step, bool lock)
|
void AddIntervalResetStep(std::function<void()> step)
|
||||||
{
|
{
|
||||||
GetSwitcher()->AddIntervalResetStep(step, lock);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
getResetIntervalSteps().emplace_back(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunPostLoadSteps()
|
void RunPostLoadSteps()
|
||||||
@@ -100,6 +119,42 @@ void RunPluginCleanupSteps()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RunIntervalResetSteps()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
for (const auto &step : getResetIntervalSteps()) {
|
||||||
|
step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddStartStep(std::function<void()> step)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
getStartSteps().emplace_back(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddStopStep(std::function<void()> step)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
getStopSteps().emplace_back(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunStartSteps()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
for (const auto &step : getStartSteps()) {
|
||||||
|
step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunStopSteps()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
for (const auto &step : getStopSteps()) {
|
||||||
|
step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void StopPlugin()
|
void StopPlugin()
|
||||||
{
|
{
|
||||||
GetSwitcher()->Stop();
|
GetSwitcher()->Stop();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ EXPORT void LoadPluginSettings(obs_data_t *);
|
|||||||
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);
|
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);
|
||||||
EXPORT void AddLoadStep(std::function<void(obs_data_t *)>);
|
EXPORT void AddLoadStep(std::function<void(obs_data_t *)>);
|
||||||
EXPORT void AddPostLoadStep(std::function<void()>);
|
EXPORT void AddPostLoadStep(std::function<void()>);
|
||||||
EXPORT void AddIntervalResetStep(std::function<void()>, bool lock = true);
|
EXPORT void AddIntervalResetStep(std::function<void()>);
|
||||||
EXPORT void RunPostLoadSteps();
|
EXPORT void RunPostLoadSteps();
|
||||||
|
|
||||||
EXPORT void AddPluginInitStep(std::function<void()>);
|
EXPORT void AddPluginInitStep(std::function<void()>);
|
||||||
@@ -25,6 +25,11 @@ EXPORT void StopPlugin();
|
|||||||
EXPORT void StartPlugin();
|
EXPORT void StartPlugin();
|
||||||
EXPORT bool PluginIsRunning();
|
EXPORT bool PluginIsRunning();
|
||||||
EXPORT int GetIntervalValue();
|
EXPORT int GetIntervalValue();
|
||||||
|
void AddStartStep(std::function<void()>);
|
||||||
|
void AddStopStep(std::function<void()>);
|
||||||
|
void RunStartSteps();
|
||||||
|
void RunStopSteps();
|
||||||
|
void RunIntervalResetSteps();
|
||||||
|
|
||||||
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
|
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
|
||||||
EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior);
|
EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior);
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ void Section::Collapse(bool collapse)
|
|||||||
_toggleButton->setChecked(collapse);
|
_toggleButton->setChecked(collapse);
|
||||||
_toggleButton->setArrowType(collapse ? Qt::ArrowType::RightArrow
|
_toggleButton->setArrowType(collapse ? Qt::ArrowType::RightArrow
|
||||||
: Qt::ArrowType::DownArrow);
|
: Qt::ArrowType::DownArrow);
|
||||||
|
if (!_toggleAnimation) {
|
||||||
|
_collapsed = collapse;
|
||||||
|
emit Collapsed(collapse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_toggleAnimation->setDirection(collapse ? QAbstractAnimation::Backward
|
_toggleAnimation->setDirection(collapse ? QAbstractAnimation::Backward
|
||||||
: QAbstractAnimation::Forward);
|
: QAbstractAnimation::Forward);
|
||||||
_transitioning = true;
|
_transitioning = true;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@@ -28,25 +27,6 @@ bool ReplaceAll(std::string &str, const std::string &from,
|
|||||||
return somethingWasReplaced;
|
return somethingWasReplaced;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> GetJsonField(const std::string &jsonStr,
|
|
||||||
const std::string &fieldToExtract)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
nlohmann::json json = nlohmann::json::parse(jsonStr);
|
|
||||||
auto it = json.find(fieldToExtract);
|
|
||||||
if (it == json.end()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (it->is_string()) {
|
|
||||||
return it->get<std::string>();
|
|
||||||
}
|
|
||||||
return it->dump();
|
|
||||||
} catch (const nlohmann::json::exception &) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CompareIgnoringLineEnding(QString &s1, QString &s2)
|
bool CompareIgnoringLineEnding(QString &s1, QString &s2)
|
||||||
{
|
{
|
||||||
// Let QT deal with different types of lineendings
|
// Let QT deal with different types of lineendings
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ EXPORT std::pair<int, int> GetCursorPos();
|
|||||||
|
|
||||||
bool ReplaceAll(std::string &str, const std::string &from,
|
bool ReplaceAll(std::string &str, const std::string &from,
|
||||||
const std::string &to);
|
const std::string &to);
|
||||||
EXPORT std::optional<std::string> GetJsonField(const std::string &json,
|
|
||||||
const std::string &id);
|
|
||||||
EXPORT bool CompareIgnoringLineEnding(QString &s1, QString &s2);
|
EXPORT bool CompareIgnoringLineEnding(QString &s1, QString &s2);
|
||||||
std::string ToString(double value);
|
std::string ToString(double value);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#include "websocket-api.hpp"
|
#include "websocket-api.hpp"
|
||||||
#include "log-helper.hpp"
|
|
||||||
#include "obs-websocket-api.h"
|
#include "obs-websocket-api.h"
|
||||||
#include "plugin-state-helpers.hpp"
|
#include "plugin-state-helpers.hpp"
|
||||||
|
|
||||||
|
// Must be after "obs-websocket-api.h" to avoid logging function conflict
|
||||||
|
#include "log-helper.hpp"
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
@@ -23,6 +25,14 @@ static bool setupDone = setup();
|
|||||||
bool setup()
|
bool setup()
|
||||||
{
|
{
|
||||||
AddPluginPostLoadStep(registerWebsocketVendor);
|
AddPluginPostLoadStep(registerWebsocketVendor);
|
||||||
|
AddStartStep([]() {
|
||||||
|
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
|
||||||
|
nullptr);
|
||||||
|
});
|
||||||
|
AddStopStep([]() {
|
||||||
|
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
|
||||||
|
nullptr);
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +119,9 @@ void RegisterWebsocketRequest(
|
|||||||
|
|
||||||
void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data)
|
void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data)
|
||||||
{
|
{
|
||||||
|
if (OBSIsShuttingDown()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data);
|
obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ void StringVariable::operator=(const char *value)
|
|||||||
void StringVariable::Load(obs_data_t *obj, const char *name)
|
void StringVariable::Load(obs_data_t *obj, const char *name)
|
||||||
{
|
{
|
||||||
_value = obs_data_get_string(obj, name);
|
_value = obs_data_get_string(obj, name);
|
||||||
|
_lastResolve = {};
|
||||||
Resolve();
|
Resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ install_advss_plugin_dependency(...)
|
|||||||
... to install the plugin and its dependencies.
|
... to install the plugin and its dependencies.
|
||||||
#]]
|
#]]
|
||||||
|
|
||||||
|
add_plugin(http)
|
||||||
add_plugin(midi)
|
add_plugin(midi)
|
||||||
add_plugin(openvr)
|
add_plugin(openvr)
|
||||||
add_plugin(stream-deck)
|
add_plugin(stream-deck)
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ target_sources(
|
|||||||
macro-action-filter.hpp
|
macro-action-filter.hpp
|
||||||
macro-action-hotkey.cpp
|
macro-action-hotkey.cpp
|
||||||
macro-action-hotkey.hpp
|
macro-action-hotkey.hpp
|
||||||
macro-action-http.cpp
|
macro-action-http-legacy.cpp
|
||||||
macro-action-http.hpp
|
macro-action-http-legacy.hpp
|
||||||
macro-action-log.cpp
|
macro-action-log.cpp
|
||||||
macro-action-log.hpp
|
macro-action-log.hpp
|
||||||
macro-action-media.cpp
|
macro-action-media.cpp
|
||||||
@@ -152,8 +152,6 @@ target_sources(
|
|||||||
utils/filter-selection.hpp
|
utils/filter-selection.hpp
|
||||||
utils/hotkey-helpers.cpp
|
utils/hotkey-helpers.cpp
|
||||||
utils/hotkey-helpers.hpp
|
utils/hotkey-helpers.hpp
|
||||||
utils/json-helpers.cpp
|
|
||||||
utils/json-helpers.hpp
|
|
||||||
utils/monitor-helpers.cpp
|
utils/monitor-helpers.cpp
|
||||||
utils/monitor-helpers.hpp
|
utils/monitor-helpers.hpp
|
||||||
utils/osc-helpers.cpp
|
utils/osc-helpers.cpp
|
||||||
|
|||||||
@@ -449,8 +449,11 @@ void MacroActionFilterEdit::SetWidgetVisibility()
|
|||||||
_entryData->_action == MacroActionFilter::Action::SETTINGS &&
|
_entryData->_action == MacroActionFilter::Action::SETTINGS &&
|
||||||
_entryData->_settingsInputMethod ==
|
_entryData->_settingsInputMethod ==
|
||||||
MacroActionFilter::SettingsInputMethod::JSON_STRING);
|
MacroActionFilter::SettingsInputMethod::JSON_STRING);
|
||||||
_getSettings->setVisible(_entryData->_action ==
|
_getSettings->setVisible(
|
||||||
MacroActionFilter::Action::SETTINGS);
|
_entryData->_action == MacroActionFilter::Action::SETTINGS &&
|
||||||
|
_entryData->_settingsInputMethod !=
|
||||||
|
MacroActionFilter::SettingsInputMethod::
|
||||||
|
INDIVIDUAL_TEMPVAR);
|
||||||
_tempVars->setVisible(_entryData->_action ==
|
_tempVars->setVisible(_entryData->_action ==
|
||||||
MacroActionFilter::Action::SETTINGS &&
|
MacroActionFilter::Action::SETTINGS &&
|
||||||
_entryData->_settingsInputMethod ==
|
_entryData->_settingsInputMethod ==
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "macro-action-http.hpp"
|
#include "macro-action-http-legacy.hpp"
|
||||||
#include "curl-helper.hpp"
|
#include "curl-helper.hpp"
|
||||||
#include "layout-helpers.hpp"
|
#include "layout-helpers.hpp"
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ const std::string MacroActionHttp::id = "http";
|
|||||||
bool MacroActionHttp::_registered = MacroActionFactory::Register(
|
bool MacroActionHttp::_registered = MacroActionFactory::Register(
|
||||||
MacroActionHttp::id,
|
MacroActionHttp::id,
|
||||||
{MacroActionHttp::Create, MacroActionHttpEdit::Create,
|
{MacroActionHttp::Create, MacroActionHttpEdit::Create,
|
||||||
"AdvSceneSwitcher.action.http"});
|
"AdvSceneSwitcher.action.http.legacy", true});
|
||||||
|
|
||||||
const static std::map<MacroActionHttp::Method, std::string> methods = {
|
const static std::map<MacroActionHttp::Method, std::string> methods = {
|
||||||
{MacroActionHttp::Method::GET, "AdvSceneSwitcher.action.http.type.get"},
|
{MacroActionHttp::Method::GET, "AdvSceneSwitcher.action.http.type.get"},
|
||||||
@@ -34,11 +34,11 @@ const static std::map<PluginStateAction, std::string> actionTypes = {
|
|||||||
|
|
||||||
const static std::map<NoMatchBehavior, std::string> noMatchValues = {
|
const static std::map<NoMatchBehavior, std::string> noMatchValues = {
|
||||||
{NoMatchBehavior::NO_SWITCH,
|
{NoMatchBehavior::NO_SWITCH,
|
||||||
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch"},
|
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch"},
|
||||||
{NoMatchBehavior::SWITCH,
|
{NoMatchBehavior::SWITCH,
|
||||||
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo"},
|
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo"},
|
||||||
{NoMatchBehavior::RANDOM_SWITCH,
|
{NoMatchBehavior::RANDOM_SWITCH,
|
||||||
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom"},
|
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void stopPlugin()
|
static void stopPlugin()
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include "source-helpers.hpp"
|
#include "source-helpers.hpp"
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
@@ -28,8 +30,34 @@ const static std::map<MacroActionProjector::Type, std::string> selectionTypes =
|
|||||||
"AdvSceneSwitcher.action.projector.type.multiview"},
|
"AdvSceneSwitcher.action.projector.type.multiview"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void closeOBSProjectorWindows(const std::string expectedWindowTitle,
|
||||||
|
const RegexConfig ®ex)
|
||||||
|
{
|
||||||
|
for (QWindow *widget : QApplication::allWindows()) {
|
||||||
|
if (!widget->property("isOBSProjectorWindow").toBool()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const windowTitle = widget->title().toStdString();
|
||||||
|
if (!regex.Enabled() && expectedWindowTitle != windowTitle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!regex.Matches(windowTitle, expectedWindowTitle)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool MacroActionProjector::PerformAction()
|
bool MacroActionProjector::PerformAction()
|
||||||
{
|
{
|
||||||
|
if (_action == Action::CLOSE) {
|
||||||
|
closeOBSProjectorWindows(_projectorWindowName, _regex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
const char *type = "";
|
const char *type = "";
|
||||||
|
|
||||||
@@ -78,10 +106,16 @@ bool MacroActionProjector::PerformAction()
|
|||||||
|
|
||||||
void MacroActionProjector::LogAction() const
|
void MacroActionProjector::LogAction() const
|
||||||
{
|
{
|
||||||
|
if (_action == Action::CLOSE) {
|
||||||
|
ablog(LOG_INFO, "closing projector window \"%s\"",
|
||||||
|
_projectorWindowName.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto it = selectionTypes.find(_type);
|
auto it = selectionTypes.find(_type);
|
||||||
if (it != selectionTypes.end()) {
|
if (it != selectionTypes.end()) {
|
||||||
ablog(LOG_INFO,
|
ablog(LOG_INFO,
|
||||||
"performed projector action \"%s\" with"
|
"open projector \"%s\" with"
|
||||||
"source \"%s\","
|
"source \"%s\","
|
||||||
"scene \"%s\","
|
"scene \"%s\","
|
||||||
"monitor %d",
|
"monitor %d",
|
||||||
@@ -96,24 +130,30 @@ void MacroActionProjector::LogAction() const
|
|||||||
bool MacroActionProjector::Save(obs_data_t *obj) const
|
bool MacroActionProjector::Save(obs_data_t *obj) const
|
||||||
{
|
{
|
||||||
MacroAction::Save(obj);
|
MacroAction::Save(obj);
|
||||||
|
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||||
obs_data_set_int(obj, "monitor", _monitor);
|
obs_data_set_int(obj, "monitor", _monitor);
|
||||||
obs_data_set_string(obj, "monitorName", _monitorName.c_str());
|
obs_data_set_string(obj, "monitorName", _monitorName.c_str());
|
||||||
obs_data_set_bool(obj, "fullscreen", _fullscreen);
|
obs_data_set_bool(obj, "fullscreen", _fullscreen);
|
||||||
_scene.Save(obj);
|
_scene.Save(obj);
|
||||||
_source.Save(obj);
|
_source.Save(obj);
|
||||||
|
_projectorWindowName.Save(obj, "projectorWindowName");
|
||||||
|
_regex.Save(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroActionProjector::Load(obs_data_t *obj)
|
bool MacroActionProjector::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroAction::Load(obj);
|
MacroAction::Load(obj);
|
||||||
|
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||||
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||||
_monitor = obs_data_get_int(obj, "monitor");
|
_monitor = obs_data_get_int(obj, "monitor");
|
||||||
_monitorName = obs_data_get_string(obj, "monitorName");
|
_monitorName = obs_data_get_string(obj, "monitorName");
|
||||||
_fullscreen = obs_data_get_bool(obj, "fullscreen");
|
_fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_source.Load(obj);
|
_source.Load(obj);
|
||||||
|
_projectorWindowName.Load(obj, "projectorWindowName");
|
||||||
|
_regex.Load(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +171,7 @@ void MacroActionProjector::ResolveVariablesToFixedValues()
|
|||||||
{
|
{
|
||||||
_source.ResolveVariables();
|
_source.ResolveVariables();
|
||||||
_scene.ResolveVariables();
|
_scene.ResolveVariables();
|
||||||
|
_projectorWindowName.ResolveVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionProjector::SetMonitor(int idx)
|
void MacroActionProjector::SetMonitor(int idx)
|
||||||
@@ -166,14 +207,22 @@ bool MacroActionProjector::MonitorSetupChanged() const
|
|||||||
QString::fromStdString(_monitorName);
|
QString::fromStdString(_monitorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void populateSelectionTypes(QComboBox *list)
|
static void populateActionSelection(QComboBox *list)
|
||||||
{
|
{
|
||||||
for (auto entry : selectionTypes) {
|
list->addItem(obs_module_text(
|
||||||
list->addItem(obs_module_text(entry.second.c_str()));
|
"AdvSceneSwitcher.action.projector.action.open"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.projector.action.close"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void populateSelectionTypes(QComboBox *list)
|
||||||
|
{
|
||||||
|
for (const auto &[_, name] : selectionTypes) {
|
||||||
|
list->addItem(obs_module_text(name.c_str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void populateWindowTypes(QComboBox *list)
|
static void populateWindowTypes(QComboBox *list)
|
||||||
{
|
{
|
||||||
list->addItem(
|
list->addItem(
|
||||||
obs_module_text("AdvSceneSwitcher.action.projector.windowed"));
|
obs_module_text("AdvSceneSwitcher.action.projector.windowed"));
|
||||||
@@ -184,14 +233,18 @@ static inline void populateWindowTypes(QComboBox *list)
|
|||||||
MacroActionProjectorEdit::MacroActionProjectorEdit(
|
MacroActionProjectorEdit::MacroActionProjectorEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroActionProjector> entryData)
|
QWidget *parent, std::shared_ptr<MacroActionProjector> entryData)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
_windowTypes(new QComboBox()),
|
_actions(new QComboBox()),
|
||||||
_types(new QComboBox()),
|
_types(new QComboBox()),
|
||||||
|
_windowTypes(new QComboBox()),
|
||||||
_scenes(new SceneSelectionWidget(window(), true, false, true, true,
|
_scenes(new SceneSelectionWidget(window(), true, false, true, true,
|
||||||
true)),
|
true)),
|
||||||
_sources(new SourceSelectionWidget(window(), QStringList(), true)),
|
_sources(new SourceSelectionWidget(window(), QStringList(), true)),
|
||||||
_monitorSelection(new QHBoxLayout()),
|
_monitors(new QComboBox()),
|
||||||
_monitors(new QComboBox())
|
_projectorWindowName(new VariableLineEdit(this)),
|
||||||
|
_regex(new RegexConfigWidget(this)),
|
||||||
|
_layout(new QHBoxLayout(this))
|
||||||
{
|
{
|
||||||
|
populateActionSelection(_actions);
|
||||||
populateWindowTypes(_windowTypes);
|
populateWindowTypes(_windowTypes);
|
||||||
populateSelectionTypes(_types);
|
populateSelectionTypes(_types);
|
||||||
auto sources = GetSourceNames();
|
auto sources = GetSourceNames();
|
||||||
@@ -201,6 +254,8 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
|
|||||||
_monitors->setPlaceholderText(
|
_monitors->setPlaceholderText(
|
||||||
obs_module_text("AdvSceneSwitcher.selectDisplay"));
|
obs_module_text("AdvSceneSwitcher.selectDisplay"));
|
||||||
|
|
||||||
|
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(ActionChanged(int)));
|
||||||
QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(WindowTypeChanged(int)));
|
SLOT(WindowTypeChanged(int)));
|
||||||
QWidget::connect(_types, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_types, SIGNAL(currentIndexChanged(int)), this,
|
||||||
@@ -212,24 +267,15 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
|
|||||||
SLOT(SourceChanged(const SourceSelection &)));
|
SLOT(SourceChanged(const SourceSelection &)));
|
||||||
QWidget::connect(_monitors, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_monitors, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(MonitorChanged(int)));
|
SLOT(MonitorChanged(int)));
|
||||||
|
QWidget::connect(_projectorWindowName, SIGNAL(editingFinished()), this,
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
SLOT(ProjectorWindowNameChanged()));
|
||||||
{"{{windowTypes}}", _windowTypes}, {"{{types}}", _types},
|
QWidget::connect(_regex,
|
||||||
{"{{scenes}}", _scenes}, {"{{sources}}", _sources},
|
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||||
{"{{monitors}}", _monitors},
|
SLOT(RegexChanged(const RegexConfig &)));
|
||||||
};
|
|
||||||
|
|
||||||
PlaceWidgets(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.action.projector.entry.monitor"),
|
|
||||||
_monitorSelection, widgetPlaceholders);
|
|
||||||
|
|
||||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
|
||||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.projector.entry"),
|
|
||||||
mainLayout, widgetPlaceholders);
|
|
||||||
mainLayout->insertLayout(mainLayout->count() - 1, _monitorSelection);
|
|
||||||
setLayout(mainLayout);
|
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
|
SetWidgetLayout();
|
||||||
|
setLayout(_layout);
|
||||||
UpdateEntryData();
|
UpdateEntryData();
|
||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
@@ -239,11 +285,15 @@ void MacroActionProjectorEdit::UpdateEntryData()
|
|||||||
if (!_entryData) {
|
if (!_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||||
_windowTypes->setCurrentIndex(_entryData->_fullscreen ? 1 : 0);
|
_windowTypes->setCurrentIndex(_entryData->_fullscreen ? 1 : 0);
|
||||||
_types->setCurrentIndex(static_cast<int>(_entryData->_type));
|
_types->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||||
_scenes->SetScene(_entryData->_scene);
|
_scenes->SetScene(_entryData->_scene);
|
||||||
_sources->SetSource(_entryData->_source);
|
_sources->SetSource(_entryData->_source);
|
||||||
_monitors->setCurrentIndex(_entryData->GetMonitor());
|
_monitors->setCurrentIndex(_entryData->GetMonitor());
|
||||||
|
_projectorWindowName->setText(_entryData->_projectorWindowName);
|
||||||
|
_regex->SetRegexConfig(_entryData->_regex);
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,12 +315,26 @@ void MacroActionProjectorEdit::MonitorChanged(int value)
|
|||||||
_entryData->SetMonitor(value);
|
_entryData->SetMonitor(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionProjectorEdit::ProjectorWindowNameChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_projectorWindowName =
|
||||||
|
_projectorWindowName->text().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionProjectorEdit::RegexChanged(const RegexConfig ®ex)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_regex = regex;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroActionProjectorEdit::WindowTypeChanged(int)
|
void MacroActionProjectorEdit::WindowTypeChanged(int)
|
||||||
{
|
{
|
||||||
GUARD_LOADING_AND_LOCK();
|
GUARD_LOADING_AND_LOCK();
|
||||||
_entryData->_fullscreen =
|
_entryData->_fullscreen =
|
||||||
_windowTypes->currentText() ==
|
_windowTypes->currentText() ==
|
||||||
obs_module_text("AdvSceneSwitcher.action.projector.fullscreen");
|
obs_module_text("AdvSceneSwitcher.action.projector.fullscreen");
|
||||||
|
SetWidgetLayout();
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,17 +345,69 @@ void MacroActionProjectorEdit::TypeChanged(int value)
|
|||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionProjectorEdit::ActionChanged(int idx)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_action = static_cast<MacroActionProjector::Action>(idx);
|
||||||
|
SetWidgetLayout();
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionProjectorEdit::SetWidgetLayout()
|
||||||
|
{
|
||||||
|
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{actions}}", _actions},
|
||||||
|
{"{{windowTypes}}", _windowTypes},
|
||||||
|
{"{{types}}", _types},
|
||||||
|
{"{{scenes}}", _scenes},
|
||||||
|
{"{{sources}}", _sources},
|
||||||
|
{"{{monitors}}", _monitors},
|
||||||
|
{"{{projectorWindowName}}", _projectorWindowName},
|
||||||
|
{"{{regex}}", _regex},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &[_, widget] : widgetPlaceholders) {
|
||||||
|
_layout->removeWidget(widget);
|
||||||
|
}
|
||||||
|
ClearLayout(_layout);
|
||||||
|
|
||||||
|
const char *layoutText;
|
||||||
|
if (_entryData->_action == MacroActionProjector::Action::CLOSE) {
|
||||||
|
layoutText = "AdvSceneSwitcher.action.projector.entry.close";
|
||||||
|
} else if (_entryData->_fullscreen) {
|
||||||
|
layoutText =
|
||||||
|
"AdvSceneSwitcher.action.projector.entry.open.fullscreen";
|
||||||
|
} else {
|
||||||
|
layoutText =
|
||||||
|
"AdvSceneSwitcher.action.projector.entry.open.windowed";
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaceWidgets(obs_module_text(layoutText), _layout, widgetPlaceholders);
|
||||||
|
}
|
||||||
|
|
||||||
void MacroActionProjectorEdit::SetWidgetVisibility()
|
void MacroActionProjectorEdit::SetWidgetVisibility()
|
||||||
{
|
{
|
||||||
if (!_entryData) {
|
if (!_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_scenes->setVisible(_entryData->_type ==
|
_projectorWindowName->setVisible(_entryData->_action ==
|
||||||
MacroActionProjector::Type::SCENE);
|
MacroActionProjector::Action::CLOSE);
|
||||||
_sources->setVisible(_entryData->_type ==
|
_regex->setVisible(_entryData->_action ==
|
||||||
MacroActionProjector::Type::SOURCE);
|
MacroActionProjector::Action::CLOSE);
|
||||||
SetLayoutVisible(_monitorSelection, _entryData->_fullscreen);
|
_types->setVisible(_entryData->_action ==
|
||||||
|
MacroActionProjector::Action::OPEN);
|
||||||
|
_windowTypes->setVisible(_entryData->_action ==
|
||||||
|
MacroActionProjector::Action::OPEN);
|
||||||
|
_scenes->setVisible(
|
||||||
|
_entryData->_action == MacroActionProjector::Action::OPEN &&
|
||||||
|
_entryData->_type == MacroActionProjector::Type::SCENE);
|
||||||
|
_sources->setVisible(
|
||||||
|
_entryData->_action == MacroActionProjector::Action::OPEN &&
|
||||||
|
_entryData->_type == MacroActionProjector::Type::SOURCE);
|
||||||
|
_monitors->setVisible(_entryData->_action ==
|
||||||
|
MacroActionProjector::Action::OPEN &&
|
||||||
|
_entryData->_fullscreen);
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "macro-action-edit.hpp"
|
#include "macro-action-edit.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
#include "scene-selection.hpp"
|
#include "scene-selection.hpp"
|
||||||
#include "source-selection.hpp"
|
#include "source-selection.hpp"
|
||||||
|
#include "variable-line-edit.hpp"
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
@@ -19,6 +21,11 @@ public:
|
|||||||
void SetMonitor(int);
|
void SetMonitor(int);
|
||||||
int GetMonitor() const;
|
int GetMonitor() const;
|
||||||
|
|
||||||
|
enum class Action {
|
||||||
|
OPEN,
|
||||||
|
CLOSE,
|
||||||
|
};
|
||||||
|
|
||||||
enum class Type {
|
enum class Type {
|
||||||
SOURCE,
|
SOURCE,
|
||||||
SCENE,
|
SCENE,
|
||||||
@@ -27,10 +34,13 @@ public:
|
|||||||
MULTIVIEW,
|
MULTIVIEW,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Action _action = Action::OPEN;
|
||||||
Type _type = Type::SCENE;
|
Type _type = Type::SCENE;
|
||||||
SourceSelection _source;
|
SourceSelection _source;
|
||||||
SceneSelection _scene;
|
SceneSelection _scene;
|
||||||
bool _fullscreen = true;
|
bool _fullscreen = true;
|
||||||
|
StringVariable _projectorWindowName = "Windowed Projector";
|
||||||
|
RegexConfig _regex = RegexConfig::PartialMatchRegexConfig(true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool MonitorSetupChanged() const;
|
bool MonitorSetupChanged() const;
|
||||||
@@ -60,21 +70,28 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void ActionChanged(int value);
|
||||||
void WindowTypeChanged(int value);
|
void WindowTypeChanged(int value);
|
||||||
void TypeChanged(int value);
|
void TypeChanged(int value);
|
||||||
void SceneChanged(const SceneSelection &);
|
void SceneChanged(const SceneSelection &);
|
||||||
void SourceChanged(const SourceSelection &);
|
void SourceChanged(const SourceSelection &);
|
||||||
void MonitorChanged(int value);
|
void MonitorChanged(int value);
|
||||||
|
void ProjectorWindowNameChanged();
|
||||||
|
void RegexChanged(const RegexConfig &);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetWidgetLayout();
|
||||||
void SetWidgetVisibility();
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
QComboBox *_windowTypes;
|
QComboBox *_actions;
|
||||||
QComboBox *_types;
|
QComboBox *_types;
|
||||||
|
QComboBox *_windowTypes;
|
||||||
SceneSelectionWidget *_scenes;
|
SceneSelectionWidget *_scenes;
|
||||||
SourceSelectionWidget *_sources;
|
SourceSelectionWidget *_sources;
|
||||||
QHBoxLayout *_monitorSelection;
|
|
||||||
QComboBox *_monitors;
|
QComboBox *_monitors;
|
||||||
|
VariableLineEdit *_projectorWindowName;
|
||||||
|
RegexConfigWidget *_regex;
|
||||||
|
QHBoxLayout *_layout;
|
||||||
|
|
||||||
std::shared_ptr<MacroActionProjector> _entryData;
|
std::shared_ptr<MacroActionProjector> _entryData;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const static std::map<obs_deinterlace_mode, std::string> deinterlaceModes = {
|
|||||||
{OBS_DEINTERLACE_MODE_DISABLE,
|
{OBS_DEINTERLACE_MODE_DISABLE,
|
||||||
"AdvSceneSwitcher.action.source.deinterlaceMode.disable"},
|
"AdvSceneSwitcher.action.source.deinterlaceMode.disable"},
|
||||||
{OBS_DEINTERLACE_MODE_DISCARD,
|
{OBS_DEINTERLACE_MODE_DISCARD,
|
||||||
"AdvSceneSwitcher.action.source.deinterlaceMode.disable"},
|
"AdvSceneSwitcher.action.source.deinterlaceMode.discard"},
|
||||||
{OBS_DEINTERLACE_MODE_RETRO,
|
{OBS_DEINTERLACE_MODE_RETRO,
|
||||||
"AdvSceneSwitcher.action.source.deinterlaceMode.retro"},
|
"AdvSceneSwitcher.action.source.deinterlaceMode.retro"},
|
||||||
{OBS_DEINTERLACE_MODE_BLEND,
|
{OBS_DEINTERLACE_MODE_BLEND,
|
||||||
@@ -526,8 +526,11 @@ void MacroActionSourceEdit::SetWidgetVisibility()
|
|||||||
_entryData->_action == MacroActionSource::Action::SETTINGS &&
|
_entryData->_action == MacroActionSource::Action::SETTINGS &&
|
||||||
_entryData->_settingsInputMethod ==
|
_entryData->_settingsInputMethod ==
|
||||||
MacroActionSource::SettingsInputMethod::JSON_STRING);
|
MacroActionSource::SettingsInputMethod::JSON_STRING);
|
||||||
_getSettings->setVisible(_entryData->_action ==
|
_getSettings->setVisible(
|
||||||
MacroActionSource::Action::SETTINGS);
|
_entryData->_action == MacroActionSource::Action::SETTINGS &&
|
||||||
|
_entryData->_settingsInputMethod !=
|
||||||
|
MacroActionSource::SettingsInputMethod::
|
||||||
|
INDIVIDUAL_TEMPVAR);
|
||||||
_tempVars->setVisible(_entryData->_action ==
|
_tempVars->setVisible(_entryData->_action ==
|
||||||
MacroActionSource::Action::SETTINGS &&
|
MacroActionSource::Action::SETTINGS &&
|
||||||
_entryData->_settingsInputMethod ==
|
_entryData->_settingsInputMethod ==
|
||||||
|
|||||||
@@ -442,6 +442,13 @@ void MacroConditionFileEdit::SetWidgetVisibility()
|
|||||||
_entryData->_onlyMatchIfChanged &&
|
_entryData->_onlyMatchIfChanged &&
|
||||||
_entryData->GetCondition() ==
|
_entryData->GetCondition() ==
|
||||||
MacroConditionFile::Condition::MATCH);
|
MacroConditionFile::Condition::MATCH);
|
||||||
|
|
||||||
|
// TODO: Remove remote file support in future version in favor of HTTP
|
||||||
|
// action.
|
||||||
|
// Hide the option for now, if it is not used already.
|
||||||
|
_fileTypes->setVisible(_entryData->_fileType ==
|
||||||
|
MacroConditionFile::FileType::REMOTE);
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ public:
|
|||||||
|
|
||||||
StringVariable _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
StringVariable _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||||
StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText");
|
StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||||
FileType _fileType = FileType::LOCAL;
|
|
||||||
RegexConfig _regex;
|
RegexConfig _regex;
|
||||||
|
|
||||||
// TODO: Remove in future version
|
// TODO: Remove in future version
|
||||||
bool _useTime = false;
|
bool _useTime = false;
|
||||||
bool _onlyMatchIfChanged = false;
|
bool _onlyMatchIfChanged = false;
|
||||||
|
FileType _fileType = FileType::LOCAL;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool MatchFileContent(QString &filedata);
|
bool MatchFileContent(QString &filedata);
|
||||||
|
|||||||
@@ -94,11 +94,15 @@ bool MacroConditionScene::CheckCondition()
|
|||||||
SetVariableValue(GetWeakSourceName(GetCurrentScene()));
|
SetVariableValue(GetWeakSourceName(GetCurrentScene()));
|
||||||
SetTempVarValue("current",
|
SetTempVarValue("current",
|
||||||
GetWeakSourceName(GetCurrentScene()));
|
GetWeakSourceName(GetCurrentScene()));
|
||||||
|
SetTempVarValue("previous",
|
||||||
|
GetWeakSourceName(GetPreviousScene()));
|
||||||
return sceneChanged;
|
return sceneChanged;
|
||||||
case Type::NOT_CHANGED:
|
case Type::NOT_CHANGED:
|
||||||
SetVariableValue(GetWeakSourceName(GetCurrentScene()));
|
SetVariableValue(GetWeakSourceName(GetCurrentScene()));
|
||||||
SetTempVarValue("current",
|
SetTempVarValue("current",
|
||||||
GetWeakSourceName(GetCurrentScene()));
|
GetWeakSourceName(GetCurrentScene()));
|
||||||
|
SetTempVarValue("previous",
|
||||||
|
GetWeakSourceName(GetPreviousScene()));
|
||||||
return !sceneChanged;
|
return !sceneChanged;
|
||||||
case Type::CURRENT_PATTERN: {
|
case Type::CURRENT_PATTERN: {
|
||||||
auto scene = getCurrentSceneHelper(_useTransitionTargetScene);
|
auto scene = getCurrentSceneHelper(_useTransitionTargetScene);
|
||||||
@@ -212,8 +216,6 @@ void MacroConditionScene::SetupTempVars()
|
|||||||
MacroCondition::SetupTempVars();
|
MacroCondition::SetupTempVars();
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case Type::CURRENT:
|
case Type::CURRENT:
|
||||||
case Type::CHANGED:
|
|
||||||
case Type::NOT_CHANGED:
|
|
||||||
case Type::CURRENT_PATTERN:
|
case Type::CURRENT_PATTERN:
|
||||||
AddTempvar("current",
|
AddTempvar("current",
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
@@ -231,6 +233,15 @@ void MacroConditionScene::SetupTempVars()
|
|||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.tempVar.scene.preview"));
|
"AdvSceneSwitcher.tempVar.scene.preview"));
|
||||||
break;
|
break;
|
||||||
|
case Type::CHANGED:
|
||||||
|
case Type::NOT_CHANGED:
|
||||||
|
AddTempvar("current",
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.scene.current"));
|
||||||
|
AddTempvar("previous",
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.scene.previous"));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
#include "json-helpers.hpp"
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
|
||||||
|
|
||||||
namespace advss {
|
|
||||||
|
|
||||||
QString FormatJsonString(std::string s)
|
|
||||||
{
|
|
||||||
return FormatJsonString(QString::fromStdString(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FormatJsonString(QString json)
|
|
||||||
{
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
|
|
||||||
if (doc.isNull()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return QString::fromUtf8(doc.toJson(QJsonDocument::Indented));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MatchJson(const std::string &json1, const std::string &json2,
|
|
||||||
const RegexConfig ®ex)
|
|
||||||
{
|
|
||||||
auto j1 = FormatJsonString(json1).toStdString();
|
|
||||||
auto j2 = FormatJsonString(json2).toStdString();
|
|
||||||
|
|
||||||
if (j1.empty()) {
|
|
||||||
j1 = json1;
|
|
||||||
}
|
|
||||||
if (j2.empty()) {
|
|
||||||
j2 = json2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regex.Enabled()) {
|
|
||||||
return regex.Matches(j1, j2);
|
|
||||||
}
|
|
||||||
return j1 == j2;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace advss
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <QString>
|
|
||||||
#include <string>
|
|
||||||
#include <regex-config.hpp>
|
|
||||||
|
|
||||||
namespace advss {
|
|
||||||
|
|
||||||
QString FormatJsonString(std::string);
|
|
||||||
QString FormatJsonString(QString);
|
|
||||||
bool MatchJson(const std::string &json1, const std::string &json2,
|
|
||||||
const RegexConfig ®ex);
|
|
||||||
|
|
||||||
} // namespace advss
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "source-setting.hpp"
|
#include "source-setting.hpp"
|
||||||
|
#include "json-helpers.hpp"
|
||||||
#include "obs-module-helper.hpp"
|
#include "obs-module-helper.hpp"
|
||||||
#include "math-helpers.hpp"
|
#include "math-helpers.hpp"
|
||||||
#include "utility.hpp"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#include "transform-setting.hpp"
|
#include "transform-setting.hpp"
|
||||||
|
#include "json-helpers.hpp"
|
||||||
#include "obs-module-helper.hpp"
|
#include "obs-module-helper.hpp"
|
||||||
#include "math-helpers.hpp"
|
#include "math-helpers.hpp"
|
||||||
#include "scene-item-transform-helpers.hpp"
|
#include "scene-item-transform-helpers.hpp"
|
||||||
#include "utility.hpp"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "websocket-helpers.hpp"
|
#include "websocket-helpers.hpp"
|
||||||
#include "connection-manager.hpp"
|
#include "connection-manager.hpp"
|
||||||
#include "log-helper.hpp"
|
|
||||||
#include "plugin-state-helpers.hpp"
|
#include "plugin-state-helpers.hpp"
|
||||||
#include "sync-helpers.hpp"
|
#include "sync-helpers.hpp"
|
||||||
#include "websocket-api.hpp"
|
#include "websocket-api.hpp"
|
||||||
@@ -8,6 +7,9 @@
|
|||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <obs-websocket-api.h>
|
#include <obs-websocket-api.h>
|
||||||
|
|
||||||
|
// Must be after "obs-websocket-api.h" to avoid logging function conflict
|
||||||
|
#include "log-helper.hpp"
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
using websocketpp::lib::placeholders::_1;
|
using websocketpp::lib::placeholders::_1;
|
||||||
|
|||||||
77
plugins/http/CMakeLists.txt
Normal file
77
plugins/http/CMakeLists.txt
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(advanced-scene-switcher-http)
|
||||||
|
|
||||||
|
# --- Check requirements ---
|
||||||
|
|
||||||
|
get_target_property(ADVSS_SOURCE_DIR advanced-scene-switcher-lib SOURCE_DIR)
|
||||||
|
set(CPP_HTTPLIB_DIR "${ADVSS_SOURCE_DIR}/deps/cpp-httplib")
|
||||||
|
if(NOT EXISTS "${CPP_HTTPLIB_DIR}/CMakeLists.txt")
|
||||||
|
message(WARNING "cpp-httplib directory \"${CPP_HTTPLIB_DIR}\" not found!\n"
|
||||||
|
"HTTP support will be disabled!")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
if(NOT TARGET httplib)
|
||||||
|
add_subdirectory("${CPP_HTTPLIB_DIR}" "${CPP_HTTPLIB_DIR}/build"
|
||||||
|
EXCLUDE_FROM_ALL)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT OPENSSL_INCLUDE_DIR OR NOT OPENSSL_LIBRARIES)
|
||||||
|
find_package(OpenSSL)
|
||||||
|
if(NOT OPENSSL_FOUND)
|
||||||
|
message(WARNING "OpenSSL not found!\n" "HTTP support will be disabled!\n\n")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(ZLIB)
|
||||||
|
if(NOT ZLIB_FOUND)
|
||||||
|
message(WARNING "zlib not found!\n" "HTTP support will be disabled!\n\n")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- End of section ---
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} MODULE)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT=1)
|
||||||
|
if(OS_MACOS)
|
||||||
|
target_compile_definitions(
|
||||||
|
${PROJECT_NAME} PRIVATE CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN=1)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework CoreFoundation")
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework Security")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_sources(
|
||||||
|
${PROJECT_NAME} PRIVATE macro-action-http.cpp macro-action-http.hpp
|
||||||
|
key-value-list.cpp key-value-list.hpp)
|
||||||
|
|
||||||
|
setup_advss_plugin(${PROJECT_NAME})
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE "${CPP_HTTPLIB_DIR}/"
|
||||||
|
"${OPENSSL_INCLUDE_DIR}")
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENSSL_LIBRARIES} ZLIB::ZLIB)
|
||||||
|
install_advss_plugin(${PROJECT_NAME})
|
||||||
|
if(OS_WINDOWS)
|
||||||
|
# Couldn't really find a better way to install runtime dependencies for
|
||||||
|
# Windows TODO: Clean this up at some point
|
||||||
|
function(FIND_FILES_WITH_PATTERN result pattern dir)
|
||||||
|
execute_process(
|
||||||
|
COMMAND
|
||||||
|
powershell -Command
|
||||||
|
"Get-ChildItem -Path '${dir}' -Recurse -Include ${pattern} |"
|
||||||
|
"Select-Object -First 1 |"
|
||||||
|
"ForEach-Object { $_.FullName -replace '\\\\', '\\\\' }"
|
||||||
|
OUTPUT_VARIABLE files
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
set(${result}
|
||||||
|
${files}
|
||||||
|
PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(OPENSSL_DLL_SEARCH_DIR "${OPENSSL_INCLUDE_DIR}/..")
|
||||||
|
find_files_with_pattern(CRYPTO_DLL_FILES "libcrypto*.dll"
|
||||||
|
"${OPENSSL_DLL_SEARCH_DIR}")
|
||||||
|
find_files_with_pattern(SSL_DLL_FILES "libssl*.dll"
|
||||||
|
"${OPENSSL_DLL_SEARCH_DIR}")
|
||||||
|
install_advss_plugin_dependency(TARGET ${PROJECT_NAME} DEPENDENCIES
|
||||||
|
"${CRYPTO_DLL_FILES}" "${SSL_DLL_FILES}")
|
||||||
|
endif()
|
||||||
213
plugins/http/key-value-list.cpp
Normal file
213
plugins/http/key-value-list.cpp
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
#include "key-value-list.hpp"
|
||||||
|
#include "name-dialog.hpp"
|
||||||
|
#include "ui-helpers.hpp"
|
||||||
|
|
||||||
|
#include <QLayout>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
KeyValueListEdit::KeyValueListEdit(QWidget *parent, const QString &addKeyString,
|
||||||
|
const QString &addKeyStringDescription,
|
||||||
|
const QString &addValueString,
|
||||||
|
const QString &addValueStringDescription)
|
||||||
|
: ListEditor(parent),
|
||||||
|
_addKeyString(addKeyString),
|
||||||
|
_addKeyStringDescription(addKeyStringDescription),
|
||||||
|
_addValueString(addValueString),
|
||||||
|
_addValueStringDescription(addValueStringDescription)
|
||||||
|
{
|
||||||
|
_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
_list->setAutoScroll(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyValueListEdit::SetStringList(const StringList &list)
|
||||||
|
{
|
||||||
|
_stringList = list;
|
||||||
|
_list->clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < list.size(); i += 2) {
|
||||||
|
AppendListEntryWidget(
|
||||||
|
list.at(i), i + 1 >= list.size() ? "" : list.at(i + 1));
|
||||||
|
}
|
||||||
|
UpdateListSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyValueListEdit::Add()
|
||||||
|
{
|
||||||
|
StringVariable key;
|
||||||
|
StringVariable value;
|
||||||
|
bool accepted = AskForKeyValue(key, value);
|
||||||
|
if (!accepted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendListEntryWidget(key, value);
|
||||||
|
_stringList << key << value;
|
||||||
|
|
||||||
|
// Delay resizing to make sure the list viewport was already updated
|
||||||
|
QTimer::singleShot(0, this, [this]() { UpdateListSize(); });
|
||||||
|
|
||||||
|
StringListChanged(_stringList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyValueListEdit::Remove()
|
||||||
|
{
|
||||||
|
int idx = _list->currentRow();
|
||||||
|
if (idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_stringList.removeAt(idx);
|
||||||
|
|
||||||
|
QListWidgetItem *item = _list->currentItem();
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete item;
|
||||||
|
|
||||||
|
// Delay resizing to make sure the list viewport was already updated
|
||||||
|
QTimer::singleShot(0, this, [this]() { UpdateListSize(); });
|
||||||
|
|
||||||
|
StringListChanged(_stringList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyValueListEdit::Up()
|
||||||
|
{
|
||||||
|
int idx = _list->currentRow();
|
||||||
|
if (idx <= 0 || idx >= _list->count()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveStringListIdxUp(idx);
|
||||||
|
|
||||||
|
auto row = _list->itemWidget(_list->currentItem());
|
||||||
|
auto newItem = _list->currentItem()->clone();
|
||||||
|
|
||||||
|
_list->insertItem(idx - 1, newItem);
|
||||||
|
_list->setItemWidget(newItem, row);
|
||||||
|
|
||||||
|
_list->takeItem(idx + 1);
|
||||||
|
_list->setCurrentRow(idx - 1);
|
||||||
|
|
||||||
|
UpdateListSize();
|
||||||
|
|
||||||
|
StringListChanged(_stringList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyValueListEdit::Down()
|
||||||
|
{
|
||||||
|
int idx = _list->currentRow();
|
||||||
|
if (idx == -1 || idx == _list->count() - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveStringListIdxUp(idx + 1);
|
||||||
|
|
||||||
|
auto row = _list->itemWidget(_list->currentItem());
|
||||||
|
auto newItem = _list->currentItem()->clone();
|
||||||
|
|
||||||
|
_list->insertItem(idx + 2, newItem);
|
||||||
|
_list->setItemWidget(newItem, row);
|
||||||
|
|
||||||
|
_list->takeItem(idx);
|
||||||
|
_list->setCurrentRow(idx + 1);
|
||||||
|
|
||||||
|
UpdateListSize();
|
||||||
|
|
||||||
|
StringListChanged(_stringList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyValueListEdit::Clicked(QListWidgetItem *item)
|
||||||
|
{
|
||||||
|
int idx = _list->currentRow();
|
||||||
|
|
||||||
|
StringVariable key = _stringList[idx * 2];
|
||||||
|
StringVariable value = _stringList[idx * 2 + 1];
|
||||||
|
bool accepted = AskForKeyValue(key, value);
|
||||||
|
if (!accepted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto container = static_cast<KeyValueListContainerWidget *>(
|
||||||
|
_list->itemWidget(item));
|
||||||
|
container->_key->setText(QString::fromStdString(key.UnresolvedValue()));
|
||||||
|
container->_value->setText(
|
||||||
|
QString::fromStdString(value.UnresolvedValue()));
|
||||||
|
container->adjustSize();
|
||||||
|
container->updateGeometry();
|
||||||
|
|
||||||
|
_stringList[idx * 2] = key;
|
||||||
|
_stringList[idx * 2 + 1] = value;
|
||||||
|
|
||||||
|
// Delay resizing to make sure the list viewport was already updated
|
||||||
|
QTimer::singleShot(0, this, [this]() { UpdateListSize(); });
|
||||||
|
StringListChanged(_stringList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyValueListEdit::MoveStringListIdxUp(int idx)
|
||||||
|
{
|
||||||
|
if (idx <= 0 || idx >= _list->count()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stringList.move(idx * 2, idx * 2 - 2);
|
||||||
|
_stringList.move(idx * 2 + 1, idx * 2 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KeyValueListEdit::AskForKeyValue(StringVariable &keyVariable,
|
||||||
|
StringVariable &valueVariable)
|
||||||
|
{
|
||||||
|
std::string key;
|
||||||
|
bool accepted = NameDialog::AskForName(
|
||||||
|
this, _addKeyString, _addKeyStringDescription, key,
|
||||||
|
QString::fromStdString(keyVariable.UnresolvedValue()), 4096,
|
||||||
|
false);
|
||||||
|
if (!accepted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string value;
|
||||||
|
accepted = NameDialog::AskForName(
|
||||||
|
this, _addValueString, _addValueStringDescription, value,
|
||||||
|
QString::fromStdString(valueVariable.UnresolvedValue()), 4096,
|
||||||
|
false);
|
||||||
|
if (!accepted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyVariable = key;
|
||||||
|
valueVariable = value;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyValueListEdit::AppendListEntryWidget(const StringVariable &key,
|
||||||
|
const StringVariable &value)
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(_list);
|
||||||
|
auto container = new KeyValueListContainerWidget(this, _list->count());
|
||||||
|
container->_key->setText(QString::fromStdString(key.UnresolvedValue()));
|
||||||
|
container->_value->setText(
|
||||||
|
QString::fromStdString(value.UnresolvedValue()));
|
||||||
|
container->adjustSize();
|
||||||
|
container->updateGeometry();
|
||||||
|
_list->addItem(item);
|
||||||
|
_list->setItemWidget(item, container);
|
||||||
|
UpdateListSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyValueListContainerWidget::KeyValueListContainerWidget(QWidget *parent,
|
||||||
|
int index)
|
||||||
|
: QWidget(parent),
|
||||||
|
_key(new QLabel("Key", this)),
|
||||||
|
_value(new QLabel("Value", this)),
|
||||||
|
_index(index)
|
||||||
|
{
|
||||||
|
auto layout = new QHBoxLayout();
|
||||||
|
layout->addWidget(_key);
|
||||||
|
layout->addWidget(_value);
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
53
plugins/http/key-value-list.hpp
Normal file
53
plugins/http/key-value-list.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "string-list.hpp"
|
||||||
|
#include "variable-line-edit.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class KeyValueListEdit final : public ListEditor {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
KeyValueListEdit(QWidget *parent, const QString &addKeyString,
|
||||||
|
const QString &addKeyStringDescription,
|
||||||
|
const QString &addValueString,
|
||||||
|
const QString &addValueStringDescription);
|
||||||
|
void SetStringList(const StringList &);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void Add();
|
||||||
|
void Remove();
|
||||||
|
void Up();
|
||||||
|
void Down();
|
||||||
|
void Clicked(QListWidgetItem *);
|
||||||
|
signals:
|
||||||
|
void StringListChanged(const StringList &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void MoveStringListIdxUp(int);
|
||||||
|
bool AskForKeyValue(StringVariable &key, StringVariable &value);
|
||||||
|
void AppendListEntryWidget(const StringVariable &key,
|
||||||
|
const StringVariable &value);
|
||||||
|
|
||||||
|
StringList _stringList;
|
||||||
|
QString _addKeyString;
|
||||||
|
QString _addKeyStringDescription;
|
||||||
|
QString _addValueString;
|
||||||
|
QString _addValueStringDescription;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyValueListContainerWidget final : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
KeyValueListContainerWidget(QWidget *parent, int index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *_key;
|
||||||
|
QLabel *_value;
|
||||||
|
|
||||||
|
int _index = -1;
|
||||||
|
friend class KeyValueListEdit;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
482
plugins/http/macro-action-http.cpp
Normal file
482
plugins/http/macro-action-http.cpp
Normal file
@@ -0,0 +1,482 @@
|
|||||||
|
#include "macro-action-http.hpp"
|
||||||
|
#include "layout-helpers.hpp"
|
||||||
|
|
||||||
|
#include <httplib.h>
|
||||||
|
|
||||||
|
#undef DELETE
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
const std::string MacroActionHttp::id = "http_v2";
|
||||||
|
|
||||||
|
bool MacroActionHttp::_registered = MacroActionFactory::Register(
|
||||||
|
MacroActionHttp::id,
|
||||||
|
{MacroActionHttp::Create, MacroActionHttpEdit::Create,
|
||||||
|
"AdvSceneSwitcher.action.http"});
|
||||||
|
|
||||||
|
static httplib::Headers getHeaders(const StringList &strings)
|
||||||
|
{
|
||||||
|
httplib::Headers headers;
|
||||||
|
for (int i = 0; i < strings.size(); i = i + 2) {
|
||||||
|
const auto pair =
|
||||||
|
i + 1 >= strings.size()
|
||||||
|
? std::make_pair(std::string(strings.at(i)), "")
|
||||||
|
: std::make_pair(
|
||||||
|
std::string(strings.at(i)),
|
||||||
|
std::string(strings.at(i + 1)));
|
||||||
|
headers.emplace(pair);
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static httplib::Params getParams(const StringList &strings)
|
||||||
|
{
|
||||||
|
httplib::Params params;
|
||||||
|
for (int i = 0; i < strings.size(); i = i + 2) {
|
||||||
|
const auto pair =
|
||||||
|
i + 1 >= strings.size()
|
||||||
|
? std::make_pair(std::string(strings.at(i)), "")
|
||||||
|
: std::make_pair(
|
||||||
|
std::string(strings.at(i)),
|
||||||
|
std::string(strings.at(i + 1)));
|
||||||
|
params.emplace(pair);
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setTimeout(httplib::Client &client, const Duration &timeout)
|
||||||
|
{
|
||||||
|
const time_t seconds = timeout.Seconds();
|
||||||
|
const time_t usecs = timeout.Milliseconds() - (seconds * 1000);
|
||||||
|
client.set_read_timeout(seconds, usecs);
|
||||||
|
client.set_write_timeout(seconds, usecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttp::SetupTempVars()
|
||||||
|
{
|
||||||
|
MacroAction::SetupTempVars();
|
||||||
|
AddTempvar("status",
|
||||||
|
obs_module_text("AdvSceneSwitcher.tempVar.http.status"));
|
||||||
|
AddTempvar("body",
|
||||||
|
obs_module_text("AdvSceneSwitcher.tempVar.http.body"));
|
||||||
|
AddTempvar("error",
|
||||||
|
obs_module_text("AdvSceneSwitcher.tempVar.http.error"),
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.http.error.description"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionHttp::PerformAction()
|
||||||
|
{
|
||||||
|
httplib::Client cli(_url);
|
||||||
|
setTimeout(cli, _timeout);
|
||||||
|
const auto params = _setParams ? getParams(_params) : httplib::Params();
|
||||||
|
const auto headers = _setHeaders ? getHeaders(_headers)
|
||||||
|
: httplib::Headers();
|
||||||
|
|
||||||
|
// This copy is necessary for some reason for MacOS but not Linux or
|
||||||
|
// Windows.
|
||||||
|
// Without it all requests fail with "Invalid argument" or
|
||||||
|
// "Failed to write connection" when sending out the data.
|
||||||
|
std::string path = _path;
|
||||||
|
|
||||||
|
httplib::Result response;
|
||||||
|
switch (_method) {
|
||||||
|
case MacroActionHttp::Method::GET:
|
||||||
|
response = cli.Get(path, params, headers);
|
||||||
|
break;
|
||||||
|
case MacroActionHttp::Method::POST: {
|
||||||
|
const auto pathWithParam =
|
||||||
|
httplib::append_query_params(path, params);
|
||||||
|
response =
|
||||||
|
cli.Post(pathWithParam, headers, _body, _contentType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MacroActionHttp::Method::PUT: {
|
||||||
|
const auto pathWithParam =
|
||||||
|
httplib::append_query_params(path, params);
|
||||||
|
response = cli.Put(pathWithParam, headers, _body, _contentType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MacroActionHttp::Method::PATCH: {
|
||||||
|
const auto pathWithParam =
|
||||||
|
httplib::append_query_params(path, params);
|
||||||
|
response =
|
||||||
|
cli.Patch(pathWithParam, headers, _body, _contentType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MacroActionHttp::Method::DELETE: {
|
||||||
|
const auto pathWithParam =
|
||||||
|
httplib::append_query_params(path, params);
|
||||||
|
response =
|
||||||
|
cli.Delete(pathWithParam, headers, _body, _contentType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VerboseLoggingEnabled() && !response) {
|
||||||
|
blog(LOG_INFO, "HTTP action error: %s",
|
||||||
|
httplib::to_string(response.error()).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTempVarValue("status",
|
||||||
|
response ? std::to_string(response->status) : "");
|
||||||
|
SetTempVarValue("body", response ? response->body : "");
|
||||||
|
SetTempVarValue("error",
|
||||||
|
response ? "" : httplib::to_string(response.error()));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::string_view methodToString(MacroActionHttp::Method method)
|
||||||
|
{
|
||||||
|
switch (method) {
|
||||||
|
case MacroActionHttp::Method::GET:
|
||||||
|
return "GET";
|
||||||
|
case MacroActionHttp::Method::POST:
|
||||||
|
return "POST";
|
||||||
|
case MacroActionHttp::Method::PUT:
|
||||||
|
return "PUT";
|
||||||
|
case MacroActionHttp::Method::PATCH:
|
||||||
|
return "PATCH";
|
||||||
|
case MacroActionHttp::Method::DELETE:
|
||||||
|
return "DELETE";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string stringListToString(const StringList &list)
|
||||||
|
{
|
||||||
|
if (list.empty()) {
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
std::string result = "[";
|
||||||
|
for (const auto &string : list) {
|
||||||
|
result += std::string(string) + ", ";
|
||||||
|
}
|
||||||
|
result.pop_back();
|
||||||
|
result.pop_back();
|
||||||
|
return result + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttp::LogAction() const
|
||||||
|
{
|
||||||
|
ablog(LOG_INFO,
|
||||||
|
"sent HTTP request (%s) "
|
||||||
|
"to URL \"%s\" "
|
||||||
|
"to path \"%s\" "
|
||||||
|
"with content type \"%s\" "
|
||||||
|
"with body \"%s\" "
|
||||||
|
"with headers \"%s\" "
|
||||||
|
"with parameters \"%s\" "
|
||||||
|
"with timeout \"%s\"",
|
||||||
|
methodToString(_method).data(), _url.c_str(), _path.c_str(),
|
||||||
|
_contentType.c_str(), _body.c_str(),
|
||||||
|
_setHeaders ? stringListToString(_headers).c_str() : "-",
|
||||||
|
_setParams ? stringListToString(_params).c_str() : "-",
|
||||||
|
_timeout.ToString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionHttp::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
MacroAction::Save(obj);
|
||||||
|
_url.Save(obj, "url");
|
||||||
|
_path.Save(obj, "path");
|
||||||
|
_contentType.Save(obj, "contentType");
|
||||||
|
_body.Save(obj, "body");
|
||||||
|
obs_data_set_bool(obj, "setHeaders", _setHeaders);
|
||||||
|
_headers.Save(obj, "headers", "header");
|
||||||
|
obs_data_set_bool(obj, "setParams", _setParams);
|
||||||
|
_params.Save(obj, "params", "param");
|
||||||
|
obs_data_set_int(obj, "method", static_cast<int>(_method));
|
||||||
|
_timeout.Save(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionHttp::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
MacroAction::Load(obj);
|
||||||
|
_url.Load(obj, "url");
|
||||||
|
_path.Load(obj, "path");
|
||||||
|
_contentType.Load(obj, "contentType");
|
||||||
|
_body.Load(obj, "body");
|
||||||
|
_setHeaders = obs_data_get_bool(obj, "setHeaders");
|
||||||
|
_headers.Load(obj, "headers", "header");
|
||||||
|
_setParams = obs_data_get_bool(obj, "setParams");
|
||||||
|
_params.Load(obj, "params", "param");
|
||||||
|
_method = static_cast<Method>(obs_data_get_int(obj, "method"));
|
||||||
|
_timeout.Load(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MacroActionHttp::GetShortDesc() const
|
||||||
|
{
|
||||||
|
return _url.UnresolvedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroAction> MacroActionHttp::Create(Macro *m)
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionHttp>(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroAction> MacroActionHttp::Copy() const
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionHttp>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttp::ResolveVariablesToFixedValues()
|
||||||
|
{
|
||||||
|
_url.ResolveVariables();
|
||||||
|
_path.ResolveVariables();
|
||||||
|
_contentType.ResolveVariables();
|
||||||
|
_body.ResolveVariables();
|
||||||
|
_headers.ResolveVariables();
|
||||||
|
_params.ResolveVariables();
|
||||||
|
_timeout.ResolveVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void populateMethodSelection(QComboBox *list)
|
||||||
|
{
|
||||||
|
const static std::map<MacroActionHttp::Method, std::string> methods = {
|
||||||
|
{MacroActionHttp::Method::GET,
|
||||||
|
"AdvSceneSwitcher.action.http.type.get"},
|
||||||
|
{MacroActionHttp::Method::POST,
|
||||||
|
"AdvSceneSwitcher.action.http.type.post"},
|
||||||
|
{MacroActionHttp::Method::PUT,
|
||||||
|
"AdvSceneSwitcher.action.http.type.put"},
|
||||||
|
{MacroActionHttp::Method::PATCH,
|
||||||
|
"AdvSceneSwitcher.action.http.type.patch"},
|
||||||
|
{MacroActionHttp::Method::DELETE,
|
||||||
|
"AdvSceneSwitcher.action.http.type.delete"},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &[value, name] : methods) {
|
||||||
|
list->addItem(obs_module_text(name.c_str()),
|
||||||
|
static_cast<int>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroActionHttpEdit::MacroActionHttpEdit(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroActionHttp> entryData)
|
||||||
|
: QWidget(parent),
|
||||||
|
_url(new VariableLineEdit(this)),
|
||||||
|
_path(new VariableLineEdit(this)),
|
||||||
|
_contentType(new VariableLineEdit(this)),
|
||||||
|
_contentTypeLayout(new QHBoxLayout()),
|
||||||
|
_methods(new QComboBox()),
|
||||||
|
_body(new VariableTextEdit(this)),
|
||||||
|
_bodyLayout(new QVBoxLayout()),
|
||||||
|
_setHeaders(new QCheckBox(
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.setHeaders"))),
|
||||||
|
_headerList(new KeyValueListEdit(
|
||||||
|
this, obs_module_text("AdvSceneSwitcher.action.http.headers"),
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.addHeader.name"),
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.headers"),
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.http.addHeader.value"))),
|
||||||
|
_headerListLayout(new QVBoxLayout()),
|
||||||
|
_setParams(new QCheckBox(
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.setParams"))),
|
||||||
|
_paramList(new KeyValueListEdit(
|
||||||
|
this, obs_module_text("AdvSceneSwitcher.action.http.params"),
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.addParam.name"),
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.params"),
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.http.addParam.value"))),
|
||||||
|
_paramListLayout(new QVBoxLayout()),
|
||||||
|
_timeout(new DurationSelection(this, false))
|
||||||
|
{
|
||||||
|
populateMethodSelection(_methods);
|
||||||
|
|
||||||
|
SetWidgetSignalConnections();
|
||||||
|
SetWidgetLayout();
|
||||||
|
|
||||||
|
_entryData = entryData;
|
||||||
|
UpdateEntryData();
|
||||||
|
_loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::UpdateEntryData()
|
||||||
|
{
|
||||||
|
if (!_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_url->setText(_entryData->_url);
|
||||||
|
_path->setText(_entryData->_path);
|
||||||
|
_contentType->setText(_entryData->_contentType);
|
||||||
|
_body->setPlainText(_entryData->_body);
|
||||||
|
_setHeaders->setChecked(_entryData->_setHeaders);
|
||||||
|
_headerList->SetStringList(_entryData->_headers);
|
||||||
|
_setParams->setChecked(_entryData->_setParams);
|
||||||
|
_paramList->SetStringList(_entryData->_params);
|
||||||
|
_methods->setCurrentIndex(
|
||||||
|
_methods->findData(static_cast<int>(_entryData->_method)));
|
||||||
|
_timeout->SetDuration(_entryData->_timeout);
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::URLChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_url = _url->text().toStdString();
|
||||||
|
emit(HeaderInfoChanged(_url->text()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::PathChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_path = _path->text().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::ContentTypeChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_contentType = _contentType->text().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::BodyChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_body = _body->toPlainText().toUtf8().constData();
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::MethodChanged(int idx)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_method = static_cast<MacroActionHttp::Method>(
|
||||||
|
_methods->itemData(idx).toInt());
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::TimeoutChanged(const Duration &dur)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_timeout = dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::SetHeadersChanged(int value)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_setHeaders = value;
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::HeadersChanged(const StringList &headers)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_headers = headers;
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::SetParamsChanged(int value)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_setParams = value;
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::ParamsChanged(const StringList ¶ms)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_params = params;
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::SetWidgetSignalConnections()
|
||||||
|
{
|
||||||
|
QWidget::connect(_url, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(URLChanged()));
|
||||||
|
QWidget::connect(_path, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(PathChanged()));
|
||||||
|
QWidget::connect(_contentType, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(ContentTypeChanged()));
|
||||||
|
QWidget::connect(_body, SIGNAL(textChanged()), this,
|
||||||
|
SLOT(BodyChanged()));
|
||||||
|
QWidget::connect(_methods, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(MethodChanged(int)));
|
||||||
|
QWidget::connect(_setHeaders, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(SetHeadersChanged(int)));
|
||||||
|
QWidget::connect(_headerList,
|
||||||
|
SIGNAL(StringListChanged(const StringList &)), this,
|
||||||
|
SLOT(HeadersChanged(const StringList &)));
|
||||||
|
QWidget::connect(_setParams, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(SetParamsChanged(int)));
|
||||||
|
QWidget::connect(_paramList,
|
||||||
|
SIGNAL(StringListChanged(const StringList &)), this,
|
||||||
|
SLOT(ParamsChanged(const StringList &)));
|
||||||
|
QWidget::connect(_timeout, SIGNAL(DurationChanged(const Duration &)),
|
||||||
|
this, SLOT(TimeoutChanged(const Duration &)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::SetWidgetLayout()
|
||||||
|
{
|
||||||
|
const std::unordered_map<std::string, QWidget *> widgets = {
|
||||||
|
{"{{url}}", _url},
|
||||||
|
{"{{path}}", _path},
|
||||||
|
{"{{contentType}}", _contentType},
|
||||||
|
{"{{method}}", _methods},
|
||||||
|
{"{{body}}", _body},
|
||||||
|
{"{{timeout}}", _timeout},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto actionLayout = new QHBoxLayout;
|
||||||
|
PlaceWidgets(
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.layout.method"),
|
||||||
|
actionLayout, widgets);
|
||||||
|
|
||||||
|
PlaceWidgets(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.http.layout.contentType"),
|
||||||
|
_contentTypeLayout, widgets);
|
||||||
|
|
||||||
|
_bodyLayout->addWidget(new QLabel(
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.body")));
|
||||||
|
_bodyLayout->addWidget(_body);
|
||||||
|
|
||||||
|
auto timeoutLayout = new QHBoxLayout;
|
||||||
|
PlaceWidgets(
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.layout.timeout"),
|
||||||
|
timeoutLayout, widgets);
|
||||||
|
|
||||||
|
_headerListLayout->addWidget(new QLabel(
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.headers")));
|
||||||
|
_headerListLayout->addWidget(_headerList);
|
||||||
|
|
||||||
|
_paramListLayout->addWidget(new QLabel(
|
||||||
|
obs_module_text("AdvSceneSwitcher.action.http.params")));
|
||||||
|
_paramListLayout->addWidget(_paramList);
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
layout->addLayout(actionLayout);
|
||||||
|
layout->addWidget(_setHeaders);
|
||||||
|
layout->addLayout(_headerListLayout);
|
||||||
|
layout->addWidget(_setParams);
|
||||||
|
layout->addLayout(_paramListLayout);
|
||||||
|
layout->addLayout(_contentTypeLayout);
|
||||||
|
layout->addLayout(_bodyLayout);
|
||||||
|
layout->addLayout(timeoutLayout);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionHttpEdit::SetWidgetVisibility()
|
||||||
|
{
|
||||||
|
SetLayoutVisible(_headerListLayout, _entryData->_setHeaders);
|
||||||
|
SetLayoutVisible(_paramListLayout, _entryData->_setParams);
|
||||||
|
SetLayoutVisible(_contentTypeLayout,
|
||||||
|
_entryData->_method != MacroActionHttp::Method::GET);
|
||||||
|
SetLayoutVisible(_bodyLayout,
|
||||||
|
_entryData->_method != MacroActionHttp::Method::GET);
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
107
plugins/http/macro-action-http.hpp
Normal file
107
plugins/http/macro-action-http.hpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "macro-action-edit.hpp"
|
||||||
|
#include "key-value-list.hpp"
|
||||||
|
#include "variable-text-edit.hpp"
|
||||||
|
#include "variable-line-edit.hpp"
|
||||||
|
#include "duration-control.hpp"
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QCheckBox>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class MacroActionHttp final : public MacroAction {
|
||||||
|
public:
|
||||||
|
MacroActionHttp(Macro *m) : MacroAction(m, true) {}
|
||||||
|
bool PerformAction();
|
||||||
|
void LogAction() const;
|
||||||
|
bool Save(obs_data_t *obj) const;
|
||||||
|
bool Load(obs_data_t *obj);
|
||||||
|
std::string GetShortDesc() const;
|
||||||
|
std::string GetId() const { return id; };
|
||||||
|
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||||
|
std::shared_ptr<MacroAction> Copy() const;
|
||||||
|
void ResolveVariablesToFixedValues();
|
||||||
|
|
||||||
|
enum class Method {
|
||||||
|
GET = 0,
|
||||||
|
POST,
|
||||||
|
PUT,
|
||||||
|
PATCH,
|
||||||
|
DELETE,
|
||||||
|
};
|
||||||
|
|
||||||
|
StringVariable _url = "127.0.0.1:8080";
|
||||||
|
StringVariable _path = "/";
|
||||||
|
StringVariable _body = obs_module_text("AdvSceneSwitcher.enterText");
|
||||||
|
StringVariable _contentType = "application/json";
|
||||||
|
bool _setHeaders = false;
|
||||||
|
StringList _headers;
|
||||||
|
bool _setParams = false;
|
||||||
|
StringList _params;
|
||||||
|
Method _method = Method::GET;
|
||||||
|
Duration _timeout = Duration(1.0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetupTempVars();
|
||||||
|
|
||||||
|
static bool _registered;
|
||||||
|
static const std::string id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroActionHttpEdit final : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MacroActionHttpEdit(
|
||||||
|
QWidget *parent,
|
||||||
|
std::shared_ptr<MacroActionHttp> entryData = nullptr);
|
||||||
|
void UpdateEntryData();
|
||||||
|
static QWidget *Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroAction> action)
|
||||||
|
{
|
||||||
|
return new MacroActionHttpEdit(
|
||||||
|
parent,
|
||||||
|
std::dynamic_pointer_cast<MacroActionHttp>(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void URLChanged();
|
||||||
|
void PathChanged();
|
||||||
|
void BodyChanged();
|
||||||
|
void ContentTypeChanged();
|
||||||
|
void MethodChanged(int);
|
||||||
|
void TimeoutChanged(const Duration &seconds);
|
||||||
|
void SetHeadersChanged(int);
|
||||||
|
void HeadersChanged(const StringList &);
|
||||||
|
void SetParamsChanged(int);
|
||||||
|
void ParamsChanged(const StringList &);
|
||||||
|
signals:
|
||||||
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetWidgetSignalConnections();
|
||||||
|
void SetWidgetLayout();
|
||||||
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
|
VariableLineEdit *_url;
|
||||||
|
VariableLineEdit *_path;
|
||||||
|
VariableLineEdit *_contentType;
|
||||||
|
QHBoxLayout *_contentTypeLayout;
|
||||||
|
QComboBox *_methods;
|
||||||
|
VariableTextEdit *_body;
|
||||||
|
QVBoxLayout *_bodyLayout;
|
||||||
|
QCheckBox *_setHeaders;
|
||||||
|
KeyValueListEdit *_headerList;
|
||||||
|
QVBoxLayout *_headerListLayout;
|
||||||
|
QCheckBox *_setParams;
|
||||||
|
KeyValueListEdit *_paramList;
|
||||||
|
QVBoxLayout *_paramListLayout;
|
||||||
|
DurationSelection *_timeout;
|
||||||
|
|
||||||
|
std::shared_ptr<MacroActionHttp> _entryData;
|
||||||
|
bool _loading = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -19,9 +19,18 @@ add_library(${PROJECT_NAME} MODULE)
|
|||||||
|
|
||||||
if(OS_WINDOWS)
|
if(OS_WINDOWS)
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_compile_options(libremidi PRIVATE /wd4251 /wd4267 /wd4275 /wd4101
|
target_compile_options(
|
||||||
/wd4244 /wd4018)
|
libremidi
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE /wd4101 /wd4244 /wd4018)
|
PRIVATE /wd4018
|
||||||
|
/wd4189
|
||||||
|
/wd4251
|
||||||
|
/wd4267
|
||||||
|
/wd4275
|
||||||
|
/wd4389
|
||||||
|
/wd4505
|
||||||
|
/wd4702
|
||||||
|
PUBLIC /wd4068 /wd4100 /wd4101 /wd4244)
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE /wd4017)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
target_compile_options(
|
target_compile_options(
|
||||||
@@ -34,21 +43,6 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(OS_MACOS)
|
|
||||||
set(PATCH_FILE
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/remove-bit-cast-for-macos-build.patch")
|
|
||||||
execute_process(
|
|
||||||
COMMAND git apply ${PATCH_FILE}
|
|
||||||
WORKING_DIRECTORY ${LIBREMIDI_DIR}
|
|
||||||
RESULT_VARIABLE PATCH_RESULT)
|
|
||||||
if(PATCH_RESULT EQUAL 0)
|
|
||||||
message("Libremidi patch \"${PATCH_FILE}\" applied successfully!")
|
|
||||||
else()
|
|
||||||
message(
|
|
||||||
WARNING "Libremidi patch \"${PATCH_FILE}\" did NOT apply successfully!")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_sources(
|
target_sources(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
PRIVATE macro-condition-midi.cpp macro-condition-midi.hpp
|
PRIVATE macro-condition-midi.cpp macro-condition-midi.hpp
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
diff --git a/include/libremidi/backends/coremidi/helpers.hpp b/include/libremidi/backends/coremidi/helpers.hpp
|
|
||||||
index 08e62ff..ba35ec7 100644
|
|
||||||
--- a/include/libremidi/backends/coremidi/helpers.hpp
|
|
||||||
+++ b/include/libremidi/backends/coremidi/helpers.hpp
|
|
||||||
@@ -207,7 +207,8 @@ inline CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
|
|
||||||
inline MIDIObjectRef
|
|
||||||
locate_object(auto& self, const port_information& info, MIDIObjectType requested_type)
|
|
||||||
{
|
|
||||||
- auto uid = std::bit_cast<std::int32_t>((uint32_t)info.port);
|
|
||||||
+ std::int32_t uid;
|
|
||||||
+ std::memcpy(&uid, &(info.port), sizeof(uint32_t));
|
|
||||||
MIDIObjectRef object{};
|
|
||||||
MIDIObjectType type{};
|
|
||||||
auto ret = MIDIObjectFindByUniqueID(uid, &object, &type);
|
|
||||||
diff --git a/include/libremidi/backends/coremidi/observer.hpp b/include/libremidi/backends/coremidi/observer.hpp
|
|
||||||
index defe623..12a936a 100644
|
|
||||||
--- a/include/libremidi/backends/coremidi/observer.hpp
|
|
||||||
+++ b/include/libremidi/backends/coremidi/observer.hpp
|
|
||||||
@@ -78,13 +78,16 @@ public:
|
|
||||||
if (!ok)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
- return std::conditional_t<Input, input_port, output_port>{
|
|
||||||
- {.client = (std::uintptr_t)this->client,
|
|
||||||
- .port = std::bit_cast<uint32_t>(get_int_property(obj, kMIDIPropertyUniqueID)),
|
|
||||||
- .manufacturer = get_string_property(obj, kMIDIPropertyManufacturer),
|
|
||||||
- .device_name = get_string_property(obj, kMIDIPropertyModel),
|
|
||||||
- .port_name = get_string_property(obj, kMIDIPropertyName),
|
|
||||||
- .display_name = get_string_property(obj, kMIDIPropertyDisplayName)}};
|
|
||||||
+ std::conditional_t<Input, input_port, output_port> ret{
|
|
||||||
+ {.client = (std::uintptr_t)this->client,
|
|
||||||
+ .port = 0,
|
|
||||||
+ .manufacturer = get_string_property(obj, kMIDIPropertyManufacturer),
|
|
||||||
+ .device_name = get_string_property(obj, kMIDIPropertyModel),
|
|
||||||
+ .port_name = get_string_property(obj, kMIDIPropertyName),
|
|
||||||
+ .display_name = get_string_property(obj, kMIDIPropertyDisplayName)}};
|
|
||||||
+ auto prop = get_int_property(obj, kMIDIPropertyUniqueID);
|
|
||||||
+ std::memcpy(&ret.port, &(prop), sizeof(prop));
|
|
||||||
+ return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<libremidi::input_port> get_input_ports() const noexcept override
|
|
||||||
@@ -10,8 +10,10 @@ if(NOT EXISTS "${CPP_HTTPLIB_DIR}/CMakeLists.txt")
|
|||||||
"Twitch support will be disabled!")
|
"Twitch support will be disabled!")
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory("${CPP_HTTPLIB_DIR}" "${CPP_HTTPLIB_DIR}/build"
|
if(NOT TARGET httplib)
|
||||||
EXCLUDE_FROM_ALL)
|
add_subdirectory("${CPP_HTTPLIB_DIR}" "${CPP_HTTPLIB_DIR}/build"
|
||||||
|
EXCLUDE_FROM_ALL)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT OPENSSL_INCLUDE_DIR OR NOT OPENSSL_LIBRARIES)
|
if(NOT OPENSSL_INCLUDE_DIR OR NOT OPENSSL_LIBRARIES)
|
||||||
find_package(OpenSSL)
|
find_package(OpenSSL)
|
||||||
@@ -28,6 +30,31 @@ if(NOT ZLIB_FOUND)
|
|||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(DATE_LIB_DIR "${ADVSS_SOURCE_DIR}/deps/date")
|
||||||
|
if(EXISTS "${DATE_LIB_DIR}/CMakeLists.txt")
|
||||||
|
set(BUILD_TZ_LIB ON)
|
||||||
|
if(OS_WINDOWS)
|
||||||
|
if(CURL_FOUND AND TARGET CURL::libcurl)
|
||||||
|
get_target_property(CURL_INCLUDE_DIR CURL::libcurl
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
add_subdirectory("${DATE_LIB_DIR}" "${DATE_LIB_DIR}/build"
|
||||||
|
EXCLUDE_FROM_ALL)
|
||||||
|
target_include_directories(date-tz PRIVATE "${CURL_INCLUDE_DIR}")
|
||||||
|
set(VERIFY_TWITCH_TIMESTAMPS ON)
|
||||||
|
else()
|
||||||
|
message(WARNING "CURL not found - not verifying Twitch timestamps")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
add_subdirectory("${DATE_LIB_DIR}" "${DATE_LIB_DIR}/build" EXCLUDE_FROM_ALL)
|
||||||
|
target_compile_options(date-tz PUBLIC -Wno-error=conversion
|
||||||
|
-Wno-error=shadow)
|
||||||
|
set(VERIFY_TWITCH_TIMESTAMPS ON)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(WARNING "date lib not found in \"${DATE_LIB_DIR}\"!\n"
|
||||||
|
"Twitch timestamps will not be checked!")
|
||||||
|
endif()
|
||||||
|
|
||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} MODULE)
|
add_library(${PROJECT_NAME} MODULE)
|
||||||
@@ -74,6 +101,14 @@ set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
|||||||
target_include_directories(${PROJECT_NAME} PRIVATE "${CPP_HTTPLIB_DIR}/"
|
target_include_directories(${PROJECT_NAME} PRIVATE "${CPP_HTTPLIB_DIR}/"
|
||||||
"${OPENSSL_INCLUDE_DIR}")
|
"${OPENSSL_INCLUDE_DIR}")
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENSSL_LIBRARIES} ZLIB::ZLIB)
|
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENSSL_LIBRARIES} ZLIB::ZLIB)
|
||||||
|
if(DEFINED VERIFY_TWITCH_TIMESTAMPS)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE VERIFY_TIMESTAMPS=1)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE date::date-tz)
|
||||||
|
if(OS_WINDOWS)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
install_advss_plugin(${PROJECT_NAME})
|
install_advss_plugin(${PROJECT_NAME})
|
||||||
if(OS_WINDOWS)
|
if(OS_WINDOWS)
|
||||||
# Couldn't really find a better way to install runtime dependencies for
|
# Couldn't really find a better way to install runtime dependencies for
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ void TwitchChannel::Save(obs_data_t *obj) const
|
|||||||
std::string TwitchChannel::GetUserID(const TwitchToken &token) const
|
std::string TwitchChannel::GetUserID(const TwitchToken &token) const
|
||||||
{
|
{
|
||||||
static std::map<std::string, std::string> userIDCache;
|
static std::map<std::string, std::string> userIDCache;
|
||||||
|
if (!token.IsValid()) {
|
||||||
|
vblog(LOG_INFO, "%s() failed: token invalid", __func__);
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
auto it = userIDCache.find(std::string(_name));
|
auto it = userIDCache.find(std::string(_name));
|
||||||
if (it != userIDCache.end()) {
|
if (it != userIDCache.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
@@ -47,7 +52,7 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (res.status != 200) {
|
if (res.status != 200) {
|
||||||
blog(LOG_INFO, "failed to get Twitch user id from token! (%d)",
|
blog(LOG_INFO, "failed to get Twitch user id for channel! (%d)",
|
||||||
res.status);
|
res.status);
|
||||||
return "invalid";
|
return "invalid";
|
||||||
}
|
}
|
||||||
@@ -90,7 +95,7 @@ getStringArrayHelper(const OBSDataAutoRelease &data, const std::string &value)
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
auto json = nlohmann::json::parse(jsonStr);
|
auto json = nlohmann::json::parse(jsonStr);
|
||||||
auto array = json[value];
|
auto &array = json[value];
|
||||||
if (!array.is_array()) {
|
if (!array.is_array()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -104,7 +109,7 @@ getStringArrayHelper(const OBSDataAutoRelease &data, const std::string &value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ChannelLiveInfo>
|
std::optional<ChannelLiveInfo>
|
||||||
TwitchChannel::GetLiveInfo(const TwitchToken &token)
|
TwitchChannel::GetLiveInfo(const TwitchToken &token) const
|
||||||
{
|
{
|
||||||
auto id = GetUserID(token);
|
auto id = GetUserID(token);
|
||||||
if (!IsValid(id)) {
|
if (!IsValid(id)) {
|
||||||
@@ -146,7 +151,8 @@ TwitchChannel::GetLiveInfo(const TwitchToken &token)
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ChannelInfo> TwitchChannel::GetInfo(const TwitchToken &token)
|
std::optional<ChannelInfo>
|
||||||
|
TwitchChannel::GetInfo(const TwitchToken &token) const
|
||||||
{
|
{
|
||||||
auto id = GetUserID(token);
|
auto id = GetUserID(token);
|
||||||
if (!IsValid(id)) {
|
if (!IsValid(id)) {
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ struct TwitchChannel {
|
|||||||
StringVariable GetName() const { return _name; }
|
StringVariable GetName() const { return _name; }
|
||||||
std::string GetUserID(const TwitchToken &token) const;
|
std::string GetUserID(const TwitchToken &token) const;
|
||||||
bool IsValid(const std::string &id) const;
|
bool IsValid(const std::string &id) const;
|
||||||
std::optional<ChannelLiveInfo> GetLiveInfo(const TwitchToken &);
|
std::optional<ChannelLiveInfo> GetLiveInfo(const TwitchToken &) const;
|
||||||
std::optional<ChannelInfo> GetInfo(const TwitchToken &);
|
std::optional<ChannelInfo> GetInfo(const TwitchToken &) const;
|
||||||
void ResolveVariables();
|
void ResolveVariables();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
#include <log-helper.hpp>
|
#include <log-helper.hpp>
|
||||||
|
|
||||||
|
#ifdef VERIFY_TIMESTAMPS
|
||||||
|
#include "date/tz.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
using websocketpp::lib::placeholders::_1;
|
using websocketpp::lib::placeholders::_1;
|
||||||
@@ -123,6 +127,11 @@ void EventSub::ClearActiveSubscriptions()
|
|||||||
_activeSubscriptions.clear();
|
_activeSubscriptions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventSub::EnableTimestampValidation(bool enable)
|
||||||
|
{
|
||||||
|
_validateTimestamps = enable;
|
||||||
|
}
|
||||||
|
|
||||||
void EventSub::Disconnect()
|
void EventSub::Disconnect()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_connectMtx);
|
std::lock_guard<std::mutex> lock(_connectMtx);
|
||||||
@@ -187,7 +196,7 @@ static obs_data_t *copyData(const OBSData &data)
|
|||||||
return obs_data_create_from_json(json);
|
return obs_data_create_from_json(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EventSub::AddEventSubscribtion(std::shared_ptr<TwitchToken> token,
|
std::string EventSub::AddEventSubscription(std::shared_ptr<TwitchToken> token,
|
||||||
Subscription subscription)
|
Subscription subscription)
|
||||||
{
|
{
|
||||||
auto eventSub = token->GetEventSub();
|
auto eventSub = token->GetEventSub();
|
||||||
@@ -237,15 +246,43 @@ void EventSub::OnOpen(connection_hdl)
|
|||||||
|
|
||||||
static bool isValidTimestamp(const std::string ×tamp)
|
static bool isValidTimestamp(const std::string ×tamp)
|
||||||
{
|
{
|
||||||
std::tm tm = {};
|
#ifdef VERIFY_TIMESTAMPS
|
||||||
std::istringstream ss(timestamp);
|
// Example input: 2023-07-19T14:56:51.634234626Z
|
||||||
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S.%fZ");
|
try {
|
||||||
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
|
// Discard the nanosecond part
|
||||||
tp += std::chrono::hours(1); // UTC
|
static constexpr size_t dotPos = 19;
|
||||||
std::chrono::system_clock::time_point currentTime =
|
std::string trimmed = timestamp.substr(0, dotPos);
|
||||||
std::chrono::system_clock::now();
|
auto tzStart = timestamp.find_first_of("Z+-", dotPos);
|
||||||
auto diff = currentTime - tp;
|
trimmed = timestamp.substr(0, dotPos);
|
||||||
return diff <= std::chrono::minutes(10);
|
if (tzStart != std::string::npos) {
|
||||||
|
trimmed += timestamp.substr(tzStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istringstream in(trimmed);
|
||||||
|
date::sys_time<std::chrono::seconds> parsedTime;
|
||||||
|
in >> date::parse("%FT%TZ", parsedTime);
|
||||||
|
if (in.fail()) {
|
||||||
|
blog(LOG_WARNING, "failed to parse timestamp %s",
|
||||||
|
timestamp.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto now = date::zoned_time{date::current_zone(),
|
||||||
|
std::chrono::system_clock::now()}
|
||||||
|
.get_sys_time();
|
||||||
|
|
||||||
|
auto duration = now - parsedTime;
|
||||||
|
// Clocks might be off by a bit, so allow negative values also
|
||||||
|
return duration <= std::chrono::minutes(10) &&
|
||||||
|
duration >= std::chrono::minutes(-1);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
blog(LOG_WARNING, "%s: %s", __func__, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Just assume timestamps are always valid
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventSub::IsValidMessageID(const std::string &id)
|
bool EventSub::IsValidMessageID(const std::string &id)
|
||||||
@@ -286,9 +323,10 @@ void EventSub::OnMessage(connection_hdl, EventSubWSClient::message_ptr message)
|
|||||||
OBSDataAutoRelease metadata = obs_data_get_obj(json, "metadata");
|
OBSDataAutoRelease metadata = obs_data_get_obj(json, "metadata");
|
||||||
std::string timestamp =
|
std::string timestamp =
|
||||||
obs_data_get_string(metadata, "message_timestamp");
|
obs_data_get_string(metadata, "message_timestamp");
|
||||||
if (!isValidTimestamp(timestamp)) {
|
if (_validateTimestamps && !isValidTimestamp(timestamp)) {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING,
|
||||||
"Discarding Twitch EventSub with invalid timestamp");
|
"Discarding Twitch EventSub with invalid timestamp %s",
|
||||||
|
timestamp.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string id = obs_data_get_string(metadata, "message_id");
|
std::string id = obs_data_get_string(metadata, "message_id");
|
||||||
@@ -308,7 +346,7 @@ void EventSub::OnMessage(connection_hdl, EventSubWSClient::message_ptr message)
|
|||||||
} else if (messageType == "session_reconnect") {
|
} else if (messageType == "session_reconnect") {
|
||||||
HandleReconnect(payloadJson);
|
HandleReconnect(payloadJson);
|
||||||
} else if (messageType == "revocation") {
|
} else if (messageType == "revocation") {
|
||||||
HanldeRevocation(payloadJson);
|
HandleRevocation(payloadJson);
|
||||||
} else {
|
} else {
|
||||||
vblog(LOG_INFO, "ignoring message of unknown type '%s'",
|
vblog(LOG_INFO, "ignoring message of unknown type '%s'",
|
||||||
messageType.c_str());
|
messageType.c_str());
|
||||||
@@ -354,7 +392,7 @@ void EventSub::HandleReconnect(obs_data_t *data)
|
|||||||
"Twitch EventSub reconnecting", ec);
|
"Twitch EventSub reconnecting", ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventSub::HanldeRevocation(obs_data_t *data)
|
void EventSub::HandleRevocation(obs_data_t *data)
|
||||||
{
|
{
|
||||||
OBSDataAutoRelease subscription =
|
OBSDataAutoRelease subscription =
|
||||||
obs_data_get_obj(data, "subscription");
|
obs_data_get_obj(data, "subscription");
|
||||||
|
|||||||
@@ -55,9 +55,10 @@ public:
|
|||||||
void Disconnect();
|
void Disconnect();
|
||||||
[[nodiscard]] EventSubMessageBuffer RegisterForEvents();
|
[[nodiscard]] EventSubMessageBuffer RegisterForEvents();
|
||||||
bool SubscriptionIsActive(const std::string &id);
|
bool SubscriptionIsActive(const std::string &id);
|
||||||
static std::string AddEventSubscribtion(std::shared_ptr<TwitchToken>,
|
static std::string AddEventSubscription(std::shared_ptr<TwitchToken>,
|
||||||
Subscription);
|
Subscription);
|
||||||
void ClearActiveSubscriptions();
|
void ClearActiveSubscriptions();
|
||||||
|
void EnableTimestampValidation(bool enable);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnOpen(connection_hdl hdl);
|
void OnOpen(connection_hdl hdl);
|
||||||
@@ -74,7 +75,7 @@ private:
|
|||||||
void HandleKeepAlive() const;
|
void HandleKeepAlive() const;
|
||||||
void HandleNotification(obs_data_t *);
|
void HandleNotification(obs_data_t *);
|
||||||
void HandleReconnect(obs_data_t *);
|
void HandleReconnect(obs_data_t *);
|
||||||
void HanldeRevocation(obs_data_t *);
|
void HandleRevocation(obs_data_t *);
|
||||||
|
|
||||||
void RegisterInstance();
|
void RegisterInstance();
|
||||||
void UnregisterInstance();
|
void UnregisterInstance();
|
||||||
@@ -89,6 +90,7 @@ private:
|
|||||||
std::atomic_bool _disconnect{false};
|
std::atomic_bool _disconnect{false};
|
||||||
std::string _url;
|
std::string _url;
|
||||||
std::string _sessionID;
|
std::string _sessionID;
|
||||||
|
bool _validateTimestamps = true;
|
||||||
|
|
||||||
std::deque<std::string> _messageIDs;
|
std::deque<std::string> _messageIDs;
|
||||||
std::mutex _subscriptionMtx;
|
std::mutex _subscriptionMtx;
|
||||||
|
|||||||
@@ -826,7 +826,7 @@ MacroActionTwitchEdit::MacroActionTwitchEdit(
|
|||||||
_userInfoQueryType(new QComboBox(this)),
|
_userInfoQueryType(new QComboBox(this)),
|
||||||
_userLogin(new VariableLineEdit(this)),
|
_userLogin(new VariableLineEdit(this)),
|
||||||
_userId(new VariableSpinBox(this)),
|
_userId(new VariableSpinBox(this)),
|
||||||
_pointsReward(new TwitchPointsRewardWidget(this)),
|
_pointsReward(new TwitchPointsRewardWidget(this, false)),
|
||||||
_rewardVariable(new VariableSelection(this)),
|
_rewardVariable(new VariableSelection(this)),
|
||||||
_toggleRewardSelection(new QPushButton())
|
_toggleRewardSelection(new QPushButton())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1008,7 +1008,7 @@ waitForSubscription(const std::shared_ptr<TwitchToken> &token,
|
|||||||
const Subscription &subscription)
|
const Subscription &subscription)
|
||||||
{
|
{
|
||||||
return std::async(std::launch::async, [token, subscription]() {
|
return std::async(std::launch::async, [token, subscription]() {
|
||||||
auto id = EventSub::AddEventSubscribtion(token, subscription);
|
auto id = EventSub::AddEventSubscription(token, subscription);
|
||||||
return id;
|
return id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1450,7 +1450,7 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
|||||||
_tokens(new TwitchConnectionSelection()),
|
_tokens(new TwitchConnectionSelection()),
|
||||||
_tokenWarning(new QLabel()),
|
_tokenWarning(new QLabel()),
|
||||||
_channel(new TwitchChannelSelection(this)),
|
_channel(new TwitchChannelSelection(this)),
|
||||||
_pointsReward(new TwitchPointsRewardWidget(this)),
|
_pointsReward(new TwitchPointsRewardWidget(this, true)),
|
||||||
_streamTitle(new VariableLineEdit(this)),
|
_streamTitle(new VariableLineEdit(this)),
|
||||||
_regexTitle(new RegexConfigWidget(parent)),
|
_regexTitle(new RegexConfigWidget(parent)),
|
||||||
_chatMesageEdit(new ChatMessageEdit(this)),
|
_chatMesageEdit(new ChatMessageEdit(this)),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "points-reward-selection.hpp"
|
#include "points-reward-selection.hpp"
|
||||||
#include "twitch-helpers.hpp"
|
#include "twitch-helpers.hpp"
|
||||||
|
|
||||||
|
#include <log-helper.hpp>
|
||||||
#include <obs-module-helper.hpp>
|
#include <obs-module-helper.hpp>
|
||||||
#include <ui-helpers.hpp>
|
#include <ui-helpers.hpp>
|
||||||
|
|
||||||
@@ -135,9 +136,10 @@ void TwitchPointsRewardSelection::SelectionChanged(int index)
|
|||||||
emit PointsRewardChanged(pointsReward);
|
emit PointsRewardChanged(pointsReward);
|
||||||
}
|
}
|
||||||
|
|
||||||
TwitchPointsRewardWidget::TwitchPointsRewardWidget(QWidget *parent)
|
TwitchPointsRewardWidget::TwitchPointsRewardWidget(QWidget *parent,
|
||||||
|
bool allowAny)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
_selection(new TwitchPointsRewardSelection(this)),
|
_selection(new TwitchPointsRewardSelection(this, allowAny)),
|
||||||
_refreshButton(new QPushButton(this))
|
_refreshButton(new QPushButton(this))
|
||||||
{
|
{
|
||||||
_refreshButton->setMaximumWidth(22);
|
_refreshButton->setMaximumWidth(22);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class TwitchPointsRewardSelection : public FilterComboBox {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TwitchPointsRewardSelection(QWidget *parent, bool allowAny = true);
|
TwitchPointsRewardSelection(QWidget *parent, bool allowAny);
|
||||||
|
|
||||||
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
||||||
void SetChannel(const TwitchChannel &channel);
|
void SetChannel(const TwitchChannel &channel);
|
||||||
@@ -53,7 +53,7 @@ class TwitchPointsRewardWidget : public QWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TwitchPointsRewardWidget(QWidget *parent);
|
TwitchPointsRewardWidget(QWidget *parent, bool allowAny);
|
||||||
|
|
||||||
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
||||||
void SetChannel(const TwitchChannel &channel);
|
void SetChannel(const TwitchChannel &channel);
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const std::unordered_map<std::string, std::string> TokenOption::_apiIdToLocale{
|
|||||||
static void saveConnections(obs_data_t *obj);
|
static void saveConnections(obs_data_t *obj);
|
||||||
static void loadConnections(obs_data_t *obj);
|
static void loadConnections(obs_data_t *obj);
|
||||||
|
|
||||||
bool setupTwitchTokenSupport()
|
static bool setupTwitchTokenSupport()
|
||||||
{
|
{
|
||||||
AddSaveStep(saveConnections);
|
AddSaveStep(saveConnections);
|
||||||
AddLoadStep(loadConnections);
|
AddLoadStep(loadConnections);
|
||||||
@@ -153,11 +153,37 @@ bool TokenOption::operator<(const TokenOption &other) const
|
|||||||
return apiId < other.apiId;
|
return apiId < other.apiId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TwitchToken::TwitchToken(const TwitchToken &other)
|
||||||
|
: Item(other._name),
|
||||||
|
_token(other._token),
|
||||||
|
_lastValidityCheckTime(),
|
||||||
|
_lastValidityCheckValue(),
|
||||||
|
_lastValidityCheckResult(false),
|
||||||
|
_userID(other._userID),
|
||||||
|
_tokenOptions(other._tokenOptions),
|
||||||
|
_eventSub(),
|
||||||
|
_validateEventSubTimestamps(other._validateEventSubTimestamps)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TwitchToken &TwitchToken::operator=(const TwitchToken &other)
|
||||||
|
{
|
||||||
|
_name = other._name;
|
||||||
|
_token = other._token;
|
||||||
|
_userID = other._userID;
|
||||||
|
_tokenOptions = other._tokenOptions;
|
||||||
|
_validateEventSubTimestamps = other._validateEventSubTimestamps;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void TwitchToken::Load(obs_data_t *obj)
|
void TwitchToken::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
Item::Load(obj);
|
Item::Load(obj);
|
||||||
_token = obs_data_get_string(obj, "token");
|
_token = obs_data_get_string(obj, "token");
|
||||||
_userID = obs_data_get_string(obj, "userID");
|
_userID = obs_data_get_string(obj, "userID");
|
||||||
|
obs_data_set_default_bool(obj, "validateEventSubTimestamps", true);
|
||||||
|
_validateEventSubTimestamps =
|
||||||
|
obs_data_get_bool(obj, "validateEventSubTimestamps");
|
||||||
_tokenOptions.clear();
|
_tokenOptions.clear();
|
||||||
OBSDataArrayAutoRelease options = obs_data_get_array(obj, "options");
|
OBSDataArrayAutoRelease options = obs_data_get_array(obj, "options");
|
||||||
size_t count = obs_data_array_count(options);
|
size_t count = obs_data_array_count(options);
|
||||||
@@ -174,6 +200,8 @@ void TwitchToken::Save(obs_data_t *obj) const
|
|||||||
Item::Save(obj);
|
Item::Save(obj);
|
||||||
obs_data_set_string(obj, "token", _token.c_str());
|
obs_data_set_string(obj, "token", _token.c_str());
|
||||||
obs_data_set_string(obj, "userID", _userID.c_str());
|
obs_data_set_string(obj, "userID", _userID.c_str());
|
||||||
|
obs_data_set_bool(obj, "validateEventSubTimestamps",
|
||||||
|
_validateEventSubTimestamps);
|
||||||
OBSDataArrayAutoRelease options = obs_data_array_create();
|
OBSDataArrayAutoRelease options = obs_data_array_create();
|
||||||
for (auto &option : _tokenOptions) {
|
for (auto &option : _tokenOptions) {
|
||||||
OBSDataAutoRelease arrayObj = obs_data_create();
|
OBSDataAutoRelease arrayObj = obs_data_create();
|
||||||
@@ -260,45 +288,46 @@ std::shared_ptr<EventSub> TwitchToken::GetEventSub()
|
|||||||
{
|
{
|
||||||
if (!_eventSub) {
|
if (!_eventSub) {
|
||||||
_eventSub = std::make_shared<EventSub>();
|
_eventSub = std::make_shared<EventSub>();
|
||||||
|
_eventSub->EnableTimestampValidation(
|
||||||
|
_validateEventSubTimestamps);
|
||||||
}
|
}
|
||||||
return _eventSub;
|
return _eventSub;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TwitchToken::IsValid(bool forceUpdate) const
|
bool TwitchToken::IsValid(bool forceUpdate) const
|
||||||
{
|
{
|
||||||
static std::chrono::system_clock::time_point queryTime;
|
|
||||||
static std::string lastQueryToken;
|
|
||||||
static httplib::Result response;
|
|
||||||
static httplib::Client cli("https://id.twitch.tv");
|
static httplib::Client cli("https://id.twitch.tv");
|
||||||
httplib::Headers headers{{"Authorization", "OAuth " + _token}};
|
httplib::Headers headers{{"Authorization", "OAuth " + _token}};
|
||||||
|
|
||||||
|
std::scoped_lock<std::mutex> lock(_cacheMutex);
|
||||||
|
|
||||||
auto currentTime = std::chrono::system_clock::now();
|
auto currentTime = std::chrono::system_clock::now();
|
||||||
auto diff = currentTime - queryTime;
|
auto diff = currentTime - _lastValidityCheckTime;
|
||||||
const bool cacheIsTooOld = diff >= std::chrono::hours(1);
|
const bool cacheIsTooOld = diff >= std::chrono::hours(1);
|
||||||
|
|
||||||
const bool tokenChanged = lastQueryToken != _token;
|
const auto checkToken = [&]() -> bool {
|
||||||
if (tokenChanged) {
|
const auto response =
|
||||||
response =
|
|
||||||
cli.Get("/oauth2/validate", httplib::Params{}, headers);
|
cli.Get("/oauth2/validate", httplib::Params{}, headers);
|
||||||
queryTime = std::chrono::system_clock::now();
|
_lastValidityCheckTime = std::chrono::system_clock::now();
|
||||||
lastQueryToken = _token;
|
_lastValidityCheckValue = _token;
|
||||||
return response && response->status == 200;
|
_lastValidityCheckResult = response && response->status == 200;
|
||||||
|
if (!_lastValidityCheckResult) {
|
||||||
|
blog(LOG_INFO, "Twitch token %s is not valid!",
|
||||||
|
_name.c_str());
|
||||||
|
}
|
||||||
|
return _lastValidityCheckResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool tokenChanged = _lastValidityCheckValue != _token;
|
||||||
|
if (tokenChanged) {
|
||||||
|
return checkToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
// No point in checking again as token will not become valid again
|
if (!forceUpdate && !cacheIsTooOld) {
|
||||||
if (!forceUpdate && response && response->status != 200) {
|
return _lastValidityCheckResult;
|
||||||
blog(LOG_INFO, "Twitch token %s is not valid!", _name.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!forceUpdate && !cacheIsTooOld && response) {
|
return checkToken();
|
||||||
return response->status == 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
response = cli.Get("/oauth2/validate", httplib::Params{}, headers);
|
|
||||||
queryTime = std::chrono::system_clock::now();
|
|
||||||
lastQueryToken = _token;
|
|
||||||
return response && response->status == 200;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TwitchToken *GetTwitchTokenByName(const QString &name)
|
TwitchToken *GetTwitchTokenByName(const QString &name)
|
||||||
@@ -445,7 +474,9 @@ TwitchTokenSettingsDialog::TwitchTokenSettingsDialog(
|
|||||||
_showToken(new QPushButton()),
|
_showToken(new QPushButton()),
|
||||||
_currentTokenValue(new QLineEdit()),
|
_currentTokenValue(new QLineEdit()),
|
||||||
_tokenStatus(new QLabel()),
|
_tokenStatus(new QLabel()),
|
||||||
_generalSettingsGrid(new QGridLayout())
|
_generalSettingsGrid(new QGridLayout()),
|
||||||
|
_validateTimestamps(new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.twitchToken.validateTimestamps")))
|
||||||
{
|
{
|
||||||
_showToken->setMaximumWidth(22);
|
_showToken->setMaximumWidth(22);
|
||||||
_showToken->setFlat(true);
|
_showToken->setFlat(true);
|
||||||
@@ -511,6 +542,7 @@ TwitchTokenSettingsDialog::TwitchTokenSettingsDialog(
|
|||||||
auto layout = new QVBoxLayout(contentWidget);
|
auto layout = new QVBoxLayout(contentWidget);
|
||||||
layout->addLayout(_generalSettingsGrid);
|
layout->addLayout(_generalSettingsGrid);
|
||||||
layout->addWidget(optionsBox);
|
layout->addWidget(optionsBox);
|
||||||
|
layout->addWidget(_validateTimestamps);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
scrollArea->setWidget(contentWidget);
|
scrollArea->setWidget(contentWidget);
|
||||||
|
|
||||||
@@ -532,6 +564,8 @@ TwitchTokenSettingsDialog::TwitchTokenSettingsDialog(
|
|||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_validateTimestamps->setChecked(settings._validateEventSubTimestamps);
|
||||||
|
|
||||||
_currentToken = settings;
|
_currentToken = settings;
|
||||||
|
|
||||||
QWidget::connect(&_validationTimer, &QTimer::timeout, this,
|
QWidget::connect(&_validationTimer, &QTimer::timeout, this,
|
||||||
@@ -737,6 +771,12 @@ bool TwitchTokenSettingsDialog::AskForSettings(QWidget *parent,
|
|||||||
|
|
||||||
settings = dialog._currentToken;
|
settings = dialog._currentToken;
|
||||||
settings._tokenOptions = dialog.GetEnabledOptions();
|
settings._tokenOptions = dialog.GetEnabledOptions();
|
||||||
|
settings._validateEventSubTimestamps =
|
||||||
|
dialog._validateTimestamps->isChecked();
|
||||||
|
if (settings._eventSub) {
|
||||||
|
settings._eventSub->EnableTimestampValidation(
|
||||||
|
settings._validateEventSubTimestamps);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ private:
|
|||||||
|
|
||||||
class TwitchToken : public Item {
|
class TwitchToken : public Item {
|
||||||
public:
|
public:
|
||||||
|
TwitchToken() = default;
|
||||||
|
TwitchToken(const TwitchToken &);
|
||||||
|
TwitchToken &operator=(const TwitchToken &);
|
||||||
|
|
||||||
static std::shared_ptr<Item> Create()
|
static std::shared_ptr<Item> Create()
|
||||||
{
|
{
|
||||||
return std::make_shared<TwitchToken>();
|
return std::make_shared<TwitchToken>();
|
||||||
@@ -49,14 +53,20 @@ public:
|
|||||||
std::optional<std::string> GetToken() const;
|
std::optional<std::string> GetToken() const;
|
||||||
std::string GetUserID() const { return _userID; }
|
std::string GetUserID() const { return _userID; }
|
||||||
std::shared_ptr<EventSub> GetEventSub();
|
std::shared_ptr<EventSub> GetEventSub();
|
||||||
|
bool ValidateTimestamps() const { return _validateEventSubTimestamps; }
|
||||||
bool IsValid(bool forceUpdate = false) const;
|
bool IsValid(bool forceUpdate = false) const;
|
||||||
size_t PermissionCount() const { return _tokenOptions.size(); }
|
size_t PermissionCount() const { return _tokenOptions.size(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _token;
|
std::string _token;
|
||||||
|
mutable std::mutex _cacheMutex;
|
||||||
|
mutable std::string _lastValidityCheckValue;
|
||||||
|
mutable bool _lastValidityCheckResult = false;
|
||||||
|
mutable std::chrono::system_clock::time_point _lastValidityCheckTime;
|
||||||
std::string _userID;
|
std::string _userID;
|
||||||
std::set<TokenOption> _tokenOptions = TokenOption::GetAllTokenOptions();
|
std::set<TokenOption> _tokenOptions = TokenOption::GetAllTokenOptions();
|
||||||
std::shared_ptr<EventSub> _eventSub;
|
std::shared_ptr<EventSub> _eventSub;
|
||||||
|
bool _validateEventSubTimestamps = false;
|
||||||
|
|
||||||
static bool _setup;
|
static bool _setup;
|
||||||
|
|
||||||
@@ -120,6 +130,7 @@ private:
|
|||||||
TokenGrabberThread _tokenGrabber;
|
TokenGrabberThread _tokenGrabber;
|
||||||
TwitchToken _currentToken;
|
TwitchToken _currentToken;
|
||||||
std::unordered_map<std::string, QCheckBox *> _optionWidgets;
|
std::unordered_map<std::string, QCheckBox *> _optionWidgets;
|
||||||
|
QCheckBox *_validateTimestamps;
|
||||||
QTimer _validationTimer;
|
QTimer _validationTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,17 +29,23 @@ public:
|
|||||||
_headers(headers)
|
_headers(headers)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
bool operator<(const Args &other) const
|
bool operator==(const Args &other) const
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
ret = ret && _uri < other._uri;
|
ret = ret && _uri == other._uri;
|
||||||
ret = ret && _path < other._path;
|
ret = ret && _path == other._path;
|
||||||
ret = ret && _params < other._params;
|
ret = ret && _params == other._params;
|
||||||
ret = ret && _data < other._data;
|
ret = ret && _data == other._data;
|
||||||
ret = ret && _headers < other._headers;
|
ret = ret && _headers == other._headers;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string &uri() const { return _uri; }
|
||||||
|
const std::string &path() const { return _path; }
|
||||||
|
const std::string &data() const { return _data; }
|
||||||
|
const httplib::Params ¶ms() const { return _params; }
|
||||||
|
const httplib::Headers &headers() const { return _headers; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _uri;
|
std::string _uri;
|
||||||
std::string _path;
|
std::string _path;
|
||||||
@@ -48,6 +54,37 @@ private:
|
|||||||
httplib::Headers _headers;
|
httplib::Headers _headers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}; // namespace advss
|
||||||
|
|
||||||
|
template<> struct std::hash<advss::Args> {
|
||||||
|
inline std::size_t operator()(const advss::Args &args) const
|
||||||
|
{
|
||||||
|
static constexpr auto hash_combine = [](std::size_t &seed,
|
||||||
|
std::size_t hashValue) {
|
||||||
|
seed ^= hashValue + 0x9e3779b9 + (seed << 6) +
|
||||||
|
(seed >> 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::size_t seed = 0;
|
||||||
|
hash_combine(seed, std::hash<std::string>()(args.uri()));
|
||||||
|
hash_combine(seed, std::hash<std::string>()(args.path()));
|
||||||
|
hash_combine(seed, std::hash<std::string>()(args.data()));
|
||||||
|
|
||||||
|
for (const auto &[key, value] : args.params()) {
|
||||||
|
hash_combine(seed, std::hash<std::string>()(key));
|
||||||
|
hash_combine(seed, std::hash<std::string>()(value));
|
||||||
|
}
|
||||||
|
for (const auto &[key, value] : args.headers()) {
|
||||||
|
hash_combine(seed, std::hash<std::string>()(key));
|
||||||
|
hash_combine(seed, std::hash<std::string>()(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
struct CacheEntry {
|
struct CacheEntry {
|
||||||
RequestResult result;
|
RequestResult result;
|
||||||
std::chrono::system_clock::time_point cacheTime =
|
std::chrono::system_clock::time_point cacheTime =
|
||||||
@@ -61,14 +98,15 @@ static bool cacheIsTooOld(const CacheEntry &cache)
|
|||||||
return diff >= std::chrono::seconds(cacheTimeoutSeconds);
|
return diff >= std::chrono::seconds(cacheTimeoutSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cacheIsValid(const std::map<Args, CacheEntry> &cache,
|
static bool cacheIsValid(const std::unordered_map<Args, CacheEntry> &cache,
|
||||||
const Args &args)
|
const Args &args)
|
||||||
{
|
{
|
||||||
auto it = cache.find(args);
|
auto it = cache.find(args);
|
||||||
return it != cache.end() && !cacheIsTooOld(it->second);
|
return it != cache.end() && !cacheIsTooOld(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanupCache(std::map<Args, CacheEntry> &cache, std::mutex &mtx)
|
static void cleanupCache(std::unordered_map<Args, CacheEntry> &cache,
|
||||||
|
std::mutex &mtx)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
cache.clear();
|
cache.clear();
|
||||||
@@ -181,7 +219,7 @@ RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<Args, CacheEntry> cache;
|
static std::unordered_map<Args, CacheEntry> cache;
|
||||||
static std::mutex mtx;
|
static std::mutex mtx;
|
||||||
[[maybe_unused]] static bool _ = []() {
|
[[maybe_unused]] static bool _ = []() {
|
||||||
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
||||||
@@ -251,7 +289,7 @@ RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<Args, CacheEntry> cache;
|
static std::unordered_map<Args, CacheEntry> cache;
|
||||||
static std::mutex mtx;
|
static std::mutex mtx;
|
||||||
[[maybe_unused]] static bool _ = []() {
|
[[maybe_unused]] static bool _ = []() {
|
||||||
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
||||||
@@ -322,7 +360,7 @@ RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<Args, CacheEntry> cache;
|
static std::unordered_map<Args, CacheEntry> cache;
|
||||||
static std::mutex mtx;
|
static std::mutex mtx;
|
||||||
[[maybe_unused]] static bool _ = []() {
|
[[maybe_unused]] static bool _ = []() {
|
||||||
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
||||||
@@ -393,7 +431,7 @@ RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<Args, CacheEntry> cache;
|
static std::unordered_map<Args, CacheEntry> cache;
|
||||||
static std::mutex mtx;
|
static std::mutex mtx;
|
||||||
[[maybe_unused]] static bool _ = []() {
|
[[maybe_unused]] static bool _ = []() {
|
||||||
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
||||||
|
|||||||
@@ -148,6 +148,39 @@ def script_load(settings):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Other signals and procedures, which might be useful
|
||||||
|
def plugin_stop_handler(data):
|
||||||
|
obs.script_log(obs.LOG_INFO, "Hello from stop handler!")
|
||||||
|
get_running_status()
|
||||||
|
|
||||||
|
def plugin_start_handler(data):
|
||||||
|
obs.script_log(obs.LOG_INFO, "Hello from start handler!")
|
||||||
|
get_running_status()
|
||||||
|
|
||||||
|
def get_running_status():
|
||||||
|
proc_handler = obs.obs_get_proc_handler()
|
||||||
|
data = obs.calldata_create()
|
||||||
|
obs.proc_handler_call(proc_handler, "advss_plugin_running", data)
|
||||||
|
success = obs.calldata_bool(data, "is_running")
|
||||||
|
obs.script_log(
|
||||||
|
obs.LOG_INFO, f"The advanced scene switcher is currently running: {success}"
|
||||||
|
)
|
||||||
|
obs.calldata_destroy(data)
|
||||||
|
|
||||||
|
def interval_reset_handler(data):
|
||||||
|
obs.script_log(obs.LOG_INFO, "Hello from reset handler!")
|
||||||
|
|
||||||
|
signal_handler = obs.obs_get_signal_handler()
|
||||||
|
obs.signal_handler_connect(
|
||||||
|
signal_handler, "advss_plugin_stopped", plugin_stop_handler
|
||||||
|
)
|
||||||
|
obs.signal_handler_connect(
|
||||||
|
signal_handler, "advss_plugin_started", plugin_start_handler
|
||||||
|
)
|
||||||
|
obs.signal_handler_connect(
|
||||||
|
signal_handler, "advss_interval_reset", interval_reset_handler
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def script_unload():
|
def script_unload():
|
||||||
# Deregistering is useful if you plan on reloading the script files
|
# Deregistering is useful if you plan on reloading the script files
|
||||||
|
|||||||
@@ -64,9 +64,14 @@ target_sources(
|
|||||||
|
|
||||||
# --- json --- #
|
# --- json --- #
|
||||||
|
|
||||||
|
if(TARGET jsoncons)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE jsoncons)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE JSONPATH_SUPPORT=1)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC nlohmann_json::nlohmann_json)
|
||||||
target_sources(
|
target_sources(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME} PRIVATE test-json.cpp
|
||||||
PRIVATE test-json.cpp ${ADVSS_SOURCE_DIR}/plugins/base/utils/json-helpers.cpp)
|
${ADVSS_SOURCE_DIR}/lib/utils/json-helpers.cpp)
|
||||||
|
|
||||||
# --- math --- #
|
# --- math --- #
|
||||||
|
|
||||||
@@ -95,8 +100,6 @@ target_sources(
|
|||||||
|
|
||||||
# --- utility --- #
|
# --- utility --- #
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC nlohmann_json::nlohmann_json)
|
|
||||||
|
|
||||||
target_sources(
|
target_sources(
|
||||||
${PROJECT_NAME} PRIVATE test-utility.cpp
|
${PROJECT_NAME} PRIVATE test-utility.cpp
|
||||||
${ADVSS_SOURCE_DIR}/lib/utils/utility.cpp)
|
${ADVSS_SOURCE_DIR}/lib/utils/utility.cpp)
|
||||||
|
|||||||
@@ -51,3 +51,55 @@ TEST_CASE("MatchJson", "[json-helpers]")
|
|||||||
result = advss::MatchJson("{\n \"test\": true\n}\n", "(", regex);
|
result = advss::MatchJson("{\n \"test\": true\n}\n", "(", regex);
|
||||||
REQUIRE(result == false);
|
REQUIRE(result == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("GetJsonField", "[json-helpers]")
|
||||||
|
{
|
||||||
|
auto result = advss::GetJsonField("{}", "");
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::GetJsonField("{}", "not there");
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::GetJsonField("invalid json", "not there");
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::GetJsonField("{ \"test\": 1 }", "test");
|
||||||
|
REQUIRE(*result == "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("QueryJson", "[json-helpers]")
|
||||||
|
{
|
||||||
|
auto result = advss::QueryJson("{}", "");
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::QueryJson("invalid json", "$.test");
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::QueryJson("{}", "invalid query");
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::QueryJson("{}", "$.test");
|
||||||
|
REQUIRE(*result == "[]");
|
||||||
|
|
||||||
|
result = advss::QueryJson("{ \"test\": { \"something\" : 1 } }",
|
||||||
|
"$.test.something");
|
||||||
|
REQUIRE(*result == "[1]");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("AccessJsonArrayIndex", "[json-helpers]")
|
||||||
|
{
|
||||||
|
auto result = advss::AccessJsonArrayIndex("{}", 0);
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::AccessJsonArrayIndex("invalid json", 0);
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::AccessJsonArrayIndex("[]", 0);
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::AccessJsonArrayIndex("[\"1\", \"2\"]", -1);
|
||||||
|
REQUIRE_FALSE(result);
|
||||||
|
|
||||||
|
result = advss::AccessJsonArrayIndex("[\"1\", \"2\"]", 1);
|
||||||
|
REQUIRE(*result == "2");
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,21 +20,6 @@ TEST_CASE("ReplaceAll", "[utility]")
|
|||||||
REQUIRE(input == "sleeping");
|
REQUIRE(input == "sleeping");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("GetJsonField", "[utility]")
|
|
||||||
{
|
|
||||||
auto result = advss::GetJsonField("{}", "");
|
|
||||||
REQUIRE_FALSE(result);
|
|
||||||
|
|
||||||
result = advss::GetJsonField("{}", "not there");
|
|
||||||
REQUIRE_FALSE(result);
|
|
||||||
|
|
||||||
result = advss::GetJsonField("invalid json", "not there");
|
|
||||||
REQUIRE_FALSE(result);
|
|
||||||
|
|
||||||
result = advss::GetJsonField("{ \"test\": 1 }", "test");
|
|
||||||
REQUIRE(*result == "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("CompareIgnoringLineEnding", "[utility]")
|
TEST_CASE("CompareIgnoringLineEnding", "[utility]")
|
||||||
{
|
{
|
||||||
QString s1;
|
QString s1;
|
||||||
|
|||||||
Reference in New Issue
Block a user