mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 04:36:21 -05:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bcced524b | ||
|
|
3bb2ddfaac | ||
|
|
50e26aba72 | ||
|
|
267ab6a7c1 | ||
|
|
65ea7987c7 | ||
|
|
a6839666ce | ||
|
|
e5843de4fc | ||
|
|
a330378c16 | ||
|
|
ddb7888e61 | ||
|
|
73aef4f0b2 | ||
|
|
67c3b73e10 | ||
|
|
9a3d381cf0 | ||
|
|
8b8829f7df | ||
|
|
6fff3b54e6 | ||
|
|
dd69ca1b35 | ||
|
|
66ddf3b4ef | ||
|
|
c9b5bbfc14 | ||
|
|
3e1e8d01b6 | ||
|
|
c22f40e333 | ||
|
|
b83acaca79 | ||
|
|
53bef18d15 | ||
|
|
ced1c7605a | ||
|
|
f78e91eec4 | ||
|
|
7fdff72a92 | ||
|
|
d2d042bae3 | ||
|
|
f70ae803a3 | ||
|
|
cdefbf383c | ||
|
|
5f8641b18a | ||
|
|
d107f653de | ||
|
|
22f4c11ee3 | ||
|
|
7683957267 | ||
|
|
e0d4c7244d | ||
|
|
b2ccd30f68 | ||
|
|
1dea2e5f5d | ||
|
|
c95fb6c2ea | ||
|
|
d779ced27f | ||
|
|
c84c1638bf | ||
|
|
3ac4e22600 | ||
|
|
a59d15a77a | ||
|
|
c2edc7c612 | ||
|
|
77fe75f738 | ||
|
|
fdb70a32c6 | ||
|
|
487db7302e | ||
|
|
12a74caf62 | ||
|
|
742996fc83 | ||
|
|
f5a0b2e91a | ||
|
|
114ca7b545 | ||
|
|
a8953f23f4 | ||
|
|
106be642aa | ||
|
|
4c20ac605d | ||
|
|
75cdd3c80d | ||
|
|
b2585ae176 | ||
|
|
d1e912cb24 | ||
|
|
bd5ecb3b3b | ||
|
|
1a8a4584c1 | ||
|
|
6d755ad570 | ||
|
|
99ceb6a42a | ||
|
|
7ddfdcf5be | ||
|
|
a243e026c8 | ||
|
|
f40f98c702 | ||
|
|
d99618d2f6 | ||
|
|
a93305e16e | ||
|
|
fa3ae607b5 | ||
|
|
bddbcebece | ||
|
|
aeaa13955c | ||
|
|
f99eb6c3c8 | ||
|
|
c16b97e5cd | ||
|
|
0d29fb2ee1 | ||
|
|
0169f6c445 | ||
|
|
886639fe24 | ||
|
|
7304fa349e |
8
.github/actions/build-plugin/action.yml
vendored
8
.github/actions/build-plugin/action.yml
vendored
@@ -20,6 +20,10 @@ inputs:
|
||||
description: 'Visual Studio version (Windows only)'
|
||||
required: false
|
||||
default: 'Visual Studio 16 2019'
|
||||
portable:
|
||||
description: 'Set portable mode (Linux only)'
|
||||
required: false
|
||||
default: 'false'
|
||||
workingDirectory:
|
||||
description: 'Working directory for packaging'
|
||||
required: false
|
||||
@@ -56,6 +60,10 @@ runs:
|
||||
build_args+=(--debug)
|
||||
fi
|
||||
|
||||
if [[ '${{ inputs.portable }}' == 'true' ]]; then
|
||||
build_args+=(-p)
|
||||
fi
|
||||
|
||||
${{ inputs.workingDirectory }}/.github/scripts/build-linux.sh "${build_args[@]}"
|
||||
|
||||
- name: Run Windows Build
|
||||
|
||||
8
.github/actions/package-plugin/action.yml
vendored
8
.github/actions/package-plugin/action.yml
vendored
@@ -36,6 +36,10 @@ inputs:
|
||||
description: 'Create InnoSetup installer (Windows only)'
|
||||
required: false
|
||||
default: 'false'
|
||||
portable:
|
||||
description: 'Create deb package / portable archive (Linux only)'
|
||||
required: false
|
||||
default: 'false'
|
||||
workingDirectory:
|
||||
description: 'Working directory for packaging'
|
||||
required: false
|
||||
@@ -75,6 +79,10 @@ runs:
|
||||
build_args+=(--debug)
|
||||
fi
|
||||
|
||||
if [[ '${{ inputs.portable }}' == 'true' ]]; then
|
||||
package_args+=(-z)
|
||||
fi
|
||||
|
||||
${{ inputs.workingDirectory }}/.github/scripts/package-linux.sh "${package_args[@]}"
|
||||
|
||||
- name: Run Windows packaging
|
||||
|
||||
8
.github/scripts/.build.zsh
vendored
8
.github/scripts/.build.zsh
vendored
@@ -76,6 +76,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
%B-t | --target%b Specify target - default: %B%F{green}${host_os}-${CPUTYPE}%f%b
|
||||
%B-c | --config%b Build configuration - default: %B%F{green}RelWithDebInfo%f%b
|
||||
%B-s | --codesign%b Enable codesigning (macOS only)
|
||||
%B-p | --portable%b Enable portable mode (Linux only)
|
||||
%B--generator%b Specify build system to generate - default: %B%F{green}Ninja%f%b
|
||||
Available generators:
|
||||
- Ninja
|
||||
@@ -130,6 +131,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
shift 2
|
||||
;;
|
||||
-s|--codesign) CODESIGN=1; shift ;;
|
||||
-p|--portable) typeset -g PORTABLE=1; shift ;;
|
||||
-q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
|
||||
-v|--verbose) (( _verbosity += 1 )); shift ;;
|
||||
-h|--help) log_output ${_usage}; exit 0 ;;
|
||||
@@ -245,7 +247,11 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
|
||||
;;
|
||||
linux-*)
|
||||
if (( ${+CI} )) {
|
||||
if (( ${+PORTABLE} )) {
|
||||
cmake_args+=(
|
||||
-DLINUX_PORTABLE=ON
|
||||
)
|
||||
} else {
|
||||
cmake_args+=(
|
||||
-DCMAKE_INSTALL_PREFIX=/usr
|
||||
-DLINUX_PORTABLE=OFF
|
||||
|
||||
21
.github/scripts/.package.zsh
vendored
21
.github/scripts/.package.zsh
vendored
@@ -61,6 +61,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
%B-c | --config%b Build configuration - default: %B%F{green}RelWithDebInfo%f%b
|
||||
%B-s | --codesign%b Enable codesigning (macOS only)
|
||||
%B-n | --notarize%b Enable notarization (macOS only)
|
||||
%B-z | --zip%b Zip only (Linux only)
|
||||
|
||||
%F{yellow} Output options%f
|
||||
-----------------------------------------------------------------------------
|
||||
@@ -110,6 +111,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
;;
|
||||
-s|--codesign) typeset -g CODESIGN=1; shift ;;
|
||||
-n|--notarize) typeset -g NOTARIZE=1; typeset -g CODESIGN=1; shift ;;
|
||||
-z|--zip) typeset -g ZIP=1; shift ;;
|
||||
-q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
|
||||
-v|--verbose) (( _verbosity += 1 )); shift ;;
|
||||
-h|--help) log_output ${_usage}; exit 0 ;;
|
||||
@@ -132,7 +134,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
if [[ ${host_os} == 'macos' ]] {
|
||||
autoload -Uz check_packages read_codesign read_codesign_installer read_codesign_pass
|
||||
|
||||
local output_name="${product_name}-${product_version}-${host_os}-${target##*-}.pkg"
|
||||
local output_name="${product_name}-${host_os}-${target##*-}.pkg"
|
||||
|
||||
if [[ ! -d ${project_root}/release/${product_name}.plugin ]] {
|
||||
log_error 'No release artifact found. Run the build script or the CMake install procedure first.'
|
||||
@@ -180,12 +182,19 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
}
|
||||
popd
|
||||
} elif [[ ${host_os} == 'linux' ]] {
|
||||
local -a cmake_args=()
|
||||
if (( _loglevel > 1 )) cmake_args+=(--verbose)
|
||||
if (( ${+ZIP} )) {
|
||||
local output_name="${product_name}-${host_os}-${target##*-}.zip"
|
||||
pushd ${project_root}/release
|
||||
zip -r "${output_name}" *
|
||||
popd
|
||||
} else {
|
||||
local -a cmake_args=()
|
||||
if (( _loglevel > 1 )) cmake_args+=(--verbose)
|
||||
|
||||
pushd ${project_root}
|
||||
cmake --build build_${target##*-} --config ${BUILD_CONFIG:-RelWithDebInfo} -t package ${cmake_args}
|
||||
popd
|
||||
pushd ${project_root}
|
||||
cmake --build build_${target##*-} --config ${BUILD_CONFIG:-RelWithDebInfo} -t package ${cmake_args}
|
||||
popd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
.github/scripts/Package-Windows.ps1
vendored
3
.github/scripts/Package-Windows.ps1
vendored
@@ -38,9 +38,8 @@ function Package {
|
||||
|
||||
$BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json
|
||||
$ProductName = $BuildSpec.name
|
||||
$ProductVersion = $BuildSpec.version
|
||||
|
||||
$OutputName = "${ProductName}-${ProductVersion}-windows-${Target}"
|
||||
$OutputName = "${ProductName}-windows-${Target}"
|
||||
|
||||
Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile"
|
||||
|
||||
|
||||
69
.github/workflows/build-debian.yml
vendored
Normal file
69
.github/workflows/build-debian.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: debian-build
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'data/locale/**'
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'data/locale/**'
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: "recursive"
|
||||
- name: check_libobs_revision
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libobs-dev
|
||||
mkdir source
|
||||
cd source
|
||||
dpkg -l libobs-dev | tr -s " "| grep libobs | cut -d" " -f3 > libobs.rev
|
||||
- name: install_frontend_header
|
||||
run: |
|
||||
[ -e /usr/include/obs/obs-frontend-api.h ] && { echo "ERROR: obs-frontend-api.h already in system. Maybe the package libobs-dev is installing it."; exit 1; }
|
||||
cd source
|
||||
LIBOBSREV=$(cat libobs.rev)
|
||||
sudo apt update
|
||||
sudo apt install devscripts
|
||||
dget -u http://archive.ubuntu.com/ubuntu/pool/universe/o/obs-studio/obs-studio_$LIBOBSREV.dsc
|
||||
cd ..
|
||||
sudo find -name obs-frontend-api.h -exec cp {} /usr/include/obs/ \;
|
||||
egrep '#include <obs.h>' /usr/include/obs/obs-frontend-api.h || { echo "ERROR: check if the sed commands are needed from now."; exit 1; }
|
||||
sudo sed -i 's/#include <obs.h>/#include <obs\/obs.h>/' /usr/include/obs/obs-frontend-api.h
|
||||
sudo sed -i 's/#include <util\/darray.h>/#include <obs\/util\/darray.h>/' /usr/include/obs/obs-frontend-api.h
|
||||
- name: create_tarball
|
||||
run: |
|
||||
cd ..
|
||||
tar --exclude=.git -cvzf obs-scene-switcher_0.1+testonly.orig.tar.gz SceneSwitcher
|
||||
- name: create_debian_dir
|
||||
run: |
|
||||
cp -a CI/linux/debian .
|
||||
- name: install_dependencies
|
||||
run: |
|
||||
# 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
|
||||
- name: build
|
||||
run: |
|
||||
debuild --no-lintian --no-sign
|
||||
mv ../*.deb .
|
||||
- name: Publish
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: "obs-scene-switcher.deb"
|
||||
path: ${{ github.workspace }}/*.deb
|
||||
55
.github/workflows/main.yml
vendored
55
.github/workflows/main.yml
vendored
@@ -92,21 +92,21 @@ jobs:
|
||||
if [[ '${{ secrets.MACOS_SIGNING_APPLICATION_IDENTITY }}' != '' && \
|
||||
'${{ secrets.MACOS_SIGNING_INSTALLER_IDENTITY }}' != '' && \
|
||||
'${{ secrets.MACOS_SIGNING_CERT }}' != '' ]] {
|
||||
print '::set-output name=haveCodesignIdent::true'
|
||||
print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print '::set-output name=haveCodesignIdent::false'
|
||||
print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
|
||||
if [[ '${{ secrets.MACOS_NOTARIZATION_USERNAME }}' != '' && \
|
||||
'${{ secrets.MACOS_NOTARIZATION_PASSWORD }}' != '' ]] {
|
||||
print '::set-output name=haveNotarizationUser::true'
|
||||
print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print '::set-output name=haveNotarizationUser::false'
|
||||
print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
print '::endgroup::'
|
||||
|
||||
print "::set-output name=ccacheDate::$(date +"%Y-%m-%d")"
|
||||
print "::set-output name=commitHash::${"$(git rev-parse HEAD)"[0,9]}"
|
||||
print "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
|
||||
print "commitHash=${"$(git rev-parse HEAD)"[0,9]}" >> $GITHUB_OUTPUT
|
||||
echo "$PWD/.github/scripts" >> $GITHUB_PATH
|
||||
|
||||
- name: Restore Compilation Cache
|
||||
@@ -123,9 +123,9 @@ jobs:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]] {
|
||||
print '::set-output name=found::true'
|
||||
print 'found=true' >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print '::set-output name=found::false'
|
||||
print 'found=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
|
||||
- name: Install Apple Developer Certificate
|
||||
@@ -169,7 +169,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.PLUGIN_NAME }}-macos-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}-*-macos-${{ matrix.arch }}.pkg
|
||||
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}*-macos-${{ matrix.arch }}.pkg
|
||||
|
||||
linux_build:
|
||||
name: 02 - Linux
|
||||
@@ -178,6 +178,7 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
arch: [x86_64]
|
||||
portable: [true, false]
|
||||
if: always()
|
||||
needs: [clang_check]
|
||||
outputs:
|
||||
@@ -205,8 +206,8 @@ jobs:
|
||||
id: setup
|
||||
run: |
|
||||
## SETUP ENVIRONMENT SCRIPT
|
||||
echo "::set-output name=ccacheDate::$(date +"%Y-%m-%d")"
|
||||
echo "::set-output name=commitHash::$(git rev-parse HEAD | cut -c1-9)"
|
||||
echo "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
|
||||
echo "commitHash=$(git rev-parse HEAD | cut -c1-9)" >> $GITHUB_OUTPUT
|
||||
echo "$PWD/.github/scripts" >> $GITHUB_PATH
|
||||
|
||||
- name: Restore Compilation Cache
|
||||
@@ -224,9 +225,9 @@ jobs:
|
||||
run: |
|
||||
## GITHUB LABEL SCRIPT
|
||||
if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]]; then
|
||||
echo '::set-output name=found::true'
|
||||
echo 'found=true' >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo '::set-output name=found::false'
|
||||
echo 'found=false' >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build Plugin
|
||||
@@ -235,13 +236,7 @@ jobs:
|
||||
workingDirectory: ${{ github.workspace }}/plugin
|
||||
target: ${{ matrix.arch }}
|
||||
config: RelWithDebInfo
|
||||
|
||||
- name: Upload Build Artifact
|
||||
if: ${{ success() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.PLUGIN_NAME }}-linux-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/plugin/release/*
|
||||
portable: ${{ matrix.portable }}
|
||||
|
||||
- name: Package Plugin
|
||||
uses: ./plugin/.github/actions/package-plugin
|
||||
@@ -249,13 +244,21 @@ jobs:
|
||||
workingDirectory: ${{ github.workspace }}/plugin
|
||||
target: ${{ matrix.arch }}
|
||||
config: RelWithDebInfo
|
||||
portable: ${{ matrix.portable }}
|
||||
|
||||
- name: Upload Package Artifact
|
||||
if: ${{ success() }}
|
||||
if: ${{ matrix.portable }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.PLUGIN_NAME }}-linux-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}-portable
|
||||
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}*-linux-${{ matrix.arch }}.*
|
||||
|
||||
- name: Upload Build Artifact
|
||||
if: ${{ ! matrix.portable }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.PLUGIN_NAME }}-linux-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}-*-linux-${{ matrix.arch }}.*
|
||||
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}*-linux-${{ matrix.arch }}.*
|
||||
|
||||
windows_build:
|
||||
name: 02 - Windows
|
||||
@@ -263,7 +266,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
arch: [x86, x64]
|
||||
arch: [x64]
|
||||
if: always()
|
||||
needs: [clang_check]
|
||||
outputs:
|
||||
@@ -292,7 +295,7 @@ jobs:
|
||||
run: |
|
||||
## SETUP ENVIRONMENT SCRIPT
|
||||
$CommitHash = (git rev-parse HEAD)[0..8] -join ''
|
||||
Write-Output "::set-output name=commitHash::${CommitHash}"
|
||||
"commitHash=${CommitHash}" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Check for GitHub Labels
|
||||
id: seekingTesters
|
||||
@@ -313,7 +316,7 @@ jobs:
|
||||
$false
|
||||
}
|
||||
|
||||
Write-Output "::set-output name=found::$(([string]${LabelFound}).ToLower())"
|
||||
"found=$(([string]${LabelFound}).ToLower())" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Build Plugin
|
||||
uses: ./plugin/.github/actions/build-plugin
|
||||
@@ -364,7 +367,7 @@ jobs:
|
||||
id: metadata
|
||||
run: |
|
||||
## METADATA SCRIPT
|
||||
echo "::set-output name=version::${GITHUB_REF/refs\/tags\//}"
|
||||
echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,6 +10,3 @@
|
||||
[submodule "deps/openvr"]
|
||||
path = deps/openvr
|
||||
url = https://github.com/ValveSoftware/openvr.git
|
||||
[submodule "deps/obs-websocket"]
|
||||
path = deps/obs-websocket
|
||||
url = https://github.com/obsproject/obs-websocket.git
|
||||
|
||||
123
BUILDING.md
123
BUILDING.md
@@ -6,6 +6,8 @@ You have the option to ...
|
||||
|
||||
As both methods require you to have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio) and [CMake](https://cmake.org/download/) it is recommended to build the plugin in tree as it is easier to set up and will enable straightforward debugging.
|
||||
|
||||
The plugin can be compiled for OBS 25 and above, although using the latest version of OBS is recommended to support all features.
|
||||
|
||||
## Compiling in tree (recommended for development)
|
||||
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
|
||||
```
|
||||
@@ -13,7 +15,7 @@ cd obs-studio/UI/frontend-plugins/
|
||||
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
|
||||
```
|
||||
|
||||
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt file and add an entry for the scene switcher:
|
||||
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt Example and add an entry for the scene switcher:
|
||||
```
|
||||
add_subdirectory(SceneSwitcher)
|
||||
```
|
||||
@@ -25,6 +27,7 @@ Note that on Linux systems it might be necessary to additionally install the fol
|
||||
sudo apt-get install \
|
||||
libxtst-dev \
|
||||
libxss-dev \
|
||||
libprocps-dev \
|
||||
libopencv-dev
|
||||
```
|
||||
|
||||
@@ -63,11 +66,123 @@ Finally, start the plugin build using your provided generator. (E.g. Ninja on Li
|
||||
|
||||
Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out.
|
||||
|
||||
If you would like to expand upon the macro system by adding a new condition or action type have a loot at the examples in `src/macro-core`.
|
||||
In general changes in the `src/legacy` folder should be avoided.
|
||||
|
||||
If you would like to expand upon the macro system by adding a new condition or action type have a loot at the examples in `src/macro-core`.
|
||||
The key functions to add conditions or are the Register() functions.
|
||||
## Macro condition
|
||||
Macro conditions should inherit from the `MacroCondition` class and must implement the following functions:
|
||||
|
||||
```
|
||||
class MacroConditionExample : public MacroCondition {
|
||||
public:
|
||||
MacroConditionExample(Macro *m) : MacroCondition(m) {}
|
||||
// This function should perfrom the condition check
|
||||
bool CheckCondition();
|
||||
// This function should store the required condition data to "obj"
|
||||
// For example called on OBS shutdown
|
||||
bool Save(obs_data_t *obj);
|
||||
// This function should load the condition data from "obj"
|
||||
// For example called on OBS startup
|
||||
bool Load(obs_data_t *obj);
|
||||
// This function should return a unique id for this condition type
|
||||
// The _id is defined below
|
||||
std::string GetId() { return _id; };
|
||||
// Helper function called when new conditions are created
|
||||
// Will be used later when regeistering the new condition type
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionExample>(m);
|
||||
}
|
||||
|
||||
private:
|
||||
// Used to register new condition type
|
||||
static bool _registered;
|
||||
// Unique id identifying this condition type
|
||||
static const std::string _id;
|
||||
};
|
||||
```
|
||||
When defining the widget used to control the settings of the condition type, it is important to add a static `Create()` method.
|
||||
It will be called whenever a new condition MacroConditionExample is created. (See `MacroConditionFactory::Register()`)
|
||||
```
|
||||
class MacroConditionExampleEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// ...
|
||||
MacroConditionExampleEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionExample> cond = nullptr);
|
||||
|
||||
// Function will be used to create the widget for editing the settings of the condition
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionExampleEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionExample>(cond));
|
||||
}
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
To finally register the new condition type you will have to add the following call to `MacroConditionFactory::Register()`.
|
||||
|
||||
```
|
||||
bool MacroConditionExample::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionExample::id, // Unique string identifying this condition type
|
||||
{
|
||||
MacroConditionExample::Create, // Function called to create the object performing the condition
|
||||
MacroConditionExampleEdit::Create, // Function called to create the widget configure the condition
|
||||
"AdvSceneSwitcher.condition.example", // User facing name of the condition type (will be translated)
|
||||
true // Condition type supports duration modifiers (default true)
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Macro action
|
||||
|
||||
The process of adding a new action type is very similar to adding a new condition.
|
||||
The differences are highlighted in the comments below.
|
||||
|
||||
```
|
||||
class MacroActionExample : public MacroAction {
|
||||
public:
|
||||
MacroActionExample(Macro *m) : MacroAction(m) {}
|
||||
// This function should perfrom the action
|
||||
// If false is returned the macro will aborted
|
||||
bool PerformAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return _id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionExample>(m);
|
||||
}
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string _id;
|
||||
};
|
||||
```
|
||||
```
|
||||
class MacroActionExampleEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// ...
|
||||
MacroActionExampleEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionExample> entryData = nullptr);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionExampleEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionExample>(action));
|
||||
}
|
||||
// ...
|
||||
};
|
||||
```
|
||||
```
|
||||
MacroActionFactory::Register(
|
||||
MacroActionExample::id, // Unique string identifying this action type
|
||||
@@ -78,6 +193,6 @@ MacroActionFactory::Register(
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## External dependencies
|
||||
If your intention is to add macro functionality which depends on external libraries, which is likely not to exist on all user setups, try to follow the examples under `src/macro-external`.
|
||||
These are basically plugins themselves that get attempted to be loaded on startup of the advanced scene switcher.
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
Note that you will have to use OBS version 27 or newer!
|
||||
|
||||
--- Windows ---
|
||||
Recommended: Run the provided installer. (You might have to click 'More info' and select 'Run anyway' if it is blocked by Windows)
|
||||
|
||||
Alternatively manually copy the 'obs-plugins' and 'data' folders in the respective OBS Studio installation directory.
|
||||
It is usually located at 'C:\Program Files (x86)\obs-studio\'.
|
||||
Remember to install the Visual C++ Redistributable for Visual Studio 2019, if you have not done so already. (See plugin overview page)
|
||||
|
||||
--- macOS ---
|
||||
Run the provided installer. (You might have to right-click or control-click and select 'open' if it is blocked)
|
||||
|
||||
--- Linux ---
|
||||
Note that the plugin has dependencies to:
|
||||
* libXss
|
||||
* libopencv-imgproc
|
||||
* libopencv-objdetect
|
||||
Optional:
|
||||
* libXtst
|
||||
* libcurl
|
||||
|
||||
If you are using snap you can install the following package for an OBS installation which comes bundled with the plugin:
|
||||
sudo snap install obs-studio
|
||||
|
||||
The plugin is also available via the Flatpak package manager for users who installed OBS via Flatpak:
|
||||
flatpak install com.obsproject.Studio.Plugin.SceneSwitcher
|
||||
|
||||
If you have installed OBS via other means, it is most likely necessary to install the plugin manually.
|
||||
To do so copy the advanced-scene-switcher.so file and into the OBS Studio plugin folder.
|
||||
The location of this folder can vary, so you might have to look around a bit.
|
||||
|
||||
Examples are ...
|
||||
/usr/lib/obs-plugins/
|
||||
/usr/lib/x86_64-linux-gnu/obs-plugins/
|
||||
/usr/share/obs/obs-plugins/
|
||||
~/.config/obs-studio/plugins/advanced-scene-switcher/bin/64bit/
|
||||
~/.local/share/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/lib/obs-plugins/
|
||||
/var/lib/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/lib/obs-plugins/
|
||||
|
||||
Unfortunately the expected location of the "locale" and "res" folder, which can be found in the data folder, can vary also.
|
||||
The data folder usually should be copied to "~/.config/obs-studio/plugins/advanced-scene-switcher/" and reflect the following structure:
|
||||
|
||||
/home/user/.config/obs-studio/plugins/
|
||||
└── advanced-scene-switcher
|
||||
├── bin
|
||||
│ └── 64bit
|
||||
│ └── advanced-scene-switcher.so
|
||||
└── data
|
||||
├── locale
|
||||
│ ├── de-DE.ini
|
||||
│ ├── en-US.ini
|
||||
│ ├── ru-RU.ini
|
||||
│ └── zh-CN.ini
|
||||
└── res
|
||||
├── cascadeClassifiers
|
||||
│ ├── haarcascade_eye_tree_eyeglasses.xml
|
||||
│ ├── haarcascade_eye.xml
|
||||
│ ├── haarcascade_frontalface_alt2.xml
|
||||
│ ├── haarcascade_frontalface_alt_tree.xml
|
||||
│ ├── haarcascade_frontalface_alt.xml
|
||||
│ ├── haarcascade_frontalface_default.xml
|
||||
│ ├── haarcascade_fullbody.xml
|
||||
│ ├── haarcascade_lefteye_2splits.xml
|
||||
│ ├── haarcascade_lowerbody.xml
|
||||
│ ├── haarcascade_profileface.xml
|
||||
│ ├── haarcascade_righteye_2splits.xml
|
||||
│ └── haarcascade_upperbody.xml
|
||||
└── time.svg
|
||||
|
||||
If this does not work you can try to copy the "locale" folder found inside the data folder to:
|
||||
/usr/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||
~/.local/share/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||
/var/lib/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||
|
||||
In doubt, please check where other "en-US.ini" files are located on your system.
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
cmake_minimum_required(VERSION 3.16.3)
|
||||
|
||||
project(advanced-scene-switcher VERSION 1.0.0)
|
||||
set(LIB_NAME "${PROJECT_NAME}-lib")
|
||||
@@ -147,6 +147,8 @@ target_sources(
|
||||
src/macro-core/macro-action-timer.hpp
|
||||
src/macro-core/macro-action-transition.cpp
|
||||
src/macro-core/macro-action-transition.hpp
|
||||
src/macro-core/macro-action-variable.cpp
|
||||
src/macro-core/macro-action-variable.hpp
|
||||
src/macro-core/macro-action-virtual-cam.cpp
|
||||
src/macro-core/macro-action-virtual-cam.hpp
|
||||
src/macro-core/macro-action-wait.cpp
|
||||
@@ -205,6 +207,8 @@ target_sources(
|
||||
src/macro-core/macro-condition-timer.hpp
|
||||
src/macro-core/macro-condition-transition.cpp
|
||||
src/macro-core/macro-condition-transition.hpp
|
||||
src/macro-core/macro-condition-variable.cpp
|
||||
src/macro-core/macro-condition-variable.hpp
|
||||
src/macro-core/macro-condition-virtual-cam.cpp
|
||||
src/macro-core/macro-condition-virtual-cam.hpp
|
||||
src/macro-core/macro-condition-websocket.cpp
|
||||
@@ -238,12 +242,16 @@ target_sources(
|
||||
src/utils/curl-helper.hpp
|
||||
src/utils/duration-control.cpp
|
||||
src/utils/duration-control.hpp
|
||||
src/utils/item-selection-helpers.cpp
|
||||
src/utils/item-selection-helpers.hpp
|
||||
src/utils/file-selection.cpp
|
||||
src/utils/file-selection.hpp
|
||||
src/utils/macro-list.cpp
|
||||
src/utils/macro-list.hpp
|
||||
src/utils/name-dialog.cpp
|
||||
src/utils/name-dialog.hpp
|
||||
src/utils/regex-config.cpp
|
||||
src/utils/regex-config.hpp
|
||||
src/utils/resizing-text-edit.cpp
|
||||
src/utils/resizing-text-edit.hpp
|
||||
src/utils/scene-item-selection.cpp
|
||||
@@ -258,29 +266,27 @@ target_sources(
|
||||
src/utils/transition-selection.hpp
|
||||
src/utils/utility.cpp
|
||||
src/utils/utility.hpp
|
||||
src/utils/variable.cpp
|
||||
src/utils/variable.hpp
|
||||
src/utils/volume-control.cpp
|
||||
src/utils/volume-control.hpp
|
||||
src/utils/websocket-helpers.cpp
|
||||
src/utils/websocket-helpers.hpp
|
||||
src/utils/volume-control.hpp)
|
||||
src/utils/websocket-helpers.hpp)
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${LIB_NAME})
|
||||
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
find_package(libobs REQUIRED)
|
||||
find_package(obs-frontend-api REQUIRED)
|
||||
include(cmake/ObsPluginHelpers.cmake)
|
||||
target_link_libraries(${LIB_NAME} PUBLIC OBS::libobs OBS::obs-frontend-api)
|
||||
else()
|
||||
target_link_libraries(${LIB_NAME} PUBLIC OBS::libobs OBS::frontend-api)
|
||||
endif()
|
||||
include(cmake/AdvSSHelpers.cmake)
|
||||
setup_obs_lib_dependency(${LIB_NAME})
|
||||
setup_obs_lib_dependency(${PROJECT_NAME})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${LIB_NAME})
|
||||
|
||||
find_qt(COMPONENTS Widgets Core)
|
||||
target_link_libraries(${LIB_NAME} PUBLIC Qt::Core Qt::Widgets)
|
||||
|
||||
include(cmake/AdvSSHelpers.cmake)
|
||||
|
||||
# --- Platform-independent build settings ---
|
||||
|
||||
target_include_directories(
|
||||
@@ -357,9 +363,10 @@ elseif(OS_MACOS)
|
||||
|
||||
# --- Linux-specific build settings and tasks ---
|
||||
else()
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH "$ORIGIN")
|
||||
target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra)
|
||||
|
||||
set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")
|
||||
set_target_properties(${LIB_NAME} PROPERTIES SOVERSION 1)
|
||||
|
||||
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
|
||||
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
|
||||
@@ -392,31 +399,26 @@ endif()
|
||||
# --- End of section ---
|
||||
|
||||
target_link_libraries(${LIB_NAME} PUBLIC ${${PROJECT_NAME}_PLATFORM_LIBS})
|
||||
setup_plugin_target(${PROJECT_NAME})
|
||||
install_advss_lib(${LIB_NAME})
|
||||
|
||||
# --- Helpers for debian package creation ---
|
||||
|
||||
if(DEB_INSTALL)
|
||||
# Additional commands to install the module in the correct place. Find all the
|
||||
# translation files so we can copy them to the correct place later on.
|
||||
file(GLOB ASS_TRANSLATION_FILES "data/locale/*.ini")
|
||||
|
||||
# Linux
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(NOT LIB_OUT_DIR)
|
||||
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||
endif()
|
||||
if(NOT DATA_OUT_DIR)
|
||||
set(DATA_OUT_DIR "/share/obs/obs-plugins/${PROJECT_NAME}")
|
||||
endif()
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR})
|
||||
install(DIRECTORY data/locale
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/${DATA_OUT_DIR})
|
||||
install(DIRECTORY data/res
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/${DATA_OUT_DIR})
|
||||
if(NOT LIB_OUT_DIR)
|
||||
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||
endif()
|
||||
if(NOT DATA_OUT_DIR)
|
||||
set(DATA_OUT_DIR "/share/obs/obs-plugins/${PROJECT_NAME}")
|
||||
endif()
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR})
|
||||
install(TARGETS ${LIB_NAME}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR})
|
||||
install(DIRECTORY data/locale
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/${DATA_OUT_DIR})
|
||||
install(DIRECTORY data/res
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/${DATA_OUT_DIR})
|
||||
else()
|
||||
setup_plugin_target(${PROJECT_NAME})
|
||||
install_advss_lib(${LIB_NAME})
|
||||
endif()
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
@@ -26,6 +26,7 @@ If `apt` is supported on your system they can be installed using:
|
||||
sudo apt-get install \
|
||||
libxtst-dev \
|
||||
libxss-dev \
|
||||
libprocps-dev \
|
||||
libopencv-dev
|
||||
```
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"obs-studio": {
|
||||
"version": "28.0.0-beta1",
|
||||
"version": "28.0.1",
|
||||
"repository": "https://github.com/obsproject/obs-studio.git",
|
||||
"branch": "master",
|
||||
"hash": "43a49dca47344a5170159ef99b86b97f90d4e4ad"
|
||||
"hash": "e8dc70d0eef4503378d6ac300e680215eb5c9379"
|
||||
},
|
||||
"prebuilt": {
|
||||
"version": "2022-08-02",
|
||||
|
||||
@@ -67,18 +67,27 @@ if(OS_MACOS)
|
||||
else()
|
||||
# --- Windows / Linux section ---
|
||||
function(plugin_install_helper what where where_deb)
|
||||
install(
|
||||
TARGETS ${what}
|
||||
RUNTIME DESTINATION "${where}" COMPONENT ${what}_Runtime
|
||||
LIBRARY DESTINATION "${where}"
|
||||
COMPONENT ${what}_Runtime
|
||||
NAMELINK_COMPONENT ${what}_Development)
|
||||
install(
|
||||
FILES $<TARGET_FILE:${what}>
|
||||
DESTINATION $<CONFIG>/${where}
|
||||
COMPONENT ${what}_rundir
|
||||
EXCLUDE_FROM_ALL)
|
||||
if(DEB_INSTALL)
|
||||
if(NOT LIB_OUT_DIR)
|
||||
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||
endif()
|
||||
install(
|
||||
TARGETS ${what}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR}/${where_deb})
|
||||
else()
|
||||
install(
|
||||
TARGETS ${what}
|
||||
RUNTIME DESTINATION "${where}" COMPONENT ${what}_Runtime
|
||||
LIBRARY DESTINATION "${where}"
|
||||
COMPONENT ${what}_Runtime
|
||||
NAMELINK_COMPONENT ${what}_Development)
|
||||
|
||||
install(
|
||||
FILES $<TARGET_FILE:${what}>
|
||||
DESTINATION $<CONFIG>/${where}
|
||||
COMPONENT ${what}_rundir
|
||||
EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
if(OS_WINDOWS)
|
||||
install(
|
||||
FILES $<TARGET_PDB_FILE:${what}>
|
||||
@@ -93,7 +102,6 @@ else()
|
||||
COMPONENT ${what}_rundir
|
||||
OPTIONAL EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${what}
|
||||
POST_BUILD
|
||||
@@ -104,25 +112,23 @@ else()
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake_install.cmake
|
||||
COMMENT "Installing ${what} to plugin rundir ${OBS_OUTPUT_DIR}/${where}\n"
|
||||
VERBATIM)
|
||||
|
||||
if(OS_POSIX AND DEB_INSTALL)
|
||||
if(NOT LIB_OUT_DIR)
|
||||
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||
endif()
|
||||
install(
|
||||
TARGETS ${what}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR}/${where_deb})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(install_advss_lib target)
|
||||
plugin_install_helper("${target}" "${OBS_PLUGIN_DESTINATION}" "")
|
||||
if(OS_POSIX)
|
||||
set_target_properties(${target} PROPERTIES INSTALL_RPATH "$ORIGIN")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(install_advss_plugin target)
|
||||
plugin_install_helper(
|
||||
"${target}" "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
"${_PLUGIN_FOLDER}")
|
||||
if(OS_POSIX)
|
||||
set_target_properties(${target} PROPERTIES INSTALL_RPATH
|
||||
"$ORIGIN:$ORIGIN/..")
|
||||
endif()
|
||||
message(STATUS "ADVSS: ENABLED PLUGIN ${target}")
|
||||
endfunction()
|
||||
|
||||
@@ -205,14 +211,50 @@ else()
|
||||
endif()
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
function(setup_advss_plugin target)
|
||||
function(setup_obs_lib_dependency target)
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
target_link_libraries(${target} PUBLIC OBS::libobs OBS::obs-frontend-api)
|
||||
find_package(libobs)
|
||||
if(libobs_FOUND)
|
||||
target_link_libraries(${target} PUBLIC OBS::libobs)
|
||||
else()
|
||||
if(NOT LIBOBS_LIB)
|
||||
message(FATAL_ERROR "obs library not found - please set LIBOBS_LIB")
|
||||
endif()
|
||||
target_link_libraries(${target} PUBLIC ${LIBOBS_LIB})
|
||||
if(NOT LIBOBS_INCLUDE_DIR)
|
||||
message(
|
||||
FATAL_ERROR "obs.hpp header not found - please set LIBOBS_INCLUDE_DIR"
|
||||
)
|
||||
endif()
|
||||
target_include_directories(${target} PRIVATE ${LIBOBS_INCLUDE_DIR})
|
||||
endif()
|
||||
find_package(obs-frontend-api)
|
||||
if(obs-frontend-api_FOUND)
|
||||
target_link_libraries(${target} PUBLIC OBS::obs-frontend-api)
|
||||
else()
|
||||
if(NOT LIBOBS_FRONTEND_API_LIB)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"libobs frontend-api library not found - please set LIBOBS_FRONTEND_API_LIB"
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(${target} PUBLIC ${LIBOBS_FRONTEND_API_LIB})
|
||||
if(NOT LIBOBS_FRONTEND_INCLUDE_DIR)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
" obs-frontend-api.h not found - please set LIBOBS_FRONTEND_INCLUDE_DIR"
|
||||
)
|
||||
endif()
|
||||
target_include_directories(${target}
|
||||
PRIVATE ${LIBOBS_FRONTEND_INCLUDE_DIR})
|
||||
endif()
|
||||
else()
|
||||
target_link_libraries(${target} PUBLIC OBS::libobs OBS::frontend-api)
|
||||
endif()
|
||||
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)
|
||||
|
||||
|
||||
@@ -346,13 +346,18 @@ else()
|
||||
set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${LINUX_MAINTAINER_EMAIL}")
|
||||
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}")
|
||||
set(CPACK_PACKAGE_FILE_NAME
|
||||
"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-linux-x86_64")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-linux-x86_64")
|
||||
|
||||
set(CPACK_GENERATOR "DEB")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS
|
||||
"obs-studio (>= 27.0.0), libqt5core5a (>= 5.9.0~beta), libqt5gui5 (>= 5.3.0), libqt5widgets5 (>= 5.7.0)"
|
||||
)
|
||||
if(${QT_VERSION} STREQUAL "6")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS
|
||||
"obs-studio (>= 28.0.0), libqt6core6 (>= 6.2.0), libqt6gui6 (>= 6.1.2), libqt6widgets6 (>= 6.1.2)"
|
||||
)
|
||||
else()
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS
|
||||
"obs-studio (>= 28.0.0), libqt5core5a (>= 5.9.0~beta), libqt5gui5 (>= 5.3.0), libqt5widgets5 (>= 5.7.0)"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(CPACK_OUTPUT_FILE_PREFIX ${CMAKE_SOURCE_DIR}/release)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define MyAppName "@CMAKE_PROJECT_NAME@"
|
||||
#define MyAppVersion "@CMAKE_PROJECT_VERSION@"
|
||||
#define MyAppVersion "@GIT_TAG@"
|
||||
#define MyAppPublisher "@PLUGIN_AUTHOR@"
|
||||
#define MyAppURL "http://www.mywebsite.com"
|
||||
|
||||
|
||||
@@ -51,13 +51,13 @@ AdvSceneSwitcher.generalTab.priority.media="Medien"
|
||||
AdvSceneSwitcher.generalTab.priority.time="Zeit"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Verwende Übergangsüberschreibungen"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Verwende Übergangsüberschreibungen"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang, vor denen welche auf den übrigen Tabs konfiguriert wurden.<br/><br/>Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist"
|
||||
|
||||
@@ -55,6 +55,11 @@ AdvSceneSwitcher.generalTab.priority.time="Time"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Macro"
|
||||
AdvSceneSwitcher.generalTab.transition="Transitions"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="When changing transitions:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Set transition overrides"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
@@ -83,7 +88,8 @@ AdvSceneSwitcher.macroTab.minimize="Minimize"
|
||||
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
|
||||
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
|
||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
|
||||
AdvSceneSwitcher.macroTab.disableHotkeys="Register hotkeys to control pause state of selected macro"
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control pause state of new macros"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control pause state of selected macro"
|
||||
|
||||
; Macro List
|
||||
AdvSceneSwitcher.macroList.deleted="deleted"
|
||||
@@ -127,8 +133,12 @@ AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
|
||||
AdvSceneSwitcher.condition.window="Window"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
|
||||
AdvSceneSwitcher.condition.window.entry.line3="Current foreground window: {{focusWindow}}"
|
||||
AdvSceneSwitcher.condition.file="File"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Content of {{fileType}} {{filePath}} matches:"
|
||||
AdvSceneSwitcher.condition.file.type.match="matches"
|
||||
AdvSceneSwitcher.condition.file.type.contentChange="content changed"
|
||||
AdvSceneSwitcher.condition.file.type.dateChange="modification date changed"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Content of{{fileType}}{{filePath}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Media"
|
||||
@@ -145,14 +155,20 @@ AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed"
|
||||
AdvSceneSwitcher.condition.video.condition.noImage="has no output"
|
||||
AdvSceneSwitcher.condition.video.condition.pattern="matches pattern"
|
||||
AdvSceneSwitcher.condition.video.condition.object="contains object"
|
||||
AdvSceneSwitcher.condition.video.condition.brightness="brightness"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
||||
AdvSceneSwitcher.condition.video.reduceLatency="Reduce matching latency"
|
||||
AdvSceneSwitcher.condition.video.reduceLatency.tooltip="Enabling will reduce the matching latency, but will slow down the condition checks of all macros overall."
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="Use pattern matching"
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="This will allow you to control how much the image has to change for the condition to be true."
|
||||
AdvSceneSwitcher.condition.video.patternThreshold="Threshold: "
|
||||
AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely."
|
||||
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Use alpha channel as mask for pattern."
|
||||
AdvSceneSwitcher.condition.video.brightnessThreshold="Average brightness:"
|
||||
AdvSceneSwitcher.condition.video.brightnessThresholdDescription="A high value is indicating a bright image and a low one a darker one."
|
||||
AdvSceneSwitcher.condition.video.currentBrightness="Current average brightness: %1"
|
||||
AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
|
||||
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
|
||||
AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches."
|
||||
@@ -169,7 +185,8 @@ AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePa
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU load by performing check only every {{throttleCount}} milliseconds"
|
||||
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}Perform check only in area {{checkArea}} {{selectArea}}"
|
||||
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Perform check only in area"
|
||||
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
|
||||
AdvSceneSwitcher.condition.video.minSize="Minimum size:"
|
||||
AdvSceneSwitcher.condition.video.maxSize="Maximum size:"
|
||||
AdvSceneSwitcher.condition.video.selectArea="Select area"
|
||||
@@ -189,6 +206,7 @@ AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
|
||||
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
|
||||
AdvSceneSwitcher.condition.process="Process"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="Current foreground process: {{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="Idle"
|
||||
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Plugin state"
|
||||
@@ -227,7 +245,6 @@ AdvSceneSwitcher.condition.source="Source"
|
||||
AdvSceneSwitcher.condition.source.type.active="Is active"
|
||||
AdvSceneSwitcher.condition.source.type.showing="Is showing"
|
||||
AdvSceneSwitcher.condition.source.type.settings="Settings match"
|
||||
AdvSceneSwitcher.condition.source.regex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
@@ -240,7 +257,6 @@ AdvSceneSwitcher.condition.filter="Filter"
|
||||
AdvSceneSwitcher.condition.filter.type.active="Is enabled"
|
||||
AdvSceneSwitcher.condition.filter.type.showing="Is disabled"
|
||||
AdvSceneSwitcher.condition.filter.type.settings="Settings match"
|
||||
AdvSceneSwitcher.condition.filter.regex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.filter.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.condition.filter.entry.line1="On {{sources}} {{filters}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||
@@ -286,7 +302,6 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
|
||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} On repeat update selected date to repeat date"
|
||||
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
||||
AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="On{{scenes}}{{sources}}matches transform"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
@@ -335,12 +350,25 @@ AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
|
||||
AdvSceneSwitcher.condition.profile="Profile"
|
||||
AdvSceneSwitcher.condition.profile.entry="Current active profile is {{profiles}}"
|
||||
AdvSceneSwitcher.condition.websocket="Websocket"
|
||||
AdvSceneSwitcher.condition.websocket.type.request="Request"
|
||||
AdvSceneSwitcher.condition.websocket.type.event="Event"
|
||||
AdvSceneSwitcher.condition.websocket.useRegex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.websocket.entry="Message was received:"
|
||||
AdvSceneSwitcher.condition.websocket.entry.request="{{type}} was received:"
|
||||
AdvSceneSwitcher.condition.websocket.entry.event="{{type}} was received from {{connection}}:"
|
||||
AdvSceneSwitcher.condition.variable="Variable"
|
||||
AdvSceneSwitcher.condition.variable.type.compare="equals"
|
||||
AdvSceneSwitcher.condition.variable.type.empty="is empty"
|
||||
AdvSceneSwitcher.condition.variable.type.number="is a number"
|
||||
AdvSceneSwitcher.condition.variable.type.lessThan="is less than"
|
||||
AdvSceneSwitcher.condition.variable.type.greaterThan="is greater than"
|
||||
AdvSceneSwitcher.condition.variable.type.valueChanged="value changed"
|
||||
AdvSceneSwitcher.condition.variable.regex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
||||
AdvSceneSwitcher.action.scene.entry="Switch to scene {{scenes}} using {{transitions}} with a duration of {{duration}} seconds"
|
||||
AdvSceneSwitcher.action.scene.entry="Switch to scene{{scenes}}using{{transitions}}with a duration of{{duration}}seconds"
|
||||
AdvSceneSwitcher.action.scene.entry.noDuration="Switch to scene{{scenes}}using{{transitions}}"
|
||||
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Wait until transition to target scene is complete"
|
||||
AdvSceneSwitcher.action.wait="Wait"
|
||||
AdvSceneSwitcher.action.wait.type.fixed="fixed"
|
||||
@@ -481,8 +509,13 @@ AdvSceneSwitcher.action.random.entry="Randomly run any of the following macros (
|
||||
AdvSceneSwitcher.action.systray="System tray notification"
|
||||
AdvSceneSwitcher.action.systray.entry="Show notification: {{message}}"
|
||||
AdvSceneSwitcher.action.screenshot="Screenshot"
|
||||
AdvSceneSwitcher.action.screenshot.save.default="Default"
|
||||
AdvSceneSwitcher.action.screenshot.save.custom="Custom"
|
||||
AdvSceneSwitcher.action.screenshot.type.source="Source"
|
||||
AdvSceneSwitcher.action.screenshot.type.scene="Scene"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's main output"
|
||||
AdvSceneSwitcher.action.screenshot.entry="Screenshot {{sources}}"
|
||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="Sources or scenes, which are not always rendered, may result in some parts of screenshots to remain blank."
|
||||
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}and save to{{saveType}}location"
|
||||
AdvSceneSwitcher.action.profile="Profile"
|
||||
AdvSceneSwitcher.action.profile.entry="Switch active profile to {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Scene collection"
|
||||
@@ -495,20 +528,26 @@ AdvSceneSwitcher.action.sequence.status.none="none"
|
||||
AdvSceneSwitcher.action.sequence.restart="Restart from beginning once end of list is reached"
|
||||
AdvSceneSwitcher.action.sequence.continueFrom="Continue with selected item"
|
||||
AdvSceneSwitcher.action.websocket="Websocket"
|
||||
AdvSceneSwitcher.action.websocket.entry="Send scene switcher message via {{connection}}"
|
||||
AdvSceneSwitcher.action.websocket.type.request="Request"
|
||||
AdvSceneSwitcher.action.websocket.type.event="Event"
|
||||
AdvSceneSwitcher.action.websocket.entry.request="Send scene switcher {{type}} via {{connection}}"
|
||||
AdvSceneSwitcher.action.websocket.entry.event="Send scene switcher {{type}} to connected clients"
|
||||
AdvSceneSwitcher.action.http="Http"
|
||||
AdvSceneSwitcher.action.http.type.get="GET"
|
||||
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"
|
||||
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.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}"
|
||||
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="When changing transitions:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Set transition overrides"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Change active transition type"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active"
|
||||
@@ -757,15 +796,27 @@ AdvSceneSwitcher.askForMacro="Select macro {{macroSelection}}"
|
||||
AdvSceneSwitcher.close="Close"
|
||||
AdvSceneSwitcher.browse="Browse"
|
||||
|
||||
AdvSceneSwitcher.item.select="--select item--"
|
||||
AdvSceneSwitcher.item.add="Add new item"
|
||||
AdvSceneSwitcher.item.rename="Rename"
|
||||
AdvSceneSwitcher.item.remove="Remove"
|
||||
AdvSceneSwitcher.item.properties="Properties"
|
||||
AdvSceneSwitcher.item.newName="New name:"
|
||||
AdvSceneSwitcher.item.emptyName="Empty name not allowed!"
|
||||
AdvSceneSwitcher.item.nameNotAvailable="Name not available!"
|
||||
AdvSceneSwitcher.item.nameReserved="Name reserved!"
|
||||
|
||||
AdvSceneSwitcher.variable.select="--select variable--"
|
||||
AdvSceneSwitcher.variable.add="Add new variable"
|
||||
AdvSceneSwitcher.variable.name="Name:"
|
||||
AdvSceneSwitcher.variable.value="Current value:"
|
||||
AdvSceneSwitcher.variable.save="Save / load behavior"
|
||||
AdvSceneSwitcher.variable.save.dontSave="Don't save variable value"
|
||||
AdvSceneSwitcher.variable.save.save="Save variable value"
|
||||
AdvSceneSwitcher.variable.save.default="Set to value"
|
||||
|
||||
AdvSceneSwitcher.connection.select="--select connection--"
|
||||
AdvSceneSwitcher.connection.add="Add new connection"
|
||||
AdvSceneSwitcher.connection.rename="Rename"
|
||||
AdvSceneSwitcher.connection.remove="Remove"
|
||||
AdvSceneSwitcher.connection.properties="Properties"
|
||||
AdvSceneSwitcher.connection.newName="New name:"
|
||||
AdvSceneSwitcher.connection.emptyName="Empty name not allowed!"
|
||||
AdvSceneSwitcher.connection.nameNotAvailable="Name not available!"
|
||||
AdvSceneSwitcher.connection.nameReserved="Name reserved!"
|
||||
AdvSceneSwitcher.connection.name="Name:"
|
||||
AdvSceneSwitcher.connection.address="Address:"
|
||||
AdvSceneSwitcher.connection.port="Port:"
|
||||
@@ -779,9 +830,17 @@ AdvSceneSwitcher.connection.status.connecting="Connecting"
|
||||
AdvSceneSwitcher.connection.status.connected="Connected, but not authenticated"
|
||||
AdvSceneSwitcher.connection.status.authenticated="Connected and authenticated"
|
||||
|
||||
AdvSceneSwitcher.regex.enable="Enable regular expressions"
|
||||
AdvSceneSwitcher.regex.partialMatch="Allow partial match"
|
||||
AdvSceneSwitcher.regex.caseInsensitive="Match case insensitive"
|
||||
AdvSceneSwitcher.regex.dotMatchNewline=". matches newlines"
|
||||
AdvSceneSwitcher.regex.multiLine="^ and $ match start/end of line"
|
||||
AdvSceneSwitcher.regex.extendedPattern="Enable Qt's ExtendedPatternSyntax"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
AdvSceneSwitcher.selectPreviewScene="Preview Scene"
|
||||
AdvSceneSwitcher.selectAnyScene="Any Scene"
|
||||
AdvSceneSwitcher.currentTransition="Current Transition"
|
||||
AdvSceneSwitcher.anyTransition="Any Transition"
|
||||
@@ -790,7 +849,7 @@ AdvSceneSwitcher.selectWindow="--select window--"
|
||||
AdvSceneSwitcher.selectSource="--select source--"
|
||||
AdvSceneSwitcher.selectAudioSource="--select audio source--"
|
||||
AdvSceneSwitcher.selectVideoSource="--select video source--"
|
||||
AdvSceneSwitcher.OBSVideoOutput="OBS video output"
|
||||
AdvSceneSwitcher.OBSVideoOutput="OBS's main output"
|
||||
AdvSceneSwitcher.selectMediaSource="--select media source--"
|
||||
AdvSceneSwitcher.selectProcess="--select process--"
|
||||
AdvSceneSwitcher.selectFilter="--select filter--"
|
||||
|
||||
@@ -54,6 +54,10 @@ AdvSceneSwitcher.generalTab.priority.time="Tiempo"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Macro"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="Al cambiar las transiciones:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Establecer anulaciones de transición"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
@@ -123,7 +127,7 @@ AdvSceneSwitcher.condition.window="Ventana"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} existen y ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... es {{fullscreen}} pantalla completa {{maximized}} maximizada {{focused}} enfocada {{windowFocusChanged}} ventana de primer plano cambiada"
|
||||
AdvSceneSwitcher.condition.file="Archivo"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Contenido de {{fileType}} {{filePath}} coincidencias:"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Contenido de{{fileType}}{{filePath}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Medios"
|
||||
@@ -164,6 +168,7 @@ AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePa
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="Mínimo de vecinos: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzca la carga de la CPU realizando una comprobación solo cada {{throttleCount}} milisegundos"
|
||||
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Realizar comprobación solo en el área"
|
||||
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}Realizar comprobación solo en el área {{checkArea}} {{selectArea}}"
|
||||
AdvSceneSwitcher.condition.video.minSize="Tamaño mínimo:"
|
||||
AdvSceneSwitcher.condition.video.maxSize="Tamaño máximo:"
|
||||
@@ -463,7 +468,6 @@ AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
|
||||
AdvSceneSwitcher.action.systray.entry="Mostrar notificación: {{message}}"
|
||||
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
|
||||
AdvSceneSwitcher.action.screenshot.entry="Captura de pantalla {{sources}}"
|
||||
AdvSceneSwitcher.action.profile="Perfil"
|
||||
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
|
||||
@@ -478,10 +482,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="Continuar con el elemento selecci
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transición"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="Al cambiar las transiciones:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Establecer anulaciones de transición"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliza la transición para el cambio de escena automatizado de la escena A a la escena B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body> <p> Estos ajustes <span style =\"font-style: italic; \"> solo </span> surgió a las transiciones causadas por el selector de escenas - Echa un vistazo a <a href =\"https://obsproject.com/forum/resources/transition-table.1174/\"> <span style=\" texto-decoración: subrayado; color: # 268bd2; \"> Tabla de transición </span> </a> si desea configurar esto para cambios de escena manuales. <br/> Los definiciones definidos aquí tienen prioridad sobre los ajustes de transición configurados en cualquier otro lugar del selector de escenas. < br/> <br/> Haz clic en el símbolo más a continuación para agregar una nueva entrada. </p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Cambiar transición si la escena está activa"
|
||||
|
||||
@@ -52,6 +52,10 @@ AdvSceneSwitcher.generalTab.priority.time="Время"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Аудио"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Видео"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Макрос"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="При изменении переходов:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Установить переопределение переходов"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Изменить тип активного перехода"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Макрос"
|
||||
@@ -147,10 +151,6 @@ AdvSceneSwitcher.action.run="Запустить"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Переход"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="При изменении переходов:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Установить переопределение переходов"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Изменить тип активного перехода"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Использовать переход для автоматического переключения сцены со сцены A на сцену B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Эти настройки <span style=\"font-style:italic;\">только</span> влияют на переходы, вызванные переключателем сцен - Проверьте <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> если вы хотите настроить его для ручного изменения сцены.<br/>Настройки, определенные здесь, имеют приоритет над настройками перехода, сконфигурированными в других местах переключателя сцен.<br/><br/>Нажмите на символ плюса ниже, чтобы добавить новую запись.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Изменить переход, если сцена активна"
|
||||
|
||||
@@ -53,6 +53,10 @@ AdvSceneSwitcher.generalTab.priority.time="Zaman"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Ses"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Makro"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="Geçişleri değiştirirken:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Makro"
|
||||
@@ -111,7 +115,7 @@ AdvSceneSwitcher.condition.window="Pencere"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} Varolan ve ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... {{fullscreen}} Tam ekran {{maximized}} büyütüldü {{focused}} odaklanıldı {{windowFocusChanged}} ön plan penceresi değişikliği"
|
||||
AdvSceneSwitcher.condition.file="Dosya"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="İçerik {{fileType}} {{filePath}} eşleştirmeler:"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="İçerik{{fileType}}{{filePath}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Medya"
|
||||
@@ -383,7 +387,6 @@ AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
|
||||
AdvSceneSwitcher.action.systray.entry="Bildirimleri göster: {{message}}"
|
||||
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
|
||||
AdvSceneSwitcher.action.screenshot.entry="Ekran görüntüsü {{sources}}"
|
||||
AdvSceneSwitcher.action.profile="Profil"
|
||||
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
|
||||
@@ -392,10 +395,6 @@ AdvSceneSwitcher.action.sceneCollection.warning="Not: Değişen sahne koleksiyon
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Geçiş"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="Geçişleri değiştirirken:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Sahne A'dan sahne B'ye otomatik sahne geçişi için geçişi kullanın"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Bu ayarlar <span style=\"font-style:italic;\">yalnızca</span>, sahne değiştiricinin neden olduğu geçişleri etkiler - <a href=\ Göz atın "https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Geçiş Tablosu</span></ a> bunu manuel sahne değişiklikleri için yapılandırmak istiyorsanız.<br/>Burada tanımlanan ayarlar, sahne değiştiricide başka bir yerde yapılandırılmış geçiş ayarlarına göre önceliklidir.<br/><br/>Yeni bir giriş eklemek için aşağıdaki artı simgesini tıklayın. .</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Sahne aktifse geçişi değiştir"
|
||||
|
||||
@@ -54,6 +54,9 @@ AdvSceneSwitcher.generalTab.priority.time="时间"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="音频"
|
||||
AdvSceneSwitcher.generalTab.priority.video="视频"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="宏"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="更改转场特效时:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="更改激活转场特效类型"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="宏"
|
||||
@@ -116,7 +119,6 @@ AdvSceneSwitcher.condition.window="窗口"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} 存在..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... 并且是 {{fullscreen}} 全屏 {{maximized}} 最大化 {{focused}} 获得焦点 {{windowFocusChanged}} 焦点窗口已更改"
|
||||
AdvSceneSwitcher.condition.file="文件"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="内容 {{fileType}} {{filePath}} (本地是文本,网络是获取的网页)完全匹配:"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="媒体"
|
||||
@@ -424,7 +426,6 @@ AdvSceneSwitcher.action.systray="系统托盘通知"
|
||||
AdvSceneSwitcher.action.systray.entry="显示通知: {{message}}"
|
||||
AdvSceneSwitcher.action.screenshot="截图"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS 预览画面"
|
||||
AdvSceneSwitcher.action.screenshot.entry="截图 {{sources}}"
|
||||
AdvSceneSwitcher.action.profile="配置文件"
|
||||
AdvSceneSwitcher.action.profile.entry="将活动配置文件切换到 {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="场景集合"
|
||||
@@ -439,9 +440,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="继续所选项目"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="转场特效"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="更改转场特效时:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="更改激活转场特效类型"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>单击加号添加项目.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效"
|
||||
|
||||
1
deps/obs-websocket
vendored
1
deps/obs-websocket
vendored
Submodule deps/obs-websocket deleted from a25427c7cc
216
deps/obs-websocket/lib/obs-websocket-api.h
vendored
Normal file
216
deps/obs-websocket/lib/obs-websocket-api.h
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
obs-websocket
|
||||
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
|
||||
Copyright (C) 2020-2022 Kyle Manning <tt2468@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _OBS_WEBSOCKET_API_H
|
||||
#define _OBS_WEBSOCKET_API_H
|
||||
|
||||
#include <obs.h>
|
||||
|
||||
#define OBS_WEBSOCKET_API_VERSION 2
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void *obs_websocket_vendor;
|
||||
typedef void (*obs_websocket_request_callback_function)(obs_data_t *, obs_data_t *, void *);
|
||||
|
||||
struct obs_websocket_request_response {
|
||||
unsigned int status_code;
|
||||
char *comment;
|
||||
char *response_data; // JSON string, because obs_data_t* only supports array<object>, so conversions would break API.
|
||||
};
|
||||
|
||||
/* ==================== INTERNAL DEFINITIONS ==================== */
|
||||
|
||||
struct obs_websocket_request_callback {
|
||||
obs_websocket_request_callback_function callback;
|
||||
void *priv_data;
|
||||
};
|
||||
|
||||
inline proc_handler_t *_ph;
|
||||
|
||||
/* ==================== INTERNAL API FUNCTIONS ==================== */
|
||||
|
||||
static inline proc_handler_t *obs_websocket_get_ph(void)
|
||||
{
|
||||
proc_handler_t *global_ph = obs_get_proc_handler();
|
||||
assert(global_ph != NULL);
|
||||
|
||||
calldata_t cd = {0};
|
||||
if (!proc_handler_call(global_ph, "obs_websocket_api_get_ph", &cd))
|
||||
blog(LOG_DEBUG, "Unable to fetch obs-websocket proc handler object. obs-websocket not installed?");
|
||||
proc_handler_t *ret = (proc_handler_t *)calldata_ptr(&cd, "ph");
|
||||
calldata_free(&cd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool obs_websocket_ensure_ph(void)
|
||||
{
|
||||
if (!_ph)
|
||||
_ph = obs_websocket_get_ph();
|
||||
return _ph != NULL;
|
||||
}
|
||||
|
||||
static inline bool obs_websocket_vendor_run_simple_proc(obs_websocket_vendor vendor, const char *proc_name, calldata_t *cd)
|
||||
{
|
||||
if (!obs_websocket_ensure_ph())
|
||||
return false;
|
||||
|
||||
if (!vendor || !proc_name || !strlen(proc_name) || !cd)
|
||||
return false;
|
||||
|
||||
calldata_set_ptr(cd, "vendor", vendor);
|
||||
|
||||
proc_handler_call(_ph, proc_name, cd);
|
||||
return calldata_bool(cd, "success");
|
||||
}
|
||||
|
||||
/* ==================== GENERAL API FUNCTIONS ==================== */
|
||||
|
||||
// Gets the API version built with the obs-websocket plugin
|
||||
static inline unsigned int obs_websocket_get_api_version(void)
|
||||
{
|
||||
if (!obs_websocket_ensure_ph())
|
||||
return 0;
|
||||
|
||||
calldata_t cd = {0};
|
||||
|
||||
if (!proc_handler_call(_ph, "get_api_version", &cd))
|
||||
return 1; // API v1 does not include get_api_version
|
||||
|
||||
unsigned int ret = calldata_int(&cd, "version");
|
||||
|
||||
calldata_free(&cd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Calls an obs-websocket request. Free response with `obs_websocket_request_response_free()`
|
||||
static inline obs_websocket_request_response *obs_websocket_call_request(const char *request_type, obs_data_t *request_data = NULL)
|
||||
{
|
||||
if (!obs_websocket_ensure_ph())
|
||||
return NULL;
|
||||
|
||||
const char *request_data_string = NULL;
|
||||
if (request_data)
|
||||
request_data_string = obs_data_get_json(request_data);
|
||||
|
||||
calldata_t cd = {0};
|
||||
|
||||
calldata_set_string(&cd, "request_type", request_type);
|
||||
calldata_set_string(&cd, "request_data", request_data_string);
|
||||
|
||||
proc_handler_call(_ph, "call_request", &cd);
|
||||
|
||||
auto ret = (struct obs_websocket_request_response *)calldata_ptr(&cd, "response");
|
||||
|
||||
calldata_free(&cd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Free a request response object returned by `obs_websocket_call_request()`
|
||||
static inline void obs_websocket_request_response_free(struct obs_websocket_request_response *response)
|
||||
{
|
||||
if (!response)
|
||||
return;
|
||||
|
||||
if (response->comment)
|
||||
bfree(response->comment);
|
||||
if (response->response_data)
|
||||
bfree(response->response_data);
|
||||
bfree(response);
|
||||
}
|
||||
|
||||
/* ==================== VENDOR API FUNCTIONS ==================== */
|
||||
|
||||
// ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
|
||||
// Registers a new "vendor" (Example: obs-ndi)
|
||||
static inline obs_websocket_vendor obs_websocket_register_vendor(const char *vendor_name)
|
||||
{
|
||||
if (!obs_websocket_ensure_ph())
|
||||
return NULL;
|
||||
|
||||
calldata_t cd = {0};
|
||||
|
||||
calldata_set_string(&cd, "name", vendor_name);
|
||||
|
||||
proc_handler_call(_ph, "vendor_register", &cd);
|
||||
obs_websocket_vendor ret = calldata_ptr(&cd, "vendor");
|
||||
calldata_free(&cd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Registers a new request for a vendor
|
||||
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type,
|
||||
obs_websocket_request_callback_function request_callback, void *priv_data)
|
||||
{
|
||||
calldata_t cd = {0};
|
||||
|
||||
struct obs_websocket_request_callback cb = {};
|
||||
cb.callback = request_callback;
|
||||
cb.priv_data = priv_data;
|
||||
|
||||
calldata_set_string(&cd, "type", request_type);
|
||||
calldata_set_ptr(&cd, "callback", &cb);
|
||||
|
||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_register", &cd);
|
||||
calldata_free(&cd);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Unregisters an existing vendor request
|
||||
static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor, const char *request_type)
|
||||
{
|
||||
calldata_t cd = {0};
|
||||
|
||||
calldata_set_string(&cd, "type", request_type);
|
||||
|
||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_unregister", &cd);
|
||||
calldata_free(&cd);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Does not affect event_data refcount.
|
||||
// Emits an event under the vendor's name
|
||||
static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor, const char *event_name, obs_data_t *event_data)
|
||||
{
|
||||
calldata_t cd = {0};
|
||||
|
||||
calldata_set_string(&cd, "type", event_name);
|
||||
calldata_set_ptr(&cd, "data", (void *)event_data);
|
||||
|
||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_event_emit", &cd);
|
||||
calldata_free(&cd);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/* ==================== END API FUNCTIONS ==================== */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -68,7 +68,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>957</width>
|
||||
<height>817</height>
|
||||
<height>905</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||
@@ -419,6 +419,49 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="transitionBox">
|
||||
<property name="title">
|
||||
<string>AdvSceneSwitcher.generalTab.transition</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="transitionLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="adjustActiveTransitionType">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.adjustActiveTransitionType</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.setTransitionBy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="transitionOverridecheckBox">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.transitionOverride</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="priorityBox">
|
||||
<property name="title">
|
||||
@@ -610,7 +653,7 @@
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
@@ -642,7 +685,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -658,7 +701,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -713,17 +756,14 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string>upArrowIconSmall</string>
|
||||
<string notr="true">upArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -732,17 +772,14 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string>downArrowIconSmall</string>
|
||||
<string notr="true">downArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -769,7 +806,7 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_33">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0,0,0">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
@@ -789,16 +826,36 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runMacroInParallel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.runInParallel</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runMacroOnChange">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.onChange</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -819,7 +876,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
@@ -864,7 +921,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -880,7 +937,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -935,7 +992,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
@@ -954,7 +1011,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -1006,7 +1063,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -1022,7 +1079,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -1077,7 +1134,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -1093,7 +1150,7 @@
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
@@ -1716,44 +1773,6 @@
|
||||
<string>AdvSceneSwitcher.transitionTab.title</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_15">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="adjustActiveTransitionType">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.transitionTab.adjustActiveTransitionType</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.transitionTab.setTransitionBy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="transitionOverridecheckBox">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.transitionTab.transitionOverride</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="transitionsGroup">
|
||||
<property name="title">
|
||||
|
||||
@@ -227,7 +227,7 @@ void SwitcherData::Thread()
|
||||
}
|
||||
}
|
||||
|
||||
websocketMessages.clear();
|
||||
ClearWebsocketMessages();
|
||||
|
||||
// After this point we will call frontend functions like
|
||||
// obs_frontend_set_current_scene() and
|
||||
@@ -280,6 +280,9 @@ void SwitcherData::setPreconditions()
|
||||
}
|
||||
currentTitle = title;
|
||||
|
||||
// Process name
|
||||
GetForegroundProcessName(currentForegroundProcess);
|
||||
|
||||
// Cursor
|
||||
std::pair<int, int> cursorPos = getCursorPos();
|
||||
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||
|
||||
@@ -629,9 +632,11 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
|
||||
resetLiveTime();
|
||||
break;
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
|
||||
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
|
||||
setReplayBufferSaved();
|
||||
break;
|
||||
#endif
|
||||
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
||||
setTranstionEnd();
|
||||
break;
|
||||
|
||||
@@ -95,6 +95,9 @@ signals:
|
||||
void ConnectionAdded(const QString &);
|
||||
void ConnectionRenamed(const QString &oldName, const QString &newName);
|
||||
void ConnectionRemoved(const QString &);
|
||||
void VariableAdded(const QString &);
|
||||
void VariableRenamed(const QString &oldName, const QString &newName);
|
||||
void VariableRemoved(const QString &);
|
||||
|
||||
public slots:
|
||||
void on_windowUp_clicked();
|
||||
|
||||
@@ -192,7 +192,9 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
||||
|
||||
bool isLegacyTab(const QString &name)
|
||||
{
|
||||
return name == obs_module_text("AdvSceneSwitcher.networkTab.title") ||
|
||||
return name == obs_module_text(
|
||||
"AdvSceneSwitcher.sceneGroupTab.title") ||
|
||||
name == obs_module_text("AdvSceneSwitcher.networkTab.title") ||
|
||||
name == obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.title") ||
|
||||
name == obs_module_text(
|
||||
@@ -221,13 +223,13 @@ void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
|
||||
|
||||
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
|
||||
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
ui->tabWidget->setTabVisible(idx, !state);
|
||||
#else
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
|
||||
ui->tabWidget->setTabEnabled(idx, !state);
|
||||
ui->tabWidget->setStyleSheet(
|
||||
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
|
||||
#else
|
||||
ui->tabWidget->setTabVisible(idx, !state);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -494,6 +496,39 @@ void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
|
||||
SetShowFrames();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state && !switcher->adjustActiveTransitionType) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError"));
|
||||
ui->adjustActiveTransitionType->setChecked(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->transitionOverrideOverride = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This option only makes sense if we are allowed to use transition overrides
|
||||
if (!state && !switcher->transitionOverrideOverride) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError"));
|
||||
ui->transitionOverridecheckBox->setChecked(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->adjustActiveTransitionType = state;
|
||||
}
|
||||
|
||||
void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
{
|
||||
if (!obj) {
|
||||
@@ -503,6 +538,7 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
// Needs to be loaded before any entries which might rely on scene group
|
||||
// selections to be available.
|
||||
loadSceneGroups(obj);
|
||||
loadVariables(obj);
|
||||
loadMacros(obj);
|
||||
loadConnections(obj);
|
||||
loadWindowTitleSwitches(obj);
|
||||
@@ -537,6 +573,7 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
saveSceneGroups(obj);
|
||||
saveMacros(obj);
|
||||
saveConnections(obj);
|
||||
saveVariables(obj);
|
||||
saveWindowTitleSwitches(obj);
|
||||
saveScreenRegionSwitches(obj);
|
||||
savePauseSwitches(obj);
|
||||
@@ -595,6 +632,13 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
||||
obs_data_set_int(obj, "priority10", functionNamesByPriority[10]);
|
||||
|
||||
obs_data_set_int(obj, "threadPriority", threadPriority);
|
||||
|
||||
obs_data_set_bool(obj, "tansitionOverrideOverride",
|
||||
transitionOverrideOverride);
|
||||
obs_data_set_default_bool(obj, "adjustActiveTransitionType",
|
||||
adjustActiveTransitionType);
|
||||
obs_data_set_bool(obj, "adjustActiveTransitionType",
|
||||
adjustActiveTransitionType);
|
||||
}
|
||||
|
||||
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
@@ -670,6 +714,11 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
obs_data_set_default_int(obj, "threadPriority",
|
||||
QThread::NormalPriority);
|
||||
threadPriority = obs_data_get_int(obj, "threadPriority");
|
||||
|
||||
transitionOverrideOverride =
|
||||
obs_data_get_bool(obj, "tansitionOverrideOverride");
|
||||
adjustActiveTransitionType =
|
||||
obs_data_get_bool(obj, "adjustActiveTransitionType");
|
||||
}
|
||||
|
||||
void saveSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)
|
||||
@@ -687,6 +736,7 @@ void saveSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)
|
||||
|
||||
void loadSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)
|
||||
{
|
||||
sizes.clear();
|
||||
obs_data_array_t *array = obs_data_get_array(obj, name.c_str());
|
||||
size_t count = obs_data_array_count(array);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
|
||||
@@ -185,18 +185,22 @@ void frontEndActionThread(sceneTriggerAction action, double delay)
|
||||
case sceneTriggerAction::STOP_STREAMING:
|
||||
obs_frontend_streaming_stop();
|
||||
break;
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
|
||||
case sceneTriggerAction::START_REPLAY_BUFFER:
|
||||
obs_frontend_replay_buffer_start();
|
||||
break;
|
||||
case sceneTriggerAction::STOP_REPLAY_BUFFER:
|
||||
obs_frontend_replay_buffer_stop();
|
||||
break;
|
||||
#endif
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||
case sceneTriggerAction::START_VCAM:
|
||||
obs_frontend_start_virtualcam();
|
||||
break;
|
||||
case sceneTriggerAction::STOP_VCAM:
|
||||
obs_frontend_stop_virtualcam();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
blog(LOG_WARNING, "ignoring unexpected frontend action '%d'",
|
||||
static_cast<int>(action));
|
||||
|
||||
@@ -169,39 +169,6 @@ void SwitcherData::checkDefaultSceneTransitions()
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state && !switcher->adjustActiveTransitionType) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
|
||||
ui->adjustActiveTransitionType->setChecked(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->transitionOverrideOverride = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This option only makes sense if we are allowed to use transition overrides
|
||||
if (!state && !switcher->transitionOverrideOverride) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
|
||||
ui->transitionOverridecheckBox->setChecked(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->adjustActiveTransitionType = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::defTransitionDelayValueChanged(int value)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -335,13 +302,6 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
|
||||
}
|
||||
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
|
||||
obs_data_array_release(defaultTransitionsArray);
|
||||
|
||||
obs_data_set_bool(obj, "tansitionOverrideOverride",
|
||||
transitionOverrideOverride);
|
||||
obs_data_set_default_bool(obj, "adjustActiveTransitionType",
|
||||
adjustActiveTransitionType);
|
||||
obs_data_set_bool(obj, "adjustActiveTransitionType",
|
||||
adjustActiveTransitionType);
|
||||
obs_data_set_default_int(obj, "defTransitionDelay",
|
||||
default_def_transition_dealy);
|
||||
obs_data_set_int(obj, "defTransitionDelay",
|
||||
@@ -380,11 +340,6 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
||||
}
|
||||
obs_data_array_release(defaultTransitionsArray);
|
||||
|
||||
transitionOverrideOverride =
|
||||
obs_data_get_bool(obj, "tansitionOverrideOverride");
|
||||
adjustActiveTransitionType =
|
||||
obs_data_get_bool(obj, "adjustActiveTransitionType");
|
||||
|
||||
// Check for invalid config
|
||||
if (!transitionOverrideOverride && !adjustActiveTransitionType) {
|
||||
adjustActiveTransitionType = true;
|
||||
|
||||
@@ -434,10 +434,24 @@ std::string getProcNameFromPid(int pid)
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
bool isInFocus(const QString &executable)
|
||||
void GetForegroundProcessName(QString &proc)
|
||||
{
|
||||
std::string temp;
|
||||
GetForegroundProcessName(temp);
|
||||
proc = QString::fromStdString(temp);
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(std::string &proc)
|
||||
{
|
||||
proc.resize(0);
|
||||
auto pid = getForegroundProcessPid();
|
||||
std::string current = getProcNameFromPid(pid);
|
||||
}
|
||||
|
||||
bool isInFocus(const QString &executable)
|
||||
{
|
||||
std::string current;
|
||||
GetForegroundProcessName(current);
|
||||
|
||||
// True if executable switch equals current window
|
||||
bool equals = (executable.toStdString() == current);
|
||||
|
||||
@@ -43,9 +43,11 @@ bool MacroActionRecord::PerformAction()
|
||||
obs_frontend_recording_pause(false);
|
||||
}
|
||||
break;
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(28, 0, 0)
|
||||
case RecordAction::SPLIT:
|
||||
obs_frontend_recording_split_file();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -75,22 +75,22 @@ MacroActionRunEdit::MacroActionRunEdit(
|
||||
_filePath = new FileSelection();
|
||||
_argList = new QListWidget();
|
||||
_addArg = new QPushButton();
|
||||
_addArg->setMaximumSize(QSize(22, 22));
|
||||
_addArg->setMaximumWidth(22);
|
||||
_addArg->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("addIconSmall")));
|
||||
_addArg->setFlat(true);
|
||||
_removeArg = new QPushButton();
|
||||
_removeArg->setMaximumSize(QSize(22, 22));
|
||||
_removeArg->setMaximumWidth(22);
|
||||
_removeArg->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||
_removeArg->setFlat(true);
|
||||
_argUp = new QPushButton();
|
||||
_argUp->setMaximumSize(QSize(22, 22));
|
||||
_argUp->setMaximumWidth(22);
|
||||
_argUp->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
||||
_argUp->setFlat(true);
|
||||
_argDown = new QPushButton();
|
||||
_argDown->setMaximumSize(QSize(22, 22));
|
||||
_argDown->setMaximumWidth(22);
|
||||
_argDown->setProperty(
|
||||
"themeID", QVariant(QString::fromUtf8("downArrowIconSmall")));
|
||||
_argDown->setFlat(true);
|
||||
|
||||
@@ -157,7 +157,7 @@ MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneOrder> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_actions = new QComboBox();
|
||||
_position = new QSpinBox();
|
||||
|
||||
@@ -55,7 +55,7 @@ int getTransitionOverrideDuration(OBSWeakSource &scene)
|
||||
return duration;
|
||||
}
|
||||
|
||||
bool isUsingFixedLengthTransition(OBSWeakSource &transition)
|
||||
bool isUsingFixedLengthTransition(const OBSWeakSource &transition)
|
||||
{
|
||||
obs_source_t *source = obs_weak_source_get_source(transition);
|
||||
bool ret = obs_transition_fixed(source);
|
||||
@@ -123,15 +123,15 @@ void MacroActionSwitchScene::LogAction()
|
||||
auto t = _scene.GetType();
|
||||
auto sceneName = GetWeakSourceName(_scene.GetScene(false));
|
||||
switch (t) {
|
||||
case SceneSelectionType::SCENE:
|
||||
case SceneSelection::Type::SCENE:
|
||||
vblog(LOG_INFO, "switch to scene '%s'",
|
||||
_scene.ToString().c_str());
|
||||
break;
|
||||
case SceneSelectionType::GROUP:
|
||||
case SceneSelection::Type::GROUP:
|
||||
vblog(LOG_INFO, "switch to scene '%s' (scene group '%s')",
|
||||
sceneName.c_str(), _scene.ToString().c_str());
|
||||
break;
|
||||
case SceneSelectionType::PREVIOUS:
|
||||
case SceneSelection::Type::PREVIOUS:
|
||||
vblog(LOG_INFO, "switch to previous scene '%s'",
|
||||
sceneName.c_str());
|
||||
break;
|
||||
@@ -153,30 +153,6 @@ bool MacroActionSwitchScene::Save(obs_data_t *obj)
|
||||
|
||||
bool MacroActionSwitchScene::Load(obs_data_t *obj)
|
||||
{
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "targetType")) {
|
||||
auto sceneName = obs_data_get_string(obj, "target");
|
||||
obs_data_set_string(obj, "scene", sceneName);
|
||||
auto transitionName = obs_data_get_string(obj, "transition");
|
||||
bool usePreviousScene =
|
||||
strcmp(sceneName, previous_scene_name) == 0;
|
||||
bool useCurrentTransition =
|
||||
strcmp(transitionName, current_transition_name) == 0;
|
||||
obs_data_set_int(
|
||||
obj, "sceneType",
|
||||
(usePreviousScene)
|
||||
? static_cast<int>(SceneSelectionType::PREVIOUS)
|
||||
: obs_data_get_int(obj, "targetType"));
|
||||
obs_data_set_int(
|
||||
obj, "transitionType",
|
||||
(useCurrentTransition)
|
||||
? static_cast<int>(
|
||||
TransitionSelectionType::CURRENT)
|
||||
: static_cast<int>(
|
||||
TransitionSelectionType::TRANSITION));
|
||||
}
|
||||
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
_transition.Load(obj);
|
||||
@@ -193,14 +169,14 @@ std::string MacroActionSwitchScene::GetShortDesc()
|
||||
|
||||
MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSwitchScene> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_scenes(new SceneSelectionWidget(window(), true, true, true)),
|
||||
_transitions(new TransitionSelectionWidget(this)),
|
||||
_duration(new DurationSelection(parent, false)),
|
||||
_blockUntilTransitionDone(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.scene.blockUntilTransitionDone"))),
|
||||
_entryLayout(new QHBoxLayout())
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), true, true);
|
||||
_transitions = new TransitionSelectionWidget(this);
|
||||
_duration = new DurationSelection(parent, false);
|
||||
_blockUntilTransitionDone = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.scene.blockUntilTransitionDone"));
|
||||
|
||||
_duration->SpinBox()->setSpecialValueText("-");
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
@@ -214,7 +190,6 @@ MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
||||
QWidget::connect(_blockUntilTransitionDone, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(BlockUntilTransitionDoneChanged(int)));
|
||||
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{transitions}}", _transitions},
|
||||
@@ -222,10 +197,10 @@ MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
||||
{"{{blockUntilTransitionDone}}", _blockUntilTransitionDone},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
_entryLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addLayout(_entryLayout);
|
||||
mainLayout->addWidget(_blockUntilTransitionDone);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -235,6 +210,7 @@ MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_blockUntilTransitionDone->setChecked(
|
||||
_entryData->_blockUntilTransitionDone);
|
||||
SetDurationVisibility();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
@@ -258,6 +234,37 @@ void MacroActionSwitchSceneEdit::BlockUntilTransitionDoneChanged(int state)
|
||||
_entryData->_blockUntilTransitionDone = state;
|
||||
}
|
||||
|
||||
void MacroActionSwitchSceneEdit::SetDurationVisibility()
|
||||
{
|
||||
if (_entryData->_transition.GetType() !=
|
||||
TransitionSelection::Type::TRANSITION) {
|
||||
_duration->show();
|
||||
}
|
||||
const bool fixedDuration = isUsingFixedLengthTransition(
|
||||
_entryData->_transition.GetTransition());
|
||||
_duration->setVisible(!fixedDuration);
|
||||
|
||||
_entryLayout->removeWidget(_scenes);
|
||||
_entryLayout->removeWidget(_transitions);
|
||||
_entryLayout->removeWidget(_duration);
|
||||
clearLayout(_entryLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{transitions}}", _transitions},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
if (fixedDuration) {
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.scene.entry.noDuration"),
|
||||
_entryLayout, widgetPlaceholders);
|
||||
} else {
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.scene.entry"),
|
||||
_entryLayout, widgetPlaceholders);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionSwitchSceneEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -278,4 +285,5 @@ void MacroActionSwitchSceneEdit::TransitionChanged(const TransitionSelection &t)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_transition = t;
|
||||
SetDurationVisibility();
|
||||
}
|
||||
|
||||
@@ -61,9 +61,11 @@ protected:
|
||||
TransitionSelectionWidget *_transitions;
|
||||
DurationSelection *_duration;
|
||||
QCheckBox *_blockUntilTransitionDone;
|
||||
QHBoxLayout *_entryLayout;
|
||||
|
||||
std::shared_ptr<MacroActionSwitchScene> _entryData;
|
||||
|
||||
private:
|
||||
void SetDurationVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -107,7 +107,7 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneTransform> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
|
||||
|
||||
@@ -166,7 +166,7 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, true, true);
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, true, true);
|
||||
_sourceTypes = new QComboBox();
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_sourceGroups = new QComboBox();
|
||||
|
||||
@@ -9,73 +9,155 @@ bool MacroActionScreenshot::_registered = MacroActionFactory::Register(
|
||||
{MacroActionScreenshot::Create, MacroActionScreenshotEdit::Create,
|
||||
"AdvSceneSwitcher.action.screenshot"});
|
||||
|
||||
bool MacroActionScreenshot::PerformAction()
|
||||
void MacroActionScreenshot::FrontendScreenshot(OBSWeakSource &source)
|
||||
{
|
||||
if (_source) {
|
||||
auto s = obs_weak_source_get_source(_source);
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
|
||||
if (source) {
|
||||
auto s = obs_weak_source_get_source(source);
|
||||
obs_frontend_take_source_screenshot(s);
|
||||
obs_source_release(s);
|
||||
} else {
|
||||
obs_frontend_take_screenshot();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MacroActionScreenshot::CustomScreenshot(OBSWeakSource &source)
|
||||
{
|
||||
if (!source && _targetType == TargetType::SCENE) {
|
||||
return;
|
||||
}
|
||||
auto s = obs_weak_source_get_source(source);
|
||||
_screenshot.~ScreenshotHelper();
|
||||
new (&_screenshot) ScreenshotHelper(s, false, 0, true, _path);
|
||||
obs_source_release(s);
|
||||
}
|
||||
|
||||
bool MacroActionScreenshot::PerformAction()
|
||||
{
|
||||
OBSWeakSource source = nullptr;
|
||||
switch (_targetType) {
|
||||
case MacroActionScreenshot::TargetType::SOURCE:
|
||||
source = _source;
|
||||
break;
|
||||
case MacroActionScreenshot::TargetType::SCENE:
|
||||
source = _scene.GetScene(false);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (_saveType) {
|
||||
case MacroActionScreenshot::SaveType::OBS_DEFAULT:
|
||||
FrontendScreenshot(source);
|
||||
break;
|
||||
case MacroActionScreenshot::SaveType::CUSTOM:
|
||||
CustomScreenshot(source);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionScreenshot::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "trigger screenshot for source \"%s\"",
|
||||
GetWeakSourceName(_source).c_str());
|
||||
vblog(LOG_INFO, "trigger screenshot for \"%s\"",
|
||||
_targetType == TargetType::SOURCE
|
||||
? GetWeakSourceName(_source).c_str()
|
||||
: _scene.ToString().c_str());
|
||||
}
|
||||
|
||||
bool MacroActionScreenshot::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
obs_data_set_int(obj, "saveType", static_cast<int>(_saveType));
|
||||
obs_data_set_int(obj, "targetType", static_cast<int>(_targetType));
|
||||
obs_data_set_string(obj, "savePath", _path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionScreenshot::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_saveType = static_cast<SaveType>(obs_data_get_int(obj, "saveType"));
|
||||
_targetType =
|
||||
static_cast<TargetType>(obs_data_get_int(obj, "targetType"));
|
||||
_path = obs_data_get_string(obj, "savePath");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionScreenshot::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
if (_targetType == TargetType::SCENE) {
|
||||
return _scene.ToString();
|
||||
} else {
|
||||
return GetWeakSourceName(_source);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void addOBSMainOutputEntry(QComboBox *cb)
|
||||
static void populateSaveTypeSelection(QComboBox *list)
|
||||
{
|
||||
cb->insertItem(
|
||||
0, obs_module_text(
|
||||
"AdvSceneSwitcher.action.screenshot.mainOutput"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.screenshot.save.default"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.screenshot.save.custom"));
|
||||
}
|
||||
|
||||
static void populateTargetTypeSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.screenshot.type.source"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.screenshot.type.scene"));
|
||||
}
|
||||
|
||||
MacroActionScreenshotEdit::MacroActionScreenshotEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionScreenshot> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_scenes(new SceneSelectionWidget(this, true, false, true, true,
|
||||
true)),
|
||||
_sources(new QComboBox()),
|
||||
_saveType(new QComboBox()),
|
||||
_targetType(new QComboBox()),
|
||||
_savePath(new FileSelection(FileSelection::Type::WRITE, this))
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
populateVideoSelection(_sources, true, false);
|
||||
addOBSMainOutputEntry(_sources);
|
||||
setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.action.screenshot.blackscreenNote"));
|
||||
populateVideoSelection(_sources, true);
|
||||
populateSaveTypeSelection(_saveType);
|
||||
populateTargetTypeSelection(_targetType);
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_saveType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(SaveTypeChanged(int)));
|
||||
QWidget::connect(_targetType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TargetTypeChanged(int)));
|
||||
QWidget::connect(_savePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(PathChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{saveType}}", _saveType},
|
||||
{"{{targetType}}", _targetType},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.screenshot.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
layout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(layout);
|
||||
mainLayout->addWidget(_savePath);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -93,8 +175,55 @@ void MacroActionScreenshotEdit::UpdateEntryData()
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
} else {
|
||||
_sources->setCurrentIndex(0);
|
||||
_sources->setCurrentIndex(1);
|
||||
}
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_saveType->setCurrentIndex(static_cast<int>(_entryData->_saveType));
|
||||
_targetType->setCurrentIndex(static_cast<int>(_entryData->_targetType));
|
||||
_savePath->SetPath(QString::fromStdString(_entryData->_path));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionScreenshotEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionScreenshotEdit::SaveTypeChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
}
|
||||
|
||||
_entryData->_saveType =
|
||||
static_cast<MacroActionScreenshot::SaveType>(index);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionScreenshotEdit::TargetTypeChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
}
|
||||
|
||||
_entryData->_targetType =
|
||||
static_cast<MacroActionScreenshot::TargetType>(index);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionScreenshotEdit::PathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_path = text.toUtf8().constData();
|
||||
}
|
||||
|
||||
void MacroActionScreenshotEdit::SourceChanged(const QString &text)
|
||||
@@ -108,3 +237,18 @@ void MacroActionScreenshotEdit::SourceChanged(const QString &text)
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionScreenshotEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_savePath->setVisible(_entryData->_saveType ==
|
||||
MacroActionScreenshot::SaveType::CUSTOM);
|
||||
_sources->setVisible(_entryData->_targetType ==
|
||||
MacroActionScreenshot::TargetType::SOURCE);
|
||||
_scenes->setVisible(_entryData->_targetType ==
|
||||
MacroActionScreenshot::TargetType::SCENE);
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "file-selection.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "screenshot-helper.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
@@ -16,9 +19,25 @@ public:
|
||||
{
|
||||
return std::make_shared<MacroActionScreenshot>(m);
|
||||
}
|
||||
enum class SaveType {
|
||||
OBS_DEFAULT,
|
||||
CUSTOM,
|
||||
};
|
||||
SaveType _saveType = SaveType::OBS_DEFAULT;
|
||||
enum class TargetType {
|
||||
SOURCE,
|
||||
SCENE,
|
||||
};
|
||||
TargetType _targetType = TargetType::SOURCE;
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source;
|
||||
std::string _path = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
|
||||
private:
|
||||
void FrontendScreenshot(OBSWeakSource &);
|
||||
void CustomScreenshot(OBSWeakSource &);
|
||||
|
||||
ScreenshotHelper _screenshot;
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -40,14 +59,24 @@ public:
|
||||
action));
|
||||
}
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const QString &text);
|
||||
void SaveTypeChanged(int index);
|
||||
void TargetTypeChanged(int index);
|
||||
void PathChanged(const QString &text);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sources;
|
||||
QComboBox *_saveType;
|
||||
QComboBox *_targetType;
|
||||
FileSelection *_savePath;
|
||||
std::shared_ptr<MacroActionScreenshot> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -124,7 +124,7 @@ MacroActionSudioModeEdit::MacroActionSudioModeEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSudioMode> entryData)
|
||||
: QWidget(parent),
|
||||
_actions(new QComboBox()),
|
||||
_scenes(new SceneSelectionWidget(window(), true, true, true))
|
||||
_scenes(new SceneSelectionWidget(window(), true, true, true, true))
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
|
||||
@@ -49,8 +49,32 @@ void MacroActionTransition::SetTransitionOverride()
|
||||
obs_source_release(scene);
|
||||
}
|
||||
|
||||
#if (LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)) && \
|
||||
(LIBOBS_API_VER < MAKE_SEMANTIC_VERSION(28, 0, 0))
|
||||
void obs_sceneitem_set_transition(obs_sceneitem_t *item, bool show,
|
||||
obs_source_t *transition)
|
||||
{
|
||||
if (show) {
|
||||
obs_sceneitem_set_show_transition(item, transition);
|
||||
} else {
|
||||
obs_sceneitem_set_hide_transition(item, transition);
|
||||
}
|
||||
}
|
||||
|
||||
void obs_sceneitem_set_transition_duration(obs_sceneitem_t *item, bool show,
|
||||
uint32_t duration_ms)
|
||||
{
|
||||
if (show) {
|
||||
obs_sceneitem_set_show_transition_duration(item, duration_ms);
|
||||
} else {
|
||||
obs_sceneitem_set_hide_transition_duration(item, duration_ms);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void MacroActionTransition::SetSourceTransition(bool show)
|
||||
{
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||
auto transition =
|
||||
obs_weak_source_get_source(_transition.GetTransition());
|
||||
obs_data_t *settings = obs_source_get_settings(transition);
|
||||
@@ -73,6 +97,7 @@ void MacroActionTransition::SetSourceTransition(bool show)
|
||||
}
|
||||
|
||||
obs_source_release(t);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MacroActionTransition::PerformAction()
|
||||
@@ -182,7 +207,7 @@ MacroActionTransitionEdit::MacroActionTransitionEdit(
|
||||
: QWidget(parent),
|
||||
_actions(new QComboBox),
|
||||
_sources(new SceneItemSelectionWidget(parent, false)),
|
||||
_scenes(new SceneSelectionWidget(this, false, false, true)),
|
||||
_scenes(new SceneSelectionWidget(this, true, false, false, true)),
|
||||
_setTransition(new QCheckBox),
|
||||
_setDuration(new QCheckBox),
|
||||
_transitions(new TransitionSelectionWidget(this, false)),
|
||||
|
||||
253
src/macro-core/macro-action-variable.cpp
Normal file
253
src/macro-core/macro-action-variable.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#include "macro-action-variable.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
const std::string MacroActionVariable::id = "variable";
|
||||
|
||||
bool MacroActionVariable::_registered = MacroActionFactory::Register(
|
||||
MacroActionVariable::id,
|
||||
{MacroActionVariable::Create, MacroActionVariableEdit::Create,
|
||||
"AdvSceneSwitcher.action.variable"});
|
||||
|
||||
static std::map<MacroActionVariable::Type, std::string> waitTypes = {
|
||||
{MacroActionVariable::Type::SET,
|
||||
"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"},
|
||||
};
|
||||
|
||||
static void apppend(Variable &var, const std::string &value)
|
||||
{
|
||||
auto currentValue = var.Value();
|
||||
var.SetValue(currentValue + value);
|
||||
}
|
||||
|
||||
static void modifyNumValue(Variable &var, double val, const bool increment)
|
||||
{
|
||||
double current;
|
||||
if (!var.DoubleValue(current)) {
|
||||
return;
|
||||
}
|
||||
if (increment) {
|
||||
var.SetValue(current + val);
|
||||
} else {
|
||||
var.SetValue(current - val);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionVariable::PerformAction()
|
||||
{
|
||||
auto var = GetVariableByName(_variableName);
|
||||
if (!var) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_type) {
|
||||
case MacroActionVariable::Type::SET:
|
||||
var->SetValue(_strValue);
|
||||
break;
|
||||
case MacroActionVariable::Type::APPEND:
|
||||
apppend(*var, _strValue);
|
||||
break;
|
||||
case MacroActionVariable::Type::APPEND_VAR: {
|
||||
auto var2 = GetVariableByName(_variable2Name);
|
||||
if (!var2) {
|
||||
return true;
|
||||
}
|
||||
apppend(*var, var2->Value());
|
||||
break;
|
||||
}
|
||||
case MacroActionVariable::Type::INCREMENT:
|
||||
modifyNumValue(*var, _numValue, true);
|
||||
break;
|
||||
case MacroActionVariable::Type::DECREMENT:
|
||||
modifyNumValue(*var, _numValue, false);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionVariable::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "variableName", _variableName.c_str());
|
||||
obs_data_set_string(obj, "variable2Name", _variable2Name.c_str());
|
||||
obs_data_set_string(obj, "strValue", _strValue.c_str());
|
||||
obs_data_set_double(obj, "numValue", _numValue);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_type));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionVariable::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_variableName = obs_data_get_string(obj, "variableName");
|
||||
_variable2Name = obs_data_get_string(obj, "variable2Name");
|
||||
_strValue = obs_data_get_string(obj, "strValue");
|
||||
_numValue = obs_data_get_double(obj, "numValue");
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "condition"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionVariable::GetShortDesc()
|
||||
{
|
||||
return _variableName;
|
||||
}
|
||||
|
||||
static inline void populateTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : waitTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionVariable> entryData)
|
||||
: QWidget(parent),
|
||||
_variables(new VariableSelection(this)),
|
||||
_variables2(new VariableSelection(this)),
|
||||
_actions(new QComboBox()),
|
||||
_strValue(new QLineEdit()),
|
||||
_numValue(new QDoubleSpinBox())
|
||||
{
|
||||
_numValue->setMinimum(-9999999999);
|
||||
_numValue->setMaximum(9999999999);
|
||||
populateTypeSelection(_actions);
|
||||
|
||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(VariableChanged(const QString &)));
|
||||
QWidget::connect(_variables2, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(Variable2Changed(const QString &)));
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_strValue, SIGNAL(editingFinished()), this,
|
||||
SLOT(StrValueChanged()));
|
||||
QWidget::connect(_numValue, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(NumValueChanged(double)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{variables}}", _variables}, {"{{variables2}}", _variables2},
|
||||
{"{{actions}}", _actions}, {"{{strValue}}", _strValue},
|
||||
{"{{numValue}}", _numValue},
|
||||
};
|
||||
|
||||
auto mainLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_variables->SetVariable(_entryData->_variableName);
|
||||
_variables2->SetVariable(_entryData->_variable2Name);
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_strValue->setText(QString::fromStdString(_entryData->_strValue));
|
||||
_numValue->setValue(_entryData->_numValue);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::VariableChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_variableName = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::Variable2Changed(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_variable2Name = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_type = static_cast<MacroActionVariable::Type>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::StrValueChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_strValue = _strValue->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::NumValueChanged(double val)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_numValue = val;
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto type = _entryData->_type;
|
||||
switch (type) {
|
||||
case MacroActionVariable::Type::SET:
|
||||
_variables2->hide();
|
||||
_strValue->show();
|
||||
_numValue->hide();
|
||||
break;
|
||||
case MacroActionVariable::Type::APPEND:
|
||||
_variables2->hide();
|
||||
_strValue->show();
|
||||
_numValue->hide();
|
||||
break;
|
||||
case MacroActionVariable::Type::APPEND_VAR:
|
||||
_variables2->show();
|
||||
_strValue->hide();
|
||||
_numValue->hide();
|
||||
break;
|
||||
case MacroActionVariable::Type::INCREMENT:
|
||||
_variables2->hide();
|
||||
_strValue->hide();
|
||||
_numValue->show();
|
||||
break;
|
||||
case MacroActionVariable::Type::DECREMENT:
|
||||
_variables2->hide();
|
||||
_strValue->hide();
|
||||
_numValue->show();
|
||||
break;
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
76
src/macro-core/macro-action-variable.hpp
Normal file
76
src/macro-core/macro-action-variable.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
class MacroActionVariable : public MacroAction {
|
||||
public:
|
||||
MacroActionVariable(Macro *m) : MacroAction(m) {}
|
||||
bool PerformAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionVariable>(m);
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
SET,
|
||||
APPEND,
|
||||
APPEND_VAR,
|
||||
INCREMENT,
|
||||
DECREMENT,
|
||||
//...
|
||||
};
|
||||
|
||||
Type _type = Type::SET;
|
||||
std::string _variableName = "";
|
||||
std::string _variable2Name = "";
|
||||
std::string _strValue = "";
|
||||
double _numValue = 0;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionVariableEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionVariableEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionVariable> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionVariableEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionVariable>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void VariableChanged(const QString &);
|
||||
void Variable2Changed(const QString &);
|
||||
void ActionChanged(int);
|
||||
void StrValueChanged();
|
||||
void NumValueChanged(double);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
VariableSelection *_variables;
|
||||
VariableSelection *_variables2;
|
||||
QComboBox *_actions;
|
||||
QLineEdit *_strValue;
|
||||
QDoubleSpinBox *_numValue;
|
||||
std::shared_ptr<MacroActionVariable> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -16,6 +16,7 @@ const static std::map<VCamAction, std::string> actionTypes = {
|
||||
|
||||
bool MacroActionVCam::PerformAction()
|
||||
{
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||
switch (_action) {
|
||||
case VCamAction::STOP:
|
||||
if (obs_frontend_virtualcam_active()) {
|
||||
@@ -30,6 +31,7 @@ bool MacroActionVCam::PerformAction()
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,25 +9,58 @@ bool MacroActionWebsocket::_registered = MacroActionFactory::Register(
|
||||
{MacroActionWebsocket::Create, MacroActionWebsocketEdit::Create,
|
||||
"AdvSceneSwitcher.action.websocket"});
|
||||
|
||||
bool MacroActionWebsocket::PerformAction()
|
||||
static std::map<MacroActionWebsocket::Type, std::string> actionTypes = {
|
||||
{MacroActionWebsocket::Type::REQUEST,
|
||||
"AdvSceneSwitcher.action.websocket.type.request"},
|
||||
{MacroActionWebsocket::Type::EVENT,
|
||||
"AdvSceneSwitcher.action.websocket.type.event"},
|
||||
};
|
||||
|
||||
void MacroActionWebsocket::SendRequest()
|
||||
{
|
||||
auto connection = GetConnectionByName(_connection);
|
||||
if (!connection) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
connection->SendMsg(_message);
|
||||
}
|
||||
|
||||
bool MacroActionWebsocket::PerformAction()
|
||||
{
|
||||
switch (_type) {
|
||||
case MacroActionWebsocket::Type::REQUEST:
|
||||
SendRequest();
|
||||
break;
|
||||
case MacroActionWebsocket::Type::EVENT:
|
||||
SendWebsocketEvent(_message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionWebsocket::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "sent msg \"%s\" via \"%s\"", _message.c_str(),
|
||||
_connection.c_str());
|
||||
switch (_type) {
|
||||
case MacroActionWebsocket::Type::REQUEST:
|
||||
vblog(LOG_INFO, "sent msg \"%s\" via \"%s\"", _message.c_str(),
|
||||
_connection.c_str());
|
||||
break;
|
||||
case MacroActionWebsocket::Type::EVENT:
|
||||
vblog(LOG_INFO, "sent event \"%s\" to connected clients",
|
||||
_message.c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionWebsocket::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
obs_data_set_string(obj, "message", _message.c_str());
|
||||
obs_data_set_string(obj, "connection", _connection.c_str());
|
||||
return true;
|
||||
@@ -36,6 +69,7 @@ bool MacroActionWebsocket::Save(obs_data_t *obj)
|
||||
bool MacroActionWebsocket::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||
_message = obs_data_get_string(obj, "message");
|
||||
_connection = obs_data_get_string(obj, "connection");
|
||||
return true;
|
||||
@@ -43,30 +77,39 @@ bool MacroActionWebsocket::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroActionWebsocket::GetShortDesc()
|
||||
{
|
||||
return _connection;
|
||||
if (_type == Type::REQUEST) {
|
||||
return _connection;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionWebsocketEdit::MacroActionWebsocketEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionWebsocket> entryData)
|
||||
: QWidget(parent),
|
||||
_actions(new QComboBox(this)),
|
||||
_message(new ResizingPlainTextEdit(this)),
|
||||
_connection(new ConnectionSelection(this))
|
||||
_connection(new ConnectionSelection(this)),
|
||||
_editLayout(new QHBoxLayout())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_message, SIGNAL(textChanged()), this,
|
||||
SLOT(MessageChanged()));
|
||||
QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)),
|
||||
this,
|
||||
SLOT(ConnectionSelectionChanged(const QString &)));
|
||||
|
||||
auto *connectionLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{connection}}", _connection},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.websocket.entry"),
|
||||
connectionLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(connectionLayout);
|
||||
mainLayout->addLayout(_editLayout);
|
||||
mainLayout->addWidget(_message);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -75,19 +118,73 @@ MacroActionWebsocketEdit::MacroActionWebsocketEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::SetupRequestEdit()
|
||||
{
|
||||
_editLayout->removeWidget(_actions);
|
||||
_editLayout->removeWidget(_connection);
|
||||
clearLayout(_editLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", _actions},
|
||||
{"{{connection}}", _connection},
|
||||
};
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.entry.request"),
|
||||
_editLayout, widgetPlaceholders);
|
||||
_connection->show();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::SetupEventEdit()
|
||||
{
|
||||
_editLayout->removeWidget(_actions);
|
||||
_editLayout->removeWidget(_connection);
|
||||
clearLayout(_editLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", _actions},
|
||||
{"{{connection}}", _connection},
|
||||
};
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.entry.event"),
|
||||
_editLayout, widgetPlaceholders);
|
||||
_connection->hide();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_message->setPlainText(QString::fromStdString(_entryData->_message));
|
||||
_connection->SetConnection(_entryData->_connection);
|
||||
|
||||
if (_entryData->_type == MacroActionWebsocket::Type::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
} else {
|
||||
SetupEventEdit();
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::ActionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_type = static_cast<MacroActionWebsocket::Type>(index);
|
||||
if (_entryData->_type == MacroActionWebsocket::Type::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
} else {
|
||||
SetupEventEdit();
|
||||
}
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::MessageChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
||||
@@ -22,10 +22,18 @@ public:
|
||||
return std::make_shared<MacroActionWebsocket>(m);
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
REQUEST,
|
||||
EVENT,
|
||||
};
|
||||
|
||||
Type _type = Type::REQUEST;
|
||||
std::string _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
std::string _connection;
|
||||
|
||||
private:
|
||||
void SendRequest();
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -47,6 +55,7 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int);
|
||||
void MessageChanged();
|
||||
void ConnectionSelectionChanged(const QString &);
|
||||
signals:
|
||||
@@ -56,7 +65,12 @@ protected:
|
||||
std::shared_ptr<MacroActionWebsocket> _entryData;
|
||||
|
||||
private:
|
||||
void SetupRequestEdit();
|
||||
void SetupEventEdit();
|
||||
|
||||
QComboBox *_actions;
|
||||
ResizingPlainTextEdit *_message;
|
||||
ConnectionSelection *_connection;
|
||||
QHBoxLayout *_editLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -67,6 +67,26 @@ bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionDate::CheckBetween(QDateTime &now)
|
||||
{
|
||||
// In the case of ignoring the date component treat the "left" value as
|
||||
// the start of the time range and the "right" value as the end
|
||||
if (_ignoreDate) {
|
||||
if (_dateTime2 >= _dateTime) {
|
||||
return now >= _dateTime && now <= _dateTime2;
|
||||
}
|
||||
// Assume that the end / start value should be shifted by 24h
|
||||
// as otherwise the condition would never be true
|
||||
return (now >= _dateTime && now <= _dateTime2.addDays(1)) ||
|
||||
(now >= _dateTime.addDays(-1) && now <= _dateTime2);
|
||||
}
|
||||
|
||||
if (_dateTime2 >= _dateTime) {
|
||||
return now >= _dateTime && now <= _dateTime2;
|
||||
}
|
||||
return now >= _dateTime2 && now <= _dateTime;
|
||||
}
|
||||
|
||||
bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
|
||||
{
|
||||
bool match = false;
|
||||
@@ -92,11 +112,7 @@ bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
|
||||
match = cur <= _dateTime;
|
||||
break;
|
||||
case DateCondition::BETWEEN:
|
||||
if (_dateTime2 > _dateTime) {
|
||||
match = cur >= _dateTime && cur <= _dateTime2;
|
||||
} else {
|
||||
match = cur >= _dateTime2 && cur <= _dateTime;
|
||||
}
|
||||
match = CheckBetween(cur);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -232,12 +248,12 @@ void MacroConditionDate::SetTime2(const QTime &time)
|
||||
|
||||
QDateTime MacroConditionDate::GetDateTime1()
|
||||
{
|
||||
return _updateOnRepeat ? _dateTime : _origDateTime;
|
||||
return _repeat && _updateOnRepeat ? _dateTime : _origDateTime;
|
||||
}
|
||||
|
||||
QDateTime MacroConditionDate::GetDateTime2()
|
||||
{
|
||||
return _updateOnRepeat ? _dateTime2 : _origDateTime2;
|
||||
return _repeat && _updateOnRepeat ? _dateTime2 : _origDateTime2;
|
||||
}
|
||||
|
||||
QDateTime MacroConditionDate::GetNextMatchDateTime()
|
||||
@@ -576,12 +592,12 @@ void MacroConditionDateEdit::UpdateEntryData()
|
||||
_dayOfWeek->setCurrentIndex(
|
||||
static_cast<int>(static_cast<int>(_entryData->_dayOfWeek)));
|
||||
_ignoreWeekTime->setChecked(!_entryData->_ignoreTime);
|
||||
_weekTime->setDateTime(_entryData->GetDateTime1());
|
||||
_weekTime->setTime(_entryData->GetDateTime1().time());
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_date->setDateTime(_entryData->GetDateTime1());
|
||||
_time->setDateTime(_entryData->GetDateTime1());
|
||||
_date2->setDateTime(_entryData->GetDateTime2());
|
||||
_time2->setDateTime(_entryData->GetDateTime2());
|
||||
_date->setDate(_entryData->GetDateTime1().date());
|
||||
_time->setTime(_entryData->GetDateTime1().time());
|
||||
_date2->setDate(_entryData->GetDateTime2().date());
|
||||
_time2->setTime(_entryData->GetDateTime2().time());
|
||||
_ignoreDate->setChecked(!_entryData->_ignoreDate);
|
||||
_ignoreTime->setChecked(!_entryData->_ignoreTime);
|
||||
_repeat->setChecked(_entryData->_repeat);
|
||||
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
private:
|
||||
bool CheckDayOfWeek(int64_t);
|
||||
bool CheckRegularDate(int64_t);
|
||||
bool CheckBetween(QDateTime &now);
|
||||
|
||||
QDateTime _dateTime = QDateTime::currentDateTime();
|
||||
QDateTime _dateTime2 = QDateTime::currentDateTime();
|
||||
|
||||
@@ -120,7 +120,7 @@ DurationModifierEdit::DurationModifierEdit(QWidget *parent)
|
||||
_condition = new QComboBox(parent);
|
||||
_duration = new DurationSelection(parent);
|
||||
_toggle = new QPushButton(parent);
|
||||
_toggle->setMaximumSize(22, 22);
|
||||
_toggle->setMaximumWidth(22);
|
||||
_toggle->setIcon(
|
||||
QIcon(QString::fromStdString(getDataFilePath("res/time.svg"))));
|
||||
populateDurationModifierTypes(_condition);
|
||||
@@ -447,6 +447,9 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
||||
if (idx == 0 && macro->Conditions().size() > 0) {
|
||||
auto newRoot = macro->Conditions().at(0);
|
||||
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
conditionsList->WidgetAt(0))
|
||||
->SetRootNode(true);
|
||||
}
|
||||
SetConditionData(*macro);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ static std::string getRemoteData(std::string &url)
|
||||
return readBuffer;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::matchFileContent(QString &filedata)
|
||||
bool MacroConditionFile::MatchFileContent(QString &filedata)
|
||||
{
|
||||
if (_onlyMatchIfChanged) {
|
||||
size_t newHash = strHash(filedata.toUtf8().constData());
|
||||
@@ -50,31 +50,30 @@ bool MacroConditionFile::matchFileContent(QString &filedata)
|
||||
_lastHash = newHash;
|
||||
}
|
||||
|
||||
if (_useRegex) {
|
||||
try {
|
||||
std::regex expr(_text);
|
||||
return std::regex_match(filedata.toStdString(), expr);
|
||||
} catch (const std::regex_error &) {
|
||||
if (_regex.Enabled()) {
|
||||
auto expr = _regex.GetRegularExpression(_text);
|
||||
if (!expr.isValid()) {
|
||||
return false;
|
||||
}
|
||||
auto match = expr.match(filedata);
|
||||
return match.hasMatch();
|
||||
}
|
||||
|
||||
QString text = QString::fromStdString(_text);
|
||||
return compareIgnoringLineEnding(text, filedata);
|
||||
}
|
||||
|
||||
bool MacroConditionFile::checkRemoteFileContent()
|
||||
bool MacroConditionFile::CheckRemoteFileContent()
|
||||
{
|
||||
std::string data = getRemoteData(_file);
|
||||
QString qdata = QString::fromStdString(data);
|
||||
return matchFileContent(qdata);
|
||||
return MatchFileContent(qdata);
|
||||
}
|
||||
|
||||
bool MacroConditionFile::checkLocalFileContent()
|
||||
bool MacroConditionFile::CheckLocalFileContent()
|
||||
{
|
||||
QString t = QString::fromStdString(_text);
|
||||
QFile file(QString::fromStdString(_file));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -87,28 +86,78 @@ bool MacroConditionFile::checkLocalFileContent()
|
||||
}
|
||||
|
||||
QString filedata = QTextStream(&file).readAll();
|
||||
bool match = matchFileContent(filedata);
|
||||
bool match = MatchFileContent(filedata);
|
||||
|
||||
file.close();
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckCondition()
|
||||
bool MacroConditionFile::CheckChangeContent()
|
||||
{
|
||||
QString filedata;
|
||||
switch (_fileType) {
|
||||
case FileType::LOCAL: {
|
||||
QFile file(QString::fromStdString(_file));
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return false;
|
||||
}
|
||||
filedata = QTextStream(&file).readAll();
|
||||
file.close();
|
||||
} break;
|
||||
case FileType::REMOTE: {
|
||||
std::string data = getRemoteData(_file);
|
||||
QString filedata = QString::fromStdString(data);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
size_t newHash = strHash(filedata.toUtf8().constData());
|
||||
const bool contentChanged = newHash != _lastHash;
|
||||
_lastHash = newHash;
|
||||
return contentChanged;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckChangeDate()
|
||||
{
|
||||
if (_fileType == FileType::REMOTE) {
|
||||
return checkRemoteFileContent();
|
||||
} else {
|
||||
return checkLocalFileContent();
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(QString::fromStdString(_file));
|
||||
QDateTime newLastMod = QFileInfo(file).lastModified();
|
||||
const bool dateChanged = _lastMod != newLastMod;
|
||||
_lastMod = newLastMod;
|
||||
return dateChanged;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckCondition()
|
||||
{
|
||||
switch (_condition) {
|
||||
case MacroConditionFile::ConditionType::MATCH:
|
||||
if (_fileType == FileType::REMOTE) {
|
||||
return CheckRemoteFileContent();
|
||||
}
|
||||
return CheckLocalFileContent();
|
||||
case MacroConditionFile::ConditionType::CONTENT_CHANGE:
|
||||
return CheckChangeContent();
|
||||
case MacroConditionFile::ConditionType::DATE_CHANGE:
|
||||
return CheckChangeDate();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_regex.Save(obj);
|
||||
obs_data_set_string(obj, "file", _file.c_str());
|
||||
obs_data_set_string(obj, "text", _text.c_str());
|
||||
obs_data_set_int(obj, "fileType", static_cast<int>(_fileType));
|
||||
obs_data_set_bool(obj, "useRegex", _useRegex);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_bool(obj, "useTime", _useTime);
|
||||
obs_data_set_bool(obj, "onlyMatchIfChanged", _onlyMatchIfChanged);
|
||||
return true;
|
||||
@@ -117,10 +166,17 @@ bool MacroConditionFile::Save(obs_data_t *obj)
|
||||
bool MacroConditionFile::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_regex.Load(obj);
|
||||
// TODO: remove in future version
|
||||
if (obs_data_has_user_value(obj, "useRegex")) {
|
||||
_regex.CreateBackwardsCompatibleRegex(
|
||||
obs_data_get_bool(obj, "useRegex"));
|
||||
}
|
||||
_file = obs_data_get_string(obj, "file");
|
||||
_text = obs_data_get_string(obj, "text");
|
||||
_fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType"));
|
||||
_useRegex = obs_data_get_bool(obj, "useRegex");
|
||||
_condition =
|
||||
static_cast<ConditionType>(obs_data_get_int(obj, "condition"));
|
||||
_useTime = obs_data_get_bool(obj, "useTime");
|
||||
_onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
||||
return true;
|
||||
@@ -131,41 +187,59 @@ std::string MacroConditionFile::GetShortDesc()
|
||||
return _file;
|
||||
}
|
||||
|
||||
static void populateFileTypes(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.fileTab.local"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.fileTab.remote"));
|
||||
}
|
||||
|
||||
static void populateConditions(QComboBox *list)
|
||||
{
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.type.match"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.file.type.contentChange"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.file.type.dateChange"));
|
||||
}
|
||||
|
||||
MacroConditionFileEdit::MacroConditionFileEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionFile> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_fileTypes(new QComboBox()),
|
||||
_conditions(new QComboBox()),
|
||||
_filePath(new FileSelection()),
|
||||
_matchText(new ResizingPlainTextEdit(this)),
|
||||
_regex(new RegexConfigWidget(parent)),
|
||||
_checkModificationDate(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.fileTab.checkfileContentTime"))),
|
||||
_checkFileContent(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.checkfileContent")))
|
||||
{
|
||||
_fileType = new QComboBox();
|
||||
_filePath = new FileSelection();
|
||||
_matchText = new ResizingPlainTextEdit(this);
|
||||
_useRegex = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.useRegExp"));
|
||||
_checkModificationDate = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.fileTab.checkfileContentTime"));
|
||||
_checkFileContent = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.checkfileContent"));
|
||||
populateFileTypes(_fileTypes);
|
||||
populateConditions(_conditions);
|
||||
|
||||
QWidget::connect(_fileType, SIGNAL(currentIndexChanged(int)), this,
|
||||
QWidget::connect(_fileTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(FileTypeChanged(int)));
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(PathChanged(const QString &)));
|
||||
QWidget::connect(_matchText, SIGNAL(textChanged()), this,
|
||||
SLOT(MatchTextChanged()));
|
||||
QWidget::connect(_useRegex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseRegexChanged(int)));
|
||||
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||
SLOT(RegexChanged(RegexConfig)));
|
||||
QWidget::connect(_checkModificationDate, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(CheckModificationDateChanged(int)));
|
||||
QWidget::connect(_checkFileContent, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(OnlyMatchIfChangedChanged(int)));
|
||||
|
||||
_fileType->addItem(obs_module_text("AdvSceneSwitcher.fileTab.local"));
|
||||
_fileType->addItem(obs_module_text("AdvSceneSwitcher.fileTab.remote"));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{fileType}}", _fileType},
|
||||
{"{{fileType}}", _fileTypes},
|
||||
{"{{conditions}}", _conditions},
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{matchText}}", _matchText},
|
||||
{"{{useRegex}}", _useRegex},
|
||||
{"{{useRegex}}", _regex},
|
||||
{"{{checkModificationDate}}", _checkModificationDate},
|
||||
{"{{checkFileContent}}", _checkFileContent},
|
||||
};
|
||||
@@ -200,16 +274,23 @@ void MacroConditionFileEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_fileType->setCurrentIndex(static_cast<int>(_entryData->_fileType));
|
||||
|
||||
_fileTypes->setCurrentIndex(static_cast<int>(_entryData->_fileType));
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_filePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||
_matchText->setPlainText(QString::fromStdString(_entryData->_text));
|
||||
_useRegex->setChecked(_entryData->_useRegex);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
_checkModificationDate->setChecked(_entryData->_useTime);
|
||||
_checkFileContent->setChecked(_entryData->_onlyMatchIfChanged);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
// TODO: Remove in future version
|
||||
if (!_entryData->_useTime) {
|
||||
_checkModificationDate->hide();
|
||||
}
|
||||
if (!_entryData->_onlyMatchIfChanged) {
|
||||
_checkFileContent->hide();
|
||||
}
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::FileTypeChanged(int index)
|
||||
@@ -218,9 +299,10 @@ void MacroConditionFileEdit::FileTypeChanged(int index)
|
||||
return;
|
||||
}
|
||||
|
||||
FileType type = static_cast<FileType>(index);
|
||||
MacroConditionFile::FileType type =
|
||||
static_cast<MacroConditionFile::FileType>(index);
|
||||
|
||||
if (type == FileType::LOCAL) {
|
||||
if (type == MacroConditionFile::FileType::LOCAL) {
|
||||
_filePath->Button()->setDisabled(false);
|
||||
_checkModificationDate->setDisabled(false);
|
||||
} else {
|
||||
@@ -232,6 +314,18 @@ void MacroConditionFileEdit::FileTypeChanged(int index)
|
||||
_entryData->_fileType = type;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition =
|
||||
static_cast<MacroConditionFile::ConditionType>(index);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::PathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -257,14 +351,16 @@ void MacroConditionFileEdit::MatchTextChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::UseRegexChanged(int state)
|
||||
void MacroConditionFileEdit::RegexChanged(RegexConfig conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_useRegex = state;
|
||||
_entryData->_regex = conf;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::CheckModificationDateChanged(int state)
|
||||
@@ -286,3 +382,25 @@ void MacroConditionFileEdit::OnlyMatchIfChangedChanged(int state)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_onlyMatchIfChanged = state;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_matchText->setVisible(_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_regex->setVisible(_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_checkModificationDate->setVisible(
|
||||
_entryData->_useTime &&
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_checkFileContent->setVisible(
|
||||
_entryData->_onlyMatchIfChanged &&
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "macro.hpp"
|
||||
#include "file-selection.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
@@ -10,11 +11,6 @@
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
|
||||
enum class FileType {
|
||||
LOCAL,
|
||||
REMOTE,
|
||||
};
|
||||
|
||||
class MacroConditionFile : public MacroCondition {
|
||||
public:
|
||||
MacroConditionFile(Macro *m) : MacroCondition(m) {}
|
||||
@@ -28,17 +24,33 @@ public:
|
||||
return std::make_shared<MacroConditionFile>(m);
|
||||
}
|
||||
|
||||
enum class FileType {
|
||||
LOCAL,
|
||||
REMOTE,
|
||||
};
|
||||
|
||||
enum class ConditionType {
|
||||
MATCH,
|
||||
CONTENT_CHANGE,
|
||||
DATE_CHANGE,
|
||||
};
|
||||
|
||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
FileType _fileType = FileType::LOCAL;
|
||||
bool _useRegex = false;
|
||||
ConditionType _condition = ConditionType::MATCH;
|
||||
RegexConfig _regex;
|
||||
|
||||
// TODO: Remove in future version
|
||||
bool _useTime = false;
|
||||
bool _onlyMatchIfChanged = false;
|
||||
|
||||
private:
|
||||
bool matchFileContent(QString &filedata);
|
||||
bool checkRemoteFileContent();
|
||||
bool checkLocalFileContent();
|
||||
bool MatchFileContent(QString &filedata);
|
||||
bool CheckRemoteFileContent();
|
||||
bool CheckLocalFileContent();
|
||||
bool CheckChangeContent();
|
||||
bool CheckChangeDate();
|
||||
|
||||
QDateTime _lastMod;
|
||||
size_t _lastHash = 0;
|
||||
@@ -64,23 +76,27 @@ public:
|
||||
|
||||
private slots:
|
||||
void FileTypeChanged(int index);
|
||||
void ConditionChanged(int index);
|
||||
void PathChanged(const QString &text);
|
||||
void MatchTextChanged();
|
||||
void UseRegexChanged(int state);
|
||||
void RegexChanged(RegexConfig);
|
||||
void CheckModificationDateChanged(int state);
|
||||
void OnlyMatchIfChangedChanged(int state);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_fileType;
|
||||
QComboBox *_fileTypes;
|
||||
QComboBox *_conditions;
|
||||
FileSelection *_filePath;
|
||||
ResizingPlainTextEdit *_matchText;
|
||||
QCheckBox *_useRegex;
|
||||
RegexConfigWidget *_regex;
|
||||
QCheckBox *_checkModificationDate;
|
||||
QCheckBox *_checkFileContent;
|
||||
std::shared_ptr<MacroConditionFile> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ bool MacroConditionFilter::Save(obs_data_t *obj)
|
||||
obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str());
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||
obs_data_set_bool(obj, "regex", _regex);
|
||||
_regex.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,12 @@ bool MacroConditionFilter::Load(obs_data_t *obj)
|
||||
_condition = static_cast<FilterCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_settings = obs_data_get_string(obj, "settings");
|
||||
_regex = obs_data_get_bool(obj, "regex");
|
||||
_regex.Load(obj);
|
||||
// TOOD: remove in future version
|
||||
if (obs_data_has_user_value(obj, "regex")) {
|
||||
_regex.CreateBackwardsCompatibleRegex(
|
||||
obs_data_get_bool(obj, "regex"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -92,18 +97,16 @@ static inline void populateConditionSelection(QComboBox *list)
|
||||
|
||||
MacroConditionFilterEdit::MacroConditionFilterEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionFilter> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_sources(new QComboBox()),
|
||||
_filters(new QComboBox()),
|
||||
_conditions(new QComboBox()),
|
||||
_getSettings(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.filter.getSettings"))),
|
||||
_settings(new ResizingPlainTextEdit(this)),
|
||||
_regex(new RegexConfigWidget(parent))
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
_filters = new QComboBox();
|
||||
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_conditions = new QComboBox();
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.filter.getSettings"));
|
||||
_settings = new ResizingPlainTextEdit(this);
|
||||
_regex = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.condition.filter.regex"));
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
populateSourcesWithFilterSelection(_sources);
|
||||
|
||||
@@ -117,8 +120,8 @@ MacroConditionFilterEdit::MacroConditionFilterEdit(
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
SLOT(SettingsChanged()));
|
||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(RegexChanged(int)));
|
||||
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||
SLOT(RegexChanged(RegexConfig)));
|
||||
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
@@ -194,7 +197,7 @@ void MacroConditionFilterEdit::GetSettingsClicked()
|
||||
}
|
||||
|
||||
QString json = formatJsonString(getSourceSettings(_entryData->_filter));
|
||||
if (_entryData->_regex) {
|
||||
if (_entryData->_regex.Enabled()) {
|
||||
json = escapeForRegex(json);
|
||||
}
|
||||
_settings->setPlainText(json);
|
||||
@@ -213,14 +216,17 @@ void MacroConditionFilterEdit::SettingsChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionFilterEdit::RegexChanged(int state)
|
||||
void MacroConditionFilterEdit::RegexChanged(RegexConfig conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_regex = state;
|
||||
_entryData->_regex = conf;
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionFilterEdit::SetSettingsSelectionVisible(bool visible)
|
||||
@@ -244,7 +250,7 @@ void MacroConditionFilterEdit::UpdateEntryData()
|
||||
GetWeakSourceName(_entryData->_filter).c_str());
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||
_regex->setChecked(_entryData->_regex);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
SetSettingsSelectionVisible(_entryData->_condition ==
|
||||
FilterCondition::SETTINGS);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
@@ -29,7 +30,7 @@ public:
|
||||
OBSWeakSource _filter;
|
||||
FilterCondition _condition = FilterCondition::ENABLED;
|
||||
std::string _settings = "";
|
||||
bool _regex = false;
|
||||
RegexConfig _regex;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -58,7 +59,7 @@ private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
void RegexChanged(int);
|
||||
void RegexChanged(RegexConfig);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -68,7 +69,7 @@ protected:
|
||||
QComboBox *_conditions;
|
||||
QPushButton *_getSettings;
|
||||
ResizingPlainTextEdit *_settings;
|
||||
QCheckBox *_regex;
|
||||
RegexConfigWidget *_regex;
|
||||
|
||||
std::shared_ptr<MacroConditionFilter> _entryData;
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ bool MacroConditionMacro::Save(obs_data_t *obj)
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_counterCondition));
|
||||
obs_data_set_int(obj, "count", _count);
|
||||
obs_data_set_int(obj, "multiStateCount", _multiSateCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,6 +137,7 @@ bool MacroConditionMacro::Load(obs_data_t *obj)
|
||||
_counterCondition = static_cast<CounterCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_count = obs_data_get_int(obj, "count");
|
||||
_multiSateCount = obs_data_get_int(obj, "multiStateCount");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,39 +10,40 @@ bool MacroConditionMedia::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionMedia::Create, MacroConditionMediaEdit::Create,
|
||||
"AdvSceneSwitcher.condition.media"});
|
||||
|
||||
static std::map<MediaTimeRestriction, std::string> mediaTimeRestrictions = {
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_NONE,
|
||||
static std::map<MacroConditionMedia::Time, std::string> mediaTimeRestrictions = {
|
||||
{MacroConditionMedia::Time::TIME_RESTRICTION_NONE,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.none"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_SHORTER,
|
||||
{MacroConditionMedia::Time::TIME_RESTRICTION_SHORTER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.shorter"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_LONGER,
|
||||
{MacroConditionMedia::Time::TIME_RESTRICTION_LONGER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.longer"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER,
|
||||
{MacroConditionMedia::Time::TIME_RESTRICTION_REMAINING_SHORTER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER,
|
||||
{MacroConditionMedia::Time::TIME_RESTRICTION_REMAINING_LONGER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger"},
|
||||
};
|
||||
|
||||
static std::map<MediaState, std::string> mediaStates = {
|
||||
{MediaState::OBS_MEDIA_STATE_NONE,
|
||||
static std::map<MacroConditionMedia::State, std::string> mediaStates = {
|
||||
{MacroConditionMedia::State::OBS_MEDIA_STATE_NONE,
|
||||
"AdvSceneSwitcher.mediaTab.states.none"},
|
||||
{MediaState::OBS_MEDIA_STATE_PLAYING,
|
||||
{MacroConditionMedia::State::OBS_MEDIA_STATE_PLAYING,
|
||||
"AdvSceneSwitcher.mediaTab.states.playing"},
|
||||
{MediaState::OBS_MEDIA_STATE_OPENING,
|
||||
{MacroConditionMedia::State::OBS_MEDIA_STATE_OPENING,
|
||||
"AdvSceneSwitcher.mediaTab.states.opening"},
|
||||
{MediaState::OBS_MEDIA_STATE_BUFFERING,
|
||||
{MacroConditionMedia::State::OBS_MEDIA_STATE_BUFFERING,
|
||||
"AdvSceneSwitcher.mediaTab.states.buffering"},
|
||||
{MediaState::OBS_MEDIA_STATE_PAUSED,
|
||||
{MacroConditionMedia::State::OBS_MEDIA_STATE_PAUSED,
|
||||
"AdvSceneSwitcher.mediaTab.states.paused"},
|
||||
{MediaState::OBS_MEDIA_STATE_STOPPED,
|
||||
{MacroConditionMedia::State::OBS_MEDIA_STATE_STOPPED,
|
||||
"AdvSceneSwitcher.mediaTab.states.stopped"},
|
||||
{MediaState::OBS_MEDIA_STATE_ENDED,
|
||||
{MacroConditionMedia::State::OBS_MEDIA_STATE_ENDED,
|
||||
"AdvSceneSwitcher.mediaTab.states.ended"},
|
||||
{MediaState::OBS_MEDIA_STATE_ERROR,
|
||||
{MacroConditionMedia::State::OBS_MEDIA_STATE_ERROR,
|
||||
"AdvSceneSwitcher.mediaTab.states.error"},
|
||||
{MediaState::PLAYLIST_ENDED,
|
||||
{MacroConditionMedia::State::PLAYLIST_ENDED,
|
||||
"AdvSceneSwitcher.mediaTab.states.playlistEnd"},
|
||||
{MediaState::ANY, "AdvSceneSwitcher.mediaTab.states.any"},
|
||||
{MacroConditionMedia::State::ANY,
|
||||
"AdvSceneSwitcher.mediaTab.states.any"},
|
||||
};
|
||||
|
||||
MacroConditionMedia::~MacroConditionMedia()
|
||||
@@ -65,20 +66,20 @@ bool MacroConditionMedia::CheckTime()
|
||||
bool match = false;
|
||||
|
||||
switch (_restriction) {
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_NONE:
|
||||
case Time::TIME_RESTRICTION_NONE:
|
||||
match = true;
|
||||
break;
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_SHORTER:
|
||||
case Time::TIME_RESTRICTION_SHORTER:
|
||||
match = currentTime < _time.seconds * 1000;
|
||||
break;
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_LONGER:
|
||||
case Time::TIME_RESTRICTION_LONGER:
|
||||
match = currentTime > _time.seconds * 1000;
|
||||
break;
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER:
|
||||
case Time::TIME_RESTRICTION_REMAINING_SHORTER:
|
||||
match = duration > currentTime &&
|
||||
duration - currentTime < _time.seconds * 1000;
|
||||
break;
|
||||
case MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER:
|
||||
case Time::TIME_RESTRICTION_REMAINING_LONGER:
|
||||
match = duration > currentTime &&
|
||||
duration - currentTime > _time.seconds * 1000;
|
||||
break;
|
||||
@@ -100,24 +101,24 @@ bool MacroConditionMedia::CheckState()
|
||||
int expectedState = static_cast<int>(_state);
|
||||
|
||||
switch (_state) {
|
||||
case MediaState::OBS_MEDIA_STATE_STOPPED:
|
||||
case State::OBS_MEDIA_STATE_STOPPED:
|
||||
match = _stopped || currentState == OBS_MEDIA_STATE_STOPPED;
|
||||
break;
|
||||
case MediaState::OBS_MEDIA_STATE_ENDED:
|
||||
case State::OBS_MEDIA_STATE_ENDED:
|
||||
match = _ended || currentState == OBS_MEDIA_STATE_ENDED;
|
||||
break;
|
||||
case MediaState::PLAYLIST_ENDED:
|
||||
case State::PLAYLIST_ENDED:
|
||||
match = CheckPlaylistEnd(currentState);
|
||||
break;
|
||||
case MediaState::ANY:
|
||||
case State::ANY:
|
||||
match = true;
|
||||
break;
|
||||
case MediaState::OBS_MEDIA_STATE_NONE:
|
||||
case MediaState::OBS_MEDIA_STATE_PLAYING:
|
||||
case MediaState::OBS_MEDIA_STATE_OPENING:
|
||||
case MediaState::OBS_MEDIA_STATE_BUFFERING:
|
||||
case MediaState::OBS_MEDIA_STATE_PAUSED:
|
||||
case MediaState::OBS_MEDIA_STATE_ERROR:
|
||||
case State::OBS_MEDIA_STATE_NONE:
|
||||
case State::OBS_MEDIA_STATE_PLAYING:
|
||||
case State::OBS_MEDIA_STATE_OPENING:
|
||||
case State::OBS_MEDIA_STATE_BUFFERING:
|
||||
case State::OBS_MEDIA_STATE_PAUSED:
|
||||
case State::OBS_MEDIA_STATE_ERROR:
|
||||
match = currentState == expectedState;
|
||||
break;
|
||||
default:
|
||||
@@ -148,7 +149,7 @@ bool MacroConditionMedia::CheckMediaMatch()
|
||||
bool match = false;
|
||||
bool matched = CheckState() && CheckTime();
|
||||
|
||||
if (matched && !(_onlyMatchonChagne && _alreadyMatched)) {
|
||||
if (matched && !(_onlyMatchOnChagne && _alreadyMatched)) {
|
||||
match = true;
|
||||
}
|
||||
_alreadyMatched = matched;
|
||||
@@ -161,16 +162,22 @@ bool MacroConditionMedia::CheckMediaMatch()
|
||||
return match;
|
||||
}
|
||||
|
||||
void MacroConditionMedia::HandleSceneChange()
|
||||
{
|
||||
UpdateMediaSourcesOfSceneList();
|
||||
_lastConfigureScene = switcher->currentScene;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::CheckCondition()
|
||||
{
|
||||
bool match = false;
|
||||
switch (_sourceType) {
|
||||
case MediaSourceType::ANY:
|
||||
case Type::ANY:
|
||||
for (auto &source : _sources) {
|
||||
match = match || source.CheckCondition();
|
||||
}
|
||||
break;
|
||||
case MediaSourceType::ALL: {
|
||||
case Type::ALL: {
|
||||
bool res = true;
|
||||
for (auto &source : _sources) {
|
||||
res = res && source.CheckCondition();
|
||||
@@ -178,12 +185,17 @@ bool MacroConditionMedia::CheckCondition()
|
||||
match = res;
|
||||
break;
|
||||
}
|
||||
case MediaSourceType::SOURCE:
|
||||
case Type::SOURCE:
|
||||
match = CheckMediaMatch();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (_lastConfigureScene != switcher->currentScene) {
|
||||
HandleSceneChange();
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
@@ -196,7 +208,7 @@ bool MacroConditionMedia::Save(obs_data_t *obj)
|
||||
obs_data_set_int(obj, "state", static_cast<int>(_state));
|
||||
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
|
||||
_time.Save(obj);
|
||||
obs_data_set_bool(obj, "matchOnChagne", _onlyMatchonChagne);
|
||||
obs_data_set_bool(obj, "matchOnChagne", _onlyMatchOnChagne);
|
||||
obs_data_set_int(obj, "version", 0);
|
||||
return true;
|
||||
}
|
||||
@@ -222,22 +234,24 @@ static bool enumSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
return true;
|
||||
}
|
||||
|
||||
void forMediaSourceOnSceneAddMediaCondition(
|
||||
OBSWeakSource sceneWeakSource, MacroConditionMedia *origCond,
|
||||
std::vector<MacroConditionMedia> &conditions)
|
||||
void MacroConditionMedia::UpdateMediaSourcesOfSceneList()
|
||||
{
|
||||
conditions.clear();
|
||||
_sources.clear();
|
||||
if (!_scene.GetScene(false)) {
|
||||
return;
|
||||
}
|
||||
std::vector<OBSWeakSource> mediaSources;
|
||||
auto s = obs_weak_source_get_source(sceneWeakSource);
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene(false));
|
||||
auto scene = obs_scene_from_source(s);
|
||||
obs_scene_enum_items(scene, enumSceneItem, &mediaSources);
|
||||
obs_source_release(s);
|
||||
_sources.reserve(mediaSources.size());
|
||||
|
||||
for (auto &source : mediaSources) {
|
||||
MacroConditionMedia cond(*origCond);
|
||||
cond._sourceType = MediaSourceType::SOURCE;
|
||||
MacroConditionMedia cond(*this);
|
||||
cond._sourceType = Type::SOURCE;
|
||||
cond._source = source;
|
||||
conditions.push_back(cond);
|
||||
_sources.push_back(cond);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,15 +261,15 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_scene.Load(obj);
|
||||
_sourceType = static_cast<MediaSourceType>(
|
||||
obs_data_get_int(obj, "sourceType"));
|
||||
_state = static_cast<MediaState>(obs_data_get_int(obj, "state"));
|
||||
_restriction = static_cast<MediaTimeRestriction>(
|
||||
_sourceType = static_cast<Type>(obs_data_get_int(obj, "sourceType"));
|
||||
_state = static_cast<MacroConditionMedia::State>(
|
||||
obs_data_get_int(obj, "state"));
|
||||
_restriction = static_cast<MacroConditionMedia::Time>(
|
||||
obs_data_get_int(obj, "restriction"));
|
||||
_time.Load(obj);
|
||||
_onlyMatchonChagne = obs_data_get_bool(obj, "matchOnChagne");
|
||||
_onlyMatchOnChagne = obs_data_get_bool(obj, "matchOnChagne");
|
||||
|
||||
if (_sourceType == MediaSourceType::SOURCE) {
|
||||
if (_sourceType == Type::SOURCE) {
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
signal_handler_t *sh =
|
||||
obs_source_get_signal_handler(mediasource);
|
||||
@@ -265,11 +279,11 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
forMediaSourceOnSceneAddMediaCondition(_scene.GetScene(), this,
|
||||
_sources);
|
||||
UpdateMediaSourcesOfSceneList();
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
if (_state == MediaState::OBS_MEDIA_STATE_ENDED) {
|
||||
_state = MediaState::PLAYLIST_ENDED;
|
||||
if (_state ==
|
||||
MacroConditionMedia::State::OBS_MEDIA_STATE_ENDED) {
|
||||
_state = MacroConditionMedia::State::PLAYLIST_ENDED;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -278,20 +292,20 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
|
||||
std::string MacroConditionMedia::GetShortDesc()
|
||||
{
|
||||
switch (_sourceType) {
|
||||
case MediaSourceType::SOURCE:
|
||||
case Type::SOURCE:
|
||||
if (_source) {
|
||||
return GetWeakSourceName(_source);
|
||||
}
|
||||
break;
|
||||
case MediaSourceType::ANY:
|
||||
if (_scene.GetScene()) {
|
||||
case Type::ANY:
|
||||
if (_scene.GetScene(false)) {
|
||||
return obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.anyOnScene") +
|
||||
std::string(" ") + _scene.ToString();
|
||||
}
|
||||
break;
|
||||
case MediaSourceType::ALL:
|
||||
if (_scene.GetScene()) {
|
||||
case Type::ALL:
|
||||
if (_scene.GetScene(false)) {
|
||||
return obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.allOnScene") +
|
||||
std::string(" ") + _scene.ToString();
|
||||
@@ -356,7 +370,7 @@ void MacroConditionMedia::MediaNext(void *data, calldata_t *)
|
||||
media->_next = true;
|
||||
}
|
||||
|
||||
static void populateMediaTimeRestrictions(QComboBox *list)
|
||||
static void populateMediaTimes(QComboBox *list)
|
||||
{
|
||||
for (auto entry : mediaTimeRestrictions) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
@@ -383,7 +397,8 @@ static void addAnyAndAllStates(QComboBox *list)
|
||||
MacroConditionMediaEdit::MacroConditionMediaEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData)
|
||||
: QWidget(parent),
|
||||
_scenes(new SceneSelectionWidget(window())),
|
||||
_scenes(new SceneSelectionWidget(window(), true, true, true, true,
|
||||
true)),
|
||||
_mediaSources(new QComboBox()),
|
||||
_states(new QComboBox()),
|
||||
_timeRestrictions(new QComboBox()),
|
||||
@@ -414,7 +429,7 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
|
||||
populateMediaSelection(_mediaSources);
|
||||
addAnyAndAllStates(_mediaSources);
|
||||
populateMediaStates(_states);
|
||||
populateMediaTimeRestrictions(_timeRestrictions);
|
||||
populateMediaTimes(_timeRestrictions);
|
||||
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
@@ -446,14 +461,14 @@ void MacroConditionMediaEdit::SourceChanged(const QString &text)
|
||||
|
||||
if (text ==
|
||||
obs_module_text("AdvSceneSwitcher.condition.media.anyOnScene")) {
|
||||
_entryData->_sourceType = MediaSourceType::ANY;
|
||||
_entryData->_sourceType = MacroConditionMedia::Type::ANY;
|
||||
} else if (text ==
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.allOnScene")) {
|
||||
_entryData->_sourceType = MediaSourceType::ALL;
|
||||
_entryData->_sourceType = MacroConditionMedia::Type::ALL;
|
||||
} else {
|
||||
_entryData->_sources.clear();
|
||||
_entryData->_sourceType = MediaSourceType::SOURCE;
|
||||
_entryData->_sourceType = MacroConditionMedia::Type::SOURCE;
|
||||
}
|
||||
|
||||
_entryData->ClearSignalHandler();
|
||||
@@ -473,21 +488,21 @@ void MacroConditionMediaEdit::SceneChanged(const SceneSelection &s)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
forMediaSourceOnSceneAddMediaCondition(_entryData->_scene.GetScene(),
|
||||
_entryData.get(),
|
||||
_entryData->_sources);
|
||||
_entryData->UpdateMediaSourcesOfSceneList();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
MediaState getMediaStateFromIdx(int idx)
|
||||
MacroConditionMedia::State getMediaStateFromIdx(int idx)
|
||||
{
|
||||
if (idx < static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE)) {
|
||||
return static_cast<MediaState>(idx);
|
||||
if (idx < static_cast<int>(
|
||||
MacroConditionMedia::State::LAST_OBS_MEDIA_STATE)) {
|
||||
return static_cast<MacroConditionMedia::State>(idx);
|
||||
} else {
|
||||
return static_cast<MediaState>(
|
||||
return static_cast<MacroConditionMedia::State>(
|
||||
idx -
|
||||
static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE) +
|
||||
static_cast<int>(
|
||||
MacroConditionMedia::State::LAST_OBS_MEDIA_STATE) +
|
||||
custom_media_states_offset);
|
||||
}
|
||||
}
|
||||
@@ -500,10 +515,8 @@ void MacroConditionMediaEdit::StateChanged(int index)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_state = getMediaStateFromIdx(index);
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
|
||||
_entryData->UpdateMediaSourcesOfSceneList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,19 +526,18 @@ void MacroConditionMediaEdit::TimeRestrictionChanged(int index)
|
||||
return;
|
||||
}
|
||||
|
||||
if (static_cast<MediaTimeRestriction>(index) ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE) {
|
||||
if (static_cast<MacroConditionMedia::Time>(index) ==
|
||||
MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
|
||||
_time->setDisabled(true);
|
||||
} else {
|
||||
_time->setDisabled(false);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_restriction = static_cast<MediaTimeRestriction>(index);
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
_entryData->_restriction =
|
||||
static_cast<MacroConditionMedia::Time>(index);
|
||||
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
|
||||
_entryData->UpdateMediaSourcesOfSceneList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,10 +549,8 @@ void MacroConditionMediaEdit::TimeChanged(double seconds)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_time.seconds = seconds;
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
|
||||
_entryData->UpdateMediaSourcesOfSceneList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,10 +562,8 @@ void MacroConditionMediaEdit::TimeUnitChanged(DurationUnit unit)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_time.displayUnit = unit;
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
|
||||
_entryData->UpdateMediaSourcesOfSceneList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,29 +574,29 @@ void MacroConditionMediaEdit::OnChangeChanged(int value)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_onlyMatchonChagne = value;
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
_entryData->_onlyMatchOnChagne = value;
|
||||
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
|
||||
_entryData->UpdateMediaSourcesOfSceneList();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::SetWidgetVisibility()
|
||||
{
|
||||
_scenes->setVisible(_entryData->_sourceType != MediaSourceType::SOURCE);
|
||||
_scenes->setVisible(_entryData->_sourceType !=
|
||||
MacroConditionMedia::Type::SOURCE);
|
||||
if (!_onChange->isChecked()) {
|
||||
_onChange->hide();
|
||||
}
|
||||
}
|
||||
|
||||
int getIdxFromMediaState(MediaState state)
|
||||
int getIdxFromMediaState(MacroConditionMedia::State state)
|
||||
{
|
||||
if (state < MediaState::LAST_OBS_MEDIA_STATE) {
|
||||
if (state < MacroConditionMedia::State::LAST_OBS_MEDIA_STATE) {
|
||||
return static_cast<int>(state);
|
||||
} else {
|
||||
return static_cast<int>(state) - custom_media_states_offset +
|
||||
static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE);
|
||||
static_cast<int>(
|
||||
MacroConditionMedia::State::LAST_OBS_MEDIA_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,15 +607,15 @@ void MacroConditionMediaEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
switch (_entryData->_sourceType) {
|
||||
case MediaSourceType::ANY:
|
||||
case MacroConditionMedia::Type::ANY:
|
||||
_mediaSources->setCurrentText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.anyOnScene"));
|
||||
break;
|
||||
case MediaSourceType::ALL:
|
||||
case MacroConditionMedia::Type::ALL:
|
||||
_mediaSources->setCurrentText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.allOnScene"));
|
||||
break;
|
||||
case MediaSourceType::SOURCE:
|
||||
case MacroConditionMedia::Type::SOURCE:
|
||||
_mediaSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
break;
|
||||
@@ -621,9 +629,9 @@ void MacroConditionMediaEdit::UpdateEntryData()
|
||||
static_cast<int>(_entryData->_restriction));
|
||||
_time->SetDuration(_entryData->_time);
|
||||
if (_entryData->_restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE) {
|
||||
MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
|
||||
_time->setDisabled(true);
|
||||
}
|
||||
_onChange->setChecked(_entryData->_onlyMatchonChagne);
|
||||
_onChange->setChecked(_entryData->_onlyMatchOnChagne);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -8,39 +8,8 @@
|
||||
#include "duration-control.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
|
||||
enum class MediaTimeRestriction {
|
||||
TIME_RESTRICTION_NONE,
|
||||
TIME_RESTRICTION_SHORTER,
|
||||
TIME_RESTRICTION_LONGER,
|
||||
TIME_RESTRICTION_REMAINING_SHORTER,
|
||||
TIME_RESTRICTION_REMAINING_LONGER,
|
||||
};
|
||||
|
||||
constexpr auto custom_media_states_offset = 100;
|
||||
|
||||
enum class MediaState {
|
||||
// OBS's internal states
|
||||
OBS_MEDIA_STATE_NONE,
|
||||
OBS_MEDIA_STATE_PLAYING,
|
||||
OBS_MEDIA_STATE_OPENING,
|
||||
OBS_MEDIA_STATE_BUFFERING,
|
||||
OBS_MEDIA_STATE_PAUSED,
|
||||
OBS_MEDIA_STATE_STOPPED,
|
||||
OBS_MEDIA_STATE_ENDED,
|
||||
OBS_MEDIA_STATE_ERROR,
|
||||
// Just a marker
|
||||
LAST_OBS_MEDIA_STATE,
|
||||
// states added for use in the plugin
|
||||
PLAYLIST_ENDED = custom_media_states_offset,
|
||||
ANY,
|
||||
};
|
||||
|
||||
enum class MediaSourceType {
|
||||
SOURCE,
|
||||
ANY,
|
||||
ALL,
|
||||
};
|
||||
|
||||
class MacroConditionMedia : public MacroCondition {
|
||||
public:
|
||||
MacroConditionMedia(Macro *m) : MacroCondition(m) {}
|
||||
@@ -56,25 +25,57 @@ public:
|
||||
}
|
||||
void ClearSignalHandler();
|
||||
void ResetSignalHandler();
|
||||
void UpdateMediaSourcesOfSceneList();
|
||||
static void MediaStopped(void *data, calldata_t *);
|
||||
static void MediaEnded(void *data, calldata_t *);
|
||||
static void MediaNext(void *data, calldata_t *);
|
||||
|
||||
MediaSourceType _sourceType = MediaSourceType::SOURCE;
|
||||
enum class Type {
|
||||
SOURCE,
|
||||
ANY,
|
||||
ALL,
|
||||
};
|
||||
Type _sourceType = Type::SOURCE;
|
||||
|
||||
enum class State {
|
||||
// OBS's internal states
|
||||
OBS_MEDIA_STATE_NONE,
|
||||
OBS_MEDIA_STATE_PLAYING,
|
||||
OBS_MEDIA_STATE_OPENING,
|
||||
OBS_MEDIA_STATE_BUFFERING,
|
||||
OBS_MEDIA_STATE_PAUSED,
|
||||
OBS_MEDIA_STATE_STOPPED,
|
||||
OBS_MEDIA_STATE_ENDED,
|
||||
OBS_MEDIA_STATE_ERROR,
|
||||
// Just a marker
|
||||
LAST_OBS_MEDIA_STATE,
|
||||
// states added for use in the plugin
|
||||
PLAYLIST_ENDED = custom_media_states_offset,
|
||||
ANY,
|
||||
};
|
||||
State _state = State::OBS_MEDIA_STATE_NONE;
|
||||
|
||||
enum class Time {
|
||||
TIME_RESTRICTION_NONE,
|
||||
TIME_RESTRICTION_SHORTER,
|
||||
TIME_RESTRICTION_LONGER,
|
||||
TIME_RESTRICTION_REMAINING_SHORTER,
|
||||
TIME_RESTRICTION_REMAINING_LONGER,
|
||||
};
|
||||
Time _restriction = Time::TIME_RESTRICTION_NONE;
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source = nullptr;
|
||||
std::vector<MacroConditionMedia> _sources;
|
||||
MediaState _state = MediaState::OBS_MEDIA_STATE_NONE;
|
||||
MediaTimeRestriction _restriction =
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE;
|
||||
Duration _time;
|
||||
bool _onlyMatchonChagne = false;
|
||||
bool _onlyMatchOnChagne = false;
|
||||
|
||||
private:
|
||||
bool CheckTime();
|
||||
bool CheckState();
|
||||
bool CheckPlaylistEnd(const obs_media_state);
|
||||
bool CheckMediaMatch();
|
||||
void HandleSceneChange();
|
||||
|
||||
bool _stopped = false;
|
||||
bool _ended = false;
|
||||
@@ -84,6 +85,8 @@ private:
|
||||
bool _alreadyMatched = false;
|
||||
// Workaround to enable use of "ended" to specify end of VLC playlist
|
||||
bool _previousStateEnded = false;
|
||||
// Used to keep track of scene changes
|
||||
OBSWeakSource _lastConfigureScene;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
|
||||
@@ -48,36 +48,48 @@ std::string MacroConditionProcess::GetShortDesc()
|
||||
|
||||
MacroConditionProcessEdit::MacroConditionProcessEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionProcess> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_processSelection(new QComboBox()),
|
||||
_focused(new QCheckBox()),
|
||||
_focusProcess(new QLabel()),
|
||||
_focusLayout(new QHBoxLayout())
|
||||
{
|
||||
_processSelection = new QComboBox();
|
||||
_processSelection->setEditable(true);
|
||||
_processSelection->setMaxVisibleItems(20);
|
||||
|
||||
_focused = new QCheckBox();
|
||||
|
||||
QWidget::connect(_processSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ProcessChanged(const QString &)));
|
||||
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FocusChanged(int)));
|
||||
QWidget::connect(&_timer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateFocusProcess()));
|
||||
|
||||
populateProcessSelection(_processSelection);
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{processes}}", _processSelection},
|
||||
{"{{focused}}", _focused},
|
||||
{"{{focusProcess}}", _focusProcess},
|
||||
};
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
auto entryLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.process.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
entryLayout, widgetPlaceholders);
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.process.entry.focus"),
|
||||
_focusLayout, widgetPlaceholders);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addLayout(_focusLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
|
||||
_timer.start(1000);
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::ProcessChanged(const QString &text)
|
||||
@@ -100,6 +112,22 @@ void MacroConditionProcessEdit::FocusChanged(int state)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_focus = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::UpdateFocusProcess()
|
||||
{
|
||||
_focusProcess->setText(
|
||||
QString::fromStdString(switcher->currentForegroundProcess));
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
setLayoutVisible(_focusLayout, _entryData->_focus);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::UpdateEntryData()
|
||||
@@ -110,4 +138,5 @@ void MacroConditionProcessEdit::UpdateEntryData()
|
||||
|
||||
_processSelection->setCurrentText(_entryData->_process.c_str());
|
||||
_focused->setChecked(_entryData->_focus);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -44,14 +44,20 @@ public:
|
||||
private slots:
|
||||
void ProcessChanged(const QString &text);
|
||||
void FocusChanged(int state);
|
||||
void UpdateFocusProcess();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_processSelection;
|
||||
QCheckBox *_focused;
|
||||
QLabel *_focusProcess;
|
||||
QHBoxLayout *_focusLayout;
|
||||
QTimer _timer;
|
||||
std::shared_ptr<MacroConditionProcess> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -140,7 +140,7 @@ bool MacroConditionSceneOrder::Save(obs_data_t *obj)
|
||||
MacroCondition::Save(obj);
|
||||
_scene.Save(obj);
|
||||
_source.Save(obj);
|
||||
_source2.Save(obj, "sceneItem2", "sceneItemTarget2", "sceneItemIdx2");
|
||||
_source2.Save(obj, "sceneItemSelection2");
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_int(obj, "position", _position);
|
||||
return true;
|
||||
@@ -160,7 +160,14 @@ bool MacroConditionSceneOrder::Load(obs_data_t *obj)
|
||||
MacroCondition::Load(obj);
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
_source2.Load(obj, "sceneItem2", "sceneItemTarget2", "sceneItemIdx2");
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "sceneItem2")) {
|
||||
_source2.Load(obj, "sceneItem2", "sceneItemTarget2",
|
||||
"sceneItemIdx2");
|
||||
} else {
|
||||
_source2.Load(obj, "sceneItemSelection2");
|
||||
}
|
||||
_condition = static_cast<SceneOrderCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_position = obs_data_get_int(obj, "position");
|
||||
@@ -191,7 +198,7 @@ MacroConditionSceneOrderEdit::MacroConditionSceneOrderEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionSceneOrder> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_sources2 = new SceneItemSelectionWidget(parent);
|
||||
_conditions = new QComboBox();
|
||||
|
||||
@@ -33,7 +33,7 @@ bool MacroConditionSceneTransform::Save(obs_data_t *obj)
|
||||
_scene.Save(obj);
|
||||
_source.Save(obj);
|
||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||
obs_data_set_bool(obj, "regex", _regex);
|
||||
_regex.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,12 @@ bool MacroConditionSceneTransform::Load(obs_data_t *obj)
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
_settings = obs_data_get_string(obj, "settings");
|
||||
_regex = obs_data_get_bool(obj, "regex");
|
||||
_regex.Load(obj);
|
||||
// TOOD: remove in future version
|
||||
if (obs_data_has_user_value(obj, "regex")) {
|
||||
_regex.CreateBackwardsCompatibleRegex(
|
||||
obs_data_get_bool(obj, "regex"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -66,17 +71,16 @@ std::string MacroConditionSceneTransform::GetShortDesc()
|
||||
MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionSceneTransform> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_scenes(new SceneSelectionWidget(window(), true, false, false, true)),
|
||||
_sources(new SceneItemSelectionWidget(
|
||||
parent, true,
|
||||
SceneItemSelectionWidget::AllSelectionType::ANY)),
|
||||
_getSettings(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.getTransform"))),
|
||||
_settings(new ResizingPlainTextEdit(this)),
|
||||
_regex(new RegexConfigWidget(parent))
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_sources = new SceneItemSelectionWidget(
|
||||
parent, true, SceneItemSelectionWidget::AllSelectionType::ANY);
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.getTransform"));
|
||||
_settings = new ResizingPlainTextEdit(this);
|
||||
_regex = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.regex"));
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
@@ -88,8 +92,8 @@ MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
SLOT(SettingsChanged()));
|
||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(RegexChanged(int)));
|
||||
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||
SLOT(RegexChanged(RegexConfig)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes}, {"{{sources}}", _sources},
|
||||
@@ -132,7 +136,7 @@ void MacroConditionSceneTransformEdit::UpdateEntryData()
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_sources->SetSceneItem(_entryData->_source);
|
||||
_regex->setChecked(_entryData->_regex);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||
|
||||
adjustSize();
|
||||
@@ -175,7 +179,7 @@ void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
||||
}
|
||||
|
||||
auto settings = formatJsonString(getSceneItemTransform(items[0]));
|
||||
if (_entryData->_regex) {
|
||||
if (_entryData->_regex.Enabled()) {
|
||||
settings = escapeForRegex(settings);
|
||||
}
|
||||
_settings->setPlainText(settings);
|
||||
@@ -198,12 +202,15 @@ void MacroConditionSceneTransformEdit::SettingsChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::RegexChanged(int state)
|
||||
void MacroConditionSceneTransformEdit::RegexChanged(RegexConfig conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_regex = state;
|
||||
_entryData->_regex = conf;
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QCheckBox>
|
||||
@@ -22,7 +23,7 @@ public:
|
||||
|
||||
SceneSelection _scene;
|
||||
SceneItemSelection _source;
|
||||
bool _regex = false;
|
||||
RegexConfig _regex;
|
||||
std::string _settings = "";
|
||||
|
||||
private:
|
||||
@@ -53,7 +54,7 @@ private slots:
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
void RegexChanged(int);
|
||||
void RegexChanged(RegexConfig);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -62,7 +63,7 @@ protected:
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QPushButton *_getSettings;
|
||||
ResizingPlainTextEdit *_settings;
|
||||
QCheckBox *_regex;
|
||||
RegexConfigWidget *_regex;
|
||||
|
||||
std::shared_ptr<MacroConditionSceneTransform> _entryData;
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ MacroConditionSceneVisibilityEdit::MacroConditionSceneVisibilityEdit(
|
||||
std::shared_ptr<MacroConditionSceneVisibility> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, true, true);
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, true, true);
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_conditions = new QComboBox();
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionScene> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, false);
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, false, false);
|
||||
_sceneType = new QComboBox();
|
||||
_useTransitionTargetScene = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour"));
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
#include "utility.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
const std::string MacroConditionSource::id = "source";
|
||||
|
||||
bool MacroConditionSource::_registered = MacroConditionFactory::Register(
|
||||
@@ -55,7 +53,7 @@ bool MacroConditionSource::Save(obs_data_t *obj)
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||
obs_data_set_bool(obj, "regex", _regex);
|
||||
_regex.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -67,7 +65,12 @@ bool MacroConditionSource::Load(obs_data_t *obj)
|
||||
_condition = static_cast<SourceCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_settings = obs_data_get_string(obj, "settings");
|
||||
_regex = obs_data_get_bool(obj, "regex");
|
||||
_regex.Load(obj);
|
||||
// TOOD: remove in future version
|
||||
if (obs_data_has_user_value(obj, "regex")) {
|
||||
_regex.CreateBackwardsCompatibleRegex(
|
||||
obs_data_get_bool(obj, "regex"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,16 +91,14 @@ static inline void populateConditionSelection(QComboBox *list)
|
||||
|
||||
MacroConditionSourceEdit::MacroConditionSourceEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionSource> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_sources(new QComboBox()),
|
||||
_conditions(new QComboBox()),
|
||||
_getSettings(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.filter.getSettings"))),
|
||||
_settings(new ResizingPlainTextEdit(this)),
|
||||
_regex(new RegexConfigWidget(parent))
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
_conditions = new QComboBox();
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.getSettings"));
|
||||
_settings = new ResizingPlainTextEdit(this);
|
||||
_regex = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.condition.source.regex"));
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
populateSourceSelection(_sources);
|
||||
|
||||
@@ -109,8 +110,8 @@ MacroConditionSourceEdit::MacroConditionSourceEdit(
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
SLOT(SettingsChanged()));
|
||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(RegexChanged(int)));
|
||||
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||
SLOT(RegexChanged(RegexConfig)));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
@@ -171,7 +172,7 @@ void MacroConditionSourceEdit::GetSettingsClicked()
|
||||
}
|
||||
|
||||
QString json = formatJsonString(getSourceSettings(_entryData->_source));
|
||||
if (_entryData->_regex) {
|
||||
if (_entryData->_regex.Enabled()) {
|
||||
json = escapeForRegex(json);
|
||||
}
|
||||
_settings->setPlainText(json);
|
||||
@@ -190,14 +191,17 @@ void MacroConditionSourceEdit::SettingsChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::RegexChanged(int state)
|
||||
void MacroConditionSourceEdit::RegexChanged(RegexConfig conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_regex = state;
|
||||
_entryData->_regex = conf;
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible)
|
||||
@@ -218,7 +222,7 @@ void MacroConditionSourceEdit::UpdateEntryData()
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||
_regex->setChecked(_entryData->_regex);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
SetSettingsSelectionVisible(_entryData->_condition ==
|
||||
SourceCondition::SETTINGS);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
@@ -28,7 +29,7 @@ public:
|
||||
OBSWeakSource _source = nullptr;
|
||||
SourceCondition _condition = SourceCondition::ACTIVE;
|
||||
std::string _settings = "";
|
||||
bool _regex = false;
|
||||
RegexConfig _regex;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -56,7 +57,7 @@ private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
void RegexChanged(int);
|
||||
void RegexChanged(RegexConfig);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -65,7 +66,7 @@ protected:
|
||||
QComboBox *_conditions;
|
||||
QPushButton *_getSettings;
|
||||
ResizingPlainTextEdit *_settings;
|
||||
QCheckBox *_regex;
|
||||
RegexConfigWidget *_regex;
|
||||
|
||||
std::shared_ptr<MacroConditionSource> _entryData;
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ MacroConditionStudioModeEdit::MacroConditionStudioModeEdit(
|
||||
: QWidget(parent)
|
||||
{
|
||||
_condition = new QComboBox();
|
||||
_scenes = new SceneSelectionWidget(window());
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, true, true);
|
||||
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ bool MacroConditionTransition::CheckCondition()
|
||||
bool transitionStarted = false;
|
||||
bool transitionEnded = false;
|
||||
|
||||
if (_transition.GetType() == TransitionSelectionType::ANY) {
|
||||
if (_transition.GetType() == TransitionSelection::Type::ANY) {
|
||||
transitionStarted = anyTransitionStarted;
|
||||
transitionEnded = anyTransitionEnded;
|
||||
} else {
|
||||
@@ -183,7 +183,7 @@ MacroConditionTransitionEdit::MacroConditionTransitionEdit(
|
||||
{
|
||||
_conditions = new QComboBox();
|
||||
_transitions = new TransitionSelectionWidget(this, true, true);
|
||||
_scenes = new SceneSelectionWidget(this, false, true, true);
|
||||
_scenes = new SceneSelectionWidget(this, true, false, true, true);
|
||||
_duration = new DurationSelection(this, false);
|
||||
_durationSuffix = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.transition.durationSuffix"));
|
||||
|
||||
283
src/macro-core/macro-condition-variable.cpp
Normal file
283
src/macro-core/macro-condition-variable.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "macro-condition-variable.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
const std::string MacroConditionVariable::id = "variable";
|
||||
|
||||
bool MacroConditionVariable::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionVariable::id,
|
||||
{MacroConditionVariable::Create, MacroConditionVariableEdit::Create,
|
||||
"AdvSceneSwitcher.condition.variable"});
|
||||
|
||||
const static std::map<MacroConditionVariable::Type, std::string>
|
||||
conditionTypes = {
|
||||
{MacroConditionVariable::Type::EQUALS,
|
||||
"AdvSceneSwitcher.condition.variable.type.compare"},
|
||||
{MacroConditionVariable::Type::IS_EMPTY,
|
||||
"AdvSceneSwitcher.condition.variable.type.empty"},
|
||||
{MacroConditionVariable::Type::IS_NUMBER,
|
||||
"AdvSceneSwitcher.condition.variable.type.number"},
|
||||
{MacroConditionVariable::Type::LESS_THAN,
|
||||
"AdvSceneSwitcher.condition.variable.type.lessThan"},
|
||||
{MacroConditionVariable::Type::GREATER_THAN,
|
||||
"AdvSceneSwitcher.condition.variable.type.greaterThan"},
|
||||
{MacroConditionVariable::Type::VALUE_CHANGED,
|
||||
"AdvSceneSwitcher.condition.variable.type.valueChanged"},
|
||||
};
|
||||
|
||||
static bool isNumber(const Variable &var)
|
||||
{
|
||||
double _;
|
||||
return var.DoubleValue(_);
|
||||
}
|
||||
|
||||
static bool compareNumber(const Variable &var, double value, bool less)
|
||||
{
|
||||
double varValue;
|
||||
|
||||
if (!var.DoubleValue(varValue)) {
|
||||
return false;
|
||||
}
|
||||
if (less) {
|
||||
return varValue < value;
|
||||
}
|
||||
return varValue > value;
|
||||
}
|
||||
|
||||
bool MacroConditionVariable::Compare(const Variable &var) const
|
||||
{
|
||||
if (_regex) {
|
||||
try {
|
||||
std::regex expr(_strValue);
|
||||
return std::regex_match(var.Value(), expr);
|
||||
} catch (const std::regex_error &) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return _strValue == var.Value();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroConditionVariable::ValueChanged(const Variable &var)
|
||||
{
|
||||
bool changed = var.Value() != _lastValue;
|
||||
if (changed) {
|
||||
_lastValue = var.Value();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool MacroConditionVariable::CheckCondition()
|
||||
{
|
||||
auto var = GetVariableByName(_variableName);
|
||||
if (!var) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (_type) {
|
||||
case MacroConditionVariable::Type::EQUALS:
|
||||
return Compare(*var);
|
||||
case MacroConditionVariable::Type::IS_EMPTY:
|
||||
return var->Value().empty();
|
||||
case MacroConditionVariable::Type::IS_NUMBER:
|
||||
return isNumber(*var);
|
||||
case MacroConditionVariable::Type::LESS_THAN:
|
||||
return compareNumber(*var, _numValue, true);
|
||||
case MacroConditionVariable::Type::GREATER_THAN:
|
||||
return compareNumber(*var, _numValue, false);
|
||||
case MacroConditionVariable::Type::VALUE_CHANGED:
|
||||
return ValueChanged(*var);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionVariable::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "variableName", _variableName.c_str());
|
||||
obs_data_set_string(obj, "strValue", _strValue.c_str());
|
||||
obs_data_set_double(obj, "numValue", _numValue);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_type));
|
||||
obs_data_set_bool(obj, "regex", _regex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionVariable::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_variableName = obs_data_get_string(obj, "variableName");
|
||||
_strValue = obs_data_get_string(obj, "strValue");
|
||||
_numValue = obs_data_get_double(obj, "numValue");
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "condition"));
|
||||
_regex = obs_data_get_bool(obj, "regex");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionVariable::GetShortDesc()
|
||||
{
|
||||
return _variableName;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : conditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionVariableEdit::MacroConditionVariableEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionVariable> entryData)
|
||||
: QWidget(parent),
|
||||
_variables(new VariableSelection(this)),
|
||||
_conditions(new QComboBox()),
|
||||
_strValue(new QLineEdit()),
|
||||
_numValue(new QDoubleSpinBox()),
|
||||
_regex(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.condition.variable.regex")))
|
||||
{
|
||||
_numValue->setMinimum(-9999999999);
|
||||
_numValue->setMaximum(9999999999);
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(VariableChanged(const QString &)));
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_strValue, SIGNAL(editingFinished()), this,
|
||||
SLOT(StrValueChanged()));
|
||||
QWidget::connect(_numValue, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(NumValueChanged(double)));
|
||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(RegexChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{variables}}", _variables}, {"{{conditions}}", _conditions},
|
||||
{"{{strValue}}", _strValue}, {"{{numValue}}", _numValue},
|
||||
{"{{regex}}", _regex},
|
||||
};
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.variable.entry"),
|
||||
layout, widgetPlaceholders);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(layout);
|
||||
mainLayout->addWidget(_regex);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_variables->SetVariable(_entryData->_variableName);
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_strValue->setText(QString::fromStdString(_entryData->_strValue));
|
||||
_numValue->setValue(_entryData->_numValue);
|
||||
_regex->setChecked(_entryData->_regex);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::VariableChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_variableName = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::ConditionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_type = static_cast<MacroConditionVariable::Type>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::StrValueChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_strValue = _strValue->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::NumValueChanged(double val)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_numValue = val;
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::RegexChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_regex = state;
|
||||
}
|
||||
|
||||
void MacroConditionVariableEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_entryData->_type) {
|
||||
case MacroConditionVariable::Type::EQUALS:
|
||||
_regex->show();
|
||||
_strValue->show();
|
||||
_numValue->hide();
|
||||
break;
|
||||
case MacroConditionVariable::Type::IS_EMPTY:
|
||||
_regex->hide();
|
||||
_strValue->hide();
|
||||
_numValue->hide();
|
||||
break;
|
||||
case MacroConditionVariable::Type::IS_NUMBER:
|
||||
_regex->hide();
|
||||
_strValue->hide();
|
||||
_numValue->hide();
|
||||
break;
|
||||
case MacroConditionVariable::Type::LESS_THAN:
|
||||
_regex->hide();
|
||||
_strValue->hide();
|
||||
_numValue->show();
|
||||
break;
|
||||
case MacroConditionVariable::Type::GREATER_THAN:
|
||||
_regex->hide();
|
||||
_strValue->hide();
|
||||
_numValue->show();
|
||||
break;
|
||||
case MacroConditionVariable::Type::VALUE_CHANGED:
|
||||
_regex->hide();
|
||||
_strValue->hide();
|
||||
_numValue->hide();
|
||||
break;
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
88
src/macro-core/macro-condition-variable.hpp
Normal file
88
src/macro-core/macro-condition-variable.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
|
||||
class MacroConditionVariable : public MacroCondition {
|
||||
public:
|
||||
MacroConditionVariable(Macro *m) : MacroCondition(m) {}
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionVariable>(m);
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
EQUALS,
|
||||
IS_EMPTY,
|
||||
IS_NUMBER,
|
||||
LESS_THAN,
|
||||
GREATER_THAN,
|
||||
VALUE_CHANGED,
|
||||
//...
|
||||
};
|
||||
|
||||
Type _type = Type::EQUALS;
|
||||
std::string _variableName = "";
|
||||
std::string _strValue = "";
|
||||
double _numValue = 0;
|
||||
bool _regex = false;
|
||||
|
||||
private:
|
||||
bool Compare(const Variable &) const;
|
||||
bool ValueChanged(const Variable &);
|
||||
|
||||
std::string _lastValue = "";
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionVariableEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionVariableEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionVariable> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionVariableEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionVariable>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void VariableChanged(const QString &);
|
||||
void ConditionChanged(int);
|
||||
void StrValueChanged();
|
||||
void NumValueChanged(double);
|
||||
void RegexChanged(int state);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
VariableSelection *_variables;
|
||||
QComboBox *_conditions;
|
||||
QLineEdit *_strValue;
|
||||
QDoubleSpinBox *_numValue;
|
||||
QCheckBox *_regex;
|
||||
std::shared_ptr<MacroConditionVariable> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -20,6 +20,7 @@ static std::map<VCamState, std::string> VCamStates = {
|
||||
bool MacroConditionVCam::CheckCondition()
|
||||
{
|
||||
bool stateMatch = false;
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||
switch (_state) {
|
||||
case VCamState::STOP:
|
||||
stateMatch = !obs_frontend_virtualcam_active();
|
||||
@@ -30,6 +31,7 @@ bool MacroConditionVCam::CheckCondition()
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return stateMatch;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,21 +12,50 @@ bool MacroConditionWebsocket::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionWebsocket::Create, MacroConditionWebsocketEdit::Create,
|
||||
"AdvSceneSwitcher.condition.websocket"});
|
||||
|
||||
static bool matchRegex(const std::string &msg, const std::string &expr)
|
||||
static std::map<MacroConditionWebsocket::Type, std::string> conditionTypes = {
|
||||
{MacroConditionWebsocket::Type::REQUEST,
|
||||
"AdvSceneSwitcher.condition.websocket.type.request"},
|
||||
{MacroConditionWebsocket::Type::EVENT,
|
||||
"AdvSceneSwitcher.condition.websocket.type.event"},
|
||||
};
|
||||
|
||||
static bool matchRegex(const RegexConfig &conf, const std::string &msg,
|
||||
const std::string &expr)
|
||||
{
|
||||
try {
|
||||
std::regex regExpr(expr);
|
||||
return std::regex_match(msg, regExpr);
|
||||
} catch (const std::regex_error &) {
|
||||
auto regex = conf.GetRegularExpression(expr);
|
||||
if (!regex.isValid()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
auto match = regex.match(QString::fromStdString(msg));
|
||||
return match.hasMatch();
|
||||
}
|
||||
|
||||
bool MacroConditionWebsocket::CheckCondition()
|
||||
{
|
||||
for (const auto &msg : switcher->websocketMessages) {
|
||||
if (_useRegex) {
|
||||
if (matchRegex(msg, _message)) {
|
||||
const std::vector<std::string> *messages = nullptr;
|
||||
switch (_type) {
|
||||
case MacroConditionWebsocket::Type::REQUEST:
|
||||
messages = &switcher->websocketMessages;
|
||||
break;
|
||||
case MacroConditionWebsocket::Type::EVENT: {
|
||||
auto connection = GetConnectionByName(_connection);
|
||||
if (!connection) {
|
||||
return false;
|
||||
}
|
||||
messages = &connection->Events();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!messages) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &msg : *messages) {
|
||||
if (_regex.Enabled()) {
|
||||
if (matchRegex(_regex, msg, _message)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -41,42 +70,72 @@ bool MacroConditionWebsocket::CheckCondition()
|
||||
bool MacroConditionWebsocket::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
obs_data_set_string(obj, "message", _message.c_str());
|
||||
obs_data_set_bool(obj, "useRegex", _useRegex);
|
||||
_regex.Save(obj);
|
||||
obs_data_set_string(obj, "connection", _connection.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionWebsocket::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||
_message = obs_data_get_string(obj, "message");
|
||||
_useRegex = obs_data_get_bool(obj, "useRegex");
|
||||
_regex.Load(obj);
|
||||
// TOOD: remove in future version
|
||||
if (obs_data_has_user_value(obj, "useRegex")) {
|
||||
_regex.CreateBackwardsCompatibleRegex(
|
||||
obs_data_get_bool(obj, "useRegex"), false);
|
||||
}
|
||||
_connection = obs_data_get_string(obj, "connection");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionWebsocket::GetShortDesc()
|
||||
{
|
||||
return "";
|
||||
if (_type == Type::REQUEST) {
|
||||
return "";
|
||||
}
|
||||
return _connection;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : conditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionWebsocketEdit::MacroConditionWebsocketEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionWebsocket> entryData)
|
||||
: QWidget(parent),
|
||||
_conditions(new QComboBox(this)),
|
||||
_message(new ResizingPlainTextEdit(this)),
|
||||
_useRegex(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.websocket.useRegex")))
|
||||
_regex(new RegexConfigWidget(parent)),
|
||||
_connection(new ConnectionSelection(this)),
|
||||
_editLayout(new QHBoxLayout())
|
||||
{
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_message, SIGNAL(textChanged()), this,
|
||||
SLOT(MessageChanged()));
|
||||
QWidget::connect(_useRegex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseRegexChanged(int)));
|
||||
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||
SLOT(RegexChanged(RegexConfig)));
|
||||
QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)),
|
||||
this,
|
||||
SLOT(ConnectionSelectionChanged(const QString &)));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
|
||||
mainLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.condition.websocket.entry")));
|
||||
mainLayout->addLayout(_editLayout);
|
||||
mainLayout->addWidget(_message);
|
||||
mainLayout->addWidget(_useRegex);
|
||||
auto regexLayout = new QHBoxLayout;
|
||||
regexLayout->addWidget(_regex);
|
||||
regexLayout->addStretch();
|
||||
regexLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->addLayout(regexLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -84,19 +143,76 @@ MacroConditionWebsocketEdit::MacroConditionWebsocketEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::SetupRequestEdit()
|
||||
{
|
||||
_editLayout->removeWidget(_conditions);
|
||||
_editLayout->removeWidget(_connection);
|
||||
clearLayout(_editLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", _conditions},
|
||||
{"{{connection}}", _connection},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.websocket.entry.request"),
|
||||
_editLayout, widgetPlaceholders);
|
||||
_connection->hide();
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::SetupEventEdit()
|
||||
{
|
||||
_editLayout->removeWidget(_conditions);
|
||||
_editLayout->removeWidget(_connection);
|
||||
clearLayout(_editLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", _conditions},
|
||||
{"{{connection}}", _connection},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.websocket.entry.event"),
|
||||
_editLayout, widgetPlaceholders);
|
||||
_connection->show();
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_message->setPlainText(QString::fromStdString(_entryData->_message));
|
||||
_useRegex->setChecked(_entryData->_useRegex);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
_connection->SetConnection(_entryData->_connection);
|
||||
|
||||
if (_entryData->_type == MacroConditionWebsocket::Type::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
} else {
|
||||
SetupEventEdit();
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_type = static_cast<MacroConditionWebsocket::Type>(index);
|
||||
if (_entryData->_type == MacroConditionWebsocket::Type::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
} else {
|
||||
SetupEventEdit();
|
||||
}
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::MessageChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -110,12 +226,27 @@ void MacroConditionWebsocketEdit::MessageChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::UseRegexChanged(int state)
|
||||
void MacroConditionWebsocketEdit::ConnectionSelectionChanged(
|
||||
const QString &connection)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_useRegex = state;
|
||||
_entryData->_connection = connection.toStdString();
|
||||
emit(HeaderInfoChanged(connection));
|
||||
}
|
||||
|
||||
void MacroConditionWebsocketEdit::RegexChanged(RegexConfig conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_regex = conf;
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "connection-manager.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
@@ -17,8 +19,15 @@ public:
|
||||
return std::make_shared<MacroConditionWebsocket>(m);
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
REQUEST,
|
||||
EVENT,
|
||||
};
|
||||
|
||||
Type _type = Type::REQUEST;
|
||||
std::string _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
bool _useRegex = false;
|
||||
RegexConfig _regex;
|
||||
std::string _connection;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -43,16 +52,24 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int);
|
||||
void MessageChanged();
|
||||
void UseRegexChanged(int state);
|
||||
void RegexChanged(RegexConfig);
|
||||
void ConnectionSelectionChanged(const QString &);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
ResizingPlainTextEdit *_message;
|
||||
QCheckBox *_useRegex;
|
||||
std::shared_ptr<MacroConditionWebsocket> _entryData;
|
||||
|
||||
private:
|
||||
void SetupRequestEdit();
|
||||
void SetupEventEdit();
|
||||
|
||||
QComboBox *_conditions;
|
||||
ResizingPlainTextEdit *_message;
|
||||
RegexConfigWidget *_regex;
|
||||
ConnectionSelection *_connection;
|
||||
QHBoxLayout *_editLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ bool MacroConditionWindow::_registered = MacroConditionFactory::Register(
|
||||
"AdvSceneSwitcher.condition.window"});
|
||||
|
||||
bool MacroConditionWindow::CheckWindowTitleSwitchDirect(
|
||||
std::string ¤tWindowTitle)
|
||||
const std::string ¤tWindowTitle)
|
||||
{
|
||||
bool focus = (!_focus || _window == currentWindowTitle);
|
||||
bool fullscreen = (!_fullscreen || isFullscreen(_window));
|
||||
@@ -23,7 +23,8 @@ bool MacroConditionWindow::CheckWindowTitleSwitchDirect(
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::CheckWindowTitleSwitchRegex(
|
||||
std::string ¤tWindowTitle, std::vector<std::string> &windowList)
|
||||
const std::string ¤tWindowTitle,
|
||||
const std::vector<std::string> &windowList)
|
||||
{
|
||||
bool match = false;
|
||||
for (auto &window : windowList) {
|
||||
@@ -54,7 +55,7 @@ bool foregroundWindowChanged()
|
||||
|
||||
bool MacroConditionWindow::CheckCondition()
|
||||
{
|
||||
std::string currentWindowTitle = switcher->currentTitle;
|
||||
const std::string ¤tWindowTitle = switcher->currentTitle;
|
||||
std::vector<std::string> windowList;
|
||||
GetWindowList(windowList);
|
||||
|
||||
@@ -101,17 +102,18 @@ std::string MacroConditionWindow::GetShortDesc()
|
||||
|
||||
MacroConditionWindowEdit::MacroConditionWindowEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionWindow> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_windowSelection(new QComboBox()),
|
||||
_fullscreen(new QCheckBox()),
|
||||
_maximized(new QCheckBox()),
|
||||
_focused(new QCheckBox()),
|
||||
_windowFocusChanged(new QCheckBox()),
|
||||
_focusWindow(new QLabel()),
|
||||
_focusLayout(new QHBoxLayout())
|
||||
{
|
||||
_windowSelection = new QComboBox();
|
||||
_windowSelection->setEditable(true);
|
||||
_windowSelection->setMaxVisibleItems(20);
|
||||
|
||||
_fullscreen = new QCheckBox();
|
||||
_maximized = new QCheckBox();
|
||||
_focused = new QCheckBox();
|
||||
_windowFocusChanged = new QCheckBox();
|
||||
|
||||
QWidget::connect(_windowSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(WindowChanged(const QString &)));
|
||||
@@ -123,6 +125,8 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
|
||||
SLOT(FocusedChanged(int)));
|
||||
QWidget::connect(_windowFocusChanged, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(WindowFocusChanged(int)));
|
||||
QWidget::connect(&_timer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateFocusWindow()));
|
||||
|
||||
populateWindowSelection(_windowSelection);
|
||||
|
||||
@@ -132,24 +136,31 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
|
||||
{"{{maximized}}", _maximized},
|
||||
{"{{focused}}", _focused},
|
||||
{"{{windowFocusChanged}}", _windowFocusChanged},
|
||||
{"{{focusWindow}}", _focusWindow},
|
||||
};
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
auto *line1Layout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
auto *line2Layout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.line2"),
|
||||
line2Layout, widgetPlaceholders);
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.line3"),
|
||||
_focusLayout, widgetPlaceholders);
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
mainLayout->addLayout(_focusLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
|
||||
_timer.start(1000);
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::WindowChanged(const QString &text)
|
||||
@@ -192,6 +203,7 @@ void MacroConditionWindowEdit::FocusedChanged(int state)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_focus = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::WindowFocusChanged(int state)
|
||||
@@ -202,6 +214,22 @@ void MacroConditionWindowEdit::WindowFocusChanged(int state)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_windowFocusChanged = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::UpdateFocusWindow()
|
||||
{
|
||||
_focusWindow->setText(QString::fromStdString(switcher->currentTitle));
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
setLayoutVisible(_focusLayout,
|
||||
_entryData->_focus || _entryData->_windowFocusChanged);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::UpdateEntryData()
|
||||
@@ -215,4 +243,5 @@ void MacroConditionWindowEdit::UpdateEntryData()
|
||||
_maximized->setChecked(_entryData->_maximized);
|
||||
_focused->setChecked(_entryData->_focus);
|
||||
_windowFocusChanged->setChecked(_entryData->_windowFocusChanged);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool CheckWindowTitleSwitchDirect(std::string ¤tWindowTitle);
|
||||
bool CheckWindowTitleSwitchRegex(std::string ¤tWindowTitle,
|
||||
std::vector<std::string> &windowList);
|
||||
bool
|
||||
CheckWindowTitleSwitchDirect(const std::string ¤tWindowTitle);
|
||||
bool
|
||||
CheckWindowTitleSwitchRegex(const std::string ¤tWindowTitle,
|
||||
const std::vector<std::string> &windowList);
|
||||
|
||||
public:
|
||||
std::string _window;
|
||||
@@ -56,6 +58,7 @@ private slots:
|
||||
void MaximizedChanged(int state);
|
||||
void FocusedChanged(int state);
|
||||
void WindowFocusChanged(int state);
|
||||
void UpdateFocusWindow();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -65,8 +68,13 @@ protected:
|
||||
QCheckBox *_maximized;
|
||||
QCheckBox *_focused;
|
||||
QCheckBox *_windowFocusChanged;
|
||||
QLabel *_focusWindow;
|
||||
QHBoxLayout *_focusLayout;
|
||||
QTimer _timer;
|
||||
std::shared_ptr<MacroConditionWindow> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@ void MacroProperties::Save(obs_data_t *obj)
|
||||
obs_data_set_bool(data, "highlightExecuted", _highlightExecuted);
|
||||
obs_data_set_bool(data, "highlightConditions", _highlightConditions);
|
||||
obs_data_set_bool(data, "highlightActions", _highlightActions);
|
||||
obs_data_set_bool(data, "newMacroRegisterHotkey",
|
||||
_newMacroRegisterHotkeys);
|
||||
obs_data_set_obj(obj, "macroProperties", data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
@@ -17,16 +19,11 @@ void MacroProperties::Save(obs_data_t *obj)
|
||||
void MacroProperties::Load(obs_data_t *obj)
|
||||
{
|
||||
auto data = obs_data_get_obj(obj, "macroProperties");
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "highlightExecutedMacros")) {
|
||||
_highlightExecuted =
|
||||
obs_data_get_bool(obj, "highlightExecutedMacros");
|
||||
} else {
|
||||
_highlightExecuted =
|
||||
obs_data_get_bool(data, "highlightExecuted");
|
||||
}
|
||||
_highlightExecuted = obs_data_get_bool(data, "highlightExecuted");
|
||||
_highlightConditions = obs_data_get_bool(data, "highlightConditions");
|
||||
_highlightActions = obs_data_get_bool(data, "highlightActions");
|
||||
_newMacroRegisterHotkeys =
|
||||
obs_data_get_bool(data, "newMacroRegisterHotkey");
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
@@ -40,8 +37,10 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
|
||||
_actions(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
|
||||
_hotkeys(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.disableHotkeys")))
|
||||
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
|
||||
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDisableHotkeys")))
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
@@ -52,17 +51,26 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
_executed->setChecked(prop._highlightExecuted);
|
||||
_conditions->setChecked(prop._highlightConditions);
|
||||
_actions->setChecked(prop._highlightActions);
|
||||
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys);
|
||||
if (macro) {
|
||||
_hotkeys->setChecked(macro->PauseHotkeysEnabled());
|
||||
_currentMacroRegisterHotkeys->setChecked(
|
||||
macro->PauseHotkeysEnabled());
|
||||
} else {
|
||||
_hotkeys->hide();
|
||||
_currentMacroRegisterHotkeys->hide();
|
||||
}
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(_executed);
|
||||
layout->addWidget(_conditions);
|
||||
layout->addWidget(_actions);
|
||||
layout->addWidget(_hotkeys);
|
||||
layout->addWidget(_newMacroRegisterHotkeys);
|
||||
if (macro) {
|
||||
QFrame *line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
layout->addWidget(line);
|
||||
}
|
||||
layout->addWidget(_currentMacroRegisterHotkeys);
|
||||
setLayout(layout);
|
||||
|
||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||
@@ -85,8 +93,11 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
|
||||
userInput._highlightExecuted = dialog._executed->isChecked();
|
||||
userInput._highlightConditions = dialog._conditions->isChecked();
|
||||
userInput._highlightActions = dialog._actions->isChecked();
|
||||
userInput._newMacroRegisterHotkeys =
|
||||
dialog._newMacroRegisterHotkeys->isChecked();
|
||||
if (macro) {
|
||||
macro->EnablePauseHotkeys(dialog._hotkeys->isChecked());
|
||||
macro->EnablePauseHotkeys(
|
||||
dialog._currentMacroRegisterHotkeys->isChecked());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
bool _highlightExecuted = false;
|
||||
bool _highlightConditions = false;
|
||||
bool _highlightActions = false;
|
||||
bool _newMacroRegisterHotkeys = true;
|
||||
};
|
||||
|
||||
// Dialog for configuring global and macro specific settings
|
||||
@@ -31,5 +32,7 @@ private:
|
||||
QCheckBox *_executed;
|
||||
QCheckBox *_conditions;
|
||||
QCheckBox *_actions;
|
||||
QCheckBox *_hotkeys;
|
||||
QCheckBox *_newMacroRegisterHotkeys;
|
||||
// Current macro specific settings
|
||||
QCheckBox *_currentMacroRegisterHotkeys;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
#include <obs-module.h>
|
||||
|
||||
|
||||
@@ -58,8 +58,9 @@ bool AdvSceneSwitcher::addNewMacro(std::string &name, std::string format)
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->macros.emplace_back(
|
||||
std::make_shared<Macro>(name, true));
|
||||
switcher->macros.emplace_back(std::make_shared<Macro>(
|
||||
name,
|
||||
switcher->macroProperties._newMacroRegisterHotkeys));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ Macro::Macro(const std::string &name, const bool addHotkey)
|
||||
if (addHotkey) {
|
||||
SetupHotkeys();
|
||||
}
|
||||
_registerHotkeys = addHotkey;
|
||||
}
|
||||
|
||||
Macro::~Macro()
|
||||
|
||||
@@ -33,6 +33,8 @@ static std::map<VideoCondition, std::string> conditionTypes = {
|
||||
"AdvSceneSwitcher.condition.video.condition.pattern"},
|
||||
{VideoCondition::OBJECT,
|
||||
"AdvSceneSwitcher.condition.video.condition.object"},
|
||||
{VideoCondition::BRIGHTNESS,
|
||||
"AdvSceneSwitcher.condition.video.condition.brightness"},
|
||||
};
|
||||
|
||||
cv::CascadeClassifier initObjectCascade(std::string &path)
|
||||
@@ -82,6 +84,10 @@ bool MacroConditionVideo::CheckCondition()
|
||||
return _lastMatchResult;
|
||||
}
|
||||
|
||||
if (_blockUntilScreenshotDone) {
|
||||
GetScreenshot(true);
|
||||
}
|
||||
|
||||
if (_screenshotData.done) {
|
||||
match = Compare();
|
||||
_lastMatchResult = match;
|
||||
@@ -94,7 +100,7 @@ bool MacroConditionVideo::CheckCondition()
|
||||
match = _lastMatchResult;
|
||||
}
|
||||
|
||||
if (_getNextScreenshot) {
|
||||
if (!_blockUntilScreenshotDone && _getNextScreenshot) {
|
||||
GetScreenshot();
|
||||
}
|
||||
return match;
|
||||
@@ -106,10 +112,13 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
|
||||
_video.Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_string(obj, "filePath", _file.c_str());
|
||||
obs_data_set_bool(obj, "blockUntilScreenshotDone",
|
||||
_blockUntilScreenshotDone);
|
||||
obs_data_set_bool(obj, "usePatternForChangedCheck",
|
||||
_usePatternForChangedCheck);
|
||||
obs_data_set_double(obj, "threshold", _patternThreshold);
|
||||
obs_data_set_bool(obj, "useAlphaAsMask", _useAlphaAsMask);
|
||||
obs_data_set_double(obj, "brightness", _brightnessThreshold);
|
||||
obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str());
|
||||
obs_data_set_double(obj, "scaleFactor", _scaleFactor);
|
||||
obs_data_set_int(obj, "minNeighbors", _minNeighbors);
|
||||
@@ -143,10 +152,13 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
|
||||
_condition =
|
||||
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
||||
_file = obs_data_get_string(obj, "filePath");
|
||||
_blockUntilScreenshotDone =
|
||||
obs_data_get_bool(obj, "blockUntilScreenshotDone");
|
||||
_usePatternForChangedCheck =
|
||||
obs_data_get_bool(obj, "usePatternForChangedCheck");
|
||||
_patternThreshold = obs_data_get_double(obj, "threshold");
|
||||
_useAlphaAsMask = obs_data_get_bool(obj, "useAlphaAsMask");
|
||||
_brightnessThreshold = obs_data_get_double(obj, "brightness");
|
||||
_modelDataPath = obs_data_get_string(obj, "modelDataPath");
|
||||
_scaleFactor = obs_data_get_double(obj, "scaleFactor");
|
||||
if (!isScaleFactorValid(_scaleFactor)) {
|
||||
@@ -186,11 +198,12 @@ std::string MacroConditionVideo::GetShortDesc()
|
||||
return _video.ToString();
|
||||
}
|
||||
|
||||
void MacroConditionVideo::GetScreenshot()
|
||||
void MacroConditionVideo::GetScreenshot(bool blocking)
|
||||
{
|
||||
auto source = obs_weak_source_get_source(_video.GetVideo());
|
||||
_screenshotData.~ScreenshotHelper();
|
||||
new (&_screenshotData) ScreenshotHelper(source);
|
||||
new (&_screenshotData)
|
||||
ScreenshotHelper(source, blocking, GetSwitcher()->interval);
|
||||
obs_source_release(source);
|
||||
_getNextScreenshot = false;
|
||||
}
|
||||
@@ -200,6 +213,8 @@ bool MacroConditionVideo::LoadImageFromFile()
|
||||
if (!_matchImage.load(QString::fromStdString(_file))) {
|
||||
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
||||
_file.c_str());
|
||||
(&_matchImage)->~QImage();
|
||||
new (&_matchImage) QImage();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -244,6 +259,12 @@ bool MacroConditionVideo::ScreenshotContainsObject()
|
||||
return objects.size() > 0;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::CheckBrightnessThreshold()
|
||||
{
|
||||
_currentBrigthness = getAvgBrightness(_screenshotData.image) / 255.;
|
||||
return _currentBrigthness > _brightnessThreshold;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Compare()
|
||||
{
|
||||
if (_checkAreaEnable && _condition != VideoCondition::NO_IMAGE) {
|
||||
@@ -267,6 +288,8 @@ bool MacroConditionVideo::Compare()
|
||||
return ScreenshotContainsPattern();
|
||||
case VideoCondition::OBJECT:
|
||||
return ScreenshotContainsObject();
|
||||
case VideoCondition::BRIGHTNESS:
|
||||
return CheckBrightnessThreshold();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -285,6 +308,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
: QWidget(parent),
|
||||
_videoSelection(new VideoSelectionWidget(this)),
|
||||
_condition(new QComboBox()),
|
||||
_reduceLatency(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.reduceLatency"))),
|
||||
_usePatternForChangedCheck(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck"))),
|
||||
_imagePath(new FileSelection()),
|
||||
@@ -296,6 +321,13 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
"AdvSceneSwitcher.condition.video.patternThresholdDescription"))),
|
||||
_useAlphaAsMask(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"))),
|
||||
_brightnessThreshold(new ThresholdSlider(
|
||||
0., 1.,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.brightnessThreshold"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.brightnessThresholdDescription"))),
|
||||
_currentBrightness(new QLabel),
|
||||
_modelDataPath(new FileSelection()),
|
||||
_modelPathLayout(new QHBoxLayout),
|
||||
_objectScaleThreshold(new ThresholdSlider(
|
||||
@@ -312,7 +344,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
_minSize(new SizeSelection(0, 1024)),
|
||||
_maxSize(new SizeSelection(0, 4096)),
|
||||
_checkAreaControlLayout(new QHBoxLayout),
|
||||
_checkAreaEnable(new QCheckBox()),
|
||||
_checkAreaEnable(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.entry.checkAreaEnable"))),
|
||||
_checkArea(new AreaSelection(0, 99999)),
|
||||
_selectArea(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.selectArea"))),
|
||||
@@ -323,6 +356,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
"AdvSceneSwitcher.condition.video.showMatch"))),
|
||||
_previewDialog(this, entryData.get(), &GetSwitcher()->m)
|
||||
{
|
||||
_reduceLatency->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.reduceLatency.tooltip"));
|
||||
_imagePath->Button()->disconnect();
|
||||
_usePatternForChangedCheck->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip"));
|
||||
@@ -338,6 +373,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
SLOT(VideoSelectionChanged(const VideoSelection &)));
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_reduceLatency, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(ReduceLatencyChanged(int)));
|
||||
QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(ImagePathChanged(const QString &)));
|
||||
QWidget::connect(_imagePath->Button(), SIGNAL(clicked()), this,
|
||||
@@ -348,6 +385,9 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
this, SLOT(PatternThresholdChanged(double)));
|
||||
QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseAlphaAsMaskChanged(int)));
|
||||
QWidget::connect(_brightnessThreshold,
|
||||
SIGNAL(DoubleValueChanged(double)), this,
|
||||
SLOT(BrightnessThresholdChanged(double)));
|
||||
QWidget::connect(_objectScaleThreshold,
|
||||
SIGNAL(DoubleValueChanged(double)), this,
|
||||
SLOT(ObjectScaleThresholdChanged(double)));
|
||||
@@ -380,6 +420,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{videoSources}}", _videoSelection},
|
||||
{"{{condition}}", _condition},
|
||||
{"{{reduceLatency}}", _reduceLatency},
|
||||
{"{{imagePath}}", _imagePath},
|
||||
{"{{minNeighbors}}", _minNeighbors},
|
||||
{"{{minSize}}", _minSize},
|
||||
@@ -432,19 +473,26 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
mainLayout->addWidget(_usePatternForChangedCheck);
|
||||
mainLayout->addWidget(_patternThreshold);
|
||||
mainLayout->addWidget(_useAlphaAsMask);
|
||||
mainLayout->addWidget(_brightnessThreshold);
|
||||
mainLayout->addWidget(_currentBrightness);
|
||||
mainLayout->addLayout(_modelPathLayout);
|
||||
mainLayout->addWidget(_objectScaleThreshold);
|
||||
mainLayout->addLayout(_neighborsControlLayout);
|
||||
mainLayout->addWidget(_minNeighborsDescription);
|
||||
mainLayout->addLayout(_sizeLayout);
|
||||
mainLayout->addLayout(showMatchLayout);
|
||||
mainLayout->addLayout(_throttleControlLayout);
|
||||
mainLayout->addLayout(_checkAreaControlLayout);
|
||||
mainLayout->addWidget(_reduceLatency);
|
||||
mainLayout->addLayout(showMatchLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
|
||||
connect(&_updateBrightnessTimer, &QTimer::timeout, this,
|
||||
&MacroConditionVideoEdit::UpdateCurrentBrightness);
|
||||
_updateBrightnessTimer.start(1000);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UpdatePreviewTooltip()
|
||||
@@ -632,6 +680,16 @@ void MacroConditionVideoEdit::PatternThresholdChanged(double value)
|
||||
_entryData->_patternThreshold = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ReduceLatencyChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_blockUntilScreenshotDone = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -643,6 +701,16 @@ void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
|
||||
_entryData->LoadImageFromFile();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::BrightnessThresholdChanged(double value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_brightnessThreshold = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -693,6 +761,9 @@ void MacroConditionVideoEdit::CheckAreaEnableChanged(int value)
|
||||
_entryData->_checkAreaEnable = value;
|
||||
_checkArea->setEnabled(value);
|
||||
_selectArea->setEnabled(value);
|
||||
_checkArea->setVisible(value);
|
||||
_selectArea->setVisible(value);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::CheckAreaChanged(advss::Area value)
|
||||
@@ -741,6 +812,14 @@ void MacroConditionVideoEdit::ShowMatchClicked()
|
||||
_previewDialog.ShowMatch();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UpdateCurrentBrightness()
|
||||
{
|
||||
QString text = obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.currentBrightness");
|
||||
_currentBrightness->setText(
|
||||
text.arg(_entryData->GetCurrentBrightness()));
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::SelectAreaClicked()
|
||||
{
|
||||
_previewDialog.show();
|
||||
@@ -810,6 +889,10 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
|
||||
_patternThreshold->setVisible(needsThreshold(_entryData->_condition));
|
||||
_useAlphaAsMask->setVisible(_entryData->_condition ==
|
||||
VideoCondition::PATTERN);
|
||||
_brightnessThreshold->setVisible(_entryData->_condition ==
|
||||
VideoCondition::BRIGHTNESS);
|
||||
_currentBrightness->setVisible(_entryData->_condition ==
|
||||
VideoCondition::BRIGHTNESS);
|
||||
_showMatch->setVisible(needsShowMatch(_entryData->_condition));
|
||||
_objectScaleThreshold->setVisible(
|
||||
needsObjectControls(_entryData->_condition));
|
||||
@@ -825,6 +908,8 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
|
||||
needsThrottleControls(_entryData->_condition));
|
||||
setLayoutVisible(_checkAreaControlLayout,
|
||||
needsAreaControls(_entryData->_condition));
|
||||
_checkArea->setVisible(_entryData->_checkAreaEnable);
|
||||
_selectArea->setVisible(_entryData->_checkAreaEnable);
|
||||
|
||||
if (_entryData->_condition == VideoCondition::HAS_CHANGED ||
|
||||
_entryData->_condition == VideoCondition::HAS_NOT_CHANGED) {
|
||||
@@ -843,11 +928,13 @@ void MacroConditionVideoEdit::UpdateEntryData()
|
||||
|
||||
_videoSelection->SetVideoSelection(_entryData->_video);
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone);
|
||||
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||
_usePatternForChangedCheck->setChecked(
|
||||
_entryData->_usePatternForChangedCheck);
|
||||
_patternThreshold->SetDoubleValue(_entryData->_patternThreshold);
|
||||
_useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask);
|
||||
_brightnessThreshold->SetDoubleValue(_entryData->_brightnessThreshold);
|
||||
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
|
||||
_objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor);
|
||||
_minNeighbors->setValue(_entryData->_minNeighbors);
|
||||
|
||||
@@ -25,6 +25,7 @@ enum class VideoCondition {
|
||||
NO_IMAGE,
|
||||
PATTERN,
|
||||
OBJECT,
|
||||
BRIGHTNESS,
|
||||
};
|
||||
|
||||
class MacroConditionVideo : public MacroCondition {
|
||||
@@ -40,19 +41,28 @@ public:
|
||||
{
|
||||
return std::make_shared<MacroConditionVideo>(m);
|
||||
}
|
||||
void GetScreenshot();
|
||||
void GetScreenshot(bool blocking = false);
|
||||
bool LoadImageFromFile();
|
||||
bool LoadModelData(std::string &path);
|
||||
std::string GetModelDataPath() { return _modelDataPath; }
|
||||
void ResetLastMatch() { _lastMatchResult = false; }
|
||||
double GetCurrentBrightness() { return _currentBrigthness; }
|
||||
|
||||
VideoSelection _video;
|
||||
VideoCondition _condition = VideoCondition::MATCH;
|
||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
// Enabling this will reduce matching latency, but slow down the
|
||||
// the condition checks of all macros overall.
|
||||
//
|
||||
// If not set the screenshot will be gathered in one interval and
|
||||
// checked in the next one.
|
||||
// If set both operations will happen in the same interval.
|
||||
bool _blockUntilScreenshotDone = false;
|
||||
bool _useAlphaAsMask = false;
|
||||
bool _usePatternForChangedCheck = false;
|
||||
PatternMatchData _patternData;
|
||||
double _patternThreshold = 0.8;
|
||||
double _brightnessThreshold = 0.5;
|
||||
cv::CascadeClassifier _objectCascade;
|
||||
double _scaleFactor = 1.1;
|
||||
int _minNeighbors = minMinNeighbors;
|
||||
@@ -69,6 +79,7 @@ private:
|
||||
bool OutputChanged();
|
||||
bool ScreenshotContainsPattern();
|
||||
bool ScreenshotContainsObject();
|
||||
bool CheckBrightnessThreshold();
|
||||
bool Compare();
|
||||
bool CheckShouldBeSkipped();
|
||||
|
||||
@@ -81,6 +92,7 @@ private:
|
||||
"/res/cascadeClassifiers/haarcascade_frontalface_alt.xml");
|
||||
bool _lastMatchResult = false;
|
||||
int _runCount = 0;
|
||||
double _currentBrigthness = 0.;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
@@ -107,12 +119,17 @@ public:
|
||||
private slots:
|
||||
void VideoSelectionChanged(const VideoSelection &);
|
||||
void ConditionChanged(int cond);
|
||||
void ReduceLatencyChanged(int value);
|
||||
|
||||
void ImagePathChanged(const QString &text);
|
||||
void ImageBrowseButtonClicked();
|
||||
|
||||
void UsePatternForChangedCheckChanged(int value);
|
||||
void PatternThresholdChanged(double);
|
||||
void UseAlphaAsMaskChanged(int value);
|
||||
|
||||
void BrightnessThresholdChanged(double);
|
||||
|
||||
void ModelPathChanged(const QString &text);
|
||||
void ObjectScaleThresholdChanged(double);
|
||||
void MinNeighborsChanged(int value);
|
||||
@@ -127,6 +144,8 @@ private slots:
|
||||
void ThrottleEnableChanged(int value);
|
||||
void ThrottleCountChanged(int value);
|
||||
void ShowMatchClicked();
|
||||
|
||||
void UpdateCurrentBrightness();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -134,11 +153,17 @@ protected:
|
||||
VideoSelectionWidget *_videoSelection;
|
||||
QComboBox *_condition;
|
||||
|
||||
QCheckBox *_reduceLatency;
|
||||
|
||||
QCheckBox *_usePatternForChangedCheck;
|
||||
FileSelection *_imagePath;
|
||||
|
||||
ThresholdSlider *_patternThreshold;
|
||||
QCheckBox *_useAlphaAsMask;
|
||||
|
||||
ThresholdSlider *_brightnessThreshold;
|
||||
QLabel *_currentBrightness;
|
||||
|
||||
FileSelection *_modelDataPath;
|
||||
QHBoxLayout *_modelPathLayout;
|
||||
ThresholdSlider *_objectScaleThreshold;
|
||||
@@ -164,6 +189,8 @@ protected:
|
||||
std::shared_ptr<MacroConditionVideo> _entryData;
|
||||
|
||||
private:
|
||||
QTimer _updateBrightnessTimer;
|
||||
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
PatternMatchData createPatternData(QImage &pattern)
|
||||
{
|
||||
PatternMatchData data;
|
||||
PatternMatchData data{};
|
||||
if (pattern.isNull()) {
|
||||
return data;
|
||||
}
|
||||
@@ -77,6 +77,21 @@ std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
|
||||
return objects;
|
||||
}
|
||||
|
||||
uchar getAvgBrightness(QImage &img)
|
||||
{
|
||||
auto i = QImageToMat(img);
|
||||
cv::Mat hsvImage, rgbImage;
|
||||
cv::cvtColor(i, rgbImage, cv::COLOR_RGBA2RGB);
|
||||
cv::cvtColor(rgbImage, hsvImage, cv::COLOR_RGB2HSV);
|
||||
long long brightnessSum = 0;
|
||||
for (int i = 0; i < hsvImage.rows; ++i) {
|
||||
for (int j = 0; j < hsvImage.cols; ++j) {
|
||||
brightnessSum += hsvImage.at<cv::Vec3b>(i, j)[2];
|
||||
}
|
||||
}
|
||||
return brightnessSum / (hsvImage.rows * hsvImage.cols);
|
||||
}
|
||||
|
||||
// Assumption is that QImage uses Format_RGBA8888.
|
||||
// Conversion from: https://github.com/dbzhang800/QtOpenCV
|
||||
cv::Mat QImageToMat(const QImage &img)
|
||||
|
||||
@@ -20,5 +20,6 @@ void matchPattern(QImage &img, QImage &pattern, double threshold,
|
||||
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
|
||||
double scaleFactor, int minNeighbors,
|
||||
cv::Size minSize, cv::Size maxSize);
|
||||
uchar getAvgBrightness(QImage &img);
|
||||
cv::Mat QImageToMat(const QImage &img);
|
||||
QImage MatToQImage(const cv::Mat &mat);
|
||||
|
||||
@@ -288,10 +288,38 @@ void GetProcessList(QStringList &list)
|
||||
}
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(std::string &proc)
|
||||
{
|
||||
proc.resize(0);
|
||||
@autoreleasepool {
|
||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
||||
NSArray *array = [ws runningApplications];
|
||||
for (NSRunningApplication *app in array) {
|
||||
if (!app.isActive) {
|
||||
continue;
|
||||
}
|
||||
NSString *name = app.localizedName;
|
||||
if (!name) {
|
||||
break;
|
||||
}
|
||||
const char *str = name.UTF8String;
|
||||
proc = std::string(str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(QString &proc)
|
||||
{
|
||||
std::string temp;
|
||||
GetForegroundProcessName(temp);
|
||||
proc = QString::fromStdString(temp);
|
||||
}
|
||||
|
||||
bool isInFocus(const QString &executable)
|
||||
{
|
||||
std::string current;
|
||||
GetCurrentWindowTitle(current);
|
||||
GetForegroundProcessName(current);
|
||||
|
||||
// True if executable switch equals current window
|
||||
bool equals = (executable.toStdString() == current);
|
||||
|
||||
@@ -13,6 +13,7 @@ bool isMaximized(const std::string &title);
|
||||
std::pair<int, int> getCursorPos();
|
||||
int secondsSinceLastInput();
|
||||
void GetProcessList(QStringList &processes);
|
||||
void GetForegroundProcessName(std::string &name);
|
||||
bool isInFocus(const QString &executable);
|
||||
void PressKeys(const std::vector<HotkeyType> keys, int duration);
|
||||
void PlatformInit();
|
||||
|
||||
@@ -72,6 +72,7 @@ typedef struct transitionData {
|
||||
|
||||
} transitionData;
|
||||
|
||||
class Item;
|
||||
class SwitcherThread;
|
||||
|
||||
/********************************************************************************
|
||||
@@ -138,13 +139,16 @@ struct SwitcherData {
|
||||
|
||||
Curlhelper curl;
|
||||
|
||||
std::deque<Connection> connections;
|
||||
std::deque<std::shared_ptr<Item>> connections;
|
||||
std::vector<std::string> websocketMessages;
|
||||
|
||||
std::deque<std::shared_ptr<Item>> variables;
|
||||
|
||||
std::deque<WindowSwitch> windowSwitches;
|
||||
std::vector<std::string> ignoreIdleWindows;
|
||||
std::string lastTitle;
|
||||
std::string currentTitle;
|
||||
std::string currentForegroundProcess;
|
||||
|
||||
std::deque<ScreenRegionSwitch> screenRegionSwitches;
|
||||
std::pair<int, int> lastCursorPos = {0, 0};
|
||||
@@ -291,6 +295,7 @@ struct SwitcherData {
|
||||
void saveSettings(obs_data_t *obj);
|
||||
void saveMacros(obs_data_t *obj);
|
||||
void saveConnections(obs_data_t *obj);
|
||||
void saveVariables(obs_data_t *obj);
|
||||
void saveWindowTitleSwitches(obs_data_t *obj);
|
||||
void saveScreenRegionSwitches(obs_data_t *obj);
|
||||
void savePauseSwitches(obs_data_t *obj);
|
||||
@@ -315,6 +320,7 @@ struct SwitcherData {
|
||||
void loadSettings(obs_data_t *obj);
|
||||
void loadMacros(obs_data_t *obj);
|
||||
void loadConnections(obs_data_t *obj);
|
||||
void loadVariables(obs_data_t *obj);
|
||||
void loadWindowTitleSwitches(obs_data_t *obj);
|
||||
void loadScreenRegionSwitches(obs_data_t *obj);
|
||||
void loadPauseSwitches(obs_data_t *obj);
|
||||
|
||||
@@ -14,9 +14,9 @@ Q_DECLARE_METATYPE(Connection *);
|
||||
void SwitcherData::saveConnections(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *connectionArray = obs_data_array_create();
|
||||
for (Connection &c : connections) {
|
||||
for (const auto &c : connections) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
c.Save(array_obj);
|
||||
c->Save(array_obj);
|
||||
obs_data_array_push_back(connectionArray, array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
@@ -34,8 +34,9 @@ void SwitcherData::loadConnections(obs_data_t *obj)
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(connectionArray, i);
|
||||
connections.emplace_back();
|
||||
connections.back().Load(array_obj);
|
||||
auto con = Connection::Create();
|
||||
connections.emplace_back(con);
|
||||
connections.back()->Load(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(connectionArray);
|
||||
@@ -44,7 +45,7 @@ void SwitcherData::loadConnections(obs_data_t *obj)
|
||||
Connection::Connection(std::string name, std::string address, uint64_t port,
|
||||
std::string pass, bool connectOnStart, bool reconnect,
|
||||
int reconnectDelay)
|
||||
: _name(name),
|
||||
: Item(name),
|
||||
_address(address),
|
||||
_port(port),
|
||||
_password(pass),
|
||||
@@ -54,7 +55,7 @@ Connection::Connection(std::string name, std::string address, uint64_t port,
|
||||
{
|
||||
}
|
||||
|
||||
Connection::Connection(const Connection &other)
|
||||
Connection::Connection(const Connection &other) : Item(other)
|
||||
{
|
||||
_name = other._name;
|
||||
_address = other._address;
|
||||
@@ -125,8 +126,8 @@ Connection *GetConnectionByName(const QString &name)
|
||||
Connection *GetConnectionByName(const std::string &name)
|
||||
{
|
||||
for (auto &con : switcher->connections) {
|
||||
if (con.GetName() == name) {
|
||||
return &con;
|
||||
if (con->Name() == name) {
|
||||
return dynamic_cast<Connection *>(con.get());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -142,58 +143,38 @@ bool ConnectionNameAvailable(const std::string &name)
|
||||
return ConnectionNameAvailable(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
ConnectionSelection::ConnectionSelection(QWidget *parent)
|
||||
: QWidget(parent), _selection(new QComboBox), _modify(new QPushButton)
|
||||
static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
|
||||
{
|
||||
_modify->setMaximumSize(22, 22);
|
||||
QIcon icon;
|
||||
icon.addFile(
|
||||
QString::fromUtf8(":/settings/images/settings/general.svg"),
|
||||
QSize(), QIcon::Normal, QIcon::Off);
|
||||
_modify->setIcon(icon);
|
||||
_modify->setFlat(true);
|
||||
Connection &ConnectionSettings = dynamic_cast<Connection &>(settings);
|
||||
return ConnectionSettingsDialog::AskForSettings(parent,
|
||||
ConnectionSettings);
|
||||
}
|
||||
|
||||
ConnectionSelection::ConnectionSelection(QWidget *parent)
|
||||
: ItemSelection(switcher->connections, Connection::Create,
|
||||
AskForSettingsWrapper,
|
||||
"AdvSceneSwitcher.connection.select",
|
||||
"AdvSceneSwitcher.connection.add", parent)
|
||||
{
|
||||
// Connect to slots
|
||||
QWidget::connect(_selection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ChangeSelection(const QString &)));
|
||||
QWidget::connect(_modify, SIGNAL(clicked()), this,
|
||||
SLOT(ModifyButtonClicked()));
|
||||
QWidget::connect(
|
||||
window(),
|
||||
SIGNAL(ConnectionRenamed(const QString &, const QString &)),
|
||||
this, SLOT(RenameConnection(const QString &, const QString &)));
|
||||
this, SLOT(RenameItem(const QString &, const QString &)));
|
||||
QWidget::connect(window(), SIGNAL(ConnectionAdded(const QString &)),
|
||||
this, SLOT(AddConnection(const QString &)));
|
||||
this, SLOT(AddItem(const QString &)));
|
||||
QWidget::connect(window(), SIGNAL(ConnectionRemoved(const QString &)),
|
||||
this, SLOT(RemoveConnection(const QString &)));
|
||||
this, SLOT(RemoveItem(const QString &)));
|
||||
|
||||
// Forward signals
|
||||
QWidget::connect(
|
||||
this,
|
||||
SIGNAL(ConnectionRenamed(const QString &, const QString &)),
|
||||
this, SIGNAL(ItemRenamed(const QString &, const QString &)),
|
||||
window(),
|
||||
SIGNAL(ConnectionRenamed(const QString &, const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ConnectionAdded(const QString &)),
|
||||
window(), SIGNAL(ConnectionAdded(const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ConnectionRemoved(const QString &)),
|
||||
window(), SIGNAL(ConnectionRemoved(const QString &)));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->addWidget(_selection);
|
||||
layout->addWidget(_modify);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
|
||||
for (const auto &c : switcher->connections) {
|
||||
_selection->addItem(QString::fromStdString(c._name));
|
||||
}
|
||||
_selection->model()->sort(0);
|
||||
addSelectionEntry(
|
||||
_selection,
|
||||
obs_module_text("AdvSceneSwitcher.connection.select"));
|
||||
_selection->insertSeparator(_selection->count());
|
||||
_selection->addItem(obs_module_text("AdvSceneSwitcher.connection.add"));
|
||||
QWidget::connect(this, SIGNAL(ItemAdded(const QString &)), window(),
|
||||
SIGNAL(ConnectionAdded(const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ItemRemoved(const QString &)), window(),
|
||||
SIGNAL(ConnectionRemoved(const QString &)));
|
||||
}
|
||||
|
||||
void ConnectionSelection::SetConnection(const std::string &con)
|
||||
@@ -206,191 +187,30 @@ void ConnectionSelection::SetConnection(const std::string &con)
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSelection::ChangeSelection(const QString &sel)
|
||||
{
|
||||
if (sel == obs_module_text("AdvSceneSwitcher.connection.add")) {
|
||||
Connection connection;
|
||||
bool accepted = ConnectionSettingsDialog::AskForSettings(
|
||||
this, connection);
|
||||
if (!accepted) {
|
||||
_selection->setCurrentIndex(0);
|
||||
return;
|
||||
}
|
||||
switcher->connections.emplace_back(connection);
|
||||
const QSignalBlocker b(_selection);
|
||||
const QString name = QString::fromStdString(connection._name);
|
||||
AddConnection(name);
|
||||
_selection->setCurrentText(name);
|
||||
emit ConnectionAdded(name);
|
||||
emit SelectionChanged(name);
|
||||
return;
|
||||
}
|
||||
auto connection = GetCurrentConnection();
|
||||
if (connection) {
|
||||
emit SelectionChanged(
|
||||
QString::fromStdString(connection->_name));
|
||||
} else {
|
||||
emit SelectionChanged("");
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSelection::ModifyButtonClicked()
|
||||
{
|
||||
auto connection = GetCurrentConnection();
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
auto properties = [&]() {
|
||||
const auto oldName = connection->_name;
|
||||
bool accepted = ConnectionSettingsDialog::AskForSettings(
|
||||
this, *connection);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
if (oldName != connection->_name) {
|
||||
emit ConnectionRenamed(
|
||||
QString::fromStdString(oldName),
|
||||
QString::fromStdString(connection->_name));
|
||||
}
|
||||
};
|
||||
|
||||
QMenu menu(this);
|
||||
|
||||
QAction *action = new QAction(
|
||||
obs_module_text("AdvSceneSwitcher.connection.rename"), &menu);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(RenameConnection()));
|
||||
action->setProperty("connetion", QVariant::fromValue(connection));
|
||||
menu.addAction(action);
|
||||
|
||||
action = new QAction(
|
||||
obs_module_text("AdvSceneSwitcher.connection.remove"), &menu);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(RemoveConnection()));
|
||||
menu.addAction(action);
|
||||
|
||||
action = new QAction(
|
||||
obs_module_text("AdvSceneSwitcher.connection.properties"),
|
||||
&menu);
|
||||
connect(action, &QAction::triggered, properties);
|
||||
menu.addAction(action);
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void ConnectionSelection::RenameConnection()
|
||||
{
|
||||
QAction *action = reinterpret_cast<QAction *>(sender());
|
||||
QVariant variant = action->property("connetion");
|
||||
Connection *connection = variant.value<Connection *>();
|
||||
|
||||
std::string name;
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.windowTitle"),
|
||||
obs_module_text("AdvSceneSwitcher.connection.newName"), name,
|
||||
QString::fromStdString(name));
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
if (name.empty()) {
|
||||
DisplayMessage("AdvSceneSwitcher.connection.emptyName");
|
||||
return;
|
||||
}
|
||||
if (_selection->currentText().toStdString() != name &&
|
||||
!ConnectionNameAvailable(name)) {
|
||||
DisplayMessage("AdvSceneSwitcher.connection.nameNotAvailable");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto oldName = connection->_name;
|
||||
connection->_name = name;
|
||||
emit ConnectionRenamed(QString::fromStdString(oldName),
|
||||
QString::fromStdString(name));
|
||||
}
|
||||
|
||||
void ConnectionSelection::RenameConnection(const QString &oldName,
|
||||
const QString &name)
|
||||
{
|
||||
int idx = _selection->findText(oldName);
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
_selection->setItemText(idx, name);
|
||||
}
|
||||
|
||||
void ConnectionSelection::AddConnection(const QString &name)
|
||||
{
|
||||
if (_selection->findText(name) == -1) {
|
||||
_selection->insertItem(1, name);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSelection::RemoveConnection()
|
||||
{
|
||||
auto connection = GetCurrentConnection();
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
int idx =
|
||||
_selection->findText(QString::fromStdString(connection->_name));
|
||||
if (idx == -1 || idx == _selection->count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit ConnectionRemoved(QString::fromStdString(connection->_name));
|
||||
|
||||
for (auto it = switcher->connections.begin();
|
||||
it != switcher->connections.end(); ++it) {
|
||||
if (it->_name == connection->_name) {
|
||||
switcher->connections.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSelection::RemoveConnection(const QString &name)
|
||||
{
|
||||
const int idx = _selection->findText(name);
|
||||
if (idx == _selection->currentIndex()) {
|
||||
_selection->setCurrentIndex(0);
|
||||
}
|
||||
_selection->removeItem(idx);
|
||||
}
|
||||
|
||||
Connection *ConnectionSelection::GetCurrentConnection()
|
||||
{
|
||||
return GetConnectionByName(_selection->currentText());
|
||||
}
|
||||
|
||||
ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
||||
const Connection &settings)
|
||||
: QDialog(parent),
|
||||
_name(new QLineEdit()),
|
||||
_nameHint(new QLabel),
|
||||
: ItemSettingsDialog(settings, switcher->connections,
|
||||
"AdvSceneSwitcher.connection.select",
|
||||
"AdvSceneSwitcher.connection.add", parent),
|
||||
_address(new QLineEdit()),
|
||||
_port(new QSpinBox()),
|
||||
_password(new QLineEdit()),
|
||||
_showPassword(new QPushButton()),
|
||||
_connectOnStart(new QCheckBox()),
|
||||
_reconnect(new QCheckBox()),
|
||||
_reconnectDelay(new QSpinBox()),
|
||||
_test(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.connection.test"))),
|
||||
_status(new QLabel()),
|
||||
_buttonbox(new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel))
|
||||
_status(new QLabel())
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setFixedWidth(555);
|
||||
setMinimumHeight(100);
|
||||
|
||||
_port->setMaximum(65535);
|
||||
_showPassword->setMaximumWidth(22);
|
||||
_showPassword->setFlat(true);
|
||||
_showPassword->setStyleSheet(
|
||||
"QPushButton { background-color: transparent; border: 0px }");
|
||||
_reconnectDelay->setMaximum(9999);
|
||||
_reconnectDelay->setSuffix("s");
|
||||
_buttonbox->setCenterButtons(true);
|
||||
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
|
||||
|
||||
_name->setText(QString::fromStdString(settings._name));
|
||||
_address->setText(QString::fromStdString(settings._address));
|
||||
_port->setValue(settings._port);
|
||||
_password->setText(QString::fromStdString(settings._password));
|
||||
@@ -398,18 +218,15 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
||||
_reconnect->setChecked(settings._reconnect);
|
||||
_reconnectDelay->setValue(settings._reconnectDelay);
|
||||
|
||||
QWidget::connect(_name, SIGNAL(textEdited(const QString &)), this,
|
||||
SLOT(NameChanged(const QString &)));
|
||||
QWidget::connect(_reconnect, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(ReconnectChanged(int)));
|
||||
QWidget::connect(_showPassword, SIGNAL(pressed()), this,
|
||||
SLOT(ShowPassword()));
|
||||
QWidget::connect(_showPassword, SIGNAL(released()), this,
|
||||
SLOT(HidePassword()));
|
||||
QWidget::connect(_test, SIGNAL(clicked()), this,
|
||||
SLOT(TestConnection()));
|
||||
|
||||
QWidget::connect(_buttonbox, &QDialogButtonBox::accepted, this,
|
||||
&QDialog::accept);
|
||||
QWidget::connect(_buttonbox, &QDialogButtonBox::rejected, this,
|
||||
&QDialog::reject);
|
||||
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
int row = 0;
|
||||
layout->addWidget(
|
||||
@@ -433,7 +250,10 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.connection.password")),
|
||||
row, 0);
|
||||
layout->addWidget(_password, row, 1);
|
||||
QHBoxLayout *passLayout = new QHBoxLayout;
|
||||
passLayout->addWidget(_password);
|
||||
passLayout->addWidget(_showPassword);
|
||||
layout->addLayout(passLayout, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text(
|
||||
@@ -458,30 +278,8 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
||||
layout->addWidget(_buttonbox, row, 0, 1, -1);
|
||||
setLayout(layout);
|
||||
|
||||
NameChanged(_name->text());
|
||||
ReconnectChanged(_reconnect->isChecked());
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::NameChanged(const QString &text)
|
||||
{
|
||||
|
||||
if (text != _name->text() && !ConnectionNameAvailable(text)) {
|
||||
SetNameWarning(obs_module_text(
|
||||
"AdvSceneSwitcher.connection.nameNotAvailable"));
|
||||
return;
|
||||
}
|
||||
if (text.isEmpty()) {
|
||||
SetNameWarning(obs_module_text(
|
||||
"AdvSceneSwitcher.connection.emptyName"));
|
||||
return;
|
||||
}
|
||||
if (text == obs_module_text("AdvSceneSwitcher.connection.select") ||
|
||||
text == obs_module_text("AdvSceneSwitcher.connection.add")) {
|
||||
SetNameWarning(obs_module_text(
|
||||
"AdvSceneSwitcher.connection.nameReserved"));
|
||||
return;
|
||||
}
|
||||
SetNameWarning("");
|
||||
HidePassword();
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::ReconnectChanged(int state)
|
||||
@@ -513,6 +311,18 @@ void ConnectionSettingsDialog::SetStatus()
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::ShowPassword()
|
||||
{
|
||||
setButtonIcon(_showPassword, ":res/images/visible.svg");
|
||||
_password->setEchoMode(QLineEdit::Normal);
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::HidePassword()
|
||||
{
|
||||
setButtonIcon(_showPassword, ":res/images/invisible.svg");
|
||||
_password->setEchoMode(QLineEdit::PasswordEchoOnEdit);
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::TestConnection()
|
||||
{
|
||||
_testConnection.Disconnect();
|
||||
@@ -545,21 +355,9 @@ bool ConnectionSettingsDialog::AskForSettings(QWidget *parent,
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::SetNameWarning(const QString warn)
|
||||
{
|
||||
if (warn.isEmpty()) {
|
||||
_nameHint->hide();
|
||||
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(false);
|
||||
return;
|
||||
}
|
||||
_nameHint->setText(warn);
|
||||
_nameHint->show();
|
||||
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
|
||||
}
|
||||
|
||||
void Connection::Load(obs_data_t *obj)
|
||||
{
|
||||
_name = obs_data_get_string(obj, "name");
|
||||
Item::Load(obj);
|
||||
_address = obs_data_get_string(obj, "address");
|
||||
_port = obs_data_get_int(obj, "port");
|
||||
_password = obs_data_get_string(obj, "password");
|
||||
@@ -573,9 +371,9 @@ void Connection::Load(obs_data_t *obj)
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Save(obs_data_t *obj)
|
||||
void Connection::Save(obs_data_t *obj) const
|
||||
{
|
||||
obs_data_set_string(obj, "name", _name.c_str());
|
||||
Item::Save(obj);
|
||||
obs_data_set_string(obj, "address", _address.c_str());
|
||||
obs_data_set_int(obj, "port", _port);
|
||||
obs_data_set_string(obj, "password", _password.c_str());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "item-selection-helpers.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
@@ -7,7 +8,6 @@
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSpinBox>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
@@ -18,7 +18,7 @@
|
||||
class ConnectionSelection;
|
||||
class ConnectionSettingsDialog;
|
||||
|
||||
class Connection {
|
||||
class Connection : public Item {
|
||||
public:
|
||||
Connection(std::string name, std::string address, uint64_t port,
|
||||
std::string pass, bool connectOnStart, bool reconnect,
|
||||
@@ -27,15 +27,19 @@ public:
|
||||
Connection(const Connection &);
|
||||
Connection &operator=(const Connection &);
|
||||
~Connection();
|
||||
static std::shared_ptr<Item> Create()
|
||||
{
|
||||
return std::make_shared<Connection>();
|
||||
}
|
||||
|
||||
void Reconnect();
|
||||
void SendMsg(const std::string &msg);
|
||||
void Load(obs_data_t *obj);
|
||||
void Save(obs_data_t *obj);
|
||||
void Save(obs_data_t *obj) const;
|
||||
std::string GetName() { return _name; }
|
||||
std::vector<std::string> &Events() { return _client.Events(); }
|
||||
|
||||
private:
|
||||
std::string _name = "";
|
||||
std::string _address = "localhost";
|
||||
uint64_t _port = 4455;
|
||||
std::string _password = "password";
|
||||
@@ -53,7 +57,7 @@ private:
|
||||
Connection *GetConnectionByName(const QString &);
|
||||
Connection *GetConnectionByName(const std::string &);
|
||||
|
||||
class ConnectionSettingsDialog : public QDialog {
|
||||
class ConnectionSettingsDialog : public ItemSettingsDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -61,54 +65,31 @@ public:
|
||||
static bool AskForSettings(QWidget *parent, Connection &settings);
|
||||
|
||||
private slots:
|
||||
void NameChanged(const QString &);
|
||||
void ReconnectChanged(int);
|
||||
void ShowPassword();
|
||||
void HidePassword();
|
||||
void SetStatus();
|
||||
void TestConnection();
|
||||
|
||||
private:
|
||||
void SetNameWarning(const QString);
|
||||
|
||||
QLineEdit *_name;
|
||||
QLabel *_nameHint;
|
||||
QLineEdit *_address;
|
||||
QSpinBox *_port;
|
||||
QLineEdit *_password;
|
||||
QPushButton *_showPassword;
|
||||
QCheckBox *_connectOnStart;
|
||||
QCheckBox *_reconnect;
|
||||
QSpinBox *_reconnectDelay;
|
||||
QPushButton *_test;
|
||||
QLabel *_status;
|
||||
QDialogButtonBox *_buttonbox;
|
||||
|
||||
QTimer _statusTimer;
|
||||
WSConnection _testConnection;
|
||||
};
|
||||
|
||||
class ConnectionSelection : public QWidget {
|
||||
class ConnectionSelection : public ItemSelection {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionSelection(QWidget *parent = 0);
|
||||
void SetConnection(const std::string &);
|
||||
|
||||
private slots:
|
||||
void ModifyButtonClicked();
|
||||
void ChangeSelection(const QString &);
|
||||
void RenameConnection();
|
||||
void RenameConnection(const QString &oldName, const QString &name);
|
||||
void AddConnection(const QString &);
|
||||
void RemoveConnection();
|
||||
void RemoveConnection(const QString &);
|
||||
signals:
|
||||
void SelectionChanged(const QString &);
|
||||
void ConnectionAdded(const QString &);
|
||||
void ConnectionRemoved(const QString &);
|
||||
void ConnectionRenamed(const QString &oldName, const QString &name);
|
||||
|
||||
private:
|
||||
Connection *GetCurrentConnection();
|
||||
|
||||
QComboBox *_selection;
|
||||
QPushButton *_modify;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <obs-module.h>
|
||||
#include <QLayout>
|
||||
#include <QFileDialog>
|
||||
#include <QStandardPaths>
|
||||
#include <filesystem>
|
||||
|
||||
FileSelection::FileSelection(FileSelection::Type type, QWidget *parent)
|
||||
: QWidget(parent), _type(type)
|
||||
@@ -29,12 +31,22 @@ void FileSelection::SetPath(const QString &path)
|
||||
|
||||
void FileSelection::BrowseButtonClicked()
|
||||
{
|
||||
QString defaultPath;
|
||||
if (std::filesystem::exists(
|
||||
std::filesystem::path(_filePath->text().toStdString()))) {
|
||||
defaultPath = _filePath->text();
|
||||
} else {
|
||||
defaultPath = QStandardPaths::writableLocation(
|
||||
QStandardPaths::DesktopLocation);
|
||||
}
|
||||
|
||||
QString path;
|
||||
if (_type == FileSelection::Type::WRITE) {
|
||||
path = QFileDialog::getSaveFileName(this);
|
||||
path = QFileDialog::getSaveFileName(this, "", defaultPath);
|
||||
} else {
|
||||
path = QFileDialog::getOpenFileName(this);
|
||||
path = QFileDialog::getOpenFileName(this, "", defaultPath);
|
||||
}
|
||||
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
317
src/utils/item-selection-helpers.cpp
Normal file
317
src/utils/item-selection-helpers.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
#include "item-selection-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "name-dialog.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QLayout>
|
||||
#include <obs-module.h>
|
||||
|
||||
Q_DECLARE_METATYPE(Item *);
|
||||
|
||||
Item::Item(std::string name) : _name(name) {}
|
||||
|
||||
static Item *GetItemByName(const std::string &name,
|
||||
std::deque<std::shared_ptr<Item>> &items)
|
||||
{
|
||||
for (auto &item : items) {
|
||||
if (item->Name() == name) {
|
||||
return item.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Item *GetItemByName(const QString &name,
|
||||
std::deque<std::shared_ptr<Item>> &items)
|
||||
{
|
||||
return GetItemByName(name.toStdString(), items);
|
||||
}
|
||||
|
||||
bool ItemNameAvailable(const QString &name,
|
||||
std::deque<std::shared_ptr<Item>> &items)
|
||||
{
|
||||
return !GetItemByName(name, items);
|
||||
}
|
||||
|
||||
bool ItemNameAvailable(const std::string &name,
|
||||
std::deque<std::shared_ptr<Item>> &items)
|
||||
{
|
||||
return ItemNameAvailable(QString::fromStdString(name), items);
|
||||
}
|
||||
|
||||
ItemSelection::ItemSelection(std::deque<std::shared_ptr<Item>> &items,
|
||||
CreateItemFunc create, SettingsCallback callback,
|
||||
std::string_view select, std::string_view add,
|
||||
QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_selection(new QComboBox),
|
||||
_modify(new QPushButton),
|
||||
_create(create),
|
||||
_askForSettings(callback),
|
||||
_items(items),
|
||||
_selectStr(select),
|
||||
_addStr(add)
|
||||
{
|
||||
_modify->setMaximumWidth(22);
|
||||
setButtonIcon(_modify, ":/settings/images/settings/general.svg");
|
||||
_modify->setFlat(true);
|
||||
|
||||
// Connect to slots
|
||||
QWidget::connect(_selection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ChangeSelection(const QString &)));
|
||||
QWidget::connect(_modify, SIGNAL(clicked()), this,
|
||||
SLOT(ModifyButtonClicked()));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->addWidget(_selection);
|
||||
layout->addWidget(_modify);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
|
||||
for (const auto &i : items) {
|
||||
_selection->addItem(QString::fromStdString(i->_name));
|
||||
}
|
||||
_selection->model()->sort(0);
|
||||
addSelectionEntry(_selection, obs_module_text(_selectStr.data()));
|
||||
_selection->insertSeparator(_selection->count());
|
||||
_selection->addItem(obs_module_text(_addStr.data()));
|
||||
}
|
||||
|
||||
void ItemSelection::SetItem(const std::string &item)
|
||||
{
|
||||
const QSignalBlocker blocker(_selection);
|
||||
if (!!GetItemByName(item, _items)) {
|
||||
_selection->setCurrentText(QString::fromStdString(item));
|
||||
} else {
|
||||
_selection->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ItemSelection::ChangeSelection(const QString &sel)
|
||||
{
|
||||
if (sel == obs_module_text(_addStr.data())) {
|
||||
auto item = _create();
|
||||
bool accepted = _askForSettings(this, *item.get());
|
||||
if (!accepted) {
|
||||
_selection->setCurrentIndex(0);
|
||||
return;
|
||||
}
|
||||
_items.emplace_back(item);
|
||||
const QSignalBlocker b(_selection);
|
||||
const QString name = QString::fromStdString(item->_name);
|
||||
AddItem(name);
|
||||
_selection->setCurrentText(name);
|
||||
emit ItemAdded(name);
|
||||
emit SelectionChanged(name);
|
||||
return;
|
||||
}
|
||||
auto item = GetCurrentItem();
|
||||
if (item) {
|
||||
emit SelectionChanged(QString::fromStdString(item->_name));
|
||||
} else {
|
||||
emit SelectionChanged("");
|
||||
}
|
||||
}
|
||||
|
||||
void ItemSelection::ModifyButtonClicked()
|
||||
{
|
||||
auto item = GetCurrentItem();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
auto properties = [&]() {
|
||||
const auto oldName = item->_name;
|
||||
bool accepted = _askForSettings(this, *item);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
if (oldName != item->_name) {
|
||||
emit ItemRenamed(QString::fromStdString(oldName),
|
||||
QString::fromStdString(item->_name));
|
||||
}
|
||||
};
|
||||
|
||||
QMenu menu(this);
|
||||
|
||||
QAction *action = new QAction(
|
||||
obs_module_text("AdvSceneSwitcher.item.rename"), &menu);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(RenameItem()));
|
||||
action->setProperty("connetion", QVariant::fromValue(item));
|
||||
menu.addAction(action);
|
||||
|
||||
action = new QAction(obs_module_text("AdvSceneSwitcher.item.remove"),
|
||||
&menu);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(RemoveItem()));
|
||||
menu.addAction(action);
|
||||
|
||||
action = new QAction(
|
||||
obs_module_text("AdvSceneSwitcher.item.properties"), &menu);
|
||||
connect(action, &QAction::triggered, properties);
|
||||
menu.addAction(action);
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void ItemSelection::RenameItem()
|
||||
{
|
||||
QAction *action = reinterpret_cast<QAction *>(sender());
|
||||
QVariant variant = action->property("connetion");
|
||||
Item *item = variant.value<Item *>();
|
||||
|
||||
std::string name;
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.windowTitle"),
|
||||
obs_module_text("AdvSceneSwitcher.item.newName"), name,
|
||||
QString::fromStdString(name));
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
if (name.empty()) {
|
||||
DisplayMessage("AdvSceneSwitcher.item.emptyName");
|
||||
return;
|
||||
}
|
||||
if (_selection->currentText().toStdString() != name &&
|
||||
!ItemNameAvailable(name, _items)) {
|
||||
DisplayMessage("AdvSceneSwitcher.item.nameNotAvailable");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto oldName = item->_name;
|
||||
item->_name = name;
|
||||
emit ItemRenamed(QString::fromStdString(oldName),
|
||||
QString::fromStdString(name));
|
||||
}
|
||||
|
||||
void ItemSelection::RenameItem(const QString &oldName, const QString &name)
|
||||
{
|
||||
int idx = _selection->findText(oldName);
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
_selection->setItemText(idx, name);
|
||||
}
|
||||
|
||||
void ItemSelection::AddItem(const QString &name)
|
||||
{
|
||||
if (_selection->findText(name) == -1) {
|
||||
_selection->insertItem(1, name);
|
||||
}
|
||||
}
|
||||
|
||||
void ItemSelection::RemoveItem()
|
||||
{
|
||||
auto item = GetCurrentItem();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = _selection->findText(QString::fromStdString(item->_name));
|
||||
if (idx == -1 || idx == _selection->count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto name = item->_name;
|
||||
for (auto it = _items.begin(); it != _items.end(); ++it) {
|
||||
if (it->get()->_name == item->_name) {
|
||||
_items.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
emit ItemRemoved(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
void ItemSelection::RemoveItem(const QString &name)
|
||||
{
|
||||
const int idx = _selection->findText(name);
|
||||
if (idx == _selection->currentIndex()) {
|
||||
_selection->setCurrentIndex(0);
|
||||
}
|
||||
_selection->removeItem(idx);
|
||||
}
|
||||
|
||||
Item *ItemSelection::GetCurrentItem()
|
||||
{
|
||||
return GetItemByName(_selection->currentText(), _items);
|
||||
}
|
||||
|
||||
ItemSettingsDialog::ItemSettingsDialog(const Item &settings,
|
||||
std::deque<std::shared_ptr<Item>> &items,
|
||||
std::string_view select,
|
||||
std::string_view add, QWidget *parent)
|
||||
: QDialog(parent),
|
||||
_name(new QLineEdit()),
|
||||
_nameHint(new QLabel),
|
||||
_buttonbox(new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel)),
|
||||
_items(items),
|
||||
_selectStr(select),
|
||||
_addStr(add)
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setFixedWidth(555);
|
||||
setMinimumHeight(100);
|
||||
|
||||
_buttonbox->setCenterButtons(true);
|
||||
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
|
||||
|
||||
_name->setText(QString::fromStdString(settings._name));
|
||||
|
||||
QWidget::connect(_name, SIGNAL(textEdited(const QString &)), this,
|
||||
SLOT(NameChanged(const QString &)));
|
||||
QWidget::connect(_buttonbox, &QDialogButtonBox::accepted, this,
|
||||
&QDialog::accept);
|
||||
QWidget::connect(_buttonbox, &QDialogButtonBox::rejected, this,
|
||||
&QDialog::reject);
|
||||
|
||||
NameChanged(_name->text());
|
||||
}
|
||||
|
||||
void ItemSettingsDialog::NameChanged(const QString &text)
|
||||
{
|
||||
|
||||
if (text != _name->text() && !ItemNameAvailable(text, _items)) {
|
||||
SetNameWarning(obs_module_text(
|
||||
"AdvSceneSwitcher.item.nameNotAvailable"));
|
||||
return;
|
||||
}
|
||||
if (text.isEmpty()) {
|
||||
SetNameWarning(
|
||||
obs_module_text("AdvSceneSwitcher.item.emptyName"));
|
||||
return;
|
||||
}
|
||||
if (text == obs_module_text(_selectStr.data()) ||
|
||||
text == obs_module_text(_addStr.data())) {
|
||||
SetNameWarning(
|
||||
obs_module_text("AdvSceneSwitcher.item.nameReserved"));
|
||||
return;
|
||||
}
|
||||
SetNameWarning("");
|
||||
}
|
||||
|
||||
void ItemSettingsDialog::SetNameWarning(const QString warn)
|
||||
{
|
||||
if (warn.isEmpty()) {
|
||||
_nameHint->hide();
|
||||
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(false);
|
||||
return;
|
||||
}
|
||||
_nameHint->setText(warn);
|
||||
_nameHint->show();
|
||||
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
|
||||
}
|
||||
|
||||
void Item::Load(obs_data_t *obj)
|
||||
{
|
||||
_name = obs_data_get_string(obj, "name");
|
||||
}
|
||||
|
||||
void Item::Save(obs_data_t *obj) const
|
||||
{
|
||||
obs_data_set_string(obj, "name", _name.c_str());
|
||||
}
|
||||
101
src/utils/item-selection-helpers.hpp
Normal file
101
src/utils/item-selection-helpers.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSpinBox>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
#include <deque>
|
||||
#include <obs.hpp>
|
||||
#include <websocket-helpers.hpp>
|
||||
|
||||
class ItemSelection;
|
||||
class ItemSettingsDialog;
|
||||
|
||||
class Item {
|
||||
public:
|
||||
Item(std::string name);
|
||||
Item() = default;
|
||||
virtual ~Item() = default;
|
||||
|
||||
virtual void Load(obs_data_t *obj);
|
||||
virtual void Save(obs_data_t *obj) const;
|
||||
std::string Name() const { return _name; }
|
||||
|
||||
protected:
|
||||
std::string _name = "";
|
||||
|
||||
friend ItemSelection;
|
||||
friend ItemSettingsDialog;
|
||||
};
|
||||
|
||||
class ItemSettingsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ItemSettingsDialog(const Item &, std::deque<std::shared_ptr<Item>> &,
|
||||
std::string_view = "AdvSceneSwitcher.item.select",
|
||||
std::string_view = "AdvSceneSwitcher.item.select",
|
||||
QWidget *parent = 0);
|
||||
virtual ~ItemSettingsDialog() = default;
|
||||
|
||||
private slots:
|
||||
void NameChanged(const QString &);
|
||||
|
||||
protected:
|
||||
void SetNameWarning(const QString);
|
||||
|
||||
QLineEdit *_name;
|
||||
QLabel *_nameHint;
|
||||
QDialogButtonBox *_buttonbox;
|
||||
std::deque<std::shared_ptr<Item>> &_items;
|
||||
std::string_view _selectStr;
|
||||
std::string_view _addStr;
|
||||
};
|
||||
|
||||
typedef bool (*SettingsCallback)(QWidget *, Item &);
|
||||
typedef std::shared_ptr<Item> (*CreateItemFunc)();
|
||||
|
||||
class ItemSelection : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ItemSelection(std::deque<std::shared_ptr<Item>> &, CreateItemFunc,
|
||||
SettingsCallback,
|
||||
std::string_view = "AdvSceneSwitcher.item.select",
|
||||
std::string_view = "AdvSceneSwitcher.item.select",
|
||||
QWidget *parent = 0);
|
||||
virtual ~ItemSelection() = default;
|
||||
void SetItem(const std::string &);
|
||||
|
||||
private slots:
|
||||
void ModifyButtonClicked();
|
||||
void ChangeSelection(const QString &);
|
||||
void RenameItem();
|
||||
void RenameItem(const QString &oldName, const QString &name);
|
||||
void AddItem(const QString &);
|
||||
void RemoveItem();
|
||||
void RemoveItem(const QString &);
|
||||
signals:
|
||||
void SelectionChanged(const QString &);
|
||||
void ItemAdded(const QString &);
|
||||
void ItemRemoved(const QString &);
|
||||
void ItemRenamed(const QString &oldName, const QString &name);
|
||||
|
||||
protected:
|
||||
Item *GetCurrentItem();
|
||||
|
||||
QComboBox *_selection;
|
||||
QPushButton *_modify;
|
||||
CreateItemFunc _create;
|
||||
SettingsCallback _askForSettings;
|
||||
std::deque<std::shared_ptr<Item>> &_items;
|
||||
std::string_view _selectStr;
|
||||
std::string_view _addStr;
|
||||
};
|
||||
@@ -17,19 +17,19 @@ MacroList::MacroList(QWidget *parent, bool allowDuplicates, bool reorder)
|
||||
_up->setVisible(reorder);
|
||||
_down->setVisible(reorder);
|
||||
|
||||
_add->setMaximumSize(QSize(22, 22));
|
||||
_add->setMaximumWidth(22);
|
||||
_add->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("addIconSmall")));
|
||||
_add->setFlat(true);
|
||||
_remove->setMaximumSize(QSize(22, 22));
|
||||
_remove->setMaximumWidth(22);
|
||||
_remove->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||
_remove->setFlat(true);
|
||||
_up->setMaximumSize(QSize(22, 22));
|
||||
_up->setMaximumWidth(22);
|
||||
_up->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
||||
_up->setFlat(true);
|
||||
_down->setMaximumSize(QSize(22, 22));
|
||||
_down->setMaximumWidth(22);
|
||||
_down->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("downArrowIconSmall")));
|
||||
_down->setFlat(true);
|
||||
|
||||
175
src/utils/regex-config.cpp
Normal file
175
src/utils/regex-config.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "regex-config.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QLayout>
|
||||
#include <obs-module.h>
|
||||
|
||||
void RegexConfig::Save(obs_data_t *obj)
|
||||
{
|
||||
auto data = obs_data_create();
|
||||
obs_data_set_bool(data, "enable", _enable);
|
||||
obs_data_set_bool(data, "partial", _partialMatch);
|
||||
obs_data_set_int(data, "options", _options);
|
||||
obs_data_set_obj(obj, "regexConfig", data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
void RegexConfig::Load(obs_data_t *obj)
|
||||
{
|
||||
auto data = obs_data_get_obj(obj, "regexConfig");
|
||||
_enable = obs_data_get_bool(data, "enable");
|
||||
_partialMatch = obs_data_get_bool(data, "partial");
|
||||
_options = static_cast<QRegularExpression::PatternOption>(
|
||||
obs_data_get_int(data, "options"));
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
void RegexConfig::CreateBackwardsCompatibleRegex(bool enable, bool setOptions)
|
||||
{
|
||||
_enable = enable;
|
||||
if (setOptions) {
|
||||
_options |= QRegularExpression::DotMatchesEverythingOption;
|
||||
}
|
||||
}
|
||||
|
||||
QRegularExpression RegexConfig::GetRegularExpression(const QString &expr) const
|
||||
{
|
||||
if (_partialMatch) {
|
||||
return QRegularExpression(expr, _options);
|
||||
}
|
||||
return QRegularExpression(QRegularExpression::anchoredPattern(expr),
|
||||
_options);
|
||||
}
|
||||
|
||||
QRegularExpression
|
||||
RegexConfig::GetRegularExpression(const std::string &expr) const
|
||||
{
|
||||
return GetRegularExpression(QString::fromStdString(expr));
|
||||
}
|
||||
|
||||
RegexConfigWidget::RegexConfigWidget(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_openSettings(new QPushButton()),
|
||||
_enable(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.regex.enable")))
|
||||
{
|
||||
_openSettings->setMaximumWidth(22);
|
||||
setButtonIcon(_openSettings, ":/settings/images/settings/general.svg");
|
||||
_openSettings->setFlat(true);
|
||||
|
||||
QWidget::connect(_enable, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(EnableChanged(int)));
|
||||
QWidget::connect(_openSettings, SIGNAL(clicked()), this,
|
||||
SLOT(OpenSettingsClicked()));
|
||||
|
||||
auto layout = new QHBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(_enable);
|
||||
layout->addWidget(_openSettings);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void RegexConfigWidget::SetRegexConfig(const RegexConfig &config)
|
||||
{
|
||||
_config = config;
|
||||
_enable->setChecked(config._enable);
|
||||
SetVisibility();
|
||||
}
|
||||
|
||||
void RegexConfigWidget::OpenSettingsClicked()
|
||||
{
|
||||
bool accepted = RegexConfigDialog::AskForSettings(this, _config);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
emit RegexConfigChanged(_config);
|
||||
}
|
||||
|
||||
void RegexConfigWidget::SetVisibility()
|
||||
{
|
||||
_openSettings->setVisible(_config._enable);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void RegexConfigWidget::EnableChanged(int value)
|
||||
{
|
||||
_config._enable = value;
|
||||
SetVisibility();
|
||||
emit RegexConfigChanged(_config);
|
||||
}
|
||||
|
||||
RegexConfigDialog::RegexConfigDialog(QWidget *parent, const RegexConfig &config)
|
||||
: QDialog(parent),
|
||||
_partialMatch(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.regex.partialMatch"))),
|
||||
_caseInsensitive(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.regex.caseInsensitive"))),
|
||||
_dotMatch(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.regex.dotMatchNewline"))),
|
||||
_multiLine(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.regex.multiLine"))),
|
||||
_extendedPattern(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.regex.extendedPattern"))),
|
||||
_buttonbox(new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel))
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
_partialMatch->setChecked(config._partialMatch);
|
||||
_caseInsensitive->setChecked(config._options &
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
_dotMatch->setChecked(config._options &
|
||||
QRegularExpression::DotMatchesEverythingOption);
|
||||
_multiLine->setChecked(config._options &
|
||||
QRegularExpression::MultilineOption);
|
||||
_extendedPattern->setChecked(
|
||||
config._options &
|
||||
QRegularExpression::ExtendedPatternSyntaxOption);
|
||||
|
||||
QWidget::connect(_buttonbox, &QDialogButtonBox::accepted, this,
|
||||
&QDialog::accept);
|
||||
QWidget::connect(_buttonbox, &QDialogButtonBox::rejected, this,
|
||||
&QDialog::reject);
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addWidget(_partialMatch);
|
||||
layout->addWidget(_caseInsensitive);
|
||||
layout->addWidget(_dotMatch);
|
||||
layout->addWidget(_multiLine);
|
||||
layout->addWidget(_extendedPattern);
|
||||
layout->addWidget(_buttonbox, Qt::AlignHCenter);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void RegexConfigDialog::SetOption(RegexConfig &conf,
|
||||
QRegularExpression::PatternOptions what,
|
||||
QCheckBox *cb)
|
||||
{
|
||||
if (cb->isChecked()) {
|
||||
conf._options |= what;
|
||||
} else {
|
||||
conf._options &= ~what;
|
||||
}
|
||||
}
|
||||
|
||||
bool RegexConfigDialog::AskForSettings(QWidget *parent, RegexConfig &settings)
|
||||
{
|
||||
RegexConfigDialog dialog(parent, settings);
|
||||
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
settings._partialMatch = dialog._partialMatch->isChecked();
|
||||
SetOption(settings, QRegularExpression::CaseInsensitiveOption,
|
||||
dialog._caseInsensitive);
|
||||
SetOption(settings, QRegularExpression::DotMatchesEverythingOption,
|
||||
dialog._dotMatch);
|
||||
SetOption(settings, QRegularExpression::MultilineOption,
|
||||
dialog._multiLine);
|
||||
SetOption(settings, QRegularExpression::ExtendedPatternSyntaxOption,
|
||||
dialog._extendedPattern);
|
||||
return true;
|
||||
}
|
||||
74
src/utils/regex-config.hpp
Normal file
74
src/utils/regex-config.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include "obs-data.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QRegularExpression>
|
||||
|
||||
class RegexConfigWidget;
|
||||
class RegexConfigDialog;
|
||||
|
||||
class RegexConfig {
|
||||
public:
|
||||
void Save(obs_data_t *obj);
|
||||
void Load(obs_data_t *obj);
|
||||
|
||||
bool Enabled() const { return _enable; }
|
||||
void CreateBackwardsCompatibleRegex(bool, bool = true);
|
||||
QRegularExpression::PatternOptions GetPatternOptions() const
|
||||
{
|
||||
return _options;
|
||||
};
|
||||
QRegularExpression GetRegularExpression(const QString &) const;
|
||||
QRegularExpression GetRegularExpression(const std::string &) const;
|
||||
|
||||
private:
|
||||
bool _enable = false;
|
||||
bool _partialMatch = false;
|
||||
QRegularExpression::PatternOptions _options =
|
||||
QRegularExpression::NoPatternOption;
|
||||
friend RegexConfigWidget;
|
||||
friend RegexConfigDialog;
|
||||
};
|
||||
|
||||
class RegexConfigWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RegexConfigWidget(QWidget *parent = nullptr);
|
||||
void SetRegexConfig(const RegexConfig &);
|
||||
|
||||
private slots:
|
||||
void EnableChanged(int);
|
||||
void OpenSettingsClicked();
|
||||
signals:
|
||||
void RegexConfigChanged(RegexConfig);
|
||||
|
||||
private:
|
||||
void SetVisibility();
|
||||
|
||||
QPushButton *_openSettings;
|
||||
QCheckBox *_enable;
|
||||
RegexConfig _config;
|
||||
};
|
||||
|
||||
class RegexConfigDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RegexConfigDialog(QWidget *parent, const RegexConfig &);
|
||||
static bool AskForSettings(QWidget *parent, RegexConfig &settings);
|
||||
|
||||
private:
|
||||
static void SetOption(RegexConfig &, QRegularExpression::PatternOptions,
|
||||
QCheckBox *);
|
||||
|
||||
QCheckBox *_partialMatch;
|
||||
QCheckBox *_caseInsensitive;
|
||||
QCheckBox *_dotMatch;
|
||||
QCheckBox *_multiLine;
|
||||
QCheckBox *_extendedPattern;
|
||||
QDialogButtonBox *_buttonbox;
|
||||
};
|
||||
@@ -2,28 +2,105 @@
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
void SceneItemSelection::Save(obs_data_t *obj, const char *name,
|
||||
const char *targetName, const char *idxName)
|
||||
constexpr std::string_view typeSaveName = "type";
|
||||
constexpr std::string_view itemSaveName = "item";
|
||||
constexpr std::string_view idxSaveName = "idx";
|
||||
constexpr std::string_view idxTypeSaveName = "idxType";
|
||||
|
||||
void SceneItemSelection::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
obs_data_set_int(obj, targetName, static_cast<int>(_target));
|
||||
if (_target == SceneItemSelection::Target::INDIVIDUAL) {
|
||||
obs_data_set_int(obj, idxName, _idx);
|
||||
auto data = obs_data_create();
|
||||
obs_data_set_int(data, typeSaveName.data(), static_cast<int>(_type));
|
||||
obs_data_set_int(data, idxTypeSaveName.data(),
|
||||
static_cast<int>(_idxType));
|
||||
if (_idxType == IdxType::INDIVIDUAL) {
|
||||
obs_data_set_int(data, idxSaveName.data(), _idx);
|
||||
} else {
|
||||
obs_data_set_int(obj, idxName, 0);
|
||||
obs_data_set_int(data, idxSaveName.data(), 0);
|
||||
}
|
||||
obs_data_set_string(obj, name, GetWeakSourceName(_sceneItem).c_str());
|
||||
if (_type == SceneItemSelection::Type::SOURCE) {
|
||||
obs_data_set_string(data, itemSaveName.data(),
|
||||
GetWeakSourceName(_sceneItem).c_str());
|
||||
} else {
|
||||
auto var = _variable.lock();
|
||||
if (var) {
|
||||
obs_data_set_string(data, itemSaveName.data(),
|
||||
var->Name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
obs_data_set_obj(obj, name, data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
// TODO: Remove in future version
|
||||
void SceneItemSelection::Load(obs_data_t *obj, const char *name,
|
||||
const char *targetName, const char *idxName)
|
||||
const char *typeName, const char *idxName)
|
||||
{
|
||||
_target = static_cast<SceneItemSelection::Target>(
|
||||
obs_data_get_int(obj, targetName));
|
||||
_type = Type::SOURCE;
|
||||
_idxType = static_cast<IdxType>(obs_data_get_int(obj, typeName));
|
||||
_idx = obs_data_get_int(obj, idxName);
|
||||
auto sceneItemName = obs_data_get_string(obj, name);
|
||||
_sceneItem = GetWeakSourceByName(sceneItemName);
|
||||
}
|
||||
|
||||
void SceneItemSelection::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
// TODO: Remove in future version
|
||||
if (!obs_data_has_user_value(obj, name)) {
|
||||
Load(obj, "sceneItem", "sceneItemTarget", "sceneItemIdx");
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = obs_data_get_obj(obj, name);
|
||||
_type = static_cast<Type>(obs_data_get_int(data, typeSaveName.data()));
|
||||
_idxType = static_cast<IdxType>(
|
||||
obs_data_get_int(data, idxTypeSaveName.data()));
|
||||
_idx = obs_data_get_int(data, idxSaveName.data());
|
||||
const auto itemName = obs_data_get_string(data, itemSaveName.data());
|
||||
switch (_type) {
|
||||
case SceneItemSelection::Type::SOURCE:
|
||||
_sceneItem = GetWeakSourceByName(itemName);
|
||||
break;
|
||||
case SceneItemSelection::Type::VARIABLE:
|
||||
_variable = GetWeakVariableByName(itemName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
struct ItemInfo {
|
||||
std::string name;
|
||||
std::vector<obs_sceneitem_t *> items = {};
|
||||
};
|
||||
|
||||
static bool getSceneItems(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
{
|
||||
ItemInfo *moveInfo = reinterpret_cast<ItemInfo *>(ptr);
|
||||
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
if (moveInfo->name == sourceName) {
|
||||
obs_sceneitem_addref(item);
|
||||
moveInfo->items.push_back(item);
|
||||
}
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, getSceneItems, ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<obs_scene_item *> getSceneItemsWithName(obs_scene_t *scene,
|
||||
std::string &name)
|
||||
{
|
||||
ItemInfo itemInfo = {name};
|
||||
obs_scene_enum_items(scene, getSceneItems, &itemInfo);
|
||||
return itemInfo.items;
|
||||
}
|
||||
|
||||
struct ItemCountData {
|
||||
std::string name;
|
||||
int count = 0;
|
||||
@@ -44,12 +121,12 @@ static bool countSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
return true;
|
||||
}
|
||||
|
||||
int getCountOfSceneItemOccurance(SceneSelection &s, std::string &name,
|
||||
int getCountOfSceneItemOccurance(const SceneSelection &s,
|
||||
const std::string &name,
|
||||
bool enumAllScenes = true)
|
||||
{
|
||||
ItemCountData data{name};
|
||||
if (enumAllScenes && (s.GetType() == SceneSelectionType::CURRENT ||
|
||||
s.GetType() == SceneSelectionType::PREVIOUS)) {
|
||||
if (enumAllScenes && (s.GetType() != SceneSelection::Type::SCENE)) {
|
||||
auto enumScenes = [](void *param, obs_source_t *source) {
|
||||
if (!source) {
|
||||
return true;
|
||||
@@ -72,17 +149,25 @@ int getCountOfSceneItemOccurance(SceneSelection &s, std::string &name,
|
||||
std::vector<obs_scene_item *>
|
||||
SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
|
||||
{
|
||||
std::vector<obs_scene_item *> ret;
|
||||
auto s = obs_weak_source_get_source(sceneSelection.GetScene(false));
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto name = GetWeakSourceName(_sceneItem);
|
||||
std::string name;
|
||||
if (_type == Type::VARIABLE) {
|
||||
auto var = _variable.lock();
|
||||
if (!var) {
|
||||
return ret;
|
||||
}
|
||||
name = var->Value();
|
||||
} else {
|
||||
name = GetWeakSourceName(_sceneItem);
|
||||
}
|
||||
int count = getCountOfSceneItemOccurance(sceneSelection, name, false);
|
||||
auto items = getSceneItemsWithName(scene, name);
|
||||
obs_source_release(s);
|
||||
|
||||
std::vector<obs_scene_item *> ret;
|
||||
|
||||
if (_target == SceneItemSelection::Target::ALL ||
|
||||
_target == SceneItemSelection::Target::ANY) {
|
||||
if (_idxType == SceneItemSelection::IdxType::ALL ||
|
||||
_idxType == SceneItemSelection::IdxType::ANY) {
|
||||
ret = items;
|
||||
} else {
|
||||
// Index order starts at the bottom and increases to the top
|
||||
@@ -103,13 +188,96 @@ SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
|
||||
|
||||
std::string SceneItemSelection::ToString()
|
||||
{
|
||||
if (_type == Type::VARIABLE) {
|
||||
auto var = _variable.lock();
|
||||
if (!var) {
|
||||
return "";
|
||||
}
|
||||
return var->Name();
|
||||
}
|
||||
return GetWeakSourceName(_sceneItem);
|
||||
}
|
||||
|
||||
static bool enumSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
{
|
||||
QStringList *names = reinterpret_cast<QStringList *>(ptr);
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, enumSceneItem, ptr);
|
||||
}
|
||||
auto name = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
names->append(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
void populateSceneItemSelection(QComboBox *list)
|
||||
{
|
||||
QStringList names;
|
||||
obs_scene_enum_items(nullptr, enumSceneItem, &names);
|
||||
names.removeDuplicates();
|
||||
names.sort();
|
||||
list->addItems(names);
|
||||
addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem"));
|
||||
list->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
QStringList GetSceneItemsList(SceneSelection &s)
|
||||
{
|
||||
QStringList names;
|
||||
if (s.GetType() != SceneSelection::Type::SCENE) {
|
||||
auto enumScenes = [](void *param, obs_source_t *source) {
|
||||
if (!source) {
|
||||
return true;
|
||||
}
|
||||
QStringList *names =
|
||||
reinterpret_cast<QStringList *>(param);
|
||||
auto scene = obs_scene_from_source(source);
|
||||
obs_scene_enum_items(scene, enumSceneItem, names);
|
||||
return true;
|
||||
};
|
||||
|
||||
obs_enum_scenes(enumScenes, &names);
|
||||
} else {
|
||||
auto source = obs_weak_source_get_source(s.GetScene(false));
|
||||
auto scene = obs_scene_from_source(source);
|
||||
obs_scene_enum_items(scene, enumSceneItem, &names);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
names.removeDuplicates();
|
||||
names.sort();
|
||||
return names;
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::Reset()
|
||||
{
|
||||
auto previousSel = _currentSelection;
|
||||
PopulateItemSelection();
|
||||
SetSceneItem(previousSel);
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::PopulateItemSelection()
|
||||
{
|
||||
_sceneItems->clear();
|
||||
addSelectionEntry(_sceneItems,
|
||||
obs_module_text("AdvSceneSwitcher.selectItem"));
|
||||
_sceneItems->insertSeparator(_sceneItems->count());
|
||||
|
||||
const QStringList variables = GetVariablesNameList();
|
||||
addSelectionGroup(_sceneItems, variables);
|
||||
_variablesEndIdx = _sceneItems->count();
|
||||
|
||||
const QStringList sceneItmes = GetSceneItemsList(_scene);
|
||||
addSelectionGroup(_sceneItems, sceneItmes, false);
|
||||
_itemsEndIdx = _sceneItems->count();
|
||||
_sceneItems->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
||||
bool showAll,
|
||||
AllSelectionType type)
|
||||
: QWidget(parent), _showAll(showAll), _allType(type)
|
||||
: QWidget(parent), _hasAllEntry(showAll), _allType(type)
|
||||
{
|
||||
_sceneItems = new QComboBox();
|
||||
_idx = new QComboBox();
|
||||
@@ -124,6 +292,16 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
||||
SLOT(SelectionChanged(const QString &)));
|
||||
QWidget::connect(_idx, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(IdxChanged(int)));
|
||||
// Variables
|
||||
QWidget::connect(window(), SIGNAL(VariableAdded(const QString &)), this,
|
||||
SLOT(ItemAdd(const QString &)));
|
||||
QWidget::connect(window(), SIGNAL(VariableRemoved(const QString &)),
|
||||
this, SLOT(ItemRemove(const QString &)));
|
||||
QWidget::connect(
|
||||
window(),
|
||||
SIGNAL(VariableRenamed(const QString &, const QString &)), this,
|
||||
SLOT(ItemRename(const QString &, const QString &)));
|
||||
|
||||
auto layout = new QHBoxLayout;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(_idx);
|
||||
@@ -134,21 +312,48 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
||||
|
||||
void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item)
|
||||
{
|
||||
_sceneItems->setCurrentText(
|
||||
QString::fromStdString(GetWeakSourceName(item._sceneItem)));
|
||||
if (item._target == SceneItemSelection::Target::ALL) {
|
||||
_allType = AllSelectionType::ALL;
|
||||
_idx->setCurrentIndex(0);
|
||||
} else if (item._target == SceneItemSelection::Target::ANY) {
|
||||
_allType = AllSelectionType::ANY;
|
||||
_idx->setCurrentIndex(0);
|
||||
} else {
|
||||
int itemIdx = 0;
|
||||
switch (item._type) {
|
||||
case SceneItemSelection::Type::SOURCE: {
|
||||
int idx = item._idx;
|
||||
if (_showAll) {
|
||||
if (_hasAllEntry) {
|
||||
idx += 1;
|
||||
}
|
||||
_idx->setCurrentIndex(idx);
|
||||
itemIdx = findIdxInRagne(_sceneItems, _variablesEndIdx,
|
||||
_itemsEndIdx,
|
||||
GetWeakSourceName(item._sceneItem));
|
||||
_sceneItems->setCurrentIndex(itemIdx);
|
||||
break;
|
||||
}
|
||||
case SceneItemSelection::Type::VARIABLE: {
|
||||
|
||||
auto var = item._variable.lock();
|
||||
if (!var) {
|
||||
break;
|
||||
}
|
||||
itemIdx = findIdxInRagne(_sceneItems, _selectIdx,
|
||||
_variablesEndIdx, var->Name());
|
||||
_sceneItems->setCurrentIndex(itemIdx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (item._idxType) {
|
||||
case SceneItemSelection::IdxType::ALL:
|
||||
case SceneItemSelection::IdxType::ANY:
|
||||
_allType = AllSelectionType::ALL;
|
||||
_idx->setCurrentIndex(0);
|
||||
break;
|
||||
case SceneItemSelection::IdxType::INDIVIDUAL:
|
||||
int idx = item._idx;
|
||||
if (_hasAllEntry) {
|
||||
idx += 1;
|
||||
}
|
||||
_idx->setCurrentIndex(idx);
|
||||
break;
|
||||
}
|
||||
_currentSelection = item;
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetScene(const SceneSelection &s)
|
||||
@@ -156,12 +361,12 @@ void SceneItemSelectionWidget::SetScene(const SceneSelection &s)
|
||||
_scene = s;
|
||||
_sceneItems->clear();
|
||||
_idx->hide();
|
||||
populateSceneItemSelection(_sceneItems, _scene);
|
||||
PopulateItemSelection();
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetShowAll(bool value)
|
||||
{
|
||||
_showAll = value;
|
||||
_hasAllEntry = value;
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetShowAllSelectionType(AllSelectionType t)
|
||||
@@ -179,21 +384,36 @@ void SceneItemSelectionWidget::SceneChanged(const SceneSelection &s)
|
||||
void SceneItemSelectionWidget::SelectionChanged(const QString &name)
|
||||
{
|
||||
SceneItemSelection s;
|
||||
_sceneItem = GetWeakSourceByQString(name);
|
||||
s._sceneItem = _sceneItem;
|
||||
if (_allType == AllSelectionType::ALL) {
|
||||
s._target = SceneItemSelection::Target::ALL;
|
||||
} else {
|
||||
s._target = SceneItemSelection::Target::ANY;
|
||||
}
|
||||
auto stdName = name.toStdString();
|
||||
int sceneItemCount = getCountOfSceneItemOccurance(_scene, stdName);
|
||||
int sceneItemCount =
|
||||
getCountOfSceneItemOccurance(_scene, name.toStdString());
|
||||
if (sceneItemCount > 1) {
|
||||
_idx->show();
|
||||
SetupIdxSelection(sceneItemCount);
|
||||
} else {
|
||||
_idx->hide();
|
||||
}
|
||||
|
||||
if (_hasAllEntry) {
|
||||
switch (_allType) {
|
||||
case SceneItemSelectionWidget::AllSelectionType::ALL:
|
||||
s._idxType = SceneItemSelection::IdxType::ALL;
|
||||
break;
|
||||
case SceneItemSelectionWidget::AllSelectionType::ANY:
|
||||
s._idxType = SceneItemSelection::IdxType::ANY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int idx = _sceneItems->currentIndex();
|
||||
if (idx < _variablesEndIdx) {
|
||||
s._type = SceneItemSelection::Type::VARIABLE;
|
||||
s._variable = GetWeakVariableByQString(name);
|
||||
} else if (idx < _itemsEndIdx) {
|
||||
s._type = SceneItemSelection::Type::SOURCE;
|
||||
s._sceneItem = GetWeakSourceByQString(name);
|
||||
}
|
||||
|
||||
_currentSelection = s;
|
||||
emit SceneItemChanged(s);
|
||||
}
|
||||
|
||||
@@ -203,29 +423,50 @@ void SceneItemSelectionWidget::IdxChanged(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
SceneItemSelection s;
|
||||
s._sceneItem = _sceneItem;
|
||||
if (_showAll && idx == 0) {
|
||||
if (_allType == AllSelectionType::ALL) {
|
||||
s._target = SceneItemSelection::Target::ALL;
|
||||
} else {
|
||||
s._target = SceneItemSelection::Target::ANY;
|
||||
_currentSelection._idx = idx;
|
||||
if (_hasAllEntry && idx == 0) {
|
||||
switch (_allType) {
|
||||
case SceneItemSelectionWidget::AllSelectionType::ALL:
|
||||
_currentSelection._idxType =
|
||||
SceneItemSelection::IdxType::ALL;
|
||||
break;
|
||||
case SceneItemSelectionWidget::AllSelectionType::ANY:
|
||||
_currentSelection._idxType =
|
||||
SceneItemSelection::IdxType::ANY;
|
||||
break;
|
||||
}
|
||||
s._idx = 0;
|
||||
} else {
|
||||
s._target = SceneItemSelection::Target::INDIVIDUAL;
|
||||
if (_showAll) {
|
||||
idx -= 1;
|
||||
}
|
||||
s._idx = idx;
|
||||
}
|
||||
emit SceneItemChanged(s);
|
||||
if (_hasAllEntry && idx > 0) {
|
||||
_currentSelection._idx -= 1;
|
||||
_currentSelection._idxType =
|
||||
SceneItemSelection::IdxType::INDIVIDUAL;
|
||||
}
|
||||
emit SceneItemChanged(_currentSelection);
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::ItemAdd(const QString &)
|
||||
{
|
||||
blockSignals(true);
|
||||
Reset();
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::ItemRemove(const QString &)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::ItemRename(const QString &, const QString &)
|
||||
{
|
||||
blockSignals(true);
|
||||
Reset();
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetupIdxSelection(int sceneItemCount)
|
||||
{
|
||||
_idx->clear();
|
||||
if (_showAll) {
|
||||
if (_hasAllEntry) {
|
||||
if (_allType == AllSelectionType::ALL) {
|
||||
_idx->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneItemSelection.all"));
|
||||
|
||||
@@ -1,28 +1,41 @@
|
||||
#pragma once
|
||||
#include "scene-selection.hpp"
|
||||
#include "variable.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <obs-data.h>
|
||||
|
||||
#include "scene-selection.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
class SceneItemSelection {
|
||||
public:
|
||||
void Save(obs_data_t *obj, const char *name = "sceneItem",
|
||||
const char *targetName = "sceneItemTarget",
|
||||
const char *idxName = "sceneItemIdx");
|
||||
void Load(obs_data_t *obj, const char *name = "sceneItem",
|
||||
const char *targetName = "sceneItemTarget",
|
||||
const char *idxName = "sceneItemIdx");
|
||||
void Save(obs_data_t *obj,
|
||||
const char *name = "sceneItemSelection") const;
|
||||
void Load(obs_data_t *obj, const char *name = "sceneItemSelection");
|
||||
// TODO: Remove in future version
|
||||
void Load(obs_data_t *obj, const char *name, const char *targetName,
|
||||
const char *idxName);
|
||||
|
||||
enum class Target { ALL, ANY, INDIVIDUAL };
|
||||
Target GetType() { return _target; }
|
||||
enum class Type {
|
||||
SOURCE,
|
||||
VARIABLE,
|
||||
};
|
||||
enum class IdxType {
|
||||
ALL,
|
||||
ANY,
|
||||
INDIVIDUAL,
|
||||
};
|
||||
|
||||
Type GetType() { return _type; }
|
||||
IdxType GetIndexType() { return _idxType; }
|
||||
std::vector<obs_scene_item *> GetSceneItems(SceneSelection &s);
|
||||
std::string ToString();
|
||||
|
||||
private:
|
||||
OBSWeakSource _sceneItem;
|
||||
Target _target = Target::ALL;
|
||||
int _idx = 0;
|
||||
std::weak_ptr<Variable> _variable;
|
||||
Type _type = Type::SOURCE;
|
||||
IdxType _idxType = IdxType::ALL;
|
||||
int _idx = 0; // Multiple items with the same name can exist
|
||||
friend class SceneItemSelectionWidget;
|
||||
};
|
||||
|
||||
@@ -30,9 +43,10 @@ class SceneItemSelectionWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Influences what is being displaye for the "all" entry
|
||||
enum class AllSelectionType { ALL, ANY };
|
||||
SceneItemSelectionWidget(
|
||||
QWidget *parent, bool allSelection = true,
|
||||
QWidget *parent, bool addAllSelection = true,
|
||||
AllSelectionType allType = AllSelectionType::ALL);
|
||||
void SetSceneItem(const SceneItemSelection &);
|
||||
void SetScene(const SceneSelection &);
|
||||
@@ -45,15 +59,28 @@ private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SelectionChanged(const QString &name);
|
||||
void IdxChanged(int);
|
||||
void ItemAdd(const QString &name);
|
||||
void ItemRemove(const QString &name);
|
||||
void ItemRename(const QString &oldName, const QString &newName);
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
void PopulateItemSelection();
|
||||
void SetupIdxSelection(int);
|
||||
|
||||
QComboBox *_sceneItems;
|
||||
QComboBox *_idx;
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _sceneItem;
|
||||
bool _showAll = false;
|
||||
SceneItemSelection _currentSelection;
|
||||
bool _hasAllEntry = false;
|
||||
AllSelectionType _allType = AllSelectionType::ALL;
|
||||
|
||||
// Order of entries
|
||||
// 1. "select entry" entry
|
||||
// 2. Variables
|
||||
// 3. Scene items
|
||||
const int _selectIdx = 0;
|
||||
int _variablesEndIdx = -1;
|
||||
int _itemsEndIdx = -1;
|
||||
};
|
||||
|
||||
@@ -1,52 +1,95 @@
|
||||
#include "scene-selection.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
void SceneSelection::Save(obs_data_t *obj, const char *name,
|
||||
const char *typeName)
|
||||
constexpr std::string_view typeSaveName = "type";
|
||||
constexpr std::string_view nameSaveName = "name";
|
||||
constexpr std::string_view selectionSaveName = "sceneSelection";
|
||||
|
||||
void SceneSelection::Save(obs_data_t *obj) const
|
||||
{
|
||||
obs_data_set_int(obj, typeName, static_cast<int>(_type));
|
||||
|
||||
auto data = obs_data_create();
|
||||
obs_data_set_int(data, typeSaveName.data(), static_cast<int>(_type));
|
||||
switch (_type) {
|
||||
case SceneSelectionType::SCENE:
|
||||
obs_data_set_string(obj, name,
|
||||
case Type::SCENE:
|
||||
obs_data_set_string(data, nameSaveName.data(),
|
||||
GetWeakSourceName(_scene).c_str());
|
||||
break;
|
||||
case SceneSelectionType::GROUP:
|
||||
obs_data_set_string(obj, name, _group->name.c_str());
|
||||
case Type::GROUP:
|
||||
obs_data_set_string(data, nameSaveName.data(),
|
||||
_group->name.c_str());
|
||||
break;
|
||||
case Type::VARIABLE: {
|
||||
auto var = _variable.lock();
|
||||
if (!var) {
|
||||
break;
|
||||
}
|
||||
obs_data_set_string(data, nameSaveName.data(),
|
||||
var->Name().c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_data_set_obj(obj, selectionSaveName.data(), data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
void SceneSelection::Load(obs_data_t *obj, const char *name,
|
||||
const char *typeName)
|
||||
{
|
||||
_type = static_cast<SceneSelectionType>(
|
||||
obs_data_get_int(obj, typeName));
|
||||
auto target = obs_data_get_string(obj, name);
|
||||
// TODO: Remove in future version
|
||||
if (!obs_data_has_user_value(obj, selectionSaveName.data())) {
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, typeName));
|
||||
auto targetName = obs_data_get_string(obj, name);
|
||||
switch (_type) {
|
||||
case Type::SCENE:
|
||||
_scene = GetWeakSourceByName(targetName);
|
||||
break;
|
||||
case Type::GROUP:
|
||||
_group = GetSceneGroupByName(targetName);
|
||||
break;
|
||||
case Type::PREVIOUS:
|
||||
break;
|
||||
case Type::CURRENT:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = obs_data_get_obj(obj, selectionSaveName.data());
|
||||
_type = static_cast<Type>(obs_data_get_int(data, typeSaveName.data()));
|
||||
auto targetName = obs_data_get_string(data, nameSaveName.data());
|
||||
switch (_type) {
|
||||
case SceneSelectionType::SCENE:
|
||||
_scene = GetWeakSourceByName(target);
|
||||
case Type::SCENE:
|
||||
_scene = GetWeakSourceByName(targetName);
|
||||
break;
|
||||
case SceneSelectionType::GROUP:
|
||||
_group = GetSceneGroupByName(target);
|
||||
case Type::GROUP:
|
||||
_group = GetSceneGroupByName(targetName);
|
||||
break;
|
||||
case SceneSelectionType::PREVIOUS:
|
||||
case Type::PREVIOUS:
|
||||
break;
|
||||
case SceneSelectionType::CURRENT:
|
||||
case Type::CURRENT:
|
||||
break;
|
||||
case Type::PREVIEW:
|
||||
break;
|
||||
case Type::VARIABLE:
|
||||
_variable = GetWeakVariableByName(targetName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
OBSWeakSource SceneSelection::GetScene(bool advance)
|
||||
OBSWeakSource SceneSelection::GetScene(bool advance) const
|
||||
{
|
||||
switch (_type) {
|
||||
case SceneSelectionType::SCENE:
|
||||
case Type::SCENE:
|
||||
return _scene;
|
||||
case SceneSelectionType::GROUP:
|
||||
case Type::GROUP:
|
||||
if (!_group) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -54,199 +97,339 @@ OBSWeakSource SceneSelection::GetScene(bool advance)
|
||||
return _group->getNextScene();
|
||||
}
|
||||
return _group->getCurrentScene();
|
||||
case SceneSelectionType::PREVIOUS:
|
||||
case Type::PREVIOUS:
|
||||
return switcher->previousScene;
|
||||
case SceneSelectionType::CURRENT:
|
||||
case Type::CURRENT:
|
||||
return switcher->currentScene;
|
||||
case Type::PREVIEW: {
|
||||
auto s = obs_frontend_get_current_preview_scene();
|
||||
auto scene = obs_source_get_weak_source(s);
|
||||
obs_weak_source_release(scene);
|
||||
obs_source_release(s);
|
||||
return scene;
|
||||
}
|
||||
case Type::VARIABLE: {
|
||||
auto var = _variable.lock();
|
||||
if (!var) {
|
||||
return nullptr;
|
||||
}
|
||||
return GetWeakSourceByName(var->Value().c_str());
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string SceneSelection::ToString()
|
||||
std::string SceneSelection::ToString() const
|
||||
{
|
||||
switch (_type) {
|
||||
case SceneSelectionType::SCENE:
|
||||
case Type::SCENE:
|
||||
return GetWeakSourceName(_scene);
|
||||
case SceneSelectionType::GROUP:
|
||||
case Type::GROUP:
|
||||
if (_group) {
|
||||
return _group->name;
|
||||
}
|
||||
break;
|
||||
case SceneSelectionType::PREVIOUS:
|
||||
case Type::PREVIOUS:
|
||||
return obs_module_text("AdvSceneSwitcher.selectPreviousScene");
|
||||
case SceneSelectionType::CURRENT:
|
||||
case Type::CURRENT:
|
||||
return obs_module_text("AdvSceneSwitcher.selectCurrentScene");
|
||||
case Type::PREVIEW:
|
||||
return obs_module_text("AdvSceneSwitcher.selectPreviewScene");
|
||||
case Type::VARIABLE: {
|
||||
auto var = _variable.lock();
|
||||
if (!var) {
|
||||
return "";
|
||||
}
|
||||
return var->Name();
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
SceneSelectionWidget::SceneSelectionWidget(QWidget *parent, bool sceneGroups,
|
||||
bool previous, bool current)
|
||||
: QComboBox(parent)
|
||||
SceneSelection SceneSelectionWidget::CurrentSelection()
|
||||
{
|
||||
SceneSelection s;
|
||||
const int idx = currentIndex();
|
||||
const auto name = currentText();
|
||||
|
||||
if (idx < _placeholderEndIdx) {
|
||||
if (IsCurrentSceneSelected(name)) {
|
||||
s._type = SceneSelection::Type::CURRENT;
|
||||
}
|
||||
if (IsPreviousSceneSelected(name)) {
|
||||
s._type = SceneSelection::Type::PREVIOUS;
|
||||
}
|
||||
if (IsPreviewSceneSelected(name)) {
|
||||
s._type = SceneSelection::Type::PREVIEW;
|
||||
}
|
||||
} else if (idx < _variablesEndIdx) {
|
||||
s._type = SceneSelection::Type::VARIABLE;
|
||||
s._variable = GetWeakVariableByQString(name);
|
||||
} else if (idx < _groupsEndIdx) {
|
||||
s._type = SceneSelection::Type::GROUP;
|
||||
s._group = GetSceneGroupByQString(name);
|
||||
} else if (idx < _scenesEndIdx) {
|
||||
s._type = SceneSelection::Type::SCENE;
|
||||
s._scene = GetWeakSourceByQString(name);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static QStringList getOrderList(bool current, bool previous, bool preview)
|
||||
{
|
||||
QStringList list;
|
||||
if (current) {
|
||||
list << obs_module_text("AdvSceneSwitcher.selectCurrentScene");
|
||||
}
|
||||
if (previous) {
|
||||
list << obs_module_text("AdvSceneSwitcher.selectPreviousScene");
|
||||
}
|
||||
if (preview) {
|
||||
list << obs_module_text("AdvSceneSwitcher.selectPreviewScene");
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static QStringList getScenesList()
|
||||
{
|
||||
QStringList list;
|
||||
char **scenes = obs_frontend_get_scene_names();
|
||||
char **temp = scenes;
|
||||
while (*temp) {
|
||||
const char *name = *temp;
|
||||
list << name;
|
||||
temp++;
|
||||
}
|
||||
bfree(scenes);
|
||||
return list;
|
||||
}
|
||||
|
||||
static QStringList getSceneGroupsList()
|
||||
{
|
||||
QStringList list;
|
||||
for (const auto &sg : switcher->sceneGroups) {
|
||||
list << QString::fromStdString(sg.name);
|
||||
}
|
||||
list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::Reset()
|
||||
{
|
||||
auto previousSel = _currentSelection;
|
||||
PopulateSelection();
|
||||
SetScene(previousSel);
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::PopulateSelection()
|
||||
{
|
||||
clear();
|
||||
addSelectionEntry(this,
|
||||
obs_module_text("AdvSceneSwitcher.selectScene"));
|
||||
insertSeparator(count());
|
||||
|
||||
if (_current || _previous) {
|
||||
const QStringList order =
|
||||
getOrderList(_current, _previous, _preview);
|
||||
addSelectionGroup(this, order);
|
||||
}
|
||||
_placeholderEndIdx = count();
|
||||
|
||||
if (_variables) {
|
||||
const QStringList variables = GetVariablesNameList();
|
||||
addSelectionGroup(this, variables);
|
||||
}
|
||||
_variablesEndIdx = count();
|
||||
|
||||
if (_sceneGroups) {
|
||||
const QStringList sceneGroups = getSceneGroupsList();
|
||||
addSelectionGroup(this, sceneGroups);
|
||||
}
|
||||
_groupsEndIdx = count();
|
||||
|
||||
const QStringList scenes = getScenesList();
|
||||
addSelectionGroup(this, scenes);
|
||||
_scenesEndIdx = count();
|
||||
|
||||
// Remove last separator
|
||||
removeItem(count() - 1);
|
||||
setCurrentIndex(0);
|
||||
}
|
||||
|
||||
SceneSelectionWidget::SceneSelectionWidget(QWidget *parent, bool variables,
|
||||
bool sceneGroups, bool previous,
|
||||
bool current, bool preview)
|
||||
: QComboBox(parent),
|
||||
_current(current),
|
||||
_previous(previous),
|
||||
_preview(preview),
|
||||
_variables(variables),
|
||||
_sceneGroups(sceneGroups)
|
||||
{
|
||||
// For the rare occasion of a name conflict with current / previous
|
||||
setDuplicatesEnabled(true);
|
||||
populateSceneSelection(this, previous, current, false, sceneGroups,
|
||||
&switcher->sceneGroups);
|
||||
PopulateSelection();
|
||||
|
||||
QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SelectionChanged(const QString &)));
|
||||
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
|
||||
SLOT(SceneGroupAdd(const QString &)));
|
||||
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
|
||||
this, SLOT(SceneGroupRemove(const QString &)));
|
||||
|
||||
// Scene groups
|
||||
QWidget::connect(window(), SIGNAL(SceneGroupAdded(const QString &)),
|
||||
this, SLOT(ItemAdd(const QString &)));
|
||||
QWidget::connect(window(), SIGNAL(SceneGroupRemoved(const QString &)),
|
||||
this, SLOT(ItemRemove(const QString &)));
|
||||
QWidget::connect(
|
||||
parent,
|
||||
window(),
|
||||
SIGNAL(SceneGroupRenamed(const QString &, const QString &)),
|
||||
this, SLOT(SceneGroupRename(const QString &, const QString &)));
|
||||
this, SLOT(ItemRename(const QString &, const QString &)));
|
||||
|
||||
// Variables
|
||||
QWidget::connect(window(), SIGNAL(VariableAdded(const QString &)), this,
|
||||
SLOT(ItemAdd(const QString &)));
|
||||
QWidget::connect(window(), SIGNAL(VariableRemoved(const QString &)),
|
||||
this, SLOT(ItemRemove(const QString &)));
|
||||
QWidget::connect(
|
||||
window(),
|
||||
SIGNAL(VariableRenamed(const QString &, const QString &)), this,
|
||||
SLOT(ItemRename(const QString &, const QString &)));
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::SetScene(SceneSelection &s)
|
||||
void SceneSelectionWidget::SetScene(const SceneSelection &s)
|
||||
{
|
||||
// Order of entries
|
||||
// 1. Any Scene (current not used)
|
||||
// 2. Current Scene
|
||||
// 3. Previous Scene
|
||||
// 4. Scenes / Scene Groups
|
||||
|
||||
int idx;
|
||||
int idx = 0;
|
||||
|
||||
switch (s.GetType()) {
|
||||
case SceneSelectionType::SCENE:
|
||||
case SceneSelectionType::GROUP:
|
||||
setCurrentText(QString::fromStdString(s.ToString()));
|
||||
break;
|
||||
case SceneSelectionType::PREVIOUS:
|
||||
idx = findText(QString::fromStdString(obs_module_text(
|
||||
"AdvSceneSwitcher.selectPreviousScene")));
|
||||
if (idx != -1) {
|
||||
setCurrentIndex(idx);
|
||||
case SceneSelection::Type::SCENE: {
|
||||
if (_scenesEndIdx == -1) {
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
idx = findIdxInRagne(this, _groupsEndIdx, _scenesEndIdx,
|
||||
s.ToString());
|
||||
break;
|
||||
case SceneSelectionType::CURRENT:
|
||||
idx = findText(QString::fromStdString(obs_module_text(
|
||||
"AdvSceneSwitcher.selectCurrentScene")));
|
||||
if (idx != -1) {
|
||||
setCurrentIndex(idx);
|
||||
}
|
||||
case SceneSelection::Type::GROUP: {
|
||||
if (_groupsEndIdx == -1) {
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
idx = findIdxInRagne(this, _variablesEndIdx, _groupsEndIdx,
|
||||
s.ToString());
|
||||
break;
|
||||
}
|
||||
case SceneSelection::Type::PREVIOUS: {
|
||||
if (_placeholderEndIdx == -1) {
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
idx = findIdxInRagne(
|
||||
this, _selectIdx, _placeholderEndIdx,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.selectPreviousScene"));
|
||||
break;
|
||||
}
|
||||
case SceneSelection::Type::CURRENT: {
|
||||
if (_placeholderEndIdx == -1) {
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
idx = findIdxInRagne(
|
||||
this, _selectIdx, _placeholderEndIdx,
|
||||
obs_module_text("AdvSceneSwitcher.selectCurrentScene"));
|
||||
break;
|
||||
}
|
||||
case SceneSelection::Type::PREVIEW: {
|
||||
if (_placeholderEndIdx == -1) {
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
idx = findIdxInRagne(
|
||||
this, _selectIdx, _placeholderEndIdx,
|
||||
obs_module_text("AdvSceneSwitcher.selectPreviewScene"));
|
||||
break;
|
||||
}
|
||||
case SceneSelection::Type::VARIABLE: {
|
||||
if (_variablesEndIdx == -1) {
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
idx = findIdxInRagne(this, _placeholderEndIdx, _variablesEndIdx,
|
||||
s.ToString());
|
||||
break;
|
||||
default:
|
||||
setCurrentIndex(0);
|
||||
idx = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isFirstEntry(QComboBox *l, QString name, int idx)
|
||||
{
|
||||
for (auto i = l->count() - 1; i >= 0; i--) {
|
||||
if (l->itemText(i) == name) {
|
||||
return idx == i;
|
||||
}
|
||||
}
|
||||
|
||||
// If entry cannot be found we dont want the selection to be empty
|
||||
return false;
|
||||
setCurrentIndex(idx);
|
||||
_currentSelection = s;
|
||||
}
|
||||
|
||||
bool SceneSelectionWidget::IsCurrentSceneSelected(const QString &name)
|
||||
{
|
||||
if (name == QString::fromStdString((obs_module_text(
|
||||
"AdvSceneSwitcher.selectCurrentScene")))) {
|
||||
return isFirstEntry(this, name, currentIndex());
|
||||
}
|
||||
return false;
|
||||
return name == QString::fromStdString((obs_module_text(
|
||||
"AdvSceneSwitcher.selectCurrentScene")));
|
||||
}
|
||||
|
||||
bool SceneSelectionWidget::IsPreviousSceneSelected(const QString &name)
|
||||
{
|
||||
if (name == QString::fromStdString((obs_module_text(
|
||||
"AdvSceneSwitcher.selectPreviousScene")))) {
|
||||
return isFirstEntry(this, name, currentIndex());
|
||||
return name == QString::fromStdString((obs_module_text(
|
||||
"AdvSceneSwitcher.selectPreviousScene")));
|
||||
}
|
||||
|
||||
bool SceneSelectionWidget::IsPreviewSceneSelected(const QString &name)
|
||||
{
|
||||
return name == QString::fromStdString((obs_module_text(
|
||||
"AdvSceneSwitcher.selectPreviewScene")));
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::SelectionChanged(const QString &)
|
||||
{
|
||||
_currentSelection = CurrentSelection();
|
||||
emit SceneChanged(_currentSelection);
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::ItemAdd(const QString &)
|
||||
{
|
||||
blockSignals(true);
|
||||
Reset();
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
bool SceneSelectionWidget::NameUsed(const QString &name)
|
||||
{
|
||||
if (_currentSelection._type == SceneSelection::Type::GROUP &&
|
||||
_currentSelection._group &&
|
||||
_currentSelection._group->name == name.toStdString()) {
|
||||
return true;
|
||||
}
|
||||
if (_currentSelection._type == SceneSelection::Type::VARIABLE) {
|
||||
auto var = _currentSelection._variable.lock();
|
||||
if (var && var->Name() == name.toStdString()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::SelectionChanged(const QString &name)
|
||||
void SceneSelectionWidget::ItemRemove(const QString &name)
|
||||
{
|
||||
SceneSelection s;
|
||||
auto scene = GetWeakSourceByQString(name);
|
||||
if (scene) {
|
||||
s._type = SceneSelectionType::SCENE;
|
||||
s._scene = scene;
|
||||
if (!NameUsed(name)) {
|
||||
blockSignals(true);
|
||||
}
|
||||
|
||||
auto group = GetSceneGroupByQString(name);
|
||||
if (group) {
|
||||
s._type = SceneSelectionType::GROUP;
|
||||
s._scene = nullptr;
|
||||
s._group = group;
|
||||
}
|
||||
|
||||
if (!scene && !group) {
|
||||
if (IsCurrentSceneSelected(name)) {
|
||||
s._type = SceneSelectionType::CURRENT;
|
||||
}
|
||||
if (IsPreviousSceneSelected(name)) {
|
||||
s._type = SceneSelectionType::PREVIOUS;
|
||||
}
|
||||
}
|
||||
|
||||
emit SceneChanged(s);
|
||||
Reset();
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::SceneGroupAdd(const QString &name)
|
||||
void SceneSelectionWidget::ItemRename(const QString &, const QString &)
|
||||
{
|
||||
addItem(name);
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::SceneGroupRemove(const QString &name)
|
||||
{
|
||||
int idx = findText(name);
|
||||
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int curIdx = currentIndex();
|
||||
removeItem(idx);
|
||||
|
||||
if (curIdx == idx) {
|
||||
SceneSelection s;
|
||||
emit SceneChanged(s);
|
||||
}
|
||||
|
||||
setCurrentIndex(0);
|
||||
}
|
||||
|
||||
static int findLastOf(QComboBox *l, QString name)
|
||||
{
|
||||
int idx = 0;
|
||||
for (auto i = l->count() - 1; i >= 0; i--) {
|
||||
if (l->itemText(i) == name) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
void SceneSelectionWidget::SceneGroupRename(const QString &oldName,
|
||||
const QString &newName)
|
||||
{
|
||||
bool renameSelected = currentText() == oldName;
|
||||
int idx = findText(oldName);
|
||||
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeItem(idx);
|
||||
insertItem(idx, newName);
|
||||
|
||||
if (renameSelected) {
|
||||
setCurrentIndex(findLastOf(this, newName));
|
||||
}
|
||||
blockSignals(true);
|
||||
Reset();
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,34 @@
|
||||
#pragma once
|
||||
#include "scene-group.hpp"
|
||||
#include "variable.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
enum class SceneSelectionType {
|
||||
SCENE,
|
||||
GROUP,
|
||||
PREVIOUS,
|
||||
CURRENT,
|
||||
};
|
||||
|
||||
class SceneSelection {
|
||||
public:
|
||||
void Save(obs_data_t *obj, const char *name = "scene",
|
||||
const char *typeName = "sceneType");
|
||||
void Save(obs_data_t *obj) const;
|
||||
void Load(obs_data_t *obj, const char *name = "scene",
|
||||
const char *typeName = "sceneType");
|
||||
|
||||
SceneSelectionType GetType() { return _type; }
|
||||
OBSWeakSource GetScene(bool advance = true);
|
||||
std::string ToString();
|
||||
enum class Type {
|
||||
SCENE,
|
||||
GROUP,
|
||||
PREVIOUS,
|
||||
CURRENT,
|
||||
PREVIEW,
|
||||
VARIABLE,
|
||||
};
|
||||
|
||||
Type GetType() const { return _type; }
|
||||
OBSWeakSource GetScene(bool advance = true) const;
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
OBSWeakSource _scene;
|
||||
SceneGroup *_group = nullptr;
|
||||
SceneSelectionType _type = SceneSelectionType::SCENE;
|
||||
std::weak_ptr<Variable> _variable;
|
||||
Type _type = Type::SCENE;
|
||||
friend class SceneSelectionWidget;
|
||||
};
|
||||
|
||||
@@ -33,19 +36,45 @@ class SceneSelectionWidget : public QComboBox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SceneSelectionWidget(QWidget *parent, bool sceneGroups = false,
|
||||
bool previous = false, bool current = false);
|
||||
void SetScene(SceneSelection &);
|
||||
SceneSelectionWidget(QWidget *parent, bool variables = false,
|
||||
bool sceneGroups = false, bool previous = false,
|
||||
bool current = false, bool preview = false);
|
||||
void SetScene(const SceneSelection &);
|
||||
signals:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
|
||||
private slots:
|
||||
void SelectionChanged(const QString &name);
|
||||
void SceneGroupAdd(const QString &name);
|
||||
void SceneGroupRemove(const QString &name);
|
||||
void SceneGroupRename(const QString &oldName, const QString &newName);
|
||||
void ItemAdd(const QString &name);
|
||||
void ItemRemove(const QString &name);
|
||||
void ItemRename(const QString &oldName, const QString &newName);
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
SceneSelection CurrentSelection();
|
||||
void PopulateSelection();
|
||||
bool IsCurrentSceneSelected(const QString &name);
|
||||
bool IsPreviousSceneSelected(const QString &name);
|
||||
bool IsPreviewSceneSelected(const QString &name);
|
||||
bool NameUsed(const QString &name);
|
||||
|
||||
bool _current;
|
||||
bool _previous;
|
||||
bool _preview;
|
||||
bool _variables;
|
||||
bool _sceneGroups;
|
||||
|
||||
SceneSelection _currentSelection;
|
||||
|
||||
// Order of entries
|
||||
// 1. "select entry" entry
|
||||
// 2. Current / previous / preview scene
|
||||
// 3. Variables
|
||||
// 4. Scene groups
|
||||
// 5. Regular scenes
|
||||
const int _selectIdx = 0;
|
||||
int _placeholderEndIdx = -1;
|
||||
int _variablesEndIdx = -1;
|
||||
int _groupsEndIdx = -1;
|
||||
int _scenesEndIdx = -1;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,35 @@
|
||||
#include "screenshot-helper.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
static void ScreenshotTick(void *param, float);
|
||||
|
||||
ScreenshotHelper::ScreenshotHelper(obs_source_t *source)
|
||||
: weakSource(OBSGetWeakRef(source))
|
||||
ScreenshotHelper::ScreenshotHelper(obs_source_t *source, bool blocking,
|
||||
int timeout, bool saveToFile,
|
||||
std::string path)
|
||||
: weakSource(OBSGetWeakRef(source)),
|
||||
_blocking(blocking),
|
||||
_saveToFile(saveToFile),
|
||||
_path(path)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_initDone = true;
|
||||
obs_add_tick_callback(ScreenshotTick, this);
|
||||
if (_blocking) {
|
||||
auto res =
|
||||
_cv.wait_for(lock, std::chrono::milliseconds(timeout));
|
||||
if (res == std::cv_status::timeout) {
|
||||
if (source) {
|
||||
blog(LOG_WARNING,
|
||||
"Failed to get screenshot in time for source %s",
|
||||
obs_source_get_name(source));
|
||||
} else {
|
||||
blog(LOG_WARNING,
|
||||
"Failed to get screenshot in time");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScreenshotHelper::~ScreenshotHelper()
|
||||
@@ -20,6 +42,9 @@ ScreenshotHelper::~ScreenshotHelper()
|
||||
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
}
|
||||
if (_saveThread.joinable()) {
|
||||
_saveThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenshotHelper::Screenshot()
|
||||
@@ -98,6 +123,26 @@ void ScreenshotHelper::MarkDone()
|
||||
{
|
||||
time = std::chrono::high_resolution_clock::now();
|
||||
done = true;
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cv.notify_all();
|
||||
}
|
||||
|
||||
void ScreenshotHelper::WriteToFile()
|
||||
{
|
||||
if (!_saveToFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
_saveThread = std::thread([this]() {
|
||||
if (image.save(QString::fromStdString(_path))) {
|
||||
vblog(LOG_INFO, "Wrote screenshot to \"%s\"",
|
||||
_path.c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING,
|
||||
"Failed to save screenshot to \"%s\"!\nMaybe unknown format?",
|
||||
_path.c_str());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#define STAGE_SCREENSHOT 0
|
||||
@@ -124,6 +169,7 @@ static void ScreenshotTick(void *param, float)
|
||||
break;
|
||||
case STAGE_COPY_AND_SAVE:
|
||||
data->Copy();
|
||||
data->WriteToFile();
|
||||
data->MarkDone();
|
||||
|
||||
obs_remove_tick_callback(ScreenshotTick, data);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user