Compare commits

...

53 Commits

Author SHA1 Message Date
WarmUpTill
17d9b73b9a Fix thread flood when Twitch event sub connection is lost
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-15 12:31:13 +01:00
WarmUpTill
d6185a6099 Add handling for Twitch API throttling 2025-03-15 12:31:13 +01:00
WarmUpTill
41c06b9edb Refactor Twitch condition event handling 2025-03-15 12:31:13 +01:00
WarmUpTill
d8807077da Add option to query reward information 2025-03-15 12:31:13 +01:00
WarmUpTill
34e125f82d Rework chache handling
* No longer provide multiple HTTP helper functions with the same name
* Default to cache not being used
* Explicitly use the cache for certain Twitch actions and conditions
* Clear caches on shutdown to avoid leaks being reported
2025-03-15 12:31:13 +01:00
WarmUpTill
d670b6d07e Move JSON temp var heper functions 2025-03-15 12:31:13 +01:00
WarmUpTill
7b0f985f18 Add macro properties for 'reward' object in redemption events 2025-03-15 12:31:13 +01:00
WarmUpTill
212be923f6 Add option to query user information 2025-03-15 12:31:13 +01:00
WarmUpTill
88fcb57e9f Cleanup 2025-03-15 12:31:13 +01:00
WarmUpTill
9b609c118f Fix disabling chat emote only mode attempting to send chat messages 2025-03-15 12:31:13 +01:00
WarmUpTill
37c398d37a Indicate that "Set to condition/action value" should be avoided 2025-03-15 12:31:13 +01:00
WarmUpTill
a51b7f6b13 Fix UI theming issues
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-11 22:12:05 +01:00
WarmUpTill
808fd84b83 Remove Qt5 support 2025-03-11 22:12:05 +01:00
WarmUpTill
57bcea15f5 cmake: Replace find_qt with find_package 2025-03-10 19:48:01 +01:00
WarmUpTill
4e51b56b9b Add game capture condition 2025-03-10 18:49:07 +01:00
WarmUpTill
ef5cf41d34 Add OBS 31 support 2025-03-10 18:49:07 +01:00
WarmUpTill
911e79ea6a Fix OCR controls showing in place of color controls
This happened when OCR support was disabled at build time
2025-03-10 18:33:13 +01:00
WarmUpTill
d586177de0 Add screenshot condition
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-03-09 20:57:51 +01:00
WarmUpTill
202c36646c Fix UI issues when opening ItemSettingsDialogs
Some widgets were displayed incorrectly if the parent of the dialog
window is not the main settings window
2025-03-09 20:05:07 +01:00
WarmUpTill
ab5102f5ca Fix typo 2025-03-09 20:05:07 +01:00
WarmUpTill
e612fb99f6 Cleanup
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
* Reorder action type selection
* Refactor
2025-03-06 21:30:03 +01:00
WarmUpTill
691e77a69a Add option to generate random number 2025-03-06 21:30:03 +01:00
Kaito Udagawa
930118c61f Call Setup() after Load()
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Tesseract API depends on the languageCode and this must be reinitialized after Load
2025-03-06 08:43:54 +01:00
Kaito Udagawa
1e05f2f2ce Fix languageCode
LanguageCode is not respected by OCRParameters and settings properly.
2025-03-06 08:43:54 +01:00
WarmUpTill
ae25b4d023 Improve setting selection handling
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Will now try to keep the current selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
af055a12a1 Cleanup 2025-03-05 14:16:04 +01:00
WarmUpTill
7de28eedd3 Improve settings button selection handling
Will now try to keep the current button selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
8dfe81f522 Display failure if Twitch token account name cannot be fetched
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-27 19:52:41 +01:00
WarmUpTill
a1d8ae291d Add option to hide empty name warning in ItemSettingsDialog 2025-02-27 19:52:41 +01:00
WarmUpTill
eb6989527d Add support for libproc2 version 4.0.5
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-10 18:12:08 +01:00
WarmUpTill
2986a5dd96 Fix crash when actions were copied into a action queue
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The steps added via AddPostLoadStep() from the copy were never executed.
Thus they might be invalid when RunPostLoadSteps() is next called.

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

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

It will be resolved when the minimum OBS version is bumped to OBS 31.
2024-11-02 08:28:52 +01:00
WarmUpTill
63a545b293 Fix "Date" condition returning true unexpectedly
This could happen if the plugin was stopped on the General tab and
restarted or the first time the plugin was started.
2024-11-02 08:28:52 +01:00
WarmUpTill
bc0497d2c9 Add MacroWasCheckedSinceLastStart() 2024-11-02 08:28:52 +01:00
WarmUpTill
37226a3a4c Fix macro group icon not being displayed correctly in OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-31 19:39:38 +01:00
WarmUpTill
9f15fbe47c Recursively search for source button in property groups
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-30 18:29:23 +01:00
105 changed files with 12022 additions and 5181 deletions

View File

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

View File

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

View File

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

View File

@@ -480,12 +480,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
popd popd
;; ;;
linux) linux)
# Hacky workaround to support libproc2 with Ubuntu 22 build environment # Nothing to do for now
local lsb_version=$(lsb_release -r | cut -f 2 || true)
if [[ "${lsb_version}" == '22.04' ]] {
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
}
;; ;;
} }
} }

0
.github/test vendored
View File

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,12 @@ if(BUILD_OUT_OF_TREE)
include(helpers) include(helpers)
endif() endif()
# OBS 31 no longer defines find_qt so check if we need to include it for in-tree
# builds
if(NOT COMMAND find_qt)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/helpers_common.cmake")
endif()
set(LIB_NAME "${PROJECT_NAME}-lib") set(LIB_NAME "${PROJECT_NAME}-lib")
add_library(${PROJECT_NAME} MODULE) add_library(${PROJECT_NAME} MODULE)
add_library(${LIB_NAME} SHARED) add_library(${LIB_NAME} SHARED)
@@ -289,9 +295,9 @@ include(cmake/common/advss_helpers.cmake)
setup_obs_lib_dependency(${LIB_NAME}) setup_obs_lib_dependency(${LIB_NAME})
setup_obs_lib_dependency(${PROJECT_NAME}) setup_obs_lib_dependency(${PROJECT_NAME})
find_qt(COMPONENTS Widgets Core) find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets) target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
target_link_libraries(${LIB_NAME} PRIVATE Qt::Core Qt::Widgets) target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
target_compile_options( target_compile_options(
${PROJECT_NAME} ${PROJECT_NAME}
PRIVATE PRIVATE
@@ -407,7 +413,12 @@ else()
set(PROCESS_CONDITION_SUPPORTED 1) set(PROCESS_CONDITION_SUPPORTED 1)
endif() endif()
if(PROCPS2_INCLUDE_DIR) if(PROCPS2_INCLUDE_DIR)
message(STATUS "${PROJECT_NAME} using libproc2") find_package(PkgConfig REQUIRED)
pkg_check_modules(libproc2 IMPORTED_TARGET libproc2<4.0.5 QUIET)
if(libproc2_FOUND)
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_USE_INFO)
endif()
pkg_check_modules(libproc2 REQUIRED IMPORTED_TARGET libproc2)
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}") set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE) target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
set(PROCESS_CONDITION_SUPPORTED 1) set(PROCESS_CONDITION_SUPPORTED 1)

View File

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

View File

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

View File

@@ -283,8 +283,8 @@ endfunction()
function(setup_advss_plugin target) function(setup_advss_plugin target)
setup_obs_lib_dependency(${target}) setup_obs_lib_dependency(${target})
find_qt(COMPONENTS Widgets Core) find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets) target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
set_target_properties( set_target_properties(
${target} ${target}

View File

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

View File

@@ -769,6 +769,11 @@ AdvSceneSwitcher.condition.streamDeck.checkData="Check data field"
AdvSceneSwitcher.condition.streamDeck.startListen="Start listening" AdvSceneSwitcher.condition.streamDeck.startListen="Start listening"
AdvSceneSwitcher.condition.streamDeck.stopListen="Stop listening" AdvSceneSwitcher.condition.streamDeck.stopListen="Stop listening"
AdvSceneSwitcher.condition.streamDeck.pluginDownload="<html><head/><body><p>The Stream Deck plugin can be found <a href=\"https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin/releases\"><span style=\" text-decoration: underline; color:#268bd2;\">here on GitHub</span></a>.</p></body></html>" AdvSceneSwitcher.condition.streamDeck.pluginDownload="<html><head/><body><p>The Stream Deck plugin can be found <a href=\"https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin/releases\"><span style=\" text-decoration: underline; color:#268bd2;\">here on GitHub</span></a>.</p></body></html>"
AdvSceneSwitcher.condition.gameCapture="Game capture"
AdvSceneSwitcher.condition.gameCapture.entry="{{sources}}hooked a game."
AdvSceneSwitcher.condition.screenshot="Screenshot"
AdvSceneSwitcher.condition.screenshot.entry="A screenshot was taken"
# Macro Actions # Macro Actions
AdvSceneSwitcher.action.unknown="Unknown action" AdvSceneSwitcher.action.unknown="Unknown action"
@@ -1054,13 +1059,13 @@ AdvSceneSwitcher.action.http.type.post="POST"
AdvSceneSwitcher.action.http.entry.line1="Send{{method}}to{{url}}" AdvSceneSwitcher.action.http.entry.line1="Send{{method}}to{{url}}"
AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds" AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds"
AdvSceneSwitcher.action.variable="Variable" AdvSceneSwitcher.action.variable="Variable"
AdvSceneSwitcher.action.variable.type.set="Set to fixed value" AdvSceneSwitcher.action.variable.type.set="Set to value"
AdvSceneSwitcher.action.variable.type.append="Append" AdvSceneSwitcher.action.variable.type.append="Append"
AdvSceneSwitcher.action.variable.type.appendVar="Append variable" AdvSceneSwitcher.action.variable.type.appendVar="Append variable"
AdvSceneSwitcher.action.variable.type.increment="Increment" AdvSceneSwitcher.action.variable.type.increment="Increment"
AdvSceneSwitcher.action.variable.type.decrement="Decrement" AdvSceneSwitcher.action.variable.type.decrement="Decrement"
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value" AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value (legacy)"
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value" AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value (legacy)"
AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer" AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer"
AdvSceneSwitcher.action.variable.type.subString="Set to substring of current value" AdvSceneSwitcher.action.variable.type.subString="Set to substring of current value"
AdvSceneSwitcher.action.variable.type.findAndReplace="Find and replace in current value" AdvSceneSwitcher.action.variable.type.findAndReplace="Find and replace in current value"
@@ -1078,6 +1083,7 @@ AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values" AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values"
AdvSceneSwitcher.action.variable.type.trim="Trim whitespace" AdvSceneSwitcher.action.variable.type.trim="Trim whitespace"
AdvSceneSwitcher.action.variable.type.changeCase="Change case" AdvSceneSwitcher.action.variable.type.changeCase="Change case"
AdvSceneSwitcher.action.variable.type.randomNumber="Generate random number"
AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase" AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase"
AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE" AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE"
AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized" AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized"
@@ -1092,6 +1098,7 @@ AdvSceneSwitcher.action.variable.findAndReplace.replace="Text to replace with"
AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip="Using \1, \2, etc. to represent first, second, etc. capture groups is supported." AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip="Using \1, \2, etc. to represent first, second, etc. capture groups is supported."
AdvSceneSwitcher.action.variable.subString.begin="Beginning" AdvSceneSwitcher.action.variable.subString.begin="Beginning"
AdvSceneSwitcher.action.variable.subString.all="All" AdvSceneSwitcher.action.variable.subString.all="All"
AdvSceneSwitcher.action.variable.generateInteger="Only generate integers"
AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!" AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!" AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
@@ -1104,6 +1111,7 @@ AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMat
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}" AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}" AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}" AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.entry.randomNumber="Generate random number in range from{{randomNumberStart}}to{{randomNumberEnd}}"
AdvSceneSwitcher.action.projector="Projector" AdvSceneSwitcher.action.projector="Projector"
AdvSceneSwitcher.action.projector.type.source="Source" AdvSceneSwitcher.action.projector.type.source="Source"
AdvSceneSwitcher.action.projector.type.scene="Scene" AdvSceneSwitcher.action.projector.type.scene="Scene"
@@ -1134,10 +1142,15 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Create stream clip"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Send chat announcement" AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Send chat announcement"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Enable chat's emote-only mode" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Enable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote-only mode" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Send chat message" AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}" AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}" AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="Enter title" AdvSceneSwitcher.action.twitch.title.title="Enter title"
AdvSceneSwitcher.action.twitch.marker.description="Describe marker" AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip" AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
@@ -1147,6 +1160,8 @@ AdvSceneSwitcher.action.twitch.announcement.blue="Blue"
AdvSceneSwitcher.action.twitch.announcement.green="Green" AdvSceneSwitcher.action.twitch.announcement.green="Green"
AdvSceneSwitcher.action.twitch.announcement.orange="Orange" AdvSceneSwitcher.action.twitch.announcement.orange="Orange"
AdvSceneSwitcher.action.twitch.announcement.purple="Purple" AdvSceneSwitcher.action.twitch.announcement.purple="Purple"
AdvSceneSwitcher.action.twitch.user.getInfo.queryType.id="User Id"
AdvSceneSwitcher.action.twitch.user.getInfo.queryType.login="User login"
AdvSceneSwitcher.action.clipboard="Clipboard" AdvSceneSwitcher.action.clipboard="Clipboard"
AdvSceneSwitcher.action.clipboard.type.copy.text="Copy text" AdvSceneSwitcher.action.clipboard.type.copy.text="Copy text"
AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image" AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image"
@@ -1211,7 +1226,7 @@ AdvSceneSwitcher.validation.text.maxLength="Only %1 characters are allowed in th
AdvSceneSwitcher.variable.select="--select variable--" AdvSceneSwitcher.variable.select="--select variable--"
AdvSceneSwitcher.variable.add="Add new variable" AdvSceneSwitcher.variable.add="Add new variable"
AdvSceneSwitcher.variable.configure="Configure variable settings" AdvSceneSwitcher.variable.configure="Configure variable settings"
AdvSceneSwitcher.variable.invalid="Invalid varialbe selection" AdvSceneSwitcher.variable.invalid="Invalid variable selection"
AdvSceneSwitcher.variable.name="Name:" AdvSceneSwitcher.variable.name="Name:"
AdvSceneSwitcher.variable.value="Current value:" AdvSceneSwitcher.variable.value="Current value:"
AdvSceneSwitcher.variable.save="Save / load behavior" AdvSceneSwitcher.variable.save="Save / load behavior"
@@ -1590,12 +1605,21 @@ AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived="Shoutout time"
AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout." AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout."
AdvSceneSwitcher.tempVar.twitch.id.reward="Reward ID" AdvSceneSwitcher.tempVar.twitch.id.reward="Reward ID"
AdvSceneSwitcher.tempVar.twitch.id.redemption="Redemption ID"
AdvSceneSwitcher.tempVar.twitch.user_id.reward="User ID redeeming the reward" AdvSceneSwitcher.tempVar.twitch.user_id.reward="User ID redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming the reward" AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_name.reward="User name redeeming the reward" AdvSceneSwitcher.tempVar.twitch.user_name.reward="User name redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward" AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward"
AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information" AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information (JSON)"
AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed, at the time it was redeemed." AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed in JSON form, at the time it was redeemed."
AdvSceneSwitcher.tempVar.twitch.reward.id.reward="Reward ID"
AdvSceneSwitcher.tempVar.twitch.reward.id.reward.description="The ID that uniquely identifies the reward."
AdvSceneSwitcher.tempVar.twitch.reward.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.reward.title.reward.description="The rewards title."
AdvSceneSwitcher.tempVar.twitch.reward.prompt.reward="Reward prompt"
AdvSceneSwitcher.tempVar.twitch.reward.prompt.reward.description="The prompt displayed to the viewer if user input is required."
AdvSceneSwitcher.tempVar.twitch.reward.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.reward.cost.reward.description="The rewards cost, in Channel Points."
AdvSceneSwitcher.tempVar.twitch.status.reward="Reward status" AdvSceneSwitcher.tempVar.twitch.status.reward="Reward status"
AdvSceneSwitcher.tempVar.twitch.status.reward.description="Defaults to unfulfilled. Possible values are unknown, unfulfilled, fulfilled, and canceled." AdvSceneSwitcher.tempVar.twitch.status.reward.description="Defaults to unfulfilled. Possible values are unknown, unfulfilled, fulfilled, and canceled."
AdvSceneSwitcher.tempVar.twitch.redeemed_at.reward="Redemption time" AdvSceneSwitcher.tempVar.twitch.redeemed_at.reward="Redemption time"
@@ -1610,22 +1634,37 @@ AdvSceneSwitcher.tempVar.twitch.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.cost.reward="Reward cost" AdvSceneSwitcher.tempVar.twitch.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.prompt.reward="Reward description" AdvSceneSwitcher.tempVar.twitch.prompt.reward="Reward description"
AdvSceneSwitcher.tempVar.twitch.is_user_input_required.reward="Reward requires user input" AdvSceneSwitcher.tempVar.twitch.is_user_input_required.reward="Reward requires user input"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Reward maximum per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream"
AdvSceneSwitcher.tempVar.twitch.background_color.reward="Reward background color" AdvSceneSwitcher.tempVar.twitch.background_color.reward="Reward background color"
AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image" AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image (JSON)"
AdvSceneSwitcher.tempVar.twitch.image.reward.description="Set of custom images of 1x, 2x and 4x sizes for the reward. Can be null if no images have been uploaded." AdvSceneSwitcher.tempVar.twitch.image.reward.description="Set of custom images of 1x, 2x and 4x sizes for the reward. Can be null if no images have been uploaded."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image" AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward="Reward image URL"
AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward.description="The URL to the custom image for the reward. This field is null if the broadcaster didnt upload images."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image (JSON)"
AdvSceneSwitcher.tempVar.twitch.default_image.reward.description="Set of default images of 1x, 2x and 4x sizes for the reward." AdvSceneSwitcher.tempVar.twitch.default_image.reward.description="Set of default images of 1x, 2x and 4x sizes for the reward."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Reward cooldown" AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward="Reward default image URL"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds." AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward.description="The URL to the default image for the reward."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward="Redemption count" AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward="Redemption count"
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled." AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled." AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Maximum redemptions per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward="Maximum redemptions per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward="Maximum redemptions per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward.description="The maximum number of redemptions allowed per live stream."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward="Maximum redemptions per user per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per user per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward="Maximum redemptions per user per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward.description="The settings used to determine whether to apply a maximum to the number of redemptions allowed per user per live stream."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Global cooldown (JSON)"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward="Global cooldown enabled"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward.description="A Boolean value that determines whether to apply a cooldown period. Is true if a cooldown period is enabled."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward="Global cooldown"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward.description="The cooldown period, in seconds."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.charity_name="Charity name" AdvSceneSwitcher.tempVar.twitch.charity_name="Charity name"
AdvSceneSwitcher.tempVar.twitch.charity_description="Charity description" AdvSceneSwitcher.tempVar.twitch.charity_description="Charity description"
@@ -1794,6 +1833,24 @@ AdvSceneSwitcher.tempVar.twitch.to_broadcaster_user_name.raid.description="The b
AdvSceneSwitcher.tempVar.twitch.viewers.raid="Raid participants" AdvSceneSwitcher.tempVar.twitch.viewers.raid="Raid participants"
AdvSceneSwitcher.tempVar.twitch.viewers.raid.description="The number of viewers in the raid." AdvSceneSwitcher.tempVar.twitch.viewers.raid.description="The number of viewers in the raid."
AdvSceneSwitcher.tempVar.twitch.id.user.getInfo="User ID"
AdvSceneSwitcher.tempVar.twitch.id.user.getInfo.description="The user ID of the queried account."
AdvSceneSwitcher.tempVar.twitch.login.user.getInfo="User login"
AdvSceneSwitcher.tempVar.twitch.login.user.getInfo.description="The user login of the queried account."
AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo="User name"
AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo.description="The display name of the queried account."
AdvSceneSwitcher.tempVar.twitch.type.user.getInfo="User type"
AdvSceneSwitcher.tempVar.twitch.type.user.getInfo.description="The type of user. Possible values are:\n admin — Twitch administrator\n global_mod\n staff — Twitch staff\n \"\" — Normal user"
AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo="Broadcaster type"
AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo.description="The type of broadcaster. Possible values are:\n affiliate — An affiliate broadcaster\n partner — A partner broadcaster\n \"\" — A normal broadcaster"
AdvSceneSwitcher.tempVar.twitch.description.user.getInfo="Description"
AdvSceneSwitcher.tempVar.twitch.description.user.getInfo.description="The user's description of their channel."
AdvSceneSwitcher.tempVar.twitch.profile_image_url.user.getInfo="Profile image URL"
AdvSceneSwitcher.tempVar.twitch.offline_image_url.user.getInfo="Offline image URL"
AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo="Account creation date"
AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo.description="The UTC date and time that the user's account was created. The timestamp is in RFC3339 format."
AdvSceneSwitcher.tempVar.audio.output_volume="Output volume" AdvSceneSwitcher.tempVar.audio.output_volume="Output volume"
AdvSceneSwitcher.tempVar.audio.output_volume.description="The volume the audio source is outputting." AdvSceneSwitcher.tempVar.audio.output_volume.description="The volume the audio source is outputting."
AdvSceneSwitcher.tempVar.audio.configured_volume="Configured volume" AdvSceneSwitcher.tempVar.audio.configured_volume="Configured volume"
@@ -1971,6 +2028,16 @@ AdvSceneSwitcher.tempVar.queue.size="Size"
AdvSceneSwitcher.tempVar.queue.running="Is running" AdvSceneSwitcher.tempVar.queue.running="Is running"
AdvSceneSwitcher.tempVar.queue.running.description="Returns \"true\" if the queue is started and \"false\" if it is stopped." AdvSceneSwitcher.tempVar.queue.running.description="Returns \"true\" if the queue is started and \"false\" if it is stopped."
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath="Last screenshot path"
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath.description="The filesystem path to the last screenshot taken."
AdvSceneSwitcher.tempVar.gameCapture.title="Title"
AdvSceneSwitcher.tempVar.gameCapture.title.description="Window title of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.class="Class"
AdvSceneSwitcher.tempVar.gameCapture.class.description="Window class of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
AdvSceneSwitcher.selectScene="--select scene--" AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene" AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene" AdvSceneSwitcher.selectCurrentScene="Current Scene"

View File

@@ -1089,10 +1089,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="ストリームクリップの
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="チャット通知の送信" AdvSceneSwitcher.action.twitch.type.chat.announcement.send="チャット通知の送信"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="チャットのエモート専用モードを有効にする" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="チャットのエモート専用モードを有効にする"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="チャットのエモート専用モードを無効にする" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="チャットのエモート専用モードを無効にする"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="チャットメッセージを送る" AdvSceneSwitcher.action.twitch.type.chat.sendMessage="チャットメッセージを送る"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません"
; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}" ; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}} でアカウント{{account}}{{actions}}を使用しています" AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力してください" AdvSceneSwitcher.action.twitch.title.title="タイトルを入力してください"
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明" AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します" AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"

View File

@@ -1070,10 +1070,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Criar clipe da transmissão"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Enviar anúncio de chat" AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Enviar anúncio de chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas emotes no chat" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Enviar mensagem de chat" AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}" AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}" AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título" AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador" AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe" AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"

13156
deps/exprtk/exprtk.hpp vendored

File diff suppressed because it is too large Load Diff

View File

@@ -735,7 +735,7 @@ void OpenSettingsWindow()
} }
} }
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable) void AdvSceneSwitcher::HighlightMacroSettingsButton(bool enable)
{ {
static QObject *highlight = nullptr; static QObject *highlight = nullptr;
if ((highlight && enable) || (!highlight && !enable)) { if ((highlight && enable) || (!highlight && !enable)) {
@@ -755,13 +755,13 @@ void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
highlight = HighlightWidget(ui->macroSettings, Qt::green); highlight = HighlightWidget(ui->macroSettings, Qt::green);
} }
void HighligthMacroSettingsButton(bool enable) void HighlightMacroSettingsButton(bool enable)
{ {
auto window = GetSettingsWindow(); auto window = GetSettingsWindow();
if (!window) { if (!window) {
return; return;
} }
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton( static_cast<AdvSceneSwitcher *>(window)->HighlightMacroSettingsButton(
enable); enable);
} }

View File

@@ -97,7 +97,7 @@ public:
void SetConditionData(Macro &m); void SetConditionData(Macro &m);
void SwapActions(Macro *m, int pos1, int pos2); void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2); void SwapConditions(Macro *m, int pos1, int pos2);
void HighligthMacroSettingsButton(bool enable = true); void HighlightMacroSettingsButton(bool enable = true);
public slots: public slots:
void on_macroAdd_clicked(); void on_macroAdd_clicked();
@@ -386,6 +386,6 @@ private:
}; };
void OpenSettingsWindow(); void OpenSettingsWindow();
void HighligthMacroSettingsButton(bool enable); void HighlightMacroSettingsButton(bool enable);
} // namespace advss } // namespace advss

View File

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

View File

@@ -122,11 +122,7 @@ void SwitcherData::writeToStatusFile(const QString &msg)
QFile file(QString::fromStdString(fileIO.writePath)); QFile file(QString::fromStdString(fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) { if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file); QTextStream stream(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream << msg << "\n";
#else
stream << msg << Qt::endl; stream << msg << Qt::endl;
#endif
} }
file.close(); file.close();
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,8 @@
#include "source-helpers.hpp" #include "source-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <random>
namespace advss { namespace advss {
const std::string MacroActionVariable::id = "variable"; const std::string MacroActionVariable::id = "variable";
@@ -17,66 +19,6 @@ bool MacroActionVariable::_registered = MacroActionFactory::Register(
{MacroActionVariable::Create, MacroActionVariableEdit::Create, {MacroActionVariable::Create, MacroActionVariableEdit::Create,
"AdvSceneSwitcher.action.variable"}); "AdvSceneSwitcher.action.variable"});
const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
{MacroActionVariable::Type::SET_FIXED_VALUE,
"AdvSceneSwitcher.action.variable.type.set"},
{MacroActionVariable::Type::APPEND,
"AdvSceneSwitcher.action.variable.type.append"},
{MacroActionVariable::Type::APPEND_VAR,
"AdvSceneSwitcher.action.variable.type.appendVar"},
{MacroActionVariable::Type::INCREMENT,
"AdvSceneSwitcher.action.variable.type.increment"},
{MacroActionVariable::Type::DECREMENT,
"AdvSceneSwitcher.action.variable.type.decrement"},
{MacroActionVariable::Type::SET_CONDITION_VALUE,
"AdvSceneSwitcher.action.variable.type.setConditionValue"},
{MacroActionVariable::Type::SET_ACTION_VALUE,
"AdvSceneSwitcher.action.variable.type.setActionValue"},
{MacroActionVariable::Type::ROUND_TO_INT,
"AdvSceneSwitcher.action.variable.type.roundToInt"},
{MacroActionVariable::Type::SUBSTRING,
"AdvSceneSwitcher.action.variable.type.subString"},
{MacroActionVariable::Type::FIND_AND_REPLACE,
"AdvSceneSwitcher.action.variable.type.findAndReplace"},
{MacroActionVariable::Type::MATH_EXPRESSION,
"AdvSceneSwitcher.action.variable.type.mathExpression"},
{MacroActionVariable::Type::USER_INPUT,
"AdvSceneSwitcher.action.variable.type.askForValue"},
{MacroActionVariable::Type::ENV_VARIABLE,
"AdvSceneSwitcher.action.variable.type.environmentVariable"},
{MacroActionVariable::Type::SCENE_ITEM_COUNT,
"AdvSceneSwitcher.action.variable.type.sceneItemCount"},
{MacroActionVariable::Type::STRING_LENGTH,
"AdvSceneSwitcher.action.variable.type.stringLength"},
{MacroActionVariable::Type::EXTRACT_JSON,
"AdvSceneSwitcher.action.variable.type.extractJson"},
{MacroActionVariable::Type::SET_TO_TEMPVAR,
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
{MacroActionVariable::Type::SCENE_ITEM_NAME,
"AdvSceneSwitcher.action.variable.type.sceneItemName"},
{MacroActionVariable::Type::PAD,
"AdvSceneSwitcher.action.variable.type.padValue"},
{MacroActionVariable::Type::TRUNCATE,
"AdvSceneSwitcher.action.variable.type.truncateValue"},
{MacroActionVariable::Type::SWAP_VALUES,
"AdvSceneSwitcher.action.variable.type.swapValues"},
{MacroActionVariable::Type::TRIM,
"AdvSceneSwitcher.action.variable.type.trim"},
{MacroActionVariable::Type::CHANGE_CASE,
"AdvSceneSwitcher.action.variable.type.changeCase"},
};
const static std::map<MacroActionVariable::CaseType, std::string> caseTypes = {
{MacroActionVariable::CaseType::LOWER_CASE,
"AdvSceneSwitcher.action.variable.case.type.lowerCase"},
{MacroActionVariable::CaseType::UPPER_CASE,
"AdvSceneSwitcher.action.variable.case.type.upperCase"},
{MacroActionVariable::CaseType::CAPITALIZED,
"AdvSceneSwitcher.action.variable.case.type.capitalized"},
{MacroActionVariable::CaseType::START_CASE,
"AdvSceneSwitcher.action.variable.case.type.startCase"},
};
static void apppend(Variable &var, const std::string &value) static void apppend(Variable &var, const std::string &value)
{ {
auto currentValue = var.Value(); auto currentValue = var.Value();
@@ -253,6 +195,27 @@ void MacroActionVariable::SetToSceneItemName(Variable *var)
var->SetValue(data.name); var->SetValue(data.name);
} }
void MacroActionVariable::GenerateRandomNumber(Variable *var)
{
static std::random_device rd;
static std::mt19937 gen(rd());
double start = _randomNumberStart;
double end = _randomNumberEnd;
if (start > end) {
std::swap(start, end);
}
if (_generateInteger) {
std::uniform_int_distribution<int> dis(start, end);
var->SetValue(dis(gen));
} else {
std::uniform_real_distribution<double> dis(start, end);
var->SetValue(dis(gen));
}
}
struct AskForInputParams { struct AskForInputParams {
AskForInputParams(const QString &prompt_, const QString &placeholder_) AskForInputParams(const QString &prompt_, const QString &placeholder_)
: prompt(prompt_), : prompt(prompt_),
@@ -313,14 +276,14 @@ bool MacroActionVariable::PerformAction()
return true; return true;
} }
switch (_type) { switch (_action) {
case Type::SET_FIXED_VALUE: case Action::SET_VALUE:
var->SetValue(_strValue); var->SetValue(_strValue);
break; break;
case Type::APPEND: case Action::APPEND:
apppend(*var, _strValue); apppend(*var, _strValue);
break; break;
case Type::APPEND_VAR: { case Action::APPEND_VAR: {
auto var2 = _variable2.lock(); auto var2 = _variable2.lock();
if (!var2) { if (!var2) {
return true; return true;
@@ -328,14 +291,14 @@ bool MacroActionVariable::PerformAction()
apppend(*var, var2->Value()); apppend(*var, var2->Value());
break; break;
} }
case Type::INCREMENT: case Action::INCREMENT:
modifyNumValue(*var, _numValue.GetValue(), true); modifyNumValue(*var, _numValue.GetValue(), true);
break; break;
case Type::DECREMENT: case Action::DECREMENT:
modifyNumValue(*var, _numValue.GetValue(), false); modifyNumValue(*var, _numValue.GetValue(), false);
break; break;
case Type::SET_CONDITION_VALUE: case Action::SET_CONDITION_VALUE:
case Type::SET_ACTION_VALUE: { case Action::SET_ACTION_VALUE: {
auto m = GetMacro(); auto m = GetMacro();
if (!m) { if (!m) {
return true; return true;
@@ -350,7 +313,7 @@ bool MacroActionVariable::PerformAction()
var->SetValue(segment->GetVariableValue()); var->SetValue(segment->GetVariableValue());
break; break;
} }
case Type::ROUND_TO_INT: { case Action::ROUND_TO_INT: {
auto curValue = var->DoubleValue(); auto curValue = var->DoubleValue();
if (!curValue.has_value()) { if (!curValue.has_value()) {
return true; return true;
@@ -358,7 +321,7 @@ bool MacroActionVariable::PerformAction()
var->SetValue(std::to_string(int(std::round(*curValue)))); var->SetValue(std::to_string(int(std::round(*curValue))));
return true; return true;
} }
case Type::SUBSTRING: { case Action::SUBSTRING: {
if (_subStringRegex.Enabled()) { if (_subStringRegex.Enabled()) {
HandleRegexSubString(var.get()); HandleRegexSubString(var.get());
return true; return true;
@@ -366,15 +329,15 @@ bool MacroActionVariable::PerformAction()
HandleIndexSubString(var.get()); HandleIndexSubString(var.get());
return true; return true;
} }
case Type::FIND_AND_REPLACE: { case Action::FIND_AND_REPLACE: {
HandleFindAndReplace(var.get()); HandleFindAndReplace(var.get());
return true; return true;
} }
case Type::MATH_EXPRESSION: { case Action::MATH_EXPRESSION: {
HandleMathExpression(var.get()); HandleMathExpression(var.get());
return true; return true;
} }
case Type::USER_INPUT: { case Action::USER_INPUT: {
auto params = std::make_shared<AskForInputParams>( auto params = std::make_shared<AskForInputParams>(
_useCustomPrompt _useCustomPrompt
? QString::fromStdString(_inputPrompt) ? QString::fromStdString(_inputPrompt)
@@ -399,22 +362,22 @@ bool MacroActionVariable::PerformAction()
var->SetValue(*params->result); var->SetValue(*params->result);
return true; return true;
} }
case Type::ENV_VARIABLE: { case Action::ENV_VARIABLE: {
auto value = std::getenv(_envVariableName.c_str()); auto value = std::getenv(_envVariableName.c_str());
if (value) { if (value) {
var->SetValue(value); var->SetValue(value);
} }
return true; return true;
} }
case Type::SCENE_ITEM_COUNT: { case Action::SCENE_ITEM_COUNT: {
var->SetValue(GetSceneItemCount(_scene.GetScene(false))); var->SetValue(GetSceneItemCount(_scene.GetScene(false)));
return true; return true;
} }
case Type::STRING_LENGTH: { case Action::STRING_LENGTH: {
var->SetValue(std::string(_strValue).length()); var->SetValue(std::string(_strValue).length());
return true; return true;
} }
case Type::EXTRACT_JSON: { case Action::EXTRACT_JSON: {
auto value = GetJsonField(var->Value(), _strValue); auto value = GetJsonField(var->Value(), _strValue);
if (!value.has_value()) { if (!value.has_value()) {
return true; return true;
@@ -422,7 +385,7 @@ bool MacroActionVariable::PerformAction()
var->SetValue(*value); var->SetValue(*value);
return true; return true;
} }
case Type::SET_TO_TEMPVAR: { case Action::SET_TO_TEMPVAR: {
auto tempVar = _tempVar.GetTempVariable(GetMacro()); auto tempVar = _tempVar.GetTempVariable(GetMacro());
if (!tempVar) { if (!tempVar) {
return true; return true;
@@ -434,18 +397,18 @@ bool MacroActionVariable::PerformAction()
var->SetValue(*value); var->SetValue(*value);
return true; return true;
} }
case Type::SCENE_ITEM_NAME: case Action::SCENE_ITEM_NAME:
SetToSceneItemName(var.get()); SetToSceneItemName(var.get());
return true; return true;
case Type::PAD: case Action::PAD:
var->SetValue(pad(var->Value(), _direction, _stringLength, var->SetValue(pad(var->Value(), _direction, _stringLength,
_paddingChar)); _paddingChar));
return true; return true;
case Type::TRUNCATE: case Action::TRUNCATE:
var->SetValue( var->SetValue(
truncate(var->Value(), _direction, _stringLength)); truncate(var->Value(), _direction, _stringLength));
return true; return true;
case Type::SWAP_VALUES: { case Action::SWAP_VALUES: {
auto var2 = _variable2.lock(); auto var2 = _variable2.lock();
if (!var2) { if (!var2) {
return true; return true;
@@ -456,15 +419,18 @@ bool MacroActionVariable::PerformAction()
var2->SetValue(tempValue); var2->SetValue(tempValue);
return true; return true;
} }
case Type::TRIM: { case Action::TRIM: {
var->SetValue(QString::fromStdString(var->Value()) var->SetValue(QString::fromStdString(var->Value())
.trimmed() .trimmed()
.toStdString()); .toStdString());
return true; return true;
} }
case Type::CHANGE_CASE: case Action::CHANGE_CASE:
HandleCaseChange(var.get()); HandleCaseChange(var.get());
return true; return true;
case Action::RANDOM_NUMBER:
GenerateRandomNumber(var.get());
return true;
} }
return true; return true;
@@ -479,7 +445,7 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
GetWeakVariableName(_variable2).c_str()); GetWeakVariableName(_variable2).c_str());
_strValue.Save(obj, "strValue"); _strValue.Save(obj, "strValue");
_numValue.Save(obj, "numValue"); _numValue.Save(obj, "numValue");
obs_data_set_int(obj, "condition", static_cast<int>(_type)); obs_data_set_int(obj, "condition", static_cast<int>(_action));
obs_data_set_int(obj, "segmentIdx", GetSegmentIndexValue()); obs_data_set_int(obj, "segmentIdx", GetSegmentIndexValue());
_subStringStart.Save(obj, "subStringStart"); _subStringStart.Save(obj, "subStringStart");
_subStringSize.Save(obj, "subStringSize"); _subStringSize.Save(obj, "subStringSize");
@@ -502,6 +468,9 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
_stringLength.Save(obj, "stringLength"); _stringLength.Save(obj, "stringLength");
obs_data_set_int(obj, "paddingChar", _paddingChar); obs_data_set_int(obj, "paddingChar", _paddingChar);
obs_data_set_int(obj, "caseType", static_cast<int>(_caseType)); obs_data_set_int(obj, "caseType", static_cast<int>(_caseType));
_randomNumberStart.Save(obj, "randomNumberStart");
_randomNumberEnd.Save(obj, "randomNumberEnd");
obs_data_set_bool(obj, "generateInteger", _generateInteger);
obs_data_set_int(obj, "version", 1); obs_data_set_int(obj, "version", 1);
@@ -516,7 +485,7 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_variable2 = GetWeakVariableByName( _variable2 = GetWeakVariableByName(
obs_data_get_string(obj, "variable2Name")); obs_data_get_string(obj, "variable2Name"));
_strValue.Load(obj, "strValue"); _strValue.Load(obj, "strValue");
_type = static_cast<Type>(obs_data_get_int(obj, "condition")); _action = static_cast<Action>(obs_data_get_int(obj, "condition"));
_segmentIdxLoadValue = obs_data_get_int(obj, "segmentIdx"); _segmentIdxLoadValue = obs_data_get_int(obj, "segmentIdx");
_subStringRegex.Load(obj); _subStringRegex.Load(obj);
_regexPattern = obs_data_get_string(obj, "regexPattern"); _regexPattern = obs_data_get_string(obj, "regexPattern");
@@ -555,6 +524,10 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_regexMatchIdx.Load(obj, "regexMatchIdx"); _regexMatchIdx.Load(obj, "regexMatchIdx");
} }
_randomNumberStart.Load(obj, "randomNumberStart");
_randomNumberEnd.Load(obj, "randomNumberEnd");
_generateInteger = obs_data_get_bool(obj, "generateInteger");
return true; return true;
} }
@@ -595,11 +568,11 @@ void MacroActionVariable::SetSegmentIndexValue(int value)
} }
std::shared_ptr<MacroSegment> segment; std::shared_ptr<MacroSegment> segment;
if (_type == Type::SET_CONDITION_VALUE) { if (_action == Action::SET_CONDITION_VALUE) {
if (value < (int)m->Conditions().size()) { if (value < (int)m->Conditions().size()) {
segment = m->Conditions().at(value); segment = m->Conditions().at(value);
} }
} else if (_type == Type::SET_ACTION_VALUE) { } else if (_action == Action::SET_ACTION_VALUE) {
if (value < (int)m->Actions().size()) { if (value < (int)m->Actions().size()) {
segment = m->Actions().at(value); segment = m->Actions().at(value);
} }
@@ -623,14 +596,14 @@ int MacroActionVariable::GetSegmentIndexValue() const
return -1; return -1;
} }
if (_type == Type::SET_CONDITION_VALUE) { if (_action == Action::SET_CONDITION_VALUE) {
auto it = std::find(m->Conditions().begin(), auto it = std::find(m->Conditions().begin(),
m->Conditions().end(), segment); m->Conditions().end(), segment);
if (it != m->Conditions().end()) { if (it != m->Conditions().end()) {
return std::distance(m->Conditions().begin(), it); return std::distance(m->Conditions().begin(), it);
} }
return -1; return -1;
} else if (_type == Type::SET_ACTION_VALUE) { } else if (_action == Action::SET_ACTION_VALUE) {
auto it = std::find(m->Actions().begin(), m->Actions().end(), auto it = std::find(m->Actions().begin(), m->Actions().end(),
segment); segment);
if (it != m->Actions().end()) { if (it != m->Actions().end()) {
@@ -665,16 +638,99 @@ void MacroActionVariable::DecrementCurrentSegmentVariableRef()
DecrementVariableRef(segment.get()); DecrementVariableRef(segment.get());
} }
static inline void populateTypeSelection(QComboBox *list) static inline void populateActionSelection(QComboBox *list)
{ {
for (const auto &[action, name] : actionTypes) { static const std::vector<std::pair<
list->addItem(obs_module_text(name.c_str()), std::variant<MacroActionVariable::Action, bool>, std::string>>
static_cast<int>(action)); actions = {
{MacroActionVariable::Action::SET_VALUE,
"AdvSceneSwitcher.action.variable.type.set"},
{MacroActionVariable::Action::APPEND,
"AdvSceneSwitcher.action.variable.type.append"},
{MacroActionVariable::Action::PAD,
"AdvSceneSwitcher.action.variable.type.padValue"},
{MacroActionVariable::Action::TRUNCATE,
"AdvSceneSwitcher.action.variable.type.truncateValue"},
{MacroActionVariable::Action::SUBSTRING,
"AdvSceneSwitcher.action.variable.type.subString"},
{MacroActionVariable::Action::FIND_AND_REPLACE,
"AdvSceneSwitcher.action.variable.type.findAndReplace"},
{MacroActionVariable::Action::EXTRACT_JSON,
"AdvSceneSwitcher.action.variable.type.extractJson"},
{MacroActionVariable::Action::STRING_LENGTH,
"AdvSceneSwitcher.action.variable.type.stringLength"},
{MacroActionVariable::Action::TRIM,
"AdvSceneSwitcher.action.variable.type.trim"},
{MacroActionVariable::Action::CHANGE_CASE,
"AdvSceneSwitcher.action.variable.type.changeCase"},
{true, ""}, // Separator
{MacroActionVariable::Action::SET_TO_TEMPVAR,
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
{MacroActionVariable::Action::SET_CONDITION_VALUE,
"AdvSceneSwitcher.action.variable.type.setConditionValue"},
{MacroActionVariable::Action::SET_ACTION_VALUE,
"AdvSceneSwitcher.action.variable.type.setActionValue"},
{true, ""}, // Separator
{MacroActionVariable::Action::MATH_EXPRESSION,
"AdvSceneSwitcher.action.variable.type.mathExpression"},
{MacroActionVariable::Action::INCREMENT,
"AdvSceneSwitcher.action.variable.type.increment"},
{MacroActionVariable::Action::DECREMENT,
"AdvSceneSwitcher.action.variable.type.decrement"},
{MacroActionVariable::Action::ROUND_TO_INT,
"AdvSceneSwitcher.action.variable.type.roundToInt"},
{MacroActionVariable::Action::RANDOM_NUMBER,
"AdvSceneSwitcher.action.variable.type.randomNumber"},
{true, ""}, // Separator
{MacroActionVariable::Action::USER_INPUT,
"AdvSceneSwitcher.action.variable.type.askForValue"},
{MacroActionVariable::Action::ENV_VARIABLE,
"AdvSceneSwitcher.action.variable.type.environmentVariable"},
{true, ""}, // Separator
{MacroActionVariable::Action::SCENE_ITEM_NAME,
"AdvSceneSwitcher.action.variable.type.sceneItemName"},
{MacroActionVariable::Action::SCENE_ITEM_COUNT,
"AdvSceneSwitcher.action.variable.type.sceneItemCount"},
{true, ""}, // Separator
{MacroActionVariable::Action::SWAP_VALUES,
"AdvSceneSwitcher.action.variable.type.swapValues"},
{MacroActionVariable::Action::APPEND_VAR,
"AdvSceneSwitcher.action.variable.type.appendVar"},
};
for (const auto &[action, name] : actions) {
if (std::holds_alternative<bool>(action)) {
list->insertSeparator(actions.size());
continue;
}
list->addItem(
obs_module_text(name.c_str()),
static_cast<int>(
std::get<MacroActionVariable::Action>(action)));
} }
} }
static inline void populateCaseTypeSelection(QComboBox *list) static inline void populateCaseTypeSelection(QComboBox *list)
{ {
const static std::map<MacroActionVariable::CaseType, std::string>
caseTypes = {
{MacroActionVariable::CaseType::LOWER_CASE,
"AdvSceneSwitcher.action.variable.case.type.lowerCase"},
{MacroActionVariable::CaseType::UPPER_CASE,
"AdvSceneSwitcher.action.variable.case.type.upperCase"},
{MacroActionVariable::CaseType::CAPITALIZED,
"AdvSceneSwitcher.action.variable.case.type.capitalized"},
{MacroActionVariable::CaseType::START_CASE,
"AdvSceneSwitcher.action.variable.case.type.startCase"},
};
for (const auto &[type, name] : caseTypes) { for (const auto &[type, name] : caseTypes) {
list->addItem(obs_module_text(name.c_str()), list->addItem(obs_module_text(name.c_str()),
static_cast<int>(type)); static_cast<int>(type));
@@ -734,6 +790,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_stringLength(new VariableSpinBox(this)), _stringLength(new VariableSpinBox(this)),
_paddingCharSelection(new SingleCharSelection()), _paddingCharSelection(new SingleCharSelection()),
_caseType(new FilterComboBox(this)), _caseType(new FilterComboBox(this)),
_randomNumberStart(new VariableDoubleSpinBox(this)),
_randomNumberEnd(new VariableDoubleSpinBox(this)),
_generateInteger(new QCheckBox(
obs_module_text(
"AdvSceneSwitcher.action.variable.generateInteger"),
this)),
_randomLayout(new QVBoxLayout()),
_entryLayout(new QHBoxLayout()) _entryLayout(new QHBoxLayout())
{ {
_numValue->setMinimum(-9999999999); _numValue->setMinimum(-9999999999);
@@ -756,9 +819,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
QSizePolicy::Preferred); QSizePolicy::Preferred);
_sceneItemIndex->setMinimum(1); _sceneItemIndex->setMinimum(1);
_stringLength->setMaximum(999); _stringLength->setMaximum(999);
populateTypeSelection(_actions); populateActionSelection(_actions);
populateDirectionSelection(_direction); populateDirectionSelection(_direction);
populateCaseTypeSelection(_caseType); populateCaseTypeSelection(_caseType);
_randomNumberStart->setMinimum(-9999999999);
_randomNumberStart->setMaximum(9999999999);
_randomNumberEnd->setMinimum(-9999999999);
_randomNumberEnd->setMaximum(9999999999);
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)), QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
this, SLOT(VariableChanged(const QString &))); this, SLOT(VariableChanged(const QString &)));
@@ -833,6 +900,18 @@ MacroActionVariableEdit::MacroActionVariableEdit(
SLOT(CharSelectionChanged(const QString &))); SLOT(CharSelectionChanged(const QString &)));
QWidget::connect(_caseType, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_caseType, SIGNAL(currentIndexChanged(int)), this,
SLOT(CaseTypeChanged(int))); SLOT(CaseTypeChanged(int)));
QWidget::connect(
_randomNumberStart,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this,
SLOT(RandomNumberStartChanged(const NumberVariable<double> &)));
QWidget::connect(
_randomNumberEnd,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this,
SLOT(RandomNumberEndChanged(const NumberVariable<double> &)));
QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this,
SLOT(GenerateIntegerChanged(int)));
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = { const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{variables}}", _variables}, {"{{variables}}", _variables},
@@ -861,6 +940,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
{"{{stringLength}}", _stringLength}, {"{{stringLength}}", _stringLength},
{"{{paddingCharSelection}}", _paddingCharSelection}, {"{{paddingCharSelection}}", _paddingCharSelection},
{"{{caseType}}", _caseType}, {"{{caseType}}", _caseType},
{"{{randomNumberStart}}", _randomNumberStart},
{"{{randomNumberEnd}}", _randomNumberEnd},
{"{{generateInteger}}", _generateInteger},
}; };
PlaceWidgets( PlaceWidgets(
obs_module_text("AdvSceneSwitcher.action.variable.entry.other"), obs_module_text("AdvSceneSwitcher.action.variable.entry.other"),
@@ -890,6 +972,14 @@ MacroActionVariableEdit::MacroActionVariableEdit(
"AdvSceneSwitcher.action.variable.entry.userInput.placeholder"), "AdvSceneSwitcher.action.variable.entry.userInput.placeholder"),
_placeholderLayout, widgetPlaceholders); _placeholderLayout, widgetPlaceholders);
auto randomLayout = new QHBoxLayout();
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.variable.entry.randomNumber"),
randomLayout, widgetPlaceholders);
_randomLayout->addLayout(randomLayout);
_randomLayout->addWidget(_generateInteger);
auto regexConfigLayout = new QHBoxLayout; auto regexConfigLayout = new QHBoxLayout;
regexConfigLayout->addWidget(_substringRegex); regexConfigLayout->addWidget(_substringRegex);
regexConfigLayout->addStretch(); regexConfigLayout->addStretch();
@@ -908,6 +998,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
layout->addWidget(_mathExpressionResult); layout->addWidget(_mathExpressionResult);
layout->addLayout(_promptLayout); layout->addLayout(_promptLayout);
layout->addLayout(_placeholderLayout); layout->addLayout(_placeholderLayout);
layout->addLayout(_randomLayout);
setLayout(layout); setLayout(layout);
_entryData = entryData; _entryData = entryData;
@@ -929,14 +1020,14 @@ void MacroActionVariableEdit::UpdateEntryData()
_variables->SetVariable(_entryData->_variable); _variables->SetVariable(_entryData->_variable);
_variables2->SetVariable(_entryData->_variable2); _variables2->SetVariable(_entryData->_variable2);
_actions->setCurrentIndex( _actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->_type))); _actions->findData(static_cast<int>(_entryData->_action)));
_strValue->setPlainText(_entryData->_strValue); _strValue->setPlainText(_entryData->_strValue);
_numValue->SetValue(_entryData->_numValue); _numValue->SetValue(_entryData->_numValue);
_segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1); _segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1);
_segmentIdx->SetMacro(_entryData->GetMacro()); _segmentIdx->SetMacro(_entryData->GetMacro());
_segmentIdx->SetType( _segmentIdx->SetType(
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_CONDITION_VALUE MacroActionVariable::Action::SET_CONDITION_VALUE
? MacroSegmentSelection::Type::CONDITION ? MacroSegmentSelection::Type::CONDITION
: MacroSegmentSelection::Type::ACTION); : MacroSegmentSelection::Type::ACTION);
_subStringStart->SetValue(_entryData->_subStringStart); _subStringStart->SetValue(_entryData->_subStringStart);
@@ -963,6 +1054,9 @@ void MacroActionVariableEdit::UpdateEntryData()
QChar::fromLatin1(_entryData->_paddingChar)); QChar::fromLatin1(_entryData->_paddingChar));
_caseType->setCurrentIndex( _caseType->setCurrentIndex(
_caseType->findData(static_cast<int>(_entryData->_caseType))); _caseType->findData(static_cast<int>(_entryData->_caseType)));
_randomNumberStart->SetValue(_entryData->_randomNumberStart);
_randomNumberEnd->SetValue(_entryData->_randomNumberEnd);
_generateInteger->setChecked(_entryData->_generateInteger);
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -996,13 +1090,14 @@ void MacroActionVariableEdit::ActionChanged(int idx)
} }
auto lock = LockContext(); auto lock = LockContext();
_entryData->_type = static_cast<MacroActionVariable::Type>( _entryData->_action = static_cast<MacroActionVariable::Action>(
_actions->itemData(idx).toInt()); _actions->itemData(idx).toInt());
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE) { if (_entryData->_action ==
MacroActionVariable::Action::SET_ACTION_VALUE) {
_segmentIdx->SetType(MacroSegmentSelection::Type::ACTION); _segmentIdx->SetType(MacroSegmentSelection::Type::ACTION);
} else if (_entryData->_type == } else if (_entryData->_action ==
MacroActionVariable::Type::SET_CONDITION_VALUE) { MacroActionVariable::Action::SET_CONDITION_VALUE) {
_segmentIdx->SetType(MacroSegmentSelection::Type::CONDITION); _segmentIdx->SetType(MacroSegmentSelection::Type::CONDITION);
} }
SetWidgetVisibility(); SetWidgetVisibility();
@@ -1054,10 +1149,10 @@ void MacroActionVariableEdit::SetSegmentValueError(const QString &text)
void MacroActionVariableEdit::UpdateSegmentVariableValue() void MacroActionVariableEdit::UpdateSegmentVariableValue()
{ {
if (!_entryData || if (!_entryData ||
!(_entryData->_type == !(_entryData->_action ==
MacroActionVariable::Type::SET_CONDITION_VALUE || MacroActionVariable::Action::SET_CONDITION_VALUE ||
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_ACTION_VALUE)) { MacroActionVariable::Action::SET_ACTION_VALUE)) {
return; return;
} }
@@ -1076,13 +1171,14 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
} }
std::shared_ptr<MacroSegment> segment; std::shared_ptr<MacroSegment> segment;
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE) { if (_entryData->_action ==
MacroActionVariable::Action::SET_ACTION_VALUE) {
const auto &actions = m->Actions(); const auto &actions = m->Actions();
if (index < (int)actions.size()) { if (index < (int)actions.size()) {
segment = actions.at(index); segment = actions.at(index);
} }
} else if (_entryData->_type == } else if (_entryData->_action ==
MacroActionVariable::Type::SET_CONDITION_VALUE) { MacroActionVariable::Action::SET_CONDITION_VALUE) {
const auto &conditions = m->Conditions(); const auto &conditions = m->Conditions();
if (index < (int)conditions.size()) { if (index < (int)conditions.size()) {
segment = conditions.at(index); segment = conditions.at(index);
@@ -1099,14 +1195,14 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
std::string type; std::string type;
QString fmt; QString fmt;
if (_entryData->_type == if (_entryData->_action ==
MacroActionVariable::Type::SET_ACTION_VALUE) { MacroActionVariable::Action::SET_ACTION_VALUE) {
type = MacroActionFactory::GetActionName( type = MacroActionFactory::GetActionName(
segment->GetId()); segment->GetId());
fmt = QString(obs_module_text( fmt = QString(obs_module_text(
"AdvSceneSwitcher.action.variable.actionNoVariableSupport")); "AdvSceneSwitcher.action.variable.actionNoVariableSupport"));
} else if (_entryData->_type == } else if (_entryData->_action ==
MacroActionVariable::Type::SET_CONDITION_VALUE) { MacroActionVariable::Action::SET_CONDITION_VALUE) {
type = MacroConditionFactory::GetConditionName( type = MacroConditionFactory::GetConditionName(
segment->GetId()); segment->GetId());
fmt = QString(obs_module_text( fmt = QString(obs_module_text(
@@ -1385,6 +1481,26 @@ void MacroActionVariableEdit::CaseTypeChanged(int index)
_caseType->itemData(index).toInt()); _caseType->itemData(index).toInt());
} }
void MacroActionVariableEdit::RandomNumberStartChanged(
const NumberVariable<double> &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_randomNumberStart = value;
}
void MacroActionVariableEdit::RandomNumberEndChanged(
const NumberVariable<double> &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_randomNumberEnd = value;
}
void MacroActionVariableEdit::GenerateIntegerChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_generateInteger = value;
}
void MacroActionVariableEdit::SetWidgetVisibility() void MacroActionVariableEdit::SetWidgetVisibility()
{ {
if (!_entryData) { if (!_entryData) {
@@ -1411,10 +1527,11 @@ void MacroActionVariableEdit::SetWidgetVisibility()
}; };
const char *layoutString = ""; const char *layoutString = "";
if (_entryData->_type == MacroActionVariable::Type::PAD) { if (_entryData->_action == MacroActionVariable::Action::PAD) {
layoutString = obs_module_text( layoutString = obs_module_text(
"AdvSceneSwitcher.action.variable.entry.pad"); "AdvSceneSwitcher.action.variable.entry.pad");
} else if (_entryData->_type == MacroActionVariable::Type::TRUNCATE) { } else if (_entryData->_action ==
MacroActionVariable::Action::TRUNCATE) {
layoutString = obs_module_text( layoutString = obs_module_text(
"AdvSceneSwitcher.action.variable.entry.truncate"); "AdvSceneSwitcher.action.variable.entry.truncate");
} else { } else {
@@ -1429,64 +1546,70 @@ void MacroActionVariableEdit::SetWidgetVisibility()
ClearLayout(_entryLayout); ClearLayout(_entryLayout);
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders); PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
if (_entryData->_type == MacroActionVariable::Type::SET_FIXED_VALUE || if (_entryData->_action == MacroActionVariable::Action::SET_VALUE ||
_entryData->_type == MacroActionVariable::Type::APPEND || _entryData->_action == MacroActionVariable::Action::APPEND ||
_entryData->_type == MacroActionVariable::Type::MATH_EXPRESSION || _entryData->_action ==
_entryData->_type == MacroActionVariable::Type::ENV_VARIABLE || MacroActionVariable::Action::MATH_EXPRESSION ||
_entryData->_type == MacroActionVariable::Type::STRING_LENGTH || _entryData->_action == MacroActionVariable::Action::ENV_VARIABLE ||
_entryData->_type == MacroActionVariable::Type::EXTRACT_JSON) { _entryData->_action == MacroActionVariable::Action::STRING_LENGTH ||
_entryData->_action == MacroActionVariable::Action::EXTRACT_JSON) {
RemoveStretchIfPresent(_entryLayout); RemoveStretchIfPresent(_entryLayout);
} else { } else {
AddStretchIfNecessary(_entryLayout); AddStretchIfNecessary(_entryLayout);
} }
_variables2->setVisible( _variables2->setVisible(
_entryData->_type == MacroActionVariable::Type::APPEND_VAR || _entryData->_action ==
_entryData->_type == MacroActionVariable::Type::SWAP_VALUES); MacroActionVariable::Action::APPEND_VAR ||
_entryData->_action ==
MacroActionVariable::Action::SWAP_VALUES);
_strValue->setVisible( _strValue->setVisible(
_entryData->_type == _entryData->_action == MacroActionVariable::Action::SET_VALUE ||
MacroActionVariable::Type::SET_FIXED_VALUE || _entryData->_action == MacroActionVariable::Action::APPEND ||
_entryData->_type == MacroActionVariable::Type::APPEND || _entryData->_action ==
_entryData->_type == MacroActionVariable::Type::STRING_LENGTH || MacroActionVariable::Action::STRING_LENGTH ||
_entryData->_type == MacroActionVariable::Type::EXTRACT_JSON); _entryData->_action ==
MacroActionVariable::Action::EXTRACT_JSON);
_numValue->setVisible( _numValue->setVisible(
_entryData->_type == MacroActionVariable::Type::INCREMENT || _entryData->_action == MacroActionVariable::Action::INCREMENT ||
_entryData->_type == MacroActionVariable::Type::DECREMENT); _entryData->_action == MacroActionVariable::Action::DECREMENT);
_segmentValueStatus->setVisible( _segmentValueStatus->setVisible(
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_ACTION_VALUE || MacroActionVariable::Action::SET_ACTION_VALUE ||
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_CONDITION_VALUE); MacroActionVariable::Action::SET_CONDITION_VALUE);
_segmentValue->setVisible( _segmentValue->setVisible(
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_ACTION_VALUE || MacroActionVariable::Action::SET_ACTION_VALUE ||
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_CONDITION_VALUE); MacroActionVariable::Action::SET_CONDITION_VALUE);
_segmentIdx->setVisible( _segmentIdx->setVisible(
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_ACTION_VALUE || MacroActionVariable::Action::SET_ACTION_VALUE ||
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_CONDITION_VALUE); MacroActionVariable::Action::SET_CONDITION_VALUE);
SetLayoutVisible(_substringLayout, SetLayoutVisible(_substringLayout,
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SUBSTRING); MacroActionVariable::Action::SUBSTRING);
if (_entryData->_type == MacroActionVariable::Type::SUBSTRING) { if (_entryData->_action == MacroActionVariable::Action::SUBSTRING) {
bool showRegex = _entryData->_subStringRegex.Enabled(); bool showRegex = _entryData->_subStringRegex.Enabled();
SetLayoutVisible(_subStringIndexEntryLayout, !showRegex); SetLayoutVisible(_subStringIndexEntryLayout, !showRegex);
SetLayoutVisible(_subStringRegexEntryLayout, showRegex); SetLayoutVisible(_subStringRegexEntryLayout, showRegex);
_regexPattern->setVisible(showRegex); _regexPattern->setVisible(showRegex);
} }
SetLayoutVisible(_findReplaceLayout, SetLayoutVisible(_findReplaceLayout,
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::FIND_AND_REPLACE); MacroActionVariable::Action::FIND_AND_REPLACE);
_mathExpression->setVisible(_entryData->_type == _mathExpression->setVisible(
MacroActionVariable::Type::MATH_EXPRESSION); _entryData->_action ==
MacroActionVariable::Action::MATH_EXPRESSION);
_mathExpressionResult->hide(); _mathExpressionResult->hide();
SetLayoutVisible(_promptLayout, SetLayoutVisible(_promptLayout,
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::USER_INPUT); MacroActionVariable::Action::USER_INPUT);
_inputPrompt->setVisible( _inputPrompt->setVisible(
_entryData->_type == MacroActionVariable::Type::USER_INPUT && _entryData->_action ==
MacroActionVariable::Action::USER_INPUT &&
_entryData->_useCustomPrompt); _entryData->_useCustomPrompt);
if (_entryData->_useCustomPrompt) { if (_entryData->_useCustomPrompt) {
RemoveStretchIfPresent(_promptLayout); RemoveStretchIfPresent(_promptLayout);
@@ -1495,13 +1618,16 @@ void MacroActionVariableEdit::SetWidgetVisibility()
} }
SetLayoutVisible( SetLayoutVisible(
_placeholderLayout, _placeholderLayout,
_entryData->_type == MacroActionVariable::Type::USER_INPUT && _entryData->_action ==
MacroActionVariable::Action::USER_INPUT &&
_entryData->_useCustomPrompt); _entryData->_useCustomPrompt);
_useInputPlaceholder->setVisible( _useInputPlaceholder->setVisible(
_entryData->_type == MacroActionVariable::Type::USER_INPUT && _entryData->_action ==
MacroActionVariable::Action::USER_INPUT &&
_entryData->_useCustomPrompt); _entryData->_useCustomPrompt);
_inputPlaceholder->setVisible( _inputPlaceholder->setVisible(
_entryData->_type == MacroActionVariable::Type::USER_INPUT && _entryData->_action ==
MacroActionVariable::Action::USER_INPUT &&
_entryData->_useCustomPrompt && _entryData->_useCustomPrompt &&
_entryData->_useInputPlaceholder); _entryData->_useInputPlaceholder);
if (_entryData->_useInputPlaceholder) { if (_entryData->_useInputPlaceholder) {
@@ -1509,31 +1635,35 @@ void MacroActionVariableEdit::SetWidgetVisibility()
} else { } else {
AddStretchIfNecessary(_placeholderLayout); AddStretchIfNecessary(_placeholderLayout);
} }
_envVariable->setVisible(_entryData->_type == _envVariable->setVisible(_entryData->_action ==
MacroActionVariable::Type::ENV_VARIABLE); MacroActionVariable::Action::ENV_VARIABLE);
_scenes->setVisible( _scenes->setVisible(
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SCENE_ITEM_COUNT || MacroActionVariable::Action::SCENE_ITEM_COUNT ||
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SCENE_ITEM_NAME); MacroActionVariable::Action::SCENE_ITEM_NAME);
_tempVars->setVisible(_entryData->_type == _tempVars->setVisible(_entryData->_action ==
MacroActionVariable::Type::SET_TO_TEMPVAR); MacroActionVariable::Action::SET_TO_TEMPVAR);
_tempVarsHelp->setVisible( _tempVarsHelp->setVisible(
_entryData->_type == _entryData->_action ==
MacroActionVariable::Type::SET_TO_TEMPVAR && MacroActionVariable::Action::SET_TO_TEMPVAR &&
!_entryData->_tempVar.HasValidID()); !_entryData->_tempVar.HasValidID());
_sceneItemIndex->setVisible(_entryData->_type == _sceneItemIndex->setVisible(
MacroActionVariable::Type::SCENE_ITEM_NAME); _entryData->_action ==
MacroActionVariable::Action::SCENE_ITEM_NAME);
_direction->setVisible( _direction->setVisible(
_entryData->_type == MacroActionVariable::Type::PAD || _entryData->_action == MacroActionVariable::Action::PAD ||
_entryData->_type == MacroActionVariable::Type::TRUNCATE); _entryData->_action == MacroActionVariable::Action::TRUNCATE);
_stringLength->setVisible( _stringLength->setVisible(
_entryData->_type == MacroActionVariable::Type::PAD || _entryData->_action == MacroActionVariable::Action::PAD ||
_entryData->_type == MacroActionVariable::Type::TRUNCATE); _entryData->_action == MacroActionVariable::Action::TRUNCATE);
_paddingCharSelection->setVisible(_entryData->_type == _paddingCharSelection->setVisible(_entryData->_action ==
MacroActionVariable::Type::PAD); MacroActionVariable::Action::PAD);
_caseType->setVisible(_entryData->_type == _caseType->setVisible(_entryData->_action ==
MacroActionVariable::Type::CHANGE_CASE); MacroActionVariable::Action::CHANGE_CASE);
SetLayoutVisible(_randomLayout,
_entryData->_action ==
MacroActionVariable::Action::RANDOM_NUMBER);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();

View File

@@ -28,8 +28,8 @@ public:
int GetSegmentIndexValue() const; int GetSegmentIndexValue() const;
void ResolveVariablesToFixedValues(); void ResolveVariablesToFixedValues();
enum class Type { enum class Action {
SET_FIXED_VALUE, SET_VALUE,
APPEND, APPEND,
APPEND_VAR, APPEND_VAR,
INCREMENT, INCREMENT,
@@ -52,9 +52,10 @@ public:
SWAP_VALUES, SWAP_VALUES,
TRIM, TRIM,
CHANGE_CASE, CHANGE_CASE,
RANDOM_NUMBER,
}; };
Type _type = Type::SET_FIXED_VALUE; Action _action = Action::SET_VALUE;
std::weak_ptr<Variable> _variable; std::weak_ptr<Variable> _variable;
std::weak_ptr<Variable> _variable2; std::weak_ptr<Variable> _variable2;
StringVariable _strValue = ""; StringVariable _strValue = "";
@@ -99,6 +100,9 @@ public:
}; };
CaseType _caseType = CaseType::LOWER_CASE; CaseType _caseType = CaseType::LOWER_CASE;
DoubleVariable _randomNumberStart = 0;
DoubleVariable _randomNumberEnd = 100;
bool _generateInteger = true;
private: private:
void DecrementCurrentSegmentVariableRef(); void DecrementCurrentSegmentVariableRef();
@@ -108,6 +112,7 @@ private:
void HandleMathExpression(Variable *); void HandleMathExpression(Variable *);
void HandleCaseChange(Variable *); void HandleCaseChange(Variable *);
void SetToSceneItemName(Variable *); void SetToSceneItemName(Variable *);
void GenerateRandomNumber(Variable *);
std::weak_ptr<MacroSegment> _macroSegment; std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1; int _segmentIdxLoadValue = -1;
@@ -161,6 +166,9 @@ private slots:
void StringLengthChanged(const NumberVariable<int> &); void StringLengthChanged(const NumberVariable<int> &);
void CharSelectionChanged(const QString &); void CharSelectionChanged(const QString &);
void CaseTypeChanged(int index); void CaseTypeChanged(int index);
void RandomNumberStartChanged(const NumberVariable<double> &);
void RandomNumberEndChanged(const NumberVariable<double> &);
void GenerateIntegerChanged(int);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -206,6 +214,10 @@ private:
VariableSpinBox *_stringLength; VariableSpinBox *_stringLength;
SingleCharSelection *_paddingCharSelection; SingleCharSelection *_paddingCharSelection;
FilterComboBox *_caseType; FilterComboBox *_caseType;
VariableDoubleSpinBox *_randomNumberStart;
VariableDoubleSpinBox *_randomNumberEnd;
QCheckBox *_generateInteger;
QVBoxLayout *_randomLayout;
QHBoxLayout *_entryLayout; QHBoxLayout *_entryLayout;
std::shared_ptr<MacroActionVariable> _entryData; std::shared_ptr<MacroActionVariable> _entryData;

View File

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

View File

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

View File

@@ -109,6 +109,14 @@ bool MacroWasPausedSince(
return macro ? macro->WasPausedSince(time) : false; return macro ? macro->WasPausedSince(time) : false;
} }
bool MacroWasCheckedSinceLastStart(Macro *macro)
{
if (!macro) {
return false;
}
return macro->LastConditionCheckTime().time_since_epoch().count() != 0;
}
void AddMacroHelperThread(Macro *macro, std::thread &&newThread) void AddMacroHelperThread(Macro *macro, std::thread &&newThread)
{ {
if (!macro) { if (!macro) {

View File

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

View File

@@ -181,15 +181,7 @@ void MacroInputEdit::SetInputVariablesAndValues(
_variables = inputs; _variables = inputs;
_values = values; _values = values;
if ((size_t)_values.size() < _variables._inputVariables.size()) { if ((size_t)_values.size() < _variables._inputVariables.size()) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
for (int i = 0;
_variables._inputVariables.size() - (size_t)_values.size();
i++) {
_values.push_back({});
}
#else
_values.resize(_variables._inputVariables.size()); _values.resize(_variables._inputVariables.size());
#endif
} }
SetupWidgets(); SetupWidgets();
} }
@@ -199,7 +191,7 @@ bool MacroInputEdit::HasInputsToSet() const
return !_variables._inputVariables.empty(); return !_variables._inputVariables.empty();
} }
void HighligthMacroSettingsButton(bool enable); void HighlightMacroSettingsButton(bool enable);
void MacroInputEdit::SetupWidgets() void MacroInputEdit::SetupWidgets()
{ {

View File

@@ -29,6 +29,10 @@ static constexpr std::string_view defaultSettingsParam = "default_settings";
static constexpr std::string_view propertiesSignalParam = static constexpr std::string_view propertiesSignalParam =
"properties_signal_name"; "properties_signal_name";
static constexpr std::string_view triggerSignalParam = "trigger_signal_name"; static constexpr std::string_view triggerSignalParam = "trigger_signal_name";
static constexpr std::string_view newInstanceSignalNameParam =
"new_instance_signal_name";
static constexpr std::string_view deletedInstanceSignalNameParam =
"deleted_instance_signal_name";
static std::string getRegisterScriptSegmentDeclString(const char *funcName) static std::string getRegisterScriptSegmentDeclString(const char *funcName)
{ {
@@ -72,16 +76,37 @@ static const std::string deregisterScriptConditionDeclString =
/* Script variables */ /* Script variables */
static constexpr std::string_view valueParam = "value"; static constexpr std::string_view valueParam = "value";
static constexpr std::string_view tempVarIdParam = "temp_var_id";
static constexpr std::string_view tempVarNameParam = "temp_var_name";
static constexpr std::string_view tempVarHelpParam = "temp_var_help";
static constexpr std::string_view getVariableValueFuncName = static constexpr std::string_view getVariableValueFuncName =
"advss_get_variable_value"; "advss_get_variable_value";
static constexpr std::string_view setVariableValueFuncName = static constexpr std::string_view setVariableValueFuncName =
"advss_set_variable_value"; "advss_set_variable_value";
static constexpr std::string_view registerTempVarFuncName =
"advss_register_temp_var";
static constexpr std::string_view deregisterAllTempVarsFuncName =
"advss_deregister_temp_vars";
static constexpr std::string_view setTempVarValueFuncName =
"advss_set_temp_var_value";
static const std::string getVariableValueDeclString = static const std::string getVariableValueDeclString =
std::string("bool ") + getVariableValueFuncName.data() + "(in string " + std::string("bool ") + getVariableValueFuncName.data() + "(in string " +
nameParam.data() + ", out string " + valueParam.data() + ")"; nameParam.data() + ", out string " + valueParam.data() + ")";
static const std::string setVariableValueDeclString = static const std::string setVariableValueDeclString =
std::string("bool ") + setVariableValueFuncName.data() + "(in string " + std::string("bool ") + setVariableValueFuncName.data() + "(in string " +
nameParam.data() + ", in string " + valueParam.data() + ")"; nameParam.data() + ", in string " + valueParam.data() + ")";
static const std::string registerTempVarDeclString =
std::string("bool ") + registerTempVarFuncName.data() + "(in string " +
tempVarIdParam.data() + ", in string " + tempVarNameParam.data() +
", in string " + tempVarHelpParam.data() + ", in int " +
GetInstanceIdParamName().data() + ")";
static const std::string deregisterAllTempVarsDeclString =
std::string("bool ") + deregisterAllTempVarsFuncName.data() +
"(in int " + GetInstanceIdParamName().data() + ")";
static const std::string setTempVarValueDeclString =
std::string("bool ") + setTempVarValueFuncName.data() + "(in string " +
tempVarIdParam.data() + ", in string " + valueParam.data() +
", in int " + GetInstanceIdParamName().data() + ")";
static bool setup(); static bool setup();
static bool setupDone = setup(); static bool setupDone = setup();
@@ -103,13 +128,23 @@ static bool setup()
&ScriptHandler::GetVariableValue, nullptr); &ScriptHandler::GetVariableValue, nullptr);
proc_handler_add(ph, setVariableValueDeclString.c_str(), proc_handler_add(ph, setVariableValueDeclString.c_str(),
&ScriptHandler::SetVariableValue, nullptr); &ScriptHandler::SetVariableValue, nullptr);
proc_handler_add(ph, registerTempVarDeclString.c_str(),
&ScriptHandler::RegisterTempVar, nullptr);
proc_handler_add(ph, deregisterAllTempVarsDeclString.c_str(),
&ScriptHandler::DeregisterAllTempVars, nullptr);
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
&ScriptHandler::SetTempVarValue, nullptr);
return true; return true;
} }
static void replaceWhitespace(std::string &string) static void replaceProblematicChars(std::string &string)
{ {
std::transform(string.begin(), string.end(), string.begin(), [](char c) { std::transform(string.begin(), string.end(), string.begin(), [](char c) {
return std::isspace(static_cast<unsigned char>(c)) ? '_' : c; if (std::isspace(static_cast<unsigned char>(c)) || c == '(' ||
c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
return '_';
}
return c;
}); });
} }
@@ -122,7 +157,7 @@ static std::string getTriggerSignal(const std::string &name,
const bool isAction) const bool isAction)
{ {
std::string signal = name; std::string signal = name;
replaceWhitespace(signal); replaceProblematicChars(signal);
signal += "_run"; signal += "_run";
signal += isAction ? "_action" : "_condition"; signal += isAction ? "_action" : "_condition";
return signal; return signal;
@@ -140,12 +175,32 @@ static std::string getPropertiesSignal(const std::string &name,
const bool isAction) const bool isAction)
{ {
std::string signal = name; std::string signal = name;
replaceWhitespace(signal); replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition"; signal += isAction ? "_action" : "_condition";
signal += "_get_properties"; signal += "_get_properties";
return signal; return signal;
} }
static std::string getNewInstanceSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition";
signal += "_new_instance";
return signal;
}
static std::string getDeletedInstanceSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition";
signal += "_deleted_instance";
return signal;
}
void ScriptHandler::RegisterScriptAction(void *, calldata_t *data) void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
{ {
const char *actionName; const char *actionName;
@@ -177,14 +232,19 @@ void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
auto triggerSignalName = getTriggerSignal(actionName, true); auto triggerSignalName = getTriggerSignal(actionName, true);
auto completionSignalName = getCompletionSignal(actionName, true); auto completionSignalName = getCompletionSignal(actionName, true);
auto propertiesSignalName = getPropertiesSignal(actionName, true); auto propertiesSignalName = getPropertiesSignal(actionName, true);
auto newInstanceSignalName = getNewInstanceSignal(actionName, true);
auto deletedInstanceSignalName =
getDeletedInstanceSignal(actionName, true);
const auto createScriptAction = const auto createScriptAction =
[id, defaultSettings, propertiesSignalName, triggerSignalName, [id, defaultSettings, propertiesSignalName, triggerSignalName,
completionSignalName]( completionSignalName, newInstanceSignalName,
deletedInstanceSignalName](
Macro *m) -> std::shared_ptr<MacroAction> { Macro *m) -> std::shared_ptr<MacroAction> {
return std::make_shared<MacroActionScript>( return std::make_shared<MacroActionScript>(
m, id, defaultSettings, propertiesSignalName, m, id, defaultSettings, propertiesSignalName,
triggerSignalName, completionSignalName); triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName);
}; };
if (!MacroActionFactory::Register(id, {createScriptAction, if (!MacroActionFactory::Register(id, {createScriptAction,
MacroSegmentScriptEdit::Create, MacroSegmentScriptEdit::Create,
@@ -202,9 +262,15 @@ void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
triggerSignalName.c_str()); triggerSignalName.c_str());
calldata_set_string(data, propertiesSignalParam.data(), calldata_set_string(data, propertiesSignalParam.data(),
propertiesSignalName.c_str()); propertiesSignalName.c_str());
calldata_set_string(data, newInstanceSignalNameParam.data(),
newInstanceSignalName.c_str());
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
deletedInstanceSignalName.c_str());
_actions.emplace(id, ScriptSegmentType(id, propertiesSignalName, _actions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
triggerSignalName, triggerSignalName,
completionSignalName)); completionSignalName,
newInstanceSignalName,
deletedInstanceSignalName));
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@@ -279,14 +345,19 @@ void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
auto triggerSignalName = getTriggerSignal(conditionName, false); auto triggerSignalName = getTriggerSignal(conditionName, false);
auto completionSignalName = getCompletionSignal(conditionName, false); auto completionSignalName = getCompletionSignal(conditionName, false);
auto propertiesSignalName = getPropertiesSignal(conditionName, false); auto propertiesSignalName = getPropertiesSignal(conditionName, false);
auto newInstanceSignalName = getNewInstanceSignal(conditionName, false);
auto deletedInstanceSignalName =
getDeletedInstanceSignal(conditionName, false);
const auto createScriptCondition = const auto createScriptCondition =
[id, defaultSettings, propertiesSignalName, triggerSignalName, [id, defaultSettings, propertiesSignalName, triggerSignalName,
completionSignalName]( completionSignalName, newInstanceSignalName,
deletedInstanceSignalName](
Macro *m) -> std::shared_ptr<MacroCondition> { Macro *m) -> std::shared_ptr<MacroCondition> {
return std::make_shared<MacroConditionScript>( return std::make_shared<MacroConditionScript>(
m, id, defaultSettings, propertiesSignalName, m, id, defaultSettings, propertiesSignalName,
triggerSignalName, completionSignalName); triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName);
}; };
if (!MacroConditionFactory::Register( if (!MacroConditionFactory::Register(
id, {createScriptCondition, MacroSegmentScriptEdit::Create, id, {createScriptCondition, MacroSegmentScriptEdit::Create,
@@ -304,9 +375,15 @@ void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
triggerSignalName.c_str()); triggerSignalName.c_str());
calldata_set_string(data, propertiesSignalParam.data(), calldata_set_string(data, propertiesSignalParam.data(),
propertiesSignalName.c_str()); propertiesSignalName.c_str());
calldata_set_string(data, newInstanceSignalNameParam.data(),
newInstanceSignalName.c_str());
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
deletedInstanceSignalName.c_str());
_conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName, _conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
triggerSignalName, triggerSignalName,
completionSignalName)); completionSignalName,
newInstanceSignalName,
deletedInstanceSignalName));
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
@@ -399,6 +476,92 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
{
const char *variableId;
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
strlen(variableId) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarIdParam.data());
RETURN_FAILURE();
}
const char *name;
if (!calldata_get_string(data, tempVarNameParam.data(), &name) ||
strlen(name) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarNameParam.data());
RETURN_FAILURE();
}
const char *helpText;
if (!calldata_get_string(data, tempVarHelpParam.data(), &helpText)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarHelpParam.data());
RETURN_FAILURE();
}
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::RegisterTempVar(variableId, name, helpText,
instanceId);
RETURN_SUCCESS();
}
void ScriptHandler::DeregisterAllTempVars(void *, calldata_t *data)
{
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
deregisterAllTempVarsFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::DeregisterAllTempVars(instanceId);
RETURN_SUCCESS();
}
void ScriptHandler::SetTempVarValue(void *, calldata_t *data)
{
const char *variableId;
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
strlen(variableId) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
setTempVarValueFuncName.data(), tempVarIdParam.data());
RETURN_FAILURE();
}
const char *variableValue;
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
strlen(variableValue) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
setTempVarValueFuncName.data(), valueParam.data());
RETURN_FAILURE();
}
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
setTempVarValueFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::SetTempVarValue(variableId, variableValue,
instanceId);
RETURN_SUCCESS();
}
bool ScriptHandler::ActionIdIsValid(const std::string &id) bool ScriptHandler::ActionIdIsValid(const std::string &id)
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
@@ -424,24 +587,39 @@ static std::string signalNameToPropertiesSignalDecl(const std::string &name)
static std::string signalNameToCompletionSignalDecl(const std::string &name) static std::string signalNameToCompletionSignalDecl(const std::string &name)
{ {
return std::string("bool ") + name + "(in int " + return std::string("bool ") + name + "(in int " +
GeCompletionIdParamName().data() + ")"; GetCompletionIdParamName().data() + ")";
} }
ScriptSegmentType::ScriptSegmentType(const std::string &id, static std::string signalNameToInstanceSignalDecl(const std::string &name)
const std::string &propertiesSignal, {
const std::string &triggerSignal, return std::string("void ") + name + "(out int " +
const std::string &completionSignal) GetInstanceIdParamName().data() + ")";
}
ScriptSegmentType::ScriptSegmentType(
const std::string &id, const std::string &propertiesSignalName,
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: _id(id) : _id(id)
{ {
auto sh = obs_get_signal_handler();
signal_handler_add( signal_handler_add(
obs_get_signal_handler(), sh,
signalNameToPropertiesSignalDecl(propertiesSignal).c_str()); signalNameToPropertiesSignalDecl(propertiesSignalName).c_str());
signal_handler_add( signal_handler_add(
obs_get_signal_handler(), sh, signalNameToTriggerSignalDecl(triggerSignalName).c_str());
signalNameToTriggerSignalDecl(triggerSignal).c_str());
signal_handler_add( signal_handler_add(
obs_get_signal_handler(), sh,
signalNameToCompletionSignalDecl(completionSignal).c_str()); signalNameToCompletionSignalDecl(completionSignalName).c_str());
signal_handler_add(
sh,
signalNameToInstanceSignalDecl(newInstanceSignalName).c_str());
signal_handler_add(
sh, signalNameToInstanceSignalDecl(deletedInstanceSignalName)
.c_str());
} }
} // namespace advss } // namespace advss

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -244,8 +244,13 @@ bool Macro::CheckConditions(bool ignorePause)
return false; return false;
} }
_stop = false;
_matched = false; _matched = false;
for (auto &condition : _conditions) { for (auto &condition : _conditions) {
if (!condition) {
continue;
}
if (_paused && !ignorePause) { if (_paused && !ignorePause) {
vblog(LOG_INFO, "Macro %s is paused", _name.c_str()); vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
return false; return false;
@@ -383,6 +388,9 @@ bool Macro::RunActionsHelper(
bool actionsExecutedSuccessfully = true; bool actionsExecutedSuccessfully = true;
for (auto &action : actions) { for (auto &action : actions) {
if (!action) {
continue;
}
if (action->Enabled()) { if (action->Enabled()) {
action->LogAction(); action->LogAction();
actionsExecutedSuccessfully = actionsExecutedSuccessfully =

View File

@@ -40,8 +40,8 @@ ActionQueueTable *ActionQueueTable::Create()
void ActionQueueTable::Add() void ActionQueueTable::Add()
{ {
auto newQueue = std::make_shared<ActionQueue>(); auto newQueue = std::make_shared<ActionQueue>();
auto accepted = auto accepted = ActionQueueSettingsDialog::AskForSettings(
ActionQueueSettingsDialog::AskForSettings(this, *newQueue); GetSettingsWindow(), *newQueue);
if (!accepted) { if (!accepted) {
return; return;
} }
@@ -124,7 +124,7 @@ static void openSettingsDialog()
auto oldName = queue->Name(); auto oldName = queue->Name();
bool accepted = ActionQueueSettingsDialog::AskForSettings( bool accepted = ActionQueueSettingsDialog::AskForSettings(
tabWidget->Table(), *queue.get()); GetSettingsWindow(), *queue.get());
if (accepted && oldName != queue->Name()) { if (accepted && oldName != queue->Name()) {
ActionQueueSignalManager::Instance()->Rename( ActionQueueSignalManager::Instance()->Rename(
QString::fromStdString(oldName), QString::fromStdString(oldName),

View File

@@ -113,6 +113,7 @@ void ActionQueue::Add(const std::shared_ptr<MacroAction> &action)
action->Save(data); action->Save(data);
copy->Load(data); copy->Load(data);
copy->PostLoad(); copy->PostLoad();
RunPostLoadSteps();
copy->ResolveVariablesToFixedValues(); copy->ResolveVariablesToFixedValues();
_actions.emplace_back(copy); _actions.emplace_back(copy);
} else { } else {
@@ -179,7 +180,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
"AdvSceneSwitcher.actionQueues.select", "AdvSceneSwitcher.actionQueues.select",
"AdvSceneSwitcher.actionQueues.add", "AdvSceneSwitcher.actionQueues.add",
"AdvSceneSwitcher.actionQueues.nameNotAvailable", "AdvSceneSwitcher.actionQueues.nameNotAvailable",
parent), true, parent),
_queueRunStatus(new QLabel()), _queueRunStatus(new QLabel()),
_startStopToggle(new QPushButton()), _startStopToggle(new QPushButton()),
_queueSize(new QLabel()), _queueSize(new QLabel()),

View File

@@ -14,11 +14,7 @@ AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
&AutoUpdateTooltipLabel::UpdateTooltip); &AutoUpdateTooltipLabel::UpdateTooltip);
} }
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event) void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
#else
void AutoUpdateTooltipLabel::enterEvent(QEvent *event)
#endif
{ {
_timer->start(_updateIntervalMs); _timer->start(_updateIntervalMs);
QLabel::enterEvent(event); QLabel::enterEvent(event);

View File

@@ -17,11 +17,7 @@ public:
int updateIntervalMs = 300); int updateIntervalMs = 300);
protected: protected:
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent *event) override; void enterEvent(QEnterEvent *event) override;
#else
void enterEvent(QEvent *event) override;
#endif
void leaveEvent(QEvent *event) override; void leaveEvent(QEvent *event) override;
private slots: private slots:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -81,7 +81,7 @@ private slots:
void SelectionChanged(const SourceSetting &); void SelectionChanged(const SourceSetting &);
void ManualSettingsValueChanged(); void ManualSettingsValueChanged();
void RefreshVariableSourceSelectionValue(); void RefreshVariableSourceSelectionValue();
void ButtonChanged(int idx); void ButtonChanged(const SourceSettingButton &);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -100,7 +100,7 @@ private:
SourceSettingSelection *_filterSettings; SourceSettingSelection *_filterSettings;
VariableTextEdit *_settingsString; VariableTextEdit *_settingsString;
QPushButton *_refreshSettingSelection; QPushButton *_refreshSettingSelection;
QComboBox *_settingsButtons; SourceSettingsButtonSelection *_settingsButtons;
std::shared_ptr<MacroActionFilter> _entryData; std::shared_ptr<MacroActionFilter> _entryData;
bool _loading = true; bool _loading = true;

View File

@@ -263,28 +263,12 @@ static inline void populateActionSelection(QComboBox *list)
} }
} }
static inline void populateDeinterlaceModeSelection(QComboBox *list) template<class T>
static inline void populateModeSelection(QComboBox *list,
const std::map<T, std::string> &modes)
{ {
list->clear(); list->clear();
for (const auto &[value, name] : deinterlaceModes) { for (const auto &[value, name] : modes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
static inline void populateDeinterlaceFieldOrderSelection(QComboBox *list)
{
list->clear();
for (const auto &[value, name] : deinterlaceFieldOrders) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
static inline void populateSettingsInputMethods(QComboBox *list)
{
list->clear();
for (const auto &[value, name] : inputMethods) {
list->addItem(obs_module_text(name.c_str()), list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value)); static_cast<int>(value));
} }
@@ -295,7 +279,7 @@ MacroActionSourceEdit::MacroActionSourceEdit(
: QWidget(parent), : QWidget(parent),
_sources(new SourceSelectionWidget(this, QStringList(), true)), _sources(new SourceSelectionWidget(this, QStringList(), true)),
_actions(new QComboBox()), _actions(new QComboBox()),
_settingsButtons(new QComboBox()), _settingsButtons(new SourceSettingsButtonSelection(this)),
_settingsLayout(new QHBoxLayout()), _settingsLayout(new QHBoxLayout()),
_settingsInputMethods(new QComboBox(this)), _settingsInputMethods(new QComboBox(this)),
_manualSettingValue(new VariableTextEdit(this, 5, 1, 1)), _manualSettingValue(new VariableTextEdit(this, 5, 1, 1)),
@@ -315,16 +299,18 @@ MacroActionSourceEdit::MacroActionSourceEdit(
auto sources = GetSourceNames(); auto sources = GetSourceNames();
sources.sort(); sources.sort();
_sources->SetSourceNameList(sources); _sources->SetSourceNameList(sources);
populateDeinterlaceModeSelection(_deinterlaceMode); populateModeSelection(_deinterlaceMode, deinterlaceModes);
populateDeinterlaceFieldOrderSelection(_deinterlaceOrder); populateModeSelection(_deinterlaceOrder, deinterlaceFieldOrders);
populateSettingsInputMethods(_settingsInputMethods); populateModeSelection(_settingsInputMethods, inputMethods);
_refreshSettingSelection->setToolTip(obs_module_text( _refreshSettingSelection->setToolTip(obs_module_text(
"AdvSceneSwitcher.action.source.refresh.tooltip")); "AdvSceneSwitcher.action.source.refresh.tooltip"));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int))); SLOT(ActionChanged(int)));
QWidget::connect(_settingsButtons, SIGNAL(currentIndexChanged(int)), QWidget::connect(_settingsButtons,
this, SLOT(ButtonChanged(int))); SIGNAL(SelectionChanged(const SourceSettingButton &)),
this,
SLOT(ButtonChanged(const SourceSettingButton &)));
QWidget::connect(_sources, QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this, SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &))); SLOT(SourceChanged(const SourceSelection &)));
@@ -394,14 +380,11 @@ void MacroActionSourceEdit::UpdateEntryData()
return; return;
} }
PopulateSourceButtonSelection(_settingsButtons, const auto weakSource = _entryData->_source.GetSource();
_entryData->_source.GetSource()); _settingsButtons->SetSelection(weakSource, _entryData->_button);
_actions->setCurrentIndex(static_cast<int>(_entryData->_action)); _actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->SetSource(_entryData->_source); _sources->SetSource(_entryData->_source);
_sourceSettings->SetSource(_entryData->_source.GetSource()); _sourceSettings->SetSelection(weakSource, _entryData->_setting);
_sourceSettings->SetSetting(_entryData->_setting);
_settingsButtons->setCurrentText(
QString::fromStdString(_entryData->_button.ToString()));
_settingsString->setPlainText(_entryData->_settingsString); _settingsString->setPlainText(_entryData->_settingsString);
_deinterlaceMode->setCurrentIndex(_deinterlaceMode->findData( _deinterlaceMode->setCurrentIndex(_deinterlaceMode->findData(
static_cast<int>(_entryData->_deinterlaceMode))); static_cast<int>(_entryData->_deinterlaceMode)));
@@ -425,9 +408,10 @@ void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
auto lock = LockContext(); auto lock = LockContext();
_entryData->_source = source; _entryData->_source = source;
} }
PopulateSourceButtonSelection(_settingsButtons,
_entryData->_source.GetSource()); const auto weakSource = _entryData->_source.GetSource();
_sourceSettings->SetSource(_entryData->_source.GetSource()); _sourceSettings->SetSource(weakSource);
_settingsButtons->SetSource(weakSource);
SetWidgetVisibility(); SetWidgetVisibility();
emit HeaderInfoChanged( emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc())); QString::fromStdString(_entryData->GetShortDesc()));
@@ -435,24 +419,15 @@ void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
void MacroActionSourceEdit::ActionChanged(int value) void MacroActionSourceEdit::ActionChanged(int value)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionSource::Action>(value); _entryData->_action = static_cast<MacroActionSource::Action>(value);
SetWidgetVisibility(); SetWidgetVisibility();
} }
void MacroActionSourceEdit::ButtonChanged(int idx) void MacroActionSourceEdit::ButtonChanged(const SourceSettingButton &button)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return; _entryData->_button = button;
}
auto lock = LockContext();
_entryData->_button = qvariant_cast<SourceSettingButton>(
_settingsButtons->itemData(idx));
} }
void MacroActionSourceEdit::GetSettingsClicked() void MacroActionSourceEdit::GetSettingsClicked()
@@ -479,11 +454,7 @@ void MacroActionSourceEdit::GetSettingsClicked()
void MacroActionSourceEdit::SettingsStringChanged() void MacroActionSourceEdit::SettingsStringChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_settingsString = _entryData->_settingsString =
_settingsString->toPlainText().toStdString(); _settingsString->toPlainText().toStdString();
@@ -493,22 +464,14 @@ void MacroActionSourceEdit::SettingsStringChanged()
void MacroActionSourceEdit::DeinterlaceModeChanged(int idx) void MacroActionSourceEdit::DeinterlaceModeChanged(int idx)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_deinterlaceMode = static_cast<obs_deinterlace_mode>( _entryData->_deinterlaceMode = static_cast<obs_deinterlace_mode>(
_deinterlaceMode->itemData(idx).toInt()); _deinterlaceMode->itemData(idx).toInt());
} }
void MacroActionSourceEdit::DeinterlaceOrderChanged(int idx) void MacroActionSourceEdit::DeinterlaceOrderChanged(int idx)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_deinterlaceOrder = _entryData->_deinterlaceOrder =
static_cast<obs_deinterlace_field_order>( static_cast<obs_deinterlace_field_order>(
_deinterlaceOrder->itemData(idx).toInt()); _deinterlaceOrder->itemData(idx).toInt());
@@ -516,21 +479,13 @@ void MacroActionSourceEdit::DeinterlaceOrderChanged(int idx)
void MacroActionSourceEdit::SelectionChanged(const TempVariableRef &var) void MacroActionSourceEdit::SelectionChanged(const TempVariableRef &var)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_tempVar = var; _entryData->_tempVar = var;
} }
void MacroActionSourceEdit::SettingsInputMethodChanged(int idx) void MacroActionSourceEdit::SettingsInputMethodChanged(int idx)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_settingsInputMethod = _entryData->_settingsInputMethod =
static_cast<MacroActionSource::SettingsInputMethod>( static_cast<MacroActionSource::SettingsInputMethod>(
_settingsInputMethods->itemData(idx).toInt()); _settingsInputMethods->itemData(idx).toInt());
@@ -539,21 +494,13 @@ void MacroActionSourceEdit::SettingsInputMethodChanged(int idx)
void MacroActionSourceEdit::SelectionChanged(const SourceSetting &setting) void MacroActionSourceEdit::SelectionChanged(const SourceSetting &setting)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_setting = setting; _entryData->_setting = setting;
} }
void MacroActionSourceEdit::ManualSettingsValueChanged() void MacroActionSourceEdit::ManualSettingsValueChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_manualSettingValue = _entryData->_manualSettingValue =
_manualSettingValue->toPlainText().toStdString(); _manualSettingValue->toPlainText().toStdString();

View File

@@ -80,7 +80,7 @@ public:
private slots: private slots:
void SourceChanged(const SourceSelection &); void SourceChanged(const SourceSelection &);
void ActionChanged(int value); void ActionChanged(int value);
void ButtonChanged(int idx); void ButtonChanged(const SourceSettingButton &);
void GetSettingsClicked(); void GetSettingsClicked();
void SettingsStringChanged(); void SettingsStringChanged();
void DeinterlaceModeChanged(int); void DeinterlaceModeChanged(int);
@@ -99,7 +99,7 @@ private:
SourceSelectionWidget *_sources; SourceSelectionWidget *_sources;
QComboBox *_actions; QComboBox *_actions;
QComboBox *_settingsButtons; SourceSettingsButtonSelection *_settingsButtons;
QHBoxLayout *_settingsLayout; QHBoxLayout *_settingsLayout;
QComboBox *_settingsInputMethods; QComboBox *_settingsInputMethods;
VariableTextEdit *_manualSettingValue; VariableTextEdit *_manualSettingValue;

View File

@@ -257,7 +257,9 @@ void MacroActionStreamEdit::StringValueChanged()
void MacroActionStreamEdit::ShowPassword() void MacroActionStreamEdit::ShowPassword()
{ {
SetButtonIcon(_showPassword, ":res/images/visible.svg"); SetButtonIcon(_showPassword, GetThemeTypeName() == "Light"
? ":res/images/visible.svg"
: "theme:Dark/visible.svg");
_stringValue->setEchoMode(QLineEdit::Normal); _stringValue->setEchoMode(QLineEdit::Normal);
} }

View File

@@ -123,27 +123,23 @@ MacroActionSystrayEdit::MacroActionSystrayEdit(
void MacroActionSystrayEdit::TitleChanged() void MacroActionSystrayEdit::TitleChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_title = _title->text().toStdString(); _entryData->_title = _title->text().toStdString();
} }
void MacroActionSystrayEdit::IconPathChanged(const QString &text) void MacroActionSystrayEdit::IconPathChanged(const QString &text)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_iconPath = text.toStdString(); _entryData->_iconPath = text.toStdString();
} }
void MacroActionSystrayEdit::CheckIfTrayIsDisabled() void MacroActionSystrayEdit::CheckIfTrayIsDisabled()
{ {
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(31, 0, 0)
auto config = obs_frontend_get_user_config();
#else
auto config = obs_frontend_get_global_config(); auto config = obs_frontend_get_global_config();
#endif
if (!config) { if (!config) {
return; return;
} }

View File

@@ -176,15 +176,21 @@ bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
bool MacroConditionDate::CheckCondition() bool MacroConditionDate::CheckCondition()
{ {
auto m = GetMacro(); auto macro = GetMacro();
if (!m) { if (!macro) {
return false; return false;
} }
const auto timePassed = std::chrono::high_resolution_clock::now() -
LastMacroConditionCheckTime(m); const auto now = std::chrono::high_resolution_clock::now();
const auto lastCheck = LastMacroConditionCheckTime(macro);
const auto timePassed = now - lastCheck;
const auto msSinceLastCheck = const auto msSinceLastCheck =
std::chrono::duration_cast<std::chrono::milliseconds>( MacroWasCheckedSinceLastStart(macro)
timePassed); ? std::chrono::duration_cast<std::chrono::milliseconds>(
timePassed)
: std::chrono::milliseconds(0);
if (_dayOfWeekCheck) { if (_dayOfWeekCheck) {
return CheckDayOfWeek(msSinceLastCheck.count()); return CheckDayOfWeek(msSinceLastCheck.count());

View File

@@ -420,8 +420,9 @@ void MacroConditionFilterEdit::UpdateEntryData()
_regex->SetRegexConfig(_entryData->_regex); _regex->SetRegexConfig(_entryData->_regex);
const auto filters = const auto filters =
_entryData->_filter.GetFilters(_entryData->_source); _entryData->_filter.GetFilters(_entryData->_source);
_settingSelection->SetSource(filters.empty() ? nullptr : filters.at(0)); _settingSelection->SetSelection(filters.empty() ? nullptr
_settingSelection->SetSetting(_entryData->_setting); : filters.at(0),
_entryData->_setting);
SetWidgetVisibility(); SetWidgetVisibility();
adjustSize(); adjustSize();

View File

@@ -0,0 +1,197 @@
#include "macro-condition-game-capture.hpp"
#include "layout-helpers.hpp"
namespace advss {
const std::string MacroConditionGameCapture::id = "game_capture";
#ifdef _WIN32
bool MacroConditionGameCapture::_registered = MacroConditionFactory::Register(
MacroConditionGameCapture::id,
{MacroConditionGameCapture::Create,
MacroConditionGameCaptureEdit::Create,
"AdvSceneSwitcher.condition.gameCapture"});
#else
bool MacroConditionGameCapture::_registered =
false; // For now the 'game_capture' source only exists on Windows
#endif
bool MacroConditionGameCapture::CheckCondition()
{
auto source = OBSGetStrongRef(_source.GetSource());
if (source != _lastSource) {
SetupSignalHandler(source);
return false;
}
std::lock_guard<std::mutex> lock(_mtx);
if (_hooked) {
SetTempVarValue("title", _title);
SetTempVarValue("class", _class);
SetTempVarValue("executable", _executable);
}
return _hooked;
}
bool MacroConditionGameCapture::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
_source.Save(obj);
return true;
}
bool MacroConditionGameCapture::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_source.Load(obj);
SetupSignalHandler(OBSGetStrongRef(_source.GetSource()));
return true;
}
void MacroConditionGameCapture::GetCalldataInfo(calldata_t *cd)
{
const char *title = "";
if (!calldata_get_string(cd, "title", &title)) {
blog(LOG_WARNING, "%s failed to get title", __func__);
}
_title = title;
const char *className = "";
if (!calldata_get_string(cd, "class", &className)) {
blog(LOG_WARNING, "%s failed to get class", __func__);
}
_class = className;
const char *executable = "";
if (!calldata_get_string(cd, "executable", &executable)) {
blog(LOG_WARNING, "%s failed to get executable", __func__);
}
_executable = executable;
}
void MacroConditionGameCapture::HookedSignalReceived(void *data, calldata_t *cd)
{
auto condition = static_cast<MacroConditionGameCapture *>(data);
std::lock_guard<std::mutex> lock(condition->_mtx);
condition->GetCalldataInfo(cd);
condition->_hooked = true;
}
void MacroConditionGameCapture::UnhookedSignalReceived(void *data, calldata_t *)
{
auto condition = static_cast<MacroConditionGameCapture *>(data);
std::lock_guard<std::mutex> lock(condition->_mtx);
condition->_hooked = false;
}
void MacroConditionGameCapture::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar(
"title",
obs_module_text("AdvSceneSwitcher.tempVar.gameCapture.title"),
obs_module_text(
"AdvSceneSwitcher.tempVar.gameCapture.title.description"));
AddTempvar(
"class",
obs_module_text("AdvSceneSwitcher.tempVar.gameCapture.class"),
obs_module_text(
"AdvSceneSwitcher.tempVar.gameCapture.class.description"));
AddTempvar(
"executable",
obs_module_text(
"AdvSceneSwitcher.tempVar.gameCapture.executable"),
obs_module_text(
"AdvSceneSwitcher.tempVar.gameCapture.executable.description"));
}
void MacroConditionGameCapture::SetupSignalHandler(obs_source_t *source)
{
signal_handler_t *sh = obs_source_get_signal_handler(source);
_hookSignal = OBSSignal(sh, "hooked", HookedSignalReceived, this);
_unhookSignal = OBSSignal(sh, "unhooked", UnhookedSignalReceived, this);
_lastSource = source;
SetupInitialState(source);
}
void MacroConditionGameCapture::SetupInitialState(obs_source_t *source)
{
std::lock_guard<std::mutex> lock(_mtx);
_hooked = false;
if (!source) {
return;
}
calldata_t cd;
calldata_init(&cd);
auto ph = obs_source_get_proc_handler(source);
if (!proc_handler_call(ph, "get_hooked", &cd)) {
blog(LOG_WARNING,
"%s failed to call proc_handler for 'get_hooked'",
__func__);
return;
}
if (!calldata_get_bool(&cd, "hooked", &_hooked)) {
blog(LOG_WARNING, "%s failed to get hooked state", __func__);
}
GetCalldataInfo(&cd);
calldata_free(&cd);
}
static QStringList getGameCaptureSourceNames()
{
auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */
{
QStringList *list = reinterpret_cast<QStringList *>(param);
if (strcmp(obs_source_get_unversioned_id(source),
"game_capture") == 0) {
*list << obs_source_get_name(source);
}
return true;
};
QStringList list;
obs_enum_sources(sourceEnum, &list);
return list;
}
MacroConditionGameCaptureEdit::MacroConditionGameCaptureEdit(
QWidget *parent, std::shared_ptr<MacroConditionGameCapture> entryData)
: QWidget(parent),
_sources(new SourceSelectionWidget(this, QStringList(), true))
{
auto sources = getGameCaptureSourceNames();
sources.sort();
_sources->SetSourceNameList(sources);
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
auto layout = new QHBoxLayout;
PlaceWidgets(
obs_module_text("AdvSceneSwitcher.condition.gameCapture.entry"),
layout, {{"{{sources}}", _sources}});
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionGameCaptureEdit::SourceChanged(const SourceSelection &source)
{
GUARD_LOADING_AND_LOCK();
_entryData->_source = source;
}
void MacroConditionGameCaptureEdit::UpdateEntryData()
{
_sources->SetSource(_entryData->_source);
}
} // namespace advss

View File

@@ -0,0 +1,74 @@
#pragma once
#include "macro-condition-edit.hpp"
#include "source-selection.hpp"
#include <QWidget>
#include <QComboBox>
namespace advss {
class MacroConditionGameCapture : public MacroCondition {
public:
MacroConditionGameCapture(Macro *m) : MacroCondition(m) {}
bool CheckCondition();
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionGameCapture>(m);
}
SourceSelection _source;
private:
static void HookedSignalReceived(void *data, calldata_t *);
static void UnhookedSignalReceived(void *data, calldata_t *);
void SetupTempVars();
void SetupSignalHandler(obs_source_t *source);
void SetupInitialState(obs_source_t *source);
void GetCalldataInfo(calldata_t *cd);
obs_source_t *_lastSource = nullptr;
OBSSignal _hookSignal;
OBSSignal _unhookSignal;
bool _hooked = false;
std::string _title = "";
std::string _class = "";
std::string _executable = "";
std::mutex _mtx;
static bool _registered;
static const std::string id;
};
class MacroConditionGameCaptureEdit : public QWidget {
Q_OBJECT
public:
MacroConditionGameCaptureEdit(
QWidget *parent,
std::shared_ptr<MacroConditionGameCapture> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionGameCaptureEdit(
parent,
std::dynamic_pointer_cast<MacroConditionGameCapture>(
cond));
}
private slots:
void SourceChanged(const SourceSelection &);
private:
SourceSelectionWidget *_sources;
std::shared_ptr<MacroConditionGameCapture> _entryData;
bool _loading = true;
};
} // namespace advss

View File

@@ -548,14 +548,14 @@ void MacroConditionMedia::MediaNext(void *data, calldata_t *)
void MacroConditionMedia::SetSourceType(SourceType t) void MacroConditionMedia::SetSourceType(SourceType t)
{ {
SetupTempVars();
_sourceType = t; _sourceType = t;
SetupTempVars();
} }
void MacroConditionMedia::SetCheckType(CheckType t) void MacroConditionMedia::SetCheckType(CheckType t)
{ {
SetupTempVars();
_checkType = t; _checkType = t;
SetupTempVars();
} }
static void populateSateSelection(QComboBox *list, bool addLegacyEntries) static void populateSateSelection(QComboBox *list, bool addLegacyEntries)

View File

@@ -0,0 +1,94 @@
#include "macro-condition-screenshot.hpp"
#include "layout-helpers.hpp"
#include <obs-frontend-api.h>
#include <thread>
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(29, 0, 0)
namespace advss {
const std::string MacroConditionScreenshot::id = "screenshot";
bool MacroConditionScreenshot::_registered = MacroConditionFactory::Register(
MacroConditionScreenshot::id,
{MacroConditionScreenshot::Create, MacroConditionScreenshotEdit::Create,
"AdvSceneSwitcher.condition.screenshot"});
static std::chrono::high_resolution_clock::time_point screenshotTakenTime{};
static bool setupScreenshotTakenEventHandler();
static bool setupDone = setupScreenshotTakenEventHandler();
static bool setupScreenshotTakenEventHandler()
{
static auto handleScreenshotEvent = [](enum obs_frontend_event event,
void *) {
switch (event) {
case OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN:
screenshotTakenTime =
std::chrono::high_resolution_clock::now();
break;
default:
break;
};
};
obs_frontend_add_event_callback(handleScreenshotEvent, nullptr);
return true;
}
bool MacroConditionScreenshot::CheckCondition()
{
if (!_screenshotTimeInitialized) {
_screenshotTime = screenshotTakenTime;
_screenshotTimeInitialized = true;
return false;
}
auto lastScreenshotPath = obs_frontend_get_last_screenshot();
SetTempVarValue("lastScreenshotPath", lastScreenshotPath);
bfree(lastScreenshotPath);
bool newSaveOccurred = _screenshotTime != screenshotTakenTime;
_screenshotTime = screenshotTakenTime;
return newSaveOccurred;
}
bool MacroConditionScreenshot::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
return true;
}
bool MacroConditionScreenshot::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
return true;
}
void MacroConditionScreenshot::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar(
"lastScreenshotPath",
obs_module_text(
"AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath"),
obs_module_text(
"AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath.description"));
}
MacroConditionScreenshotEdit::MacroConditionScreenshotEdit(
QWidget *parent, std::shared_ptr<MacroConditionScreenshot> entryData)
: QWidget(parent)
{
auto layout = new QHBoxLayout;
PlaceWidgets(
obs_module_text("AdvSceneSwitcher.condition.screenshot.entry"),
layout, {});
setLayout(layout);
_entryData = entryData;
}
} // namespace advss
#endif

View File

@@ -0,0 +1,55 @@
#pragma once
#include "macro-condition-edit.hpp"
#include <obs-config.h>
#include <QWidget>
#include <QComboBox>
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(29, 0, 0)
namespace advss {
class MacroConditionScreenshot : public MacroCondition {
public:
MacroConditionScreenshot(Macro *m) : MacroCondition(m) {}
bool CheckCondition();
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionScreenshot>(m);
}
private:
void SetupTempVars();
bool _screenshotTimeInitialized = false;
std::chrono::high_resolution_clock::time_point _screenshotTime = {};
static bool _registered;
static const std::string id;
};
class MacroConditionScreenshotEdit : public QWidget {
Q_OBJECT
public:
MacroConditionScreenshotEdit(
QWidget *parent,
std::shared_ptr<MacroConditionScreenshot> cond = nullptr);
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionScreenshotEdit(
parent,
std::dynamic_pointer_cast<MacroConditionScreenshot>(
cond));
}
private:
std::shared_ptr<MacroConditionScreenshot> _entryData;
};
} // namespace advss
#endif

View File

@@ -483,8 +483,8 @@ void MacroConditionSourceEdit::UpdateEntryData()
static_cast<int>(_entryData->GetCondition())); static_cast<int>(_entryData->GetCondition()));
_settings->setPlainText(_entryData->_settings); _settings->setPlainText(_entryData->_settings);
_regex->SetRegexConfig(_entryData->_regex); _regex->SetRegexConfig(_entryData->_regex);
_settingSelection->SetSource(_entryData->_source.GetSource()); _settingSelection->SetSelection(_entryData->_source.GetSource(),
_settingSelection->SetSetting(_entryData->_setting); _entryData->_setting);
_size->SetValue(_entryData->_size); _size->SetValue(_entryData->_size);
_sizeCompareMethods->setCurrentIndex( _sizeCompareMethods->setCurrentIndex(
static_cast<int>(_entryData->_comparision)); static_cast<int>(_entryData->_comparision));

View File

@@ -111,27 +111,42 @@ bool MacroConditionTransition::CheckCondition()
return ret; return ret;
} }
void MacroConditionTransition::ConnectToTransitionSignals() void advss::MacroConditionTransition::AddTransitionSignals(
obs_source_t *transition)
{ {
const bool useFrontendTransitionSelection = signal_handler_t *sh = obs_source_get_signal_handler(transition);
_transition.GetType() !=
TransitionSelection::Type::TRANSITION ||
(_condition == Condition::TRANSITION_SOURCE ||
_condition == Condition::TRANSITION_TARGET);
_signals.clear();
OBSSourceAutoRelease source =
useFrontendTransitionSelection
? obs_frontend_get_current_transition()
: obs_weak_source_get_source(
_transition.GetTransition());
signal_handler_t *sh = obs_source_get_signal_handler(source);
_signals.emplace_back(sh, "transition_start", TransitionStarted, this); _signals.emplace_back(sh, "transition_start", TransitionStarted, this);
_signals.emplace_back(sh, "transition_stop", TransitionEnded, this); _signals.emplace_back(sh, "transition_stop", TransitionEnded, this);
_signals.emplace_back(sh, "transition_video_stop", TransitionVideoEnded, _signals.emplace_back(sh, "transition_video_stop", TransitionVideoEnded,
this); this);
} }
void MacroConditionTransition::ConnectToTransitionSignals()
{
_signals.clear();
const bool watchSingleTransitionType =
_transition.GetType() ==
TransitionSelection::Type::TRANSITION &&
!(_condition == Condition::TRANSITION_SOURCE ||
_condition == Condition::TRANSITION_TARGET);
if (watchSingleTransitionType) {
OBSSourceAutoRelease transition =
OBSGetStrongRef(_transition.GetTransition());
AddTransitionSignals(transition);
return;
}
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *transition = transitions.sources.array[i];
AddTransitionSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
void MacroConditionTransition::TransitionStarted(void *data, calldata_t *cd) void MacroConditionTransition::TransitionStarted(void *data, calldata_t *cd)
{ {
auto *condition = static_cast<MacroConditionTransition *>(data); auto *condition = static_cast<MacroConditionTransition *>(data);

View File

@@ -42,6 +42,7 @@ private:
static void TransitionEnded(void *data, calldata_t *); static void TransitionEnded(void *data, calldata_t *);
static void TransitionVideoEnded(void *data, calldata_t *); static void TransitionVideoEnded(void *data, calldata_t *);
static void HandleFrontendEvent(enum obs_frontend_event event, void *); static void HandleFrontendEvent(enum obs_frontend_event event, void *);
void AddTransitionSignals(obs_source_t *);
std::vector<OBSSignal> _signals; std::vector<OBSSignal> _signals;

View File

@@ -332,7 +332,8 @@ WSConnectionSettingsDialog::WSConnectionSettingsDialog(
: ItemSettingsDialog(settings, connections, : ItemSettingsDialog(settings, connections,
"AdvSceneSwitcher.connection.select", "AdvSceneSwitcher.connection.select",
"AdvSceneSwitcher.connection.add", "AdvSceneSwitcher.connection.add",
"AdvSceneSwitcher.item.nameNotAvailable", parent), "AdvSceneSwitcher.item.nameNotAvailable", true,
parent),
_useCustomURI(new QCheckBox()), _useCustomURI(new QCheckBox()),
_customUri(new QLineEdit()), _customUri(new QLineEdit()),
_address(new QLineEdit()), _address(new QLineEdit()),
@@ -502,7 +503,9 @@ void WSConnectionSettingsDialog::SetStatus()
void WSConnectionSettingsDialog::ShowPassword() void WSConnectionSettingsDialog::ShowPassword()
{ {
SetButtonIcon(_showPassword, ":res/images/visible.svg"); SetButtonIcon(_showPassword, GetThemeTypeName() == "Light"
? ":res/images/visible.svg"
: "theme:Dark/visible.svg");
_password->setEchoMode(QLineEdit::Normal); _password->setEchoMode(QLineEdit::Normal);
} }

View File

@@ -698,7 +698,10 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
_nameConflictIndex->setSizeAdjustPolicy(QComboBox::AdjustToContents); _nameConflictIndex->setSizeAdjustPolicy(QComboBox::AdjustToContents);
_changeType->setMaximumWidth(22); _changeType->setMaximumWidth(22);
SetButtonIcon(_changeType, ":/settings/images/settings/general.svg"); SetButtonIcon(_changeType,
GetThemeTypeName() == "Light"
? ":/settings/images/settings/general.svg"
: "theme:Dark/settings/general.svg");
_changeType->setFlat(true); _changeType->setFlat(true);
_changeType->setToolTip(obs_module_text( _changeType->setToolTip(obs_module_text(
"AdvSceneSwitcher.sceneItemSelection.configure")); "AdvSceneSwitcher.sceneItemSelection.configure"));

View File

@@ -29,20 +29,37 @@ std::string SourceSettingButton::ToString() const
return "[" + id + "] " + description; return "[" + id + "] " + description;
} }
std::vector<SourceSettingButton> GetSourceButtons(OBSWeakSource source) static void getSourceButtonsHelper(obs_properties_t *sourceProperties,
std::vector<SourceSettingButton> &buttons)
{ {
OBSSourceAutoRelease s = obs_weak_source_get_source(source);
std::vector<SourceSettingButton> buttons;
obs_properties_t *sourceProperties = obs_source_properties(s);
auto it = obs_properties_first(sourceProperties); auto it = obs_properties_first(sourceProperties);
do { do {
if (!it || obs_property_get_type(it) != OBS_PROPERTY_BUTTON) { if (!it) {
continue;
}
auto type = obs_property_get_type(it);
if (type == OBS_PROPERTY_GROUP) {
auto group = obs_property_group_content(it);
getSourceButtonsHelper(group, buttons);
continue;
}
if (obs_property_get_type(it) != OBS_PROPERTY_BUTTON) {
continue; continue;
} }
SourceSettingButton button = {obs_property_name(it), SourceSettingButton button = {obs_property_name(it),
obs_property_description(it)}; obs_property_description(it)};
buttons.emplace_back(button); buttons.emplace_back(button);
} while (obs_property_next(&it)); } while (obs_property_next(&it));
}
std::vector<SourceSettingButton> GetSourceButtons(OBSWeakSource source)
{
OBSSourceAutoRelease s = obs_weak_source_get_source(source);
std::vector<SourceSettingButton> buttons;
auto sourceProperties = obs_source_properties(s);
getSourceButtonsHelper(sourceProperties, buttons);
obs_properties_destroy(sourceProperties); obs_properties_destroy(sourceProperties);
return buttons; return buttons;
} }
@@ -59,19 +76,46 @@ void PressSourceButton(const SourceSettingButton &button, obs_source_t *source)
obs_properties_destroy(sourceProperties); obs_properties_destroy(sourceProperties);
} }
void PopulateSourceButtonSelection(QComboBox *list, OBSWeakSource source) SourceSettingsButtonSelection::SourceSettingsButtonSelection(QWidget *parent)
: FilterComboBox(parent)
{ {
list->clear(); connect(this, &FilterComboBox::currentIndexChanged, [this](int idx) {
emit SelectionChanged(
qvariant_cast<SourceSettingButton>(itemData(idx)));
});
}
void SourceSettingsButtonSelection::SetSource(const OBSWeakSource &source,
bool restorePreviousSelection)
{
const auto previousSelection = currentText();
PopulateSelection(source);
if (restorePreviousSelection) {
setCurrentIndex(findText(previousSelection));
}
}
void SourceSettingsButtonSelection::SetSelection(
const OBSWeakSource &source, const SourceSettingButton &button)
{
SetSource(source, false);
setCurrentText(QString::fromStdString(button.ToString()));
}
void SourceSettingsButtonSelection::PopulateSelection(
const OBSWeakSource &source)
{
const QSignalBlocker b(this);
clear();
auto buttons = GetSourceButtons(source); auto buttons = GetSourceButtons(source);
if (buttons.empty()) { if (buttons.empty()) {
list->addItem( addItem(obs_module_text("AdvSceneSwitcher.noSettingsButtons"));
obs_module_text("AdvSceneSwitcher.noSettingsButtons"));
} }
for (const auto &button : buttons) { for (const auto &button : buttons) {
QVariant value; QVariant value;
value.setValue(button); value.setValue(button);
list->addItem(QString::fromStdString(button.ToString()), value); addItem(QString::fromStdString(button.ToString()), value);
} }
} }

View File

@@ -1,12 +1,16 @@
#pragma once #pragma once
#include "filter-combo-box.hpp"
#include <obs.hpp> #include <obs.hpp>
#include <QComboBox>
#include <qmetatype.h> #include <qmetatype.h>
#include <string> #include <string>
#include <vector> #include <vector>
namespace advss { namespace advss {
class SourceSelection;
class FilterSelection;
struct SourceSettingButton { struct SourceSettingButton {
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
@@ -18,7 +22,23 @@ struct SourceSettingButton {
std::vector<SourceSettingButton> GetSourceButtons(OBSWeakSource source); std::vector<SourceSettingButton> GetSourceButtons(OBSWeakSource source);
void PressSourceButton(const SourceSettingButton &button, obs_source_t *source); void PressSourceButton(const SourceSettingButton &button, obs_source_t *source);
void PopulateSourceButtonSelection(QComboBox *list, OBSWeakSource source);
class SourceSettingsButtonSelection : public FilterComboBox {
Q_OBJECT
public:
SourceSettingsButtonSelection(QWidget *parent = nullptr);
void SetSource(const OBSWeakSource &source,
bool restorePreviousSelection = true);
void SetSelection(const OBSWeakSource &source,
const SourceSettingButton &button);
signals:
void SelectionChanged(const SourceSettingButton &);
private:
void PopulateSelection(const OBSWeakSource &source);
};
} // namespace advss } // namespace advss

View File

@@ -249,14 +249,21 @@ SourceSettingSelection::SourceSettingSelection(QWidget *parent)
setLayout(layout); setLayout(layout);
} }
void SourceSettingSelection::SetSource(const OBSWeakSource &source) void SourceSettingSelection::SetSource(const OBSWeakSource &source,
bool restorePreviousSelection)
{ {
_settings->clear(); const auto previousSelection = _settings->currentData();
Populate(source); Populate(source);
if (restorePreviousSelection) {
_settings->setCurrentIndex(
_settings->findData(previousSelection));
}
} }
void SourceSettingSelection::SetSetting(const SourceSetting &setting) void SourceSettingSelection::SetSelection(const OBSWeakSource &source,
const SourceSetting &setting)
{ {
SetSource(source, false);
QVariant variant; QVariant variant;
variant.setValue(setting); variant.setValue(setting);
_settings->setCurrentIndex(_settings->findData(variant)); _settings->setCurrentIndex(_settings->findData(variant));
@@ -281,6 +288,7 @@ void SourceSettingSelection::SelectionIdxChanged(int idx)
void SourceSettingSelection::Populate(const OBSWeakSource &source) void SourceSettingSelection::Populate(const OBSWeakSource &source)
{ {
_settings->clear();
OBSSourceAutoRelease s = obs_weak_source_get_source(source); OBSSourceAutoRelease s = obs_weak_source_get_source(source);
auto settings = GetSoruceSettings(s); auto settings = GetSoruceSettings(s);
for (const auto &setting : settings) { for (const auto &setting : settings) {

View File

@@ -40,8 +40,9 @@ class SourceSettingSelection : public QWidget {
public: public:
SourceSettingSelection(QWidget *parent = nullptr); SourceSettingSelection(QWidget *parent = nullptr);
void SetSource(const OBSWeakSource &); void SetSource(const OBSWeakSource &,
void SetSetting(const SourceSetting &); bool restorePreviousSelection = true);
void SetSelection(const OBSWeakSource &source, const SourceSetting &);
private slots: private slots:
void SelectionIdxChanged(int); void SelectionIdxChanged(int);

View File

@@ -40,7 +40,7 @@ void WSConnectionsTable::Add()
{ {
auto newConnection = std::make_shared<WSConnection>(); auto newConnection = std::make_shared<WSConnection>();
auto accepted = WSConnectionSettingsDialog::AskForSettings( auto accepted = WSConnectionSettingsDialog::AskForSettings(
this, *newConnection); GetSettingsWindow(), *newConnection);
if (!accepted) { if (!accepted) {
return; return;
} }
@@ -143,7 +143,7 @@ static void openSettingsDialog()
auto oldName = connection->Name(); auto oldName = connection->Name();
bool accepted = WSConnectionSettingsDialog::AskForSettings( bool accepted = WSConnectionSettingsDialog::AskForSettings(
tabWidget->Table(), *connection.get()); GetSettingsWindow(), *connection.get());
if (accepted && oldName != connection->Name()) { if (accepted && oldName != connection->Name()) {
ConnectionSelectionSignalManager::Instance()->Rename( ConnectionSelectionSignalManager::Instance()->Rename(
QString::fromStdString(oldName), QString::fromStdString(oldName),

View File

@@ -139,7 +139,7 @@ void TwitchCategorySelection::SelectionChanged(int index)
{ {
TwitchCategory category{itemData(index).toInt(), TwitchCategory category{itemData(index).toInt(),
currentText().toStdString()}; currentText().toStdString()};
emit CategoreyChanged(category); emit CategoryChanged(category);
} }
std::map<QString, int> CategoryGrabber::_categoryMap = {}; std::map<QString, int> CategoryGrabber::_categoryMap = {};
@@ -193,7 +193,7 @@ void CategoryGrabber::Search(const std::string &)
httplib::Params params = {{"first", "100"}, httplib::Params params = {{"first", "100"},
{"after", cursor}, {"after", cursor},
{"query", _searchString}}; {"query", _searchString}};
auto response = SendGetRequest(*_token, uri, path, params); auto response = SendGetRequest(*_token, uri, path, params, true);
while (response.status == 200 && !_stop) { while (response.status == 200 && !_stop) {
cursor = ParseReply(response.data); cursor = ParseReply(response.data);
@@ -203,7 +203,7 @@ void CategoryGrabber::Search(const std::string &)
params = {{"first", "100"}, params = {{"first", "100"},
{"after", cursor}, {"after", cursor},
{"query", _searchString}}; {"query", _searchString}};
response = SendGetRequest(*_token, uri, path, params); response = SendGetRequest(*_token, uri, path, params, true);
emit CategoryCountUpdated(_categoryMap.size() - startCount); emit CategoryCountUpdated(_categoryMap.size() - startCount);
} }
} }
@@ -217,7 +217,7 @@ void CategoryGrabber::GetAll()
static std::string cursor; static std::string cursor;
httplib::Params params = {{"first", "100"}, {"after", cursor}}; httplib::Params params = {{"first", "100"}, {"after", cursor}};
auto response = SendGetRequest(*_token, uri, path, params); auto response = SendGetRequest(*_token, uri, path, params, true);
while (response.status == 200 && !_stop) { while (response.status == 200 && !_stop) {
cursor = ParseReply(response.data); cursor = ParseReply(response.data);
@@ -225,7 +225,7 @@ void CategoryGrabber::GetAll()
break; // End of category list break; // End of category list
} }
params = {{"first", "100"}, {"after", cursor}}; params = {{"first", "100"}, {"after", cursor}};
response = SendGetRequest(*_token, uri, path, params); response = SendGetRequest(*_token, uri, path, params, true);
emit CategoryCountUpdated(_categoryMap.size()); emit CategoryCountUpdated(_categoryMap.size());
} }
} }
@@ -364,8 +364,8 @@ TwitchCategoryWidget::TwitchCategoryWidget(QWidget *parent)
_manualSearch(new TwitchCategorySearchButton(this)) _manualSearch(new TwitchCategorySearchButton(this))
{ {
QWidget::connect(_selection, QWidget::connect(_selection,
SIGNAL(CategoreyChanged(const TwitchCategory &)), this, SIGNAL(CategoryChanged(const TwitchCategory &)), this,
SIGNAL(CategoreyChanged(const TwitchCategory &))); SIGNAL(CategoryChanged(const TwitchCategory &)));
auto layout = new QHBoxLayout(); auto layout = new QHBoxLayout();
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);

View File

@@ -78,7 +78,7 @@ private slots:
void UpdateCategoryList(); void UpdateCategoryList();
signals: signals:
void CategoreyChanged(const TwitchCategory &); void CategoryChanged(const TwitchCategory &);
protected: protected:
void showPopup() override; void showPopup() override;
@@ -125,7 +125,7 @@ public:
void SetToken(const std::weak_ptr<TwitchToken> &); void SetToken(const std::weak_ptr<TwitchToken> &);
signals: signals:
void CategoreyChanged(const TwitchCategory &); void CategoryChanged(const TwitchCategory &);
private: private:
TwitchCategorySelection *_selection; TwitchCategorySelection *_selection;

View File

@@ -1,4 +1,5 @@
#include "channel-selection.hpp" #include "channel-selection.hpp"
#include "log-helper.hpp"
#include "twitch-helpers.hpp" #include "twitch-helpers.hpp"
#include "token.hpp" #include "token.hpp"
@@ -36,7 +37,7 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const
} }
auto res = SendGetRequest(token, "https://api.twitch.tv", auto res = SendGetRequest(token, "https://api.twitch.tv",
"/helix/users", {{"login", _name}}); "/helix/users", {{"login", _name}}, true);
if (res.status == 400) { if (res.status == 400) {
userIDCache[_name] = "invalid"; userIDCache[_name] = "invalid";

View File

@@ -196,12 +196,12 @@ std::string EventSub::AddEventSubscribtion(std::shared_ptr<TwitchToken> token,
return ""; return "";
} }
std::lock_guard<std::mutex> lock(eventSub->_subscriptionMtx); std::unique_lock<std::mutex> lock(eventSub->_subscriptionMtx);
if (!eventSub->_connected) { if (!eventSub->_connected) {
std::thread t([eventSub]() { eventSub->Connect(); });
t.detach();
vblog(LOG_INFO, "Twitch EventSub connect started for %s", vblog(LOG_INFO, "Twitch EventSub connect started for %s",
token->GetName().c_str()); token->GetName().c_str());
lock.unlock();
eventSub->Connect();
return ""; return "";
} }

View File

@@ -3,6 +3,7 @@
#include <log-helper.hpp> #include <log-helper.hpp>
#include <layout-helpers.hpp> #include <layout-helpers.hpp>
#include <ui-helpers.hpp>
namespace advss { namespace advss {
@@ -21,6 +22,18 @@ void MacroActionTwitch::ResolveVariablesToFixedValues()
_announcementMessage.ResolveVariables(); _announcementMessage.ResolveVariables();
_channel.ResolveVariables(); _channel.ResolveVariables();
_chatMessage.ResolveVariables(); _chatMessage.ResolveVariables();
_userLogin.ResolveVariables();
_userId.ResolveVariables();
_useVariableForRewardSelection = false;
auto token = _token.lock();
if (token) {
ResolveVariableSelectionToRewardId(token);
_pointsReward.title = _lastResolvedRewardTitle;
_pointsReward.id = _lastResolvedRewardId;
} else {
_pointsReward.id = "";
_pointsReward.id = "";
}
} }
std::shared_ptr<MacroAction> MacroActionTwitch::Create(Macro *m) std::shared_ptr<MacroAction> MacroActionTwitch::Create(Macro *m)
@@ -58,7 +71,11 @@ const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
{MacroActionTwitch::Action::CHAT_EMOTE_ONLY_DISABLE, {MacroActionTwitch::Action::CHAT_EMOTE_ONLY_DISABLE,
"AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable"}, "AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable"},
{MacroActionTwitch::Action::SEND_CHAT_MESSAGE, {MacroActionTwitch::Action::SEND_CHAT_MESSAGE,
"AdvSceneSwitcher.action.twitch.type.sendChatMessage"}, "AdvSceneSwitcher.action.twitch.type.chat.sendMessage"},
{MacroActionTwitch::Action::USER_GET_INFO,
"AdvSceneSwitcher.action.twitch.type.user.getInfo"},
{MacroActionTwitch::Action::POINTS_REWARD_GET_INFO,
"AdvSceneSwitcher.action.twitch.type.reward.getInfo"},
}; };
const static std::map<MacroActionTwitch::AnnouncementColor, std::string> const static std::map<MacroActionTwitch::AnnouncementColor, std::string>
@@ -264,6 +281,228 @@ void MacroActionTwitch::SendChatMessage(
_chatConnection->SendChatMessage(_chatMessage); _chatConnection->SendChatMessage(_chatMessage);
} }
void MacroActionTwitch::GetUserInfo(const std::shared_ptr<TwitchToken> &token)
{
httplib::Params params;
switch (_userInfoQueryType) {
case UserInfoQueryType::ID:
params.insert({"id", std::to_string(_userId)});
break;
case UserInfoQueryType::LOGIN:
params.insert({"login", _userLogin});
break;
default:
break;
}
auto result = SendGetRequest(*token, "https://api.twitch.tv",
"/helix/users", params, true);
if (result.status != 200) {
blog(LOG_INFO, "Failed get user info! (%d)\n", result.status);
return;
}
OBSDataArrayAutoRelease array = obs_data_get_array(result.data, "data");
size_t count = obs_data_array_count(array);
if (count == 0) {
blog(LOG_WARNING, "%s did not return any data!", __func__);
return;
}
OBSDataAutoRelease data = obs_data_array_item(array, 0);
SetJsonTempVars(data, [this](const char *id, const char *value) {
SetTempVarValue(id, value);
});
}
bool MacroActionTwitch::ResolveVariableSelectionToRewardId(
const std::shared_ptr<TwitchToken> &token)
{
if (!token) {
return false;
}
auto variable = _rewardVariable.lock();
if (!variable) {
return false;
}
const auto rewardTitle = variable->Value();
if (rewardTitle.empty()) {
return false;
}
if (_lastResolvedRewardTitle == rewardTitle) {
return true;
}
const auto rewards = GetPointsRewardsForChannel(token, _channel);
if (!rewards) {
return false;
}
for (const auto &reward : *rewards) {
if (reward.title == rewardTitle) {
_lastResolvedRewardId = reward.id;
_lastResolvedRewardTitle = reward.title;
return true;
}
}
return false;
}
void MacroActionTwitch::GetRewardInfo(const std::shared_ptr<TwitchToken> &token)
{
if (_useVariableForRewardSelection &&
!ResolveVariableSelectionToRewardId(token)) {
if (VerboseLoggingEnabled()) {
auto variable = _rewardVariable.lock();
if (!variable) {
blog(LOG_WARNING,
"failed to resolve variable reward name to reward id (invalid selection)");
return;
}
blog(LOG_WARNING,
"failed to resolve variable reward name '%s' to reward id",
variable->Value().c_str());
return;
}
return;
}
httplib::Params params = {
{"broadcaster_id", token->GetUserID()},
{"id", _useVariableForRewardSelection ? _lastResolvedRewardId
: _pointsReward.id},
};
auto result = SendGetRequest(*token, "https://api.twitch.tv",
"/helix/channel_points/custom_rewards",
params, true);
if (result.status != 200) {
blog(LOG_INFO, "Failed get reward info! (%d)\n", result.status);
return;
}
OBSDataArrayAutoRelease array = obs_data_get_array(result.data, "data");
size_t count = obs_data_array_count(array);
if (count == 0) {
blog(LOG_WARNING, "%s did not return any data!", __func__);
return;
}
OBSDataAutoRelease data = obs_data_array_item(array, 0);
SetJsonTempVars(data, [this](const char *id, const char *value) {
SetTempVarValue(id, value);
});
OBSDataAutoRelease image = obs_data_get_obj(data, "image");
SetTempVarValue("image.url_4x", obs_data_get_string(image, "url_4x"));
OBSDataAutoRelease default_image =
obs_data_get_obj(data, "default_image");
SetTempVarValue("default_image.url_4x",
obs_data_get_string(default_image, "url_4x"));
OBSDataAutoRelease max_per_stream_setting =
obs_data_get_obj(data, "max_per_stream_setting");
SetTempVarValue("max_per_stream.is_enabled",
obs_data_get_bool(max_per_stream_setting,
"is_enabled"));
SetTempVarValue("max_per_stream.max_per_stream",
std::to_string(obs_data_get_int(max_per_stream_setting,
"max_per_stream")));
OBSDataAutoRelease max_per_user_per_stream_setting =
obs_data_get_obj(data, "max_per_user_per_stream_setting");
SetTempVarValue("max_per_user_per_stream.is_enabled",
obs_data_get_bool(max_per_user_per_stream_setting,
"is_enabled"));
SetTempVarValue(
"max_per_user_per_stream.max_per_user_per_stream",
std::to_string(obs_data_get_int(max_per_user_per_stream_setting,
"max_per_user_per_stream")));
OBSDataAutoRelease global_cooldown_setting =
obs_data_get_obj(data, "global_cooldown_setting");
SetTempVarValue("global_cooldown.is_enabled",
obs_data_get_bool(global_cooldown_setting,
"is_enabled"));
SetTempVarValue(
"max_per_user_per_stream.global_cooldown_seconds",
std::to_string(obs_data_get_int(max_per_user_per_stream_setting,
"global_cooldown_seconds")));
}
static std::string tryTranslate(const std::string &testString)
{
auto translated = obs_module_text(testString.c_str());
if (testString != translated) {
return translated;
}
return "";
}
void MacroActionTwitch::SetupTempVars()
{
MacroAction::SetupTempVars();
auto setupTempVarHelper = [&](const std::string &id,
const std::string &extra = "") {
std::string name = tryTranslate(
"AdvSceneSwitcher.tempVar.twitch." + id + extra);
std::string description =
tryTranslate("AdvSceneSwitcher.tempVar.twitch." + id +
extra + ".description");
AddTempvar(id, name.empty() ? id : name, description);
};
switch (_action) {
case Action::POINTS_REWARD_GET_INFO:
setupTempVarHelper("title", ".reward");
setupTempVarHelper("prompt", ".reward");
setupTempVarHelper("cost", ".reward");
setupTempVarHelper("background_color", ".reward");
setupTempVarHelper("is_enabled", ".reward");
setupTempVarHelper("is_user_input_required", ".reward");
setupTempVarHelper("is_paused", ".reward");
setupTempVarHelper("is_in_stock", ".reward");
setupTempVarHelper("should_redemptions_skip_request_queue",
".reward");
setupTempVarHelper("redemptions_redeemed_current_stream",
".reward");
setupTempVarHelper("cooldown_expires_at", ".reward");
setupTempVarHelper("max_per_stream.is_enabled", ".reward");
setupTempVarHelper("max_per_stream.max_per_stream", ".reward");
setupTempVarHelper("max_per_user_per_stream.is_enabled",
".reward");
setupTempVarHelper(
"max_per_user_per_stream.max_per_user_per_stream",
".reward");
setupTempVarHelper("global_cooldown.is_enabled", ".reward");
setupTempVarHelper("global_cooldown.global_cooldown_seconds",
".reward");
setupTempVarHelper("image.url_4x", ".reward");
setupTempVarHelper("default_image.url_4x", ".reward");
break;
case Action::USER_GET_INFO:
setupTempVarHelper("id", ".user.getInfo");
setupTempVarHelper("login", ".user.getInfo");
setupTempVarHelper("display_name", ".user.getInfo");
setupTempVarHelper("type", ".user.getInfo");
setupTempVarHelper("broadcaster_type", ".user.getInfo");
setupTempVarHelper("description", ".user.getInfo");
setupTempVarHelper("profile_image_url", ".user.getInfo");
setupTempVarHelper("offline_image_url", ".user.getInfo");
setupTempVarHelper("created_at", ".user.getInfo");
break;
default:
break;
}
}
bool MacroActionTwitch::PerformAction() bool MacroActionTwitch::PerformAction()
{ {
auto token = _token.lock(); auto token = _token.lock();
@@ -298,9 +537,16 @@ bool MacroActionTwitch::PerformAction()
break; break;
case Action::CHAT_EMOTE_ONLY_DISABLE: case Action::CHAT_EMOTE_ONLY_DISABLE:
SetChatEmoteOnlyMode(token, false); SetChatEmoteOnlyMode(token, false);
break;
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE: case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
SendChatMessage(token); SendChatMessage(token);
break; break;
case MacroActionTwitch::Action::USER_GET_INFO:
GetUserInfo(token);
break;
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
GetRewardInfo(token);
break;
default: default:
break; break;
} }
@@ -338,6 +584,15 @@ bool MacroActionTwitch::Save(obs_data_t *obj) const
static_cast<int>(_announcementColor)); static_cast<int>(_announcementColor));
_channel.Save(obj); _channel.Save(obj);
_chatMessage.Save(obj, "chatMessage"); _chatMessage.Save(obj, "chatMessage");
obs_data_set_int(obj, "userInfoQueryType",
static_cast<int>(_userInfoQueryType));
_userLogin.Save(obj, "userLogin");
_userId.Save(obj, "userId");
_pointsReward.Save(obj);
obs_data_set_string(obj, "rewardVariable",
GetWeakVariableName(_rewardVariable).c_str());
obs_data_set_bool(obj, "useVariableForRewardSelection",
_useVariableForRewardSelection);
return true; return true;
} }
@@ -357,6 +612,16 @@ bool MacroActionTwitch::Load(obs_data_t *obj)
obs_data_get_int(obj, "announcementColor")); obs_data_get_int(obj, "announcementColor"));
_channel.Load(obj); _channel.Load(obj);
_chatMessage.Load(obj, "chatMessage"); _chatMessage.Load(obj, "chatMessage");
_userInfoQueryType = static_cast<UserInfoQueryType>(
obs_data_get_int(obj, "userInfoQueryType"));
_userLogin.Load(obj, "userLogin");
_userId.Load(obj, "userId");
_pointsReward.Load(obj);
_rewardVariable = GetWeakVariableByName(
obs_data_get_string(obj, "rewardVariable"));
_useVariableForRewardSelection =
obs_data_get_bool(obj, "useVariableForRewardSelection");
SetAction(static_cast<Action>(obs_data_get_int(obj, "action"))); SetAction(static_cast<Action>(obs_data_get_int(obj, "action")));
return true; return true;
@@ -366,117 +631,139 @@ void MacroActionTwitch::SetAction(Action action)
{ {
_action = action; _action = action;
ResetChatConnection(); ResetChatConnection();
SetupTempVars();
} }
bool MacroActionTwitch::ActionIsSupportedByToken() bool MacroActionTwitch::ActionIsSupportedByToken()
{ {
static const std::unordered_map<Action, TokenOption> requiredOption = { static const std::unordered_map<Action, std::vector<TokenOption>>
{Action::CHANNEL_INFO_TITLE_SET, {"channel:manage:broadcast"}}, requiredOption = {
{Action::CHANNEL_INFO_CATEGORY_SET, {Action::CHANNEL_INFO_TITLE_SET,
{"channel:manage:broadcast"}}, {{"channel:manage:broadcast"}}},
{Action::CHANNEL_INFO_LANGUAGE_SET, {Action::CHANNEL_INFO_CATEGORY_SET,
{"channel:manage:broadcast"}}, {{"channel:manage:broadcast"}}},
{Action::CHANNEL_INFO_DELAY_SET, {"channel:manage:broadcast"}}, {Action::CHANNEL_INFO_LANGUAGE_SET,
{Action::CHANNEL_INFO_BRANDED_CONTENT_ENABLE, {{"channel:manage:broadcast"}}},
{"channel:manage:broadcast"}}, {Action::CHANNEL_INFO_DELAY_SET,
{Action::CHANNEL_INFO_BRANDED_CONTENT_DISABLE, {{"channel:manage:broadcast"}}},
{"channel:manage:broadcast"}}, {Action::CHANNEL_INFO_BRANDED_CONTENT_ENABLE,
{Action::RAID_START, {"channel:manage:raids"}}, {{"channel:manage:broadcast"}}},
{Action::RAID_END, {"channel:manage:raids"}}, {Action::CHANNEL_INFO_BRANDED_CONTENT_DISABLE,
{Action::SHOUTOUT_SEND, {"moderator:manage:shoutouts"}}, {{"channel:manage:broadcast"}}},
{Action::POLL_END, {"channel:manage:polls"}}, {Action::RAID_START, {{"channel:manage:raids"}}},
{Action::PREDICTION_END, {"channel:manage:predictions"}}, {Action::RAID_END, {{"channel:manage:raids"}}},
{Action::SHIELD_MODE_START, {"moderator:manage:shield_mode"}}, {Action::SHOUTOUT_SEND,
{Action::SHIELD_MODE_END, {"moderator:manage:shield_mode"}}, {{"moderator:manage:shoutouts"}}},
{Action::POINTS_REWARD_ENABLE, {"channel:manage:redemptions"}}, {Action::POLL_END, {{"channel:manage:polls"}}},
{Action::POINTS_REWARD_DISABLE, {"channel:manage:redemptions"}}, {Action::PREDICTION_END,
{Action::POINTS_REWARD_PAUSE, {"channel:manage:redemptions"}}, {{"channel:manage:predictions"}}},
{Action::POINTS_REWARD_UNPAUSE, {"channel:manage:redemptions"}}, {Action::SHIELD_MODE_START,
{Action::POINTS_REWARD_TITLE_SET, {{"moderator:manage:shield_mode"}}},
{"channel:manage:redemptions"}}, {Action::SHIELD_MODE_END,
{Action::POINTS_REWARD_PROMPT_SET, {{"moderator:manage:shield_mode"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_ENABLE,
{Action::POINTS_REWARD_COST_SET, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_DISABLE,
{Action::POINTS_REWARD_USER_INPUT_REQUIRE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_PAUSE,
{Action::POINTS_REWARD_USER_INPUT_UNREQUIRE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_UNPAUSE,
{Action::POINTS_REWARD_COOLDOWN_ENABLE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_TITLE_SET,
{Action::POINTS_REWARD_COOLDOWN_DISABLE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_PROMPT_SET,
{Action::POINTS_REWARD_QUEUE_SKIP_ENABLE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_COST_SET,
{Action::POINTS_REWARD_QUEUE_SKIP_DISABLE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_USER_INPUT_REQUIRE,
{Action::POINTS_REWARD_MAX_PER_STREAM_ENABLE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_USER_INPUT_UNREQUIRE,
{Action::POINTS_REWARD_MAX_PER_STREAM_DISABLE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_COOLDOWN_ENABLE,
{Action::POINTS_REWARD_MAX_PER_USER_ENABLE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_COOLDOWN_DISABLE,
{Action::POINTS_REWARD_MAX_PER_USER_DISABLE, {{"channel:manage:redemptions"}}},
{"channel:manage:redemptions"}}, {Action::POINTS_REWARD_QUEUE_SKIP_ENABLE,
{Action::POINTS_REWARD_DELETE, {"channel:manage:redemptions"}}, {{"channel:manage:redemptions"}}},
{Action::POINTS_REWARD_REDEMPTION_CANCEL, {Action::POINTS_REWARD_QUEUE_SKIP_DISABLE,
{"channel:manage:redemptions"}}, {{"channel:manage:redemptions"}}},
{Action::POINTS_REWARD_REDEMPTION_FULFILL, {Action::POINTS_REWARD_MAX_PER_STREAM_ENABLE,
{"channel:manage:redemptions"}}, {{"channel:manage:redemptions"}}},
{Action::USER_BAN, {"moderator:manage:banned_users"}}, {Action::POINTS_REWARD_MAX_PER_STREAM_DISABLE,
{Action::USER_UNBAN, {"moderator:manage:banned_users"}}, {{"channel:manage:redemptions"}}},
{Action::USER_BLOCK, {"user:manage:blocked_users"}}, {Action::POINTS_REWARD_MAX_PER_USER_ENABLE,
{Action::USER_UNBLOCK, {"user:manage:blocked_users"}}, {{"channel:manage:redemptions"}}},
{Action::USER_MODERATOR_ADD, {"channel:manage:moderators"}}, {Action::POINTS_REWARD_MAX_PER_USER_DISABLE,
{Action::USER_MODERATOR_DELETE, {"channel:manage:moderators"}}, {{"channel:manage:redemptions"}}},
{Action::USER_VIP_ADD, {"channel:manage:vips"}}, {Action::POINTS_REWARD_DELETE,
{Action::USER_VIP_DELETE, {"channel:manage:vips"}}, {{"channel:manage:redemptions"}}},
{Action::COMMERCIAL_START, {"channel:edit:commercial"}}, {Action::POINTS_REWARD_REDEMPTION_CANCEL,
{Action::COMMERCIAL_SNOOZE, {"channel:manage:ads"}}, {{"channel:manage:redemptions"}}},
{Action::MARKER_CREATE, {"channel:manage:broadcast"}}, {Action::POINTS_REWARD_REDEMPTION_FULFILL,
{Action::CLIP_CREATE, {"clips:edit"}}, {{"channel:manage:redemptions"}}},
{Action::CHAT_ANNOUNCEMENT_SEND, {Action::USER_BAN, {{"moderator:manage:banned_users"}}},
{"moderator:manage:announcements"}}, {Action::USER_UNBAN,
{Action::CHAT_EMOTE_ONLY_ENABLE, {{"moderator:manage:banned_users"}}},
{"moderator:manage:chat_settings"}}, {Action::USER_BLOCK, {{"user:manage:blocked_users"}}},
{Action::CHAT_EMOTE_ONLY_DISABLE, {Action::USER_UNBLOCK, {{"user:manage:blocked_users"}}},
{"moderator:manage:chat_settings"}}, {Action::USER_MODERATOR_ADD,
{Action::CHAT_FOLLOWER_ONLY_ENABLE, {{"channel:manage:moderators"}}},
{"moderator:manage:chat_settings"}}, {Action::USER_MODERATOR_DELETE,
{Action::CHAT_FOLLOWER_ONLY_DISABLE, {{"channel:manage:moderators"}}},
{"moderator:manage:chat_settings"}}, {Action::USER_VIP_ADD, {{"channel:manage:vips"}}},
{Action::CHAT_SUBSCRIBER_ONLY_ENABLE, {Action::USER_VIP_DELETE, {{"channel:manage:vips"}}},
{"moderator:manage:chat_settings"}}, {Action::COMMERCIAL_START,
{Action::CHAT_SUBSCRIBER_ONLY_DISABLE, {{"channel:edit:commercial"}}},
{"moderator:manage:chat_settings"}}, {Action::COMMERCIAL_SNOOZE, {{"channel:manage:ads"}}},
{Action::CHAT_SLOW_MODE_ENABLE, {Action::MARKER_CREATE, {{"channel:manage:broadcast"}}},
{"moderator:manage:chat_settings"}}, {Action::CLIP_CREATE, {{"clips:edit"}}},
{Action::CHAT_SLOW_MODE_DISABLE, {Action::CHAT_ANNOUNCEMENT_SEND,
{"moderator:manage:chat_settings"}}, {{"moderator:manage:announcements"}}},
{Action::CHAT_NON_MODERATOR_DELAY_ENABLE, {Action::CHAT_EMOTE_ONLY_ENABLE,
{"moderator:manage:chat_settings"}}, {{"moderator:manage:chat_settings"}}},
{Action::CHAT_NON_MODERATOR_DELAY_DISABLE, {Action::CHAT_EMOTE_ONLY_DISABLE,
{"moderator:manage:chat_settings"}}, {{"moderator:manage:chat_settings"}}},
{Action::CHAT_UNIQUE_MODE_ENABLE, {Action::CHAT_FOLLOWER_ONLY_ENABLE,
{"moderator:manage:chat_settings"}}, {{"moderator:manage:chat_settings"}}},
{Action::CHAT_UNIQUE_MODE_DISABLE, {Action::CHAT_FOLLOWER_ONLY_DISABLE,
{"moderator:manage:chat_settings"}}, {{"moderator:manage:chat_settings"}}},
{Action::WHISPER_SEND, {"user:manage:whispers"}}, {Action::CHAT_SUBSCRIBER_ONLY_ENABLE,
{Action::SEND_CHAT_MESSAGE, {"chat:edit"}}, {{"moderator:manage:chat_settings"}}},
}; {Action::CHAT_SUBSCRIBER_ONLY_DISABLE,
{{"moderator:manage:chat_settings"}}},
{Action::CHAT_SLOW_MODE_ENABLE,
{{"moderator:manage:chat_settings"}}},
{Action::CHAT_SLOW_MODE_DISABLE,
{{"moderator:manage:chat_settings"}}},
{Action::CHAT_NON_MODERATOR_DELAY_ENABLE,
{{"moderator:manage:chat_settings"}}},
{Action::CHAT_NON_MODERATOR_DELAY_DISABLE,
{{"moderator:manage:chat_settings"}}},
{Action::CHAT_UNIQUE_MODE_ENABLE,
{{"moderator:manage:chat_settings"}}},
{Action::CHAT_UNIQUE_MODE_DISABLE,
{{"moderator:manage:chat_settings"}}},
{Action::WHISPER_SEND, {{"user:manage:whispers"}}},
{Action::SEND_CHAT_MESSAGE, {{"chat:edit"}}},
{Action::USER_GET_INFO, {}},
{Action::POINTS_REWARD_GET_INFO,
{{"channel:read:redemptions"},
{"channel:manage:redemptions"}}}};
auto token = _token.lock(); auto token = _token.lock();
if (!token) { if (!token) {
return false; return false;
} }
auto option = requiredOption.find(_action); auto it = requiredOption.find(_action);
assert(option != requiredOption.end()); assert(it != requiredOption.end());
if (option == requiredOption.end()) { if (it == requiredOption.end()) {
return false; return false;
} }
return token->OptionIsEnabled(option->second); const auto &[_, options] = *it;
return token->AnyOptionIsEnabled(options);
} }
void MacroActionTwitch::ResetChatConnection() void MacroActionTwitch::ResetChatConnection()
@@ -507,6 +794,18 @@ static inline void populateAnnouncementColorSelection(QComboBox *list)
} }
} }
static void populateUserQueryInfoTypeSelection(QComboBox *list)
{
list->addItem(
obs_module_text(
"AdvSceneSwitcher.action.twitch.user.getInfo.queryType.id"),
static_cast<int>(MacroActionTwitch::UserInfoQueryType::ID));
list->addItem(
obs_module_text(
"AdvSceneSwitcher.action.twitch.user.getInfo.queryType.login"),
static_cast<int>(MacroActionTwitch::UserInfoQueryType::LOGIN));
}
MacroActionTwitchEdit::MacroActionTwitchEdit( MacroActionTwitchEdit::MacroActionTwitchEdit(
QWidget *parent, std::shared_ptr<MacroActionTwitch> entryData) QWidget *parent, std::shared_ptr<MacroActionTwitch> entryData)
: QWidget(parent), : QWidget(parent),
@@ -523,7 +822,13 @@ MacroActionTwitchEdit::MacroActionTwitchEdit(
_announcementMessage(new VariableTextEdit(this)), _announcementMessage(new VariableTextEdit(this)),
_announcementColor(new QComboBox(this)), _announcementColor(new QComboBox(this)),
_channel(new TwitchChannelSelection(this)), _channel(new TwitchChannelSelection(this)),
_chatMessage(new VariableTextEdit(this)) _chatMessage(new VariableTextEdit(this)),
_userInfoQueryType(new QComboBox(this)),
_userLogin(new VariableLineEdit(this)),
_userId(new VariableSpinBox(this)),
_pointsReward(new TwitchPointsRewardWidget(this)),
_rewardVariable(new VariableSelection(this)),
_toggleRewardSelection(new QPushButton())
{ {
SetWidgetProperties(); SetWidgetProperties();
SetWidgetSignalConnections(); SetWidgetSignalConnections();
@@ -566,14 +871,11 @@ void MacroActionTwitchEdit::ActionChanged(int idx)
void MacroActionTwitchEdit::TwitchTokenChanged(const QString &token) void MacroActionTwitchEdit::TwitchTokenChanged(const QString &token)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_token = GetWeakTwitchTokenByQString(token); _entryData->_token = GetWeakTwitchTokenByQString(token);
_category->SetToken(_entryData->_token); _category->SetToken(_entryData->_token);
_channel->SetToken(_entryData->_token); _channel->SetToken(_entryData->_token);
_pointsReward->SetToken(_entryData->_token);
_entryData->ResetChatConnection(); _entryData->ResetChatConnection();
SetWidgetVisibility(); SetWidgetVisibility();
@@ -618,62 +920,38 @@ void MacroActionTwitchEdit::CheckToken()
void MacroActionTwitchEdit::StreamTitleChanged() void MacroActionTwitchEdit::StreamTitleChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_streamTitle = _streamTitle->text().toStdString(); _entryData->_streamTitle = _streamTitle->text().toStdString();
} }
void MacroActionTwitchEdit::CategoreyChanged(const TwitchCategory &category) void MacroActionTwitchEdit::CategoryChanged(const TwitchCategory &category)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_category = category; _entryData->_category = category;
} }
void MacroActionTwitchEdit::MarkerDescriptionChanged() void MacroActionTwitchEdit::MarkerDescriptionChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_markerDescription = _entryData->_markerDescription =
_markerDescription->text().toStdString(); _markerDescription->text().toStdString();
} }
void MacroActionTwitchEdit::ClipHasDelayChanged(int state) void MacroActionTwitchEdit::ClipHasDelayChanged(int state)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_clipHasDelay = state; _entryData->_clipHasDelay = state;
} }
void MacroActionTwitchEdit::DurationChanged(const Duration &duration) void MacroActionTwitchEdit::DurationChanged(const Duration &duration)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_duration = duration; _entryData->_duration = duration;
} }
void MacroActionTwitchEdit::AnnouncementMessageChanged() void MacroActionTwitchEdit::AnnouncementMessageChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_announcementMessage = _entryData->_announcementMessage =
_announcementMessage->toPlainText().toStdString(); _announcementMessage->toPlainText().toStdString();
@@ -683,11 +961,7 @@ void MacroActionTwitchEdit::AnnouncementMessageChanged()
void MacroActionTwitchEdit::AnnouncementColorChanged(int index) void MacroActionTwitchEdit::AnnouncementColorChanged(int index)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_announcementColor = _entryData->_announcementColor =
static_cast<MacroActionTwitch::AnnouncementColor>(index); static_cast<MacroActionTwitch::AnnouncementColor>(index);
} }
@@ -708,6 +982,18 @@ void MacroActionTwitchEdit::SetWidgetProperties()
populateActionSelection(_actions); populateActionSelection(_actions);
populateAnnouncementColorSelection(_announcementColor); populateAnnouncementColorSelection(_announcementColor);
populateUserQueryInfoTypeSelection(_userInfoQueryType);
_userId->setMaximum(999999999);
_toggleRewardSelection->setCheckable(true);
_toggleRewardSelection->setMaximumWidth(11);
SetButtonIcon(_toggleRewardSelection,
GetThemeTypeName() == "Light"
? ":/res/images/dots-vert.svg"
: "theme:Dark/dots-vert.svg");
_toggleRewardSelection->setToolTip(obs_module_text(
"AdvSceneSwitcher.action.twitch.reward.toggleControl"));
} }
void MacroActionTwitchEdit::SetWidgetSignalConnections() void MacroActionTwitchEdit::SetWidgetSignalConnections()
@@ -721,8 +1007,8 @@ void MacroActionTwitchEdit::SetWidgetSignalConnections()
QWidget::connect(_streamTitle, SIGNAL(editingFinished()), this, QWidget::connect(_streamTitle, SIGNAL(editingFinished()), this,
SLOT(StreamTitleChanged())); SLOT(StreamTitleChanged()));
QWidget::connect(_category, QWidget::connect(_category,
SIGNAL(CategoreyChanged(const TwitchCategory &)), this, SIGNAL(CategoryChanged(const TwitchCategory &)), this,
SLOT(CategoreyChanged(const TwitchCategory &))); SLOT(CategoryChanged(const TwitchCategory &)));
QWidget::connect(_markerDescription, SIGNAL(editingFinished()), this, QWidget::connect(_markerDescription, SIGNAL(editingFinished()), this,
SLOT(MarkerDescriptionChanged())); SLOT(MarkerDescriptionChanged()));
QObject::connect(_clipHasDelay, SIGNAL(stateChanged(int)), this, QObject::connect(_clipHasDelay, SIGNAL(stateChanged(int)), this,
@@ -738,6 +1024,23 @@ void MacroActionTwitchEdit::SetWidgetSignalConnections()
SLOT(ChannelChanged(const TwitchChannel &))); SLOT(ChannelChanged(const TwitchChannel &)));
QWidget::connect(_chatMessage, SIGNAL(textChanged()), this, QWidget::connect(_chatMessage, SIGNAL(textChanged()), this,
SLOT(ChatMessageChanged())); SLOT(ChatMessageChanged()));
QWidget::connect(_userInfoQueryType, SIGNAL(currentIndexChanged(int)),
this, SLOT(UserInfoQueryTypeChanged(int)));
QWidget::connect(_userLogin, SIGNAL(editingFinished()), this,
SLOT(UserLoginChanged()));
QWidget::connect(
_userId,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(UserIdChanged(const NumberVariable<int> &)));
QWidget::connect(
_pointsReward,
SIGNAL(PointsRewardChanged(const TwitchPointsReward &)), this,
SLOT(PointsRewardChanged(const TwitchPointsReward &)));
QWidget::connect(_rewardVariable,
SIGNAL(SelectionChanged(const QString &)), this,
SLOT(RewardVariableChanged(const QString &)));
QWidget::connect(_toggleRewardSelection, SIGNAL(toggled(bool)), this,
SLOT(ToggleRewardSelection(bool)));
} }
void MacroActionTwitchEdit::SetWidgetVisibility() void MacroActionTwitchEdit::SetWidgetVisibility()
@@ -754,7 +1057,9 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
_entryData->GetAction() == _entryData->GetAction() ==
MacroActionTwitch::Action::RAID_END || MacroActionTwitch::Action::RAID_END ||
_entryData->GetAction() == _entryData->GetAction() ==
MacroActionTwitch::Action::SEND_CHAT_MESSAGE); MacroActionTwitch::Action::SEND_CHAT_MESSAGE ||
_entryData->GetAction() ==
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO);
_duration->setVisible(_entryData->GetAction() == _duration->setVisible(_entryData->GetAction() ==
MacroActionTwitch::Action::COMMERCIAL_START); MacroActionTwitch::Action::COMMERCIAL_START);
_markerDescription->setVisible( _markerDescription->setVisible(
@@ -770,6 +1075,29 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
MacroActionTwitch::Action::CHAT_ANNOUNCEMENT_SEND); MacroActionTwitch::Action::CHAT_ANNOUNCEMENT_SEND);
_chatMessage->setVisible(_entryData->GetAction() == _chatMessage->setVisible(_entryData->GetAction() ==
MacroActionTwitch::Action::SEND_CHAT_MESSAGE); MacroActionTwitch::Action::SEND_CHAT_MESSAGE);
_userInfoQueryType->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::USER_GET_INFO);
_userLogin->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::USER_GET_INFO &&
_entryData->_userInfoQueryType ==
MacroActionTwitch::UserInfoQueryType::LOGIN);
_userId->setVisible(_entryData->GetAction() ==
MacroActionTwitch::Action::USER_GET_INFO &&
_entryData->_userInfoQueryType ==
MacroActionTwitch::UserInfoQueryType::ID);
_pointsReward->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO &&
!_entryData->_useVariableForRewardSelection);
_rewardVariable->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO &&
_entryData->_useVariableForRewardSelection);
_toggleRewardSelection->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO);
if (_entryData->GetAction() == if (_entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_INFO_TITLE_SET || MacroActionTwitch::Action::CHANNEL_INFO_TITLE_SET ||
@@ -788,46 +1116,104 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
void MacroActionTwitchEdit::ChannelChanged(const TwitchChannel &channel) void MacroActionTwitchEdit::ChannelChanged(const TwitchChannel &channel)
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_channel = channel; _entryData->_channel = channel;
_pointsReward->SetChannel(channel);
_entryData->ResetChatConnection(); _entryData->ResetChatConnection();
} }
void MacroActionTwitchEdit::ChatMessageChanged() void MacroActionTwitchEdit::ChatMessageChanged()
{ {
if (_loading || !_entryData) { GUARD_LOADING_AND_LOCK();
return;
}
auto lock = LockContext();
_entryData->_chatMessage = _chatMessage->toPlainText().toStdString(); _entryData->_chatMessage = _chatMessage->toPlainText().toStdString();
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();
} }
void MacroActionTwitchEdit::UserInfoQueryTypeChanged(int idx)
{
GUARD_LOADING_AND_LOCK();
_entryData->_userInfoQueryType =
static_cast<MacroActionTwitch::UserInfoQueryType>(
_userInfoQueryType->itemData(idx).toInt());
SetWidgetVisibility();
}
void MacroActionTwitchEdit::UserLoginChanged()
{
GUARD_LOADING_AND_LOCK();
_entryData->_userLogin = _userLogin->text().toStdString();
}
void MacroActionTwitchEdit::RewardVariableChanged(const QString &text)
{
GUARD_LOADING_AND_LOCK()
_entryData->_rewardVariable = GetWeakVariableByQString(text);
}
void MacroActionTwitchEdit::ToggleRewardSelection(bool)
{
GUARD_LOADING_AND_LOCK();
_entryData->_useVariableForRewardSelection =
!_entryData->_useVariableForRewardSelection;
SetWidgetVisibility();
}
void MacroActionTwitchEdit::UserIdChanged(const NumberVariable<int> &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_userId = value;
}
void MacroActionTwitchEdit::PointsRewardChanged(const TwitchPointsReward &reward)
{
GUARD_LOADING_AND_LOCK();
_entryData->_pointsReward = reward;
}
void MacroActionTwitchEdit::SetWidgetLayout() void MacroActionTwitchEdit::SetWidgetLayout()
{ {
const std::vector<QWidget *> widgets{ const std::vector<QWidget *> widgets{_tokens,
_tokens, _actions, _streamTitle, _actions,
_category, _markerDescription, _clipHasDelay, _streamTitle,
_duration, _announcementColor, _channel}; _category,
_markerDescription,
_clipHasDelay,
_duration,
_announcementColor,
_channel,
_userInfoQueryType,
_userLogin,
_userId,
_pointsReward,
_rewardVariable,
_toggleRewardSelection};
for (auto widget : widgets) { for (auto widget : widgets) {
_layout->removeWidget(widget); _layout->removeWidget(widget);
} }
ClearLayout(_layout); ClearLayout(_layout);
auto layoutText = const char *layoutText;
_entryData->GetAction() == switch (_entryData->GetAction()) {
MacroActionTwitch::Action::SEND_CHAT_MESSAGE case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
? obs_module_text( layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.chat") "AdvSceneSwitcher.action.twitch.entry.chat");
: obs_module_text( break;
"AdvSceneSwitcher.action.twitch.entry.default"); case MacroActionTwitch::Action::USER_GET_INFO:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.user.getInfo");
break;
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.reward.getInfo");
break;
default:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.entry.default");
break;
}
PlaceWidgets(layoutText, _layout, PlaceWidgets(layoutText, _layout,
{{"{{account}}", _tokens}, {{"{{account}}", _tokens},
{"{{actions}}", _actions}, {"{{actions}}", _actions},
@@ -837,7 +1223,14 @@ void MacroActionTwitchEdit::SetWidgetLayout()
{"{{clipHasDelay}}", _clipHasDelay}, {"{{clipHasDelay}}", _clipHasDelay},
{"{{duration}}", _duration}, {"{{duration}}", _duration},
{"{{announcementColor}}", _announcementColor}, {"{{announcementColor}}", _announcementColor},
{"{{channel}}", _channel}}); {"{{channel}}", _channel},
{"{{userInfoQueryType}}", _userInfoQueryType},
{"{{userLogin}}", _userLogin},
{"{{userId}}", _userId},
{"{{pointsReward}}", _pointsReward},
{"{{rewardVariable}}", _rewardVariable},
{"{{toggleRewardSelection}}", _toggleRewardSelection}});
_layout->setContentsMargins(0, 0, 0, 0); _layout->setContentsMargins(0, 0, 0, 0);
} }
@@ -862,6 +1255,14 @@ void MacroActionTwitchEdit::UpdateEntryData()
_channel->SetToken(_entryData->_token); _channel->SetToken(_entryData->_token);
_channel->SetChannel(_entryData->_channel); _channel->SetChannel(_entryData->_channel);
_chatMessage->setPlainText(_entryData->_chatMessage); _chatMessage->setPlainText(_entryData->_chatMessage);
_userInfoQueryType->setCurrentIndex(_userInfoQueryType->findData(
static_cast<int>(_entryData->_userInfoQueryType)));
_userLogin->setText(_entryData->_userLogin);
_userId->SetValue(_entryData->_userId);
_pointsReward->SetToken(_entryData->_token);
_pointsReward->SetChannel(_entryData->_channel);
_pointsReward->SetPointsReward(_entryData->_pointsReward);
_rewardVariable->SetVariable(_entryData->_rewardVariable);
SetWidgetVisibility(); SetWidgetVisibility();
} }

View File

@@ -54,6 +54,9 @@ public:
// Reward add // Reward add
POINTS_REWARD_ADD = 600, // TODO POINTS_REWARD_ADD = 600, // TODO
// Get reward information
POINTS_REWARD_GET_INFO = 650,
// Reward update // Reward update
POINTS_REWARD_ENABLE = 710, POINTS_REWARD_ENABLE = 710,
POINTS_REWARD_DISABLE = 711, POINTS_REWARD_DISABLE = 711,
@@ -134,6 +137,9 @@ public:
CHANNEL_SCHEDULE_SEGMENT_DELETE = 3500, // TODO CHANNEL_SCHEDULE_SEGMENT_DELETE = 3500, // TODO
SEND_CHAT_MESSAGE = 5000, SEND_CHAT_MESSAGE = 5000,
// Get user info
USER_GET_INFO = 6000
}; };
enum class AnnouncementColor { enum class AnnouncementColor {
@@ -146,6 +152,8 @@ public:
enum class RedemptionStatus { CANCELED, FULFILLED }; enum class RedemptionStatus { CANCELED, FULFILLED };
enum class UserInfoQueryType { ID, LOGIN };
bool PerformAction(); bool PerformAction();
void LogAction() const; void LogAction() const;
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
@@ -169,6 +177,12 @@ public:
AnnouncementColor _announcementColor = AnnouncementColor::PRIMARY; AnnouncementColor _announcementColor = AnnouncementColor::PRIMARY;
TwitchChannel _channel; TwitchChannel _channel;
StringVariable _chatMessage; StringVariable _chatMessage;
UserInfoQueryType _userInfoQueryType = UserInfoQueryType::LOGIN;
StringVariable _userLogin = "user login";
IntVariable _userId = 0;
TwitchPointsReward _pointsReward;
std::weak_ptr<Variable> _rewardVariable;
bool _useVariableForRewardSelection = false;
private: private:
void SetStreamTitle(const std::shared_ptr<TwitchToken> &) const; void SetStreamTitle(const std::shared_ptr<TwitchToken> &) const;
@@ -181,10 +195,20 @@ private:
bool enable) const; bool enable) const;
void StartRaid(const std::shared_ptr<TwitchToken> &); void StartRaid(const std::shared_ptr<TwitchToken> &);
void SendChatMessage(const std::shared_ptr<TwitchToken> &); void SendChatMessage(const std::shared_ptr<TwitchToken> &);
void GetUserInfo(const std::shared_ptr<TwitchToken> &);
void GetRewardInfo(const std::shared_ptr<TwitchToken> &);
bool ResolveVariableSelectionToRewardId(
const std::shared_ptr<TwitchToken> &);
void SetupTempVars();
Action _action = Action::CHANNEL_INFO_TITLE_SET; Action _action = Action::CHANNEL_INFO_TITLE_SET;
std::shared_ptr<TwitchChatConnection> _chatConnection; std::shared_ptr<TwitchChatConnection> _chatConnection;
std::string _lastResolvedRewardTitle;
std::string _lastResolvedRewardId;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };
@@ -210,7 +234,7 @@ private slots:
void TwitchTokenChanged(const QString &); void TwitchTokenChanged(const QString &);
void CheckToken(); void CheckToken();
void StreamTitleChanged(); void StreamTitleChanged();
void CategoreyChanged(const TwitchCategory &); void CategoryChanged(const TwitchCategory &);
void MarkerDescriptionChanged(); void MarkerDescriptionChanged();
void ClipHasDelayChanged(int state); void ClipHasDelayChanged(int state);
void DurationChanged(const Duration &); void DurationChanged(const Duration &);
@@ -218,6 +242,12 @@ private slots:
void AnnouncementColorChanged(int index); void AnnouncementColorChanged(int index);
void ChannelChanged(const TwitchChannel &); void ChannelChanged(const TwitchChannel &);
void ChatMessageChanged(); void ChatMessageChanged();
void UserInfoQueryTypeChanged(int);
void UserLoginChanged();
void UserIdChanged(const NumberVariable<int> &);
void PointsRewardChanged(const TwitchPointsReward &);
void RewardVariableChanged(const QString &);
void ToggleRewardSelection(bool);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -246,6 +276,12 @@ private:
QComboBox *_announcementColor; QComboBox *_announcementColor;
TwitchChannelSelection *_channel; TwitchChannelSelection *_channel;
VariableTextEdit *_chatMessage; VariableTextEdit *_chatMessage;
QComboBox *_userInfoQueryType;
VariableLineEdit *_userLogin;
VariableSpinBox *_userId;
TwitchPointsRewardWidget *_pointsReward;
VariableSelection *_rewardVariable;
QPushButton *_toggleRewardSelection;
bool _loading = true; bool _loading = true;
}; };

View File

@@ -257,65 +257,15 @@ void MacroConditionTwitch::ResetChatConnection()
_chatConnection.reset(); _chatConnection.reset();
} }
static void
setTempVarsHelper(const std::string &jsonStr,
std::function<void(const char *, const char *)> setVar)
{
try {
auto json = nlohmann::json::parse(jsonStr);
for (auto it = json.begin(); it != json.end(); ++it) {
if (it->is_string()) {
setVar(it.key().c_str(),
it->get<std::string>().c_str());
continue;
}
setVar(it.key().c_str(), it->dump().c_str());
}
} catch (const nlohmann::json::exception &e) {
vblog(LOG_INFO, "%s", jsonStr.c_str());
vblog(LOG_INFO, "%s", e.what());
}
}
static void
setTempVarsHelper(obs_data_t *data,
std::function<void(const char *, const char *)> setVar)
{
auto jsonStr = obs_data_get_json(data);
if (!jsonStr) {
return;
}
setTempVarsHelper(jsonStr, setVar);
}
bool MacroConditionTwitch::CheckChannelGenericEvents() bool MacroConditionTwitch::CheckChannelGenericEvents()
{ {
if (!_eventBuffer) { return HandleMatchingSubscriptionEvents([this](const Event &event) {
return false; SetVariableValue(event.ToString());
} SetJsonTempVars(event.data,
[this](const char *id, const char *value) {
while (!_eventBuffer->Empty()) { SetTempVarValue(id, value);
auto event = _eventBuffer->ConsumeMessage(); });
if (!event) { });
continue;
}
if (_subscriptionID != event->id) {
continue;
}
SetVariableValue(event->ToString());
setTempVarsHelper(event->data,
[this](const char *id, const char *value) {
SetTempVarValue(id, value);
});
if (_clearBufferOnMatch) {
_eventBuffer->Clear();
}
return true;
}
return false;
} }
bool MacroConditionTwitch::CheckChannelLiveEvents() bool MacroConditionTwitch::CheckChannelLiveEvents()
@@ -345,10 +295,105 @@ bool MacroConditionTwitch::CheckChannelLiveEvents()
} }
SetVariableValue(event->ToString()); SetVariableValue(event->ToString());
setTempVarsHelper(event->data, SetJsonTempVars(event->data,
[this](const char *id, const char *value) { [this](const char *id, const char *value) {
SetTempVarValue(id, value); SetTempVarValue(id, value);
}); });
if (_clearBufferOnMatch) {
_eventBuffer->Clear();
}
return true;
}
return false;
}
bool MacroConditionTwitch::CheckChannelRewardChangeEvents()
{
return HandleMatchingSubscriptionEvents([this](const Event &event) {
SetVariableValue(event.ToString());
SetJsonTempVars(event.data,
[this](const char *id, const char *value) {
SetTempVarValue(id, value);
});
OBSDataAutoRelease image =
obs_data_get_obj(event.data, "image");
SetTempVarValue("image.url_4x",
obs_data_get_string(image, "url_4x"));
OBSDataAutoRelease default_image =
obs_data_get_obj(event.data, "default_image");
SetTempVarValue("default_image.url_4x",
obs_data_get_string(default_image, "url_4x"));
OBSDataAutoRelease max_per_stream =
obs_data_get_obj(event.data, "max_per_stream");
SetTempVarValue("max_per_stream.is_enabled",
obs_data_get_bool(max_per_stream,
"is_enabled"));
SetTempVarValue("max_per_stream.max_per_stream",
std::to_string(obs_data_get_int(max_per_stream,
"value")));
OBSDataAutoRelease max_per_user_per_stream =
obs_data_get_obj(event.data, "max_per_user_per_stream");
SetTempVarValue("max_per_user_per_stream.is_enabled",
obs_data_get_bool(max_per_user_per_stream,
"is_enabled"));
SetTempVarValue(
"max_per_user_per_stream.max_per_user_per_stream",
std::to_string(obs_data_get_int(max_per_user_per_stream,
"value")));
OBSDataAutoRelease global_cooldown =
obs_data_get_obj(event.data, "global_cooldown");
SetTempVarValue("global_cooldown.is_enabled",
obs_data_get_bool(global_cooldown,
"is_enabled"));
SetTempVarValue(
"max_per_user_per_stream.global_cooldown_seconds",
std::to_string(obs_data_get_int(max_per_user_per_stream,
"seconds")));
});
}
bool MacroConditionTwitch::CheckChannelRewardRedemptionEvents()
{
return HandleMatchingSubscriptionEvents([this](const Event &event) {
SetVariableValue(event.ToString());
SetJsonTempVars(event.data,
[this](const char *id, const char *value) {
SetTempVarValue(id, value);
});
OBSDataAutoRelease rewardInfo =
obs_data_get_obj(event.data, "reward");
SetJsonTempVars(rewardInfo, [this](const char *nestedId,
const char *value) {
const std::string id =
"reward." + std::string(nestedId);
SetTempVarValue(id, value);
});
});
}
bool MacroConditionTwitch::HandleMatchingSubscriptionEvents(
const std::function<void(const Event &)> &matchCb)
{
if (!_eventBuffer) {
return false;
}
while (!_eventBuffer->Empty()) {
auto event = _eventBuffer->ConsumeMessage();
if (!event) {
continue;
}
if (_subscriptionID != event->id) {
continue;
}
matchCb(*event);
if (_clearBufferOnMatch) { if (_clearBufferOnMatch) {
_eventBuffer->Clear(); _eventBuffer->Clear();
@@ -618,16 +663,18 @@ bool MacroConditionTwitch::CheckCondition()
case Condition::CHARITY_CAMPAIGN_END_EVENT: case Condition::CHARITY_CAMPAIGN_END_EVENT:
case Condition::SHIELD_MODE_START_EVENT: case Condition::SHIELD_MODE_START_EVENT:
case Condition::SHIELD_MODE_END_EVENT: case Condition::SHIELD_MODE_END_EVENT:
case Condition::POINTS_REWARD_ADDITION_EVENT:
case Condition::POINTS_REWARD_UPDATE_EVENT:
case Condition::POINTS_REWARD_DELETION_EVENT:
case Condition::POINTS_REWARD_REDEMPTION_EVENT:
case Condition::POINTS_REWARD_REDEMPTION_UPDATE_EVENT:
case Condition::USER_BAN_EVENT: case Condition::USER_BAN_EVENT:
case Condition::USER_UNBAN_EVENT: case Condition::USER_UNBAN_EVENT:
case Condition::USER_MODERATOR_ADDITION_EVENT: case Condition::USER_MODERATOR_ADDITION_EVENT:
case Condition::USER_MODERATOR_DELETION_EVENT: case Condition::USER_MODERATOR_DELETION_EVENT:
return CheckChannelGenericEvents(); return CheckChannelGenericEvents();
case Condition::POINTS_REWARD_ADDITION_EVENT:
case Condition::POINTS_REWARD_UPDATE_EVENT:
case Condition::POINTS_REWARD_DELETION_EVENT:
return CheckChannelRewardChangeEvents();
case Condition::POINTS_REWARD_REDEMPTION_EVENT:
case Condition::POINTS_REWARD_REDEMPTION_UPDATE_EVENT:
return CheckChannelRewardRedemptionEvents();
case Condition::STREAM_ONLINE_LIVE_EVENT: case Condition::STREAM_ONLINE_LIVE_EVENT:
case Condition::STREAM_ONLINE_PLAYLIST_EVENT: case Condition::STREAM_ONLINE_PLAYLIST_EVENT:
case Condition::STREAM_ONLINE_WATCHPARTY_EVENT: case Condition::STREAM_ONLINE_WATCHPARTY_EVENT:
@@ -1206,25 +1253,41 @@ void MacroConditionTwitch::SetupTempVars()
setupTempVarHelper("is_user_input_required", ".reward"); setupTempVarHelper("is_user_input_required", ".reward");
setupTempVarHelper("should_redemptions_skip_request_queue", setupTempVarHelper("should_redemptions_skip_request_queue",
".reward"); ".reward");
setupTempVarHelper("max_per_stream", ".reward");
setupTempVarHelper("max_per_user_per_stream", ".reward");
setupTempVarHelper("background_color", ".reward"); setupTempVarHelper("background_color", ".reward");
setupTempVarHelper("image", ".reward"); setupTempVarHelper("image", ".reward");
setupTempVarHelper("image.url_4x", ".reward");
setupTempVarHelper("default_image", ".reward"); setupTempVarHelper("default_image", ".reward");
setupTempVarHelper("global_cooldown", ".reward"); setupTempVarHelper("default_image.url_4x", ".reward");
setupTempVarHelper("cooldown_expires_at", ".reward"); setupTempVarHelper("cooldown_expires_at", ".reward");
setupTempVarHelper("redemptions_redeemed_current_stream", setupTempVarHelper("redemptions_redeemed_current_stream",
".reward"); ".reward");
setupTempVarHelper("max_per_stream", ".reward");
setupTempVarHelper("max_per_stream.is_enabled", ".reward");
setupTempVarHelper("max_per_stream.max_per_stream", ".reward");
setupTempVarHelper("max_per_user_per_stream", ".reward");
setupTempVarHelper("max_per_user_per_stream.is_enabled",
".reward");
setupTempVarHelper(
"max_per_user_per_stream.max_per_user_per_stream",
".reward");
setupTempVarHelper("global_cooldown", ".reward");
setupTempVarHelper("global_cooldown.is_enabled", ".reward");
setupTempVarHelper("global_cooldown.global_cooldown_seconds",
".reward");
break; break;
case Condition::POINTS_REWARD_REDEMPTION_EVENT: case Condition::POINTS_REWARD_REDEMPTION_EVENT:
case Condition::POINTS_REWARD_REDEMPTION_UPDATE_EVENT: case Condition::POINTS_REWARD_REDEMPTION_UPDATE_EVENT:
setupTempVarHelper("id", ".reward"); setupTempVarHelper("id", ".redemption");
setupTempVarHelper("user_id", ".reward"); setupTempVarHelper("user_id", ".reward");
setupTempVarHelper("user_login", ".reward"); setupTempVarHelper("user_login", ".reward");
setupTempVarHelper("user_name", ".reward"); setupTempVarHelper("user_name", ".reward");
setupTempVarHelper("user_input", ".reward"); setupTempVarHelper("user_input", ".reward");
setupTempVarHelper("status", ".reward"); setupTempVarHelper("status", ".reward");
setupTempVarHelper("reward", ".reward"); setupTempVarHelper("reward", ".reward");
setupTempVarHelper("reward.id", ".reward");
setupTempVarHelper("reward.title", ".reward");
setupTempVarHelper("reward.prompt", ".reward");
setupTempVarHelper("reward.cost", ".reward");
setupTempVarHelper("redeemed_at", ".reward"); setupTempVarHelper("redeemed_at", ".reward");
break; break;
case Condition::USER_BAN_EVENT: case Condition::USER_BAN_EVENT:
@@ -1425,10 +1488,8 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
this, this,
SLOT(ChatMessagePatternChanged(const ChatMessagePattern &))); SLOT(ChatMessagePatternChanged(const ChatMessagePattern &)));
QWidget::connect(_category, QWidget::connect(_category,
SIGNAL(CategoreyChanged(const TwitchCategory &)), this, SIGNAL(CategoryChanged(const TwitchCategory &)), this,
SLOT(CategoreyChanged(const TwitchCategory &))); SLOT(CategoryChanged(const TwitchCategory &)));
QWidget::connect(this, SIGNAL(TempVarsChanged()), window(),
SIGNAL(SegmentTempVarsChanged()));
QWidget::connect(_clearBufferOnMatch, SIGNAL(stateChanged(int)), this, QWidget::connect(_clearBufferOnMatch, SIGNAL(stateChanged(int)), this,
SLOT(ClearBufferOnMatchChanged(int))); SLOT(ClearBufferOnMatchChanged(int)));
@@ -1480,7 +1541,6 @@ void MacroConditionTwitchEdit::ConditionChanged(int idx)
_entryData->SetCondition(static_cast<MacroConditionTwitch::Condition>( _entryData->SetCondition(static_cast<MacroConditionTwitch::Condition>(
_conditions->itemData(idx).toInt())); _conditions->itemData(idx).toInt()));
SetWidgetVisibility(); SetWidgetVisibility();
emit TempVarsChanged();
} }
void MacroConditionTwitchEdit::TwitchTokenChanged(const QString &token) void MacroConditionTwitchEdit::TwitchTokenChanged(const QString &token)
@@ -1572,7 +1632,7 @@ void MacroConditionTwitchEdit::RegexTitleChanged(const RegexConfig &conf)
updateGeometry(); updateGeometry();
} }
void MacroConditionTwitchEdit::CategoreyChanged(const TwitchCategory &category) void MacroConditionTwitchEdit::CategoryChanged(const TwitchCategory &category)
{ {
GUARD_LOADING_AND_LOCK(); GUARD_LOADING_AND_LOCK();
_entryData->_category = category; _entryData->_category = category;

View File

@@ -111,7 +111,11 @@ public:
private: private:
bool CheckChannelGenericEvents(); bool CheckChannelGenericEvents();
bool CheckChannelLiveEvents(); bool CheckChannelLiveEvents();
bool CheckChannelRewardChangeEvents();
bool CheckChannelRewardRedemptionEvents();
bool CheckChatMessages(TwitchToken &token); bool CheckChatMessages(TwitchToken &token);
bool HandleMatchingSubscriptionEvents(
const std::function<void(const Event &)> &matchCb);
bool CheckChatUserJoinOrLeave(TwitchToken &token); bool CheckChatUserJoinOrLeave(TwitchToken &token);
void RegisterEventSubscription(); void RegisterEventSubscription();
@@ -171,12 +175,11 @@ private slots:
void StreamTitleChanged(); void StreamTitleChanged();
void RegexTitleChanged(const RegexConfig &); void RegexTitleChanged(const RegexConfig &);
void ChatMessagePatternChanged(const ChatMessagePattern &); void ChatMessagePatternChanged(const ChatMessagePattern &);
void CategoreyChanged(const TwitchCategory &); void CategoryChanged(const TwitchCategory &);
void ClearBufferOnMatchChanged(int); void ClearBufferOnMatchChanged(int);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
void TempVarsChanged();
private: private:
void SetWidgetVisibility(); void SetWidgetVisibility();

View File

@@ -88,7 +88,7 @@ void TwitchPointsRewardSelection::PopulateSelection()
const QSignalBlocker b(this); const QSignalBlocker b(this);
clear(); clear();
auto pointsRewards = GetPointsRewards(token, *_channel); auto pointsRewards = GetPointsRewardsForChannel(token, *_channel);
if (!pointsRewards) { if (!pointsRewards) {
DisplayErrorMessage(obs_module_text( DisplayErrorMessage(obs_module_text(
"AdvSceneSwitcher.twitch.selection.points.reward.tooltip.error")); "AdvSceneSwitcher.twitch.selection.points.reward.tooltip.error"));
@@ -127,47 +127,6 @@ void TwitchPointsRewardSelection::HideErrorMessage()
setToolTip(""); setToolTip("");
} }
std::optional<std::vector<TwitchPointsReward>>
TwitchPointsRewardSelection::GetPointsRewards(
const std::shared_ptr<TwitchToken> &token, const TwitchChannel &channel)
{
httplib::Params params = {
{"broadcaster_id", channel.GetUserID(*token)}};
auto response = SendGetRequest(*token, "https://api.twitch.tv",
"/helix/channel_points/custom_rewards",
params);
if (response.status != 200) {
blog(LOG_WARNING,
"Failed to fetch points rewards for user %s and channel %s! (%d)",
token->GetName().c_str(), channel.GetName().c_str(),
response.status);
return {};
}
return ParseResponse(response.data);
}
std::vector<TwitchPointsReward>
TwitchPointsRewardSelection::ParseResponse(obs_data_t *response)
{
std::vector<TwitchPointsReward> pointRewards;
OBSDataArrayAutoRelease jsonArray =
obs_data_get_array(response, "data");
size_t count = obs_data_array_count(jsonArray);
for (size_t i = 0; i < count; ++i) {
OBSDataAutoRelease jsonObj = obs_data_array_item(jsonArray, i);
std::string id = obs_data_get_string(jsonObj, "id");
std::string title = obs_data_get_string(jsonObj, "title");
pointRewards.push_back({id, title});
}
return pointRewards;
}
void TwitchPointsRewardSelection::SelectionChanged(int index) void TwitchPointsRewardSelection::SelectionChanged(int index)
{ {
TwitchPointsReward pointsReward{ TwitchPointsReward pointsReward{
@@ -182,7 +141,9 @@ TwitchPointsRewardWidget::TwitchPointsRewardWidget(QWidget *parent)
_refreshButton(new QPushButton(this)) _refreshButton(new QPushButton(this))
{ {
_refreshButton->setMaximumWidth(22); _refreshButton->setMaximumWidth(22);
SetButtonIcon(_refreshButton, ":res/images/refresh.svg"); SetButtonIcon(_refreshButton, GetThemeTypeName() == "Light"
? ":res/images/refresh.svg"
: "theme:Dark/refresh.svg");
_refreshButton->setToolTip(obs_module_text( _refreshButton->setToolTip(obs_module_text(
"AdvSceneSwitcher.twitch.selection.points.reward.refresh")); "AdvSceneSwitcher.twitch.selection.points.reward.refresh"));
@@ -216,4 +177,44 @@ void TwitchPointsRewardWidget::SetToken(const std::weak_ptr<TwitchToken> &token)
_selection->SetToken(token); _selection->SetToken(token);
} }
static std::vector<TwitchPointsReward> parseApiResponse(obs_data_t *response)
{
std::vector<TwitchPointsReward> pointRewards;
OBSDataArrayAutoRelease jsonArray =
obs_data_get_array(response, "data");
size_t count = obs_data_array_count(jsonArray);
for (size_t i = 0; i < count; ++i) {
OBSDataAutoRelease jsonObj = obs_data_array_item(jsonArray, i);
std::string id = obs_data_get_string(jsonObj, "id");
std::string title = obs_data_get_string(jsonObj, "title");
pointRewards.push_back({id, title});
}
return pointRewards;
}
std::optional<std::vector<TwitchPointsReward>>
GetPointsRewardsForChannel(const std::shared_ptr<TwitchToken> &token,
const TwitchChannel &channel)
{
httplib::Params params = {
{"broadcaster_id", channel.GetUserID(*token)}};
auto response = SendGetRequest(*token, "https://api.twitch.tv",
"/helix/channel_points/custom_rewards",
params, true);
if (response.status != 200) {
blog(LOG_WARNING,
"Failed to fetch points rewards for user %s and channel %s! (%d)",
token->GetName().c_str(), channel.GetName().c_str(),
response.status);
return {};
}
return parseApiResponse(response.data);
}
} // namespace advss } // namespace advss

View File

@@ -35,10 +35,6 @@ protected:
private: private:
void AddPredefinedItems(); void AddPredefinedItems();
std::optional<std::vector<TwitchPointsReward>>
GetPointsRewards(const std::shared_ptr<TwitchToken> &token,
const TwitchChannel &channel);
std::vector<TwitchPointsReward> ParseResponse(obs_data_t *response);
bool _allowAny; bool _allowAny;
const std::vector<TokenOption> _supportedTokenOptions = { const std::vector<TokenOption> _supportedTokenOptions = {
@@ -71,4 +67,8 @@ protected:
QPushButton *_refreshButton; QPushButton *_refreshButton;
}; };
std::optional<std::vector<TwitchPointsReward>>
GetPointsRewardsForChannel(const std::shared_ptr<TwitchToken> &token,
const TwitchChannel &channel);
} // namespace advss } // namespace advss

View File

@@ -439,7 +439,7 @@ TwitchTokenSettingsDialog::TwitchTokenSettingsDialog(
"AdvSceneSwitcher.twitchToken.select", "AdvSceneSwitcher.twitchToken.select",
"AdvSceneSwitcher.twitchToken.add", "AdvSceneSwitcher.twitchToken.add",
"AdvSceneSwitcher.twitchToken.nameNotAvailable", "AdvSceneSwitcher.twitchToken.nameNotAvailable",
parent), false, parent),
_requestToken(new QPushButton( _requestToken(new QPushButton(
obs_module_text("AdvSceneSwitcher.twitchToken.request"))), obs_module_text("AdvSceneSwitcher.twitchToken.request"))),
_showToken(new QPushButton()), _showToken(new QPushButton()),
@@ -456,7 +456,6 @@ TwitchTokenSettingsDialog::TwitchTokenSettingsDialog(
_currentTokenValue->setText(QString::fromStdString(settings._token)); _currentTokenValue->setText(QString::fromStdString(settings._token));
_name->setReadOnly(true); _name->setReadOnly(true);
_showNameEmptyWarning = false;
QWidget::connect(_requestToken, SIGNAL(clicked()), this, QWidget::connect(_requestToken, SIGNAL(clicked()), this,
SLOT(RequestToken())); SLOT(RequestToken()));
@@ -561,7 +560,9 @@ void TwitchTokenSettingsDialog::CheckIfTokenValid()
void TwitchTokenSettingsDialog::ShowToken() void TwitchTokenSettingsDialog::ShowToken()
{ {
SetButtonIcon(_showToken, ":res/images/visible.svg"); SetButtonIcon(_showToken, GetThemeTypeName() == "Light"
? ":res/images/visible.svg"
: "theme:Dark/visible.svg");
_currentTokenValue->setEchoMode(QLineEdit::Normal); _currentTokenValue->setEchoMode(QLineEdit::Normal);
} }
@@ -682,22 +683,33 @@ void TwitchTokenSettingsDialog::RequestToken()
void TwitchTokenSettingsDialog::GotToken(const std::optional<QString> &value) void TwitchTokenSettingsDialog::GotToken(const std::optional<QString> &value)
{ {
_currentTokenValue->setText(value.value_or("")); _currentTokenValue->setText(value.value_or(""));
if (value.has_value()) { if (!value) {
_tokenStatus->setText(obs_module_text(
"AdvSceneSwitcher.twitchToken.request.success"));
_currentToken.SetToken(value.value().toStdString());
auto name = QString::fromStdString(_currentToken._name);
_name->setText(name);
_name->textEdited(name);
QMetaObject::invokeMethod(this, "NameChanged",
Q_ARG(const QString &, name));
SetTokenInfoVisible(true);
} else {
_tokenStatus->setText(obs_module_text( _tokenStatus->setText(obs_module_text(
"AdvSceneSwitcher.twitchToken.request.fail")); "AdvSceneSwitcher.twitchToken.request.fail"));
_name->setText(""); _name->setText("");
SetTokenInfoVisible(false); SetTokenInfoVisible(false);
_requestToken->setEnabled(true);
return;
} }
_currentToken.SetToken(value.value().toStdString());
auto name = QString::fromStdString(_currentToken._name);
if (name.isEmpty()) {
_tokenStatus->setText(obs_module_text(
"AdvSceneSwitcher.twitchToken.request.fail"));
_name->setText("");
SetTokenInfoVisible(false);
_requestToken->setEnabled(true);
return;
}
_tokenStatus->setText(obs_module_text(
"AdvSceneSwitcher.twitchToken.request.success"));
_name->setText(name);
_name->textEdited(name);
QMetaObject::invokeMethod(this, "NameChanged",
Q_ARG(const QString &, name));
SetTokenInfoVisible(true);
_requestToken->setEnabled(true); _requestToken->setEnabled(true);
} }

View File

@@ -1,12 +1,16 @@
#include "twitch-helpers.hpp" #include "twitch-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "token.hpp" #include "token.hpp"
#include <log-helper.hpp> #include <log-helper.hpp>
#include <nlohmann/json.hpp>
#include <string>
namespace advss { namespace advss {
static constexpr std::string_view clientID = "ds5tt4ogliifsqc04mz3d3etnck3e5"; static constexpr std::string_view clientID = "ds5tt4ogliifsqc04mz3d3etnck3e5";
static const int cacheTimeoutSeconds = 10; static const int cacheTimeoutSeconds = 10;
static std::atomic_bool apiIsThrottling = {false};
const char *GetClientID() const char *GetClientID()
{ {
@@ -64,6 +68,12 @@ static bool cacheIsValid(const std::map<Args, CacheEntry> &cache,
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)
{
std::lock_guard<std::mutex> lock(mtx);
cache.clear();
}
static httplib::Headers getTokenRequestHeaders(const std::string &token) static httplib::Headers getTokenRequestHeaders(const std::string &token)
{ {
return { return {
@@ -78,6 +88,37 @@ static std::string getRequestBody(const OBSData &data)
return json ? json : ""; return json ? json : "";
} }
static void handleThrottling(const httplib::Result &response)
{
const auto &headers = response.value().headers;
const auto rateLimitResetTimeStr = headers.find("Ratelimit-Reset");
if (rateLimitResetTimeStr == headers.end()) {
return;
}
std::time_t rateLimitResetTime =
std::stoll(rateLimitResetTimeStr->second);
auto resetTimePoint =
std::chrono::system_clock::from_time_t(rateLimitResetTime);
auto sleepDuration = resetTimePoint - std::chrono::system_clock::now();
if (sleepDuration.count() < 0) {
return;
}
blog(LOG_WARNING, "Twitch API access is throttled for %lld seconds!",
static_cast<long long int>(
std::chrono::duration_cast<std::chrono::seconds>(
sleepDuration)
.count()));
apiIsThrottling = true;
auto resetThread = std::thread([sleepDuration]() {
std::this_thread::sleep_for(sleepDuration);
apiIsThrottling = false;
});
resetThread.detach();
}
static RequestResult processResult(const httplib::Result &response, static RequestResult processResult(const httplib::Result &response,
const char *funcName) const char *funcName)
{ {
@@ -89,6 +130,10 @@ static RequestResult processResult(const httplib::Result &response,
return {}; return {};
} }
if (response->status == 429) {
handleThrottling(response);
}
RequestResult result; RequestResult result;
result.status = response->status; result.status = response->status;
@@ -103,10 +148,15 @@ static RequestResult processResult(const httplib::Result &response,
return result; return result;
} }
RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri, static RequestResult sendGetRequest(const TwitchToken &token,
const std::string &path, const std::string &uri,
const httplib::Params &params) const std::string &path,
const httplib::Params &params)
{ {
if (apiIsThrottling) {
return {};
}
httplib::Client cli(uri); httplib::Client cli(uri);
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
@@ -127,9 +177,17 @@ RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
const std::string &path, const std::string &path,
const httplib::Params &params, bool useCache) const httplib::Params &params, bool useCache)
{ {
if (apiIsThrottling) {
return {};
}
static std::map<Args, CacheEntry> cache; static std::map<Args, CacheEntry> cache;
static std::mutex mtx; static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); [[maybe_unused]] static bool _ = []() {
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
return true;
}();
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
if (!tokenStr) { if (!tokenStr) {
@@ -139,22 +197,32 @@ RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
auto headers = getTokenRequestHeaders(*tokenStr); auto headers = getTokenRequestHeaders(*tokenStr);
Args args(uri, path, "", params, headers); Args args(uri, path, "", params, headers);
if (useCache && cacheIsValid(cache, args)) { if (!useCache) {
return sendGetRequest(token, uri, path, params);
}
std::lock_guard<std::mutex> lock(mtx);
if (cacheIsValid(cache, args)) {
auto it = cache.find(args); auto it = cache.find(args);
return it->second.result; return it->second.result;
} }
auto result = SendGetRequest(token, uri, path, params); auto result = sendGetRequest(token, uri, path, params);
cache[args] = {result}; cache[args] = {result};
return result; return result;
} }
RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri, static RequestResult sendPostRequest(const TwitchToken &token,
const std::string &path, const std::string &uri,
const httplib::Params &params, const std::string &path,
const OBSData &data) const httplib::Params &params,
const OBSData &data)
{ {
if (apiIsThrottling) {
return {};
}
httplib::Client cli(uri); httplib::Client cli(uri);
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
@@ -179,9 +247,17 @@ RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
const httplib::Params &params, const httplib::Params &params,
const OBSData &data, bool useCache) const OBSData &data, bool useCache)
{ {
if (apiIsThrottling) {
return {};
}
static std::map<Args, CacheEntry> cache; static std::map<Args, CacheEntry> cache;
static std::mutex mtx; static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); [[maybe_unused]] static bool _ = []() {
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
return true;
}();
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
if (!tokenStr) { if (!tokenStr) {
@@ -192,21 +268,32 @@ RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
auto body = getRequestBody(data); auto body = getRequestBody(data);
Args args(uri, path, body, params, headers); Args args(uri, path, body, params, headers);
if (useCache && cacheIsValid(cache, args)) { if (!useCache) {
return sendPostRequest(token, uri, path, params, data);
}
std::lock_guard<std::mutex> lock(mtx);
if (cacheIsValid(cache, args)) {
auto it = cache.find(args); auto it = cache.find(args);
return it->second.result; return it->second.result;
} }
auto result = SendPostRequest(token, uri, path, params, data); auto result = sendPostRequest(token, uri, path, params, data);
cache[args] = {result}; cache[args] = {result};
return result; return result;
} }
RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri, static RequestResult sendPutRequest(const TwitchToken &token,
const std::string &path, const std::string &uri,
const httplib::Params &params, const OBSData &data) const std::string &path,
const httplib::Params &params,
const OBSData &data)
{ {
if (apiIsThrottling) {
return {};
}
httplib::Client cli(uri); httplib::Client cli(uri);
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
@@ -231,9 +318,17 @@ RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
const httplib::Params &params, const OBSData &data, const httplib::Params &params, const OBSData &data,
bool useCache) bool useCache)
{ {
if (apiIsThrottling) {
return {};
}
static std::map<Args, CacheEntry> cache; static std::map<Args, CacheEntry> cache;
static std::mutex mtx; static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); [[maybe_unused]] static bool _ = []() {
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
return true;
}();
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
if (!tokenStr) { if (!tokenStr) {
@@ -244,22 +339,32 @@ RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
auto body = getRequestBody(data); auto body = getRequestBody(data);
Args args(uri, path, body, params, headers); Args args(uri, path, body, params, headers);
if (useCache && cacheIsValid(cache, args)) { if (!useCache) {
return sendPutRequest(token, uri, path, params, data);
}
std::lock_guard<std::mutex> lock(mtx);
if (cacheIsValid(cache, args)) {
auto it = cache.find(args); auto it = cache.find(args);
return it->second.result; return it->second.result;
} }
auto result = SendPutRequest(token, uri, path, params, data); auto result = sendPutRequest(token, uri, path, params, data);
cache[args] = {result}; cache[args] = {result};
return result; return result;
} }
RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri, static RequestResult sendPatchRequest(const TwitchToken &token,
const std::string &path, const std::string &uri,
const httplib::Params &params, const std::string &path,
const OBSData &data) const httplib::Params &params,
const OBSData &data)
{ {
if (apiIsThrottling) {
return {};
}
httplib::Client cli(uri); httplib::Client cli(uri);
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
@@ -284,9 +389,17 @@ RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
const httplib::Params &params, const httplib::Params &params,
const OBSData &data, bool useCache) const OBSData &data, bool useCache)
{ {
if (apiIsThrottling) {
return {};
}
static std::map<Args, CacheEntry> cache; static std::map<Args, CacheEntry> cache;
static std::mutex mtx; static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); [[maybe_unused]] static bool _ = []() {
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
return true;
}();
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
if (!tokenStr) { if (!tokenStr) {
@@ -297,21 +410,31 @@ RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
auto body = getRequestBody(data); auto body = getRequestBody(data);
Args args(uri, path, body, params, headers); Args args(uri, path, body, params, headers);
if (useCache && cacheIsValid(cache, args)) { if (!useCache) {
return sendPatchRequest(token, uri, path, params, data);
}
std::lock_guard<std::mutex> lock(mtx);
if (cacheIsValid(cache, args)) {
auto it = cache.find(args); auto it = cache.find(args);
return it->second.result; return it->second.result;
} }
auto result = SendPatchRequest(token, uri, path, params, data); auto result = sendPatchRequest(token, uri, path, params, data);
cache[args] = {result}; cache[args] = {result};
return result; return result;
} }
RequestResult SendDeleteRequest(const TwitchToken &token, static RequestResult sendDeleteRequest(const TwitchToken &token,
const std::string &uri, const std::string &path, const std::string &uri,
const httplib::Params &params) const std::string &path,
const httplib::Params &params)
{ {
if (apiIsThrottling) {
return {};
}
httplib::Client cli(uri); httplib::Client cli(uri);
auto tokenStr = token.GetToken(); auto tokenStr = token.GetToken();
@@ -330,4 +453,34 @@ RequestResult SendDeleteRequest(const TwitchToken &token,
return processResult(response, __func__); return processResult(response, __func__);
} }
void SetJsonTempVars(const std::string &jsonStr,
std::function<void(const char *, const char *)> setVarFunc)
{
try {
auto json = nlohmann::json::parse(jsonStr);
for (auto it = json.begin(); it != json.end(); ++it) {
if (it->is_string()) {
setVarFunc(it.key().c_str(),
it->get<std::string>().c_str());
continue;
}
setVarFunc(it.key().c_str(), it->dump().c_str());
}
} catch (const nlohmann::json::exception &e) {
vblog(LOG_INFO, "%s", jsonStr.c_str());
vblog(LOG_INFO, "%s", e.what());
}
}
void SetJsonTempVars(obs_data_t *data,
std::function<void(const char *, const char *)> setVarFunc)
{
auto jsonStr = obs_data_get_json(data);
if (!jsonStr) {
return;
}
SetJsonTempVars(jsonStr, setVarFunc);
}
} // namespace advss } // namespace advss

View File

@@ -15,42 +15,33 @@ struct RequestResult {
const char *GetClientID(); const char *GetClientID();
// These functions do *not* use or create RequestResult cache entries // These functions can cache the RequestResult for 10s
RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri, RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
const std::string &path, const std::string &path,
const httplib::Params &params = {}); const httplib::Params &params = {},
bool useCache = false);
RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri, RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
const std::string &path, const std::string &path,
const httplib::Params &params = {}, const httplib::Params &params = {},
const OBSData &data = nullptr); const OBSData &data = nullptr,
bool useCache = false);
RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri, RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
const std::string &path, const std::string &path,
const httplib::Params &params = {}, const httplib::Params &params = {},
const OBSData &data = nullptr); const OBSData &data = nullptr,
bool useCache = false);
RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri, RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
const std::string &path, const std::string &path,
const httplib::Params &params = {}, const httplib::Params &params = {},
const OBSData &data = nullptr); const OBSData &data = nullptr,
RequestResult SendDeleteRequest(const TwitchToken &token, bool useCache = false);
const std::string &uri, const std::string &path,
const httplib::Params &params = {});
// These functions will cache the RequestResult for 10s // Helper functions to set temp var values
// Note that the cache will be reported as a "memory leak" on OBS shutdown
RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri, void SetJsonTempVars(const std::string &jsonStr,
const std::string &path, std::function<void(const char *, const char *)> setVarFunc);
const httplib::Params &params, bool useCache); void SetJsonTempVars(obs_data_t *data,
RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri, std::function<void(const char *, const char *)> setVarFunc);
const std::string &path,
const httplib::Params &params,
const OBSData &data, bool useCache);
RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params, const OBSData &data,
bool useCache);
RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params,
const OBSData &data, bool useCache);
} // namespace advss } // namespace advss

View File

@@ -44,8 +44,8 @@ TwitchConnectionsTable *TwitchConnectionsTable::Create()
void TwitchConnectionsTable::Add() void TwitchConnectionsTable::Add()
{ {
auto newToken = std::make_shared<TwitchToken>(); auto newToken = std::make_shared<TwitchToken>();
auto accepted = auto accepted = TwitchTokenSettingsDialog::AskForSettings(
TwitchTokenSettingsDialog::AskForSettings(this, *newToken); GetSettingsWindow(), *newToken);
if (!accepted) { if (!accepted) {
return; return;
} }
@@ -139,7 +139,7 @@ static void openSettingsDialog()
return; return;
} }
TwitchTokenSettingsDialog::AskForSettings(tabWidget->Table(), TwitchTokenSettingsDialog::AskForSettings(GetSettingsWindow(),
*token.get()); *token.get());
} }

View File

@@ -47,8 +47,8 @@ target_sources(
macro-condition-video.hpp macro-condition-video.hpp
opencv-helpers.cpp opencv-helpers.cpp
opencv-helpers.hpp opencv-helpers.hpp
paramerter-wrappers.cpp parameter-wrappers.cpp
paramerter-wrappers.hpp parameter-wrappers.hpp
preview-dialog.cpp preview-dialog.cpp
preview-dialog.hpp preview-dialog.hpp
screenshot-dialog.cpp screenshot-dialog.cpp

View File

@@ -482,8 +482,9 @@ static inline void populateVideoInputSelection(QComboBox *list)
static inline void populateConditionSelection(QComboBox *list) static inline void populateConditionSelection(QComboBox *list)
{ {
for (auto entry : conditionTypes) { for (auto &[value, name] : conditionTypes) {
list->addItem(obs_module_text(entry.second.c_str())); list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
} }
} }
@@ -1189,8 +1190,6 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
SIGNAL(VideoSelectionChanged(const VideoInput &)), SIGNAL(VideoSelectionChanged(const VideoInput &)),
&_previewDialog, &_previewDialog,
SLOT(VideoSelectionChanged(const VideoInput &))); SLOT(VideoSelectionChanged(const VideoInput &)));
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)),
&_previewDialog, SLOT(ConditionChanged(int)));
QWidget::connect(_area, SIGNAL(Resized()), this, SLOT(Resize())); QWidget::connect(_area, SIGNAL(Resized()), this, SLOT(Resize()));
QWidget::connect(entryData.get(), QWidget::connect(entryData.get(),
&MacroConditionVideo::InputFileChanged, this, &MacroConditionVideo::InputFileChanged, this,
@@ -1320,14 +1319,15 @@ void MacroConditionVideoEdit::HandleVideoInputUpdate()
emit VideoSelectionChanged(_entryData->_video); emit VideoSelectionChanged(_entryData->_video);
} }
void MacroConditionVideoEdit::ConditionChanged(int cond) void MacroConditionVideoEdit::ConditionChanged(int idx)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {
return; return;
} }
auto lock = LockContext(); auto lock = LockContext();
_entryData->SetCondition(static_cast<VideoCondition>(cond)); _entryData->SetCondition(
static_cast<VideoCondition>(_condition->itemData(idx).toInt()));
_entryData->ResetLastMatch(); _entryData->ResetLastMatch();
SetWidgetVisibility(); SetWidgetVisibility();
@@ -1384,23 +1384,12 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
"AdvSceneSwitcher.condition.video.askFileAction"), "AdvSceneSwitcher.condition.video.askFileAction"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Yes | QMessageBox::No |
QMessageBox::Cancel); QMessageBox::Cancel);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
msgBox.setButtonText(
QMessageBox::Yes,
obs_module_text(
"AdvSceneSwitcher.condition.video.askFileAction.file"));
msgBox.setButtonText(
QMessageBox::No,
obs_module_text(
"AdvSceneSwitcher.condition.video.askFileAction.screenshot"));
#else
auto yes = msgBox.button(QMessageBox::StandardButton::Yes); auto yes = msgBox.button(QMessageBox::StandardButton::Yes);
yes->setText(obs_module_text( yes->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.askFileAction.file")); "AdvSceneSwitcher.condition.video.askFileAction.file"));
auto no = msgBox.button(QMessageBox::StandardButton::No); auto no = msgBox.button(QMessageBox::StandardButton::No);
no->setText(obs_module_text( no->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.askFileAction.screenshot")); "AdvSceneSwitcher.condition.video.askFileAction.screenshot"));
#endif
msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint |
Qt::CustomizeWindowHint); Qt::CustomizeWindowHint);
const auto result = msgBox.exec(); const auto result = msgBox.exec();
@@ -1433,8 +1422,8 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
return; return;
} }
auto screenshot = auto screenshot = ScreenshotDialog::AskForScreenshot(
ScreenshotDialog::AskForScreenshot(_entryData->_video); _entryData->_video, _entryData->_areaParameters);
if (!screenshot) { if (!screenshot) {
return; return;
} }
@@ -1603,8 +1592,8 @@ void MacroConditionVideoEdit::UpdateEntryData()
static_cast<int>(_entryData->_video.type)); static_cast<int>(_entryData->_video.type));
_scenes->SetScene(_entryData->_video.scene); _scenes->SetScene(_entryData->_video.scene);
_sources->SetSource(_entryData->_video.source); _sources->SetSource(_entryData->_video.source);
_condition->setCurrentIndex( _condition->setCurrentIndex(_condition->findData(
static_cast<int>(_entryData->GetCondition())); static_cast<int>(_entryData->GetCondition())));
_reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone); _reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone);
_imagePath->SetPath(QString::fromStdString(_entryData->_file)); _imagePath->SetPath(QString::fromStdString(_entryData->_file));
_usePatternForChangedCheck->setChecked( _usePatternForChangedCheck->setChecked(

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include "opencv-helpers.hpp" #include "opencv-helpers.hpp"
#include "area-selection.hpp" #include "area-selection.hpp"
#include "paramerter-wrappers.hpp" #include "parameter-wrappers.hpp"
#include "preview-dialog.hpp" #include "preview-dialog.hpp"
#include <macro-condition-edit.hpp> #include <macro-condition-edit.hpp>

View File

@@ -1,4 +1,4 @@
#include "paramerter-wrappers.hpp" #include "parameter-wrappers.hpp"
#include <QFileInfo> #include <QFileInfo>
#include <source-helpers.hpp> #include <source-helpers.hpp>
@@ -251,7 +251,8 @@ OCRParameters::OCRParameters(const OCRParameters &other)
regex(other.regex), regex(other.regex),
color(other.color), color(other.color),
colorThreshold(other.colorThreshold), colorThreshold(other.colorThreshold),
pageSegMode(other.pageSegMode) pageSegMode(other.pageSegMode),
languageCode(other.languageCode)
{ {
if (!initDone) { if (!initDone) {
Setup(); Setup();
@@ -268,6 +269,7 @@ OCRParameters &OCRParameters::operator=(const OCRParameters &other)
color = other.color; color = other.color;
colorThreshold = other.colorThreshold; colorThreshold = other.colorThreshold;
pageSegMode = other.pageSegMode; pageSegMode = other.pageSegMode;
languageCode = other.languageCode;
if (!initDone) { if (!initDone) {
Setup(); Setup();
} }
@@ -310,6 +312,7 @@ bool OCRParameters::Load(obs_data_t *obj)
if (initDone) { if (initDone) {
ocr->SetPageSegMode(pageSegMode); ocr->SetPageSegMode(pageSegMode);
} }
Setup();
return true; return true;
} }
@@ -329,6 +332,7 @@ bool OCRParameters::SetLanguageCode(const std::string &value)
if (!fileInfo.exists(dataPath)) { if (!fileInfo.exists(dataPath)) {
return false; return false;
} }
languageCode = value;
Setup(); Setup();
ocr->SetPageSegMode(pageSegMode); ocr->SetPageSegMode(pageSegMode);
return true; return true;

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