mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c3c953c6b | ||
|
|
ae74f68db7 | ||
|
|
213f1bba36 | ||
|
|
5a2cb0bd68 | ||
|
|
23b461828b | ||
|
|
9944a1b03b | ||
|
|
eaad4d1bbd | ||
|
|
17d9b73b9a | ||
|
|
d6185a6099 | ||
|
|
41c06b9edb | ||
|
|
d8807077da | ||
|
|
34e125f82d | ||
|
|
d670b6d07e | ||
|
|
7b0f985f18 | ||
|
|
212be923f6 | ||
|
|
88fcb57e9f | ||
|
|
9b609c118f | ||
|
|
37c398d37a | ||
|
|
a51b7f6b13 | ||
|
|
808fd84b83 | ||
|
|
57bcea15f5 | ||
|
|
4e51b56b9b | ||
|
|
ef5cf41d34 | ||
|
|
911e79ea6a | ||
|
|
d586177de0 | ||
|
|
202c36646c | ||
|
|
ab5102f5ca | ||
|
|
e612fb99f6 | ||
|
|
691e77a69a | ||
|
|
930118c61f | ||
|
|
1e05f2f2ce | ||
|
|
ae25b4d023 | ||
|
|
af055a12a1 | ||
|
|
7de28eedd3 | ||
|
|
8dfe81f522 | ||
|
|
a1d8ae291d | ||
|
|
eb6989527d | ||
|
|
2986a5dd96 | ||
|
|
816ee9b244 | ||
|
|
51b53bf948 | ||
|
|
2c5121ee94 | ||
|
|
6a58684854 | ||
|
|
0b622fdbed | ||
|
|
3bce8e075f | ||
|
|
f598d84ea1 | ||
|
|
d7a244e80e | ||
|
|
408002e96a | ||
|
|
50da3d3210 | ||
|
|
9e13067769 | ||
|
|
821e768e76 | ||
|
|
4d2179448b | ||
|
|
40d9002470 | ||
|
|
7f1e310ca6 | ||
|
|
66d02f4683 | ||
|
|
0aae1b9978 | ||
|
|
db50822b05 | ||
|
|
63a545b293 | ||
|
|
bc0497d2c9 | ||
|
|
37226a3a4c | ||
|
|
9f15fbe47c |
@@ -6,15 +6,6 @@
|
||||
"autosort": true
|
||||
},
|
||||
"additional_commands": {
|
||||
"find_qt": {
|
||||
"flags": [],
|
||||
"kwargs": {
|
||||
"COMPONENTS": "+",
|
||||
"COMPONENTS_WIN": "+",
|
||||
"COMPONENTS_MACOS": "+",
|
||||
"COMPONENTS_LINUX": "+"
|
||||
}
|
||||
},
|
||||
"set_target_properties_obs": {
|
||||
"pargs": 1,
|
||||
"flags": [],
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -20,6 +20,7 @@ body:
|
||||
- macOS 13
|
||||
- macOS 12
|
||||
- macOS 11
|
||||
- Ubuntu 24.04
|
||||
- Ubuntu 23.10
|
||||
- Ubuntu 22.04
|
||||
- Other
|
||||
|
||||
2
.github/scripts/.Aptfile
vendored
2
.github/scripts/.Aptfile
vendored
@@ -9,5 +9,5 @@ package 'libxtst-dev'
|
||||
package 'libxss-dev'
|
||||
package 'libopencv-dev'
|
||||
package 'libtesseract-dev'
|
||||
package 'libprocps-dev'
|
||||
package 'libproc2-dev'
|
||||
package 'libusb-1.0-0-dev'
|
||||
7
.github/scripts/.build-deps.zsh
vendored
7
.github/scripts/.build-deps.zsh
vendored
@@ -480,12 +480,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
popd
|
||||
;;
|
||||
linux)
|
||||
# Hacky workaround to support libproc2 with Ubuntu 22 build environment
|
||||
local lsb_version=$(lsb_release -r | cut -f 2 || true)
|
||||
if [[ "${lsb_version}" == '22.04' ]] {
|
||||
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
|
||||
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
|
||||
}
|
||||
# Nothing to do for now
|
||||
;;
|
||||
}
|
||||
}
|
||||
|
||||
0
.github/test
vendored
0
.github/test
vendored
2
.github/workflows/build-debian.yml
vendored
2
.github/workflows/build-debian.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
# devscripts and libobs-dev are needed but they were already installed
|
||||
# from check_libobs_revision and install_frontend_header sections.
|
||||
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
|
||||
run: |
|
||||
debuild --no-lintian --no-sign
|
||||
|
||||
8
.github/workflows/build-project.yaml
vendored
8
.github/workflows/build-project.yaml
vendored
@@ -10,7 +10,7 @@ env:
|
||||
jobs:
|
||||
check-event:
|
||||
name: Check GitHub Event Data 🔎
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
|
||||
ubuntu-build:
|
||||
name: Build for Ubuntu 🐧
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-event
|
||||
defaults:
|
||||
run:
|
||||
@@ -241,14 +241,14 @@ jobs:
|
||||
- name: Upload Artifacts 📡
|
||||
uses: actions/upload-artifact@v4
|
||||
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*.*
|
||||
|
||||
- name: Upload debug symbol artifacts 🪲
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||
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
|
||||
|
||||
windows-build:
|
||||
|
||||
4
.github/workflows/check-format.yaml
vendored
4
.github/workflows/check-format.yaml
vendored
@@ -3,7 +3,7 @@ on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
failCondition: error
|
||||
|
||||
cmake-format:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
4
.github/workflows/push.yaml
vendored
4
.github/workflows/push.yaml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
create-release:
|
||||
name: Create Release 🛫
|
||||
if: github.ref_type == 'tag'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-project
|
||||
defaults:
|
||||
run:
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
variants=(
|
||||
'windows-x64;zip|exe'
|
||||
'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'
|
||||
)
|
||||
|
||||
|
||||
@@ -20,6 +20,12 @@ if(BUILD_OUT_OF_TREE)
|
||||
include(helpers)
|
||||
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")
|
||||
add_library(${PROJECT_NAME} MODULE)
|
||||
add_library(${LIB_NAME} SHARED)
|
||||
@@ -289,9 +295,16 @@ include(cmake/common/advss_helpers.cmake)
|
||||
setup_obs_lib_dependency(${LIB_NAME})
|
||||
setup_obs_lib_dependency(${PROJECT_NAME})
|
||||
|
||||
find_qt(COMPONENTS Widgets Core)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets)
|
||||
target_link_libraries(${LIB_NAME} PRIVATE Qt::Core Qt::Widgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
|
||||
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported Qt
|
||||
# version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
|
||||
if(Qt6_VERSION VERSION_GREATER "6.0.0")
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE QT_NO_DEPRECATED_WARNINGS)
|
||||
endif()
|
||||
|
||||
target_compile_options(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
@@ -407,7 +420,12 @@ else()
|
||||
set(PROCESS_CONDITION_SUPPORTED 1)
|
||||
endif()
|
||||
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}")
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
|
||||
set(PROCESS_CONDITION_SUPPORTED 1)
|
||||
|
||||
@@ -7,7 +7,7 @@ Build-Depends: cmake,
|
||||
libcurl4-openssl-dev,
|
||||
libobs-dev,
|
||||
libxtst-dev,
|
||||
qtbase5-dev,
|
||||
qt6-base-dev,
|
||||
libxss-dev,
|
||||
libopencv-dev
|
||||
Standards-Version: 4.6.0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
export QT_SELECT = qt5
|
||||
export QT_SELECT = qt6
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -283,8 +283,14 @@ endfunction()
|
||||
|
||||
function(setup_advss_plugin target)
|
||||
setup_obs_lib_dependency(${target})
|
||||
find_qt(COMPONENTS Widgets Core)
|
||||
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||
target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
|
||||
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported
|
||||
# Qt version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
|
||||
if(Qt6_VERSION VERSION_GREATER "6.7.0")
|
||||
target_compile_definitions(${target} PRIVATE QT_NO_DEPRECATED_WARNINGS)
|
||||
endif()
|
||||
|
||||
set_target_properties(
|
||||
${target}
|
||||
|
||||
@@ -17,85 +17,6 @@ if(NOT QT_VERSION)
|
||||
set_property(CACHE QT_VERSION PROPERTY STRINGS AUTO 5 6)
|
||||
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
|
||||
function(check_uuid uuid_string return_value)
|
||||
set(valid_uuid TRUE)
|
||||
|
||||
@@ -769,6 +769,11 @@ AdvSceneSwitcher.condition.streamDeck.checkData="Check data field"
|
||||
AdvSceneSwitcher.condition.streamDeck.startListen="Start 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.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
|
||||
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.line2="Timeout:{{timeout}}seconds"
|
||||
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.appendVar="Append variable"
|
||||
AdvSceneSwitcher.action.variable.type.increment="Increment"
|
||||
AdvSceneSwitcher.action.variable.type.decrement="Decrement"
|
||||
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value"
|
||||
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value"
|
||||
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value (legacy)"
|
||||
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value (legacy)"
|
||||
AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer"
|
||||
AdvSceneSwitcher.action.variable.type.subString="Set to substring of 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.trim="Trim whitespace"
|
||||
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.upperCase="UPPERCASE"
|
||||
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.subString.begin="Beginning"
|
||||
AdvSceneSwitcher.action.variable.subString.all="All"
|
||||
AdvSceneSwitcher.action.variable.generateInteger="Only generate integers"
|
||||
AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
|
||||
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!"
|
||||
@@ -1104,7 +1111,10 @@ AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMat
|
||||
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||
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.randomNumber="Generate random number in range from{{randomNumberStart}}to{{randomNumberEnd}}"
|
||||
AdvSceneSwitcher.action.projector="Projector"
|
||||
AdvSceneSwitcher.action.projector.action.open="Open"
|
||||
AdvSceneSwitcher.action.projector.action.close="Close"
|
||||
AdvSceneSwitcher.action.projector.type.source="Source"
|
||||
AdvSceneSwitcher.action.projector.type.scene="Scene"
|
||||
AdvSceneSwitcher.action.projector.type.preview="Preview"
|
||||
@@ -1113,8 +1123,9 @@ AdvSceneSwitcher.action.projector.type.multiview="Multiview"
|
||||
AdvSceneSwitcher.action.projector.display="Display"
|
||||
AdvSceneSwitcher.action.projector.windowed="Windowed"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="Fullscreen"
|
||||
AdvSceneSwitcher.action.projector.entry="Open{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="on{{monitors}}"
|
||||
AdvSceneSwitcher.action.projector.entry.close="{{actions}}projector with name{{projectorWindowName}}{{regex}}"
|
||||
AdvSceneSwitcher.action.projector.entry.open.windowed="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.open.fullscreen="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}on{{monitors}}"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="Send message to{{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on{{listenDevices}}:{{listenButton}}"
|
||||
@@ -1134,10 +1145,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.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.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.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
|
||||
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}"
|
||||
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}}"
|
||||
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.marker.description="Describe marker"
|
||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
|
||||
@@ -1147,6 +1163,8 @@ AdvSceneSwitcher.action.twitch.announcement.blue="Blue"
|
||||
AdvSceneSwitcher.action.twitch.announcement.green="Green"
|
||||
AdvSceneSwitcher.action.twitch.announcement.orange="Orange"
|
||||
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.type.copy.text="Copy text"
|
||||
AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image"
|
||||
@@ -1211,7 +1229,7 @@ AdvSceneSwitcher.validation.text.maxLength="Only %1 characters are allowed in th
|
||||
AdvSceneSwitcher.variable.select="--select variable--"
|
||||
AdvSceneSwitcher.variable.add="Add new variable"
|
||||
AdvSceneSwitcher.variable.configure="Configure variable settings"
|
||||
AdvSceneSwitcher.variable.invalid="Invalid varialbe selection"
|
||||
AdvSceneSwitcher.variable.invalid="Invalid variable selection"
|
||||
AdvSceneSwitcher.variable.name="Name:"
|
||||
AdvSceneSwitcher.variable.value="Current value:"
|
||||
AdvSceneSwitcher.variable.save="Save / load behavior"
|
||||
@@ -1590,12 +1608,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.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_login.reward="User login 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.reward.reward="Reward Information"
|
||||
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="Reward Information (JSON)"
|
||||
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 reward’s 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 reward’s cost, in Channel Points."
|
||||
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.redeemed_at.reward="Redemption time"
|
||||
@@ -1610,22 +1637,37 @@ AdvSceneSwitcher.tempVar.twitch.title.reward="Reward title"
|
||||
AdvSceneSwitcher.tempVar.twitch.cost.reward="Reward cost"
|
||||
AdvSceneSwitcher.tempVar.twitch.prompt.reward="Reward description"
|
||||
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.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.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 didn’t 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.global_cooldown.reward="Reward cooldown"
|
||||
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is 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 isn’t on cooldown."
|
||||
AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward="Reward default image URL"
|
||||
AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward.description="The URL to the default image for the reward."
|
||||
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 isn’t live or max_per_stream isn’t 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 isn’t live or max_per_stream isn’t 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 isn’t on cooldown."
|
||||
|
||||
AdvSceneSwitcher.tempVar.twitch.charity_name="Charity name"
|
||||
AdvSceneSwitcher.tempVar.twitch.charity_description="Charity description"
|
||||
@@ -1794,6 +1836,24 @@ AdvSceneSwitcher.tempVar.twitch.to_broadcaster_user_name.raid.description="The b
|
||||
AdvSceneSwitcher.tempVar.twitch.viewers.raid="Raid participants"
|
||||
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.description="The volume the audio source is outputting."
|
||||
AdvSceneSwitcher.tempVar.audio.configured_volume="Configured volume"
|
||||
@@ -1971,6 +2031,16 @@ AdvSceneSwitcher.tempVar.queue.size="Size"
|
||||
AdvSceneSwitcher.tempVar.queue.running="Is running"
|
||||
AdvSceneSwitcher.tempVar.queue.running.description="Returns \"true\" if the queue is started and \"false\" if it is stopped."
|
||||
|
||||
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath="Last screenshot path"
|
||||
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath.description="The filesystem path to the last screenshot taken."
|
||||
|
||||
AdvSceneSwitcher.tempVar.gameCapture.title="Title"
|
||||
AdvSceneSwitcher.tempVar.gameCapture.title.description="Window title of the application captured by the source."
|
||||
AdvSceneSwitcher.tempVar.gameCapture.class="Class"
|
||||
AdvSceneSwitcher.tempVar.gameCapture.class.description="Window class of the application captured by the source."
|
||||
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
|
||||
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
|
||||
@@ -718,8 +718,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivue"
|
||||
AdvSceneSwitcher.action.projector.display="Affichage"
|
||||
AdvSceneSwitcher.action.projector.windowed="Fenêtré"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
|
||||
AdvSceneSwitcher.action.projector.entry="Ouvrir le projecteur{{windowTypes}}de{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="sur{{monitors}}"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"
|
||||
|
||||
@@ -1068,8 +1068,6 @@ AdvSceneSwitcher.action.projector.type.multiview="マルチビュー"
|
||||
AdvSceneSwitcher.action.projector.display="ディスプレイ"
|
||||
AdvSceneSwitcher.action.projector.windowed="ウィンドウ"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン"
|
||||
AdvSceneSwitcher.action.projector.entry="{{types}}{{scenes}}{{sources}}の{{windowTypes}}プロジェクターを開きます"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="{{monitors}}上で"
|
||||
; AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}"
|
||||
@@ -1089,10 +1087,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="ストリームクリップの
|
||||
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="チャット通知の送信"
|
||||
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="チャットのエモート専用モードを有効にする"
|
||||
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.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.marker.description="マーカーの説明"
|
||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"
|
||||
|
||||
@@ -1049,8 +1049,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivisão"
|
||||
AdvSceneSwitcher.action.projector.display="Exibição"
|
||||
AdvSceneSwitcher.action.projector.windowed="Janela"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="Tela cheia"
|
||||
AdvSceneSwitcher.action.projector.entry="Abrir{{windowTypes}}projetor de{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="em{{monitors}}"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}"
|
||||
@@ -1070,10 +1068,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.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.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.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
|
||||
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}"
|
||||
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}}"
|
||||
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
|
||||
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
|
||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"
|
||||
|
||||
@@ -644,8 +644,6 @@ AdvSceneSwitcher.action.projector.type.multiview="多视图"
|
||||
AdvSceneSwitcher.action.projector.display="显示"
|
||||
AdvSceneSwitcher.action.projector.windowed="窗口"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="全屏"
|
||||
AdvSceneSwitcher.action.projector.entry="打开为{{windowTypes}} 的 {{types}}{{scenes}}{{sources}}投影"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="在 {{monitors}} 上 (从不咕咕的阿坤说:谨慎开启!)"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="发送消息到 {{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="将MIDI消息选择设置为 {{listenDevices}} 上传入的消息: {{listenButton}}"
|
||||
|
||||
13156
deps/exprtk/exprtk.hpp
vendored
13156
deps/exprtk/exprtk.hpp
vendored
File diff suppressed because it is too large
Load Diff
@@ -307,7 +307,7 @@ void SwitcherData::Thread()
|
||||
}
|
||||
}
|
||||
|
||||
ResetForNextInterval();
|
||||
RunIntervalResetSteps();
|
||||
|
||||
if (match) {
|
||||
if (macroMatch) {
|
||||
@@ -357,14 +357,6 @@ void SwitcherData::SetPreconditions()
|
||||
InvalidateMacroTempVarValues();
|
||||
}
|
||||
|
||||
void SwitcherData::ResetForNextInterval()
|
||||
{
|
||||
// Plugin reset functions
|
||||
for (const auto &func : resetIntervalSteps) {
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger,
|
||||
bool &setPrevSceneAfterLinger,
|
||||
@@ -445,7 +437,7 @@ void AutoStartActionQueues();
|
||||
void SwitcherData::Start()
|
||||
{
|
||||
if (!(th && th->isRunning())) {
|
||||
ResetForNextInterval();
|
||||
RunIntervalResetSteps();
|
||||
ResetMacros();
|
||||
AutoStartActionQueues();
|
||||
|
||||
@@ -453,10 +445,7 @@ void SwitcherData::Start()
|
||||
th = new SwitcherThread();
|
||||
th->start((QThread::Priority)threadPriority);
|
||||
|
||||
// Will be overwritten quickly but might be useful
|
||||
writeToStatusFile("Advanced Scene Switcher running");
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
|
||||
nullptr);
|
||||
RunStartSteps();
|
||||
}
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
@@ -483,11 +472,7 @@ void SwitcherData::Stop()
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
||||
if (!obsIsShuttingDown) {
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
|
||||
nullptr);
|
||||
}
|
||||
RunStopSteps();
|
||||
}
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
@@ -735,7 +720,7 @@ void OpenSettingsWindow()
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
|
||||
void AdvSceneSwitcher::HighlightMacroSettingsButton(bool enable)
|
||||
{
|
||||
static QObject *highlight = nullptr;
|
||||
if ((highlight && enable) || (!highlight && !enable)) {
|
||||
@@ -755,13 +740,13 @@ void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
|
||||
highlight = HighlightWidget(ui->macroSettings, Qt::green);
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable)
|
||||
void HighlightMacroSettingsButton(bool enable)
|
||||
{
|
||||
auto window = GetSettingsWindow();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton(
|
||||
static_cast<AdvSceneSwitcher *>(window)->HighlightMacroSettingsButton(
|
||||
enable);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
void SetConditionData(Macro &m);
|
||||
void SwapActions(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:
|
||||
void on_macroAdd_clicked();
|
||||
@@ -386,6 +386,6 @@ private:
|
||||
};
|
||||
|
||||
void OpenSettingsWindow();
|
||||
void HighligthMacroSettingsButton(bool enable);
|
||||
void HighlightMacroSettingsButton(bool enable);
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -344,6 +344,10 @@ void AdvSceneSwitcher::on_importSettings_clicked()
|
||||
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);
|
||||
switcher->LoadSettings(obj);
|
||||
switcher->lastImportPath = path.toStdString();
|
||||
|
||||
@@ -21,6 +21,29 @@ bool FileSwitch::pause = false;
|
||||
static QObject *addPulse = nullptr;
|
||||
static std::hash<std::string> strHash;
|
||||
|
||||
static void writeToStatusFile(const QString &msg)
|
||||
{
|
||||
if (!GetSwitcher() || !GetSwitcher()->fileIO.writeEnabled ||
|
||||
GetSwitcher()->fileIO.writePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(QString::fromStdString(GetSwitcher()->fileIO.writePath));
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
stream << msg << Qt::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
static bool _ = []() {
|
||||
AddStartStep(
|
||||
[]() { writeToStatusFile("Advanced Scene Switcher running"); });
|
||||
AddStopStep(
|
||||
[]() { writeToStatusFile("Advanced Scene Switcher stopped"); });
|
||||
return true;
|
||||
}();
|
||||
|
||||
void AdvSceneSwitcher::on_browseButton_clicked()
|
||||
{
|
||||
QString path = QFileDialog::getOpenFileName(
|
||||
@@ -113,24 +136,6 @@ void SwitcherData::writeSceneInfoToFile()
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::writeToStatusFile(const QString &msg)
|
||||
{
|
||||
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(QString::fromStdString(fileIO.writePath));
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
stream << msg << "\n";
|
||||
#else
|
||||
stream << msg << Qt::endl;
|
||||
#endif
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
|
||||
@@ -384,7 +384,11 @@ static void getProcessListProcps2(QStringList &processes)
|
||||
return;
|
||||
}
|
||||
while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) {
|
||||
#ifdef PROCPS2_USE_INFO
|
||||
auto cmd = PIDS_VAL(0, str, stack, info);
|
||||
#else
|
||||
auto cmd = PIDS_VAL(0, str, stack);
|
||||
#endif
|
||||
QString procName(cmd);
|
||||
if (!procName.isEmpty() && !processes.contains(procName)) {
|
||||
processes << procName;
|
||||
|
||||
@@ -309,11 +309,11 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable);
|
||||
void HighlightMacroSettingsButton(bool enable);
|
||||
|
||||
MacroActionMacroEdit::~MacroActionMacroEdit()
|
||||
{
|
||||
HighligthMacroSettingsButton(false);
|
||||
HighlightMacroSettingsButton(false);
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::UpdateEntryData()
|
||||
@@ -480,7 +480,7 @@ void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
_inputs->setVisible(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN &&
|
||||
_entryData->_runOptions.setInputs);
|
||||
HighligthMacroSettingsButton(_entryData->_action ==
|
||||
HighlightMacroSettingsButton(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN &&
|
||||
_entryData->_runOptions.setInputs &&
|
||||
!_inputs->HasInputsToSet());
|
||||
|
||||
@@ -6,14 +6,17 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroActionScript::MacroActionScript(Macro *m, const std::string &id,
|
||||
const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &completionSignal)
|
||||
MacroActionScript::MacroActionScript(
|
||||
Macro *m, const std::string &id, const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: MacroAction(m),
|
||||
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||
triggerSignal, completionSignal),
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName),
|
||||
_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
|
||||
|
||||
@@ -10,8 +10,10 @@ public:
|
||||
MacroActionScript(Macro *m, const std::string &id,
|
||||
const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &signalComplete);
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
MacroActionScript(const advss::MacroActionScript &);
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
@@ -22,6 +24,13 @@ public:
|
||||
|
||||
private:
|
||||
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 = "";
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "source-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionVariable::id = "variable";
|
||||
@@ -17,66 +19,6 @@ bool MacroActionVariable::_registered = MacroActionFactory::Register(
|
||||
{MacroActionVariable::Create, MacroActionVariableEdit::Create,
|
||||
"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)
|
||||
{
|
||||
auto currentValue = var.Value();
|
||||
@@ -253,6 +195,27 @@ void MacroActionVariable::SetToSceneItemName(Variable *var)
|
||||
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 {
|
||||
AskForInputParams(const QString &prompt_, const QString &placeholder_)
|
||||
: prompt(prompt_),
|
||||
@@ -313,14 +276,14 @@ bool MacroActionVariable::PerformAction()
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_type) {
|
||||
case Type::SET_FIXED_VALUE:
|
||||
switch (_action) {
|
||||
case Action::SET_VALUE:
|
||||
var->SetValue(_strValue);
|
||||
break;
|
||||
case Type::APPEND:
|
||||
case Action::APPEND:
|
||||
apppend(*var, _strValue);
|
||||
break;
|
||||
case Type::APPEND_VAR: {
|
||||
case Action::APPEND_VAR: {
|
||||
auto var2 = _variable2.lock();
|
||||
if (!var2) {
|
||||
return true;
|
||||
@@ -328,14 +291,14 @@ bool MacroActionVariable::PerformAction()
|
||||
apppend(*var, var2->Value());
|
||||
break;
|
||||
}
|
||||
case Type::INCREMENT:
|
||||
case Action::INCREMENT:
|
||||
modifyNumValue(*var, _numValue.GetValue(), true);
|
||||
break;
|
||||
case Type::DECREMENT:
|
||||
case Action::DECREMENT:
|
||||
modifyNumValue(*var, _numValue.GetValue(), false);
|
||||
break;
|
||||
case Type::SET_CONDITION_VALUE:
|
||||
case Type::SET_ACTION_VALUE: {
|
||||
case Action::SET_CONDITION_VALUE:
|
||||
case Action::SET_ACTION_VALUE: {
|
||||
auto m = GetMacro();
|
||||
if (!m) {
|
||||
return true;
|
||||
@@ -350,7 +313,7 @@ bool MacroActionVariable::PerformAction()
|
||||
var->SetValue(segment->GetVariableValue());
|
||||
break;
|
||||
}
|
||||
case Type::ROUND_TO_INT: {
|
||||
case Action::ROUND_TO_INT: {
|
||||
auto curValue = var->DoubleValue();
|
||||
if (!curValue.has_value()) {
|
||||
return true;
|
||||
@@ -358,7 +321,7 @@ bool MacroActionVariable::PerformAction()
|
||||
var->SetValue(std::to_string(int(std::round(*curValue))));
|
||||
return true;
|
||||
}
|
||||
case Type::SUBSTRING: {
|
||||
case Action::SUBSTRING: {
|
||||
if (_subStringRegex.Enabled()) {
|
||||
HandleRegexSubString(var.get());
|
||||
return true;
|
||||
@@ -366,15 +329,15 @@ bool MacroActionVariable::PerformAction()
|
||||
HandleIndexSubString(var.get());
|
||||
return true;
|
||||
}
|
||||
case Type::FIND_AND_REPLACE: {
|
||||
case Action::FIND_AND_REPLACE: {
|
||||
HandleFindAndReplace(var.get());
|
||||
return true;
|
||||
}
|
||||
case Type::MATH_EXPRESSION: {
|
||||
case Action::MATH_EXPRESSION: {
|
||||
HandleMathExpression(var.get());
|
||||
return true;
|
||||
}
|
||||
case Type::USER_INPUT: {
|
||||
case Action::USER_INPUT: {
|
||||
auto params = std::make_shared<AskForInputParams>(
|
||||
_useCustomPrompt
|
||||
? QString::fromStdString(_inputPrompt)
|
||||
@@ -399,22 +362,22 @@ bool MacroActionVariable::PerformAction()
|
||||
var->SetValue(*params->result);
|
||||
return true;
|
||||
}
|
||||
case Type::ENV_VARIABLE: {
|
||||
case Action::ENV_VARIABLE: {
|
||||
auto value = std::getenv(_envVariableName.c_str());
|
||||
if (value) {
|
||||
var->SetValue(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Type::SCENE_ITEM_COUNT: {
|
||||
case Action::SCENE_ITEM_COUNT: {
|
||||
var->SetValue(GetSceneItemCount(_scene.GetScene(false)));
|
||||
return true;
|
||||
}
|
||||
case Type::STRING_LENGTH: {
|
||||
case Action::STRING_LENGTH: {
|
||||
var->SetValue(std::string(_strValue).length());
|
||||
return true;
|
||||
}
|
||||
case Type::EXTRACT_JSON: {
|
||||
case Action::EXTRACT_JSON: {
|
||||
auto value = GetJsonField(var->Value(), _strValue);
|
||||
if (!value.has_value()) {
|
||||
return true;
|
||||
@@ -422,7 +385,7 @@ bool MacroActionVariable::PerformAction()
|
||||
var->SetValue(*value);
|
||||
return true;
|
||||
}
|
||||
case Type::SET_TO_TEMPVAR: {
|
||||
case Action::SET_TO_TEMPVAR: {
|
||||
auto tempVar = _tempVar.GetTempVariable(GetMacro());
|
||||
if (!tempVar) {
|
||||
return true;
|
||||
@@ -434,18 +397,18 @@ bool MacroActionVariable::PerformAction()
|
||||
var->SetValue(*value);
|
||||
return true;
|
||||
}
|
||||
case Type::SCENE_ITEM_NAME:
|
||||
case Action::SCENE_ITEM_NAME:
|
||||
SetToSceneItemName(var.get());
|
||||
return true;
|
||||
case Type::PAD:
|
||||
case Action::PAD:
|
||||
var->SetValue(pad(var->Value(), _direction, _stringLength,
|
||||
_paddingChar));
|
||||
return true;
|
||||
case Type::TRUNCATE:
|
||||
case Action::TRUNCATE:
|
||||
var->SetValue(
|
||||
truncate(var->Value(), _direction, _stringLength));
|
||||
return true;
|
||||
case Type::SWAP_VALUES: {
|
||||
case Action::SWAP_VALUES: {
|
||||
auto var2 = _variable2.lock();
|
||||
if (!var2) {
|
||||
return true;
|
||||
@@ -456,15 +419,18 @@ bool MacroActionVariable::PerformAction()
|
||||
var2->SetValue(tempValue);
|
||||
return true;
|
||||
}
|
||||
case Type::TRIM: {
|
||||
case Action::TRIM: {
|
||||
var->SetValue(QString::fromStdString(var->Value())
|
||||
.trimmed()
|
||||
.toStdString());
|
||||
return true;
|
||||
}
|
||||
case Type::CHANGE_CASE:
|
||||
case Action::CHANGE_CASE:
|
||||
HandleCaseChange(var.get());
|
||||
return true;
|
||||
case Action::RANDOM_NUMBER:
|
||||
GenerateRandomNumber(var.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -479,7 +445,7 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
|
||||
GetWeakVariableName(_variable2).c_str());
|
||||
_strValue.Save(obj, "strValue");
|
||||
_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());
|
||||
_subStringStart.Save(obj, "subStringStart");
|
||||
_subStringSize.Save(obj, "subStringSize");
|
||||
@@ -502,6 +468,9 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
|
||||
_stringLength.Save(obj, "stringLength");
|
||||
obs_data_set_int(obj, "paddingChar", _paddingChar);
|
||||
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);
|
||||
|
||||
@@ -516,7 +485,7 @@ bool MacroActionVariable::Load(obs_data_t *obj)
|
||||
_variable2 = GetWeakVariableByName(
|
||||
obs_data_get_string(obj, "variable2Name"));
|
||||
_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");
|
||||
_subStringRegex.Load(obj);
|
||||
_regexPattern = obs_data_get_string(obj, "regexPattern");
|
||||
@@ -555,6 +524,10 @@ bool MacroActionVariable::Load(obs_data_t *obj)
|
||||
_regexMatchIdx.Load(obj, "regexMatchIdx");
|
||||
}
|
||||
|
||||
_randomNumberStart.Load(obj, "randomNumberStart");
|
||||
_randomNumberEnd.Load(obj, "randomNumberEnd");
|
||||
_generateInteger = obs_data_get_bool(obj, "generateInteger");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -595,11 +568,11 @@ void MacroActionVariable::SetSegmentIndexValue(int value)
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroSegment> segment;
|
||||
if (_type == Type::SET_CONDITION_VALUE) {
|
||||
if (_action == Action::SET_CONDITION_VALUE) {
|
||||
if (value < (int)m->Conditions().size()) {
|
||||
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()) {
|
||||
segment = m->Actions().at(value);
|
||||
}
|
||||
@@ -623,14 +596,14 @@ int MacroActionVariable::GetSegmentIndexValue() const
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_type == Type::SET_CONDITION_VALUE) {
|
||||
if (_action == Action::SET_CONDITION_VALUE) {
|
||||
auto it = std::find(m->Conditions().begin(),
|
||||
m->Conditions().end(), segment);
|
||||
if (it != m->Conditions().end()) {
|
||||
return std::distance(m->Conditions().begin(), it);
|
||||
}
|
||||
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(),
|
||||
segment);
|
||||
if (it != m->Actions().end()) {
|
||||
@@ -665,16 +638,99 @@ void MacroActionVariable::DecrementCurrentSegmentVariableRef()
|
||||
DecrementVariableRef(segment.get());
|
||||
}
|
||||
|
||||
static inline void populateTypeSelection(QComboBox *list)
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[action, name] : actionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(action));
|
||||
static const std::vector<std::pair<
|
||||
std::variant<MacroActionVariable::Action, bool>, std::string>>
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(type));
|
||||
@@ -734,6 +790,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_stringLength(new VariableSpinBox(this)),
|
||||
_paddingCharSelection(new SingleCharSelection()),
|
||||
_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())
|
||||
{
|
||||
_numValue->setMinimum(-9999999999);
|
||||
@@ -756,9 +819,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
QSizePolicy::Preferred);
|
||||
_sceneItemIndex->setMinimum(1);
|
||||
_stringLength->setMaximum(999);
|
||||
populateTypeSelection(_actions);
|
||||
populateActionSelection(_actions);
|
||||
populateDirectionSelection(_direction);
|
||||
populateCaseTypeSelection(_caseType);
|
||||
_randomNumberStart->setMinimum(-9999999999);
|
||||
_randomNumberStart->setMaximum(9999999999);
|
||||
_randomNumberEnd->setMinimum(-9999999999);
|
||||
_randomNumberEnd->setMaximum(9999999999);
|
||||
|
||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(VariableChanged(const QString &)));
|
||||
@@ -833,6 +900,18 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
SLOT(CharSelectionChanged(const QString &)));
|
||||
QWidget::connect(_caseType, SIGNAL(currentIndexChanged(int)), this,
|
||||
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 = {
|
||||
{"{{variables}}", _variables},
|
||||
@@ -861,6 +940,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
{"{{stringLength}}", _stringLength},
|
||||
{"{{paddingCharSelection}}", _paddingCharSelection},
|
||||
{"{{caseType}}", _caseType},
|
||||
{"{{randomNumberStart}}", _randomNumberStart},
|
||||
{"{{randomNumberEnd}}", _randomNumberEnd},
|
||||
{"{{generateInteger}}", _generateInteger},
|
||||
};
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.variable.entry.other"),
|
||||
@@ -890,6 +972,14 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
"AdvSceneSwitcher.action.variable.entry.userInput.placeholder"),
|
||||
_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;
|
||||
regexConfigLayout->addWidget(_substringRegex);
|
||||
regexConfigLayout->addStretch();
|
||||
@@ -908,6 +998,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
layout->addWidget(_mathExpressionResult);
|
||||
layout->addLayout(_promptLayout);
|
||||
layout->addLayout(_placeholderLayout);
|
||||
layout->addLayout(_randomLayout);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -929,14 +1020,14 @@ void MacroActionVariableEdit::UpdateEntryData()
|
||||
_variables->SetVariable(_entryData->_variable);
|
||||
_variables2->SetVariable(_entryData->_variable2);
|
||||
_actions->setCurrentIndex(
|
||||
_actions->findData(static_cast<int>(_entryData->_type)));
|
||||
_actions->findData(static_cast<int>(_entryData->_action)));
|
||||
_strValue->setPlainText(_entryData->_strValue);
|
||||
_numValue->SetValue(_entryData->_numValue);
|
||||
_segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1);
|
||||
_segmentIdx->SetMacro(_entryData->GetMacro());
|
||||
_segmentIdx->SetType(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_CONDITION_VALUE
|
||||
? MacroSegmentSelection::Type::CONDITION
|
||||
: MacroSegmentSelection::Type::ACTION);
|
||||
_subStringStart->SetValue(_entryData->_subStringStart);
|
||||
@@ -963,6 +1054,9 @@ void MacroActionVariableEdit::UpdateEntryData()
|
||||
QChar::fromLatin1(_entryData->_paddingChar));
|
||||
_caseType->setCurrentIndex(
|
||||
_caseType->findData(static_cast<int>(_entryData->_caseType)));
|
||||
_randomNumberStart->SetValue(_entryData->_randomNumberStart);
|
||||
_randomNumberEnd->SetValue(_entryData->_randomNumberEnd);
|
||||
_generateInteger->setChecked(_entryData->_generateInteger);
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
@@ -996,13 +1090,14 @@ void MacroActionVariableEdit::ActionChanged(int idx)
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_type = static_cast<MacroActionVariable::Type>(
|
||||
_entryData->_action = static_cast<MacroActionVariable::Action>(
|
||||
_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);
|
||||
} else if (_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE) {
|
||||
} else if (_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_CONDITION_VALUE) {
|
||||
_segmentIdx->SetType(MacroSegmentSelection::Type::CONDITION);
|
||||
}
|
||||
SetWidgetVisibility();
|
||||
@@ -1054,10 +1149,10 @@ void MacroActionVariableEdit::SetSegmentValueError(const QString &text)
|
||||
void MacroActionVariableEdit::UpdateSegmentVariableValue()
|
||||
{
|
||||
if (!_entryData ||
|
||||
!(_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE ||
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_ACTION_VALUE)) {
|
||||
!(_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_CONDITION_VALUE ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_ACTION_VALUE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1076,13 +1171,14 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
|
||||
}
|
||||
|
||||
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();
|
||||
if (index < (int)actions.size()) {
|
||||
segment = actions.at(index);
|
||||
}
|
||||
} else if (_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE) {
|
||||
} else if (_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_CONDITION_VALUE) {
|
||||
const auto &conditions = m->Conditions();
|
||||
if (index < (int)conditions.size()) {
|
||||
segment = conditions.at(index);
|
||||
@@ -1099,14 +1195,14 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
|
||||
std::string type;
|
||||
QString fmt;
|
||||
|
||||
if (_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_ACTION_VALUE) {
|
||||
if (_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_ACTION_VALUE) {
|
||||
type = MacroActionFactory::GetActionName(
|
||||
segment->GetId());
|
||||
fmt = QString(obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.actionNoVariableSupport"));
|
||||
} else if (_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE) {
|
||||
} else if (_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_CONDITION_VALUE) {
|
||||
type = MacroConditionFactory::GetConditionName(
|
||||
segment->GetId());
|
||||
fmt = QString(obs_module_text(
|
||||
@@ -1385,6 +1481,26 @@ void MacroActionVariableEdit::CaseTypeChanged(int index)
|
||||
_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()
|
||||
{
|
||||
if (!_entryData) {
|
||||
@@ -1411,10 +1527,11 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
};
|
||||
|
||||
const char *layoutString = "";
|
||||
if (_entryData->_type == MacroActionVariable::Type::PAD) {
|
||||
if (_entryData->_action == MacroActionVariable::Action::PAD) {
|
||||
layoutString = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.pad");
|
||||
} else if (_entryData->_type == MacroActionVariable::Type::TRUNCATE) {
|
||||
} else if (_entryData->_action ==
|
||||
MacroActionVariable::Action::TRUNCATE) {
|
||||
layoutString = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.truncate");
|
||||
} else {
|
||||
@@ -1429,64 +1546,70 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
ClearLayout(_entryLayout);
|
||||
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
|
||||
|
||||
if (_entryData->_type == MacroActionVariable::Type::SET_FIXED_VALUE ||
|
||||
_entryData->_type == MacroActionVariable::Type::APPEND ||
|
||||
_entryData->_type == MacroActionVariable::Type::MATH_EXPRESSION ||
|
||||
_entryData->_type == MacroActionVariable::Type::ENV_VARIABLE ||
|
||||
_entryData->_type == MacroActionVariable::Type::STRING_LENGTH ||
|
||||
_entryData->_type == MacroActionVariable::Type::EXTRACT_JSON) {
|
||||
if (_entryData->_action == MacroActionVariable::Action::SET_VALUE ||
|
||||
_entryData->_action == MacroActionVariable::Action::APPEND ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::MATH_EXPRESSION ||
|
||||
_entryData->_action == MacroActionVariable::Action::ENV_VARIABLE ||
|
||||
_entryData->_action == MacroActionVariable::Action::STRING_LENGTH ||
|
||||
_entryData->_action == MacroActionVariable::Action::EXTRACT_JSON) {
|
||||
RemoveStretchIfPresent(_entryLayout);
|
||||
} else {
|
||||
AddStretchIfNecessary(_entryLayout);
|
||||
}
|
||||
|
||||
_variables2->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::APPEND_VAR ||
|
||||
_entryData->_type == MacroActionVariable::Type::SWAP_VALUES);
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::APPEND_VAR ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SWAP_VALUES);
|
||||
_strValue->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_FIXED_VALUE ||
|
||||
_entryData->_type == MacroActionVariable::Type::APPEND ||
|
||||
_entryData->_type == MacroActionVariable::Type::STRING_LENGTH ||
|
||||
_entryData->_type == MacroActionVariable::Type::EXTRACT_JSON);
|
||||
_entryData->_action == MacroActionVariable::Action::SET_VALUE ||
|
||||
_entryData->_action == MacroActionVariable::Action::APPEND ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::STRING_LENGTH ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::EXTRACT_JSON);
|
||||
_numValue->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::INCREMENT ||
|
||||
_entryData->_type == MacroActionVariable::Type::DECREMENT);
|
||||
_entryData->_action == MacroActionVariable::Action::INCREMENT ||
|
||||
_entryData->_action == MacroActionVariable::Action::DECREMENT);
|
||||
_segmentValueStatus->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_ACTION_VALUE ||
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE);
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_ACTION_VALUE ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_CONDITION_VALUE);
|
||||
_segmentValue->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_ACTION_VALUE ||
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE);
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_ACTION_VALUE ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_CONDITION_VALUE);
|
||||
_segmentIdx->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_ACTION_VALUE ||
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE);
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_ACTION_VALUE ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_CONDITION_VALUE);
|
||||
SetLayoutVisible(_substringLayout,
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SUBSTRING);
|
||||
if (_entryData->_type == MacroActionVariable::Type::SUBSTRING) {
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SUBSTRING);
|
||||
if (_entryData->_action == MacroActionVariable::Action::SUBSTRING) {
|
||||
bool showRegex = _entryData->_subStringRegex.Enabled();
|
||||
SetLayoutVisible(_subStringIndexEntryLayout, !showRegex);
|
||||
SetLayoutVisible(_subStringRegexEntryLayout, showRegex);
|
||||
_regexPattern->setVisible(showRegex);
|
||||
}
|
||||
SetLayoutVisible(_findReplaceLayout,
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::FIND_AND_REPLACE);
|
||||
_mathExpression->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::MATH_EXPRESSION);
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::FIND_AND_REPLACE);
|
||||
_mathExpression->setVisible(
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::MATH_EXPRESSION);
|
||||
_mathExpressionResult->hide();
|
||||
SetLayoutVisible(_promptLayout,
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::USER_INPUT);
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::USER_INPUT);
|
||||
_inputPrompt->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::USER_INPUT &&
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::USER_INPUT &&
|
||||
_entryData->_useCustomPrompt);
|
||||
if (_entryData->_useCustomPrompt) {
|
||||
RemoveStretchIfPresent(_promptLayout);
|
||||
@@ -1495,13 +1618,16 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
}
|
||||
SetLayoutVisible(
|
||||
_placeholderLayout,
|
||||
_entryData->_type == MacroActionVariable::Type::USER_INPUT &&
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::USER_INPUT &&
|
||||
_entryData->_useCustomPrompt);
|
||||
_useInputPlaceholder->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::USER_INPUT &&
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::USER_INPUT &&
|
||||
_entryData->_useCustomPrompt);
|
||||
_inputPlaceholder->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::USER_INPUT &&
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::USER_INPUT &&
|
||||
_entryData->_useCustomPrompt &&
|
||||
_entryData->_useInputPlaceholder);
|
||||
if (_entryData->_useInputPlaceholder) {
|
||||
@@ -1509,31 +1635,35 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
} else {
|
||||
AddStretchIfNecessary(_placeholderLayout);
|
||||
}
|
||||
_envVariable->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::ENV_VARIABLE);
|
||||
_envVariable->setVisible(_entryData->_action ==
|
||||
MacroActionVariable::Action::ENV_VARIABLE);
|
||||
_scenes->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SCENE_ITEM_COUNT ||
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||
_tempVars->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_TO_TEMPVAR);
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SCENE_ITEM_COUNT ||
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SCENE_ITEM_NAME);
|
||||
_tempVars->setVisible(_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_TO_TEMPVAR);
|
||||
_tempVarsHelp->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_TO_TEMPVAR &&
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SET_TO_TEMPVAR &&
|
||||
!_entryData->_tempVar.HasValidID());
|
||||
_sceneItemIndex->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||
_sceneItemIndex->setVisible(
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::SCENE_ITEM_NAME);
|
||||
_direction->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::PAD ||
|
||||
_entryData->_type == MacroActionVariable::Type::TRUNCATE);
|
||||
_entryData->_action == MacroActionVariable::Action::PAD ||
|
||||
_entryData->_action == MacroActionVariable::Action::TRUNCATE);
|
||||
_stringLength->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::PAD ||
|
||||
_entryData->_type == MacroActionVariable::Type::TRUNCATE);
|
||||
_paddingCharSelection->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::PAD);
|
||||
_caseType->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::CHANGE_CASE);
|
||||
_entryData->_action == MacroActionVariable::Action::PAD ||
|
||||
_entryData->_action == MacroActionVariable::Action::TRUNCATE);
|
||||
_paddingCharSelection->setVisible(_entryData->_action ==
|
||||
MacroActionVariable::Action::PAD);
|
||||
_caseType->setVisible(_entryData->_action ==
|
||||
MacroActionVariable::Action::CHANGE_CASE);
|
||||
SetLayoutVisible(_randomLayout,
|
||||
_entryData->_action ==
|
||||
MacroActionVariable::Action::RANDOM_NUMBER);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
|
||||
@@ -28,8 +28,8 @@ public:
|
||||
int GetSegmentIndexValue() const;
|
||||
void ResolveVariablesToFixedValues();
|
||||
|
||||
enum class Type {
|
||||
SET_FIXED_VALUE,
|
||||
enum class Action {
|
||||
SET_VALUE,
|
||||
APPEND,
|
||||
APPEND_VAR,
|
||||
INCREMENT,
|
||||
@@ -52,9 +52,10 @@ public:
|
||||
SWAP_VALUES,
|
||||
TRIM,
|
||||
CHANGE_CASE,
|
||||
RANDOM_NUMBER,
|
||||
};
|
||||
|
||||
Type _type = Type::SET_FIXED_VALUE;
|
||||
Action _action = Action::SET_VALUE;
|
||||
std::weak_ptr<Variable> _variable;
|
||||
std::weak_ptr<Variable> _variable2;
|
||||
StringVariable _strValue = "";
|
||||
@@ -99,6 +100,9 @@ public:
|
||||
};
|
||||
|
||||
CaseType _caseType = CaseType::LOWER_CASE;
|
||||
DoubleVariable _randomNumberStart = 0;
|
||||
DoubleVariable _randomNumberEnd = 100;
|
||||
bool _generateInteger = true;
|
||||
|
||||
private:
|
||||
void DecrementCurrentSegmentVariableRef();
|
||||
@@ -108,6 +112,7 @@ private:
|
||||
void HandleMathExpression(Variable *);
|
||||
void HandleCaseChange(Variable *);
|
||||
void SetToSceneItemName(Variable *);
|
||||
void GenerateRandomNumber(Variable *);
|
||||
|
||||
std::weak_ptr<MacroSegment> _macroSegment;
|
||||
int _segmentIdxLoadValue = -1;
|
||||
@@ -161,6 +166,9 @@ private slots:
|
||||
void StringLengthChanged(const NumberVariable<int> &);
|
||||
void CharSelectionChanged(const QString &);
|
||||
void CaseTypeChanged(int index);
|
||||
void RandomNumberStartChanged(const NumberVariable<double> &);
|
||||
void RandomNumberEndChanged(const NumberVariable<double> &);
|
||||
void GenerateIntegerChanged(int);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -206,6 +214,10 @@ private:
|
||||
VariableSpinBox *_stringLength;
|
||||
SingleCharSelection *_paddingCharSelection;
|
||||
FilterComboBox *_caseType;
|
||||
VariableDoubleSpinBox *_randomNumberStart;
|
||||
VariableDoubleSpinBox *_randomNumberEnd;
|
||||
QCheckBox *_generateInteger;
|
||||
QVBoxLayout *_randomLayout;
|
||||
QHBoxLayout *_entryLayout;
|
||||
|
||||
std::shared_ptr<MacroActionVariable> _entryData;
|
||||
|
||||
@@ -8,10 +8,14 @@ namespace advss {
|
||||
MacroConditionScript::MacroConditionScript(
|
||||
Macro *m, const std::string &id, const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal, const std::string &completionSignal)
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: MacroCondition(m),
|
||||
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||
triggerSignal, completionSignal),
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName),
|
||||
_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
|
||||
|
||||
@@ -10,8 +10,10 @@ public:
|
||||
MacroConditionScript(Macro *m, const std::string &id,
|
||||
const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &signalComplete);
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
MacroConditionScript(const advss::MacroConditionScript &);
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
@@ -20,6 +22,13 @@ public:
|
||||
|
||||
private:
|
||||
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 = "";
|
||||
};
|
||||
|
||||
@@ -67,9 +67,7 @@ void SetMacroSwitchedScene(bool value)
|
||||
{
|
||||
static bool setupDone = false;
|
||||
if (!setupDone) {
|
||||
// Will always be called with switcher lock already held
|
||||
AddIntervalResetStep([]() { macroSceneSwitched = false; },
|
||||
false);
|
||||
AddIntervalResetStep([]() { macroSceneSwitched = false; });
|
||||
setupDone = true;
|
||||
}
|
||||
macroSceneSwitched = value;
|
||||
@@ -109,6 +107,14 @@ bool MacroWasPausedSince(
|
||||
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)
|
||||
{
|
||||
if (!macro) {
|
||||
|
||||
@@ -51,6 +51,7 @@ EXPORT bool MacroIsPaused(Macro *);
|
||||
EXPORT bool
|
||||
MacroWasPausedSince(Macro *,
|
||||
const std::chrono::high_resolution_clock::time_point &);
|
||||
EXPORT bool MacroWasCheckedSinceLastStart(Macro *);
|
||||
|
||||
EXPORT void AddMacroHelperThread(Macro *, std::thread &&);
|
||||
|
||||
|
||||
@@ -181,15 +181,7 @@ void MacroInputEdit::SetInputVariablesAndValues(
|
||||
_variables = inputs;
|
||||
_values = values;
|
||||
if ((size_t)_values.size() < _variables._inputVariables.size()) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
for (int i = 0;
|
||||
_variables._inputVariables.size() - (size_t)_values.size();
|
||||
i++) {
|
||||
_values.push_back({});
|
||||
}
|
||||
#else
|
||||
_values.resize(_variables._inputVariables.size());
|
||||
#endif
|
||||
}
|
||||
SetupWidgets();
|
||||
}
|
||||
@@ -199,7 +191,7 @@ bool MacroInputEdit::HasInputsToSet() const
|
||||
return !_variables._inputVariables.empty();
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable);
|
||||
void HighlightMacroSettingsButton(bool enable);
|
||||
|
||||
void MacroInputEdit::SetupWidgets()
|
||||
{
|
||||
|
||||
@@ -29,6 +29,10 @@ static constexpr std::string_view defaultSettingsParam = "default_settings";
|
||||
static constexpr std::string_view propertiesSignalParam =
|
||||
"properties_signal_name";
|
||||
static constexpr std::string_view triggerSignalParam = "trigger_signal_name";
|
||||
static 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)
|
||||
{
|
||||
@@ -72,16 +76,55 @@ static const std::string deregisterScriptConditionDeclString =
|
||||
/* Script variables */
|
||||
|
||||
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 =
|
||||
"advss_get_variable_value";
|
||||
static constexpr std::string_view setVariableValueFuncName =
|
||||
"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 =
|
||||
std::string("bool ") + getVariableValueFuncName.data() + "(in string " +
|
||||
nameParam.data() + ", out string " + valueParam.data() + ")";
|
||||
static const std::string setVariableValueDeclString =
|
||||
std::string("bool ") + setVariableValueFuncName.data() + "(in string " +
|
||||
nameParam.data() + ", in string " + valueParam.data() + ")";
|
||||
static const std::string registerTempVarDeclString =
|
||||
std::string("bool ") + registerTempVarFuncName.data() + "(in string " +
|
||||
tempVarIdParam.data() + ", in string " + tempVarNameParam.data() +
|
||||
", in string " + tempVarHelpParam.data() + ", in int " +
|
||||
GetInstanceIdParamName().data() + ")";
|
||||
static const std::string deregisterAllTempVarsDeclString =
|
||||
std::string("bool ") + deregisterAllTempVarsFuncName.data() +
|
||||
"(in int " + GetInstanceIdParamName().data() + ")";
|
||||
static const std::string setTempVarValueDeclString =
|
||||
std::string("bool ") + setTempVarValueFuncName.data() + "(in string " +
|
||||
tempVarIdParam.data() + ", in string " + valueParam.data() +
|
||||
", in int " + GetInstanceIdParamName().data() + ")";
|
||||
|
||||
/* Plugin status */
|
||||
|
||||
static constexpr std::string_view stopSignalName = "advss_plugin_stopped";
|
||||
static constexpr std::string_view startSignalName = "advss_plugin_started";
|
||||
static constexpr std::string_view getRunningStatusFuncName =
|
||||
"advss_plugin_running";
|
||||
static constexpr std::string_view resetIntervalSignalName =
|
||||
"advss_interval_reset";
|
||||
|
||||
static const std::string stopSignalDeclString =
|
||||
std::string("void ") + stopSignalName.data() + "()";
|
||||
static const std::string startSignalDeclString =
|
||||
std::string("void ") + startSignalName.data() + "()";
|
||||
static const std::string resetIntervalDeclString =
|
||||
std::string("void ") + resetIntervalSignalName.data() + "()";
|
||||
static const std::string getRunningStatusDeclString =
|
||||
std::string("bool ") + getRunningStatusFuncName.data() + "()";
|
||||
|
||||
static bool setup();
|
||||
static bool setupDone = setup();
|
||||
@@ -103,13 +146,42 @@ static bool setup()
|
||||
&ScriptHandler::GetVariableValue, nullptr);
|
||||
proc_handler_add(ph, setVariableValueDeclString.c_str(),
|
||||
&ScriptHandler::SetVariableValue, nullptr);
|
||||
proc_handler_add(ph, registerTempVarDeclString.c_str(),
|
||||
&ScriptHandler::RegisterTempVar, nullptr);
|
||||
proc_handler_add(ph, deregisterAllTempVarsDeclString.c_str(),
|
||||
&ScriptHandler::DeregisterAllTempVars, nullptr);
|
||||
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
|
||||
&ScriptHandler::SetTempVarValue, nullptr);
|
||||
proc_handler_add(ph, getRunningStatusDeclString.c_str(),
|
||||
&ScriptHandler::GetRunningStatus, nullptr);
|
||||
|
||||
auto sh = obs_get_signal_handler();
|
||||
signal_handler_add(sh, stopSignalDeclString.c_str());
|
||||
signal_handler_add(sh, startSignalDeclString.c_str());
|
||||
signal_handler_add(sh, resetIntervalDeclString.c_str());
|
||||
|
||||
static constexpr auto triggerSignal = [](const std::string_view &name) {
|
||||
auto sh = obs_get_signal_handler();
|
||||
struct calldata data;
|
||||
calldata_init(&data);
|
||||
signal_handler_signal(sh, name.data(), &data);
|
||||
calldata_free(&data);
|
||||
};
|
||||
|
||||
AddIntervalResetStep([]() { triggerSignal(resetIntervalSignalName); });
|
||||
AddStartStep([]() { triggerSignal(startSignalName); });
|
||||
AddStopStep([]() { triggerSignal(stopSignalName); });
|
||||
return true;
|
||||
}
|
||||
|
||||
static void replaceWhitespace(std::string &string)
|
||||
static void replaceProblematicChars(std::string &string)
|
||||
{
|
||||
std::transform(string.begin(), string.end(), string.begin(), [](char c) {
|
||||
return std::isspace(static_cast<unsigned char>(c)) ? '_' : c;
|
||||
if (std::isspace(static_cast<unsigned char>(c)) || c == '(' ||
|
||||
c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
|
||||
return '_';
|
||||
}
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -122,7 +194,7 @@ static std::string getTriggerSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceWhitespace(signal);
|
||||
replaceProblematicChars(signal);
|
||||
signal += "_run";
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
return signal;
|
||||
@@ -140,12 +212,32 @@ static std::string getPropertiesSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceWhitespace(signal);
|
||||
replaceProblematicChars(signal);
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
signal += "_get_properties";
|
||||
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)
|
||||
{
|
||||
const char *actionName;
|
||||
@@ -177,14 +269,19 @@ void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
|
||||
auto triggerSignalName = getTriggerSignal(actionName, true);
|
||||
auto completionSignalName = getCompletionSignal(actionName, true);
|
||||
auto propertiesSignalName = getPropertiesSignal(actionName, true);
|
||||
auto newInstanceSignalName = getNewInstanceSignal(actionName, true);
|
||||
auto deletedInstanceSignalName =
|
||||
getDeletedInstanceSignal(actionName, true);
|
||||
|
||||
const auto createScriptAction =
|
||||
[id, defaultSettings, propertiesSignalName, triggerSignalName,
|
||||
completionSignalName](
|
||||
completionSignalName, newInstanceSignalName,
|
||||
deletedInstanceSignalName](
|
||||
Macro *m) -> std::shared_ptr<MacroAction> {
|
||||
return std::make_shared<MacroActionScript>(
|
||||
m, id, defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName);
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName);
|
||||
};
|
||||
if (!MacroActionFactory::Register(id, {createScriptAction,
|
||||
MacroSegmentScriptEdit::Create,
|
||||
@@ -202,9 +299,15 @@ void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
|
||||
triggerSignalName.c_str());
|
||||
calldata_set_string(data, propertiesSignalParam.data(),
|
||||
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,
|
||||
triggerSignalName,
|
||||
completionSignalName));
|
||||
completionSignalName,
|
||||
newInstanceSignalName,
|
||||
deletedInstanceSignalName));
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
@@ -279,14 +382,19 @@ void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
|
||||
auto triggerSignalName = getTriggerSignal(conditionName, false);
|
||||
auto completionSignalName = getCompletionSignal(conditionName, false);
|
||||
auto propertiesSignalName = getPropertiesSignal(conditionName, false);
|
||||
auto newInstanceSignalName = getNewInstanceSignal(conditionName, false);
|
||||
auto deletedInstanceSignalName =
|
||||
getDeletedInstanceSignal(conditionName, false);
|
||||
|
||||
const auto createScriptCondition =
|
||||
[id, defaultSettings, propertiesSignalName, triggerSignalName,
|
||||
completionSignalName](
|
||||
completionSignalName, newInstanceSignalName,
|
||||
deletedInstanceSignalName](
|
||||
Macro *m) -> std::shared_ptr<MacroCondition> {
|
||||
return std::make_shared<MacroConditionScript>(
|
||||
m, id, defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName);
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName);
|
||||
};
|
||||
if (!MacroConditionFactory::Register(
|
||||
id, {createScriptCondition, MacroSegmentScriptEdit::Create,
|
||||
@@ -304,9 +412,15 @@ void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
|
||||
triggerSignalName.c_str());
|
||||
calldata_set_string(data, propertiesSignalParam.data(),
|
||||
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,
|
||||
triggerSignalName,
|
||||
completionSignalName));
|
||||
completionSignalName,
|
||||
newInstanceSignalName,
|
||||
deletedInstanceSignalName));
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
@@ -399,6 +513,97 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
|
||||
{
|
||||
calldata_set_bool(data, "is_running", PluginIsRunning());
|
||||
}
|
||||
|
||||
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableId;
|
||||
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
|
||||
strlen(variableId) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarIdParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
const char *name;
|
||||
if (!calldata_get_string(data, tempVarNameParam.data(), &name) ||
|
||||
strlen(name) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarNameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
const char *helpText;
|
||||
if (!calldata_get_string(data, tempVarHelpParam.data(), &helpText)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarHelpParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::RegisterTempVar(variableId, name, helpText,
|
||||
instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::DeregisterAllTempVars(void *, calldata_t *data)
|
||||
{
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
deregisterAllTempVarsFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::DeregisterAllTempVars(instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::SetTempVarValue(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableId;
|
||||
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
|
||||
strlen(variableId) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(), tempVarIdParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const char *variableValue;
|
||||
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
|
||||
strlen(variableValue) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(), valueParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::SetTempVarValue(variableId, variableValue,
|
||||
instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
bool ScriptHandler::ActionIdIsValid(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
@@ -424,24 +629,39 @@ static std::string signalNameToPropertiesSignalDecl(const std::string &name)
|
||||
static std::string signalNameToCompletionSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("bool ") + name + "(in int " +
|
||||
GeCompletionIdParamName().data() + ")";
|
||||
GetCompletionIdParamName().data() + ")";
|
||||
}
|
||||
|
||||
ScriptSegmentType::ScriptSegmentType(const std::string &id,
|
||||
const std::string &propertiesSignal,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &completionSignal)
|
||||
static std::string signalNameToInstanceSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("void ") + name + "(out int " +
|
||||
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)
|
||||
{
|
||||
auto sh = obs_get_signal_handler();
|
||||
|
||||
signal_handler_add(
|
||||
obs_get_signal_handler(),
|
||||
signalNameToPropertiesSignalDecl(propertiesSignal).c_str());
|
||||
sh,
|
||||
signalNameToPropertiesSignalDecl(propertiesSignalName).c_str());
|
||||
signal_handler_add(
|
||||
obs_get_signal_handler(),
|
||||
signalNameToTriggerSignalDecl(triggerSignal).c_str());
|
||||
sh, signalNameToTriggerSignalDecl(triggerSignalName).c_str());
|
||||
signal_handler_add(
|
||||
obs_get_signal_handler(),
|
||||
signalNameToCompletionSignalDecl(completionSignal).c_str());
|
||||
sh,
|
||||
signalNameToCompletionSignalDecl(completionSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh,
|
||||
signalNameToInstanceSignalDecl(newInstanceSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh, signalNameToInstanceSignalDecl(deletedInstanceSignalName)
|
||||
.c_str());
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -10,9 +10,11 @@ class ScriptSegmentType {
|
||||
public:
|
||||
ScriptSegmentType() = delete;
|
||||
ScriptSegmentType(const std::string &id,
|
||||
const std::string &propertiesSignal,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &completionSignal);
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
|
||||
private:
|
||||
std::string _id;
|
||||
@@ -26,7 +28,11 @@ public:
|
||||
static void RegisterScriptCondition(void *ctx, calldata_t *data);
|
||||
static void DeregisterScriptCondition(void *ctx, calldata_t *data);
|
||||
static void GetVariableValue(void *ctx, calldata_t *data);
|
||||
static void 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 GetRunningStatus(void *ctx, calldata_t *data);
|
||||
static bool ActionIdIsValid(const std::string &id);
|
||||
static bool ConditionIdIsValid(const std::string &id);
|
||||
|
||||
@@ -46,14 +52,19 @@ static constexpr std::string_view GetActionCompletionSignalParamName()
|
||||
return "completion_signal_name";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GeCompletionIdParamName()
|
||||
static constexpr std::string_view GetCompletionIdParamName()
|
||||
{
|
||||
return "completion_id";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GeResultSignalParamName()
|
||||
static constexpr std::string_view GetResultSignalParamName()
|
||||
{
|
||||
return "result";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetInstanceIdParamName()
|
||||
{
|
||||
return "instance_id";
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -178,11 +178,7 @@ void MacroSegmentList::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton ||
|
||||
event->button() == Qt::RightButton) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
_dragPosition = GetDragIndex(event->globalPos());
|
||||
#else
|
||||
_dragPosition = GetDragIndex(event->globalPosition().toPoint());
|
||||
#endif
|
||||
emit SelectionChanged(_dragPosition);
|
||||
} else {
|
||||
_dragPosition = -1;
|
||||
@@ -300,11 +296,7 @@ void MacroSegmentList::dragMoveEvent(QDragMoveEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
_dragCursorPos = (mapToGlobal(event->pos()));
|
||||
#else
|
||||
_dragCursorPos = (mapToGlobal(event->position().toPoint()));
|
||||
#endif
|
||||
CheckDropLine(_dragCursorPos);
|
||||
}
|
||||
|
||||
@@ -450,16 +442,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
|
||||
{
|
||||
HideLastDropLine();
|
||||
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 &&
|
||||
!widget->geometry().contains(event->position().toPoint()) &&
|
||||
widgetIsInLayout(widget, _contentLayout)) {
|
||||
@@ -470,7 +452,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
|
||||
}
|
||||
emit Reorder(dropPosition, _dragPosition);
|
||||
}
|
||||
#endif
|
||||
_dragPosition = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,33 +11,106 @@
|
||||
namespace advss {
|
||||
|
||||
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,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &completionSignal)
|
||||
MacroSegmentScript::MacroSegmentScript(
|
||||
obs_data_t *defaultSettings, const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: _settings(obs_data_get_defaults(defaultSettings)),
|
||||
_propertiesSignal(propertiesSignalName),
|
||||
_triggerSignal(triggerSignal),
|
||||
_completionSignal(completionSignal)
|
||||
_triggerSignal(triggerSignalName),
|
||||
_completionSignal(completionSignalName),
|
||||
_newInstanceSignal(newInstanceSignalName),
|
||||
_deletedInstanceSignal(deletedInstanceSignalName),
|
||||
_instanceId(++instanceIdCounter)
|
||||
{
|
||||
signal_handler_connect(obs_get_signal_handler(),
|
||||
completionSignal.c_str(),
|
||||
completionSignalName.c_str(),
|
||||
&MacroSegmentScript::CompletionSignalReceived,
|
||||
this);
|
||||
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
instances.emplace_back(this);
|
||||
|
||||
SignalNewInstance();
|
||||
}
|
||||
|
||||
MacroSegmentScript::MacroSegmentScript(const MacroSegmentScript &other)
|
||||
: _settings(obs_data_create()),
|
||||
_propertiesSignal(other._propertiesSignal),
|
||||
_triggerSignal(other._triggerSignal),
|
||||
_completionSignal(other._completionSignal)
|
||||
_completionSignal(other._completionSignal),
|
||||
_newInstanceSignal(other._newInstanceSignal),
|
||||
_deletedInstanceSignal(other._deletedInstanceSignal),
|
||||
_instanceId(++instanceIdCounter)
|
||||
{
|
||||
signal_handler_connect(obs_get_signal_handler(),
|
||||
_completionSignal.c_str(),
|
||||
&MacroSegmentScript::CompletionSignalReceived,
|
||||
this);
|
||||
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
|
||||
@@ -85,8 +158,10 @@ bool MacroSegmentScript::SendTriggerSignal()
|
||||
auto data = calldata_create();
|
||||
calldata_set_string(data, GetActionCompletionSignalParamName().data(),
|
||||
_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_int(data, GetInstanceIdParamName().data(), _instanceId);
|
||||
signal_handler_signal(obs_get_signal_handler(), _triggerSignal.c_str(),
|
||||
data);
|
||||
calldata_destroy(data);
|
||||
@@ -101,18 +176,18 @@ void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
|
||||
{
|
||||
auto segment = static_cast<MacroSegmentScript *>(param);
|
||||
long long int id;
|
||||
if (!calldata_get_int(data, GeCompletionIdParamName().data(), &id)) {
|
||||
if (!calldata_get_int(data, GetCompletionIdParamName().data(), &id)) {
|
||||
blog(LOG_WARNING,
|
||||
"received completion signal without \"%s\" parameter",
|
||||
GeCompletionIdParamName().data());
|
||||
GetCompletionIdParamName().data());
|
||||
return;
|
||||
}
|
||||
bool result;
|
||||
if (!calldata_get_bool(data, GeResultSignalParamName().data(),
|
||||
if (!calldata_get_bool(data, GetResultSignalParamName().data(),
|
||||
&result)) {
|
||||
blog(LOG_WARNING,
|
||||
"received completion signal without \"%s\" parameter",
|
||||
GeResultSignalParamName().data());
|
||||
GetResultSignalParamName().data());
|
||||
return;
|
||||
}
|
||||
if (id != segment->_completionId) {
|
||||
@@ -122,6 +197,19 @@ void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
|
||||
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)
|
||||
{
|
||||
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
|
||||
|
||||
@@ -15,9 +15,20 @@ class MacroSegmentScript {
|
||||
public:
|
||||
MacroSegmentScript(obs_data_t *defaultSettings,
|
||||
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);
|
||||
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:
|
||||
bool Save(obs_data_t *obj) const;
|
||||
@@ -31,15 +42,27 @@ protected:
|
||||
double GetTimeoutSeconds() const { return _timeout.Seconds(); };
|
||||
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:
|
||||
virtual void WaitForCompletion() const = 0;
|
||||
static void CompletionSignalReceived(void *param, calldata_t *data);
|
||||
void SignalNewInstance() const;
|
||||
|
||||
OBSDataAutoRelease _settings;
|
||||
std::string _propertiesSignal = "";
|
||||
|
||||
std::string _triggerSignal = "";
|
||||
std::string _completionSignal = "";
|
||||
std::string _newInstanceSignal = "";
|
||||
std::string _deletedInstanceSignal = "";
|
||||
std::atomic_bool _triggerIsComplete = {false};
|
||||
bool _triggerResult = false;
|
||||
int64_t _completionId = 0;
|
||||
|
||||
@@ -294,13 +294,8 @@ bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
|
||||
QMouseEvent *newEvent = new QMouseEvent(
|
||||
mouseEvent->type(),
|
||||
_headerInfo->mapTo(this, mouseEvent->pos()),
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
mouseEvent->globalPos(),
|
||||
#else
|
||||
mouseEvent->globalPosition(),
|
||||
#endif
|
||||
mouseEvent->button(), mouseEvent->buttons(),
|
||||
mouseEvent->modifiers());
|
||||
mouseEvent->globalPosition(), mouseEvent->button(),
|
||||
mouseEvent->buttons(), mouseEvent->modifiers());
|
||||
QApplication::sendEvent(parentWidget(), newEvent);
|
||||
}
|
||||
return QWidget::eventFilter(obj, ev);
|
||||
|
||||
@@ -178,6 +178,7 @@ void MacroTreeItem::Update(bool force)
|
||||
QSizePolicy::Maximum);
|
||||
_expand->setMaximumSize(10, 16);
|
||||
_expand->setMinimumSize(10, 0);
|
||||
_expand->setProperty("class", "checkbox-icon indicator-expand");
|
||||
#ifdef __APPLE__
|
||||
_expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
#endif
|
||||
@@ -888,14 +889,7 @@ void MacroTree::dropEvent(QDropEvent *event)
|
||||
QModelIndexList indices = selectedIndexes();
|
||||
|
||||
DropIndicatorPosition indicator = dropIndicatorPosition();
|
||||
int row = indexAt(
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
event->position().toPoint()
|
||||
#else
|
||||
event->pos()
|
||||
#endif
|
||||
)
|
||||
.row();
|
||||
int row = indexAt(event->position().toPoint()).row();
|
||||
bool emptyDrop = row == -1;
|
||||
|
||||
if (emptyDrop) {
|
||||
|
||||
@@ -244,8 +244,13 @@ bool Macro::CheckConditions(bool ignorePause)
|
||||
return false;
|
||||
}
|
||||
|
||||
_stop = false;
|
||||
_matched = false;
|
||||
for (auto &condition : _conditions) {
|
||||
if (!condition) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_paused && !ignorePause) {
|
||||
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
|
||||
return false;
|
||||
@@ -383,6 +388,9 @@ bool Macro::RunActionsHelper(
|
||||
|
||||
bool actionsExecutedSuccessfully = true;
|
||||
for (auto &action : actions) {
|
||||
if (!action) {
|
||||
continue;
|
||||
}
|
||||
if (action->Enabled()) {
|
||||
action->LogAction();
|
||||
actionsExecutedSuccessfully =
|
||||
|
||||
@@ -40,8 +40,8 @@ ActionQueueTable *ActionQueueTable::Create()
|
||||
void ActionQueueTable::Add()
|
||||
{
|
||||
auto newQueue = std::make_shared<ActionQueue>();
|
||||
auto accepted =
|
||||
ActionQueueSettingsDialog::AskForSettings(this, *newQueue);
|
||||
auto accepted = ActionQueueSettingsDialog::AskForSettings(
|
||||
GetSettingsWindow(), *newQueue);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
@@ -124,7 +124,7 @@ static void openSettingsDialog()
|
||||
|
||||
auto oldName = queue->Name();
|
||||
bool accepted = ActionQueueSettingsDialog::AskForSettings(
|
||||
tabWidget->Table(), *queue.get());
|
||||
GetSettingsWindow(), *queue.get());
|
||||
if (accepted && oldName != queue->Name()) {
|
||||
ActionQueueSignalManager::Instance()->Rename(
|
||||
QString::fromStdString(oldName),
|
||||
|
||||
@@ -113,6 +113,7 @@ void ActionQueue::Add(const std::shared_ptr<MacroAction> &action)
|
||||
action->Save(data);
|
||||
copy->Load(data);
|
||||
copy->PostLoad();
|
||||
RunPostLoadSteps();
|
||||
copy->ResolveVariablesToFixedValues();
|
||||
_actions.emplace_back(copy);
|
||||
} else {
|
||||
@@ -179,7 +180,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
|
||||
"AdvSceneSwitcher.actionQueues.select",
|
||||
"AdvSceneSwitcher.actionQueues.add",
|
||||
"AdvSceneSwitcher.actionQueues.nameNotAvailable",
|
||||
parent),
|
||||
true, parent),
|
||||
_queueRunStatus(new QLabel()),
|
||||
_startStopToggle(new QPushButton()),
|
||||
_queueSize(new QLabel()),
|
||||
|
||||
@@ -168,17 +168,6 @@ void SwitcherData::SaveVersion(obs_data_t *obj,
|
||||
obs_data_set_string(obj, "version", currentVersion.c_str());
|
||||
}
|
||||
|
||||
void SwitcherData::AddIntervalResetStep(std::function<void()> function,
|
||||
bool tryLock)
|
||||
{
|
||||
if (!tryLock) {
|
||||
resetIntervalSteps.emplace_back(function);
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
resetIntervalSteps.emplace_back(function);
|
||||
}
|
||||
|
||||
void SwitcherData::RunPostLoadSteps()
|
||||
{
|
||||
for (const auto &func : postLoadSteps) {
|
||||
|
||||
@@ -57,11 +57,9 @@ public:
|
||||
bool AnySceneTransitionStarted();
|
||||
|
||||
void SetPreconditions();
|
||||
void ResetForNextInterval();
|
||||
void AddSaveStep(std::function<void(obs_data_t *)>);
|
||||
void AddLoadStep(std::function<void(obs_data_t *)>);
|
||||
void AddPostLoadStep(std::function<void()>);
|
||||
void AddIntervalResetStep(std::function<void()>, bool lock = true);
|
||||
void RunPostLoadSteps();
|
||||
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &linger, bool &setPreviousSceneAsMatch,
|
||||
@@ -100,7 +98,6 @@ public:
|
||||
std::vector<std::function<void(obs_data_t *)>> saveSteps;
|
||||
std::vector<std::function<void(obs_data_t *)>> loadSteps;
|
||||
std::vector<std::function<void()>> postLoadSteps;
|
||||
std::vector<std::function<void()>> resetIntervalSteps;
|
||||
|
||||
bool firstBoot = true;
|
||||
bool transitionActive = false;
|
||||
@@ -233,7 +230,6 @@ public:
|
||||
bool checkPause();
|
||||
void checkDefaultSceneTransitions();
|
||||
void writeSceneInfoToFile();
|
||||
void writeToStatusFile(const QString &msg);
|
||||
void checkSwitchCooldown(bool &match);
|
||||
|
||||
std::deque<WindowSwitch> windowSwitches;
|
||||
|
||||
@@ -14,11 +14,7 @@ AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
|
||||
&AutoUpdateTooltipLabel::UpdateTooltip);
|
||||
}
|
||||
|
||||
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
|
||||
void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
|
||||
#else
|
||||
void AutoUpdateTooltipLabel::enterEvent(QEvent *event)
|
||||
#endif
|
||||
{
|
||||
_timer->start(_updateIntervalMs);
|
||||
QLabel::enterEvent(event);
|
||||
|
||||
@@ -17,11 +17,7 @@ public:
|
||||
int updateIntervalMs = 300);
|
||||
|
||||
protected:
|
||||
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
#else
|
||||
void enterEvent(QEvent *event) override;
|
||||
#endif
|
||||
void leaveEvent(QEvent *event) override;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -29,6 +29,7 @@ void Duration::Load(obs_data_t *obj, const char *name)
|
||||
// TODO: remove this fallback
|
||||
if (!data || !obs_data_has_user_value(data, "version") ||
|
||||
obs_data_get_int(data, "version") != 1) {
|
||||
_value = 0.;
|
||||
bool usingDefaultArgs = strcmp("duration", name) == 0;
|
||||
if (usingDefaultArgs) {
|
||||
_value = obs_data_get_double(obj, "seconds");
|
||||
|
||||
@@ -59,7 +59,10 @@ ItemSelection::ItemSelection(std::deque<std::shared_ptr<Item>> &items,
|
||||
_conflictStr(conflict)
|
||||
{
|
||||
_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);
|
||||
if (!configureTooltip.empty()) {
|
||||
_modify->setToolTip(obs_module_text(configureTooltip.data()));
|
||||
@@ -265,6 +268,7 @@ ItemSettingsDialog::ItemSettingsDialog(const Item &settings,
|
||||
std::string_view select,
|
||||
std::string_view add,
|
||||
std::string_view nameConflict,
|
||||
bool showNameEmptyWarning,
|
||||
QWidget *parent)
|
||||
: QDialog(parent),
|
||||
_name(new QLineEdit()),
|
||||
@@ -274,7 +278,8 @@ ItemSettingsDialog::ItemSettingsDialog(const Item &settings,
|
||||
_items(items),
|
||||
_selectStr(select),
|
||||
_addStr(add),
|
||||
_conflictStr(nameConflict)
|
||||
_conflictStr(nameConflict),
|
||||
_showNameEmptyWarning(showNameEmptyWarning)
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
std::string_view addString = "AdvSceneSwitcher.item.add",
|
||||
std::string_view conflictString =
|
||||
"AdvSceneSwitcher.item.nameNotAvailable",
|
||||
QWidget *parent = 0);
|
||||
bool showEmptyNameWarning = true, QWidget *parent = 0);
|
||||
virtual ~ItemSettingsDialog() = default;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -21,6 +21,24 @@ static std::vector<std::function<void()>> &getPluginCleanupSteps()
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::vector<std::function<void()>> &getResetIntervalSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::vector<std::function<void()>> &getStartSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::vector<std::function<void()>> &getStopSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::mutex mutex;
|
||||
|
||||
void SavePluginSettings(obs_data_t *obj)
|
||||
@@ -48,9 +66,10 @@ void AddPostLoadStep(std::function<void()> step)
|
||||
GetSwitcher()->AddPostLoadStep(step);
|
||||
}
|
||||
|
||||
void AddIntervalResetStep(std::function<void()> step, bool lock)
|
||||
void AddIntervalResetStep(std::function<void()> step)
|
||||
{
|
||||
GetSwitcher()->AddIntervalResetStep(step, lock);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getResetIntervalSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void RunPostLoadSteps()
|
||||
@@ -100,6 +119,42 @@ void RunPluginCleanupSteps()
|
||||
}
|
||||
}
|
||||
|
||||
void RunIntervalResetSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getResetIntervalSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void AddStartStep(std::function<void()> step)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getStartSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void AddStopStep(std::function<void()> step)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getStopSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void RunStartSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getStartSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void RunStopSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getStopSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void StopPlugin()
|
||||
{
|
||||
GetSwitcher()->Stop();
|
||||
|
||||
@@ -11,7 +11,7 @@ EXPORT void LoadPluginSettings(obs_data_t *);
|
||||
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);
|
||||
EXPORT void AddLoadStep(std::function<void(obs_data_t *)>);
|
||||
EXPORT void AddPostLoadStep(std::function<void()>);
|
||||
EXPORT void AddIntervalResetStep(std::function<void()>, bool lock = true);
|
||||
EXPORT void AddIntervalResetStep(std::function<void()>);
|
||||
EXPORT void RunPostLoadSteps();
|
||||
|
||||
EXPORT void AddPluginInitStep(std::function<void()>);
|
||||
@@ -25,6 +25,11 @@ EXPORT void StopPlugin();
|
||||
EXPORT void StartPlugin();
|
||||
EXPORT bool PluginIsRunning();
|
||||
EXPORT int GetIntervalValue();
|
||||
void AddStartStep(std::function<void()>);
|
||||
void AddStopStep(std::function<void()>);
|
||||
void RunStartSteps();
|
||||
void RunStopSteps();
|
||||
void RunIntervalResetSteps();
|
||||
|
||||
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
|
||||
EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior);
|
||||
|
||||
@@ -92,7 +92,10 @@ RegexConfigWidget::RegexConfigWidget(QWidget *parent, bool showEnable)
|
||||
_openSettings(new QToolButton()),
|
||||
_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(
|
||||
obs_module_text("AdvSceneSwitcher.regex.configure"));
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ void ResizingPlainTextEdit::AddHeightForScrollBar(bool addHeight)
|
||||
return;
|
||||
}
|
||||
|
||||
_hScrollBarAddedHeight = verticalScrollBar()->height();
|
||||
_hScrollBarAddedHeight = horizontalScrollBar()->height();
|
||||
setFixedHeight(height() + _hScrollBarAddedHeight);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,14 @@ static bool setupDone = setup();
|
||||
bool setup()
|
||||
{
|
||||
AddPluginPostLoadStep(registerWebsocketVendor);
|
||||
AddStartStep([]() {
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
|
||||
nullptr);
|
||||
});
|
||||
AddStopStep([]() {
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
|
||||
nullptr);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -109,6 +117,9 @@ void RegisterWebsocketRequest(
|
||||
|
||||
void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data)
|
||||
{
|
||||
if (OBSIsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ private:
|
||||
T _value = {};
|
||||
std::weak_ptr<Variable> _variable;
|
||||
|
||||
friend class GenericVaraiableSpinbox;
|
||||
friend class GenericVariableSpinbox;
|
||||
friend class VariableSpinBox;
|
||||
friend class VariableDoubleSpinBox;
|
||||
};
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent,
|
||||
bool wholeNumber)
|
||||
GenericVariableSpinbox::GenericVariableSpinbox(QWidget *parent,
|
||||
bool wholeNumber)
|
||||
: QWidget(parent),
|
||||
_fixedValueInt(new QSpinBox()),
|
||||
_fixedValueDouble(new QDoubleSpinBox()),
|
||||
@@ -16,7 +16,9 @@ GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent,
|
||||
{
|
||||
_toggleType->setCheckable(true);
|
||||
_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,
|
||||
SLOT(SetFixedValue(int)));
|
||||
@@ -48,7 +50,7 @@ GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent,
|
||||
SetVisibility();
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::SetValue(const NumberVariable<int> &number)
|
||||
void GenericVariableSpinbox::SetValue(const NumberVariable<int> &number)
|
||||
{
|
||||
_numberInt = number;
|
||||
const QSignalBlocker b1(_toggleType);
|
||||
@@ -59,7 +61,7 @@ void GenericVaraiableSpinbox::SetValue(const NumberVariable<int> &number)
|
||||
SetVisibility();
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::SetValue(const NumberVariable<double> &number)
|
||||
void GenericVariableSpinbox::SetValue(const NumberVariable<double> &number)
|
||||
{
|
||||
_numberDouble = number;
|
||||
const QSignalBlocker b1(_toggleType);
|
||||
@@ -70,44 +72,44 @@ void GenericVaraiableSpinbox::SetValue(const NumberVariable<double> &number)
|
||||
SetVisibility();
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::DisableVariableSelection()
|
||||
void GenericVariableSpinbox::DisableVariableSelection()
|
||||
{
|
||||
_hideTypeToggle = true;
|
||||
_toggleType->hide();
|
||||
ToggleTypeClicked(false);
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::setMinimum(double value)
|
||||
void GenericVariableSpinbox::setMinimum(double value)
|
||||
{
|
||||
_fixedValueInt->setMinimum(value);
|
||||
_fixedValueDouble->setMinimum(value);
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::setMaximum(double value)
|
||||
void GenericVariableSpinbox::setMaximum(double value)
|
||||
{
|
||||
_fixedValueInt->setMaximum(value);
|
||||
_fixedValueDouble->setMaximum(value);
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::setPrefix(const QString &prefix)
|
||||
void GenericVariableSpinbox::setPrefix(const QString &prefix)
|
||||
{
|
||||
_fixedValueInt->setPrefix(prefix);
|
||||
_fixedValueDouble->setPrefix(prefix);
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::setSuffix(const QString &suffix)
|
||||
void GenericVariableSpinbox::setSuffix(const QString &suffix)
|
||||
{
|
||||
_fixedValueInt->setSuffix(suffix);
|
||||
_fixedValueDouble->setSuffix(suffix);
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::setSpecialValueText(const QString &text)
|
||||
void GenericVariableSpinbox::setSpecialValueText(const QString &text)
|
||||
{
|
||||
_fixedValueInt->setSpecialValueText(text);
|
||||
_fixedValueDouble->setSpecialValueText(text);
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::SetFixedValue(int value)
|
||||
void GenericVariableSpinbox::SetFixedValue(int value)
|
||||
{
|
||||
_numberInt._value = value;
|
||||
const QSignalBlocker b(_fixedValueInt);
|
||||
@@ -115,7 +117,7 @@ void GenericVaraiableSpinbox::SetFixedValue(int value)
|
||||
EmitSignals();
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::SetFixedValue(double value)
|
||||
void GenericVariableSpinbox::SetFixedValue(double value)
|
||||
{
|
||||
_numberDouble._value = value;
|
||||
const QSignalBlocker b(_fixedValueDouble);
|
||||
@@ -123,14 +125,14 @@ void GenericVaraiableSpinbox::SetFixedValue(double value)
|
||||
EmitSignals();
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::VariableChanged(const QString &name)
|
||||
void GenericVariableSpinbox::VariableChanged(const QString &name)
|
||||
{
|
||||
_numberInt._variable = GetWeakVariableByQString(name);
|
||||
_numberDouble._variable = GetWeakVariableByQString(name);
|
||||
EmitSignals();
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::ToggleTypeClicked(bool useVariable)
|
||||
void GenericVariableSpinbox::ToggleTypeClicked(bool useVariable)
|
||||
{
|
||||
_numberInt._type = useVariable ? NumberVariable<int>::Type::VARIABLE
|
||||
: NumberVariable<int>::Type::FIXED_VALUE;
|
||||
@@ -142,7 +144,7 @@ void GenericVaraiableSpinbox::ToggleTypeClicked(bool useVariable)
|
||||
EmitSignals();
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::EmitSignals()
|
||||
void GenericVariableSpinbox::EmitSignals()
|
||||
{
|
||||
if (_wholeNumber) {
|
||||
emit FixedValueChanged(_numberInt);
|
||||
@@ -153,7 +155,7 @@ void GenericVaraiableSpinbox::EmitSignals()
|
||||
}
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::SetVisibility()
|
||||
void GenericVariableSpinbox::SetVisibility()
|
||||
{
|
||||
if (_wholeNumber) {
|
||||
_fixedValueDouble->hide();
|
||||
@@ -164,7 +166,7 @@ void GenericVaraiableSpinbox::SetVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::SetVisibilityInt()
|
||||
void GenericVariableSpinbox::SetVisibilityInt()
|
||||
{
|
||||
if (_numberInt.IsFixedType()) {
|
||||
_fixedValueInt->show();
|
||||
@@ -180,7 +182,7 @@ void GenericVaraiableSpinbox::SetVisibilityInt()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void GenericVaraiableSpinbox::SetVisibilityDouble()
|
||||
void GenericVariableSpinbox::SetVisibilityDouble()
|
||||
{
|
||||
if (_numberDouble.IsFixedType()) {
|
||||
_fixedValueDouble->show();
|
||||
@@ -197,12 +199,12 @@ void GenericVaraiableSpinbox::SetVisibilityDouble()
|
||||
}
|
||||
|
||||
VariableSpinBox::VariableSpinBox(QWidget *parent)
|
||||
: GenericVaraiableSpinbox(parent, true)
|
||||
: GenericVariableSpinbox(parent, true)
|
||||
{
|
||||
}
|
||||
|
||||
VariableDoubleSpinBox::VariableDoubleSpinBox(QWidget *parent)
|
||||
: GenericVaraiableSpinbox(parent, false)
|
||||
: GenericVariableSpinbox(parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ADVSS_EXPORT GenericVaraiableSpinbox : public QWidget {
|
||||
class ADVSS_EXPORT GenericVariableSpinbox : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GenericVaraiableSpinbox(QWidget *parent, bool wholeNumber);
|
||||
GenericVariableSpinbox(QWidget *parent, bool wholeNumber);
|
||||
void SetValue(const NumberVariable<int> &);
|
||||
void SetValue(const NumberVariable<double> &);
|
||||
void DisableVariableSelection();
|
||||
@@ -59,7 +59,7 @@ private:
|
||||
bool _hideTypeToggle = false;
|
||||
};
|
||||
|
||||
class ADVSS_EXPORT VariableSpinBox : public GenericVaraiableSpinbox {
|
||||
class ADVSS_EXPORT VariableSpinBox : public GenericVariableSpinbox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
QSpinBox *SpinBox() { return _fixedValueInt; }
|
||||
};
|
||||
|
||||
class ADVSS_EXPORT VariableDoubleSpinBox : public GenericVaraiableSpinbox {
|
||||
class ADVSS_EXPORT VariableDoubleSpinBox : public GenericVariableSpinbox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
@@ -43,8 +43,8 @@ VariableTable *VariableTable::Create()
|
||||
void VariableTable::Add()
|
||||
{
|
||||
auto newVariable = std::make_shared<Variable>();
|
||||
auto accepted =
|
||||
VariableSettingsDialog::AskForSettings(this, *newVariable);
|
||||
auto accepted = VariableSettingsDialog::AskForSettings(
|
||||
GetSettingsWindow(), *newVariable);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
@@ -184,7 +184,7 @@ static void openSettingsDialog()
|
||||
|
||||
auto oldName = variable->Name();
|
||||
bool accepted = VariableSettingsDialog::AskForSettings(
|
||||
tabWidget->Table(), *variable.get());
|
||||
GetSettingsWindow(), *variable.get());
|
||||
if (accepted && oldName != variable->Name()) {
|
||||
VariableSignalManager::Instance()->Rename(
|
||||
QString::fromStdString(oldName),
|
||||
|
||||
@@ -137,7 +137,8 @@ VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
|
||||
: ItemSettingsDialog(settings, variables,
|
||||
"AdvSceneSwitcher.variable.select",
|
||||
"AdvSceneSwitcher.variable.add",
|
||||
"AdvSceneSwitcher.item.nameNotAvailable", parent),
|
||||
"AdvSceneSwitcher.item.nameNotAvailable", true,
|
||||
parent),
|
||||
_value(new ResizingPlainTextEdit(this)),
|
||||
_defaultValue(new ResizingPlainTextEdit(this)),
|
||||
_save(new QComboBox())
|
||||
|
||||
@@ -89,6 +89,8 @@ target_sources(
|
||||
macro-condition-filter.hpp
|
||||
macro-condition-folder.cpp
|
||||
macro-condition-folder.hpp
|
||||
macro-condition-game-capture.cpp
|
||||
macro-condition-game-capture.hpp
|
||||
macro-condition-hotkey.cpp
|
||||
macro-condition-hotkey.hpp
|
||||
macro-condition-idle.cpp
|
||||
@@ -117,6 +119,8 @@ target_sources(
|
||||
macro-condition-scene-visibility.hpp
|
||||
macro-condition-scene.cpp
|
||||
macro-condition-scene.hpp
|
||||
macro-condition-screenshot.cpp
|
||||
macro-condition-screenshot.hpp
|
||||
macro-condition-slideshow.cpp
|
||||
macro-condition-slideshow.hpp
|
||||
macro-condition-source.cpp
|
||||
|
||||
@@ -215,7 +215,7 @@ MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
_settingsString(new VariableTextEdit(this)),
|
||||
_refreshSettingSelection(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.action.filter.refresh"))),
|
||||
_settingsButtons(new QComboBox())
|
||||
_settingsButtons(new SourceSettingsButtonSelection(this))
|
||||
{
|
||||
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
|
||||
@@ -253,8 +253,10 @@ MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
SLOT(SelectionChanged(const SourceSetting &)));
|
||||
QWidget::connect(_refreshSettingSelection, SIGNAL(clicked()), this,
|
||||
SLOT(RefreshVariableSourceSelectionValue()));
|
||||
QWidget::connect(_settingsButtons, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(ButtonChanged(int)));
|
||||
QWidget::connect(_settingsButtons,
|
||||
SIGNAL(SelectionChanged(const SourceSettingButton &)),
|
||||
this,
|
||||
SLOT(ButtonChanged(const SourceSettingButton &)));
|
||||
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources},
|
||||
@@ -305,25 +307,18 @@ void MacroActionFilterEdit::UpdateEntryData()
|
||||
const auto filters =
|
||||
_entryData->_filter.GetFilters(_entryData->_source);
|
||||
OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0);
|
||||
_filterSettings->SetSource(firstFilter);
|
||||
_filterSettings->SetSetting(_entryData->_setting);
|
||||
_filterSettings->SetSelection(firstFilter, _entryData->_setting);
|
||||
_settingsInputMethods->setCurrentIndex(_settingsInputMethods->findData(
|
||||
static_cast<int>(_entryData->_settingsInputMethod)));
|
||||
_tempVars->SetVariable(_entryData->_tempVar);
|
||||
_manualSettingValue->setPlainText(_entryData->_manualSettingValue);
|
||||
PopulateSourceButtonSelection(_settingsButtons, firstFilter);
|
||||
_settingsButtons->setCurrentText(
|
||||
QString::fromStdString(_entryData->_button.ToString()));
|
||||
_settingsButtons->SetSelection(firstFilter, _entryData->_button);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::SourceChanged(const SourceSelection &source)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_source = source;
|
||||
}
|
||||
|
||||
@@ -337,11 +332,13 @@ void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
|
||||
auto lock = LockContext();
|
||||
_entryData->_filter = filter;
|
||||
}
|
||||
|
||||
const auto filters =
|
||||
_entryData->_filter.GetFilters(_entryData->_source);
|
||||
OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0);
|
||||
_filterSettings->SetSource(firstFilter);
|
||||
PopulateSourceButtonSelection(_settingsButtons, firstFilter);
|
||||
_settingsButtons->SetSource(firstFilter);
|
||||
|
||||
SetWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
@@ -349,11 +346,7 @@ void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
|
||||
|
||||
void MacroActionFilterEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_action = static_cast<MacroActionFilter::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
@@ -391,11 +384,7 @@ void MacroActionFilterEdit::GetSettingsClicked()
|
||||
|
||||
void MacroActionFilterEdit::SettingsStringChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_settingsString =
|
||||
_settingsString->toPlainText().toStdString();
|
||||
|
||||
@@ -405,11 +394,7 @@ void MacroActionFilterEdit::SettingsStringChanged()
|
||||
|
||||
void MacroActionFilterEdit::SettingsInputMethodChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_settingsInputMethod =
|
||||
static_cast<MacroActionFilter::SettingsInputMethod>(
|
||||
_settingsInputMethods->itemData(idx).toInt());
|
||||
@@ -418,31 +403,19 @@ void MacroActionFilterEdit::SettingsInputMethodChanged(int idx)
|
||||
|
||||
void MacroActionFilterEdit::SelectionChanged(const TempVariableRef &var)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_tempVar = var;
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::SelectionChanged(const SourceSetting &setting)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_setting = setting;
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::ManualSettingsValueChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_manualSettingValue =
|
||||
_manualSettingValue->toPlainText().toStdString();
|
||||
|
||||
@@ -457,15 +430,10 @@ void MacroActionFilterEdit::RefreshVariableSourceSelectionValue()
|
||||
_filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0));
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::ButtonChanged(int idx)
|
||||
void MacroActionFilterEdit::ButtonChanged(const SourceSettingButton &button)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_button = qvariant_cast<SourceSettingButton>(
|
||||
_settingsButtons->itemData(idx));
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_button = button;
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::SetWidgetVisibility()
|
||||
|
||||
@@ -81,7 +81,7 @@ private slots:
|
||||
void SelectionChanged(const SourceSetting &);
|
||||
void ManualSettingsValueChanged();
|
||||
void RefreshVariableSourceSelectionValue();
|
||||
void ButtonChanged(int idx);
|
||||
void ButtonChanged(const SourceSettingButton &);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -100,7 +100,7 @@ private:
|
||||
SourceSettingSelection *_filterSettings;
|
||||
VariableTextEdit *_settingsString;
|
||||
QPushButton *_refreshSettingSelection;
|
||||
QComboBox *_settingsButtons;
|
||||
SourceSettingsButtonSelection *_settingsButtons;
|
||||
|
||||
std::shared_ptr<MacroActionFilter> _entryData;
|
||||
bool _loading = true;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "source-helpers.hpp"
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <QApplication>
|
||||
#include <QWindow>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -28,8 +30,34 @@ const static std::map<MacroActionProjector::Type, std::string> selectionTypes =
|
||||
"AdvSceneSwitcher.action.projector.type.multiview"},
|
||||
};
|
||||
|
||||
static void closeOBSProjectorWindows(const std::string expectedWindowTitle,
|
||||
const RegexConfig ®ex)
|
||||
{
|
||||
for (QWindow *widget : QApplication::allWindows()) {
|
||||
if (!widget->property("isOBSProjectorWindow").toBool()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const windowTitle = widget->title().toStdString();
|
||||
if (!regex.Enabled() && expectedWindowTitle != windowTitle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!regex.Matches(windowTitle, expectedWindowTitle)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
widget->close();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionProjector::PerformAction()
|
||||
{
|
||||
if (_action == Action::CLOSE) {
|
||||
closeOBSProjectorWindows(_projectorWindowName, _regex);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string name = "";
|
||||
const char *type = "";
|
||||
|
||||
@@ -78,10 +106,16 @@ bool MacroActionProjector::PerformAction()
|
||||
|
||||
void MacroActionProjector::LogAction() const
|
||||
{
|
||||
if (_action == Action::CLOSE) {
|
||||
ablog(LOG_INFO, "closing projector window \"%s\"",
|
||||
_projectorWindowName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = selectionTypes.find(_type);
|
||||
if (it != selectionTypes.end()) {
|
||||
ablog(LOG_INFO,
|
||||
"performed projector action \"%s\" with"
|
||||
"open projector \"%s\" with"
|
||||
"source \"%s\","
|
||||
"scene \"%s\","
|
||||
"monitor %d",
|
||||
@@ -96,24 +130,30 @@ void MacroActionProjector::LogAction() const
|
||||
bool MacroActionProjector::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
obs_data_set_int(obj, "monitor", _monitor);
|
||||
obs_data_set_string(obj, "monitorName", _monitorName.c_str());
|
||||
obs_data_set_bool(obj, "fullscreen", _fullscreen);
|
||||
_scene.Save(obj);
|
||||
_source.Save(obj);
|
||||
_projectorWindowName.Save(obj, "projectorWindowName");
|
||||
_regex.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionProjector::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||
_monitor = obs_data_get_int(obj, "monitor");
|
||||
_monitorName = obs_data_get_string(obj, "monitorName");
|
||||
_fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
_projectorWindowName.Load(obj, "projectorWindowName");
|
||||
_regex.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -131,6 +171,7 @@ void MacroActionProjector::ResolveVariablesToFixedValues()
|
||||
{
|
||||
_source.ResolveVariables();
|
||||
_scene.ResolveVariables();
|
||||
_projectorWindowName.ResolveVariables();
|
||||
}
|
||||
|
||||
void MacroActionProjector::SetMonitor(int idx)
|
||||
@@ -166,14 +207,22 @@ bool MacroActionProjector::MonitorSetupChanged() const
|
||||
QString::fromStdString(_monitorName);
|
||||
}
|
||||
|
||||
static inline void populateSelectionTypes(QComboBox *list)
|
||||
static void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : selectionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.projector.action.open"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.projector.action.close"));
|
||||
}
|
||||
|
||||
static void populateSelectionTypes(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : selectionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateWindowTypes(QComboBox *list)
|
||||
static void populateWindowTypes(QComboBox *list)
|
||||
{
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.action.projector.windowed"));
|
||||
@@ -184,14 +233,18 @@ static inline void populateWindowTypes(QComboBox *list)
|
||||
MacroActionProjectorEdit::MacroActionProjectorEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionProjector> entryData)
|
||||
: QWidget(parent),
|
||||
_windowTypes(new QComboBox()),
|
||||
_actions(new QComboBox()),
|
||||
_types(new QComboBox()),
|
||||
_windowTypes(new QComboBox()),
|
||||
_scenes(new SceneSelectionWidget(window(), true, false, true, true,
|
||||
true)),
|
||||
_sources(new SourceSelectionWidget(window(), QStringList(), true)),
|
||||
_monitorSelection(new QHBoxLayout()),
|
||||
_monitors(new QComboBox())
|
||||
_monitors(new QComboBox()),
|
||||
_projectorWindowName(new VariableLineEdit(this)),
|
||||
_regex(new RegexConfigWidget(this)),
|
||||
_layout(new QHBoxLayout(this))
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
populateWindowTypes(_windowTypes);
|
||||
populateSelectionTypes(_types);
|
||||
auto sources = GetSourceNames();
|
||||
@@ -201,6 +254,8 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
|
||||
_monitors->setPlaceholderText(
|
||||
obs_module_text("AdvSceneSwitcher.selectDisplay"));
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(WindowTypeChanged(int)));
|
||||
QWidget::connect(_types, SIGNAL(currentIndexChanged(int)), this,
|
||||
@@ -212,24 +267,15 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
|
||||
SLOT(SourceChanged(const SourceSelection &)));
|
||||
QWidget::connect(_monitors, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(MonitorChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{windowTypes}}", _windowTypes}, {"{{types}}", _types},
|
||||
{"{{scenes}}", _scenes}, {"{{sources}}", _sources},
|
||||
{"{{monitors}}", _monitors},
|
||||
};
|
||||
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.projector.entry.monitor"),
|
||||
_monitorSelection, widgetPlaceholders);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.projector.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
mainLayout->insertLayout(mainLayout->count() - 1, _monitorSelection);
|
||||
setLayout(mainLayout);
|
||||
QWidget::connect(_projectorWindowName, SIGNAL(editingFinished()), this,
|
||||
SLOT(ProjectorWindowNameChanged()));
|
||||
QWidget::connect(_regex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(RegexChanged(const RegexConfig &)));
|
||||
|
||||
_entryData = entryData;
|
||||
SetWidgetLayout();
|
||||
setLayout(_layout);
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
@@ -239,11 +285,15 @@ void MacroActionProjectorEdit::UpdateEntryData()
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_windowTypes->setCurrentIndex(_entryData->_fullscreen ? 1 : 0);
|
||||
_types->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_sources->SetSource(_entryData->_source);
|
||||
_monitors->setCurrentIndex(_entryData->GetMonitor());
|
||||
_projectorWindowName->setText(_entryData->_projectorWindowName);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -265,12 +315,26 @@ void MacroActionProjectorEdit::MonitorChanged(int value)
|
||||
_entryData->SetMonitor(value);
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::ProjectorWindowNameChanged()
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_projectorWindowName =
|
||||
_projectorWindowName->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::RegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_regex = regex;
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::WindowTypeChanged(int)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_fullscreen =
|
||||
_windowTypes->currentText() ==
|
||||
obs_module_text("AdvSceneSwitcher.action.projector.fullscreen");
|
||||
SetWidgetLayout();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -281,17 +345,69 @@ void MacroActionProjectorEdit::TypeChanged(int value)
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::ActionChanged(int idx)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_action = static_cast<MacroActionProjector::Action>(idx);
|
||||
SetWidgetLayout();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::SetWidgetLayout()
|
||||
{
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{windowTypes}}", _windowTypes},
|
||||
{"{{types}}", _types},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{monitors}}", _monitors},
|
||||
{"{{projectorWindowName}}", _projectorWindowName},
|
||||
{"{{regex}}", _regex},
|
||||
};
|
||||
|
||||
for (const auto &[_, widget] : widgetPlaceholders) {
|
||||
_layout->removeWidget(widget);
|
||||
}
|
||||
ClearLayout(_layout);
|
||||
|
||||
const char *layoutText;
|
||||
if (_entryData->_action == MacroActionProjector::Action::CLOSE) {
|
||||
layoutText = "AdvSceneSwitcher.action.projector.entry.close";
|
||||
} else if (_entryData->_fullscreen) {
|
||||
layoutText =
|
||||
"AdvSceneSwitcher.action.projector.entry.open.fullscreen";
|
||||
} else {
|
||||
layoutText =
|
||||
"AdvSceneSwitcher.action.projector.entry.open.windowed";
|
||||
}
|
||||
|
||||
PlaceWidgets(obs_module_text(layoutText), _layout, widgetPlaceholders);
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_scenes->setVisible(_entryData->_type ==
|
||||
MacroActionProjector::Type::SCENE);
|
||||
_sources->setVisible(_entryData->_type ==
|
||||
MacroActionProjector::Type::SOURCE);
|
||||
SetLayoutVisible(_monitorSelection, _entryData->_fullscreen);
|
||||
_projectorWindowName->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::CLOSE);
|
||||
_regex->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::CLOSE);
|
||||
_types->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::OPEN);
|
||||
_windowTypes->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::OPEN);
|
||||
_scenes->setVisible(
|
||||
_entryData->_action == MacroActionProjector::Action::OPEN &&
|
||||
_entryData->_type == MacroActionProjector::Type::SCENE);
|
||||
_sources->setVisible(
|
||||
_entryData->_action == MacroActionProjector::Action::OPEN &&
|
||||
_entryData->_type == MacroActionProjector::Type::SOURCE);
|
||||
_monitors->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::OPEN &&
|
||||
_entryData->_fullscreen);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "source-selection.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -19,6 +21,11 @@ public:
|
||||
void SetMonitor(int);
|
||||
int GetMonitor() const;
|
||||
|
||||
enum class Action {
|
||||
OPEN,
|
||||
CLOSE,
|
||||
};
|
||||
|
||||
enum class Type {
|
||||
SOURCE,
|
||||
SCENE,
|
||||
@@ -27,10 +34,13 @@ public:
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
Action _action = Action::OPEN;
|
||||
Type _type = Type::SCENE;
|
||||
SourceSelection _source;
|
||||
SceneSelection _scene;
|
||||
bool _fullscreen = true;
|
||||
StringVariable _projectorWindowName = "Windowed Projector";
|
||||
RegexConfig _regex = RegexConfig::PartialMatchRegexConfig(true);
|
||||
|
||||
private:
|
||||
bool MonitorSetupChanged() const;
|
||||
@@ -60,21 +70,28 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
void WindowTypeChanged(int value);
|
||||
void TypeChanged(int value);
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void MonitorChanged(int value);
|
||||
void ProjectorWindowNameChanged();
|
||||
void RegexChanged(const RegexConfig &);
|
||||
|
||||
private:
|
||||
void SetWidgetLayout();
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_windowTypes;
|
||||
QComboBox *_actions;
|
||||
QComboBox *_types;
|
||||
QComboBox *_windowTypes;
|
||||
SceneSelectionWidget *_scenes;
|
||||
SourceSelectionWidget *_sources;
|
||||
QHBoxLayout *_monitorSelection;
|
||||
QComboBox *_monitors;
|
||||
VariableLineEdit *_projectorWindowName;
|
||||
RegexConfigWidget *_regex;
|
||||
QHBoxLayout *_layout;
|
||||
|
||||
std::shared_ptr<MacroActionProjector> _entryData;
|
||||
bool _loading = true;
|
||||
|
||||
@@ -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();
|
||||
for (const auto &[value, name] : deinterlaceModes) {
|
||||
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) {
|
||||
for (const auto &[value, name] : modes) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(value));
|
||||
}
|
||||
@@ -295,7 +279,7 @@ MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
: QWidget(parent),
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||
_actions(new QComboBox()),
|
||||
_settingsButtons(new QComboBox()),
|
||||
_settingsButtons(new SourceSettingsButtonSelection(this)),
|
||||
_settingsLayout(new QHBoxLayout()),
|
||||
_settingsInputMethods(new QComboBox(this)),
|
||||
_manualSettingValue(new VariableTextEdit(this, 5, 1, 1)),
|
||||
@@ -315,16 +299,18 @@ MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
auto sources = GetSourceNames();
|
||||
sources.sort();
|
||||
_sources->SetSourceNameList(sources);
|
||||
populateDeinterlaceModeSelection(_deinterlaceMode);
|
||||
populateDeinterlaceFieldOrderSelection(_deinterlaceOrder);
|
||||
populateSettingsInputMethods(_settingsInputMethods);
|
||||
populateModeSelection(_deinterlaceMode, deinterlaceModes);
|
||||
populateModeSelection(_deinterlaceOrder, deinterlaceFieldOrders);
|
||||
populateModeSelection(_settingsInputMethods, inputMethods);
|
||||
_refreshSettingSelection->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.action.source.refresh.tooltip"));
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_settingsButtons, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(ButtonChanged(int)));
|
||||
QWidget::connect(_settingsButtons,
|
||||
SIGNAL(SelectionChanged(const SourceSettingButton &)),
|
||||
this,
|
||||
SLOT(ButtonChanged(const SourceSettingButton &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
||||
SLOT(SourceChanged(const SourceSelection &)));
|
||||
@@ -394,14 +380,11 @@ void MacroActionSourceEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
PopulateSourceButtonSelection(_settingsButtons,
|
||||
_entryData->_source.GetSource());
|
||||
const auto weakSource = _entryData->_source.GetSource();
|
||||
_settingsButtons->SetSelection(weakSource, _entryData->_button);
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_sources->SetSource(_entryData->_source);
|
||||
_sourceSettings->SetSource(_entryData->_source.GetSource());
|
||||
_sourceSettings->SetSetting(_entryData->_setting);
|
||||
_settingsButtons->setCurrentText(
|
||||
QString::fromStdString(_entryData->_button.ToString()));
|
||||
_sourceSettings->SetSelection(weakSource, _entryData->_setting);
|
||||
_settingsString->setPlainText(_entryData->_settingsString);
|
||||
_deinterlaceMode->setCurrentIndex(_deinterlaceMode->findData(
|
||||
static_cast<int>(_entryData->_deinterlaceMode)));
|
||||
@@ -425,9 +408,10 @@ void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
|
||||
auto lock = LockContext();
|
||||
_entryData->_source = source;
|
||||
}
|
||||
PopulateSourceButtonSelection(_settingsButtons,
|
||||
_entryData->_source.GetSource());
|
||||
_sourceSettings->SetSource(_entryData->_source.GetSource());
|
||||
|
||||
const auto weakSource = _entryData->_source.GetSource();
|
||||
_sourceSettings->SetSource(weakSource);
|
||||
_settingsButtons->SetSource(weakSource);
|
||||
SetWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
@@ -435,24 +419,15 @@ void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
|
||||
|
||||
void MacroActionSourceEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_action = static_cast<MacroActionSource::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::ButtonChanged(int idx)
|
||||
void MacroActionSourceEdit::ButtonChanged(const SourceSettingButton &button)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_button = qvariant_cast<SourceSettingButton>(
|
||||
_settingsButtons->itemData(idx));
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_button = button;
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::GetSettingsClicked()
|
||||
@@ -479,11 +454,7 @@ void MacroActionSourceEdit::GetSettingsClicked()
|
||||
|
||||
void MacroActionSourceEdit::SettingsStringChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_settingsString =
|
||||
_settingsString->toPlainText().toStdString();
|
||||
|
||||
@@ -493,22 +464,14 @@ void MacroActionSourceEdit::SettingsStringChanged()
|
||||
|
||||
void MacroActionSourceEdit::DeinterlaceModeChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_deinterlaceMode = static_cast<obs_deinterlace_mode>(
|
||||
_deinterlaceMode->itemData(idx).toInt());
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::DeinterlaceOrderChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_deinterlaceOrder =
|
||||
static_cast<obs_deinterlace_field_order>(
|
||||
_deinterlaceOrder->itemData(idx).toInt());
|
||||
@@ -516,21 +479,13 @@ void MacroActionSourceEdit::DeinterlaceOrderChanged(int idx)
|
||||
|
||||
void MacroActionSourceEdit::SelectionChanged(const TempVariableRef &var)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_tempVar = var;
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::SettingsInputMethodChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_settingsInputMethod =
|
||||
static_cast<MacroActionSource::SettingsInputMethod>(
|
||||
_settingsInputMethods->itemData(idx).toInt());
|
||||
@@ -539,21 +494,13 @@ void MacroActionSourceEdit::SettingsInputMethodChanged(int idx)
|
||||
|
||||
void MacroActionSourceEdit::SelectionChanged(const SourceSetting &setting)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_setting = setting;
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::ManualSettingsValueChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_manualSettingValue =
|
||||
_manualSettingValue->toPlainText().toStdString();
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
private slots:
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void ActionChanged(int value);
|
||||
void ButtonChanged(int idx);
|
||||
void ButtonChanged(const SourceSettingButton &);
|
||||
void GetSettingsClicked();
|
||||
void SettingsStringChanged();
|
||||
void DeinterlaceModeChanged(int);
|
||||
@@ -99,7 +99,7 @@ private:
|
||||
|
||||
SourceSelectionWidget *_sources;
|
||||
QComboBox *_actions;
|
||||
QComboBox *_settingsButtons;
|
||||
SourceSettingsButtonSelection *_settingsButtons;
|
||||
QHBoxLayout *_settingsLayout;
|
||||
QComboBox *_settingsInputMethods;
|
||||
VariableTextEdit *_manualSettingValue;
|
||||
|
||||
@@ -257,7 +257,9 @@ void MacroActionStreamEdit::StringValueChanged()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,27 +123,23 @@ MacroActionSystrayEdit::MacroActionSystrayEdit(
|
||||
|
||||
void MacroActionSystrayEdit::TitleChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_title = _title->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionSystrayEdit::IconPathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_iconPath = text.toStdString();
|
||||
}
|
||||
|
||||
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();
|
||||
#endif
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -176,15 +176,21 @@ bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
|
||||
|
||||
bool MacroConditionDate::CheckCondition()
|
||||
{
|
||||
auto m = GetMacro();
|
||||
if (!m) {
|
||||
auto macro = GetMacro();
|
||||
if (!macro) {
|
||||
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 =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
timePassed);
|
||||
MacroWasCheckedSinceLastStart(macro)
|
||||
? std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
timePassed)
|
||||
: std::chrono::milliseconds(0);
|
||||
|
||||
if (_dayOfWeekCheck) {
|
||||
return CheckDayOfWeek(msSinceLastCheck.count());
|
||||
|
||||
@@ -420,8 +420,9 @@ void MacroConditionFilterEdit::UpdateEntryData()
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
const auto filters =
|
||||
_entryData->_filter.GetFilters(_entryData->_source);
|
||||
_settingSelection->SetSource(filters.empty() ? nullptr : filters.at(0));
|
||||
_settingSelection->SetSetting(_entryData->_setting);
|
||||
_settingSelection->SetSelection(filters.empty() ? nullptr
|
||||
: filters.at(0),
|
||||
_entryData->_setting);
|
||||
SetWidgetVisibility();
|
||||
|
||||
adjustSize();
|
||||
|
||||
197
plugins/base/macro-condition-game-capture.cpp
Normal file
197
plugins/base/macro-condition-game-capture.cpp
Normal 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
|
||||
74
plugins/base/macro-condition-game-capture.hpp
Normal file
74
plugins/base/macro-condition-game-capture.hpp
Normal 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
|
||||
@@ -548,14 +548,14 @@ void MacroConditionMedia::MediaNext(void *data, calldata_t *)
|
||||
|
||||
void MacroConditionMedia::SetSourceType(SourceType t)
|
||||
{
|
||||
SetupTempVars();
|
||||
_sourceType = t;
|
||||
SetupTempVars();
|
||||
}
|
||||
|
||||
void MacroConditionMedia::SetCheckType(CheckType t)
|
||||
{
|
||||
SetupTempVars();
|
||||
_checkType = t;
|
||||
SetupTempVars();
|
||||
}
|
||||
|
||||
static void populateSateSelection(QComboBox *list, bool addLegacyEntries)
|
||||
|
||||
94
plugins/base/macro-condition-screenshot.cpp
Normal file
94
plugins/base/macro-condition-screenshot.cpp
Normal 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
|
||||
55
plugins/base/macro-condition-screenshot.hpp
Normal file
55
plugins/base/macro-condition-screenshot.hpp
Normal 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
|
||||
@@ -483,8 +483,8 @@ void MacroConditionSourceEdit::UpdateEntryData()
|
||||
static_cast<int>(_entryData->GetCondition()));
|
||||
_settings->setPlainText(_entryData->_settings);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
_settingSelection->SetSource(_entryData->_source.GetSource());
|
||||
_settingSelection->SetSetting(_entryData->_setting);
|
||||
_settingSelection->SetSelection(_entryData->_source.GetSource(),
|
||||
_entryData->_setting);
|
||||
_size->SetValue(_entryData->_size);
|
||||
_sizeCompareMethods->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_comparision));
|
||||
|
||||
@@ -111,27 +111,42 @@ bool MacroConditionTransition::CheckCondition()
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MacroConditionTransition::ConnectToTransitionSignals()
|
||||
void advss::MacroConditionTransition::AddTransitionSignals(
|
||||
obs_source_t *transition)
|
||||
{
|
||||
const bool useFrontendTransitionSelection =
|
||||
_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);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(transition);
|
||||
_signals.emplace_back(sh, "transition_start", TransitionStarted, this);
|
||||
_signals.emplace_back(sh, "transition_stop", TransitionEnded, this);
|
||||
_signals.emplace_back(sh, "transition_video_stop", TransitionVideoEnded,
|
||||
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)
|
||||
{
|
||||
auto *condition = static_cast<MacroConditionTransition *>(data);
|
||||
|
||||
@@ -42,6 +42,7 @@ private:
|
||||
static void TransitionEnded(void *data, calldata_t *);
|
||||
static void TransitionVideoEnded(void *data, calldata_t *);
|
||||
static void HandleFrontendEvent(enum obs_frontend_event event, void *);
|
||||
void AddTransitionSignals(obs_source_t *);
|
||||
|
||||
std::vector<OBSSignal> _signals;
|
||||
|
||||
|
||||
@@ -332,7 +332,8 @@ WSConnectionSettingsDialog::WSConnectionSettingsDialog(
|
||||
: ItemSettingsDialog(settings, connections,
|
||||
"AdvSceneSwitcher.connection.select",
|
||||
"AdvSceneSwitcher.connection.add",
|
||||
"AdvSceneSwitcher.item.nameNotAvailable", parent),
|
||||
"AdvSceneSwitcher.item.nameNotAvailable", true,
|
||||
parent),
|
||||
_useCustomURI(new QCheckBox()),
|
||||
_customUri(new QLineEdit()),
|
||||
_address(new QLineEdit()),
|
||||
@@ -502,7 +503,9 @@ void WSConnectionSettingsDialog::SetStatus()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -698,7 +698,10 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
||||
_nameConflictIndex->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
|
||||
_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->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneItemSelection.configure"));
|
||||
|
||||
@@ -29,20 +29,37 @@ std::string SourceSettingButton::ToString() const
|
||||
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);
|
||||
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;
|
||||
}
|
||||
SourceSettingButton button = {obs_property_name(it),
|
||||
obs_property_description(it)};
|
||||
buttons.emplace_back(button);
|
||||
} 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);
|
||||
return buttons;
|
||||
}
|
||||
@@ -59,19 +76,46 @@ void PressSourceButton(const SourceSettingButton &button, obs_source_t *source)
|
||||
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);
|
||||
if (buttons.empty()) {
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.noSettingsButtons"));
|
||||
addItem(obs_module_text("AdvSceneSwitcher.noSettingsButtons"));
|
||||
}
|
||||
|
||||
for (const auto &button : buttons) {
|
||||
QVariant value;
|
||||
value.setValue(button);
|
||||
list->addItem(QString::fromStdString(button.ToString()), value);
|
||||
addItem(QString::fromStdString(button.ToString()), value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#pragma once
|
||||
#include "filter-combo-box.hpp"
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QComboBox>
|
||||
#include <qmetatype.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class SourceSelection;
|
||||
class FilterSelection;
|
||||
|
||||
struct SourceSettingButton {
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
@@ -18,7 +22,23 @@ struct SourceSettingButton {
|
||||
|
||||
std::vector<SourceSettingButton> GetSourceButtons(OBSWeakSource 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
|
||||
|
||||
|
||||
@@ -249,14 +249,21 @@ SourceSettingSelection::SourceSettingSelection(QWidget *parent)
|
||||
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);
|
||||
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;
|
||||
variant.setValue(setting);
|
||||
_settings->setCurrentIndex(_settings->findData(variant));
|
||||
@@ -281,6 +288,7 @@ void SourceSettingSelection::SelectionIdxChanged(int idx)
|
||||
|
||||
void SourceSettingSelection::Populate(const OBSWeakSource &source)
|
||||
{
|
||||
_settings->clear();
|
||||
OBSSourceAutoRelease s = obs_weak_source_get_source(source);
|
||||
auto settings = GetSoruceSettings(s);
|
||||
for (const auto &setting : settings) {
|
||||
|
||||
@@ -40,8 +40,9 @@ class SourceSettingSelection : public QWidget {
|
||||
|
||||
public:
|
||||
SourceSettingSelection(QWidget *parent = nullptr);
|
||||
void SetSource(const OBSWeakSource &);
|
||||
void SetSetting(const SourceSetting &);
|
||||
void SetSource(const OBSWeakSource &,
|
||||
bool restorePreviousSelection = true);
|
||||
void SetSelection(const OBSWeakSource &source, const SourceSetting &);
|
||||
|
||||
private slots:
|
||||
void SelectionIdxChanged(int);
|
||||
|
||||
@@ -40,7 +40,7 @@ void WSConnectionsTable::Add()
|
||||
{
|
||||
auto newConnection = std::make_shared<WSConnection>();
|
||||
auto accepted = WSConnectionSettingsDialog::AskForSettings(
|
||||
this, *newConnection);
|
||||
GetSettingsWindow(), *newConnection);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
@@ -143,7 +143,7 @@ static void openSettingsDialog()
|
||||
|
||||
auto oldName = connection->Name();
|
||||
bool accepted = WSConnectionSettingsDialog::AskForSettings(
|
||||
tabWidget->Table(), *connection.get());
|
||||
GetSettingsWindow(), *connection.get());
|
||||
if (accepted && oldName != connection->Name()) {
|
||||
ConnectionSelectionSignalManager::Instance()->Rename(
|
||||
QString::fromStdString(oldName),
|
||||
|
||||
@@ -139,7 +139,7 @@ void TwitchCategorySelection::SelectionChanged(int index)
|
||||
{
|
||||
TwitchCategory category{itemData(index).toInt(),
|
||||
currentText().toStdString()};
|
||||
emit CategoreyChanged(category);
|
||||
emit CategoryChanged(category);
|
||||
}
|
||||
|
||||
std::map<QString, int> CategoryGrabber::_categoryMap = {};
|
||||
@@ -193,7 +193,7 @@ void CategoryGrabber::Search(const std::string &)
|
||||
httplib::Params params = {{"first", "100"},
|
||||
{"after", cursor},
|
||||
{"query", _searchString}};
|
||||
auto response = SendGetRequest(*_token, uri, path, params);
|
||||
auto response = SendGetRequest(*_token, uri, path, params, true);
|
||||
|
||||
while (response.status == 200 && !_stop) {
|
||||
cursor = ParseReply(response.data);
|
||||
@@ -203,7 +203,7 @@ void CategoryGrabber::Search(const std::string &)
|
||||
params = {{"first", "100"},
|
||||
{"after", cursor},
|
||||
{"query", _searchString}};
|
||||
response = SendGetRequest(*_token, uri, path, params);
|
||||
response = SendGetRequest(*_token, uri, path, params, true);
|
||||
emit CategoryCountUpdated(_categoryMap.size() - startCount);
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,7 @@ void CategoryGrabber::GetAll()
|
||||
static std::string 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) {
|
||||
cursor = ParseReply(response.data);
|
||||
@@ -225,7 +225,7 @@ void CategoryGrabber::GetAll()
|
||||
break; // End of category list
|
||||
}
|
||||
params = {{"first", "100"}, {"after", cursor}};
|
||||
response = SendGetRequest(*_token, uri, path, params);
|
||||
response = SendGetRequest(*_token, uri, path, params, true);
|
||||
emit CategoryCountUpdated(_categoryMap.size());
|
||||
}
|
||||
}
|
||||
@@ -364,8 +364,8 @@ TwitchCategoryWidget::TwitchCategoryWidget(QWidget *parent)
|
||||
_manualSearch(new TwitchCategorySearchButton(this))
|
||||
{
|
||||
QWidget::connect(_selection,
|
||||
SIGNAL(CategoreyChanged(const TwitchCategory &)), this,
|
||||
SIGNAL(CategoreyChanged(const TwitchCategory &)));
|
||||
SIGNAL(CategoryChanged(const TwitchCategory &)), this,
|
||||
SIGNAL(CategoryChanged(const TwitchCategory &)));
|
||||
|
||||
auto layout = new QHBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
@@ -78,7 +78,7 @@ private slots:
|
||||
void UpdateCategoryList();
|
||||
|
||||
signals:
|
||||
void CategoreyChanged(const TwitchCategory &);
|
||||
void CategoryChanged(const TwitchCategory &);
|
||||
|
||||
protected:
|
||||
void showPopup() override;
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
void SetToken(const std::weak_ptr<TwitchToken> &);
|
||||
|
||||
signals:
|
||||
void CategoreyChanged(const TwitchCategory &);
|
||||
void CategoryChanged(const TwitchCategory &);
|
||||
|
||||
private:
|
||||
TwitchCategorySelection *_selection;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "channel-selection.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "twitch-helpers.hpp"
|
||||
#include "token.hpp"
|
||||
|
||||
@@ -36,7 +37,7 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const
|
||||
}
|
||||
|
||||
auto res = SendGetRequest(token, "https://api.twitch.tv",
|
||||
"/helix/users", {{"login", _name}});
|
||||
"/helix/users", {{"login", _name}}, true);
|
||||
|
||||
if (res.status == 400) {
|
||||
userIDCache[_name] = "invalid";
|
||||
|
||||
@@ -196,12 +196,12 @@ std::string EventSub::AddEventSubscribtion(std::shared_ptr<TwitchToken> token,
|
||||
return "";
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(eventSub->_subscriptionMtx);
|
||||
std::unique_lock<std::mutex> lock(eventSub->_subscriptionMtx);
|
||||
if (!eventSub->_connected) {
|
||||
std::thread t([eventSub]() { eventSub->Connect(); });
|
||||
t.detach();
|
||||
vblog(LOG_INFO, "Twitch EventSub connect started for %s",
|
||||
token->GetName().c_str());
|
||||
lock.unlock();
|
||||
eventSub->Connect();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <log-helper.hpp>
|
||||
#include <layout-helpers.hpp>
|
||||
#include <ui-helpers.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -21,6 +22,18 @@ void MacroActionTwitch::ResolveVariablesToFixedValues()
|
||||
_announcementMessage.ResolveVariables();
|
||||
_channel.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)
|
||||
@@ -58,7 +71,11 @@ const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
|
||||
{MacroActionTwitch::Action::CHAT_EMOTE_ONLY_DISABLE,
|
||||
"AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable"},
|
||||
{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>
|
||||
@@ -264,6 +281,228 @@ void MacroActionTwitch::SendChatMessage(
|
||||
_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()
|
||||
{
|
||||
auto token = _token.lock();
|
||||
@@ -298,9 +537,16 @@ bool MacroActionTwitch::PerformAction()
|
||||
break;
|
||||
case Action::CHAT_EMOTE_ONLY_DISABLE:
|
||||
SetChatEmoteOnlyMode(token, false);
|
||||
break;
|
||||
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
|
||||
SendChatMessage(token);
|
||||
break;
|
||||
case MacroActionTwitch::Action::USER_GET_INFO:
|
||||
GetUserInfo(token);
|
||||
break;
|
||||
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
|
||||
GetRewardInfo(token);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -338,6 +584,15 @@ bool MacroActionTwitch::Save(obs_data_t *obj) const
|
||||
static_cast<int>(_announcementColor));
|
||||
_channel.Save(obj);
|
||||
_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;
|
||||
}
|
||||
@@ -357,6 +612,16 @@ bool MacroActionTwitch::Load(obs_data_t *obj)
|
||||
obs_data_get_int(obj, "announcementColor"));
|
||||
_channel.Load(obj);
|
||||
_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")));
|
||||
|
||||
return true;
|
||||
@@ -366,117 +631,139 @@ void MacroActionTwitch::SetAction(Action action)
|
||||
{
|
||||
_action = action;
|
||||
ResetChatConnection();
|
||||
SetupTempVars();
|
||||
}
|
||||
|
||||
bool MacroActionTwitch::ActionIsSupportedByToken()
|
||||
{
|
||||
static const std::unordered_map<Action, TokenOption> requiredOption = {
|
||||
{Action::CHANNEL_INFO_TITLE_SET, {"channel:manage:broadcast"}},
|
||||
{Action::CHANNEL_INFO_CATEGORY_SET,
|
||||
{"channel:manage:broadcast"}},
|
||||
{Action::CHANNEL_INFO_LANGUAGE_SET,
|
||||
{"channel:manage:broadcast"}},
|
||||
{Action::CHANNEL_INFO_DELAY_SET, {"channel:manage:broadcast"}},
|
||||
{Action::CHANNEL_INFO_BRANDED_CONTENT_ENABLE,
|
||||
{"channel:manage:broadcast"}},
|
||||
{Action::CHANNEL_INFO_BRANDED_CONTENT_DISABLE,
|
||||
{"channel:manage:broadcast"}},
|
||||
{Action::RAID_START, {"channel:manage:raids"}},
|
||||
{Action::RAID_END, {"channel:manage:raids"}},
|
||||
{Action::SHOUTOUT_SEND, {"moderator:manage:shoutouts"}},
|
||||
{Action::POLL_END, {"channel:manage:polls"}},
|
||||
{Action::PREDICTION_END, {"channel:manage:predictions"}},
|
||||
{Action::SHIELD_MODE_START, {"moderator:manage:shield_mode"}},
|
||||
{Action::SHIELD_MODE_END, {"moderator:manage:shield_mode"}},
|
||||
{Action::POINTS_REWARD_ENABLE, {"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_DISABLE, {"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_PAUSE, {"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_UNPAUSE, {"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_TITLE_SET,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_PROMPT_SET,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_COST_SET,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_USER_INPUT_REQUIRE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_USER_INPUT_UNREQUIRE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_COOLDOWN_ENABLE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_COOLDOWN_DISABLE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_QUEUE_SKIP_ENABLE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_QUEUE_SKIP_DISABLE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_MAX_PER_STREAM_ENABLE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_MAX_PER_STREAM_DISABLE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_MAX_PER_USER_ENABLE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_MAX_PER_USER_DISABLE,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_DELETE, {"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_REDEMPTION_CANCEL,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::POINTS_REWARD_REDEMPTION_FULFILL,
|
||||
{"channel:manage:redemptions"}},
|
||||
{Action::USER_BAN, {"moderator:manage:banned_users"}},
|
||||
{Action::USER_UNBAN, {"moderator:manage:banned_users"}},
|
||||
{Action::USER_BLOCK, {"user:manage:blocked_users"}},
|
||||
{Action::USER_UNBLOCK, {"user:manage:blocked_users"}},
|
||||
{Action::USER_MODERATOR_ADD, {"channel:manage:moderators"}},
|
||||
{Action::USER_MODERATOR_DELETE, {"channel:manage:moderators"}},
|
||||
{Action::USER_VIP_ADD, {"channel:manage:vips"}},
|
||||
{Action::USER_VIP_DELETE, {"channel:manage:vips"}},
|
||||
{Action::COMMERCIAL_START, {"channel:edit:commercial"}},
|
||||
{Action::COMMERCIAL_SNOOZE, {"channel:manage:ads"}},
|
||||
{Action::MARKER_CREATE, {"channel:manage:broadcast"}},
|
||||
{Action::CLIP_CREATE, {"clips:edit"}},
|
||||
{Action::CHAT_ANNOUNCEMENT_SEND,
|
||||
{"moderator:manage:announcements"}},
|
||||
{Action::CHAT_EMOTE_ONLY_ENABLE,
|
||||
{"moderator:manage:chat_settings"}},
|
||||
{Action::CHAT_EMOTE_ONLY_DISABLE,
|
||||
{"moderator:manage:chat_settings"}},
|
||||
{Action::CHAT_FOLLOWER_ONLY_ENABLE,
|
||||
{"moderator:manage:chat_settings"}},
|
||||
{Action::CHAT_FOLLOWER_ONLY_DISABLE,
|
||||
{"moderator:manage:chat_settings"}},
|
||||
{Action::CHAT_SUBSCRIBER_ONLY_ENABLE,
|
||||
{"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"}},
|
||||
};
|
||||
static const std::unordered_map<Action, std::vector<TokenOption>>
|
||||
requiredOption = {
|
||||
{Action::CHANNEL_INFO_TITLE_SET,
|
||||
{{"channel:manage:broadcast"}}},
|
||||
{Action::CHANNEL_INFO_CATEGORY_SET,
|
||||
{{"channel:manage:broadcast"}}},
|
||||
{Action::CHANNEL_INFO_LANGUAGE_SET,
|
||||
{{"channel:manage:broadcast"}}},
|
||||
{Action::CHANNEL_INFO_DELAY_SET,
|
||||
{{"channel:manage:broadcast"}}},
|
||||
{Action::CHANNEL_INFO_BRANDED_CONTENT_ENABLE,
|
||||
{{"channel:manage:broadcast"}}},
|
||||
{Action::CHANNEL_INFO_BRANDED_CONTENT_DISABLE,
|
||||
{{"channel:manage:broadcast"}}},
|
||||
{Action::RAID_START, {{"channel:manage:raids"}}},
|
||||
{Action::RAID_END, {{"channel:manage:raids"}}},
|
||||
{Action::SHOUTOUT_SEND,
|
||||
{{"moderator:manage:shoutouts"}}},
|
||||
{Action::POLL_END, {{"channel:manage:polls"}}},
|
||||
{Action::PREDICTION_END,
|
||||
{{"channel:manage:predictions"}}},
|
||||
{Action::SHIELD_MODE_START,
|
||||
{{"moderator:manage:shield_mode"}}},
|
||||
{Action::SHIELD_MODE_END,
|
||||
{{"moderator:manage:shield_mode"}}},
|
||||
{Action::POINTS_REWARD_ENABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_DISABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_PAUSE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_UNPAUSE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_TITLE_SET,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_PROMPT_SET,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_COST_SET,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_USER_INPUT_REQUIRE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_USER_INPUT_UNREQUIRE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_COOLDOWN_ENABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_COOLDOWN_DISABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_QUEUE_SKIP_ENABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_QUEUE_SKIP_DISABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_MAX_PER_STREAM_ENABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_MAX_PER_STREAM_DISABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_MAX_PER_USER_ENABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_MAX_PER_USER_DISABLE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_DELETE,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_REDEMPTION_CANCEL,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::POINTS_REWARD_REDEMPTION_FULFILL,
|
||||
{{"channel:manage:redemptions"}}},
|
||||
{Action::USER_BAN, {{"moderator:manage:banned_users"}}},
|
||||
{Action::USER_UNBAN,
|
||||
{{"moderator:manage:banned_users"}}},
|
||||
{Action::USER_BLOCK, {{"user:manage:blocked_users"}}},
|
||||
{Action::USER_UNBLOCK, {{"user:manage:blocked_users"}}},
|
||||
{Action::USER_MODERATOR_ADD,
|
||||
{{"channel:manage:moderators"}}},
|
||||
{Action::USER_MODERATOR_DELETE,
|
||||
{{"channel:manage:moderators"}}},
|
||||
{Action::USER_VIP_ADD, {{"channel:manage:vips"}}},
|
||||
{Action::USER_VIP_DELETE, {{"channel:manage:vips"}}},
|
||||
{Action::COMMERCIAL_START,
|
||||
{{"channel:edit:commercial"}}},
|
||||
{Action::COMMERCIAL_SNOOZE, {{"channel:manage:ads"}}},
|
||||
{Action::MARKER_CREATE, {{"channel:manage:broadcast"}}},
|
||||
{Action::CLIP_CREATE, {{"clips:edit"}}},
|
||||
{Action::CHAT_ANNOUNCEMENT_SEND,
|
||||
{{"moderator:manage:announcements"}}},
|
||||
{Action::CHAT_EMOTE_ONLY_ENABLE,
|
||||
{{"moderator:manage:chat_settings"}}},
|
||||
{Action::CHAT_EMOTE_ONLY_DISABLE,
|
||||
{{"moderator:manage:chat_settings"}}},
|
||||
{Action::CHAT_FOLLOWER_ONLY_ENABLE,
|
||||
{{"moderator:manage:chat_settings"}}},
|
||||
{Action::CHAT_FOLLOWER_ONLY_DISABLE,
|
||||
{{"moderator:manage:chat_settings"}}},
|
||||
{Action::CHAT_SUBSCRIBER_ONLY_ENABLE,
|
||||
{{"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();
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto option = requiredOption.find(_action);
|
||||
assert(option != requiredOption.end());
|
||||
if (option == requiredOption.end()) {
|
||||
auto it = requiredOption.find(_action);
|
||||
assert(it != requiredOption.end());
|
||||
if (it == requiredOption.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return token->OptionIsEnabled(option->second);
|
||||
const auto &[_, options] = *it;
|
||||
|
||||
return token->AnyOptionIsEnabled(options);
|
||||
}
|
||||
|
||||
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(
|
||||
QWidget *parent, std::shared_ptr<MacroActionTwitch> entryData)
|
||||
: QWidget(parent),
|
||||
@@ -523,7 +822,13 @@ MacroActionTwitchEdit::MacroActionTwitchEdit(
|
||||
_announcementMessage(new VariableTextEdit(this)),
|
||||
_announcementColor(new QComboBox(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, false)),
|
||||
_rewardVariable(new VariableSelection(this)),
|
||||
_toggleRewardSelection(new QPushButton())
|
||||
{
|
||||
SetWidgetProperties();
|
||||
SetWidgetSignalConnections();
|
||||
@@ -566,14 +871,11 @@ void MacroActionTwitchEdit::ActionChanged(int idx)
|
||||
|
||||
void MacroActionTwitchEdit::TwitchTokenChanged(const QString &token)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_token = GetWeakTwitchTokenByQString(token);
|
||||
_category->SetToken(_entryData->_token);
|
||||
_channel->SetToken(_entryData->_token);
|
||||
_pointsReward->SetToken(_entryData->_token);
|
||||
_entryData->ResetChatConnection();
|
||||
|
||||
SetWidgetVisibility();
|
||||
@@ -618,62 +920,38 @@ void MacroActionTwitchEdit::CheckToken()
|
||||
|
||||
void MacroActionTwitchEdit::StreamTitleChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_streamTitle = _streamTitle->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::CategoreyChanged(const TwitchCategory &category)
|
||||
void MacroActionTwitchEdit::CategoryChanged(const TwitchCategory &category)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_category = category;
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::MarkerDescriptionChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_markerDescription =
|
||||
_markerDescription->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::ClipHasDelayChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_clipHasDelay = state;
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::DurationChanged(const Duration &duration)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_duration = duration;
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::AnnouncementMessageChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_announcementMessage =
|
||||
_announcementMessage->toPlainText().toStdString();
|
||||
|
||||
@@ -683,11 +961,7 @@ void MacroActionTwitchEdit::AnnouncementMessageChanged()
|
||||
|
||||
void MacroActionTwitchEdit::AnnouncementColorChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_announcementColor =
|
||||
static_cast<MacroActionTwitch::AnnouncementColor>(index);
|
||||
}
|
||||
@@ -708,6 +982,18 @@ void MacroActionTwitchEdit::SetWidgetProperties()
|
||||
|
||||
populateActionSelection(_actions);
|
||||
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()
|
||||
@@ -721,8 +1007,8 @@ void MacroActionTwitchEdit::SetWidgetSignalConnections()
|
||||
QWidget::connect(_streamTitle, SIGNAL(editingFinished()), this,
|
||||
SLOT(StreamTitleChanged()));
|
||||
QWidget::connect(_category,
|
||||
SIGNAL(CategoreyChanged(const TwitchCategory &)), this,
|
||||
SLOT(CategoreyChanged(const TwitchCategory &)));
|
||||
SIGNAL(CategoryChanged(const TwitchCategory &)), this,
|
||||
SLOT(CategoryChanged(const TwitchCategory &)));
|
||||
QWidget::connect(_markerDescription, SIGNAL(editingFinished()), this,
|
||||
SLOT(MarkerDescriptionChanged()));
|
||||
QObject::connect(_clipHasDelay, SIGNAL(stateChanged(int)), this,
|
||||
@@ -738,6 +1024,23 @@ void MacroActionTwitchEdit::SetWidgetSignalConnections()
|
||||
SLOT(ChannelChanged(const TwitchChannel &)));
|
||||
QWidget::connect(_chatMessage, SIGNAL(textChanged()), this,
|
||||
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()
|
||||
@@ -754,7 +1057,9 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
|
||||
_entryData->GetAction() ==
|
||||
MacroActionTwitch::Action::RAID_END ||
|
||||
_entryData->GetAction() ==
|
||||
MacroActionTwitch::Action::SEND_CHAT_MESSAGE);
|
||||
MacroActionTwitch::Action::SEND_CHAT_MESSAGE ||
|
||||
_entryData->GetAction() ==
|
||||
MacroActionTwitch::Action::POINTS_REWARD_GET_INFO);
|
||||
_duration->setVisible(_entryData->GetAction() ==
|
||||
MacroActionTwitch::Action::COMMERCIAL_START);
|
||||
_markerDescription->setVisible(
|
||||
@@ -770,6 +1075,29 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
|
||||
MacroActionTwitch::Action::CHAT_ANNOUNCEMENT_SEND);
|
||||
_chatMessage->setVisible(_entryData->GetAction() ==
|
||||
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() ==
|
||||
MacroActionTwitch::Action::CHANNEL_INFO_TITLE_SET ||
|
||||
@@ -788,46 +1116,104 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
|
||||
|
||||
void MacroActionTwitchEdit::ChannelChanged(const TwitchChannel &channel)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_channel = channel;
|
||||
_pointsReward->SetChannel(channel);
|
||||
_entryData->ResetChatConnection();
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::ChatMessageChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_chatMessage = _chatMessage->toPlainText().toStdString();
|
||||
|
||||
adjustSize();
|
||||
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()
|
||||
{
|
||||
const std::vector<QWidget *> widgets{
|
||||
_tokens, _actions, _streamTitle,
|
||||
_category, _markerDescription, _clipHasDelay,
|
||||
_duration, _announcementColor, _channel};
|
||||
const std::vector<QWidget *> widgets{_tokens,
|
||||
_actions,
|
||||
_streamTitle,
|
||||
_category,
|
||||
_markerDescription,
|
||||
_clipHasDelay,
|
||||
_duration,
|
||||
_announcementColor,
|
||||
_channel,
|
||||
_userInfoQueryType,
|
||||
_userLogin,
|
||||
_userId,
|
||||
_pointsReward,
|
||||
_rewardVariable,
|
||||
_toggleRewardSelection};
|
||||
for (auto widget : widgets) {
|
||||
_layout->removeWidget(widget);
|
||||
}
|
||||
ClearLayout(_layout);
|
||||
|
||||
auto layoutText =
|
||||
_entryData->GetAction() ==
|
||||
MacroActionTwitch::Action::SEND_CHAT_MESSAGE
|
||||
? obs_module_text(
|
||||
"AdvSceneSwitcher.action.twitch.entry.chat")
|
||||
: obs_module_text(
|
||||
"AdvSceneSwitcher.action.twitch.entry.default");
|
||||
const char *layoutText;
|
||||
switch (_entryData->GetAction()) {
|
||||
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
|
||||
layoutText = obs_module_text(
|
||||
"AdvSceneSwitcher.action.twitch.entry.chat");
|
||||
break;
|
||||
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,
|
||||
{{"{{account}}", _tokens},
|
||||
{"{{actions}}", _actions},
|
||||
@@ -837,7 +1223,14 @@ void MacroActionTwitchEdit::SetWidgetLayout()
|
||||
{"{{clipHasDelay}}", _clipHasDelay},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{announcementColor}}", _announcementColor},
|
||||
{"{{channel}}", _channel}});
|
||||
{"{{channel}}", _channel},
|
||||
{"{{userInfoQueryType}}", _userInfoQueryType},
|
||||
{"{{userLogin}}", _userLogin},
|
||||
{"{{userId}}", _userId},
|
||||
{"{{pointsReward}}", _pointsReward},
|
||||
{"{{rewardVariable}}", _rewardVariable},
|
||||
{"{{toggleRewardSelection}}", _toggleRewardSelection}});
|
||||
|
||||
_layout->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -862,6 +1255,14 @@ void MacroActionTwitchEdit::UpdateEntryData()
|
||||
_channel->SetToken(_entryData->_token);
|
||||
_channel->SetChannel(_entryData->_channel);
|
||||
_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();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ public:
|
||||
// Reward add
|
||||
POINTS_REWARD_ADD = 600, // TODO
|
||||
|
||||
// Get reward information
|
||||
POINTS_REWARD_GET_INFO = 650,
|
||||
|
||||
// Reward update
|
||||
POINTS_REWARD_ENABLE = 710,
|
||||
POINTS_REWARD_DISABLE = 711,
|
||||
@@ -134,6 +137,9 @@ public:
|
||||
CHANNEL_SCHEDULE_SEGMENT_DELETE = 3500, // TODO
|
||||
|
||||
SEND_CHAT_MESSAGE = 5000,
|
||||
|
||||
// Get user info
|
||||
USER_GET_INFO = 6000
|
||||
};
|
||||
|
||||
enum class AnnouncementColor {
|
||||
@@ -146,6 +152,8 @@ public:
|
||||
|
||||
enum class RedemptionStatus { CANCELED, FULFILLED };
|
||||
|
||||
enum class UserInfoQueryType { ID, LOGIN };
|
||||
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
@@ -169,6 +177,12 @@ public:
|
||||
AnnouncementColor _announcementColor = AnnouncementColor::PRIMARY;
|
||||
TwitchChannel _channel;
|
||||
StringVariable _chatMessage;
|
||||
UserInfoQueryType _userInfoQueryType = UserInfoQueryType::LOGIN;
|
||||
StringVariable _userLogin = "user login";
|
||||
IntVariable _userId = 0;
|
||||
TwitchPointsReward _pointsReward;
|
||||
std::weak_ptr<Variable> _rewardVariable;
|
||||
bool _useVariableForRewardSelection = false;
|
||||
|
||||
private:
|
||||
void SetStreamTitle(const std::shared_ptr<TwitchToken> &) const;
|
||||
@@ -181,10 +195,20 @@ private:
|
||||
bool enable) const;
|
||||
void StartRaid(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;
|
||||
std::shared_ptr<TwitchChatConnection> _chatConnection;
|
||||
|
||||
std::string _lastResolvedRewardTitle;
|
||||
std::string _lastResolvedRewardId;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -210,7 +234,7 @@ private slots:
|
||||
void TwitchTokenChanged(const QString &);
|
||||
void CheckToken();
|
||||
void StreamTitleChanged();
|
||||
void CategoreyChanged(const TwitchCategory &);
|
||||
void CategoryChanged(const TwitchCategory &);
|
||||
void MarkerDescriptionChanged();
|
||||
void ClipHasDelayChanged(int state);
|
||||
void DurationChanged(const Duration &);
|
||||
@@ -218,6 +242,12 @@ private slots:
|
||||
void AnnouncementColorChanged(int index);
|
||||
void ChannelChanged(const TwitchChannel &);
|
||||
void ChatMessageChanged();
|
||||
void UserInfoQueryTypeChanged(int);
|
||||
void UserLoginChanged();
|
||||
void UserIdChanged(const NumberVariable<int> &);
|
||||
void PointsRewardChanged(const TwitchPointsReward &);
|
||||
void RewardVariableChanged(const QString &);
|
||||
void ToggleRewardSelection(bool);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -246,6 +276,12 @@ private:
|
||||
QComboBox *_announcementColor;
|
||||
TwitchChannelSelection *_channel;
|
||||
VariableTextEdit *_chatMessage;
|
||||
QComboBox *_userInfoQueryType;
|
||||
VariableLineEdit *_userLogin;
|
||||
VariableSpinBox *_userId;
|
||||
TwitchPointsRewardWidget *_pointsReward;
|
||||
VariableSelection *_rewardVariable;
|
||||
QPushButton *_toggleRewardSelection;
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -257,65 +257,15 @@ void MacroConditionTwitch::ResetChatConnection()
|
||||
_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()
|
||||
{
|
||||
if (!_eventBuffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!_eventBuffer->Empty()) {
|
||||
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;
|
||||
return HandleMatchingSubscriptionEvents([this](const Event &event) {
|
||||
SetVariableValue(event.ToString());
|
||||
SetJsonTempVars(event.data,
|
||||
[this](const char *id, const char *value) {
|
||||
SetTempVarValue(id, value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool MacroConditionTwitch::CheckChannelLiveEvents()
|
||||
@@ -345,10 +295,105 @@ bool MacroConditionTwitch::CheckChannelLiveEvents()
|
||||
}
|
||||
|
||||
SetVariableValue(event->ToString());
|
||||
setTempVarsHelper(event->data,
|
||||
[this](const char *id, const char *value) {
|
||||
SetTempVarValue(id, value);
|
||||
});
|
||||
SetJsonTempVars(event->data,
|
||||
[this](const char *id, const char *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) {
|
||||
_eventBuffer->Clear();
|
||||
@@ -618,16 +663,18 @@ bool MacroConditionTwitch::CheckCondition()
|
||||
case Condition::CHARITY_CAMPAIGN_END_EVENT:
|
||||
case Condition::SHIELD_MODE_START_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_UNBAN_EVENT:
|
||||
case Condition::USER_MODERATOR_ADDITION_EVENT:
|
||||
case Condition::USER_MODERATOR_DELETION_EVENT:
|
||||
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_PLAYLIST_EVENT:
|
||||
case Condition::STREAM_ONLINE_WATCHPARTY_EVENT:
|
||||
@@ -1206,25 +1253,41 @@ void MacroConditionTwitch::SetupTempVars()
|
||||
setupTempVarHelper("is_user_input_required", ".reward");
|
||||
setupTempVarHelper("should_redemptions_skip_request_queue",
|
||||
".reward");
|
||||
setupTempVarHelper("max_per_stream", ".reward");
|
||||
setupTempVarHelper("max_per_user_per_stream", ".reward");
|
||||
setupTempVarHelper("background_color", ".reward");
|
||||
setupTempVarHelper("image", ".reward");
|
||||
setupTempVarHelper("image.url_4x", ".reward");
|
||||
setupTempVarHelper("default_image", ".reward");
|
||||
setupTempVarHelper("global_cooldown", ".reward");
|
||||
setupTempVarHelper("default_image.url_4x", ".reward");
|
||||
setupTempVarHelper("cooldown_expires_at", ".reward");
|
||||
setupTempVarHelper("redemptions_redeemed_current_stream",
|
||||
".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;
|
||||
case Condition::POINTS_REWARD_REDEMPTION_EVENT:
|
||||
case Condition::POINTS_REWARD_REDEMPTION_UPDATE_EVENT:
|
||||
setupTempVarHelper("id", ".reward");
|
||||
setupTempVarHelper("id", ".redemption");
|
||||
setupTempVarHelper("user_id", ".reward");
|
||||
setupTempVarHelper("user_login", ".reward");
|
||||
setupTempVarHelper("user_name", ".reward");
|
||||
setupTempVarHelper("user_input", ".reward");
|
||||
setupTempVarHelper("status", ".reward");
|
||||
setupTempVarHelper("reward", ".reward");
|
||||
setupTempVarHelper("reward.id", ".reward");
|
||||
setupTempVarHelper("reward.title", ".reward");
|
||||
setupTempVarHelper("reward.prompt", ".reward");
|
||||
setupTempVarHelper("reward.cost", ".reward");
|
||||
setupTempVarHelper("redeemed_at", ".reward");
|
||||
break;
|
||||
case Condition::USER_BAN_EVENT:
|
||||
@@ -1387,7 +1450,7 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
||||
_tokens(new TwitchConnectionSelection()),
|
||||
_tokenWarning(new QLabel()),
|
||||
_channel(new TwitchChannelSelection(this)),
|
||||
_pointsReward(new TwitchPointsRewardWidget(this)),
|
||||
_pointsReward(new TwitchPointsRewardWidget(this, true)),
|
||||
_streamTitle(new VariableLineEdit(this)),
|
||||
_regexTitle(new RegexConfigWidget(parent)),
|
||||
_chatMesageEdit(new ChatMessageEdit(this)),
|
||||
@@ -1425,10 +1488,8 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
||||
this,
|
||||
SLOT(ChatMessagePatternChanged(const ChatMessagePattern &)));
|
||||
QWidget::connect(_category,
|
||||
SIGNAL(CategoreyChanged(const TwitchCategory &)), this,
|
||||
SLOT(CategoreyChanged(const TwitchCategory &)));
|
||||
QWidget::connect(this, SIGNAL(TempVarsChanged()), window(),
|
||||
SIGNAL(SegmentTempVarsChanged()));
|
||||
SIGNAL(CategoryChanged(const TwitchCategory &)), this,
|
||||
SLOT(CategoryChanged(const TwitchCategory &)));
|
||||
QWidget::connect(_clearBufferOnMatch, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(ClearBufferOnMatchChanged(int)));
|
||||
|
||||
@@ -1480,7 +1541,6 @@ void MacroConditionTwitchEdit::ConditionChanged(int idx)
|
||||
_entryData->SetCondition(static_cast<MacroConditionTwitch::Condition>(
|
||||
_conditions->itemData(idx).toInt()));
|
||||
SetWidgetVisibility();
|
||||
emit TempVarsChanged();
|
||||
}
|
||||
|
||||
void MacroConditionTwitchEdit::TwitchTokenChanged(const QString &token)
|
||||
@@ -1572,7 +1632,7 @@ void MacroConditionTwitchEdit::RegexTitleChanged(const RegexConfig &conf)
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionTwitchEdit::CategoreyChanged(const TwitchCategory &category)
|
||||
void MacroConditionTwitchEdit::CategoryChanged(const TwitchCategory &category)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_category = category;
|
||||
|
||||
@@ -111,7 +111,11 @@ public:
|
||||
private:
|
||||
bool CheckChannelGenericEvents();
|
||||
bool CheckChannelLiveEvents();
|
||||
bool CheckChannelRewardChangeEvents();
|
||||
bool CheckChannelRewardRedemptionEvents();
|
||||
bool CheckChatMessages(TwitchToken &token);
|
||||
bool HandleMatchingSubscriptionEvents(
|
||||
const std::function<void(const Event &)> &matchCb);
|
||||
bool CheckChatUserJoinOrLeave(TwitchToken &token);
|
||||
|
||||
void RegisterEventSubscription();
|
||||
@@ -171,12 +175,11 @@ private slots:
|
||||
void StreamTitleChanged();
|
||||
void RegexTitleChanged(const RegexConfig &);
|
||||
void ChatMessagePatternChanged(const ChatMessagePattern &);
|
||||
void CategoreyChanged(const TwitchCategory &);
|
||||
void CategoryChanged(const TwitchCategory &);
|
||||
void ClearBufferOnMatchChanged(int);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
void TempVarsChanged();
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
@@ -88,7 +88,7 @@ void TwitchPointsRewardSelection::PopulateSelection()
|
||||
const QSignalBlocker b(this);
|
||||
clear();
|
||||
|
||||
auto pointsRewards = GetPointsRewards(token, *_channel);
|
||||
auto pointsRewards = GetPointsRewardsForChannel(token, *_channel);
|
||||
if (!pointsRewards) {
|
||||
DisplayErrorMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.twitch.selection.points.reward.tooltip.error"));
|
||||
@@ -127,47 +127,6 @@ void TwitchPointsRewardSelection::HideErrorMessage()
|
||||
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)
|
||||
{
|
||||
TwitchPointsReward pointsReward{
|
||||
@@ -176,13 +135,16 @@ void TwitchPointsRewardSelection::SelectionChanged(int index)
|
||||
emit PointsRewardChanged(pointsReward);
|
||||
}
|
||||
|
||||
TwitchPointsRewardWidget::TwitchPointsRewardWidget(QWidget *parent)
|
||||
TwitchPointsRewardWidget::TwitchPointsRewardWidget(QWidget *parent,
|
||||
bool allowAny)
|
||||
: QWidget(parent),
|
||||
_selection(new TwitchPointsRewardSelection(this)),
|
||||
_selection(new TwitchPointsRewardSelection(this, allowAny)),
|
||||
_refreshButton(new QPushButton(this))
|
||||
{
|
||||
_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(
|
||||
"AdvSceneSwitcher.twitch.selection.points.reward.refresh"));
|
||||
|
||||
@@ -216,4 +178,44 @@ void TwitchPointsRewardWidget::SetToken(const std::weak_ptr<TwitchToken> &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
|
||||
|
||||
@@ -20,7 +20,7 @@ class TwitchPointsRewardSelection : public FilterComboBox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TwitchPointsRewardSelection(QWidget *parent, bool allowAny = true);
|
||||
TwitchPointsRewardSelection(QWidget *parent, bool allowAny);
|
||||
|
||||
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
||||
void SetChannel(const TwitchChannel &channel);
|
||||
@@ -35,10 +35,6 @@ protected:
|
||||
|
||||
private:
|
||||
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;
|
||||
const std::vector<TokenOption> _supportedTokenOptions = {
|
||||
@@ -57,7 +53,7 @@ class TwitchPointsRewardWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TwitchPointsRewardWidget(QWidget *parent);
|
||||
TwitchPointsRewardWidget(QWidget *parent, bool allowAny);
|
||||
|
||||
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
||||
void SetChannel(const TwitchChannel &channel);
|
||||
@@ -71,4 +67,8 @@ protected:
|
||||
QPushButton *_refreshButton;
|
||||
};
|
||||
|
||||
std::optional<std::vector<TwitchPointsReward>>
|
||||
GetPointsRewardsForChannel(const std::shared_ptr<TwitchToken> &token,
|
||||
const TwitchChannel &channel);
|
||||
|
||||
} // namespace advss
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user