mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 00:56:00 -05:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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)'
|
description: 'Visual Studio version (Windows only)'
|
||||||
required: false
|
required: false
|
||||||
default: 'Visual Studio 16 2019'
|
default: 'Visual Studio 16 2019'
|
||||||
|
portable:
|
||||||
|
description: 'Set portable mode (Linux only)'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
workingDirectory:
|
workingDirectory:
|
||||||
description: 'Working directory for packaging'
|
description: 'Working directory for packaging'
|
||||||
required: false
|
required: false
|
||||||
@@ -56,6 +60,10 @@ runs:
|
|||||||
build_args+=(--debug)
|
build_args+=(--debug)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ '${{ inputs.portable }}' == 'true' ]]; then
|
||||||
|
build_args+=(-p)
|
||||||
|
fi
|
||||||
|
|
||||||
${{ inputs.workingDirectory }}/.github/scripts/build-linux.sh "${build_args[@]}"
|
${{ inputs.workingDirectory }}/.github/scripts/build-linux.sh "${build_args[@]}"
|
||||||
|
|
||||||
- name: Run Windows Build
|
- 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)'
|
description: 'Create InnoSetup installer (Windows only)'
|
||||||
required: false
|
required: false
|
||||||
default: 'false'
|
default: 'false'
|
||||||
|
portable:
|
||||||
|
description: 'Create deb package / portable archive (Linux only)'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
workingDirectory:
|
workingDirectory:
|
||||||
description: 'Working directory for packaging'
|
description: 'Working directory for packaging'
|
||||||
required: false
|
required: false
|
||||||
@@ -75,6 +79,10 @@ runs:
|
|||||||
build_args+=(--debug)
|
build_args+=(--debug)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ '${{ inputs.portable }}' == 'true' ]]; then
|
||||||
|
package_args+=(-z)
|
||||||
|
fi
|
||||||
|
|
||||||
${{ inputs.workingDirectory }}/.github/scripts/package-linux.sh "${package_args[@]}"
|
${{ inputs.workingDirectory }}/.github/scripts/package-linux.sh "${package_args[@]}"
|
||||||
|
|
||||||
- name: Run Windows packaging
|
- 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-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-c | --config%b Build configuration - default: %B%F{green}RelWithDebInfo%f%b
|
||||||
%B-s | --codesign%b Enable codesigning (macOS only)
|
%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
|
%B--generator%b Specify build system to generate - default: %B%F{green}Ninja%f%b
|
||||||
Available generators:
|
Available generators:
|
||||||
- Ninja
|
- Ninja
|
||||||
@@ -130,6 +131,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
|||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
-s|--codesign) CODESIGN=1; shift ;;
|
-s|--codesign) CODESIGN=1; shift ;;
|
||||||
|
-p|--portable) typeset -g PORTABLE=1; shift ;;
|
||||||
-q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
|
-q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
|
||||||
-v|--verbose) (( _verbosity += 1 )); shift ;;
|
-v|--verbose) (( _verbosity += 1 )); shift ;;
|
||||||
-h|--help) log_output ${_usage}; exit 0 ;;
|
-h|--help) log_output ${_usage}; exit 0 ;;
|
||||||
@@ -245,7 +247,11 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
|||||||
|
|
||||||
;;
|
;;
|
||||||
linux-*)
|
linux-*)
|
||||||
if (( ${+CI} )) {
|
if (( ${+PORTABLE} )) {
|
||||||
|
cmake_args+=(
|
||||||
|
-DLINUX_PORTABLE=ON
|
||||||
|
)
|
||||||
|
} else {
|
||||||
cmake_args+=(
|
cmake_args+=(
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr
|
-DCMAKE_INSTALL_PREFIX=/usr
|
||||||
-DLINUX_PORTABLE=OFF
|
-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-c | --config%b Build configuration - default: %B%F{green}RelWithDebInfo%f%b
|
||||||
%B-s | --codesign%b Enable codesigning (macOS only)
|
%B-s | --codesign%b Enable codesigning (macOS only)
|
||||||
%B-n | --notarize%b Enable notarization (macOS only)
|
%B-n | --notarize%b Enable notarization (macOS only)
|
||||||
|
%B-z | --zip%b Zip only (Linux only)
|
||||||
|
|
||||||
%F{yellow} Output options%f
|
%F{yellow} Output options%f
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
@@ -110,6 +111,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
|||||||
;;
|
;;
|
||||||
-s|--codesign) typeset -g CODESIGN=1; shift ;;
|
-s|--codesign) typeset -g CODESIGN=1; shift ;;
|
||||||
-n|--notarize) typeset -g NOTARIZE=1; 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 ;;
|
-q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
|
||||||
-v|--verbose) (( _verbosity += 1 )); shift ;;
|
-v|--verbose) (( _verbosity += 1 )); shift ;;
|
||||||
-h|--help) log_output ${_usage}; exit 0 ;;
|
-h|--help) log_output ${_usage}; exit 0 ;;
|
||||||
@@ -132,7 +134,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
|||||||
if [[ ${host_os} == 'macos' ]] {
|
if [[ ${host_os} == 'macos' ]] {
|
||||||
autoload -Uz check_packages read_codesign read_codesign_installer read_codesign_pass
|
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 ]] {
|
if [[ ! -d ${project_root}/release/${product_name}.plugin ]] {
|
||||||
log_error 'No release artifact found. Run the build script or the CMake install procedure first.'
|
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
|
popd
|
||||||
} elif [[ ${host_os} == 'linux' ]] {
|
} elif [[ ${host_os} == 'linux' ]] {
|
||||||
local -a cmake_args=()
|
if (( ${+ZIP} )) {
|
||||||
if (( _loglevel > 1 )) cmake_args+=(--verbose)
|
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}
|
pushd ${project_root}
|
||||||
cmake --build build_${target##*-} --config ${BUILD_CONFIG:-RelWithDebInfo} -t package ${cmake_args}
|
cmake --build build_${target##*-} --config ${BUILD_CONFIG:-RelWithDebInfo} -t package ${cmake_args}
|
||||||
popd
|
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
|
$BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json
|
||||||
$ProductName = $BuildSpec.name
|
$ProductName = $BuildSpec.name
|
||||||
$ProductVersion = $BuildSpec.version
|
|
||||||
|
|
||||||
$OutputName = "${ProductName}-${ProductVersion}-windows-${Target}"
|
$OutputName = "${ProductName}-windows-${Target}"
|
||||||
|
|
||||||
Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile"
|
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
|
||||||
25
.github/workflows/main.yml
vendored
25
.github/workflows/main.yml
vendored
@@ -169,7 +169,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ env.PLUGIN_NAME }}-macos-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}
|
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:
|
linux_build:
|
||||||
name: 02 - Linux
|
name: 02 - Linux
|
||||||
@@ -178,6 +178,7 @@ jobs:
|
|||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
arch: [x86_64]
|
arch: [x86_64]
|
||||||
|
portable: [true, false]
|
||||||
if: always()
|
if: always()
|
||||||
needs: [clang_check]
|
needs: [clang_check]
|
||||||
outputs:
|
outputs:
|
||||||
@@ -235,13 +236,7 @@ jobs:
|
|||||||
workingDirectory: ${{ github.workspace }}/plugin
|
workingDirectory: ${{ github.workspace }}/plugin
|
||||||
target: ${{ matrix.arch }}
|
target: ${{ matrix.arch }}
|
||||||
config: RelWithDebInfo
|
config: RelWithDebInfo
|
||||||
|
portable: ${{ matrix.portable }}
|
||||||
- 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/*
|
|
||||||
|
|
||||||
- name: Package Plugin
|
- name: Package Plugin
|
||||||
uses: ./plugin/.github/actions/package-plugin
|
uses: ./plugin/.github/actions/package-plugin
|
||||||
@@ -249,13 +244,21 @@ jobs:
|
|||||||
workingDirectory: ${{ github.workspace }}/plugin
|
workingDirectory: ${{ github.workspace }}/plugin
|
||||||
target: ${{ matrix.arch }}
|
target: ${{ matrix.arch }}
|
||||||
config: RelWithDebInfo
|
config: RelWithDebInfo
|
||||||
|
portable: ${{ matrix.portable }}
|
||||||
|
|
||||||
- name: Upload Package Artifact
|
- 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
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ env.PLUGIN_NAME }}-linux-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}
|
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:
|
windows_build:
|
||||||
name: 02 - Windows
|
name: 02 - Windows
|
||||||
@@ -263,7 +266,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
arch: [x86, x64]
|
arch: [x64]
|
||||||
if: always()
|
if: always()
|
||||||
needs: [clang_check]
|
needs: [clang_check]
|
||||||
outputs:
|
outputs:
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,6 +10,3 @@
|
|||||||
[submodule "deps/openvr"]
|
[submodule "deps/openvr"]
|
||||||
path = deps/openvr
|
path = deps/openvr
|
||||||
url = https://github.com/ValveSoftware/openvr.git
|
url = https://github.com/ValveSoftware/openvr.git
|
||||||
[submodule "deps/obs-websocket"]
|
|
||||||
path = deps/obs-websocket
|
|
||||||
url = https://github.com/obsproject/obs-websocket.git
|
|
||||||
|
|||||||
@@ -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.
|
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)
|
## Compiling in tree (recommended for development)
|
||||||
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
|
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
|
||||||
```
|
```
|
||||||
@@ -25,6 +27,7 @@ Note that on Linux systems it might be necessary to additionally install the fol
|
|||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
libxtst-dev \
|
libxtst-dev \
|
||||||
libxss-dev \
|
libxss-dev \
|
||||||
|
libprocps-dev \
|
||||||
libopencv-dev
|
libopencv-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
project(advanced-scene-switcher VERSION 1.0.0)
|
||||||
set(LIB_NAME "${PROJECT_NAME}-lib")
|
set(LIB_NAME "${PROJECT_NAME}-lib")
|
||||||
@@ -147,6 +147,8 @@ target_sources(
|
|||||||
src/macro-core/macro-action-timer.hpp
|
src/macro-core/macro-action-timer.hpp
|
||||||
src/macro-core/macro-action-transition.cpp
|
src/macro-core/macro-action-transition.cpp
|
||||||
src/macro-core/macro-action-transition.hpp
|
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.cpp
|
||||||
src/macro-core/macro-action-virtual-cam.hpp
|
src/macro-core/macro-action-virtual-cam.hpp
|
||||||
src/macro-core/macro-action-wait.cpp
|
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-timer.hpp
|
||||||
src/macro-core/macro-condition-transition.cpp
|
src/macro-core/macro-condition-transition.cpp
|
||||||
src/macro-core/macro-condition-transition.hpp
|
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.cpp
|
||||||
src/macro-core/macro-condition-virtual-cam.hpp
|
src/macro-core/macro-condition-virtual-cam.hpp
|
||||||
src/macro-core/macro-condition-websocket.cpp
|
src/macro-core/macro-condition-websocket.cpp
|
||||||
@@ -238,12 +242,16 @@ target_sources(
|
|||||||
src/utils/curl-helper.hpp
|
src/utils/curl-helper.hpp
|
||||||
src/utils/duration-control.cpp
|
src/utils/duration-control.cpp
|
||||||
src/utils/duration-control.hpp
|
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.cpp
|
||||||
src/utils/file-selection.hpp
|
src/utils/file-selection.hpp
|
||||||
src/utils/macro-list.cpp
|
src/utils/macro-list.cpp
|
||||||
src/utils/macro-list.hpp
|
src/utils/macro-list.hpp
|
||||||
src/utils/name-dialog.cpp
|
src/utils/name-dialog.cpp
|
||||||
src/utils/name-dialog.hpp
|
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.cpp
|
||||||
src/utils/resizing-text-edit.hpp
|
src/utils/resizing-text-edit.hpp
|
||||||
src/utils/scene-item-selection.cpp
|
src/utils/scene-item-selection.cpp
|
||||||
@@ -258,29 +266,27 @@ target_sources(
|
|||||||
src/utils/transition-selection.hpp
|
src/utils/transition-selection.hpp
|
||||||
src/utils/utility.cpp
|
src/utils/utility.cpp
|
||||||
src/utils/utility.hpp
|
src/utils/utility.hpp
|
||||||
|
src/utils/variable.cpp
|
||||||
|
src/utils/variable.hpp
|
||||||
src/utils/volume-control.cpp
|
src/utils/volume-control.cpp
|
||||||
|
src/utils/volume-control.hpp
|
||||||
src/utils/websocket-helpers.cpp
|
src/utils/websocket-helpers.cpp
|
||||||
src/utils/websocket-helpers.hpp
|
src/utils/websocket-helpers.hpp)
|
||||||
src/utils/volume-control.hpp)
|
|
||||||
|
|
||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${LIB_NAME})
|
|
||||||
|
|
||||||
if(BUILD_OUT_OF_TREE)
|
if(BUILD_OUT_OF_TREE)
|
||||||
find_package(libobs REQUIRED)
|
|
||||||
find_package(obs-frontend-api REQUIRED)
|
|
||||||
include(cmake/ObsPluginHelpers.cmake)
|
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()
|
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)
|
find_qt(COMPONENTS Widgets Core)
|
||||||
target_link_libraries(${LIB_NAME} PUBLIC Qt::Core Qt::Widgets)
|
target_link_libraries(${LIB_NAME} PUBLIC Qt::Core Qt::Widgets)
|
||||||
|
|
||||||
include(cmake/AdvSSHelpers.cmake)
|
|
||||||
|
|
||||||
# --- Platform-independent build settings ---
|
# --- Platform-independent build settings ---
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
@@ -357,9 +363,10 @@ elseif(OS_MACOS)
|
|||||||
|
|
||||||
# --- Linux-specific build settings and tasks ---
|
# --- Linux-specific build settings and tasks ---
|
||||||
else()
|
else()
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH "$ORIGIN")
|
||||||
target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra)
|
target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra)
|
||||||
|
|
||||||
set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")
|
set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(${LIB_NAME} PROPERTIES SOVERSION 1)
|
||||||
|
|
||||||
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
|
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
|
||||||
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
|
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
|
||||||
@@ -392,31 +399,26 @@ endif()
|
|||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
|
||||||
target_link_libraries(${LIB_NAME} PUBLIC ${${PROJECT_NAME}_PLATFORM_LIBS})
|
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)
|
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")
|
file(GLOB ASS_TRANSLATION_FILES "data/locale/*.ini")
|
||||||
|
if(NOT LIB_OUT_DIR)
|
||||||
# Linux
|
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||||
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})
|
|
||||||
endif()
|
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()
|
endif()
|
||||||
|
|
||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ If `apt` is supported on your system they can be installed using:
|
|||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
libxtst-dev \
|
libxtst-dev \
|
||||||
libxss-dev \
|
libxss-dev \
|
||||||
|
libprocps-dev \
|
||||||
libopencv-dev
|
libopencv-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"obs-studio": {
|
"obs-studio": {
|
||||||
"version": "28.0.0-beta1",
|
"version": "28.0.1",
|
||||||
"repository": "https://github.com/obsproject/obs-studio.git",
|
"repository": "https://github.com/obsproject/obs-studio.git",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"hash": "43a49dca47344a5170159ef99b86b97f90d4e4ad"
|
"hash": "e8dc70d0eef4503378d6ac300e680215eb5c9379"
|
||||||
},
|
},
|
||||||
"prebuilt": {
|
"prebuilt": {
|
||||||
"version": "2022-08-02",
|
"version": "2022-08-02",
|
||||||
|
|||||||
@@ -67,18 +67,27 @@ if(OS_MACOS)
|
|||||||
else()
|
else()
|
||||||
# --- Windows / Linux section ---
|
# --- Windows / Linux section ---
|
||||||
function(plugin_install_helper what where where_deb)
|
function(plugin_install_helper what where where_deb)
|
||||||
install(
|
if(DEB_INSTALL)
|
||||||
TARGETS ${what}
|
if(NOT LIB_OUT_DIR)
|
||||||
RUNTIME DESTINATION "${where}" COMPONENT ${what}_Runtime
|
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||||
LIBRARY DESTINATION "${where}"
|
endif()
|
||||||
COMPONENT ${what}_Runtime
|
install(
|
||||||
NAMELINK_COMPONENT ${what}_Development)
|
TARGETS ${what}
|
||||||
install(
|
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR}/${where_deb})
|
||||||
FILES $<TARGET_FILE:${what}>
|
else()
|
||||||
DESTINATION $<CONFIG>/${where}
|
install(
|
||||||
COMPONENT ${what}_rundir
|
TARGETS ${what}
|
||||||
EXCLUDE_FROM_ALL)
|
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)
|
if(OS_WINDOWS)
|
||||||
install(
|
install(
|
||||||
FILES $<TARGET_PDB_FILE:${what}>
|
FILES $<TARGET_PDB_FILE:${what}>
|
||||||
@@ -93,7 +102,6 @@ else()
|
|||||||
COMPONENT ${what}_rundir
|
COMPONENT ${what}_rundir
|
||||||
OPTIONAL EXCLUDE_FROM_ALL)
|
OPTIONAL EXCLUDE_FROM_ALL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${what}
|
TARGET ${what}
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
@@ -104,25 +112,23 @@ else()
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}/cmake_install.cmake
|
${CMAKE_CURRENT_BINARY_DIR}/cmake_install.cmake
|
||||||
COMMENT "Installing ${what} to plugin rundir ${OBS_OUTPUT_DIR}/${where}\n"
|
COMMENT "Installing ${what} to plugin rundir ${OBS_OUTPUT_DIR}/${where}\n"
|
||||||
VERBATIM)
|
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()
|
endfunction()
|
||||||
|
|
||||||
function(install_advss_lib target)
|
function(install_advss_lib target)
|
||||||
plugin_install_helper("${target}" "${OBS_PLUGIN_DESTINATION}" "")
|
plugin_install_helper("${target}" "${OBS_PLUGIN_DESTINATION}" "")
|
||||||
|
if(OS_POSIX)
|
||||||
|
set_target_properties(${target} PROPERTIES INSTALL_RPATH "$ORIGIN")
|
||||||
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(install_advss_plugin target)
|
function(install_advss_plugin target)
|
||||||
plugin_install_helper(
|
plugin_install_helper(
|
||||||
"${target}" "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
"${target}" "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||||
"${_PLUGIN_FOLDER}")
|
"${_PLUGIN_FOLDER}")
|
||||||
|
if(OS_POSIX)
|
||||||
|
set_target_properties(${target} PROPERTIES INSTALL_RPATH
|
||||||
|
"$ORIGIN:$ORIGIN/..")
|
||||||
|
endif()
|
||||||
message(STATUS "ADVSS: ENABLED PLUGIN ${target}")
|
message(STATUS "ADVSS: ENABLED PLUGIN ${target}")
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
@@ -205,14 +211,50 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
function(setup_obs_lib_dependency target)
|
||||||
function(setup_advss_plugin target)
|
|
||||||
if(BUILD_OUT_OF_TREE)
|
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()
|
else()
|
||||||
target_link_libraries(${target} PUBLIC OBS::libobs OBS::frontend-api)
|
target_link_libraries(${target} PUBLIC OBS::libobs OBS::frontend-api)
|
||||||
endif()
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(setup_advss_plugin target)
|
||||||
|
setup_obs_lib_dependency(${target})
|
||||||
find_qt(COMPONENTS Widgets Core)
|
find_qt(COMPONENTS Widgets Core)
|
||||||
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets)
|
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets)
|
||||||
|
|
||||||
|
|||||||
@@ -346,13 +346,18 @@ else()
|
|||||||
set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
|
set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
|
||||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${LINUX_MAINTAINER_EMAIL}")
|
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${LINUX_MAINTAINER_EMAIL}")
|
||||||
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}")
|
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}")
|
||||||
set(CPACK_PACKAGE_FILE_NAME
|
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-linux-x86_64")
|
||||||
"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-linux-x86_64")
|
|
||||||
|
|
||||||
set(CPACK_GENERATOR "DEB")
|
set(CPACK_GENERATOR "DEB")
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS
|
if(${QT_VERSION} STREQUAL "6")
|
||||||
"obs-studio (>= 27.0.0), libqt5core5a (>= 5.9.0~beta), libqt5gui5 (>= 5.3.0), libqt5widgets5 (>= 5.7.0)"
|
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)
|
set(CPACK_OUTPUT_FILE_PREFIX ${CMAKE_SOURCE_DIR}/release)
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ AdvSceneSwitcher.macroTab.minimize="Minimize"
|
|||||||
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
|
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
|
||||||
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
|
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.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
|
; Macro List
|
||||||
AdvSceneSwitcher.macroList.deleted="deleted"
|
AdvSceneSwitcher.macroList.deleted="deleted"
|
||||||
@@ -128,7 +129,10 @@ AdvSceneSwitcher.condition.window="Window"
|
|||||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
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.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
|
||||||
AdvSceneSwitcher.condition.file="File"
|
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.line2="{{matchText}}"
|
||||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||||
AdvSceneSwitcher.condition.media="Media"
|
AdvSceneSwitcher.condition.media="Media"
|
||||||
@@ -148,6 +152,8 @@ AdvSceneSwitcher.condition.video.condition.object="contains object"
|
|||||||
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="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.file="Use existing file"
|
||||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
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="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.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.patternThreshold="Threshold: "
|
||||||
@@ -169,7 +175,8 @@ AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePa
|
|||||||
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
|
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
|
||||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors: {{minNeighbors}}"
|
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.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.minSize="Minimum size:"
|
||||||
AdvSceneSwitcher.condition.video.maxSize="Maximum size:"
|
AdvSceneSwitcher.condition.video.maxSize="Maximum size:"
|
||||||
AdvSceneSwitcher.condition.video.selectArea="Select area"
|
AdvSceneSwitcher.condition.video.selectArea="Select area"
|
||||||
@@ -227,7 +234,6 @@ AdvSceneSwitcher.condition.source="Source"
|
|||||||
AdvSceneSwitcher.condition.source.type.active="Is active"
|
AdvSceneSwitcher.condition.source.type.active="Is active"
|
||||||
AdvSceneSwitcher.condition.source.type.showing="Is showing"
|
AdvSceneSwitcher.condition.source.type.showing="Is showing"
|
||||||
AdvSceneSwitcher.condition.source.type.settings="Settings match"
|
AdvSceneSwitcher.condition.source.type.settings="Settings match"
|
||||||
AdvSceneSwitcher.condition.source.regex="Use regular expressions"
|
|
||||||
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
|
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
|
||||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||||
@@ -240,7 +246,6 @@ AdvSceneSwitcher.condition.filter="Filter"
|
|||||||
AdvSceneSwitcher.condition.filter.type.active="Is enabled"
|
AdvSceneSwitcher.condition.filter.type.active="Is enabled"
|
||||||
AdvSceneSwitcher.condition.filter.type.showing="Is disabled"
|
AdvSceneSwitcher.condition.filter.type.showing="Is disabled"
|
||||||
AdvSceneSwitcher.condition.filter.type.settings="Settings match"
|
AdvSceneSwitcher.condition.filter.type.settings="Settings match"
|
||||||
AdvSceneSwitcher.condition.filter.regex="Use regular expressions"
|
|
||||||
AdvSceneSwitcher.condition.filter.getSettings="Get current settings"
|
AdvSceneSwitcher.condition.filter.getSettings="Get current settings"
|
||||||
AdvSceneSwitcher.condition.filter.entry.line1="On {{sources}} {{filters}} {{conditions}}"
|
AdvSceneSwitcher.condition.filter.entry.line1="On {{sources}} {{filters}} {{conditions}}"
|
||||||
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||||
@@ -286,7 +291,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.date.entry.updateOnRepeat="{{updateOnRepeat}} On repeat update selected date to repeat date"
|
||||||
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
|
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="On{{scenes}}{{sources}}matches transform"
|
AdvSceneSwitcher.condition.sceneTransform.entry.line1="On{{scenes}}{{sources}}matches transform"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||||
@@ -335,12 +339,25 @@ AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
|
|||||||
AdvSceneSwitcher.condition.profile="Profile"
|
AdvSceneSwitcher.condition.profile="Profile"
|
||||||
AdvSceneSwitcher.condition.profile.entry="Current active profile is {{profiles}}"
|
AdvSceneSwitcher.condition.profile.entry="Current active profile is {{profiles}}"
|
||||||
AdvSceneSwitcher.condition.websocket="Websocket"
|
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.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
|
; Macro Actions
|
||||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
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.scene.blockUntilTransitionDone="Wait until transition to target scene is complete"
|
||||||
AdvSceneSwitcher.action.wait="Wait"
|
AdvSceneSwitcher.action.wait="Wait"
|
||||||
AdvSceneSwitcher.action.wait.type.fixed="fixed"
|
AdvSceneSwitcher.action.wait.type.fixed="fixed"
|
||||||
@@ -481,8 +498,13 @@ AdvSceneSwitcher.action.random.entry="Randomly run any of the following macros (
|
|||||||
AdvSceneSwitcher.action.systray="System tray notification"
|
AdvSceneSwitcher.action.systray="System tray notification"
|
||||||
AdvSceneSwitcher.action.systray.entry="Show notification: {{message}}"
|
AdvSceneSwitcher.action.systray.entry="Show notification: {{message}}"
|
||||||
AdvSceneSwitcher.action.screenshot="Screenshot"
|
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.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="Profile"
|
||||||
AdvSceneSwitcher.action.profile.entry="Switch active profile to {{profiles}}"
|
AdvSceneSwitcher.action.profile.entry="Switch active profile to {{profiles}}"
|
||||||
AdvSceneSwitcher.action.sceneCollection="Scene collection"
|
AdvSceneSwitcher.action.sceneCollection="Scene collection"
|
||||||
@@ -495,12 +517,22 @@ AdvSceneSwitcher.action.sequence.status.none="none"
|
|||||||
AdvSceneSwitcher.action.sequence.restart="Restart from beginning once end of list is reached"
|
AdvSceneSwitcher.action.sequence.restart="Restart from beginning once end of list is reached"
|
||||||
AdvSceneSwitcher.action.sequence.continueFrom="Continue with selected item"
|
AdvSceneSwitcher.action.sequence.continueFrom="Continue with selected item"
|
||||||
AdvSceneSwitcher.action.websocket="Websocket"
|
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="Http"
|
||||||
AdvSceneSwitcher.action.http.type.get="GET"
|
AdvSceneSwitcher.action.http.type.get="GET"
|
||||||
AdvSceneSwitcher.action.http.type.post="POST"
|
AdvSceneSwitcher.action.http.type.post="POST"
|
||||||
AdvSceneSwitcher.action.http.entry.line1="Send {{method}} to {{url}}"
|
AdvSceneSwitcher.action.http.entry.line1="Send {{method}} to {{url}}"
|
||||||
AdvSceneSwitcher.action.http.entry.line2="Timeout: {{timeout}} seconds"
|
AdvSceneSwitcher.action.http.entry.line2="Timeout: {{timeout}} seconds"
|
||||||
|
AdvSceneSwitcher.action.variable="Variable"
|
||||||
|
AdvSceneSwitcher.action.variable.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
|
; Transition Tab
|
||||||
@@ -757,15 +789,27 @@ AdvSceneSwitcher.askForMacro="Select macro {{macroSelection}}"
|
|||||||
AdvSceneSwitcher.close="Close"
|
AdvSceneSwitcher.close="Close"
|
||||||
AdvSceneSwitcher.browse="Browse"
|
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.select="--select connection--"
|
||||||
AdvSceneSwitcher.connection.add="Add new 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.name="Name:"
|
||||||
AdvSceneSwitcher.connection.address="Address:"
|
AdvSceneSwitcher.connection.address="Address:"
|
||||||
AdvSceneSwitcher.connection.port="Port:"
|
AdvSceneSwitcher.connection.port="Port:"
|
||||||
@@ -779,9 +823,17 @@ AdvSceneSwitcher.connection.status.connecting="Connecting"
|
|||||||
AdvSceneSwitcher.connection.status.connected="Connected, but not authenticated"
|
AdvSceneSwitcher.connection.status.connected="Connected, but not authenticated"
|
||||||
AdvSceneSwitcher.connection.status.authenticated="Connected and 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.selectScene="--select scene--"
|
||||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||||
|
AdvSceneSwitcher.selectPreviewScene="Preview Scene"
|
||||||
AdvSceneSwitcher.selectAnyScene="Any Scene"
|
AdvSceneSwitcher.selectAnyScene="Any Scene"
|
||||||
AdvSceneSwitcher.currentTransition="Current Transition"
|
AdvSceneSwitcher.currentTransition="Current Transition"
|
||||||
AdvSceneSwitcher.anyTransition="Any Transition"
|
AdvSceneSwitcher.anyTransition="Any Transition"
|
||||||
@@ -790,7 +842,7 @@ AdvSceneSwitcher.selectWindow="--select window--"
|
|||||||
AdvSceneSwitcher.selectSource="--select source--"
|
AdvSceneSwitcher.selectSource="--select source--"
|
||||||
AdvSceneSwitcher.selectAudioSource="--select audio source--"
|
AdvSceneSwitcher.selectAudioSource="--select audio source--"
|
||||||
AdvSceneSwitcher.selectVideoSource="--select video source--"
|
AdvSceneSwitcher.selectVideoSource="--select video source--"
|
||||||
AdvSceneSwitcher.OBSVideoOutput="OBS video output"
|
AdvSceneSwitcher.OBSVideoOutput="OBS's main output"
|
||||||
AdvSceneSwitcher.selectMediaSource="--select media source--"
|
AdvSceneSwitcher.selectMediaSource="--select media source--"
|
||||||
AdvSceneSwitcher.selectProcess="--select process--"
|
AdvSceneSwitcher.selectProcess="--select process--"
|
||||||
AdvSceneSwitcher.selectFilter="--select filter--"
|
AdvSceneSwitcher.selectFilter="--select filter--"
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ AdvSceneSwitcher.condition.window="Ventana"
|
|||||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} existen y ..."
|
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.window.entry.line2="... es {{fullscreen}} pantalla completa {{maximized}} maximizada {{focused}} enfocada {{windowFocusChanged}} ventana de primer plano cambiada"
|
||||||
AdvSceneSwitcher.condition.file="Archivo"
|
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.line2="{{matchText}}"
|
||||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||||
AdvSceneSwitcher.condition.media="Medios"
|
AdvSceneSwitcher.condition.media="Medios"
|
||||||
@@ -164,6 +164,7 @@ AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePa
|
|||||||
AdvSceneSwitcher.condition.video.entry.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
|
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.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.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.entry.checkArea="{{checkAreaEnable}}Realizar comprobación solo en el área {{checkArea}} {{selectArea}}"
|
||||||
AdvSceneSwitcher.condition.video.minSize="Tamaño mínimo:"
|
AdvSceneSwitcher.condition.video.minSize="Tamaño mínimo:"
|
||||||
AdvSceneSwitcher.condition.video.maxSize="Tamaño máximo:"
|
AdvSceneSwitcher.condition.video.maxSize="Tamaño máximo:"
|
||||||
@@ -463,7 +464,6 @@ AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
|
|||||||
AdvSceneSwitcher.action.systray.entry="Mostrar notificación: {{message}}"
|
AdvSceneSwitcher.action.systray.entry="Mostrar notificación: {{message}}"
|
||||||
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
|
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
|
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
|
||||||
AdvSceneSwitcher.action.screenshot.entry="Captura de pantalla {{sources}}"
|
|
||||||
AdvSceneSwitcher.action.profile="Perfil"
|
AdvSceneSwitcher.action.profile="Perfil"
|
||||||
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
|
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
|
||||||
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
|
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ AdvSceneSwitcher.condition.window="Pencere"
|
|||||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} Varolan ve ..."
|
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.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="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.line2="{{matchText}}"
|
||||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||||
AdvSceneSwitcher.condition.media="Medya"
|
AdvSceneSwitcher.condition.media="Medya"
|
||||||
@@ -383,7 +383,6 @@ AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
|
|||||||
AdvSceneSwitcher.action.systray.entry="Bildirimleri göster: {{message}}"
|
AdvSceneSwitcher.action.systray.entry="Bildirimleri göster: {{message}}"
|
||||||
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
|
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
|
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
|
||||||
AdvSceneSwitcher.action.screenshot.entry="Ekran görüntüsü {{sources}}"
|
|
||||||
AdvSceneSwitcher.action.profile="Profil"
|
AdvSceneSwitcher.action.profile="Profil"
|
||||||
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
|
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
|
||||||
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
|
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ AdvSceneSwitcher.condition.window="窗口"
|
|||||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} 存在..."
|
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} 存在..."
|
||||||
AdvSceneSwitcher.condition.window.entry.line2="... 并且是 {{fullscreen}} 全屏 {{maximized}} 最大化 {{focused}} 获得焦点 {{windowFocusChanged}} 焦点窗口已更改"
|
AdvSceneSwitcher.condition.window.entry.line2="... 并且是 {{fullscreen}} 全屏 {{maximized}} 最大化 {{focused}} 获得焦点 {{windowFocusChanged}} 焦点窗口已更改"
|
||||||
AdvSceneSwitcher.condition.file="文件"
|
AdvSceneSwitcher.condition.file="文件"
|
||||||
AdvSceneSwitcher.condition.file.entry.line1="内容 {{fileType}} {{filePath}} (本地是文本,网络是获取的网页)完全匹配:"
|
|
||||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||||
AdvSceneSwitcher.condition.media="媒体"
|
AdvSceneSwitcher.condition.media="媒体"
|
||||||
@@ -424,7 +423,6 @@ AdvSceneSwitcher.action.systray="系统托盘通知"
|
|||||||
AdvSceneSwitcher.action.systray.entry="显示通知: {{message}}"
|
AdvSceneSwitcher.action.systray.entry="显示通知: {{message}}"
|
||||||
AdvSceneSwitcher.action.screenshot="截图"
|
AdvSceneSwitcher.action.screenshot="截图"
|
||||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS 预览画面"
|
AdvSceneSwitcher.action.screenshot.mainOutput="OBS 预览画面"
|
||||||
AdvSceneSwitcher.action.screenshot.entry="截图 {{sources}}"
|
|
||||||
AdvSceneSwitcher.action.profile="配置文件"
|
AdvSceneSwitcher.action.profile="配置文件"
|
||||||
AdvSceneSwitcher.action.profile.entry="将活动配置文件切换到 {{profiles}}"
|
AdvSceneSwitcher.action.profile.entry="将活动配置文件切换到 {{profiles}}"
|
||||||
AdvSceneSwitcher.action.sceneCollection="场景集合"
|
AdvSceneSwitcher.action.sceneCollection="场景集合"
|
||||||
|
|||||||
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
|
||||||
@@ -642,7 +642,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
@@ -658,7 +658,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
@@ -713,17 +713,14 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="themeID" stdset="0">
|
<property name="themeID" stdset="0">
|
||||||
<string>upArrowIconSmall</string>
|
<string notr="true">upArrowIconSmall</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -732,17 +729,14 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="themeID" stdset="0">
|
<property name="themeID" stdset="0">
|
||||||
<string>downArrowIconSmall</string>
|
<string notr="true">downArrowIconSmall</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -819,7 +813,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -864,7 +858,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
@@ -880,7 +874,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
@@ -935,7 +929,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -954,7 +948,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
@@ -1006,7 +1000,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
@@ -1022,7 +1016,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
@@ -1077,7 +1071,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
@@ -1093,7 +1087,7 @@
|
|||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>22</width>
|
<width>22</width>
|
||||||
<height>22</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="flat">
|
<property name="flat">
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ void SwitcherData::Thread()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
websocketMessages.clear();
|
ClearWebsocketMessages();
|
||||||
|
|
||||||
// After this point we will call frontend functions like
|
// After this point we will call frontend functions like
|
||||||
// obs_frontend_set_current_scene() and
|
// obs_frontend_set_current_scene() and
|
||||||
@@ -629,9 +629,11 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
|||||||
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
|
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
|
||||||
resetLiveTime();
|
resetLiveTime();
|
||||||
break;
|
break;
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
|
||||||
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
|
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
|
||||||
setReplayBufferSaved();
|
setReplayBufferSaved();
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
||||||
setTranstionEnd();
|
setTranstionEnd();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ signals:
|
|||||||
void ConnectionAdded(const QString &);
|
void ConnectionAdded(const QString &);
|
||||||
void ConnectionRenamed(const QString &oldName, const QString &newName);
|
void ConnectionRenamed(const QString &oldName, const QString &newName);
|
||||||
void ConnectionRemoved(const QString &);
|
void ConnectionRemoved(const QString &);
|
||||||
|
void VariableAdded(const QString &);
|
||||||
|
void VariableRenamed(const QString &oldName, const QString &newName);
|
||||||
|
void VariableRemoved(const QString &);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void on_windowUp_clicked();
|
void on_windowUp_clicked();
|
||||||
|
|||||||
@@ -192,7 +192,9 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
|||||||
|
|
||||||
bool isLegacyTab(const QString &name)
|
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(
|
name == obs_module_text(
|
||||||
"AdvSceneSwitcher.transitionTab.title") ||
|
"AdvSceneSwitcher.transitionTab.title") ||
|
||||||
name == obs_module_text(
|
name == obs_module_text(
|
||||||
@@ -221,13 +223,13 @@ void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
|
|||||||
|
|
||||||
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
|
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
|
||||||
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
|
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
|
||||||
#if defined(_WIN32) || defined(__APPLE__)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
ui->tabWidget->setTabVisible(idx, !state);
|
|
||||||
#else
|
|
||||||
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
|
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
|
||||||
ui->tabWidget->setTabEnabled(idx, !state);
|
ui->tabWidget->setTabEnabled(idx, !state);
|
||||||
ui->tabWidget->setStyleSheet(
|
ui->tabWidget->setStyleSheet(
|
||||||
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
|
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
|
||||||
|
#else
|
||||||
|
ui->tabWidget->setTabVisible(idx, !state);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,6 +505,7 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
|||||||
// Needs to be loaded before any entries which might rely on scene group
|
// Needs to be loaded before any entries which might rely on scene group
|
||||||
// selections to be available.
|
// selections to be available.
|
||||||
loadSceneGroups(obj);
|
loadSceneGroups(obj);
|
||||||
|
loadVariables(obj);
|
||||||
loadMacros(obj);
|
loadMacros(obj);
|
||||||
loadConnections(obj);
|
loadConnections(obj);
|
||||||
loadWindowTitleSwitches(obj);
|
loadWindowTitleSwitches(obj);
|
||||||
@@ -537,6 +540,7 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
|||||||
saveSceneGroups(obj);
|
saveSceneGroups(obj);
|
||||||
saveMacros(obj);
|
saveMacros(obj);
|
||||||
saveConnections(obj);
|
saveConnections(obj);
|
||||||
|
saveVariables(obj);
|
||||||
saveWindowTitleSwitches(obj);
|
saveWindowTitleSwitches(obj);
|
||||||
saveScreenRegionSwitches(obj);
|
saveScreenRegionSwitches(obj);
|
||||||
savePauseSwitches(obj);
|
savePauseSwitches(obj);
|
||||||
@@ -687,6 +691,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)
|
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());
|
obs_data_array_t *array = obs_data_get_array(obj, name.c_str());
|
||||||
size_t count = obs_data_array_count(array);
|
size_t count = obs_data_array_count(array);
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
|||||||
@@ -185,18 +185,22 @@ void frontEndActionThread(sceneTriggerAction action, double delay)
|
|||||||
case sceneTriggerAction::STOP_STREAMING:
|
case sceneTriggerAction::STOP_STREAMING:
|
||||||
obs_frontend_streaming_stop();
|
obs_frontend_streaming_stop();
|
||||||
break;
|
break;
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
|
||||||
case sceneTriggerAction::START_REPLAY_BUFFER:
|
case sceneTriggerAction::START_REPLAY_BUFFER:
|
||||||
obs_frontend_replay_buffer_start();
|
obs_frontend_replay_buffer_start();
|
||||||
break;
|
break;
|
||||||
case sceneTriggerAction::STOP_REPLAY_BUFFER:
|
case sceneTriggerAction::STOP_REPLAY_BUFFER:
|
||||||
obs_frontend_replay_buffer_stop();
|
obs_frontend_replay_buffer_stop();
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||||
case sceneTriggerAction::START_VCAM:
|
case sceneTriggerAction::START_VCAM:
|
||||||
obs_frontend_start_virtualcam();
|
obs_frontend_start_virtualcam();
|
||||||
break;
|
break;
|
||||||
case sceneTriggerAction::STOP_VCAM:
|
case sceneTriggerAction::STOP_VCAM:
|
||||||
obs_frontend_stop_virtualcam();
|
obs_frontend_stop_virtualcam();
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
blog(LOG_WARNING, "ignoring unexpected frontend action '%d'",
|
blog(LOG_WARNING, "ignoring unexpected frontend action '%d'",
|
||||||
static_cast<int>(action));
|
static_cast<int>(action));
|
||||||
|
|||||||
@@ -43,9 +43,11 @@ bool MacroActionRecord::PerformAction()
|
|||||||
obs_frontend_recording_pause(false);
|
obs_frontend_recording_pause(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(28, 0, 0)
|
||||||
case RecordAction::SPLIT:
|
case RecordAction::SPLIT:
|
||||||
obs_frontend_recording_split_file();
|
obs_frontend_recording_split_file();
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,22 +75,22 @@ MacroActionRunEdit::MacroActionRunEdit(
|
|||||||
_filePath = new FileSelection();
|
_filePath = new FileSelection();
|
||||||
_argList = new QListWidget();
|
_argList = new QListWidget();
|
||||||
_addArg = new QPushButton();
|
_addArg = new QPushButton();
|
||||||
_addArg->setMaximumSize(QSize(22, 22));
|
_addArg->setMaximumWidth(22);
|
||||||
_addArg->setProperty("themeID",
|
_addArg->setProperty("themeID",
|
||||||
QVariant(QString::fromUtf8("addIconSmall")));
|
QVariant(QString::fromUtf8("addIconSmall")));
|
||||||
_addArg->setFlat(true);
|
_addArg->setFlat(true);
|
||||||
_removeArg = new QPushButton();
|
_removeArg = new QPushButton();
|
||||||
_removeArg->setMaximumSize(QSize(22, 22));
|
_removeArg->setMaximumWidth(22);
|
||||||
_removeArg->setProperty("themeID",
|
_removeArg->setProperty("themeID",
|
||||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||||
_removeArg->setFlat(true);
|
_removeArg->setFlat(true);
|
||||||
_argUp = new QPushButton();
|
_argUp = new QPushButton();
|
||||||
_argUp->setMaximumSize(QSize(22, 22));
|
_argUp->setMaximumWidth(22);
|
||||||
_argUp->setProperty("themeID",
|
_argUp->setProperty("themeID",
|
||||||
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
||||||
_argUp->setFlat(true);
|
_argUp->setFlat(true);
|
||||||
_argDown = new QPushButton();
|
_argDown = new QPushButton();
|
||||||
_argDown->setMaximumSize(QSize(22, 22));
|
_argDown->setMaximumWidth(22);
|
||||||
_argDown->setProperty(
|
_argDown->setProperty(
|
||||||
"themeID", QVariant(QString::fromUtf8("downArrowIconSmall")));
|
"themeID", QVariant(QString::fromUtf8("downArrowIconSmall")));
|
||||||
_argDown->setFlat(true);
|
_argDown->setFlat(true);
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
|
|||||||
QWidget *parent, std::shared_ptr<MacroActionSceneOrder> entryData)
|
QWidget *parent, std::shared_ptr<MacroActionSceneOrder> entryData)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
|
||||||
_sources = new SceneItemSelectionWidget(parent);
|
_sources = new SceneItemSelectionWidget(parent);
|
||||||
_actions = new QComboBox();
|
_actions = new QComboBox();
|
||||||
_position = new QSpinBox();
|
_position = new QSpinBox();
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ int getTransitionOverrideDuration(OBSWeakSource &scene)
|
|||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isUsingFixedLengthTransition(OBSWeakSource &transition)
|
bool isUsingFixedLengthTransition(const OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
obs_source_t *source = obs_weak_source_get_source(transition);
|
obs_source_t *source = obs_weak_source_get_source(transition);
|
||||||
bool ret = obs_transition_fixed(source);
|
bool ret = obs_transition_fixed(source);
|
||||||
@@ -123,15 +123,15 @@ void MacroActionSwitchScene::LogAction()
|
|||||||
auto t = _scene.GetType();
|
auto t = _scene.GetType();
|
||||||
auto sceneName = GetWeakSourceName(_scene.GetScene(false));
|
auto sceneName = GetWeakSourceName(_scene.GetScene(false));
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case SceneSelectionType::SCENE:
|
case SceneSelection::Type::SCENE:
|
||||||
vblog(LOG_INFO, "switch to scene '%s'",
|
vblog(LOG_INFO, "switch to scene '%s'",
|
||||||
_scene.ToString().c_str());
|
_scene.ToString().c_str());
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::GROUP:
|
case SceneSelection::Type::GROUP:
|
||||||
vblog(LOG_INFO, "switch to scene '%s' (scene group '%s')",
|
vblog(LOG_INFO, "switch to scene '%s' (scene group '%s')",
|
||||||
sceneName.c_str(), _scene.ToString().c_str());
|
sceneName.c_str(), _scene.ToString().c_str());
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::PREVIOUS:
|
case SceneSelection::Type::PREVIOUS:
|
||||||
vblog(LOG_INFO, "switch to previous scene '%s'",
|
vblog(LOG_INFO, "switch to previous scene '%s'",
|
||||||
sceneName.c_str());
|
sceneName.c_str());
|
||||||
break;
|
break;
|
||||||
@@ -153,30 +153,6 @@ bool MacroActionSwitchScene::Save(obs_data_t *obj)
|
|||||||
|
|
||||||
bool MacroActionSwitchScene::Load(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);
|
MacroAction::Load(obj);
|
||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_transition.Load(obj);
|
_transition.Load(obj);
|
||||||
@@ -193,14 +169,14 @@ std::string MacroActionSwitchScene::GetShortDesc()
|
|||||||
|
|
||||||
MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroActionSwitchScene> entryData)
|
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("-");
|
_duration->SpinBox()->setSpecialValueText("-");
|
||||||
|
|
||||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||||
@@ -214,7 +190,6 @@ MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
|||||||
QWidget::connect(_blockUntilTransitionDone, SIGNAL(stateChanged(int)),
|
QWidget::connect(_blockUntilTransitionDone, SIGNAL(stateChanged(int)),
|
||||||
this, SLOT(BlockUntilTransitionDoneChanged(int)));
|
this, SLOT(BlockUntilTransitionDoneChanged(int)));
|
||||||
|
|
||||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{scenes}}", _scenes},
|
{"{{scenes}}", _scenes},
|
||||||
{"{{transitions}}", _transitions},
|
{"{{transitions}}", _transitions},
|
||||||
@@ -222,10 +197,10 @@ MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
|||||||
{"{{blockUntilTransitionDone}}", _blockUntilTransitionDone},
|
{"{{blockUntilTransitionDone}}", _blockUntilTransitionDone},
|
||||||
};
|
};
|
||||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"),
|
placeWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"),
|
||||||
entryLayout, widgetPlaceholders);
|
_entryLayout, widgetPlaceholders);
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
mainLayout->addLayout(entryLayout);
|
mainLayout->addLayout(_entryLayout);
|
||||||
mainLayout->addWidget(_blockUntilTransitionDone);
|
mainLayout->addWidget(_blockUntilTransitionDone);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
@@ -235,6 +210,7 @@ MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
|||||||
_duration->SetDuration(_entryData->_duration);
|
_duration->SetDuration(_entryData->_duration);
|
||||||
_blockUntilTransitionDone->setChecked(
|
_blockUntilTransitionDone->setChecked(
|
||||||
_entryData->_blockUntilTransitionDone);
|
_entryData->_blockUntilTransitionDone);
|
||||||
|
SetDurationVisibility();
|
||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +234,37 @@ void MacroActionSwitchSceneEdit::BlockUntilTransitionDoneChanged(int state)
|
|||||||
_entryData->_blockUntilTransitionDone = 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)
|
void MacroActionSwitchSceneEdit::SceneChanged(const SceneSelection &s)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -278,4 +285,5 @@ void MacroActionSwitchSceneEdit::TransitionChanged(const TransitionSelection &t)
|
|||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
_entryData->_transition = t;
|
_entryData->_transition = t;
|
||||||
|
SetDurationVisibility();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,9 +61,11 @@ protected:
|
|||||||
TransitionSelectionWidget *_transitions;
|
TransitionSelectionWidget *_transitions;
|
||||||
DurationSelection *_duration;
|
DurationSelection *_duration;
|
||||||
QCheckBox *_blockUntilTransitionDone;
|
QCheckBox *_blockUntilTransitionDone;
|
||||||
|
QHBoxLayout *_entryLayout;
|
||||||
|
|
||||||
std::shared_ptr<MacroActionSwitchScene> _entryData;
|
std::shared_ptr<MacroActionSwitchScene> _entryData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetDurationVisibility();
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
|
|||||||
QWidget *parent, std::shared_ptr<MacroActionSceneTransform> entryData)
|
QWidget *parent, std::shared_ptr<MacroActionSceneTransform> entryData)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
|
||||||
_sources = new SceneItemSelectionWidget(parent);
|
_sources = new SceneItemSelectionWidget(parent);
|
||||||
_getSettings = new QPushButton(obs_module_text(
|
_getSettings = new QPushButton(obs_module_text(
|
||||||
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
|
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
|||||||
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
|
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
_scenes = new SceneSelectionWidget(window(), false, true, true);
|
_scenes = new SceneSelectionWidget(window(), true, false, true, true);
|
||||||
_sourceTypes = new QComboBox();
|
_sourceTypes = new QComboBox();
|
||||||
_sources = new SceneItemSelectionWidget(parent);
|
_sources = new SceneItemSelectionWidget(parent);
|
||||||
_sourceGroups = new QComboBox();
|
_sourceGroups = new QComboBox();
|
||||||
|
|||||||
@@ -9,73 +9,155 @@ bool MacroActionScreenshot::_registered = MacroActionFactory::Register(
|
|||||||
{MacroActionScreenshot::Create, MacroActionScreenshotEdit::Create,
|
{MacroActionScreenshot::Create, MacroActionScreenshotEdit::Create,
|
||||||
"AdvSceneSwitcher.action.screenshot"});
|
"AdvSceneSwitcher.action.screenshot"});
|
||||||
|
|
||||||
bool MacroActionScreenshot::PerformAction()
|
void MacroActionScreenshot::FrontendScreenshot(OBSWeakSource &source)
|
||||||
{
|
{
|
||||||
if (_source) {
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
|
||||||
auto s = obs_weak_source_get_source(_source);
|
if (source) {
|
||||||
|
auto s = obs_weak_source_get_source(source);
|
||||||
obs_frontend_take_source_screenshot(s);
|
obs_frontend_take_source_screenshot(s);
|
||||||
obs_source_release(s);
|
obs_source_release(s);
|
||||||
} else {
|
} else {
|
||||||
obs_frontend_take_screenshot();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionScreenshot::LogAction()
|
void MacroActionScreenshot::LogAction()
|
||||||
{
|
{
|
||||||
vblog(LOG_INFO, "trigger screenshot for source \"%s\"",
|
vblog(LOG_INFO, "trigger screenshot for \"%s\"",
|
||||||
GetWeakSourceName(_source).c_str());
|
_targetType == TargetType::SOURCE
|
||||||
|
? GetWeakSourceName(_source).c_str()
|
||||||
|
: _scene.ToString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroActionScreenshot::Save(obs_data_t *obj)
|
bool MacroActionScreenshot::Save(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroAction::Save(obj);
|
MacroAction::Save(obj);
|
||||||
|
_scene.Save(obj);
|
||||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroActionScreenshot::Load(obs_data_t *obj)
|
bool MacroActionScreenshot::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroAction::Load(obj);
|
MacroAction::Load(obj);
|
||||||
|
_scene.Load(obj);
|
||||||
const char *sourceName = obs_data_get_string(obj, "source");
|
const char *sourceName = obs_data_get_string(obj, "source");
|
||||||
_source = GetWeakSourceByName(sourceName);
|
_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MacroActionScreenshot::GetShortDesc()
|
std::string MacroActionScreenshot::GetShortDesc()
|
||||||
{
|
{
|
||||||
if (_source) {
|
if (_targetType == TargetType::SCENE) {
|
||||||
|
return _scene.ToString();
|
||||||
|
} else {
|
||||||
return GetWeakSourceName(_source);
|
return GetWeakSourceName(_source);
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void addOBSMainOutputEntry(QComboBox *cb)
|
static void populateSaveTypeSelection(QComboBox *list)
|
||||||
{
|
{
|
||||||
cb->insertItem(
|
list->addItem(obs_module_text(
|
||||||
0, obs_module_text(
|
"AdvSceneSwitcher.action.screenshot.save.default"));
|
||||||
"AdvSceneSwitcher.action.screenshot.mainOutput"));
|
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(
|
MacroActionScreenshotEdit::MacroActionScreenshotEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroActionScreenshot> entryData)
|
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();
|
setToolTip(obs_module_text(
|
||||||
populateVideoSelection(_sources, true, false);
|
"AdvSceneSwitcher.action.screenshot.blackscreenNote"));
|
||||||
addOBSMainOutputEntry(_sources);
|
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 &)),
|
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||||
this, SLOT(SourceChanged(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 = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{sources}}", _sources},
|
{"{{sources}}", _sources},
|
||||||
|
{"{{scenes}}", _scenes},
|
||||||
|
{"{{saveType}}", _saveType},
|
||||||
|
{"{{targetType}}", _targetType},
|
||||||
};
|
};
|
||||||
placeWidgets(
|
placeWidgets(
|
||||||
obs_module_text("AdvSceneSwitcher.action.screenshot.entry"),
|
obs_module_text("AdvSceneSwitcher.action.screenshot.entry"),
|
||||||
mainLayout, widgetPlaceholders);
|
layout, widgetPlaceholders);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
mainLayout->addLayout(layout);
|
||||||
|
mainLayout->addWidget(_savePath);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
@@ -93,8 +175,55 @@ void MacroActionScreenshotEdit::UpdateEntryData()
|
|||||||
_sources->setCurrentText(
|
_sources->setCurrentText(
|
||||||
GetWeakSourceName(_entryData->_source).c_str());
|
GetWeakSourceName(_entryData->_source).c_str());
|
||||||
} else {
|
} 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)
|
void MacroActionScreenshotEdit::SourceChanged(const QString &text)
|
||||||
@@ -108,3 +237,18 @@ void MacroActionScreenshotEdit::SourceChanged(const QString &text)
|
|||||||
emit HeaderInfoChanged(
|
emit HeaderInfoChanged(
|
||||||
QString::fromStdString(_entryData->GetShortDesc()));
|
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
|
#pragma once
|
||||||
#include "macro-action-edit.hpp"
|
#include "macro-action-edit.hpp"
|
||||||
|
#include "file-selection.hpp"
|
||||||
|
#include "scene-selection.hpp"
|
||||||
|
#include "screenshot-helper.hpp"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
|
||||||
@@ -16,9 +19,25 @@ public:
|
|||||||
{
|
{
|
||||||
return std::make_shared<MacroActionScreenshot>(m);
|
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;
|
OBSWeakSource _source;
|
||||||
|
std::string _path = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void FrontendScreenshot(OBSWeakSource &);
|
||||||
|
void CustomScreenshot(OBSWeakSource &);
|
||||||
|
|
||||||
|
ScreenshotHelper _screenshot;
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
static const std::string id;
|
static const std::string id;
|
||||||
};
|
};
|
||||||
@@ -40,14 +59,24 @@ public:
|
|||||||
action));
|
action));
|
||||||
}
|
}
|
||||||
private slots:
|
private slots:
|
||||||
|
void SceneChanged(const SceneSelection &);
|
||||||
void SourceChanged(const QString &text);
|
void SourceChanged(const QString &text);
|
||||||
|
void SaveTypeChanged(int index);
|
||||||
|
void TargetTypeChanged(int index);
|
||||||
|
void PathChanged(const QString &text);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
SceneSelectionWidget *_scenes;
|
||||||
QComboBox *_sources;
|
QComboBox *_sources;
|
||||||
|
QComboBox *_saveType;
|
||||||
|
QComboBox *_targetType;
|
||||||
|
FileSelection *_savePath;
|
||||||
std::shared_ptr<MacroActionScreenshot> _entryData;
|
std::shared_ptr<MacroActionScreenshot> _entryData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ MacroActionSudioModeEdit::MacroActionSudioModeEdit(
|
|||||||
QWidget *parent, std::shared_ptr<MacroActionSudioMode> entryData)
|
QWidget *parent, std::shared_ptr<MacroActionSudioMode> entryData)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
_actions(new QComboBox()),
|
_actions(new QComboBox()),
|
||||||
_scenes(new SceneSelectionWidget(window(), true, true, true))
|
_scenes(new SceneSelectionWidget(window(), true, true, true, true))
|
||||||
{
|
{
|
||||||
populateActionSelection(_actions);
|
populateActionSelection(_actions);
|
||||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
|||||||
@@ -49,8 +49,32 @@ void MacroActionTransition::SetTransitionOverride()
|
|||||||
obs_source_release(scene);
|
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)
|
void MacroActionTransition::SetSourceTransition(bool show)
|
||||||
{
|
{
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||||
auto transition =
|
auto transition =
|
||||||
obs_weak_source_get_source(_transition.GetTransition());
|
obs_weak_source_get_source(_transition.GetTransition());
|
||||||
obs_data_t *settings = obs_source_get_settings(transition);
|
obs_data_t *settings = obs_source_get_settings(transition);
|
||||||
@@ -73,6 +97,7 @@ void MacroActionTransition::SetSourceTransition(bool show)
|
|||||||
}
|
}
|
||||||
|
|
||||||
obs_source_release(t);
|
obs_source_release(t);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroActionTransition::PerformAction()
|
bool MacroActionTransition::PerformAction()
|
||||||
@@ -182,7 +207,7 @@ MacroActionTransitionEdit::MacroActionTransitionEdit(
|
|||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
_actions(new QComboBox),
|
_actions(new QComboBox),
|
||||||
_sources(new SceneItemSelectionWidget(parent, false)),
|
_sources(new SceneItemSelectionWidget(parent, false)),
|
||||||
_scenes(new SceneSelectionWidget(this, false, false, true)),
|
_scenes(new SceneSelectionWidget(this, true, false, false, true)),
|
||||||
_setTransition(new QCheckBox),
|
_setTransition(new QCheckBox),
|
||||||
_setDuration(new QCheckBox),
|
_setDuration(new QCheckBox),
|
||||||
_transitions(new TransitionSelectionWidget(this, false)),
|
_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()
|
bool MacroActionVCam::PerformAction()
|
||||||
{
|
{
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||||
switch (_action) {
|
switch (_action) {
|
||||||
case VCamAction::STOP:
|
case VCamAction::STOP:
|
||||||
if (obs_frontend_virtualcam_active()) {
|
if (obs_frontend_virtualcam_active()) {
|
||||||
@@ -30,6 +31,7 @@ bool MacroActionVCam::PerformAction()
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,25 +9,58 @@ bool MacroActionWebsocket::_registered = MacroActionFactory::Register(
|
|||||||
{MacroActionWebsocket::Create, MacroActionWebsocketEdit::Create,
|
{MacroActionWebsocket::Create, MacroActionWebsocketEdit::Create,
|
||||||
"AdvSceneSwitcher.action.websocket"});
|
"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);
|
auto connection = GetConnectionByName(_connection);
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
connection->SendMsg(_message);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionWebsocket::LogAction()
|
void MacroActionWebsocket::LogAction()
|
||||||
{
|
{
|
||||||
vblog(LOG_INFO, "sent msg \"%s\" via \"%s\"", _message.c_str(),
|
switch (_type) {
|
||||||
_connection.c_str());
|
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)
|
bool MacroActionWebsocket::Save(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroAction::Save(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, "message", _message.c_str());
|
||||||
obs_data_set_string(obj, "connection", _connection.c_str());
|
obs_data_set_string(obj, "connection", _connection.c_str());
|
||||||
return true;
|
return true;
|
||||||
@@ -36,6 +69,7 @@ bool MacroActionWebsocket::Save(obs_data_t *obj)
|
|||||||
bool MacroActionWebsocket::Load(obs_data_t *obj)
|
bool MacroActionWebsocket::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroAction::Load(obj);
|
MacroAction::Load(obj);
|
||||||
|
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||||
_message = obs_data_get_string(obj, "message");
|
_message = obs_data_get_string(obj, "message");
|
||||||
_connection = obs_data_get_string(obj, "connection");
|
_connection = obs_data_get_string(obj, "connection");
|
||||||
return true;
|
return true;
|
||||||
@@ -43,30 +77,39 @@ bool MacroActionWebsocket::Load(obs_data_t *obj)
|
|||||||
|
|
||||||
std::string MacroActionWebsocket::GetShortDesc()
|
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(
|
MacroActionWebsocketEdit::MacroActionWebsocketEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroActionWebsocket> entryData)
|
QWidget *parent, std::shared_ptr<MacroActionWebsocket> entryData)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
|
_actions(new QComboBox(this)),
|
||||||
_message(new ResizingPlainTextEdit(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,
|
QWidget::connect(_message, SIGNAL(textChanged()), this,
|
||||||
SLOT(MessageChanged()));
|
SLOT(MessageChanged()));
|
||||||
QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)),
|
QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)),
|
||||||
this,
|
this,
|
||||||
SLOT(ConnectionSelectionChanged(const QString &)));
|
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;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
mainLayout->addLayout(connectionLayout);
|
mainLayout->addLayout(_editLayout);
|
||||||
mainLayout->addWidget(_message);
|
mainLayout->addWidget(_message);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
@@ -75,19 +118,73 @@ MacroActionWebsocketEdit::MacroActionWebsocketEdit(
|
|||||||
_loading = false;
|
_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()
|
void MacroActionWebsocketEdit::UpdateEntryData()
|
||||||
{
|
{
|
||||||
if (!_entryData) {
|
if (!_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||||
_message->setPlainText(QString::fromStdString(_entryData->_message));
|
_message->setPlainText(QString::fromStdString(_entryData->_message));
|
||||||
_connection->SetConnection(_entryData->_connection);
|
_connection->SetConnection(_entryData->_connection);
|
||||||
|
|
||||||
|
if (_entryData->_type == MacroActionWebsocket::Type::REQUEST) {
|
||||||
|
SetupRequestEdit();
|
||||||
|
} else {
|
||||||
|
SetupEventEdit();
|
||||||
|
}
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
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()
|
void MacroActionWebsocketEdit::MessageChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
|
|||||||
@@ -22,10 +22,18 @@ public:
|
|||||||
return std::make_shared<MacroActionWebsocket>(m);
|
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 _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||||
std::string _connection;
|
std::string _connection;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SendRequest();
|
||||||
|
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
static const std::string id;
|
static const std::string id;
|
||||||
};
|
};
|
||||||
@@ -47,6 +55,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void ActionChanged(int);
|
||||||
void MessageChanged();
|
void MessageChanged();
|
||||||
void ConnectionSelectionChanged(const QString &);
|
void ConnectionSelectionChanged(const QString &);
|
||||||
signals:
|
signals:
|
||||||
@@ -56,7 +65,12 @@ protected:
|
|||||||
std::shared_ptr<MacroActionWebsocket> _entryData;
|
std::shared_ptr<MacroActionWebsocket> _entryData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetupRequestEdit();
|
||||||
|
void SetupEventEdit();
|
||||||
|
|
||||||
|
QComboBox *_actions;
|
||||||
ResizingPlainTextEdit *_message;
|
ResizingPlainTextEdit *_message;
|
||||||
ConnectionSelection *_connection;
|
ConnectionSelection *_connection;
|
||||||
|
QHBoxLayout *_editLayout;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ DurationModifierEdit::DurationModifierEdit(QWidget *parent)
|
|||||||
_condition = new QComboBox(parent);
|
_condition = new QComboBox(parent);
|
||||||
_duration = new DurationSelection(parent);
|
_duration = new DurationSelection(parent);
|
||||||
_toggle = new QPushButton(parent);
|
_toggle = new QPushButton(parent);
|
||||||
_toggle->setMaximumSize(22, 22);
|
_toggle->setMaximumWidth(22);
|
||||||
_toggle->setIcon(
|
_toggle->setIcon(
|
||||||
QIcon(QString::fromStdString(getDataFilePath("res/time.svg"))));
|
QIcon(QString::fromStdString(getDataFilePath("res/time.svg"))));
|
||||||
populateDurationModifierTypes(_condition);
|
populateDurationModifierTypes(_condition);
|
||||||
@@ -447,6 +447,9 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
|||||||
if (idx == 0 && macro->Conditions().size() > 0) {
|
if (idx == 0 && macro->Conditions().size() > 0) {
|
||||||
auto newRoot = macro->Conditions().at(0);
|
auto newRoot = macro->Conditions().at(0);
|
||||||
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
||||||
|
static_cast<MacroConditionEdit *>(
|
||||||
|
conditionsList->WidgetAt(0))
|
||||||
|
->SetRootNode(true);
|
||||||
}
|
}
|
||||||
SetConditionData(*macro);
|
SetConditionData(*macro);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ static std::string getRemoteData(std::string &url)
|
|||||||
return readBuffer;
|
return readBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionFile::matchFileContent(QString &filedata)
|
bool MacroConditionFile::MatchFileContent(QString &filedata)
|
||||||
{
|
{
|
||||||
if (_onlyMatchIfChanged) {
|
if (_onlyMatchIfChanged) {
|
||||||
size_t newHash = strHash(filedata.toUtf8().constData());
|
size_t newHash = strHash(filedata.toUtf8().constData());
|
||||||
@@ -50,31 +50,30 @@ bool MacroConditionFile::matchFileContent(QString &filedata)
|
|||||||
_lastHash = newHash;
|
_lastHash = newHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_useRegex) {
|
if (_regex.Enabled()) {
|
||||||
try {
|
auto expr = _regex.GetRegularExpression(_text);
|
||||||
std::regex expr(_text);
|
if (!expr.isValid()) {
|
||||||
return std::regex_match(filedata.toStdString(), expr);
|
|
||||||
} catch (const std::regex_error &) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
auto match = expr.match(filedata);
|
||||||
|
return match.hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString text = QString::fromStdString(_text);
|
QString text = QString::fromStdString(_text);
|
||||||
return compareIgnoringLineEnding(text, filedata);
|
return compareIgnoringLineEnding(text, filedata);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionFile::checkRemoteFileContent()
|
bool MacroConditionFile::CheckRemoteFileContent()
|
||||||
{
|
{
|
||||||
std::string data = getRemoteData(_file);
|
std::string data = getRemoteData(_file);
|
||||||
QString qdata = QString::fromStdString(data);
|
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));
|
QFile file(QString::fromStdString(_file));
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,28 +86,78 @@ bool MacroConditionFile::checkLocalFileContent()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString filedata = QTextStream(&file).readAll();
|
QString filedata = QTextStream(&file).readAll();
|
||||||
bool match = matchFileContent(filedata);
|
bool match = MatchFileContent(filedata);
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
return match;
|
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) {
|
if (_fileType == FileType::REMOTE) {
|
||||||
return checkRemoteFileContent();
|
return false;
|
||||||
} else {
|
|
||||||
return checkLocalFileContent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
bool MacroConditionFile::Save(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroCondition::Save(obj);
|
MacroCondition::Save(obj);
|
||||||
|
_regex.Save(obj);
|
||||||
obs_data_set_string(obj, "file", _file.c_str());
|
obs_data_set_string(obj, "file", _file.c_str());
|
||||||
obs_data_set_string(obj, "text", _text.c_str());
|
obs_data_set_string(obj, "text", _text.c_str());
|
||||||
obs_data_set_int(obj, "fileType", static_cast<int>(_fileType));
|
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, "useTime", _useTime);
|
||||||
obs_data_set_bool(obj, "onlyMatchIfChanged", _onlyMatchIfChanged);
|
obs_data_set_bool(obj, "onlyMatchIfChanged", _onlyMatchIfChanged);
|
||||||
return true;
|
return true;
|
||||||
@@ -117,10 +166,17 @@ bool MacroConditionFile::Save(obs_data_t *obj)
|
|||||||
bool MacroConditionFile::Load(obs_data_t *obj)
|
bool MacroConditionFile::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroCondition::Load(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");
|
_file = obs_data_get_string(obj, "file");
|
||||||
_text = obs_data_get_string(obj, "text");
|
_text = obs_data_get_string(obj, "text");
|
||||||
_fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType"));
|
_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");
|
_useTime = obs_data_get_bool(obj, "useTime");
|
||||||
_onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
_onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
||||||
return true;
|
return true;
|
||||||
@@ -131,41 +187,59 @@ std::string MacroConditionFile::GetShortDesc()
|
|||||||
return _file;
|
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(
|
MacroConditionFileEdit::MacroConditionFileEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroConditionFile> entryData)
|
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();
|
populateFileTypes(_fileTypes);
|
||||||
_filePath = new FileSelection();
|
populateConditions(_conditions);
|
||||||
_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"));
|
|
||||||
|
|
||||||
QWidget::connect(_fileType, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_fileTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(FileTypeChanged(int)));
|
SLOT(FileTypeChanged(int)));
|
||||||
|
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(ConditionChanged(int)));
|
||||||
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
|
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
|
||||||
SLOT(PathChanged(const QString &)));
|
SLOT(PathChanged(const QString &)));
|
||||||
QWidget::connect(_matchText, SIGNAL(textChanged()), this,
|
QWidget::connect(_matchText, SIGNAL(textChanged()), this,
|
||||||
SLOT(MatchTextChanged()));
|
SLOT(MatchTextChanged()));
|
||||||
QWidget::connect(_useRegex, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||||
SLOT(UseRegexChanged(int)));
|
SLOT(RegexChanged(RegexConfig)));
|
||||||
QWidget::connect(_checkModificationDate, SIGNAL(stateChanged(int)),
|
QWidget::connect(_checkModificationDate, SIGNAL(stateChanged(int)),
|
||||||
this, SLOT(CheckModificationDateChanged(int)));
|
this, SLOT(CheckModificationDateChanged(int)));
|
||||||
QWidget::connect(_checkFileContent, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_checkFileContent, SIGNAL(stateChanged(int)), this,
|
||||||
SLOT(OnlyMatchIfChangedChanged(int)));
|
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 = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{fileType}}", _fileType},
|
{"{{fileType}}", _fileTypes},
|
||||||
|
{"{{conditions}}", _conditions},
|
||||||
{"{{filePath}}", _filePath},
|
{"{{filePath}}", _filePath},
|
||||||
{"{{matchText}}", _matchText},
|
{"{{matchText}}", _matchText},
|
||||||
{"{{useRegex}}", _useRegex},
|
{"{{useRegex}}", _regex},
|
||||||
{"{{checkModificationDate}}", _checkModificationDate},
|
{"{{checkModificationDate}}", _checkModificationDate},
|
||||||
{"{{checkFileContent}}", _checkFileContent},
|
{"{{checkFileContent}}", _checkFileContent},
|
||||||
};
|
};
|
||||||
@@ -200,16 +274,23 @@ void MacroConditionFileEdit::UpdateEntryData()
|
|||||||
return;
|
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));
|
_filePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||||
_matchText->setPlainText(QString::fromStdString(_entryData->_text));
|
_matchText->setPlainText(QString::fromStdString(_entryData->_text));
|
||||||
_useRegex->setChecked(_entryData->_useRegex);
|
_regex->SetRegexConfig(_entryData->_regex);
|
||||||
_checkModificationDate->setChecked(_entryData->_useTime);
|
_checkModificationDate->setChecked(_entryData->_useTime);
|
||||||
_checkFileContent->setChecked(_entryData->_onlyMatchIfChanged);
|
_checkFileContent->setChecked(_entryData->_onlyMatchIfChanged);
|
||||||
|
|
||||||
adjustSize();
|
// TODO: Remove in future version
|
||||||
updateGeometry();
|
if (!_entryData->_useTime) {
|
||||||
|
_checkModificationDate->hide();
|
||||||
|
}
|
||||||
|
if (!_entryData->_onlyMatchIfChanged) {
|
||||||
|
_checkFileContent->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionFileEdit::FileTypeChanged(int index)
|
void MacroConditionFileEdit::FileTypeChanged(int index)
|
||||||
@@ -218,9 +299,10 @@ void MacroConditionFileEdit::FileTypeChanged(int index)
|
|||||||
return;
|
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);
|
_filePath->Button()->setDisabled(false);
|
||||||
_checkModificationDate->setDisabled(false);
|
_checkModificationDate->setDisabled(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -232,6 +314,18 @@ void MacroConditionFileEdit::FileTypeChanged(int index)
|
|||||||
_entryData->_fileType = type;
|
_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)
|
void MacroConditionFileEdit::PathChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -257,14 +351,16 @@ void MacroConditionFileEdit::MatchTextChanged()
|
|||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionFileEdit::UseRegexChanged(int state)
|
void MacroConditionFileEdit::RegexChanged(RegexConfig conf)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
_entryData->_useRegex = state;
|
_entryData->_regex = conf;
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionFileEdit::CheckModificationDateChanged(int state)
|
void MacroConditionFileEdit::CheckModificationDateChanged(int state)
|
||||||
@@ -286,3 +382,25 @@ void MacroConditionFileEdit::OnlyMatchIfChangedChanged(int state)
|
|||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
_entryData->_onlyMatchIfChanged = state;
|
_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 "macro.hpp"
|
||||||
#include "file-selection.hpp"
|
#include "file-selection.hpp"
|
||||||
#include "resizing-text-edit.hpp"
|
#include "resizing-text-edit.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
@@ -10,11 +11,6 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
|
||||||
enum class FileType {
|
|
||||||
LOCAL,
|
|
||||||
REMOTE,
|
|
||||||
};
|
|
||||||
|
|
||||||
class MacroConditionFile : public MacroCondition {
|
class MacroConditionFile : public MacroCondition {
|
||||||
public:
|
public:
|
||||||
MacroConditionFile(Macro *m) : MacroCondition(m) {}
|
MacroConditionFile(Macro *m) : MacroCondition(m) {}
|
||||||
@@ -28,17 +24,33 @@ public:
|
|||||||
return std::make_shared<MacroConditionFile>(m);
|
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 _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||||
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
|
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||||
FileType _fileType = FileType::LOCAL;
|
FileType _fileType = FileType::LOCAL;
|
||||||
bool _useRegex = false;
|
ConditionType _condition = ConditionType::MATCH;
|
||||||
|
RegexConfig _regex;
|
||||||
|
|
||||||
|
// TODO: Remove in future version
|
||||||
bool _useTime = false;
|
bool _useTime = false;
|
||||||
bool _onlyMatchIfChanged = false;
|
bool _onlyMatchIfChanged = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool matchFileContent(QString &filedata);
|
bool MatchFileContent(QString &filedata);
|
||||||
bool checkRemoteFileContent();
|
bool CheckRemoteFileContent();
|
||||||
bool checkLocalFileContent();
|
bool CheckLocalFileContent();
|
||||||
|
bool CheckChangeContent();
|
||||||
|
bool CheckChangeDate();
|
||||||
|
|
||||||
QDateTime _lastMod;
|
QDateTime _lastMod;
|
||||||
size_t _lastHash = 0;
|
size_t _lastHash = 0;
|
||||||
@@ -64,23 +76,27 @@ public:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void FileTypeChanged(int index);
|
void FileTypeChanged(int index);
|
||||||
|
void ConditionChanged(int index);
|
||||||
void PathChanged(const QString &text);
|
void PathChanged(const QString &text);
|
||||||
void MatchTextChanged();
|
void MatchTextChanged();
|
||||||
void UseRegexChanged(int state);
|
void RegexChanged(RegexConfig);
|
||||||
void CheckModificationDateChanged(int state);
|
void CheckModificationDateChanged(int state);
|
||||||
void OnlyMatchIfChangedChanged(int state);
|
void OnlyMatchIfChangedChanged(int state);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QComboBox *_fileType;
|
QComboBox *_fileTypes;
|
||||||
|
QComboBox *_conditions;
|
||||||
FileSelection *_filePath;
|
FileSelection *_filePath;
|
||||||
ResizingPlainTextEdit *_matchText;
|
ResizingPlainTextEdit *_matchText;
|
||||||
QCheckBox *_useRegex;
|
RegexConfigWidget *_regex;
|
||||||
QCheckBox *_checkModificationDate;
|
QCheckBox *_checkModificationDate;
|
||||||
QCheckBox *_checkFileContent;
|
QCheckBox *_checkFileContent;
|
||||||
std::shared_ptr<MacroConditionFile> _entryData;
|
std::shared_ptr<MacroConditionFile> _entryData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
bool _loading = true;
|
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_string(obj, "filter", GetWeakSourceName(_filter).c_str());
|
||||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||||
obs_data_set_bool(obj, "regex", _regex);
|
_regex.Save(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,12 @@ bool MacroConditionFilter::Load(obs_data_t *obj)
|
|||||||
_condition = static_cast<FilterCondition>(
|
_condition = static_cast<FilterCondition>(
|
||||||
obs_data_get_int(obj, "condition"));
|
obs_data_get_int(obj, "condition"));
|
||||||
_settings = obs_data_get_string(obj, "settings");
|
_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,18 +97,16 @@ static inline void populateConditionSelection(QComboBox *list)
|
|||||||
|
|
||||||
MacroConditionFilterEdit::MacroConditionFilterEdit(
|
MacroConditionFilterEdit::MacroConditionFilterEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroConditionFilter> entryData)
|
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);
|
_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);
|
populateConditionSelection(_conditions);
|
||||||
populateSourcesWithFilterSelection(_sources);
|
populateSourcesWithFilterSelection(_sources);
|
||||||
|
|
||||||
@@ -117,8 +120,8 @@ MacroConditionFilterEdit::MacroConditionFilterEdit(
|
|||||||
SLOT(GetSettingsClicked()));
|
SLOT(GetSettingsClicked()));
|
||||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||||
SLOT(SettingsChanged()));
|
SLOT(SettingsChanged()));
|
||||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||||
SLOT(RegexChanged(int)));
|
SLOT(RegexChanged(RegexConfig)));
|
||||||
|
|
||||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||||
@@ -194,7 +197,7 @@ void MacroConditionFilterEdit::GetSettingsClicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString json = formatJsonString(getSourceSettings(_entryData->_filter));
|
QString json = formatJsonString(getSourceSettings(_entryData->_filter));
|
||||||
if (_entryData->_regex) {
|
if (_entryData->_regex.Enabled()) {
|
||||||
json = escapeForRegex(json);
|
json = escapeForRegex(json);
|
||||||
}
|
}
|
||||||
_settings->setPlainText(json);
|
_settings->setPlainText(json);
|
||||||
@@ -213,14 +216,17 @@ void MacroConditionFilterEdit::SettingsChanged()
|
|||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionFilterEdit::RegexChanged(int state)
|
void MacroConditionFilterEdit::RegexChanged(RegexConfig conf)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
_entryData->_regex = state;
|
_entryData->_regex = conf;
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionFilterEdit::SetSettingsSelectionVisible(bool visible)
|
void MacroConditionFilterEdit::SetSettingsSelectionVisible(bool visible)
|
||||||
@@ -244,7 +250,7 @@ void MacroConditionFilterEdit::UpdateEntryData()
|
|||||||
GetWeakSourceName(_entryData->_filter).c_str());
|
GetWeakSourceName(_entryData->_filter).c_str());
|
||||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||||
_regex->setChecked(_entryData->_regex);
|
_regex->SetRegexConfig(_entryData->_regex);
|
||||||
SetSettingsSelectionVisible(_entryData->_condition ==
|
SetSettingsSelectionVisible(_entryData->_condition ==
|
||||||
FilterCondition::SETTINGS);
|
FilterCondition::SETTINGS);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
#include "resizing-text-edit.hpp"
|
#include "resizing-text-edit.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
@@ -29,7 +30,7 @@ public:
|
|||||||
OBSWeakSource _filter;
|
OBSWeakSource _filter;
|
||||||
FilterCondition _condition = FilterCondition::ENABLED;
|
FilterCondition _condition = FilterCondition::ENABLED;
|
||||||
std::string _settings = "";
|
std::string _settings = "";
|
||||||
bool _regex = false;
|
RegexConfig _regex;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
@@ -58,7 +59,7 @@ private slots:
|
|||||||
void ConditionChanged(int cond);
|
void ConditionChanged(int cond);
|
||||||
void GetSettingsClicked();
|
void GetSettingsClicked();
|
||||||
void SettingsChanged();
|
void SettingsChanged();
|
||||||
void RegexChanged(int);
|
void RegexChanged(RegexConfig);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ protected:
|
|||||||
QComboBox *_conditions;
|
QComboBox *_conditions;
|
||||||
QPushButton *_getSettings;
|
QPushButton *_getSettings;
|
||||||
ResizingPlainTextEdit *_settings;
|
ResizingPlainTextEdit *_settings;
|
||||||
QCheckBox *_regex;
|
RegexConfigWidget *_regex;
|
||||||
|
|
||||||
std::shared_ptr<MacroConditionFilter> _entryData;
|
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, "type", static_cast<int>(_type));
|
||||||
obs_data_set_int(obj, "condition", static_cast<int>(_counterCondition));
|
obs_data_set_int(obj, "condition", static_cast<int>(_counterCondition));
|
||||||
obs_data_set_int(obj, "count", _count);
|
obs_data_set_int(obj, "count", _count);
|
||||||
|
obs_data_set_int(obj, "multiStateCount", _multiSateCount);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +137,7 @@ bool MacroConditionMacro::Load(obs_data_t *obj)
|
|||||||
_counterCondition = static_cast<CounterCondition>(
|
_counterCondition = static_cast<CounterCondition>(
|
||||||
obs_data_get_int(obj, "condition"));
|
obs_data_get_int(obj, "condition"));
|
||||||
_count = obs_data_get_int(obj, "count");
|
_count = obs_data_get_int(obj, "count");
|
||||||
|
_multiSateCount = obs_data_get_int(obj, "multiStateCount");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ bool MacroConditionSceneOrder::Save(obs_data_t *obj)
|
|||||||
MacroCondition::Save(obj);
|
MacroCondition::Save(obj);
|
||||||
_scene.Save(obj);
|
_scene.Save(obj);
|
||||||
_source.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, "condition", static_cast<int>(_condition));
|
||||||
obs_data_set_int(obj, "position", _position);
|
obs_data_set_int(obj, "position", _position);
|
||||||
return true;
|
return true;
|
||||||
@@ -160,7 +160,14 @@ bool MacroConditionSceneOrder::Load(obs_data_t *obj)
|
|||||||
MacroCondition::Load(obj);
|
MacroCondition::Load(obj);
|
||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_source.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>(
|
_condition = static_cast<SceneOrderCondition>(
|
||||||
obs_data_get_int(obj, "condition"));
|
obs_data_get_int(obj, "condition"));
|
||||||
_position = obs_data_get_int(obj, "position");
|
_position = obs_data_get_int(obj, "position");
|
||||||
@@ -191,7 +198,7 @@ MacroConditionSceneOrderEdit::MacroConditionSceneOrderEdit(
|
|||||||
QWidget *parent, std::shared_ptr<MacroConditionSceneOrder> entryData)
|
QWidget *parent, std::shared_ptr<MacroConditionSceneOrder> entryData)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
|
||||||
_sources = new SceneItemSelectionWidget(parent);
|
_sources = new SceneItemSelectionWidget(parent);
|
||||||
_sources2 = new SceneItemSelectionWidget(parent);
|
_sources2 = new SceneItemSelectionWidget(parent);
|
||||||
_conditions = new QComboBox();
|
_conditions = new QComboBox();
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ bool MacroConditionSceneTransform::Save(obs_data_t *obj)
|
|||||||
_scene.Save(obj);
|
_scene.Save(obj);
|
||||||
_source.Save(obj);
|
_source.Save(obj);
|
||||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||||
obs_data_set_bool(obj, "regex", _regex);
|
_regex.Save(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,12 @@ bool MacroConditionSceneTransform::Load(obs_data_t *obj)
|
|||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_source.Load(obj);
|
_source.Load(obj);
|
||||||
_settings = obs_data_get_string(obj, "settings");
|
_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,17 +71,16 @@ std::string MacroConditionSceneTransform::GetShortDesc()
|
|||||||
MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
std::shared_ptr<MacroConditionSceneTransform> entryData)
|
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 &)),
|
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||||
@@ -88,8 +92,8 @@ MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
|||||||
SLOT(GetSettingsClicked()));
|
SLOT(GetSettingsClicked()));
|
||||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||||
SLOT(SettingsChanged()));
|
SLOT(SettingsChanged()));
|
||||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||||
SLOT(RegexChanged(int)));
|
SLOT(RegexChanged(RegexConfig)));
|
||||||
|
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{scenes}}", _scenes}, {"{{sources}}", _sources},
|
{"{{scenes}}", _scenes}, {"{{sources}}", _sources},
|
||||||
@@ -132,7 +136,7 @@ void MacroConditionSceneTransformEdit::UpdateEntryData()
|
|||||||
|
|
||||||
_scenes->SetScene(_entryData->_scene);
|
_scenes->SetScene(_entryData->_scene);
|
||||||
_sources->SetSceneItem(_entryData->_source);
|
_sources->SetSceneItem(_entryData->_source);
|
||||||
_regex->setChecked(_entryData->_regex);
|
_regex->SetRegexConfig(_entryData->_regex);
|
||||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
@@ -175,7 +179,7 @@ void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto settings = formatJsonString(getSceneItemTransform(items[0]));
|
auto settings = formatJsonString(getSceneItemTransform(items[0]));
|
||||||
if (_entryData->_regex) {
|
if (_entryData->_regex.Enabled()) {
|
||||||
settings = escapeForRegex(settings);
|
settings = escapeForRegex(settings);
|
||||||
}
|
}
|
||||||
_settings->setPlainText(settings);
|
_settings->setPlainText(settings);
|
||||||
@@ -198,12 +202,15 @@ void MacroConditionSceneTransformEdit::SettingsChanged()
|
|||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionSceneTransformEdit::RegexChanged(int state)
|
void MacroConditionSceneTransformEdit::RegexChanged(RegexConfig conf)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
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-selection.hpp"
|
||||||
#include "scene-item-selection.hpp"
|
#include "scene-item-selection.hpp"
|
||||||
#include "resizing-text-edit.hpp"
|
#include "resizing-text-edit.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
|
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
@@ -22,7 +23,7 @@ public:
|
|||||||
|
|
||||||
SceneSelection _scene;
|
SceneSelection _scene;
|
||||||
SceneItemSelection _source;
|
SceneItemSelection _source;
|
||||||
bool _regex = false;
|
RegexConfig _regex;
|
||||||
std::string _settings = "";
|
std::string _settings = "";
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -53,7 +54,7 @@ private slots:
|
|||||||
void SourceChanged(const SceneItemSelection &);
|
void SourceChanged(const SceneItemSelection &);
|
||||||
void GetSettingsClicked();
|
void GetSettingsClicked();
|
||||||
void SettingsChanged();
|
void SettingsChanged();
|
||||||
void RegexChanged(int);
|
void RegexChanged(RegexConfig);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ protected:
|
|||||||
SceneItemSelectionWidget *_sources;
|
SceneItemSelectionWidget *_sources;
|
||||||
QPushButton *_getSettings;
|
QPushButton *_getSettings;
|
||||||
ResizingPlainTextEdit *_settings;
|
ResizingPlainTextEdit *_settings;
|
||||||
QCheckBox *_regex;
|
RegexConfigWidget *_regex;
|
||||||
|
|
||||||
std::shared_ptr<MacroConditionSceneTransform> _entryData;
|
std::shared_ptr<MacroConditionSceneTransform> _entryData;
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ MacroConditionSceneVisibilityEdit::MacroConditionSceneVisibilityEdit(
|
|||||||
std::shared_ptr<MacroConditionSceneVisibility> entryData)
|
std::shared_ptr<MacroConditionSceneVisibility> entryData)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
_scenes = new SceneSelectionWidget(window(), false, true, true);
|
_scenes = new SceneSelectionWidget(window(), true, false, true, true);
|
||||||
_sources = new SceneItemSelectionWidget(parent);
|
_sources = new SceneItemSelectionWidget(parent);
|
||||||
_conditions = new QComboBox();
|
_conditions = new QComboBox();
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
|
|||||||
QWidget *parent, std::shared_ptr<MacroConditionScene> entryData)
|
QWidget *parent, std::shared_ptr<MacroConditionScene> entryData)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
_scenes = new SceneSelectionWidget(window(), false, false, false);
|
_scenes = new SceneSelectionWidget(window(), true, false, false, false);
|
||||||
_sceneType = new QComboBox();
|
_sceneType = new QComboBox();
|
||||||
_useTransitionTargetScene = new QCheckBox(obs_module_text(
|
_useTransitionTargetScene = new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour"));
|
"AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour"));
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
#include "advanced-scene-switcher.hpp"
|
#include "advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
const std::string MacroConditionSource::id = "source";
|
const std::string MacroConditionSource::id = "source";
|
||||||
|
|
||||||
bool MacroConditionSource::_registered = MacroConditionFactory::Register(
|
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_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||||
obs_data_set_bool(obj, "regex", _regex);
|
_regex.Save(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +65,12 @@ bool MacroConditionSource::Load(obs_data_t *obj)
|
|||||||
_condition = static_cast<SourceCondition>(
|
_condition = static_cast<SourceCondition>(
|
||||||
obs_data_get_int(obj, "condition"));
|
obs_data_get_int(obj, "condition"));
|
||||||
_settings = obs_data_get_string(obj, "settings");
|
_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,16 +91,14 @@ static inline void populateConditionSelection(QComboBox *list)
|
|||||||
|
|
||||||
MacroConditionSourceEdit::MacroConditionSourceEdit(
|
MacroConditionSourceEdit::MacroConditionSourceEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroConditionSource> entryData)
|
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);
|
populateConditionSelection(_conditions);
|
||||||
populateSourceSelection(_sources);
|
populateSourceSelection(_sources);
|
||||||
|
|
||||||
@@ -109,8 +110,8 @@ MacroConditionSourceEdit::MacroConditionSourceEdit(
|
|||||||
SLOT(GetSettingsClicked()));
|
SLOT(GetSettingsClicked()));
|
||||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||||
SLOT(SettingsChanged()));
|
SLOT(SettingsChanged()));
|
||||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||||
SLOT(RegexChanged(int)));
|
SLOT(RegexChanged(RegexConfig)));
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||||
@@ -171,7 +172,7 @@ void MacroConditionSourceEdit::GetSettingsClicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString json = formatJsonString(getSourceSettings(_entryData->_source));
|
QString json = formatJsonString(getSourceSettings(_entryData->_source));
|
||||||
if (_entryData->_regex) {
|
if (_entryData->_regex.Enabled()) {
|
||||||
json = escapeForRegex(json);
|
json = escapeForRegex(json);
|
||||||
}
|
}
|
||||||
_settings->setPlainText(json);
|
_settings->setPlainText(json);
|
||||||
@@ -190,14 +191,17 @@ void MacroConditionSourceEdit::SettingsChanged()
|
|||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionSourceEdit::RegexChanged(int state)
|
void MacroConditionSourceEdit::RegexChanged(RegexConfig conf)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
_entryData->_regex = state;
|
_entryData->_regex = conf;
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible)
|
void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible)
|
||||||
@@ -218,7 +222,7 @@ void MacroConditionSourceEdit::UpdateEntryData()
|
|||||||
GetWeakSourceName(_entryData->_source).c_str());
|
GetWeakSourceName(_entryData->_source).c_str());
|
||||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||||
_regex->setChecked(_entryData->_regex);
|
_regex->SetRegexConfig(_entryData->_regex);
|
||||||
SetSettingsSelectionVisible(_entryData->_condition ==
|
SetSettingsSelectionVisible(_entryData->_condition ==
|
||||||
SourceCondition::SETTINGS);
|
SourceCondition::SETTINGS);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
#include "resizing-text-edit.hpp"
|
#include "resizing-text-edit.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
@@ -28,7 +29,7 @@ public:
|
|||||||
OBSWeakSource _source = nullptr;
|
OBSWeakSource _source = nullptr;
|
||||||
SourceCondition _condition = SourceCondition::ACTIVE;
|
SourceCondition _condition = SourceCondition::ACTIVE;
|
||||||
std::string _settings = "";
|
std::string _settings = "";
|
||||||
bool _regex = false;
|
RegexConfig _regex;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
@@ -56,7 +57,7 @@ private slots:
|
|||||||
void ConditionChanged(int cond);
|
void ConditionChanged(int cond);
|
||||||
void GetSettingsClicked();
|
void GetSettingsClicked();
|
||||||
void SettingsChanged();
|
void SettingsChanged();
|
||||||
void RegexChanged(int);
|
void RegexChanged(RegexConfig);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ protected:
|
|||||||
QComboBox *_conditions;
|
QComboBox *_conditions;
|
||||||
QPushButton *_getSettings;
|
QPushButton *_getSettings;
|
||||||
ResizingPlainTextEdit *_settings;
|
ResizingPlainTextEdit *_settings;
|
||||||
QCheckBox *_regex;
|
RegexConfigWidget *_regex;
|
||||||
|
|
||||||
std::shared_ptr<MacroConditionSource> _entryData;
|
std::shared_ptr<MacroConditionSource> _entryData;
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ MacroConditionStudioModeEdit::MacroConditionStudioModeEdit(
|
|||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
_condition = new QComboBox();
|
_condition = new QComboBox();
|
||||||
_scenes = new SceneSelectionWidget(window());
|
_scenes = new SceneSelectionWidget(window(), true, false, true, true);
|
||||||
|
|
||||||
populateConditionSelection(_condition);
|
populateConditionSelection(_condition);
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ bool MacroConditionTransition::CheckCondition()
|
|||||||
bool transitionStarted = false;
|
bool transitionStarted = false;
|
||||||
bool transitionEnded = false;
|
bool transitionEnded = false;
|
||||||
|
|
||||||
if (_transition.GetType() == TransitionSelectionType::ANY) {
|
if (_transition.GetType() == TransitionSelection::Type::ANY) {
|
||||||
transitionStarted = anyTransitionStarted;
|
transitionStarted = anyTransitionStarted;
|
||||||
transitionEnded = anyTransitionEnded;
|
transitionEnded = anyTransitionEnded;
|
||||||
} else {
|
} else {
|
||||||
@@ -183,7 +183,7 @@ MacroConditionTransitionEdit::MacroConditionTransitionEdit(
|
|||||||
{
|
{
|
||||||
_conditions = new QComboBox();
|
_conditions = new QComboBox();
|
||||||
_transitions = new TransitionSelectionWidget(this, true, true);
|
_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);
|
_duration = new DurationSelection(this, false);
|
||||||
_durationSuffix = new QLabel(obs_module_text(
|
_durationSuffix = new QLabel(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.transition.durationSuffix"));
|
"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 MacroConditionVCam::CheckCondition()
|
||||||
{
|
{
|
||||||
bool stateMatch = false;
|
bool stateMatch = false;
|
||||||
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||||
switch (_state) {
|
switch (_state) {
|
||||||
case VCamState::STOP:
|
case VCamState::STOP:
|
||||||
stateMatch = !obs_frontend_virtualcam_active();
|
stateMatch = !obs_frontend_virtualcam_active();
|
||||||
@@ -30,6 +31,7 @@ bool MacroConditionVCam::CheckCondition()
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return stateMatch;
|
return stateMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,21 +12,50 @@ bool MacroConditionWebsocket::_registered = MacroConditionFactory::Register(
|
|||||||
{MacroConditionWebsocket::Create, MacroConditionWebsocketEdit::Create,
|
{MacroConditionWebsocket::Create, MacroConditionWebsocketEdit::Create,
|
||||||
"AdvSceneSwitcher.condition.websocket"});
|
"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 {
|
auto regex = conf.GetRegularExpression(expr);
|
||||||
std::regex regExpr(expr);
|
if (!regex.isValid()) {
|
||||||
return std::regex_match(msg, regExpr);
|
return false;
|
||||||
} catch (const std::regex_error &) {
|
|
||||||
}
|
}
|
||||||
return false;
|
auto match = regex.match(QString::fromStdString(msg));
|
||||||
|
return match.hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionWebsocket::CheckCondition()
|
bool MacroConditionWebsocket::CheckCondition()
|
||||||
{
|
{
|
||||||
for (const auto &msg : switcher->websocketMessages) {
|
const std::vector<std::string> *messages = nullptr;
|
||||||
if (_useRegex) {
|
switch (_type) {
|
||||||
if (matchRegex(msg, _message)) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -41,42 +70,72 @@ bool MacroConditionWebsocket::CheckCondition()
|
|||||||
bool MacroConditionWebsocket::Save(obs_data_t *obj)
|
bool MacroConditionWebsocket::Save(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroCondition::Save(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_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionWebsocket::Load(obs_data_t *obj)
|
bool MacroConditionWebsocket::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
MacroCondition::Load(obj);
|
MacroCondition::Load(obj);
|
||||||
|
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||||
_message = obs_data_get_string(obj, "message");
|
_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MacroConditionWebsocket::GetShortDesc()
|
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(
|
MacroConditionWebsocketEdit::MacroConditionWebsocketEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroConditionWebsocket> entryData)
|
QWidget *parent, std::shared_ptr<MacroConditionWebsocket> entryData)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
|
_conditions(new QComboBox(this)),
|
||||||
_message(new ResizingPlainTextEdit(this)),
|
_message(new ResizingPlainTextEdit(this)),
|
||||||
_useRegex(new QCheckBox(obs_module_text(
|
_regex(new RegexConfigWidget(parent)),
|
||||||
"AdvSceneSwitcher.condition.websocket.useRegex")))
|
_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,
|
QWidget::connect(_message, SIGNAL(textChanged()), this,
|
||||||
SLOT(MessageChanged()));
|
SLOT(MessageChanged()));
|
||||||
QWidget::connect(_useRegex, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||||
SLOT(UseRegexChanged(int)));
|
SLOT(RegexChanged(RegexConfig)));
|
||||||
|
QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)),
|
||||||
|
this,
|
||||||
|
SLOT(ConnectionSelectionChanged(const QString &)));
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
mainLayout->addLayout(_editLayout);
|
||||||
mainLayout->addWidget(new QLabel(
|
|
||||||
obs_module_text("AdvSceneSwitcher.condition.websocket.entry")));
|
|
||||||
mainLayout->addWidget(_message);
|
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);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
@@ -84,19 +143,76 @@ MacroConditionWebsocketEdit::MacroConditionWebsocketEdit(
|
|||||||
_loading = false;
|
_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()
|
void MacroConditionWebsocketEdit::UpdateEntryData()
|
||||||
{
|
{
|
||||||
if (!_entryData) {
|
if (!_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_conditions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||||
_message->setPlainText(QString::fromStdString(_entryData->_message));
|
_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();
|
adjustSize();
|
||||||
updateGeometry();
|
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()
|
void MacroConditionWebsocketEdit::MessageChanged()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -110,12 +226,27 @@ void MacroConditionWebsocketEdit::MessageChanged()
|
|||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionWebsocketEdit::UseRegexChanged(int state)
|
void MacroConditionWebsocketEdit::ConnectionSelectionChanged(
|
||||||
|
const QString &connection)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
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
|
#pragma once
|
||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
|
#include "connection-manager.hpp"
|
||||||
#include "resizing-text-edit.hpp"
|
#include "resizing-text-edit.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
|
||||||
@@ -17,8 +19,15 @@ public:
|
|||||||
return std::make_shared<MacroConditionWebsocket>(m);
|
return std::make_shared<MacroConditionWebsocket>(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
REQUEST,
|
||||||
|
EVENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
Type _type = Type::REQUEST;
|
||||||
std::string _message = obs_module_text("AdvSceneSwitcher.enterText");
|
std::string _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||||
bool _useRegex = false;
|
RegexConfig _regex;
|
||||||
|
std::string _connection;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
@@ -43,16 +52,24 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void ConditionChanged(int);
|
||||||
void MessageChanged();
|
void MessageChanged();
|
||||||
void UseRegexChanged(int state);
|
void RegexChanged(RegexConfig);
|
||||||
|
void ConnectionSelectionChanged(const QString &);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ResizingPlainTextEdit *_message;
|
|
||||||
QCheckBox *_useRegex;
|
|
||||||
std::shared_ptr<MacroConditionWebsocket> _entryData;
|
std::shared_ptr<MacroConditionWebsocket> _entryData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetupRequestEdit();
|
||||||
|
void SetupEventEdit();
|
||||||
|
|
||||||
|
QComboBox *_conditions;
|
||||||
|
ResizingPlainTextEdit *_message;
|
||||||
|
RegexConfigWidget *_regex;
|
||||||
|
ConnectionSelection *_connection;
|
||||||
|
QHBoxLayout *_editLayout;
|
||||||
bool _loading = true;
|
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, "highlightExecuted", _highlightExecuted);
|
||||||
obs_data_set_bool(data, "highlightConditions", _highlightConditions);
|
obs_data_set_bool(data, "highlightConditions", _highlightConditions);
|
||||||
obs_data_set_bool(data, "highlightActions", _highlightActions);
|
obs_data_set_bool(data, "highlightActions", _highlightActions);
|
||||||
|
obs_data_set_bool(data, "newMacroRegisterHotkey",
|
||||||
|
_newMacroRegisterHotkeys);
|
||||||
obs_data_set_obj(obj, "macroProperties", data);
|
obs_data_set_obj(obj, "macroProperties", data);
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
@@ -17,16 +19,11 @@ void MacroProperties::Save(obs_data_t *obj)
|
|||||||
void MacroProperties::Load(obs_data_t *obj)
|
void MacroProperties::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
auto data = obs_data_get_obj(obj, "macroProperties");
|
auto data = obs_data_get_obj(obj, "macroProperties");
|
||||||
// TODO: Remove in future version
|
_highlightExecuted = obs_data_get_bool(data, "highlightExecuted");
|
||||||
if (obs_data_has_user_value(obj, "highlightExecutedMacros")) {
|
|
||||||
_highlightExecuted =
|
|
||||||
obs_data_get_bool(obj, "highlightExecutedMacros");
|
|
||||||
} else {
|
|
||||||
_highlightExecuted =
|
|
||||||
obs_data_get_bool(data, "highlightExecuted");
|
|
||||||
}
|
|
||||||
_highlightConditions = obs_data_get_bool(data, "highlightConditions");
|
_highlightConditions = obs_data_get_bool(data, "highlightConditions");
|
||||||
_highlightActions = obs_data_get_bool(data, "highlightActions");
|
_highlightActions = obs_data_get_bool(data, "highlightActions");
|
||||||
|
_newMacroRegisterHotkeys =
|
||||||
|
obs_data_get_bool(data, "newMacroRegisterHotkey");
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,8 +37,10 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
|||||||
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
|
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
|
||||||
_actions(new QCheckBox(obs_module_text(
|
_actions(new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
|
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
|
||||||
_hotkeys(new QCheckBox(
|
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||||
obs_module_text("AdvSceneSwitcher.macroTab.disableHotkeys")))
|
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
|
||||||
|
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.currentDisableHotkeys")))
|
||||||
{
|
{
|
||||||
setModal(true);
|
setModal(true);
|
||||||
setWindowModality(Qt::WindowModality::WindowModal);
|
setWindowModality(Qt::WindowModality::WindowModal);
|
||||||
@@ -52,17 +51,26 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
|||||||
_executed->setChecked(prop._highlightExecuted);
|
_executed->setChecked(prop._highlightExecuted);
|
||||||
_conditions->setChecked(prop._highlightConditions);
|
_conditions->setChecked(prop._highlightConditions);
|
||||||
_actions->setChecked(prop._highlightActions);
|
_actions->setChecked(prop._highlightActions);
|
||||||
|
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys);
|
||||||
if (macro) {
|
if (macro) {
|
||||||
_hotkeys->setChecked(macro->PauseHotkeysEnabled());
|
_currentMacroRegisterHotkeys->setChecked(
|
||||||
|
macro->PauseHotkeysEnabled());
|
||||||
} else {
|
} else {
|
||||||
_hotkeys->hide();
|
_currentMacroRegisterHotkeys->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout;
|
QVBoxLayout *layout = new QVBoxLayout;
|
||||||
layout->addWidget(_executed);
|
layout->addWidget(_executed);
|
||||||
layout->addWidget(_conditions);
|
layout->addWidget(_conditions);
|
||||||
layout->addWidget(_actions);
|
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);
|
setLayout(layout);
|
||||||
|
|
||||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||||
@@ -85,8 +93,11 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
|
|||||||
userInput._highlightExecuted = dialog._executed->isChecked();
|
userInput._highlightExecuted = dialog._executed->isChecked();
|
||||||
userInput._highlightConditions = dialog._conditions->isChecked();
|
userInput._highlightConditions = dialog._conditions->isChecked();
|
||||||
userInput._highlightActions = dialog._actions->isChecked();
|
userInput._highlightActions = dialog._actions->isChecked();
|
||||||
|
userInput._newMacroRegisterHotkeys =
|
||||||
|
dialog._newMacroRegisterHotkeys->isChecked();
|
||||||
if (macro) {
|
if (macro) {
|
||||||
macro->EnablePauseHotkeys(dialog._hotkeys->isChecked());
|
macro->EnablePauseHotkeys(
|
||||||
|
dialog._currentMacroRegisterHotkeys->isChecked());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public:
|
|||||||
bool _highlightExecuted = false;
|
bool _highlightExecuted = false;
|
||||||
bool _highlightConditions = false;
|
bool _highlightConditions = false;
|
||||||
bool _highlightActions = false;
|
bool _highlightActions = false;
|
||||||
|
bool _newMacroRegisterHotkeys = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dialog for configuring global and macro specific settings
|
// Dialog for configuring global and macro specific settings
|
||||||
@@ -31,5 +32,7 @@ private:
|
|||||||
QCheckBox *_executed;
|
QCheckBox *_executed;
|
||||||
QCheckBox *_conditions;
|
QCheckBox *_conditions;
|
||||||
QCheckBox *_actions;
|
QCheckBox *_actions;
|
||||||
QCheckBox *_hotkeys;
|
QCheckBox *_newMacroRegisterHotkeys;
|
||||||
|
// Current macro specific settings
|
||||||
|
QCheckBox *_currentMacroRegisterHotkeys;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <obs-module.h>
|
#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);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->macros.emplace_back(
|
switcher->macros.emplace_back(std::make_shared<Macro>(
|
||||||
std::make_shared<Macro>(name, true));
|
name,
|
||||||
|
switcher->macroProperties._newMacroRegisterHotkeys));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Macro::Macro(const std::string &name, const bool addHotkey)
|
|||||||
if (addHotkey) {
|
if (addHotkey) {
|
||||||
SetupHotkeys();
|
SetupHotkeys();
|
||||||
}
|
}
|
||||||
|
_registerHotkeys = addHotkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
Macro::~Macro()
|
Macro::~Macro()
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ bool MacroConditionVideo::CheckCondition()
|
|||||||
return _lastMatchResult;
|
return _lastMatchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_blockUntilScreenshotDone) {
|
||||||
|
GetScreenshot(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (_screenshotData.done) {
|
if (_screenshotData.done) {
|
||||||
match = Compare();
|
match = Compare();
|
||||||
_lastMatchResult = match;
|
_lastMatchResult = match;
|
||||||
@@ -94,7 +98,7 @@ bool MacroConditionVideo::CheckCondition()
|
|||||||
match = _lastMatchResult;
|
match = _lastMatchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_getNextScreenshot) {
|
if (!_blockUntilScreenshotDone && _getNextScreenshot) {
|
||||||
GetScreenshot();
|
GetScreenshot();
|
||||||
}
|
}
|
||||||
return match;
|
return match;
|
||||||
@@ -106,6 +110,8 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
|
|||||||
_video.Save(obj);
|
_video.Save(obj);
|
||||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||||
obs_data_set_string(obj, "filePath", _file.c_str());
|
obs_data_set_string(obj, "filePath", _file.c_str());
|
||||||
|
obs_data_set_bool(obj, "blockUntilScreenshotDone",
|
||||||
|
_blockUntilScreenshotDone);
|
||||||
obs_data_set_bool(obj, "usePatternForChangedCheck",
|
obs_data_set_bool(obj, "usePatternForChangedCheck",
|
||||||
_usePatternForChangedCheck);
|
_usePatternForChangedCheck);
|
||||||
obs_data_set_double(obj, "threshold", _patternThreshold);
|
obs_data_set_double(obj, "threshold", _patternThreshold);
|
||||||
@@ -143,6 +149,8 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
|
|||||||
_condition =
|
_condition =
|
||||||
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
||||||
_file = obs_data_get_string(obj, "filePath");
|
_file = obs_data_get_string(obj, "filePath");
|
||||||
|
_blockUntilScreenshotDone =
|
||||||
|
obs_data_get_bool(obj, "blockUntilScreenshotDone");
|
||||||
_usePatternForChangedCheck =
|
_usePatternForChangedCheck =
|
||||||
obs_data_get_bool(obj, "usePatternForChangedCheck");
|
obs_data_get_bool(obj, "usePatternForChangedCheck");
|
||||||
_patternThreshold = obs_data_get_double(obj, "threshold");
|
_patternThreshold = obs_data_get_double(obj, "threshold");
|
||||||
@@ -186,11 +194,12 @@ std::string MacroConditionVideo::GetShortDesc()
|
|||||||
return _video.ToString();
|
return _video.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionVideo::GetScreenshot()
|
void MacroConditionVideo::GetScreenshot(bool blocking)
|
||||||
{
|
{
|
||||||
auto source = obs_weak_source_get_source(_video.GetVideo());
|
auto source = obs_weak_source_get_source(_video.GetVideo());
|
||||||
_screenshotData.~ScreenshotHelper();
|
_screenshotData.~ScreenshotHelper();
|
||||||
new (&_screenshotData) ScreenshotHelper(source);
|
new (&_screenshotData)
|
||||||
|
ScreenshotHelper(source, blocking, GetSwitcher()->interval);
|
||||||
obs_source_release(source);
|
obs_source_release(source);
|
||||||
_getNextScreenshot = false;
|
_getNextScreenshot = false;
|
||||||
}
|
}
|
||||||
@@ -200,6 +209,8 @@ bool MacroConditionVideo::LoadImageFromFile()
|
|||||||
if (!_matchImage.load(QString::fromStdString(_file))) {
|
if (!_matchImage.load(QString::fromStdString(_file))) {
|
||||||
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
||||||
_file.c_str());
|
_file.c_str());
|
||||||
|
(&_matchImage)->~QImage();
|
||||||
|
new (&_matchImage) QImage();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +296,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
_videoSelection(new VideoSelectionWidget(this)),
|
_videoSelection(new VideoSelectionWidget(this)),
|
||||||
_condition(new QComboBox()),
|
_condition(new QComboBox()),
|
||||||
|
_reduceLatency(new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.video.reduceLatency"))),
|
||||||
_usePatternForChangedCheck(new QCheckBox(obs_module_text(
|
_usePatternForChangedCheck(new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck"))),
|
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck"))),
|
||||||
_imagePath(new FileSelection()),
|
_imagePath(new FileSelection()),
|
||||||
@@ -312,7 +325,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
_minSize(new SizeSelection(0, 1024)),
|
_minSize(new SizeSelection(0, 1024)),
|
||||||
_maxSize(new SizeSelection(0, 4096)),
|
_maxSize(new SizeSelection(0, 4096)),
|
||||||
_checkAreaControlLayout(new QHBoxLayout),
|
_checkAreaControlLayout(new QHBoxLayout),
|
||||||
_checkAreaEnable(new QCheckBox()),
|
_checkAreaEnable(new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.video.entry.checkAreaEnable"))),
|
||||||
_checkArea(new AreaSelection(0, 99999)),
|
_checkArea(new AreaSelection(0, 99999)),
|
||||||
_selectArea(new QPushButton(obs_module_text(
|
_selectArea(new QPushButton(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.video.selectArea"))),
|
"AdvSceneSwitcher.condition.video.selectArea"))),
|
||||||
@@ -323,6 +337,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
"AdvSceneSwitcher.condition.video.showMatch"))),
|
"AdvSceneSwitcher.condition.video.showMatch"))),
|
||||||
_previewDialog(this, entryData.get(), &GetSwitcher()->m)
|
_previewDialog(this, entryData.get(), &GetSwitcher()->m)
|
||||||
{
|
{
|
||||||
|
_reduceLatency->setToolTip(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.video.reduceLatency.tooltip"));
|
||||||
_imagePath->Button()->disconnect();
|
_imagePath->Button()->disconnect();
|
||||||
_usePatternForChangedCheck->setToolTip(obs_module_text(
|
_usePatternForChangedCheck->setToolTip(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip"));
|
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip"));
|
||||||
@@ -338,6 +354,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
SLOT(VideoSelectionChanged(const VideoSelection &)));
|
SLOT(VideoSelectionChanged(const VideoSelection &)));
|
||||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(ConditionChanged(int)));
|
SLOT(ConditionChanged(int)));
|
||||||
|
QWidget::connect(_reduceLatency, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(ReduceLatencyChanged(int)));
|
||||||
QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this,
|
QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this,
|
||||||
SLOT(ImagePathChanged(const QString &)));
|
SLOT(ImagePathChanged(const QString &)));
|
||||||
QWidget::connect(_imagePath->Button(), SIGNAL(clicked()), this,
|
QWidget::connect(_imagePath->Button(), SIGNAL(clicked()), this,
|
||||||
@@ -380,6 +398,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{videoSources}}", _videoSelection},
|
{"{{videoSources}}", _videoSelection},
|
||||||
{"{{condition}}", _condition},
|
{"{{condition}}", _condition},
|
||||||
|
{"{{reduceLatency}}", _reduceLatency},
|
||||||
{"{{imagePath}}", _imagePath},
|
{"{{imagePath}}", _imagePath},
|
||||||
{"{{minNeighbors}}", _minNeighbors},
|
{"{{minNeighbors}}", _minNeighbors},
|
||||||
{"{{minSize}}", _minSize},
|
{"{{minSize}}", _minSize},
|
||||||
@@ -437,9 +456,10 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
mainLayout->addLayout(_neighborsControlLayout);
|
mainLayout->addLayout(_neighborsControlLayout);
|
||||||
mainLayout->addWidget(_minNeighborsDescription);
|
mainLayout->addWidget(_minNeighborsDescription);
|
||||||
mainLayout->addLayout(_sizeLayout);
|
mainLayout->addLayout(_sizeLayout);
|
||||||
mainLayout->addLayout(showMatchLayout);
|
|
||||||
mainLayout->addLayout(_throttleControlLayout);
|
mainLayout->addLayout(_throttleControlLayout);
|
||||||
mainLayout->addLayout(_checkAreaControlLayout);
|
mainLayout->addLayout(_checkAreaControlLayout);
|
||||||
|
mainLayout->addWidget(_reduceLatency);
|
||||||
|
mainLayout->addLayout(showMatchLayout);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
@@ -632,6 +652,16 @@ void MacroConditionVideoEdit::PatternThresholdChanged(double value)
|
|||||||
_entryData->_patternThreshold = 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)
|
void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -693,6 +723,9 @@ void MacroConditionVideoEdit::CheckAreaEnableChanged(int value)
|
|||||||
_entryData->_checkAreaEnable = value;
|
_entryData->_checkAreaEnable = value;
|
||||||
_checkArea->setEnabled(value);
|
_checkArea->setEnabled(value);
|
||||||
_selectArea->setEnabled(value);
|
_selectArea->setEnabled(value);
|
||||||
|
_checkArea->setVisible(value);
|
||||||
|
_selectArea->setVisible(value);
|
||||||
|
adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionVideoEdit::CheckAreaChanged(advss::Area value)
|
void MacroConditionVideoEdit::CheckAreaChanged(advss::Area value)
|
||||||
@@ -825,6 +858,8 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
|
|||||||
needsThrottleControls(_entryData->_condition));
|
needsThrottleControls(_entryData->_condition));
|
||||||
setLayoutVisible(_checkAreaControlLayout,
|
setLayoutVisible(_checkAreaControlLayout,
|
||||||
needsAreaControls(_entryData->_condition));
|
needsAreaControls(_entryData->_condition));
|
||||||
|
_checkArea->setVisible(_entryData->_checkAreaEnable);
|
||||||
|
_selectArea->setVisible(_entryData->_checkAreaEnable);
|
||||||
|
|
||||||
if (_entryData->_condition == VideoCondition::HAS_CHANGED ||
|
if (_entryData->_condition == VideoCondition::HAS_CHANGED ||
|
||||||
_entryData->_condition == VideoCondition::HAS_NOT_CHANGED) {
|
_entryData->_condition == VideoCondition::HAS_NOT_CHANGED) {
|
||||||
@@ -843,6 +878,7 @@ void MacroConditionVideoEdit::UpdateEntryData()
|
|||||||
|
|
||||||
_videoSelection->SetVideoSelection(_entryData->_video);
|
_videoSelection->SetVideoSelection(_entryData->_video);
|
||||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||||
|
_reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone);
|
||||||
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||||
_usePatternForChangedCheck->setChecked(
|
_usePatternForChangedCheck->setChecked(
|
||||||
_entryData->_usePatternForChangedCheck);
|
_entryData->_usePatternForChangedCheck);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
{
|
{
|
||||||
return std::make_shared<MacroConditionVideo>(m);
|
return std::make_shared<MacroConditionVideo>(m);
|
||||||
}
|
}
|
||||||
void GetScreenshot();
|
void GetScreenshot(bool blocking = false);
|
||||||
bool LoadImageFromFile();
|
bool LoadImageFromFile();
|
||||||
bool LoadModelData(std::string &path);
|
bool LoadModelData(std::string &path);
|
||||||
std::string GetModelDataPath() { return _modelDataPath; }
|
std::string GetModelDataPath() { return _modelDataPath; }
|
||||||
@@ -49,6 +49,13 @@ public:
|
|||||||
VideoSelection _video;
|
VideoSelection _video;
|
||||||
VideoCondition _condition = VideoCondition::MATCH;
|
VideoCondition _condition = VideoCondition::MATCH;
|
||||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
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 _useAlphaAsMask = false;
|
||||||
bool _usePatternForChangedCheck = false;
|
bool _usePatternForChangedCheck = false;
|
||||||
PatternMatchData _patternData;
|
PatternMatchData _patternData;
|
||||||
@@ -107,8 +114,11 @@ public:
|
|||||||
private slots:
|
private slots:
|
||||||
void VideoSelectionChanged(const VideoSelection &);
|
void VideoSelectionChanged(const VideoSelection &);
|
||||||
void ConditionChanged(int cond);
|
void ConditionChanged(int cond);
|
||||||
|
void ReduceLatencyChanged(int value);
|
||||||
|
|
||||||
void ImagePathChanged(const QString &text);
|
void ImagePathChanged(const QString &text);
|
||||||
void ImageBrowseButtonClicked();
|
void ImageBrowseButtonClicked();
|
||||||
|
|
||||||
void UsePatternForChangedCheckChanged(int value);
|
void UsePatternForChangedCheckChanged(int value);
|
||||||
void PatternThresholdChanged(double);
|
void PatternThresholdChanged(double);
|
||||||
void UseAlphaAsMaskChanged(int value);
|
void UseAlphaAsMaskChanged(int value);
|
||||||
@@ -134,8 +144,11 @@ protected:
|
|||||||
VideoSelectionWidget *_videoSelection;
|
VideoSelectionWidget *_videoSelection;
|
||||||
QComboBox *_condition;
|
QComboBox *_condition;
|
||||||
|
|
||||||
|
QCheckBox *_reduceLatency;
|
||||||
|
|
||||||
QCheckBox *_usePatternForChangedCheck;
|
QCheckBox *_usePatternForChangedCheck;
|
||||||
FileSelection *_imagePath;
|
FileSelection *_imagePath;
|
||||||
|
|
||||||
ThresholdSlider *_patternThreshold;
|
ThresholdSlider *_patternThreshold;
|
||||||
QCheckBox *_useAlphaAsMask;
|
QCheckBox *_useAlphaAsMask;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
PatternMatchData createPatternData(QImage &pattern)
|
PatternMatchData createPatternData(QImage &pattern)
|
||||||
{
|
{
|
||||||
PatternMatchData data;
|
PatternMatchData data{};
|
||||||
if (pattern.isNull()) {
|
if (pattern.isNull()) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ typedef struct transitionData {
|
|||||||
|
|
||||||
} transitionData;
|
} transitionData;
|
||||||
|
|
||||||
|
class Item;
|
||||||
class SwitcherThread;
|
class SwitcherThread;
|
||||||
|
|
||||||
/********************************************************************************
|
/********************************************************************************
|
||||||
@@ -138,9 +139,11 @@ struct SwitcherData {
|
|||||||
|
|
||||||
Curlhelper curl;
|
Curlhelper curl;
|
||||||
|
|
||||||
std::deque<Connection> connections;
|
std::deque<std::shared_ptr<Item>> connections;
|
||||||
std::vector<std::string> websocketMessages;
|
std::vector<std::string> websocketMessages;
|
||||||
|
|
||||||
|
std::deque<std::shared_ptr<Item>> variables;
|
||||||
|
|
||||||
std::deque<WindowSwitch> windowSwitches;
|
std::deque<WindowSwitch> windowSwitches;
|
||||||
std::vector<std::string> ignoreIdleWindows;
|
std::vector<std::string> ignoreIdleWindows;
|
||||||
std::string lastTitle;
|
std::string lastTitle;
|
||||||
@@ -291,6 +294,7 @@ struct SwitcherData {
|
|||||||
void saveSettings(obs_data_t *obj);
|
void saveSettings(obs_data_t *obj);
|
||||||
void saveMacros(obs_data_t *obj);
|
void saveMacros(obs_data_t *obj);
|
||||||
void saveConnections(obs_data_t *obj);
|
void saveConnections(obs_data_t *obj);
|
||||||
|
void saveVariables(obs_data_t *obj);
|
||||||
void saveWindowTitleSwitches(obs_data_t *obj);
|
void saveWindowTitleSwitches(obs_data_t *obj);
|
||||||
void saveScreenRegionSwitches(obs_data_t *obj);
|
void saveScreenRegionSwitches(obs_data_t *obj);
|
||||||
void savePauseSwitches(obs_data_t *obj);
|
void savePauseSwitches(obs_data_t *obj);
|
||||||
@@ -315,6 +319,7 @@ struct SwitcherData {
|
|||||||
void loadSettings(obs_data_t *obj);
|
void loadSettings(obs_data_t *obj);
|
||||||
void loadMacros(obs_data_t *obj);
|
void loadMacros(obs_data_t *obj);
|
||||||
void loadConnections(obs_data_t *obj);
|
void loadConnections(obs_data_t *obj);
|
||||||
|
void loadVariables(obs_data_t *obj);
|
||||||
void loadWindowTitleSwitches(obs_data_t *obj);
|
void loadWindowTitleSwitches(obs_data_t *obj);
|
||||||
void loadScreenRegionSwitches(obs_data_t *obj);
|
void loadScreenRegionSwitches(obs_data_t *obj);
|
||||||
void loadPauseSwitches(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)
|
void SwitcherData::saveConnections(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
obs_data_array_t *connectionArray = obs_data_array_create();
|
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();
|
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_array_push_back(connectionArray, array_obj);
|
||||||
obs_data_release(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++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
obs_data_t *array_obj = obs_data_array_item(connectionArray, i);
|
obs_data_t *array_obj = obs_data_array_item(connectionArray, i);
|
||||||
connections.emplace_back();
|
auto con = Connection::Create();
|
||||||
connections.back().Load(array_obj);
|
connections.emplace_back(con);
|
||||||
|
connections.back()->Load(array_obj);
|
||||||
obs_data_release(array_obj);
|
obs_data_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_array_release(connectionArray);
|
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,
|
Connection::Connection(std::string name, std::string address, uint64_t port,
|
||||||
std::string pass, bool connectOnStart, bool reconnect,
|
std::string pass, bool connectOnStart, bool reconnect,
|
||||||
int reconnectDelay)
|
int reconnectDelay)
|
||||||
: _name(name),
|
: Item(name),
|
||||||
_address(address),
|
_address(address),
|
||||||
_port(port),
|
_port(port),
|
||||||
_password(pass),
|
_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;
|
_name = other._name;
|
||||||
_address = other._address;
|
_address = other._address;
|
||||||
@@ -125,8 +126,8 @@ Connection *GetConnectionByName(const QString &name)
|
|||||||
Connection *GetConnectionByName(const std::string &name)
|
Connection *GetConnectionByName(const std::string &name)
|
||||||
{
|
{
|
||||||
for (auto &con : switcher->connections) {
|
for (auto &con : switcher->connections) {
|
||||||
if (con.GetName() == name) {
|
if (con->Name() == name) {
|
||||||
return &con;
|
return dynamic_cast<Connection *>(con.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -142,58 +143,38 @@ bool ConnectionNameAvailable(const std::string &name)
|
|||||||
return ConnectionNameAvailable(QString::fromStdString(name));
|
return ConnectionNameAvailable(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionSelection::ConnectionSelection(QWidget *parent)
|
static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
|
||||||
: QWidget(parent), _selection(new QComboBox), _modify(new QPushButton)
|
|
||||||
{
|
{
|
||||||
_modify->setMaximumSize(22, 22);
|
Connection &ConnectionSettings = dynamic_cast<Connection &>(settings);
|
||||||
QIcon icon;
|
return ConnectionSettingsDialog::AskForSettings(parent,
|
||||||
icon.addFile(
|
ConnectionSettings);
|
||||||
QString::fromUtf8(":/settings/images/settings/general.svg"),
|
}
|
||||||
QSize(), QIcon::Normal, QIcon::Off);
|
|
||||||
_modify->setIcon(icon);
|
|
||||||
_modify->setFlat(true);
|
|
||||||
|
|
||||||
|
ConnectionSelection::ConnectionSelection(QWidget *parent)
|
||||||
|
: ItemSelection(switcher->connections, Connection::Create,
|
||||||
|
AskForSettingsWrapper,
|
||||||
|
"AdvSceneSwitcher.connection.select",
|
||||||
|
"AdvSceneSwitcher.connection.add", parent)
|
||||||
|
{
|
||||||
// Connect to slots
|
// 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(
|
QWidget::connect(
|
||||||
window(),
|
window(),
|
||||||
SIGNAL(ConnectionRenamed(const QString &, const QString &)),
|
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 &)),
|
QWidget::connect(window(), SIGNAL(ConnectionAdded(const QString &)),
|
||||||
this, SLOT(AddConnection(const QString &)));
|
this, SLOT(AddItem(const QString &)));
|
||||||
QWidget::connect(window(), SIGNAL(ConnectionRemoved(const QString &)),
|
QWidget::connect(window(), SIGNAL(ConnectionRemoved(const QString &)),
|
||||||
this, SLOT(RemoveConnection(const QString &)));
|
this, SLOT(RemoveItem(const QString &)));
|
||||||
|
|
||||||
// Forward signals
|
// Forward signals
|
||||||
QWidget::connect(
|
QWidget::connect(
|
||||||
this,
|
this, SIGNAL(ItemRenamed(const QString &, const QString &)),
|
||||||
SIGNAL(ConnectionRenamed(const QString &, const QString &)),
|
|
||||||
window(),
|
window(),
|
||||||
SIGNAL(ConnectionRenamed(const QString &, const QString &)));
|
SIGNAL(ConnectionRenamed(const QString &, const QString &)));
|
||||||
QWidget::connect(this, SIGNAL(ConnectionAdded(const QString &)),
|
QWidget::connect(this, SIGNAL(ItemAdded(const QString &)), window(),
|
||||||
window(), SIGNAL(ConnectionAdded(const QString &)));
|
SIGNAL(ConnectionAdded(const QString &)));
|
||||||
QWidget::connect(this, SIGNAL(ConnectionRemoved(const QString &)),
|
QWidget::connect(this, SIGNAL(ItemRemoved(const QString &)), window(),
|
||||||
window(), SIGNAL(ConnectionRemoved(const QString &)));
|
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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionSelection::SetConnection(const std::string &con)
|
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,
|
ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
||||||
const Connection &settings)
|
const Connection &settings)
|
||||||
: QDialog(parent),
|
: ItemSettingsDialog(settings, switcher->connections,
|
||||||
_name(new QLineEdit()),
|
"AdvSceneSwitcher.connection.select",
|
||||||
_nameHint(new QLabel),
|
"AdvSceneSwitcher.connection.add", parent),
|
||||||
_address(new QLineEdit()),
|
_address(new QLineEdit()),
|
||||||
_port(new QSpinBox()),
|
_port(new QSpinBox()),
|
||||||
_password(new QLineEdit()),
|
_password(new QLineEdit()),
|
||||||
|
_showPassword(new QPushButton()),
|
||||||
_connectOnStart(new QCheckBox()),
|
_connectOnStart(new QCheckBox()),
|
||||||
_reconnect(new QCheckBox()),
|
_reconnect(new QCheckBox()),
|
||||||
_reconnectDelay(new QSpinBox()),
|
_reconnectDelay(new QSpinBox()),
|
||||||
_test(new QPushButton(
|
_test(new QPushButton(
|
||||||
obs_module_text("AdvSceneSwitcher.connection.test"))),
|
obs_module_text("AdvSceneSwitcher.connection.test"))),
|
||||||
_status(new QLabel()),
|
_status(new QLabel())
|
||||||
_buttonbox(new QDialogButtonBox(QDialogButtonBox::Ok |
|
|
||||||
QDialogButtonBox::Cancel))
|
|
||||||
{
|
{
|
||||||
setModal(true);
|
|
||||||
setWindowModality(Qt::WindowModality::WindowModal);
|
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
|
||||||
setFixedWidth(555);
|
|
||||||
setMinimumHeight(100);
|
|
||||||
|
|
||||||
_port->setMaximum(65535);
|
_port->setMaximum(65535);
|
||||||
|
_showPassword->setMaximumWidth(22);
|
||||||
|
_showPassword->setFlat(true);
|
||||||
|
_showPassword->setStyleSheet(
|
||||||
|
"QPushButton { background-color: transparent; border: 0px }");
|
||||||
_reconnectDelay->setMaximum(9999);
|
_reconnectDelay->setMaximum(9999);
|
||||||
_reconnectDelay->setSuffix("s");
|
_reconnectDelay->setSuffix("s");
|
||||||
_buttonbox->setCenterButtons(true);
|
|
||||||
_buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
|
|
||||||
|
|
||||||
_name->setText(QString::fromStdString(settings._name));
|
|
||||||
_address->setText(QString::fromStdString(settings._address));
|
_address->setText(QString::fromStdString(settings._address));
|
||||||
_port->setValue(settings._port);
|
_port->setValue(settings._port);
|
||||||
_password->setText(QString::fromStdString(settings._password));
|
_password->setText(QString::fromStdString(settings._password));
|
||||||
@@ -398,18 +218,15 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
|||||||
_reconnect->setChecked(settings._reconnect);
|
_reconnect->setChecked(settings._reconnect);
|
||||||
_reconnectDelay->setValue(settings._reconnectDelay);
|
_reconnectDelay->setValue(settings._reconnectDelay);
|
||||||
|
|
||||||
QWidget::connect(_name, SIGNAL(textEdited(const QString &)), this,
|
|
||||||
SLOT(NameChanged(const QString &)));
|
|
||||||
QWidget::connect(_reconnect, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_reconnect, SIGNAL(stateChanged(int)), this,
|
||||||
SLOT(ReconnectChanged(int)));
|
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,
|
QWidget::connect(_test, SIGNAL(clicked()), this,
|
||||||
SLOT(TestConnection()));
|
SLOT(TestConnection()));
|
||||||
|
|
||||||
QWidget::connect(_buttonbox, &QDialogButtonBox::accepted, this,
|
|
||||||
&QDialog::accept);
|
|
||||||
QWidget::connect(_buttonbox, &QDialogButtonBox::rejected, this,
|
|
||||||
&QDialog::reject);
|
|
||||||
|
|
||||||
QGridLayout *layout = new QGridLayout;
|
QGridLayout *layout = new QGridLayout;
|
||||||
int row = 0;
|
int row = 0;
|
||||||
layout->addWidget(
|
layout->addWidget(
|
||||||
@@ -433,7 +250,10 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
|||||||
layout->addWidget(new QLabel(obs_module_text(
|
layout->addWidget(new QLabel(obs_module_text(
|
||||||
"AdvSceneSwitcher.connection.password")),
|
"AdvSceneSwitcher.connection.password")),
|
||||||
row, 0);
|
row, 0);
|
||||||
layout->addWidget(_password, row, 1);
|
QHBoxLayout *passLayout = new QHBoxLayout;
|
||||||
|
passLayout->addWidget(_password);
|
||||||
|
passLayout->addWidget(_showPassword);
|
||||||
|
layout->addLayout(passLayout, row, 1);
|
||||||
++row;
|
++row;
|
||||||
layout->addWidget(
|
layout->addWidget(
|
||||||
new QLabel(obs_module_text(
|
new QLabel(obs_module_text(
|
||||||
@@ -458,30 +278,8 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
|||||||
layout->addWidget(_buttonbox, row, 0, 1, -1);
|
layout->addWidget(_buttonbox, row, 0, 1, -1);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
NameChanged(_name->text());
|
|
||||||
ReconnectChanged(_reconnect->isChecked());
|
ReconnectChanged(_reconnect->isChecked());
|
||||||
}
|
HidePassword();
|
||||||
|
|
||||||
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("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionSettingsDialog::ReconnectChanged(int state)
|
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()
|
void ConnectionSettingsDialog::TestConnection()
|
||||||
{
|
{
|
||||||
_testConnection.Disconnect();
|
_testConnection.Disconnect();
|
||||||
@@ -545,21 +355,9 @@ bool ConnectionSettingsDialog::AskForSettings(QWidget *parent,
|
|||||||
return true;
|
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)
|
void Connection::Load(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
_name = obs_data_get_string(obj, "name");
|
Item::Load(obj);
|
||||||
_address = obs_data_get_string(obj, "address");
|
_address = obs_data_get_string(obj, "address");
|
||||||
_port = obs_data_get_int(obj, "port");
|
_port = obs_data_get_int(obj, "port");
|
||||||
_password = obs_data_get_string(obj, "password");
|
_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_string(obj, "address", _address.c_str());
|
||||||
obs_data_set_int(obj, "port", _port);
|
obs_data_set_int(obj, "port", _port);
|
||||||
obs_data_set_string(obj, "password", _password.c_str());
|
obs_data_set_string(obj, "password", _password.c_str());
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "item-selection-helpers.hpp"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
@@ -7,7 +8,6 @@
|
|||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
class ConnectionSelection;
|
class ConnectionSelection;
|
||||||
class ConnectionSettingsDialog;
|
class ConnectionSettingsDialog;
|
||||||
|
|
||||||
class Connection {
|
class Connection : public Item {
|
||||||
public:
|
public:
|
||||||
Connection(std::string name, std::string address, uint64_t port,
|
Connection(std::string name, std::string address, uint64_t port,
|
||||||
std::string pass, bool connectOnStart, bool reconnect,
|
std::string pass, bool connectOnStart, bool reconnect,
|
||||||
@@ -27,15 +27,19 @@ public:
|
|||||||
Connection(const Connection &);
|
Connection(const Connection &);
|
||||||
Connection &operator=(const Connection &);
|
Connection &operator=(const Connection &);
|
||||||
~Connection();
|
~Connection();
|
||||||
|
static std::shared_ptr<Item> Create()
|
||||||
|
{
|
||||||
|
return std::make_shared<Connection>();
|
||||||
|
}
|
||||||
|
|
||||||
void Reconnect();
|
void Reconnect();
|
||||||
void SendMsg(const std::string &msg);
|
void SendMsg(const std::string &msg);
|
||||||
void Load(obs_data_t *obj);
|
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::string GetName() { return _name; }
|
||||||
|
std::vector<std::string> &Events() { return _client.Events(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _name = "";
|
|
||||||
std::string _address = "localhost";
|
std::string _address = "localhost";
|
||||||
uint64_t _port = 4455;
|
uint64_t _port = 4455;
|
||||||
std::string _password = "password";
|
std::string _password = "password";
|
||||||
@@ -53,7 +57,7 @@ private:
|
|||||||
Connection *GetConnectionByName(const QString &);
|
Connection *GetConnectionByName(const QString &);
|
||||||
Connection *GetConnectionByName(const std::string &);
|
Connection *GetConnectionByName(const std::string &);
|
||||||
|
|
||||||
class ConnectionSettingsDialog : public QDialog {
|
class ConnectionSettingsDialog : public ItemSettingsDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -61,54 +65,31 @@ public:
|
|||||||
static bool AskForSettings(QWidget *parent, Connection &settings);
|
static bool AskForSettings(QWidget *parent, Connection &settings);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void NameChanged(const QString &);
|
|
||||||
void ReconnectChanged(int);
|
void ReconnectChanged(int);
|
||||||
|
void ShowPassword();
|
||||||
|
void HidePassword();
|
||||||
void SetStatus();
|
void SetStatus();
|
||||||
void TestConnection();
|
void TestConnection();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetNameWarning(const QString);
|
|
||||||
|
|
||||||
QLineEdit *_name;
|
|
||||||
QLabel *_nameHint;
|
|
||||||
QLineEdit *_address;
|
QLineEdit *_address;
|
||||||
QSpinBox *_port;
|
QSpinBox *_port;
|
||||||
QLineEdit *_password;
|
QLineEdit *_password;
|
||||||
|
QPushButton *_showPassword;
|
||||||
QCheckBox *_connectOnStart;
|
QCheckBox *_connectOnStart;
|
||||||
QCheckBox *_reconnect;
|
QCheckBox *_reconnect;
|
||||||
QSpinBox *_reconnectDelay;
|
QSpinBox *_reconnectDelay;
|
||||||
QPushButton *_test;
|
QPushButton *_test;
|
||||||
QLabel *_status;
|
QLabel *_status;
|
||||||
QDialogButtonBox *_buttonbox;
|
|
||||||
|
|
||||||
QTimer _statusTimer;
|
QTimer _statusTimer;
|
||||||
WSConnection _testConnection;
|
WSConnection _testConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConnectionSelection : public QWidget {
|
class ConnectionSelection : public ItemSelection {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConnectionSelection(QWidget *parent = 0);
|
ConnectionSelection(QWidget *parent = 0);
|
||||||
void SetConnection(const std::string &);
|
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 <obs-module.h>
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
FileSelection::FileSelection(FileSelection::Type type, QWidget *parent)
|
FileSelection::FileSelection(FileSelection::Type type, QWidget *parent)
|
||||||
: QWidget(parent), _type(type)
|
: QWidget(parent), _type(type)
|
||||||
@@ -29,12 +31,22 @@ void FileSelection::SetPath(const QString &path)
|
|||||||
|
|
||||||
void FileSelection::BrowseButtonClicked()
|
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;
|
QString path;
|
||||||
if (_type == FileSelection::Type::WRITE) {
|
if (_type == FileSelection::Type::WRITE) {
|
||||||
path = QFileDialog::getSaveFileName(this);
|
path = QFileDialog::getSaveFileName(this, "", defaultPath);
|
||||||
} else {
|
} else {
|
||||||
path = QFileDialog::getOpenFileName(this);
|
path = QFileDialog::getOpenFileName(this, "", defaultPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.isEmpty()) {
|
if (path.isEmpty()) {
|
||||||
return;
|
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);
|
_up->setVisible(reorder);
|
||||||
_down->setVisible(reorder);
|
_down->setVisible(reorder);
|
||||||
|
|
||||||
_add->setMaximumSize(QSize(22, 22));
|
_add->setMaximumWidth(22);
|
||||||
_add->setProperty("themeID",
|
_add->setProperty("themeID",
|
||||||
QVariant(QString::fromUtf8("addIconSmall")));
|
QVariant(QString::fromUtf8("addIconSmall")));
|
||||||
_add->setFlat(true);
|
_add->setFlat(true);
|
||||||
_remove->setMaximumSize(QSize(22, 22));
|
_remove->setMaximumWidth(22);
|
||||||
_remove->setProperty("themeID",
|
_remove->setProperty("themeID",
|
||||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||||
_remove->setFlat(true);
|
_remove->setFlat(true);
|
||||||
_up->setMaximumSize(QSize(22, 22));
|
_up->setMaximumWidth(22);
|
||||||
_up->setProperty("themeID",
|
_up->setProperty("themeID",
|
||||||
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
||||||
_up->setFlat(true);
|
_up->setFlat(true);
|
||||||
_down->setMaximumSize(QSize(22, 22));
|
_down->setMaximumWidth(22);
|
||||||
_down->setProperty("themeID",
|
_down->setProperty("themeID",
|
||||||
QVariant(QString::fromUtf8("downArrowIconSmall")));
|
QVariant(QString::fromUtf8("downArrowIconSmall")));
|
||||||
_down->setFlat(true);
|
_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>
|
#include <obs-module.h>
|
||||||
|
|
||||||
void SceneItemSelection::Save(obs_data_t *obj, const char *name,
|
constexpr std::string_view typeSaveName = "type";
|
||||||
const char *targetName, const char *idxName)
|
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));
|
auto data = obs_data_create();
|
||||||
if (_target == SceneItemSelection::Target::INDIVIDUAL) {
|
obs_data_set_int(data, typeSaveName.data(), static_cast<int>(_type));
|
||||||
obs_data_set_int(obj, idxName, _idx);
|
obs_data_set_int(data, idxTypeSaveName.data(),
|
||||||
|
static_cast<int>(_idxType));
|
||||||
|
if (_idxType != IdxType::INDIVIDUAL) {
|
||||||
|
obs_data_set_int(data, idxSaveName.data(), _idx);
|
||||||
} else {
|
} 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,
|
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>(
|
_type = Type::SOURCE;
|
||||||
obs_data_get_int(obj, targetName));
|
_idxType = static_cast<IdxType>(obs_data_get_int(obj, typeName));
|
||||||
_idx = obs_data_get_int(obj, idxName);
|
_idx = obs_data_get_int(obj, idxName);
|
||||||
auto sceneItemName = obs_data_get_string(obj, name);
|
auto sceneItemName = obs_data_get_string(obj, name);
|
||||||
_sceneItem = GetWeakSourceByName(sceneItemName);
|
_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 {
|
struct ItemCountData {
|
||||||
std::string name;
|
std::string name;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@@ -44,12 +121,12 @@ static bool countSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getCountOfSceneItemOccurance(SceneSelection &s, std::string &name,
|
int getCountOfSceneItemOccurance(const SceneSelection &s,
|
||||||
|
const std::string &name,
|
||||||
bool enumAllScenes = true)
|
bool enumAllScenes = true)
|
||||||
{
|
{
|
||||||
ItemCountData data{name};
|
ItemCountData data{name};
|
||||||
if (enumAllScenes && (s.GetType() == SceneSelectionType::CURRENT ||
|
if (enumAllScenes && (s.GetType() != SceneSelection::Type::SCENE)) {
|
||||||
s.GetType() == SceneSelectionType::PREVIOUS)) {
|
|
||||||
auto enumScenes = [](void *param, obs_source_t *source) {
|
auto enumScenes = [](void *param, obs_source_t *source) {
|
||||||
if (!source) {
|
if (!source) {
|
||||||
return true;
|
return true;
|
||||||
@@ -72,17 +149,25 @@ int getCountOfSceneItemOccurance(SceneSelection &s, std::string &name,
|
|||||||
std::vector<obs_scene_item *>
|
std::vector<obs_scene_item *>
|
||||||
SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
|
SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
|
||||||
{
|
{
|
||||||
|
std::vector<obs_scene_item *> ret;
|
||||||
auto s = obs_weak_source_get_source(sceneSelection.GetScene(false));
|
auto s = obs_weak_source_get_source(sceneSelection.GetScene(false));
|
||||||
auto scene = obs_scene_from_source(s);
|
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);
|
int count = getCountOfSceneItemOccurance(sceneSelection, name, false);
|
||||||
auto items = getSceneItemsWithName(scene, name);
|
auto items = getSceneItemsWithName(scene, name);
|
||||||
obs_source_release(s);
|
obs_source_release(s);
|
||||||
|
|
||||||
std::vector<obs_scene_item *> ret;
|
if (_idxType == SceneItemSelection::IdxType::ALL ||
|
||||||
|
_idxType == SceneItemSelection::IdxType::ANY) {
|
||||||
if (_target == SceneItemSelection::Target::ALL ||
|
|
||||||
_target == SceneItemSelection::Target::ANY) {
|
|
||||||
ret = items;
|
ret = items;
|
||||||
} else {
|
} else {
|
||||||
// Index order starts at the bottom and increases to the top
|
// Index order starts at the bottom and increases to the top
|
||||||
@@ -103,13 +188,96 @@ SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
|
|||||||
|
|
||||||
std::string SceneItemSelection::ToString()
|
std::string SceneItemSelection::ToString()
|
||||||
{
|
{
|
||||||
|
if (_type == Type::VARIABLE) {
|
||||||
|
auto var = _variable.lock();
|
||||||
|
if (!var) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return var->Name();
|
||||||
|
}
|
||||||
return GetWeakSourceName(_sceneItem);
|
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,
|
SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
||||||
bool showAll,
|
bool showAll,
|
||||||
AllSelectionType type)
|
AllSelectionType type)
|
||||||
: QWidget(parent), _showAll(showAll), _allType(type)
|
: QWidget(parent), _hasAllEntry(showAll), _allType(type)
|
||||||
{
|
{
|
||||||
_sceneItems = new QComboBox();
|
_sceneItems = new QComboBox();
|
||||||
_idx = new QComboBox();
|
_idx = new QComboBox();
|
||||||
@@ -124,6 +292,16 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
|||||||
SLOT(SelectionChanged(const QString &)));
|
SLOT(SelectionChanged(const QString &)));
|
||||||
QWidget::connect(_idx, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_idx, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(IdxChanged(int)));
|
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;
|
auto layout = new QHBoxLayout;
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
layout->addWidget(_idx);
|
layout->addWidget(_idx);
|
||||||
@@ -134,21 +312,48 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
|||||||
|
|
||||||
void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item)
|
void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item)
|
||||||
{
|
{
|
||||||
_sceneItems->setCurrentText(
|
int itemIdx = 0;
|
||||||
QString::fromStdString(GetWeakSourceName(item._sceneItem)));
|
switch (item._type) {
|
||||||
if (item._target == SceneItemSelection::Target::ALL) {
|
case SceneItemSelection::Type::SOURCE: {
|
||||||
_allType = AllSelectionType::ALL;
|
|
||||||
_idx->setCurrentIndex(0);
|
|
||||||
} else if (item._target == SceneItemSelection::Target::ANY) {
|
|
||||||
_allType = AllSelectionType::ANY;
|
|
||||||
_idx->setCurrentIndex(0);
|
|
||||||
} else {
|
|
||||||
int idx = item._idx;
|
int idx = item._idx;
|
||||||
if (_showAll) {
|
if (_hasAllEntry) {
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
_idx->setCurrentIndex(idx);
|
_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)
|
void SceneItemSelectionWidget::SetScene(const SceneSelection &s)
|
||||||
@@ -156,12 +361,12 @@ void SceneItemSelectionWidget::SetScene(const SceneSelection &s)
|
|||||||
_scene = s;
|
_scene = s;
|
||||||
_sceneItems->clear();
|
_sceneItems->clear();
|
||||||
_idx->hide();
|
_idx->hide();
|
||||||
populateSceneItemSelection(_sceneItems, _scene);
|
PopulateItemSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneItemSelectionWidget::SetShowAll(bool value)
|
void SceneItemSelectionWidget::SetShowAll(bool value)
|
||||||
{
|
{
|
||||||
_showAll = value;
|
_hasAllEntry = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneItemSelectionWidget::SetShowAllSelectionType(AllSelectionType t)
|
void SceneItemSelectionWidget::SetShowAllSelectionType(AllSelectionType t)
|
||||||
@@ -179,21 +384,36 @@ void SceneItemSelectionWidget::SceneChanged(const SceneSelection &s)
|
|||||||
void SceneItemSelectionWidget::SelectionChanged(const QString &name)
|
void SceneItemSelectionWidget::SelectionChanged(const QString &name)
|
||||||
{
|
{
|
||||||
SceneItemSelection s;
|
SceneItemSelection s;
|
||||||
_sceneItem = GetWeakSourceByQString(name);
|
int sceneItemCount =
|
||||||
s._sceneItem = _sceneItem;
|
getCountOfSceneItemOccurance(_scene, name.toStdString());
|
||||||
if (_allType == AllSelectionType::ALL) {
|
|
||||||
s._target = SceneItemSelection::Target::ALL;
|
|
||||||
} else {
|
|
||||||
s._target = SceneItemSelection::Target::ANY;
|
|
||||||
}
|
|
||||||
auto stdName = name.toStdString();
|
|
||||||
int sceneItemCount = getCountOfSceneItemOccurance(_scene, stdName);
|
|
||||||
if (sceneItemCount > 1) {
|
if (sceneItemCount > 1) {
|
||||||
_idx->show();
|
_idx->show();
|
||||||
SetupIdxSelection(sceneItemCount);
|
SetupIdxSelection(sceneItemCount);
|
||||||
} else {
|
} else {
|
||||||
_idx->hide();
|
_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);
|
emit SceneItemChanged(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,29 +423,50 @@ void SceneItemSelectionWidget::IdxChanged(int idx)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneItemSelection s;
|
_currentSelection._idx = idx;
|
||||||
s._sceneItem = _sceneItem;
|
if (_hasAllEntry && idx == 0) {
|
||||||
if (_showAll && idx == 0) {
|
switch (_allType) {
|
||||||
if (_allType == AllSelectionType::ALL) {
|
case SceneItemSelectionWidget::AllSelectionType::ALL:
|
||||||
s._target = SceneItemSelection::Target::ALL;
|
_currentSelection._idxType =
|
||||||
} else {
|
SceneItemSelection::IdxType::ALL;
|
||||||
s._target = SceneItemSelection::Target::ANY;
|
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)
|
void SceneItemSelectionWidget::SetupIdxSelection(int sceneItemCount)
|
||||||
{
|
{
|
||||||
_idx->clear();
|
_idx->clear();
|
||||||
if (_showAll) {
|
if (_hasAllEntry) {
|
||||||
if (_allType == AllSelectionType::ALL) {
|
if (_allType == AllSelectionType::ALL) {
|
||||||
_idx->addItem(obs_module_text(
|
_idx->addItem(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneItemSelection.all"));
|
"AdvSceneSwitcher.sceneItemSelection.all"));
|
||||||
|
|||||||
@@ -1,28 +1,41 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "scene-selection.hpp"
|
||||||
|
#include "variable.hpp"
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <obs-data.h>
|
#include <obs-data.h>
|
||||||
|
|
||||||
#include "scene-selection.hpp"
|
|
||||||
#include "utility.hpp"
|
|
||||||
|
|
||||||
class SceneItemSelection {
|
class SceneItemSelection {
|
||||||
public:
|
public:
|
||||||
void Save(obs_data_t *obj, const char *name = "sceneItem",
|
void Save(obs_data_t *obj,
|
||||||
const char *targetName = "sceneItemTarget",
|
const char *name = "sceneItemSelection") const;
|
||||||
const char *idxName = "sceneItemIdx");
|
void Load(obs_data_t *obj, const char *name = "sceneItemSelection");
|
||||||
void Load(obs_data_t *obj, const char *name = "sceneItem",
|
// TODO: Remove in future version
|
||||||
const char *targetName = "sceneItemTarget",
|
void Load(obs_data_t *obj, const char *name, const char *targetName,
|
||||||
const char *idxName = "sceneItemIdx");
|
const char *idxName);
|
||||||
|
|
||||||
enum class Target { ALL, ANY, INDIVIDUAL };
|
enum class Type {
|
||||||
Target GetType() { return _target; }
|
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::vector<obs_scene_item *> GetSceneItems(SceneSelection &s);
|
||||||
std::string ToString();
|
std::string ToString();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OBSWeakSource _sceneItem;
|
OBSWeakSource _sceneItem;
|
||||||
Target _target = Target::ALL;
|
std::weak_ptr<Variable> _variable;
|
||||||
int _idx = 0;
|
Type _type = Type::SOURCE;
|
||||||
|
IdxType _idxType = IdxType::ALL;
|
||||||
|
int _idx = 0; // Multiple items with the same name can exist
|
||||||
friend class SceneItemSelectionWidget;
|
friend class SceneItemSelectionWidget;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,9 +43,10 @@ class SceneItemSelectionWidget : public QWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Influences what is being displaye for the "all" entry
|
||||||
enum class AllSelectionType { ALL, ANY };
|
enum class AllSelectionType { ALL, ANY };
|
||||||
SceneItemSelectionWidget(
|
SceneItemSelectionWidget(
|
||||||
QWidget *parent, bool allSelection = true,
|
QWidget *parent, bool addAllSelection = true,
|
||||||
AllSelectionType allType = AllSelectionType::ALL);
|
AllSelectionType allType = AllSelectionType::ALL);
|
||||||
void SetSceneItem(const SceneItemSelection &);
|
void SetSceneItem(const SceneItemSelection &);
|
||||||
void SetScene(const SceneSelection &);
|
void SetScene(const SceneSelection &);
|
||||||
@@ -45,15 +59,28 @@ private slots:
|
|||||||
void SceneChanged(const SceneSelection &);
|
void SceneChanged(const SceneSelection &);
|
||||||
void SelectionChanged(const QString &name);
|
void SelectionChanged(const QString &name);
|
||||||
void IdxChanged(int);
|
void IdxChanged(int);
|
||||||
|
void ItemAdd(const QString &name);
|
||||||
|
void ItemRemove(const QString &name);
|
||||||
|
void ItemRename(const QString &oldName, const QString &newName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Reset();
|
||||||
|
void PopulateItemSelection();
|
||||||
void SetupIdxSelection(int);
|
void SetupIdxSelection(int);
|
||||||
|
|
||||||
QComboBox *_sceneItems;
|
QComboBox *_sceneItems;
|
||||||
QComboBox *_idx;
|
QComboBox *_idx;
|
||||||
|
|
||||||
SceneSelection _scene;
|
SceneSelection _scene;
|
||||||
OBSWeakSource _sceneItem;
|
SceneItemSelection _currentSelection;
|
||||||
bool _showAll = false;
|
bool _hasAllEntry = false;
|
||||||
AllSelectionType _allType = AllSelectionType::ALL;
|
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 "scene-selection.hpp"
|
||||||
#include "advanced-scene-switcher.hpp"
|
#include "advanced-scene-switcher.hpp"
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
void SceneSelection::Save(obs_data_t *obj, const char *name,
|
constexpr std::string_view typeSaveName = "type";
|
||||||
const char *typeName)
|
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) {
|
switch (_type) {
|
||||||
case SceneSelectionType::SCENE:
|
case Type::SCENE:
|
||||||
obs_data_set_string(obj, name,
|
obs_data_set_string(data, nameSaveName.data(),
|
||||||
GetWeakSourceName(_scene).c_str());
|
GetWeakSourceName(_scene).c_str());
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::GROUP:
|
case Type::GROUP:
|
||||||
obs_data_set_string(obj, name, _group->name.c_str());
|
obs_data_set_string(data, nameSaveName.data(),
|
||||||
|
_group->name.c_str());
|
||||||
break;
|
break;
|
||||||
|
case Type::VARIABLE: {
|
||||||
|
auto var = _variable.lock();
|
||||||
|
if (!var) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
obs_data_set_string(data, nameSaveName.data(),
|
||||||
|
var->Name().c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
obs_data_set_obj(obj, selectionSaveName.data(), data);
|
||||||
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSelection::Load(obs_data_t *obj, const char *name,
|
void SceneSelection::Load(obs_data_t *obj, const char *name,
|
||||||
const char *typeName)
|
const char *typeName)
|
||||||
{
|
{
|
||||||
_type = static_cast<SceneSelectionType>(
|
// TODO: Remove in future version
|
||||||
obs_data_get_int(obj, typeName));
|
if (!obs_data_has_user_value(obj, selectionSaveName.data())) {
|
||||||
auto target = obs_data_get_string(obj, name);
|
_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) {
|
switch (_type) {
|
||||||
case SceneSelectionType::SCENE:
|
case Type::SCENE:
|
||||||
_scene = GetWeakSourceByName(target);
|
_scene = GetWeakSourceByName(targetName);
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::GROUP:
|
case Type::GROUP:
|
||||||
_group = GetSceneGroupByName(target);
|
_group = GetSceneGroupByName(targetName);
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::PREVIOUS:
|
case Type::PREVIOUS:
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::CURRENT:
|
case Type::CURRENT:
|
||||||
|
break;
|
||||||
|
case Type::PREVIEW:
|
||||||
|
break;
|
||||||
|
case Type::VARIABLE:
|
||||||
|
_variable = GetWeakVariableByName(targetName);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSWeakSource SceneSelection::GetScene(bool advance)
|
OBSWeakSource SceneSelection::GetScene(bool advance) const
|
||||||
{
|
{
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case SceneSelectionType::SCENE:
|
case Type::SCENE:
|
||||||
return _scene;
|
return _scene;
|
||||||
case SceneSelectionType::GROUP:
|
case Type::GROUP:
|
||||||
if (!_group) {
|
if (!_group) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -54,199 +97,339 @@ OBSWeakSource SceneSelection::GetScene(bool advance)
|
|||||||
return _group->getNextScene();
|
return _group->getNextScene();
|
||||||
}
|
}
|
||||||
return _group->getCurrentScene();
|
return _group->getCurrentScene();
|
||||||
case SceneSelectionType::PREVIOUS:
|
case Type::PREVIOUS:
|
||||||
return switcher->previousScene;
|
return switcher->previousScene;
|
||||||
case SceneSelectionType::CURRENT:
|
case Type::CURRENT:
|
||||||
return switcher->currentScene;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SceneSelection::ToString()
|
std::string SceneSelection::ToString() const
|
||||||
{
|
{
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case SceneSelectionType::SCENE:
|
case Type::SCENE:
|
||||||
return GetWeakSourceName(_scene);
|
return GetWeakSourceName(_scene);
|
||||||
case SceneSelectionType::GROUP:
|
case Type::GROUP:
|
||||||
if (_group) {
|
if (_group) {
|
||||||
return _group->name;
|
return _group->name;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::PREVIOUS:
|
case Type::PREVIOUS:
|
||||||
return obs_module_text("AdvSceneSwitcher.selectPreviousScene");
|
return obs_module_text("AdvSceneSwitcher.selectPreviousScene");
|
||||||
case SceneSelectionType::CURRENT:
|
case Type::CURRENT:
|
||||||
return obs_module_text("AdvSceneSwitcher.selectCurrentScene");
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneSelectionWidget::SceneSelectionWidget(QWidget *parent, bool sceneGroups,
|
SceneSelection SceneSelectionWidget::CurrentSelection()
|
||||||
bool previous, bool current)
|
{
|
||||||
: QComboBox(parent)
|
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);
|
setDuplicatesEnabled(true);
|
||||||
populateSceneSelection(this, previous, current, false, sceneGroups,
|
PopulateSelection();
|
||||||
&switcher->sceneGroups);
|
|
||||||
|
|
||||||
QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)),
|
QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)),
|
||||||
this, SLOT(SelectionChanged(const QString &)));
|
this, SLOT(SelectionChanged(const QString &)));
|
||||||
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
|
|
||||||
SLOT(SceneGroupAdd(const QString &)));
|
// Scene groups
|
||||||
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
|
QWidget::connect(window(), SIGNAL(SceneGroupAdded(const QString &)),
|
||||||
this, SLOT(SceneGroupRemove(const QString &)));
|
this, SLOT(ItemAdd(const QString &)));
|
||||||
|
QWidget::connect(window(), SIGNAL(SceneGroupRemoved(const QString &)),
|
||||||
|
this, SLOT(ItemRemove(const QString &)));
|
||||||
QWidget::connect(
|
QWidget::connect(
|
||||||
parent,
|
window(),
|
||||||
SIGNAL(SceneGroupRenamed(const QString &, const QString &)),
|
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
|
int idx = 0;
|
||||||
// 1. Any Scene (current not used)
|
|
||||||
// 2. Current Scene
|
|
||||||
// 3. Previous Scene
|
|
||||||
// 4. Scenes / Scene Groups
|
|
||||||
|
|
||||||
int idx;
|
|
||||||
|
|
||||||
switch (s.GetType()) {
|
switch (s.GetType()) {
|
||||||
case SceneSelectionType::SCENE:
|
case SceneSelection::Type::SCENE: {
|
||||||
case SceneSelectionType::GROUP:
|
if (_scenesEndIdx == -1) {
|
||||||
setCurrentText(QString::fromStdString(s.ToString()));
|
idx = 0;
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::PREVIOUS:
|
|
||||||
idx = findText(QString::fromStdString(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.selectPreviousScene")));
|
|
||||||
if (idx != -1) {
|
|
||||||
setCurrentIndex(idx);
|
|
||||||
}
|
}
|
||||||
|
idx = findIdxInRagne(this, _groupsEndIdx, _scenesEndIdx,
|
||||||
|
s.ToString());
|
||||||
break;
|
break;
|
||||||
case SceneSelectionType::CURRENT:
|
}
|
||||||
idx = findText(QString::fromStdString(obs_module_text(
|
case SceneSelection::Type::GROUP: {
|
||||||
"AdvSceneSwitcher.selectCurrentScene")));
|
if (_groupsEndIdx == -1) {
|
||||||
if (idx != -1) {
|
idx = 0;
|
||||||
setCurrentIndex(idx);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
setCurrentIndex(0);
|
idx = 0;
|
||||||
break;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
setCurrentIndex(idx);
|
||||||
// If entry cannot be found we dont want the selection to be empty
|
_currentSelection = s;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneSelectionWidget::IsCurrentSceneSelected(const QString &name)
|
bool SceneSelectionWidget::IsCurrentSceneSelected(const QString &name)
|
||||||
{
|
{
|
||||||
if (name == QString::fromStdString((obs_module_text(
|
return name == QString::fromStdString((obs_module_text(
|
||||||
"AdvSceneSwitcher.selectCurrentScene")))) {
|
"AdvSceneSwitcher.selectCurrentScene")));
|
||||||
return isFirstEntry(this, name, currentIndex());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneSelectionWidget::IsPreviousSceneSelected(const QString &name)
|
bool SceneSelectionWidget::IsPreviousSceneSelected(const QString &name)
|
||||||
{
|
{
|
||||||
if (name == QString::fromStdString((obs_module_text(
|
return name == QString::fromStdString((obs_module_text(
|
||||||
"AdvSceneSwitcher.selectPreviousScene")))) {
|
"AdvSceneSwitcher.selectPreviousScene")));
|
||||||
return isFirstEntry(this, name, currentIndex());
|
}
|
||||||
|
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSelectionWidget::SelectionChanged(const QString &name)
|
void SceneSelectionWidget::ItemRemove(const QString &name)
|
||||||
{
|
{
|
||||||
SceneSelection s;
|
if (!NameUsed(name)) {
|
||||||
auto scene = GetWeakSourceByQString(name);
|
blockSignals(true);
|
||||||
if (scene) {
|
|
||||||
s._type = SceneSelectionType::SCENE;
|
|
||||||
s._scene = scene;
|
|
||||||
}
|
}
|
||||||
|
Reset();
|
||||||
auto group = GetSceneGroupByQString(name);
|
blockSignals(false);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSelectionWidget::SceneGroupAdd(const QString &name)
|
void SceneSelectionWidget::ItemRename(const QString &, const QString &)
|
||||||
{
|
{
|
||||||
addItem(name);
|
blockSignals(true);
|
||||||
}
|
Reset();
|
||||||
|
blockSignals(false);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "scene-group.hpp"
|
#include "scene-group.hpp"
|
||||||
|
#include "variable.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
|
||||||
enum class SceneSelectionType {
|
|
||||||
SCENE,
|
|
||||||
GROUP,
|
|
||||||
PREVIOUS,
|
|
||||||
CURRENT,
|
|
||||||
};
|
|
||||||
|
|
||||||
class SceneSelection {
|
class SceneSelection {
|
||||||
public:
|
public:
|
||||||
void Save(obs_data_t *obj, const char *name = "scene",
|
void Save(obs_data_t *obj) const;
|
||||||
const char *typeName = "sceneType");
|
|
||||||
void Load(obs_data_t *obj, const char *name = "scene",
|
void Load(obs_data_t *obj, const char *name = "scene",
|
||||||
const char *typeName = "sceneType");
|
const char *typeName = "sceneType");
|
||||||
|
|
||||||
SceneSelectionType GetType() { return _type; }
|
enum class Type {
|
||||||
OBSWeakSource GetScene(bool advance = true);
|
SCENE,
|
||||||
std::string ToString();
|
GROUP,
|
||||||
|
PREVIOUS,
|
||||||
|
CURRENT,
|
||||||
|
PREVIEW,
|
||||||
|
VARIABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
Type GetType() const { return _type; }
|
||||||
|
OBSWeakSource GetScene(bool advance = true) const;
|
||||||
|
std::string ToString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OBSWeakSource _scene;
|
OBSWeakSource _scene;
|
||||||
SceneGroup *_group = nullptr;
|
SceneGroup *_group = nullptr;
|
||||||
SceneSelectionType _type = SceneSelectionType::SCENE;
|
std::weak_ptr<Variable> _variable;
|
||||||
|
Type _type = Type::SCENE;
|
||||||
friend class SceneSelectionWidget;
|
friend class SceneSelectionWidget;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,19 +36,45 @@ class SceneSelectionWidget : public QComboBox {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SceneSelectionWidget(QWidget *parent, bool sceneGroups = false,
|
SceneSelectionWidget(QWidget *parent, bool variables = false,
|
||||||
bool previous = false, bool current = false);
|
bool sceneGroups = false, bool previous = false,
|
||||||
void SetScene(SceneSelection &);
|
bool current = false, bool preview = false);
|
||||||
|
void SetScene(const SceneSelection &);
|
||||||
signals:
|
signals:
|
||||||
void SceneChanged(const SceneSelection &);
|
void SceneChanged(const SceneSelection &);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void SelectionChanged(const QString &name);
|
void SelectionChanged(const QString &name);
|
||||||
void SceneGroupAdd(const QString &name);
|
void ItemAdd(const QString &name);
|
||||||
void SceneGroupRemove(const QString &name);
|
void ItemRemove(const QString &name);
|
||||||
void SceneGroupRename(const QString &oldName, const QString &newName);
|
void ItemRename(const QString &oldName, const QString &newName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Reset();
|
||||||
|
SceneSelection CurrentSelection();
|
||||||
|
void PopulateSelection();
|
||||||
bool IsCurrentSceneSelected(const QString &name);
|
bool IsCurrentSceneSelected(const QString &name);
|
||||||
bool IsPreviousSceneSelected(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 "screenshot-helper.hpp"
|
||||||
#include "advanced-scene-switcher.hpp"
|
#include "advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
static void ScreenshotTick(void *param, float);
|
static void ScreenshotTick(void *param, float);
|
||||||
|
|
||||||
ScreenshotHelper::ScreenshotHelper(obs_source_t *source)
|
ScreenshotHelper::ScreenshotHelper(obs_source_t *source, bool blocking,
|
||||||
: weakSource(OBSGetWeakRef(source))
|
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;
|
_initDone = true;
|
||||||
obs_add_tick_callback(ScreenshotTick, this);
|
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()
|
ScreenshotHelper::~ScreenshotHelper()
|
||||||
@@ -20,6 +42,9 @@ ScreenshotHelper::~ScreenshotHelper()
|
|||||||
|
|
||||||
obs_remove_tick_callback(ScreenshotTick, this);
|
obs_remove_tick_callback(ScreenshotTick, this);
|
||||||
}
|
}
|
||||||
|
if (_saveThread.joinable()) {
|
||||||
|
_saveThread.join();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenshotHelper::Screenshot()
|
void ScreenshotHelper::Screenshot()
|
||||||
@@ -98,6 +123,26 @@ void ScreenshotHelper::MarkDone()
|
|||||||
{
|
{
|
||||||
time = std::chrono::high_resolution_clock::now();
|
time = std::chrono::high_resolution_clock::now();
|
||||||
done = true;
|
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
|
#define STAGE_SCREENSHOT 0
|
||||||
@@ -124,6 +169,7 @@ static void ScreenshotTick(void *param, float)
|
|||||||
break;
|
break;
|
||||||
case STAGE_COPY_AND_SAVE:
|
case STAGE_COPY_AND_SAVE:
|
||||||
data->Copy();
|
data->Copy();
|
||||||
|
data->WriteToFile();
|
||||||
data->MarkDone();
|
data->MarkDone();
|
||||||
|
|
||||||
obs_remove_tick_callback(ScreenshotTick, data);
|
obs_remove_tick_callback(ScreenshotTick, data);
|
||||||
|
|||||||
@@ -3,11 +3,16 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
class ScreenshotHelper {
|
class ScreenshotHelper {
|
||||||
public:
|
public:
|
||||||
ScreenshotHelper() = default;
|
ScreenshotHelper() = default;
|
||||||
ScreenshotHelper(obs_source_t *source);
|
ScreenshotHelper(obs_source_t *source, bool blocking = false,
|
||||||
|
int timeout = 1000, bool saveToFile = false,
|
||||||
|
std::string path = "");
|
||||||
ScreenshotHelper &operator=(const ScreenshotHelper &) = delete;
|
ScreenshotHelper &operator=(const ScreenshotHelper &) = delete;
|
||||||
ScreenshotHelper(const ScreenshotHelper &) = delete;
|
ScreenshotHelper(const ScreenshotHelper &) = delete;
|
||||||
~ScreenshotHelper();
|
~ScreenshotHelper();
|
||||||
@@ -16,6 +21,7 @@ public:
|
|||||||
void Download();
|
void Download();
|
||||||
void Copy();
|
void Copy();
|
||||||
void MarkDone();
|
void MarkDone();
|
||||||
|
void WriteToFile();
|
||||||
|
|
||||||
gs_texrender_t *texrender = nullptr;
|
gs_texrender_t *texrender = nullptr;
|
||||||
gs_stagesurf_t *stagesurf = nullptr;
|
gs_stagesurf_t *stagesurf = nullptr;
|
||||||
@@ -31,4 +37,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool _initDone = false;
|
bool _initDone = false;
|
||||||
|
bool _blocking = false;
|
||||||
|
std::thread _saveThread;
|
||||||
|
bool _saveToFile = false;
|
||||||
|
std::string _path = "";
|
||||||
|
std::mutex _mutex;
|
||||||
|
std::condition_variable _cv;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ void TransitionSelection::Save(obs_data_t *obj, const char *name,
|
|||||||
obs_data_set_int(obj, typeName, static_cast<int>(_type));
|
obs_data_set_int(obj, typeName, static_cast<int>(_type));
|
||||||
|
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case TransitionSelectionType::TRANSITION:
|
case Type::TRANSITION:
|
||||||
obs_data_set_string(obj, name,
|
obs_data_set_string(obj, name,
|
||||||
GetWeakSourceName(_transition).c_str());
|
GetWeakSourceName(_transition).c_str());
|
||||||
break;
|
break;
|
||||||
@@ -19,11 +19,10 @@ void TransitionSelection::Save(obs_data_t *obj, const char *name,
|
|||||||
void TransitionSelection::Load(obs_data_t *obj, const char *name,
|
void TransitionSelection::Load(obs_data_t *obj, const char *name,
|
||||||
const char *typeName)
|
const char *typeName)
|
||||||
{
|
{
|
||||||
_type = static_cast<TransitionSelectionType>(
|
_type = static_cast<Type>(obs_data_get_int(obj, typeName));
|
||||||
obs_data_get_int(obj, typeName));
|
|
||||||
auto target = obs_data_get_string(obj, name);
|
auto target = obs_data_get_string(obj, name);
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case TransitionSelectionType::TRANSITION:
|
case Type::TRANSITION:
|
||||||
_transition = GetWeakTransitionByName(target);
|
_transition = GetWeakTransitionByName(target);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -34,9 +33,9 @@ void TransitionSelection::Load(obs_data_t *obj, const char *name,
|
|||||||
OBSWeakSource TransitionSelection::GetTransition()
|
OBSWeakSource TransitionSelection::GetTransition()
|
||||||
{
|
{
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case TransitionSelectionType::TRANSITION:
|
case Type::TRANSITION:
|
||||||
return _transition;
|
return _transition;
|
||||||
case TransitionSelectionType::CURRENT: {
|
case Type::CURRENT: {
|
||||||
auto source = obs_frontend_get_current_transition();
|
auto source = obs_frontend_get_current_transition();
|
||||||
auto weakSource = obs_source_get_weak_source(source);
|
auto weakSource = obs_source_get_weak_source(source);
|
||||||
obs_weak_source_release(weakSource);
|
obs_weak_source_release(weakSource);
|
||||||
@@ -52,11 +51,11 @@ OBSWeakSource TransitionSelection::GetTransition()
|
|||||||
std::string TransitionSelection::ToString()
|
std::string TransitionSelection::ToString()
|
||||||
{
|
{
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case TransitionSelectionType::TRANSITION:
|
case Type::TRANSITION:
|
||||||
return GetWeakSourceName(_transition);
|
return GetWeakSourceName(_transition);
|
||||||
case TransitionSelectionType::CURRENT:
|
case Type::CURRENT:
|
||||||
return obs_module_text("AdvSceneSwitcher.currentTransition");
|
return obs_module_text("AdvSceneSwitcher.currentTransition");
|
||||||
case TransitionSelectionType::ANY:
|
case Type::ANY:
|
||||||
return obs_module_text("AdvSceneSwitcher.anyTransition");
|
return obs_module_text("AdvSceneSwitcher.anyTransition");
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -85,17 +84,17 @@ void TransitionSelectionWidget::SetTransition(TransitionSelection &t)
|
|||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
switch (t.GetType()) {
|
switch (t.GetType()) {
|
||||||
case TransitionSelectionType::TRANSITION:
|
case TransitionSelection::Type::TRANSITION:
|
||||||
setCurrentText(QString::fromStdString(t.ToString()));
|
setCurrentText(QString::fromStdString(t.ToString()));
|
||||||
break;
|
break;
|
||||||
case TransitionSelectionType::CURRENT:
|
case TransitionSelection::Type::CURRENT:
|
||||||
idx = findText(QString::fromStdString(
|
idx = findText(QString::fromStdString(
|
||||||
obs_module_text("AdvSceneSwitcher.currentTransition")));
|
obs_module_text("AdvSceneSwitcher.currentTransition")));
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
setCurrentIndex(idx);
|
setCurrentIndex(idx);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TransitionSelectionType::ANY:
|
case TransitionSelection::Type::ANY:
|
||||||
idx = findText(QString::fromStdString(
|
idx = findText(QString::fromStdString(
|
||||||
obs_module_text("AdvSceneSwitcher.anyTransition")));
|
obs_module_text("AdvSceneSwitcher.anyTransition")));
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
@@ -155,16 +154,16 @@ void TransitionSelectionWidget::SelectionChanged(const QString &name)
|
|||||||
TransitionSelection t;
|
TransitionSelection t;
|
||||||
auto transition = GetWeakTransitionByQString(name);
|
auto transition = GetWeakTransitionByQString(name);
|
||||||
if (transition) {
|
if (transition) {
|
||||||
t._type = TransitionSelectionType::TRANSITION;
|
t._type = TransitionSelection::Type::TRANSITION;
|
||||||
t._transition = transition;
|
t._transition = transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!transition) {
|
if (!transition) {
|
||||||
if (IsCurrentTransitionSelected(name)) {
|
if (IsCurrentTransitionSelected(name)) {
|
||||||
t._type = TransitionSelectionType::CURRENT;
|
t._type = TransitionSelection::Type::CURRENT;
|
||||||
}
|
}
|
||||||
if (IsAnyTransitionSelected(name)) {
|
if (IsAnyTransitionSelected(name)) {
|
||||||
t._type = TransitionSelectionType::ANY;
|
t._type = TransitionSelection::Type::ANY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,6 @@
|
|||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
|
||||||
enum class TransitionSelectionType {
|
|
||||||
TRANSITION,
|
|
||||||
CURRENT,
|
|
||||||
ANY,
|
|
||||||
};
|
|
||||||
|
|
||||||
class TransitionSelection {
|
class TransitionSelection {
|
||||||
public:
|
public:
|
||||||
void Save(obs_data_t *obj, const char *name = "transition",
|
void Save(obs_data_t *obj, const char *name = "transition",
|
||||||
@@ -16,13 +10,19 @@ public:
|
|||||||
void Load(obs_data_t *obj, const char *name = "transition",
|
void Load(obs_data_t *obj, const char *name = "transition",
|
||||||
const char *typeName = "transitionType");
|
const char *typeName = "transitionType");
|
||||||
|
|
||||||
TransitionSelectionType GetType() { return _type; }
|
enum class Type {
|
||||||
|
TRANSITION,
|
||||||
|
CURRENT,
|
||||||
|
ANY,
|
||||||
|
};
|
||||||
|
|
||||||
|
Type GetType() { return _type; }
|
||||||
OBSWeakSource GetTransition();
|
OBSWeakSource GetTransition();
|
||||||
std::string ToString();
|
std::string ToString();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OBSWeakSource _transition;
|
OBSWeakSource _transition;
|
||||||
TransitionSelectionType _type = TransitionSelectionType::TRANSITION;
|
Type _type = Type::TRANSITION;
|
||||||
friend class TransitionSelectionWidget;
|
friend class TransitionSelectionWidget;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
#include "platform-funcs.hpp"
|
#include "platform-funcs.hpp"
|
||||||
#include "scene-selection.hpp"
|
#include "scene-selection.hpp"
|
||||||
|
#include "regex-config.hpp"
|
||||||
|
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
@@ -301,41 +302,10 @@ void setSourceSettings(obs_source_t *s, const std::string &settings)
|
|||||||
obs_data_release(data);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match json1 with pattern json2
|
// Match json1 with pattern json2
|
||||||
bool matchJson(const std::string &json1, const std::string &json2,
|
bool matchJson(const std::string &json1, const std::string &json2,
|
||||||
bool useRegex)
|
const RegexConfig ®ex)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
|
||||||
auto j1 = formatJsonString(json1).toStdString();
|
auto j1 = formatJsonString(json1).toStdString();
|
||||||
auto j2 = formatJsonString(json2).toStdString();
|
auto j2 = formatJsonString(json2).toStdString();
|
||||||
|
|
||||||
@@ -346,24 +316,23 @@ bool matchJson(const std::string &json1, const std::string &json2,
|
|||||||
j2 = json2;
|
j2 = json2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useRegex) {
|
if (regex.Enabled()) {
|
||||||
try {
|
auto expr = regex.GetRegularExpression(j2);
|
||||||
std::regex expr(j2);
|
if (!expr.isValid()) {
|
||||||
ret = std::regex_match(
|
return false;
|
||||||
j1, expr, std::regex_constants::match_default);
|
|
||||||
} catch (const std::regex_error &) {
|
|
||||||
}
|
}
|
||||||
} else {
|
auto match = expr.match(QString::fromStdString(j1));
|
||||||
ret = j1 == j2;
|
return match.hasMatch();
|
||||||
}
|
}
|
||||||
return ret;
|
return j1 == j2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compareSourceSettings(const OBSWeakSource &source,
|
bool compareSourceSettings(const OBSWeakSource &source,
|
||||||
const std::string &settings, bool useRegex)
|
const std::string &settings,
|
||||||
|
const RegexConfig ®ex)
|
||||||
{
|
{
|
||||||
std::string currentSettings = getSourceSettings(source);
|
std::string currentSettings = getSourceSettings(source);
|
||||||
return matchJson(currentSettings, settings, useRegex);
|
return matchJson(currentSettings, settings, regex);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getDataFilePath(const std::string &file)
|
std::string getDataFilePath(const std::string &file)
|
||||||
@@ -652,7 +621,8 @@ void populateMediaSelection(QComboBox *sel, bool addSelect)
|
|||||||
reinterpret_cast<std::vector<std::string> *>(data);
|
reinterpret_cast<std::vector<std::string> *>(data);
|
||||||
std::string sourceId = obs_source_get_id(source);
|
std::string sourceId = obs_source_get_id(source);
|
||||||
if (sourceId.compare("ffmpeg_source") == 0 ||
|
if (sourceId.compare("ffmpeg_source") == 0 ||
|
||||||
sourceId.compare("vlc_source") == 0) {
|
sourceId.compare("vlc_source") == 0 ||
|
||||||
|
sourceId.compare("slideshow") == 0) {
|
||||||
list->push_back(obs_source_get_name(source));
|
list->push_back(obs_source_get_name(source));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -793,68 +763,6 @@ void populateFilterSelection(QComboBox *list, OBSWeakSource weakSource)
|
|||||||
list->setCurrentIndex(0);
|
list->setCurrentIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool enumSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
|
||||||
{
|
|
||||||
std::set<QString> *names = reinterpret_cast<std::set<QString> *>(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->emplace(name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void populateSceneItemSelection(QComboBox *list, OBSWeakSource sceneWeakSource)
|
|
||||||
{
|
|
||||||
std::set<QString> names;
|
|
||||||
auto s = obs_weak_source_get_source(sceneWeakSource);
|
|
||||||
auto scene = obs_scene_from_source(s);
|
|
||||||
obs_scene_enum_items(scene, enumSceneItem, &names);
|
|
||||||
obs_source_release(s);
|
|
||||||
|
|
||||||
for (auto &name : names) {
|
|
||||||
list->addItem(name);
|
|
||||||
}
|
|
||||||
list->model()->sort(0);
|
|
||||||
addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem"));
|
|
||||||
list->setCurrentIndex(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void populateSceneItemSelection(QComboBox *list, SceneSelection &s)
|
|
||||||
{
|
|
||||||
std::set<QString> names;
|
|
||||||
|
|
||||||
if (s.GetType() == SceneSelectionType::CURRENT ||
|
|
||||||
s.GetType() == SceneSelectionType::PREVIOUS) {
|
|
||||||
auto enumScenes = [](void *param, obs_source_t *source) {
|
|
||||||
if (!source) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
std::set<QString> *names =
|
|
||||||
reinterpret_cast<std::set<QString> *>(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &name : names) {
|
|
||||||
list->addItem(name);
|
|
||||||
}
|
|
||||||
list->model()->sort(0);
|
|
||||||
addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem"));
|
|
||||||
list->setCurrentIndex(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void populateSourceGroupSelection(QComboBox *list)
|
void populateSourceGroupSelection(QComboBox *list)
|
||||||
{
|
{
|
||||||
std::set<QString> sourceTypeNames;
|
std::set<QString> sourceTypeNames;
|
||||||
@@ -1020,3 +928,41 @@ bool doubleEquals(double left, double right, double epsilon)
|
|||||||
{
|
{
|
||||||
return (fabs(left - right) < epsilon);
|
return (fabs(left - right) < epsilon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setButtonIcon(QPushButton *button, const char *path)
|
||||||
|
{
|
||||||
|
QIcon icon;
|
||||||
|
icon.addFile(QString::fromUtf8(path), QSize(), QIcon::Normal,
|
||||||
|
QIcon::Off);
|
||||||
|
button->setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSelectionGroup(QComboBox *selection, const QStringList &group,
|
||||||
|
bool addSeparator)
|
||||||
|
{
|
||||||
|
selection->addItems(group);
|
||||||
|
if (addSeparator) {
|
||||||
|
selection->insertSeparator(selection->count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int findIdxInRagne(QComboBox *list, int start, int stop,
|
||||||
|
const std::string &value)
|
||||||
|
{
|
||||||
|
if (value.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto model = list->model();
|
||||||
|
auto startIdx = model->index(start, 0);
|
||||||
|
auto match = model->match(startIdx, Qt::DisplayRole,
|
||||||
|
QString::fromStdString(value), 1,
|
||||||
|
Qt::MatchExactly | Qt::MatchCaseSensitive);
|
||||||
|
if (match.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int foundIdx = match.first().row();
|
||||||
|
if (foundIdx > stop) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return foundIdx;
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "scene-group.hpp"
|
#include "scene-group.hpp"
|
||||||
|
|
||||||
class SceneSelection;
|
class SceneSelection;
|
||||||
|
class RegexConfig;
|
||||||
|
|
||||||
bool WeakSourceValid(obs_weak_source_t *ws);
|
bool WeakSourceValid(obs_weak_source_t *ws);
|
||||||
std::string GetWeakSourceName(obs_weak_source_t *weak_source);
|
std::string GetWeakSourceName(obs_weak_source_t *weak_source);
|
||||||
@@ -26,12 +27,13 @@ bool compareIgnoringLineEnding(QString &s1, QString &s2);
|
|||||||
std::string getSourceSettings(OBSWeakSource ws);
|
std::string getSourceSettings(OBSWeakSource ws);
|
||||||
void setSourceSettings(obs_source_t *s, const std::string &settings);
|
void setSourceSettings(obs_source_t *s, const std::string &settings);
|
||||||
bool compareSourceSettings(const OBSWeakSource &source,
|
bool compareSourceSettings(const OBSWeakSource &source,
|
||||||
const std::string &settings, bool regex);
|
const std::string &settings,
|
||||||
|
const RegexConfig ®ex);
|
||||||
std::vector<obs_scene_item *> getSceneItemsWithName(obs_scene_t *scene,
|
std::vector<obs_scene_item *> getSceneItemsWithName(obs_scene_t *scene,
|
||||||
std::string &name);
|
std::string &name);
|
||||||
std::string getDataFilePath(const std::string &file);
|
std::string getDataFilePath(const std::string &file);
|
||||||
bool matchJson(const std::string &json1, const std::string &json2,
|
bool matchJson(const std::string &json1, const std::string &json2,
|
||||||
bool useRegex);
|
const RegexConfig ®ex);
|
||||||
QString formatJsonString(std::string);
|
QString formatJsonString(std::string);
|
||||||
QString formatJsonString(QString);
|
QString formatJsonString(QString);
|
||||||
QString escapeForRegex(QString &s);
|
QString escapeForRegex(QString &s);
|
||||||
@@ -77,10 +79,12 @@ void populateSceneSelection(QComboBox *sel, bool addPrevious = false,
|
|||||||
void populateSourcesWithFilterSelection(QComboBox *list);
|
void populateSourcesWithFilterSelection(QComboBox *list);
|
||||||
void populateFilterSelection(QComboBox *list,
|
void populateFilterSelection(QComboBox *list,
|
||||||
OBSWeakSource weakSource = nullptr);
|
OBSWeakSource weakSource = nullptr);
|
||||||
void populateSceneItemSelection(QComboBox *list,
|
|
||||||
OBSWeakSource sceneWeakSource = nullptr);
|
|
||||||
void populateSceneItemSelection(QComboBox *list, SceneSelection &s);
|
|
||||||
void populateSourceGroupSelection(QComboBox *list);
|
void populateSourceGroupSelection(QComboBox *list);
|
||||||
void populateProfileSelection(QComboBox *list);
|
void populateProfileSelection(QComboBox *list);
|
||||||
bool windowPosValid(QPoint pos);
|
bool windowPosValid(QPoint pos);
|
||||||
bool doubleEquals(double left, double right, double epsilon);
|
bool doubleEquals(double left, double right, double epsilon);
|
||||||
|
void setButtonIcon(QPushButton *button, const char *path);
|
||||||
|
void addSelectionGroup(QComboBox *selection, const QStringList &group,
|
||||||
|
bool addSeparator = true);
|
||||||
|
int findIdxInRagne(QComboBox *list, int start, int stop,
|
||||||
|
const std::string &value);
|
||||||
|
|||||||
235
src/utils/variable.cpp
Normal file
235
src/utils/variable.cpp
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
#include "variable.hpp"
|
||||||
|
|
||||||
|
#include <switcher-data-structs.hpp>
|
||||||
|
|
||||||
|
Variable::Variable() : Item() {}
|
||||||
|
|
||||||
|
void Variable::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
Item::Load(obj);
|
||||||
|
_saveAction =
|
||||||
|
static_cast<SaveAction>(obs_data_get_int(obj, "saveAction"));
|
||||||
|
_defaultValue = obs_data_get_string(obj, "defaultValue");
|
||||||
|
if (_saveAction == SaveAction::SAVE) {
|
||||||
|
_value = obs_data_get_string(obj, "value");
|
||||||
|
} else if (_saveAction == SaveAction::SET_DEFAULT) {
|
||||||
|
_value = _defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Variable::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
Item::Save(obj);
|
||||||
|
obs_data_set_int(obj, "saveAction", static_cast<int>(_saveAction));
|
||||||
|
if (_saveAction == SaveAction::SAVE) {
|
||||||
|
obs_data_set_string(obj, "value", _value.c_str());
|
||||||
|
}
|
||||||
|
obs_data_set_string(obj, "defaultValue", _defaultValue.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Variable::DoubleValue(double &value) const
|
||||||
|
{
|
||||||
|
char *end = nullptr;
|
||||||
|
value = strtod(_value.c_str(), &end);
|
||||||
|
return end != _value.c_str() && *end == '\0' && value != HUGE_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Variable::SetValue(double value)
|
||||||
|
{
|
||||||
|
_value = std::to_string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Variable *GetVariableByName(const std::string &name)
|
||||||
|
{
|
||||||
|
for (const auto &v : switcher->variables) {
|
||||||
|
if (v->Name() == name) {
|
||||||
|
return dynamic_cast<Variable *>(v.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variable *GetVariableByQString(const QString &name)
|
||||||
|
{
|
||||||
|
return GetVariableByName(name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name)
|
||||||
|
{
|
||||||
|
for (const auto &v : switcher->variables) {
|
||||||
|
if (v->Name() == name) {
|
||||||
|
std::weak_ptr<Variable> wp =
|
||||||
|
std::dynamic_pointer_cast<Variable>(v);
|
||||||
|
return wp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::weak_ptr<Variable>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name)
|
||||||
|
{
|
||||||
|
return GetWeakVariableByName(name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GetVariablesNameList()
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
for (const auto &var : switcher->variables) {
|
||||||
|
list << QString::fromStdString(var->Name());
|
||||||
|
}
|
||||||
|
list.sort();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveVariables(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *variablesArray = obs_data_array_create();
|
||||||
|
for (const auto &v : variables) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
v->Save(array_obj);
|
||||||
|
obs_data_array_push_back(variablesArray, array_obj);
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "variables", variablesArray);
|
||||||
|
obs_data_array_release(variablesArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadVariables(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
variables.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *variablesArray = obs_data_get_array(obj, "variables");
|
||||||
|
size_t count = obs_data_array_count(variablesArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(variablesArray, i);
|
||||||
|
auto var = Variable::Create();
|
||||||
|
variables.emplace_back(var);
|
||||||
|
variables.back()->Load(array_obj);
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(variablesArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void populateSaveActionSelection(QComboBox *list)
|
||||||
|
{
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.variable.save.dontSave"));
|
||||||
|
list->addItem(obs_module_text("AdvSceneSwitcher.variable.save.save"));
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.variable.save.default"));
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
|
||||||
|
const Variable &settings)
|
||||||
|
: ItemSettingsDialog(settings, switcher->variables,
|
||||||
|
"AdvSceneSwitcher.variable.select",
|
||||||
|
"AdvSceneSwitcher.variable.add", parent),
|
||||||
|
_value(new QLineEdit()),
|
||||||
|
_defaultValue(new QLineEdit()),
|
||||||
|
_save(new QComboBox())
|
||||||
|
{
|
||||||
|
QWidget::connect(_save, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(SaveActionChanged(int)));
|
||||||
|
|
||||||
|
_value->setText(QString::fromStdString(settings._value));
|
||||||
|
_defaultValue->setText(QString::fromStdString(settings._defaultValue));
|
||||||
|
populateSaveActionSelection(_save);
|
||||||
|
_save->setCurrentIndex(static_cast<int>(settings._saveAction));
|
||||||
|
|
||||||
|
QGridLayout *layout = new QGridLayout;
|
||||||
|
int row = 0;
|
||||||
|
layout->addWidget(
|
||||||
|
new QLabel(obs_module_text("AdvSceneSwitcher.variable.name")),
|
||||||
|
row, 0);
|
||||||
|
QHBoxLayout *nameLayout = new QHBoxLayout;
|
||||||
|
nameLayout->addWidget(_name);
|
||||||
|
nameLayout->addWidget(_nameHint);
|
||||||
|
layout->addLayout(nameLayout, row, 1);
|
||||||
|
++row;
|
||||||
|
layout->addWidget(
|
||||||
|
new QLabel(obs_module_text("AdvSceneSwitcher.variable.value")),
|
||||||
|
row, 0);
|
||||||
|
layout->addWidget(_value, row, 1);
|
||||||
|
++row;
|
||||||
|
layout->addWidget(
|
||||||
|
new QLabel(obs_module_text("AdvSceneSwitcher.variable.save")),
|
||||||
|
row, 0);
|
||||||
|
auto saveLayout = new QVBoxLayout;
|
||||||
|
saveLayout->addWidget(_save);
|
||||||
|
saveLayout->addWidget(_defaultValue);
|
||||||
|
saveLayout->addStretch();
|
||||||
|
layout->addLayout(saveLayout, row, 1);
|
||||||
|
++row;
|
||||||
|
layout->addWidget(_buttonbox, row, 0, 1, -1);
|
||||||
|
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VariableSettingsDialog::SaveActionChanged(int idx)
|
||||||
|
{
|
||||||
|
const Variable::SaveAction action =
|
||||||
|
static_cast<Variable::SaveAction>(idx);
|
||||||
|
_defaultValue->setVisible(action == Variable::SaveAction::SET_DEFAULT);
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VariableSettingsDialog::AskForSettings(QWidget *parent, Variable &settings)
|
||||||
|
{
|
||||||
|
VariableSettingsDialog dialog(parent, settings);
|
||||||
|
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||||
|
if (dialog.exec() != DialogCode::Accepted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings._name = dialog._name->text().toStdString();
|
||||||
|
settings._value = dialog._value->text().toStdString();
|
||||||
|
settings._defaultValue = dialog._defaultValue->text().toStdString();
|
||||||
|
settings._saveAction =
|
||||||
|
static_cast<Variable::SaveAction>(dialog._save->currentIndex());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
|
||||||
|
{
|
||||||
|
Variable &VariableSettings = dynamic_cast<Variable &>(settings);
|
||||||
|
return VariableSettingsDialog::AskForSettings(parent, VariableSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableSelection::VariableSelection(QWidget *parent)
|
||||||
|
: ItemSelection(switcher->variables, Variable::Create,
|
||||||
|
AskForSettingsWrapper,
|
||||||
|
"AdvSceneSwitcher.variable.select",
|
||||||
|
"AdvSceneSwitcher.variable.add", parent)
|
||||||
|
{
|
||||||
|
// Connect to slots
|
||||||
|
QWidget::connect(
|
||||||
|
window(),
|
||||||
|
SIGNAL(VariableRenamed(const QString &, const QString &)), this,
|
||||||
|
SLOT(RenameItem(const QString &, const QString &)));
|
||||||
|
QWidget::connect(window(), SIGNAL(VariableAdded(const QString &)), this,
|
||||||
|
SLOT(AddItem(const QString &)));
|
||||||
|
QWidget::connect(window(), SIGNAL(VariableRemoved(const QString &)),
|
||||||
|
this, SLOT(RemoveItem(const QString &)));
|
||||||
|
|
||||||
|
// Forward signals
|
||||||
|
QWidget::connect(
|
||||||
|
this, SIGNAL(ItemRenamed(const QString &, const QString &)),
|
||||||
|
window(),
|
||||||
|
SIGNAL(VariableRenamed(const QString &, const QString &)));
|
||||||
|
QWidget::connect(this, SIGNAL(ItemAdded(const QString &)), window(),
|
||||||
|
SIGNAL(VariableAdded(const QString &)));
|
||||||
|
QWidget::connect(this, SIGNAL(ItemRemoved(const QString &)), window(),
|
||||||
|
SIGNAL(VariableRemoved(const QString &)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VariableSelection::SetVariable(const std::string &variable)
|
||||||
|
{
|
||||||
|
const QSignalBlocker blocker(_selection);
|
||||||
|
if (!!GetVariableByName(variable)) {
|
||||||
|
_selection->setCurrentText(QString::fromStdString(variable));
|
||||||
|
} else {
|
||||||
|
_selection->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/utils/variable.hpp
Normal file
68
src/utils/variable.hpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "item-selection-helpers.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <obs.hpp>
|
||||||
|
|
||||||
|
class VariableSelection;
|
||||||
|
class VariableSettingsDialog;
|
||||||
|
|
||||||
|
class Variable : public Item {
|
||||||
|
public:
|
||||||
|
Variable();
|
||||||
|
void Load(obs_data_t *obj);
|
||||||
|
void Save(obs_data_t *obj) const;
|
||||||
|
std::string Value() const { return _value; }
|
||||||
|
bool DoubleValue(double &) const;
|
||||||
|
void SetValue(const std::string &val) { _value = val; }
|
||||||
|
void SetValue(double);
|
||||||
|
static std::shared_ptr<Item> Create()
|
||||||
|
{
|
||||||
|
return std::make_shared<Variable>();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SaveAction {
|
||||||
|
DONT_SAVE,
|
||||||
|
SAVE,
|
||||||
|
SET_DEFAULT,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
SaveAction _saveAction = SaveAction::DONT_SAVE;
|
||||||
|
std::string _value = "";
|
||||||
|
std::string _defaultValue = "";
|
||||||
|
|
||||||
|
friend VariableSelection;
|
||||||
|
friend VariableSettingsDialog;
|
||||||
|
};
|
||||||
|
|
||||||
|
Variable *GetVariableByName(const std::string &name);
|
||||||
|
Variable *GetVariableByQString(const QString &name);
|
||||||
|
std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name);
|
||||||
|
std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name);
|
||||||
|
QStringList GetVariablesNameList();
|
||||||
|
|
||||||
|
class VariableSettingsDialog : public ItemSettingsDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
VariableSettingsDialog(QWidget *parent, const Variable &);
|
||||||
|
static bool AskForSettings(QWidget *parent, Variable &settings);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void SaveActionChanged(int);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLineEdit *_value;
|
||||||
|
QLineEdit *_defaultValue;
|
||||||
|
QComboBox *_save;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VariableSelection : public ItemSelection {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
VariableSelection(QWidget *parent = 0);
|
||||||
|
void SetVariable(const std::string &);
|
||||||
|
};
|
||||||
@@ -5,23 +5,24 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QStyle>
|
||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
#include <QSlider>
|
|
||||||
|
|
||||||
#include <util/platform.h>
|
#include <util/platform.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
|
#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
|
||||||
#define FADER_PRECISION 4096.0
|
#define FADER_PRECISION 100
|
||||||
|
|
||||||
|
// Size of the audio indicator in pixels
|
||||||
|
#define INDICATOR_THICKNESS 3
|
||||||
|
|
||||||
|
// Padding on top and bottom of vertical meters
|
||||||
|
#define METER_PADDING 1
|
||||||
|
|
||||||
QWeakPointer<VolumeMeterTimer> VolumeMeter::updateTimer;
|
QWeakPointer<VolumeMeterTimer> VolumeMeter::updateTimer;
|
||||||
|
|
||||||
void VolControl::OBSVolumeChanged(void *data, float db)
|
void VolControl::OBSVolumeChanged(void *, float) {}
|
||||||
{
|
|
||||||
Q_UNUSED(db);
|
|
||||||
Q_UNUSED(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VolControl::OBSVolumeLevel(void *data,
|
void VolControl::OBSVolumeLevel(void *data,
|
||||||
const float magnitude[MAX_AUDIO_CHANNELS],
|
const float magnitude[MAX_AUDIO_CHANNELS],
|
||||||
@@ -33,9 +34,19 @@ void VolControl::OBSVolumeLevel(void *data,
|
|||||||
volControl->volMeter->setLevels(magnitude, peak, inputPeak);
|
volControl->volMeter->setLevels(magnitude, peak, inputPeak);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolControl::SliderChanged(int vol)
|
void VolControl::VolumeChanged()
|
||||||
{
|
{
|
||||||
Q_UNUSED(vol);
|
slider->blockSignals(true);
|
||||||
|
slider->setValue(
|
||||||
|
(int)(obs_fader_get_deflection(obs_fader) * FADER_PRECISION));
|
||||||
|
slider->blockSignals(false);
|
||||||
|
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolControl::SliderChanged(int)
|
||||||
|
{
|
||||||
|
float prev = obs_source_get_volume(source);
|
||||||
updateText();
|
updateText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +72,12 @@ void VolControl::SetName(const QString &newName)
|
|||||||
{
|
{
|
||||||
nameLabel->setText(newName);
|
nameLabel->setText(newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VolControl::EmitConfigClicked()
|
||||||
|
{
|
||||||
|
emit ConfigClicked();
|
||||||
|
}
|
||||||
|
|
||||||
void VolControl::SetMeterDecayRate(qreal q)
|
void VolControl::SetMeterDecayRate(qreal q)
|
||||||
{
|
{
|
||||||
volMeter->setPeakDecayRate(q);
|
volMeter->setPeakDecayRate(q);
|
||||||
@@ -71,23 +88,46 @@ void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
|
|||||||
volMeter->setPeakMeterType(peakMeterType);
|
volMeter->setPeakMeterType(peakMeterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
VolControl::VolControl(OBSSource source_, bool vertical)
|
VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent)
|
||||||
|
: QSlider(parent)
|
||||||
|
{
|
||||||
|
fad = fader;
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
|
||||||
|
QWidget *parent)
|
||||||
|
: QSlider(orientation, parent)
|
||||||
|
{
|
||||||
|
fad = fader;
|
||||||
|
}
|
||||||
|
|
||||||
|
VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
||||||
: source(std::move(source_)),
|
: source(std::move(source_)),
|
||||||
levelTotal(0.0f),
|
levelTotal(0.0f),
|
||||||
levelCount(0.0f),
|
levelCount(0.0f),
|
||||||
obs_fader(obs_fader_create(OBS_FADER_LOG)),
|
obs_fader(obs_fader_create(OBS_FADER_LOG)),
|
||||||
obs_volmeter(obs_volmeter_create(OBS_FADER_LOG)),
|
obs_volmeter(obs_volmeter_create(OBS_FADER_LOG)),
|
||||||
vertical(vertical)
|
vertical(vertical),
|
||||||
|
contextMenu(nullptr)
|
||||||
{
|
{
|
||||||
nameLabel = new QLabel();
|
nameLabel = new QLabel();
|
||||||
volLabel = new QLabel();
|
volLabel = new QLabel();
|
||||||
|
|
||||||
QString sourceName = "";
|
QString sourceName = obs_source_get_name(source);
|
||||||
if (source) {
|
|
||||||
obs_source_get_name(source);
|
|
||||||
}
|
|
||||||
setObjectName(sourceName);
|
setObjectName(sourceName);
|
||||||
|
|
||||||
|
if (showConfig) {
|
||||||
|
config = new QPushButton(this);
|
||||||
|
config->setProperty("themeID", "menuIconSmall");
|
||||||
|
config->setSizePolicy(QSizePolicy::Maximum,
|
||||||
|
QSizePolicy::Maximum);
|
||||||
|
config->setMaximumSize(22, 22);
|
||||||
|
config->setAutoDefault(false);
|
||||||
|
|
||||||
|
connect(config, &QAbstractButton::clicked, this,
|
||||||
|
&VolControl::EmitConfigClicked);
|
||||||
|
}
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
mainLayout->setContentsMargins(4, 4, 4, 4);
|
mainLayout->setContentsMargins(4, 4, 4, 4);
|
||||||
mainLayout->setSpacing(2);
|
mainLayout->setSpacing(2);
|
||||||
@@ -99,7 +139,7 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||||||
QHBoxLayout *meterLayout = new QHBoxLayout;
|
QHBoxLayout *meterLayout = new QHBoxLayout;
|
||||||
|
|
||||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, true);
|
volMeter = new VolumeMeter(nullptr, obs_volmeter, true);
|
||||||
slider = new QSlider(Qt::Vertical);
|
slider = new VolumeSlider(obs_fader, Qt::Vertical);
|
||||||
|
|
||||||
nameLayout->setAlignment(Qt::AlignCenter);
|
nameLayout->setAlignment(Qt::AlignCenter);
|
||||||
meterLayout->setAlignment(Qt::AlignCenter);
|
meterLayout->setAlignment(Qt::AlignCenter);
|
||||||
@@ -113,7 +153,11 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||||||
controlLayout->setContentsMargins(0, 0, 0, 0);
|
controlLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
controlLayout->setSpacing(0);
|
controlLayout->setSpacing(0);
|
||||||
|
|
||||||
|
if (showConfig)
|
||||||
|
controlLayout->addWidget(config);
|
||||||
|
|
||||||
controlLayout->addItem(new QSpacerItem(3, 0));
|
controlLayout->addItem(new QSpacerItem(3, 0));
|
||||||
|
// Add Headphone (audio monitoring) widget here
|
||||||
|
|
||||||
meterLayout->setContentsMargins(0, 0, 0, 0);
|
meterLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
meterLayout->setSpacing(0);
|
meterLayout->setSpacing(0);
|
||||||
@@ -131,6 +175,12 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||||||
|
|
||||||
volMeter->setFocusProxy(slider);
|
volMeter->setFocusProxy(slider);
|
||||||
|
|
||||||
|
// Default size can cause clipping of long names in vertical layout.
|
||||||
|
QFont font = nameLabel->font();
|
||||||
|
QFontInfo info(font);
|
||||||
|
font.setPointSizeF(0.8 * info.pointSizeF());
|
||||||
|
nameLabel->setFont(font);
|
||||||
|
|
||||||
setMaximumWidth(110);
|
setMaximumWidth(110);
|
||||||
} else {
|
} else {
|
||||||
QHBoxLayout *volLayout = new QHBoxLayout;
|
QHBoxLayout *volLayout = new QHBoxLayout;
|
||||||
@@ -138,7 +188,7 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||||||
QHBoxLayout *botLayout = new QHBoxLayout;
|
QHBoxLayout *botLayout = new QHBoxLayout;
|
||||||
|
|
||||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, false);
|
volMeter = new VolumeMeter(nullptr, obs_volmeter, false);
|
||||||
slider = new QSlider(Qt::Horizontal);
|
slider = new VolumeSlider(obs_fader, Qt::Horizontal);
|
||||||
|
|
||||||
textLayout->setContentsMargins(0, 0, 0, 0);
|
textLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
textLayout->addWidget(nameLabel);
|
textLayout->addWidget(nameLabel);
|
||||||
@@ -153,6 +203,9 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||||||
botLayout->setSpacing(0);
|
botLayout->setSpacing(0);
|
||||||
botLayout->addLayout(volLayout);
|
botLayout->addLayout(volLayout);
|
||||||
|
|
||||||
|
if (showConfig)
|
||||||
|
botLayout->addWidget(config);
|
||||||
|
|
||||||
mainLayout->addItem(textLayout);
|
mainLayout->addItem(textLayout);
|
||||||
mainLayout->addWidget(volMeter);
|
mainLayout->addWidget(volMeter);
|
||||||
mainLayout->addItem(botLayout);
|
mainLayout->addItem(botLayout);
|
||||||
@@ -162,15 +215,10 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||||||
|
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
QFont font = nameLabel->font();
|
|
||||||
font.setPointSize(font.pointSize() - 1);
|
|
||||||
|
|
||||||
nameLabel->setText(sourceName);
|
nameLabel->setText(sourceName);
|
||||||
nameLabel->setFont(font);
|
|
||||||
volLabel->setFont(font);
|
|
||||||
|
|
||||||
slider->setMinimum(0);
|
slider->setMinimum(0);
|
||||||
slider->setMaximum(100);
|
slider->setMaximum(int(FADER_PRECISION));
|
||||||
|
|
||||||
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
|
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
|
||||||
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
|
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
|
||||||
@@ -180,6 +228,9 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||||||
|
|
||||||
obs_fader_attach_source(obs_fader, source);
|
obs_fader_attach_source(obs_fader, source);
|
||||||
obs_volmeter_attach_source(obs_volmeter, source);
|
obs_volmeter_attach_source(obs_volmeter, source);
|
||||||
|
|
||||||
|
/* Call volume changed once to init the slider position and label */
|
||||||
|
VolumeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolControl::EnableSlider(bool enable)
|
void VolControl::EnableSlider(bool enable)
|
||||||
@@ -187,11 +238,6 @@ void VolControl::EnableSlider(bool enable)
|
|||||||
slider->setEnabled(enable);
|
slider->setEnabled(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSlider *VolControl::GetSlider() const
|
|
||||||
{
|
|
||||||
return slider;
|
|
||||||
}
|
|
||||||
|
|
||||||
VolControl::~VolControl()
|
VolControl::~VolControl()
|
||||||
{
|
{
|
||||||
obs_fader_remove_callback(obs_fader, OBSVolumeChanged, this);
|
obs_fader_remove_callback(obs_fader, OBSVolumeChanged, this);
|
||||||
@@ -199,66 +245,143 @@ VolControl::~VolControl()
|
|||||||
|
|
||||||
obs_fader_destroy(obs_fader);
|
obs_fader_destroy(obs_fader);
|
||||||
obs_volmeter_destroy(obs_volmeter);
|
obs_volmeter_destroy(obs_volmeter);
|
||||||
|
if (contextMenu)
|
||||||
|
contextMenu->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QColor color_from_int(long long val)
|
||||||
|
{
|
||||||
|
QColor color(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff,
|
||||||
|
(val >> 24) & 0xff);
|
||||||
|
color.setAlpha(255);
|
||||||
|
|
||||||
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor VolumeMeter::getBackgroundNominalColor() const
|
QColor VolumeMeter::getBackgroundNominalColor() const
|
||||||
{
|
{
|
||||||
return backgroundNominalColor;
|
return p_backgroundNominalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor VolumeMeter::getBackgroundNominalColorDisabled() const
|
||||||
|
{
|
||||||
|
return backgroundNominalColorDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeMeter::setBackgroundNominalColor(QColor c)
|
void VolumeMeter::setBackgroundNominalColor(QColor c)
|
||||||
{
|
{
|
||||||
backgroundNominalColor = std::move(c);
|
p_backgroundNominalColor = std::move(c);
|
||||||
|
backgroundNominalColor = p_backgroundNominalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::setBackgroundNominalColorDisabled(QColor c)
|
||||||
|
{
|
||||||
|
backgroundNominalColorDisabled = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor VolumeMeter::getBackgroundWarningColor() const
|
QColor VolumeMeter::getBackgroundWarningColor() const
|
||||||
{
|
{
|
||||||
return backgroundWarningColor;
|
return p_backgroundWarningColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor VolumeMeter::getBackgroundWarningColorDisabled() const
|
||||||
|
{
|
||||||
|
return backgroundWarningColorDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeMeter::setBackgroundWarningColor(QColor c)
|
void VolumeMeter::setBackgroundWarningColor(QColor c)
|
||||||
{
|
{
|
||||||
backgroundWarningColor = std::move(c);
|
p_backgroundWarningColor = std::move(c);
|
||||||
|
backgroundWarningColor = p_backgroundWarningColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::setBackgroundWarningColorDisabled(QColor c)
|
||||||
|
{
|
||||||
|
backgroundWarningColorDisabled = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor VolumeMeter::getBackgroundErrorColor() const
|
QColor VolumeMeter::getBackgroundErrorColor() const
|
||||||
{
|
{
|
||||||
return backgroundErrorColor;
|
return p_backgroundErrorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor VolumeMeter::getBackgroundErrorColorDisabled() const
|
||||||
|
{
|
||||||
|
return backgroundErrorColorDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeMeter::setBackgroundErrorColor(QColor c)
|
void VolumeMeter::setBackgroundErrorColor(QColor c)
|
||||||
{
|
{
|
||||||
backgroundErrorColor = std::move(c);
|
p_backgroundErrorColor = std::move(c);
|
||||||
|
backgroundErrorColor = p_backgroundErrorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::setBackgroundErrorColorDisabled(QColor c)
|
||||||
|
{
|
||||||
|
backgroundErrorColorDisabled = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor VolumeMeter::getForegroundNominalColor() const
|
QColor VolumeMeter::getForegroundNominalColor() const
|
||||||
{
|
{
|
||||||
return foregroundNominalColor;
|
return p_foregroundNominalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor VolumeMeter::getForegroundNominalColorDisabled() const
|
||||||
|
{
|
||||||
|
return foregroundNominalColorDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeMeter::setForegroundNominalColor(QColor c)
|
void VolumeMeter::setForegroundNominalColor(QColor c)
|
||||||
{
|
{
|
||||||
foregroundNominalColor = std::move(c);
|
p_foregroundNominalColor = std::move(c);
|
||||||
|
foregroundNominalColor = p_foregroundNominalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::setForegroundNominalColorDisabled(QColor c)
|
||||||
|
{
|
||||||
|
foregroundNominalColorDisabled = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor VolumeMeter::getForegroundWarningColor() const
|
QColor VolumeMeter::getForegroundWarningColor() const
|
||||||
{
|
{
|
||||||
return foregroundWarningColor;
|
return p_foregroundWarningColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor VolumeMeter::getForegroundWarningColorDisabled() const
|
||||||
|
{
|
||||||
|
return foregroundWarningColorDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeMeter::setForegroundWarningColor(QColor c)
|
void VolumeMeter::setForegroundWarningColor(QColor c)
|
||||||
{
|
{
|
||||||
foregroundWarningColor = std::move(c);
|
p_foregroundWarningColor = std::move(c);
|
||||||
|
foregroundWarningColor = p_foregroundWarningColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::setForegroundWarningColorDisabled(QColor c)
|
||||||
|
{
|
||||||
|
foregroundWarningColorDisabled = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor VolumeMeter::getForegroundErrorColor() const
|
QColor VolumeMeter::getForegroundErrorColor() const
|
||||||
{
|
{
|
||||||
return foregroundErrorColor;
|
return p_foregroundErrorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor VolumeMeter::getForegroundErrorColorDisabled() const
|
||||||
|
{
|
||||||
|
return foregroundErrorColorDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeMeter::setForegroundErrorColor(QColor c)
|
void VolumeMeter::setForegroundErrorColor(QColor c)
|
||||||
{
|
{
|
||||||
foregroundErrorColor = std::move(c);
|
p_foregroundErrorColor = std::move(c);
|
||||||
|
foregroundErrorColor = p_foregroundErrorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::setForegroundErrorColorDisabled(QColor c)
|
||||||
|
{
|
||||||
|
foregroundErrorColorDisabled = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor VolumeMeter::getClipColor() const
|
QColor VolumeMeter::getClipColor() const
|
||||||
@@ -301,6 +424,42 @@ void VolumeMeter::setMinorTickColor(QColor c)
|
|||||||
minorTickColor = std::move(c);
|
minorTickColor = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VolumeMeter::getMeterThickness() const
|
||||||
|
{
|
||||||
|
return meterThickness;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::setMeterThickness(int v)
|
||||||
|
{
|
||||||
|
meterThickness = v;
|
||||||
|
recalculateLayout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal VolumeMeter::getMeterFontScaling() const
|
||||||
|
{
|
||||||
|
return meterFontScaling;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::setMeterFontScaling(qreal v)
|
||||||
|
{
|
||||||
|
meterFontScaling = v;
|
||||||
|
recalculateLayout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolControl::refreshColors()
|
||||||
|
{
|
||||||
|
volMeter->setBackgroundNominalColor(
|
||||||
|
volMeter->getBackgroundNominalColor());
|
||||||
|
volMeter->setBackgroundWarningColor(
|
||||||
|
volMeter->getBackgroundWarningColor());
|
||||||
|
volMeter->setBackgroundErrorColor(volMeter->getBackgroundErrorColor());
|
||||||
|
volMeter->setForegroundNominalColor(
|
||||||
|
volMeter->getForegroundNominalColor());
|
||||||
|
volMeter->setForegroundWarningColor(
|
||||||
|
volMeter->getForegroundWarningColor());
|
||||||
|
volMeter->setForegroundErrorColor(volMeter->getForegroundErrorColor());
|
||||||
|
}
|
||||||
|
|
||||||
qreal VolumeMeter::getMinimumLevel() const
|
qreal VolumeMeter::getMinimumLevel() const
|
||||||
{
|
{
|
||||||
return minimumLevel;
|
return minimumLevel;
|
||||||
@@ -434,10 +593,9 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
|
|||||||
bool vertical)
|
bool vertical)
|
||||||
: QWidget(parent), obs_volmeter(obs_volmeter), vertical(vertical)
|
: QWidget(parent), obs_volmeter(obs_volmeter), vertical(vertical)
|
||||||
{
|
{
|
||||||
// Use a font that can be rendered small.
|
setAttribute(Qt::WA_OpaquePaintEvent, true);
|
||||||
tickFont = QFont("Arial");
|
|
||||||
tickFont.setPixelSize(7);
|
// Default meter settings, they only show if
|
||||||
// Default meter color settings, they only show if
|
|
||||||
// there is no stylesheet, do not remove.
|
// there is no stylesheet, do not remove.
|
||||||
backgroundNominalColor.setRgb(0x26, 0x7f, 0x26); // Dark green
|
backgroundNominalColor.setRgb(0x26, 0x7f, 0x26); // Dark green
|
||||||
backgroundWarningColor.setRgb(0x7f, 0x7f, 0x26); // Dark yellow
|
backgroundWarningColor.setRgb(0x7f, 0x7f, 0x26); // Dark yellow
|
||||||
@@ -445,27 +603,38 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
|
|||||||
foregroundNominalColor.setRgb(0x4c, 0xff, 0x4c); // Bright green
|
foregroundNominalColor.setRgb(0x4c, 0xff, 0x4c); // Bright green
|
||||||
foregroundWarningColor.setRgb(0xff, 0xff, 0x4c); // Bright yellow
|
foregroundWarningColor.setRgb(0xff, 0xff, 0x4c); // Bright yellow
|
||||||
foregroundErrorColor.setRgb(0xff, 0x4c, 0x4c); // Bright red
|
foregroundErrorColor.setRgb(0xff, 0x4c, 0x4c); // Bright red
|
||||||
clipColor.setRgb(0xff, 0xff, 0xff); // Bright white
|
|
||||||
magnitudeColor.setRgb(0x00, 0x00, 0x00); // Black
|
|
||||||
majorTickColor.setRgb(0xff, 0xff, 0xff); // Black
|
|
||||||
minorTickColor.setRgb(0xcc, 0xcc, 0xcc); // Black
|
|
||||||
minimumLevel = -60.0; // -60 dB
|
|
||||||
warningLevel = -20.0; // -20 dB
|
|
||||||
errorLevel = -9.0; // -9 dB
|
|
||||||
clipLevel = -0.5; // -0.5 dB
|
|
||||||
minimumInputLevel = -50.0; // -50 dB
|
|
||||||
peakDecayRate = 11.76; // 20 dB / 1.7 sec
|
|
||||||
magnitudeIntegrationTime = 0.3; // 99% in 300 ms
|
|
||||||
peakHoldDuration = 20.0; // 20 seconds
|
|
||||||
inputPeakHoldDuration = 1.0; // 1 second
|
|
||||||
|
|
||||||
|
backgroundNominalColorDisabled.setRgb(90, 90, 90);
|
||||||
|
backgroundWarningColorDisabled.setRgb(117, 117, 117);
|
||||||
|
backgroundErrorColorDisabled.setRgb(65, 65, 65);
|
||||||
|
foregroundNominalColorDisabled.setRgb(163, 163, 163);
|
||||||
|
foregroundWarningColorDisabled.setRgb(217, 217, 217);
|
||||||
|
foregroundErrorColorDisabled.setRgb(113, 113, 113);
|
||||||
|
|
||||||
|
clipColor.setRgb(0xff, 0xff, 0xff); // Bright white
|
||||||
|
magnitudeColor.setRgb(0x00, 0x00, 0x00); // Black
|
||||||
|
majorTickColor.setRgb(0xff, 0xff, 0xff); // Black
|
||||||
|
minorTickColor.setRgb(0xcc, 0xcc, 0xcc); // Black
|
||||||
|
minimumLevel = -60.0; // -60 dB
|
||||||
|
warningLevel = -20.0; // -20 dB
|
||||||
|
errorLevel = -9.0; // -9 dB
|
||||||
|
clipLevel = -0.5; // -0.5 dB
|
||||||
|
minimumInputLevel = -50.0; // -50 dB
|
||||||
|
peakDecayRate = 11.76; // 20 dB / 1.7 sec
|
||||||
|
magnitudeIntegrationTime = 0.3; // 99% in 300 ms
|
||||||
|
peakHoldDuration = 20.0; // 20 seconds
|
||||||
|
inputPeakHoldDuration = 1.0; // 1 second
|
||||||
|
meterThickness = 3; // Bar thickness in pixels
|
||||||
|
meterFontScaling =
|
||||||
|
0.7; // Font size for numbers is 70% of Widget's font size
|
||||||
channels = (int)audio_output_get_channels(obs_get_audio());
|
channels = (int)audio_output_get_channels(obs_get_audio());
|
||||||
|
|
||||||
handleChannelCofigurationChange();
|
doLayout();
|
||||||
updateTimerRef = updateTimer.toStrongRef();
|
updateTimerRef = updateTimer.toStrongRef();
|
||||||
if (!updateTimerRef) {
|
if (!updateTimerRef) {
|
||||||
updateTimerRef = QSharedPointer<VolumeMeterTimer>::create();
|
updateTimerRef = QSharedPointer<VolumeMeterTimer>::create();
|
||||||
updateTimerRef->start(34);
|
updateTimerRef->setTimerType(Qt::PreciseTimer);
|
||||||
|
updateTimerRef->start(16);
|
||||||
updateTimer = updateTimerRef;
|
updateTimer = updateTimerRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +644,6 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
|
|||||||
VolumeMeter::~VolumeMeter()
|
VolumeMeter::~VolumeMeter()
|
||||||
{
|
{
|
||||||
updateTimerRef->RemoveVolControl(this);
|
updateTimerRef->RemoveVolControl(this);
|
||||||
delete tickPaintCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeMeter::setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
|
void VolumeMeter::setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
|
||||||
@@ -515,23 +683,58 @@ inline void VolumeMeter::resetLevels()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void VolumeMeter::handleChannelCofigurationChange()
|
bool VolumeMeter::needLayoutChange()
|
||||||
|
{
|
||||||
|
int currentNrAudioChannels = obs_volmeter_get_nr_channels(obs_volmeter);
|
||||||
|
|
||||||
|
if (!currentNrAudioChannels) {
|
||||||
|
struct obs_audio_info oai;
|
||||||
|
obs_get_audio_info(&oai);
|
||||||
|
currentNrAudioChannels = (oai.speakers == SPEAKERS_MONO) ? 1
|
||||||
|
: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayNrAudioChannels != currentNrAudioChannels) {
|
||||||
|
displayNrAudioChannels = currentNrAudioChannels;
|
||||||
|
recalculateLayout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return recalculateLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When this is called from the constructor, obs_volmeter_get_nr_channels has not
|
||||||
|
// yet been called and Q_PROPERTY settings have not yet been read from the
|
||||||
|
// stylesheet.
|
||||||
|
inline void VolumeMeter::doLayout()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&dataMutex);
|
QMutexLocker locker(&dataMutex);
|
||||||
|
|
||||||
int currentNrAudioChannels = obs_volmeter_get_nr_channels(obs_volmeter);
|
recalculateLayout = false;
|
||||||
if (displayNrAudioChannels != currentNrAudioChannels) {
|
|
||||||
displayNrAudioChannels = currentNrAudioChannels;
|
|
||||||
|
|
||||||
// Make room for 3 pixels meter, with one pixel between each.
|
tickFont = font();
|
||||||
// Then 9/13 pixels for ticks and numbers.
|
QFontInfo info(tickFont);
|
||||||
if (vertical)
|
tickFont.setPointSizeF(info.pointSizeF() * meterFontScaling);
|
||||||
setMinimumSize(displayNrAudioChannels * 4 + 14, 130);
|
QFontMetrics metrics(tickFont);
|
||||||
else
|
if (vertical) {
|
||||||
setMinimumSize(130, displayNrAudioChannels * 4 + 8);
|
// Each meter channel is meterThickness pixels wide, plus one pixel
|
||||||
|
// between channels, but not after the last.
|
||||||
resetLevels();
|
// Add 4 pixels for ticks, space to hold our longest label in this font,
|
||||||
|
// and a few pixels before the fader.
|
||||||
|
QRect scaleBounds = metrics.boundingRect("-88");
|
||||||
|
setMinimumSize(displayNrAudioChannels * (meterThickness + 1) -
|
||||||
|
1 + 4 + scaleBounds.width() + 2,
|
||||||
|
130);
|
||||||
|
} else {
|
||||||
|
// Each meter channel is meterThickness pixels high, plus one pixel
|
||||||
|
// between channels, but not after the last.
|
||||||
|
// Add 4 pixels for ticks, and space high enough to hold our label in
|
||||||
|
// this font, presuming that digits don't have descenders.
|
||||||
|
setMinimumSize(130,
|
||||||
|
displayNrAudioChannels * (meterThickness + 1) -
|
||||||
|
1 + 4 + metrics.capHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetLevels();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool VolumeMeter::detectIdle(uint64_t ts)
|
inline bool VolumeMeter::detectIdle(uint64_t ts)
|
||||||
@@ -649,12 +852,12 @@ void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y, int width,
|
|||||||
painter.fillRect(x, y, width, height, color);
|
painter.fillRect(x, y, width, height, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width,
|
void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width)
|
||||||
int height)
|
|
||||||
{
|
{
|
||||||
qreal scale = width / minimumLevel;
|
qreal scale = width / minimumLevel;
|
||||||
|
|
||||||
painter.setFont(tickFont);
|
painter.setFont(tickFont);
|
||||||
|
QFontMetrics metrics(tickFont);
|
||||||
painter.setPen(majorTickColor);
|
painter.setPen(majorTickColor);
|
||||||
|
|
||||||
// Draw major tick lines and numeric indicators.
|
// Draw major tick lines and numeric indicators.
|
||||||
@@ -662,10 +865,18 @@ void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width,
|
|||||||
int position = int(x + width - (i * scale) - 1);
|
int position = int(x + width - (i * scale) - 1);
|
||||||
QString str = QString::number(i);
|
QString str = QString::number(i);
|
||||||
|
|
||||||
if (i == 0 || i == -5)
|
// Center the number on the tick, but don't overflow
|
||||||
painter.drawText(position - 3, height, str);
|
QRect textBounds = metrics.boundingRect(str);
|
||||||
else
|
int pos;
|
||||||
painter.drawText(position - 5, height, str);
|
if (i == 0) {
|
||||||
|
pos = position - textBounds.width();
|
||||||
|
} else {
|
||||||
|
pos = position - (textBounds.width() / 2);
|
||||||
|
if (pos < 0)
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
painter.drawText(pos, y + 4 + metrics.capHeight(), str);
|
||||||
|
|
||||||
painter.drawLine(position, y, position, y + 2);
|
painter.drawLine(position, y, position, y + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,26 +894,31 @@ void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height)
|
|||||||
qreal scale = height / minimumLevel;
|
qreal scale = height / minimumLevel;
|
||||||
|
|
||||||
painter.setFont(tickFont);
|
painter.setFont(tickFont);
|
||||||
|
QFontMetrics metrics(tickFont);
|
||||||
painter.setPen(majorTickColor);
|
painter.setPen(majorTickColor);
|
||||||
|
|
||||||
// Draw major tick lines and numeric indicators.
|
// Draw major tick lines and numeric indicators.
|
||||||
for (int i = 0; i >= minimumLevel; i -= 5) {
|
for (int i = 0; i >= minimumLevel; i -= 5) {
|
||||||
int position = y + int((i * scale) - 1);
|
int position = y + int(i * scale) + METER_PADDING;
|
||||||
QString str = QString::number(i);
|
QString str = QString::number(i);
|
||||||
|
|
||||||
if (i == 0)
|
// Center the number on the tick, but don't overflow
|
||||||
painter.drawText(x + 5, position + 4, str);
|
if (i == 0) {
|
||||||
else if (i == -60)
|
painter.drawText(x + 6, position + metrics.capHeight(),
|
||||||
painter.drawText(x + 4, position, str);
|
str);
|
||||||
else
|
} else {
|
||||||
painter.drawText(x + 4, position + 2, str);
|
painter.drawText(x + 4,
|
||||||
|
position + (metrics.capHeight() / 2),
|
||||||
|
str);
|
||||||
|
}
|
||||||
|
|
||||||
painter.drawLine(x, position, x + 2, position);
|
painter.drawLine(x, position, x + 2, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw minor tick lines.
|
// Draw minor tick lines.
|
||||||
painter.setPen(minorTickColor);
|
painter.setPen(minorTickColor);
|
||||||
for (int i = 0; i >= minimumLevel; i--) {
|
for (int i = 0; i >= minimumLevel; i--) {
|
||||||
int position = y + int((i * scale) - 1);
|
int position = y + int(i * scale) + METER_PADDING;
|
||||||
if (i % 5 != 0)
|
if (i % 5 != 0)
|
||||||
painter.drawLine(x, position, x + 1, position);
|
painter.drawLine(x, position, x + 1, position);
|
||||||
}
|
}
|
||||||
@@ -777,7 +993,7 @@ void VolumeMeter::paintHMeter(QPainter &painter, int x, int y, int width,
|
|||||||
painter.fillRect(peakPosition, y,
|
painter.fillRect(peakPosition, y,
|
||||||
maximumPosition - peakPosition, height,
|
maximumPosition - peakPosition, height,
|
||||||
backgroundErrorColor);
|
backgroundErrorColor);
|
||||||
} else {
|
} else if (int(magnitude) != 0) {
|
||||||
if (!clipping) {
|
if (!clipping) {
|
||||||
QTimer::singleShot(CLIP_FLASH_DURATION_MS, this,
|
QTimer::singleShot(CLIP_FLASH_DURATION_MS, this,
|
||||||
SLOT(ClipEnding()));
|
SLOT(ClipEnding()));
|
||||||
@@ -879,7 +1095,7 @@ void VolumeMeter::paintVMeter(QPainter &painter, int x, int y, int width,
|
|||||||
|
|
||||||
int end = errorLength + warningLength + nominalLength;
|
int end = errorLength + warningLength + nominalLength;
|
||||||
painter.fillRect(x, minimumPosition, width, end,
|
painter.fillRect(x, minimumPosition, width, end,
|
||||||
QBrush(foregroundErrorColor));
|
foregroundErrorColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (peakHoldPosition - 3 < minimumPosition)
|
if (peakHoldPosition - 3 < minimumPosition)
|
||||||
@@ -903,53 +1119,48 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
|
|||||||
{
|
{
|
||||||
uint64_t ts = os_gettime_ns();
|
uint64_t ts = os_gettime_ns();
|
||||||
qreal timeSinceLastRedraw = (ts - lastRedrawTime) * 0.000000001;
|
qreal timeSinceLastRedraw = (ts - lastRedrawTime) * 0.000000001;
|
||||||
|
|
||||||
const QRect rect = event->region().boundingRect();
|
|
||||||
int width = rect.width();
|
|
||||||
int height = rect.height();
|
|
||||||
|
|
||||||
handleChannelCofigurationChange();
|
|
||||||
calculateBallistics(ts, timeSinceLastRedraw);
|
calculateBallistics(ts, timeSinceLastRedraw);
|
||||||
bool idle = detectIdle(ts);
|
bool idle = detectIdle(ts);
|
||||||
|
|
||||||
// Draw the ticks in a off-screen buffer when the widget changes size.
|
QRect widgetRect = rect();
|
||||||
QSize tickPaintCacheSize;
|
int width = widgetRect.width();
|
||||||
|
int height = widgetRect.height();
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
|
||||||
if (vertical)
|
if (vertical)
|
||||||
tickPaintCacheSize = QSize(14, height);
|
height -= METER_PADDING * 2;
|
||||||
else
|
|
||||||
tickPaintCacheSize = QSize(width, 9);
|
|
||||||
if (tickPaintCache == nullptr ||
|
|
||||||
tickPaintCache->size() != tickPaintCacheSize) {
|
|
||||||
delete tickPaintCache;
|
|
||||||
tickPaintCache = new QPixmap(tickPaintCacheSize);
|
|
||||||
|
|
||||||
QColor clearColor(0, 0, 0, 0);
|
// timerEvent requests update of the bar(s) only, so we can avoid the
|
||||||
tickPaintCache->fill(clearColor);
|
// overhead of repainting the scale and labels.
|
||||||
|
if (event->region().boundingRect() != getBarRect()) {
|
||||||
|
if (needLayoutChange())
|
||||||
|
doLayout();
|
||||||
|
|
||||||
|
// Paint window background color (as widget is opaque)
|
||||||
|
QColor background =
|
||||||
|
palette().color(QPalette::ColorRole::Window);
|
||||||
|
painter.fillRect(widgetRect, background);
|
||||||
|
|
||||||
QPainter tickPainter(tickPaintCache);
|
|
||||||
if (vertical) {
|
if (vertical) {
|
||||||
tickPainter.translate(0, height);
|
paintVTicks(painter,
|
||||||
tickPainter.scale(1, -1);
|
displayNrAudioChannels *
|
||||||
paintVTicks(tickPainter, 0, 11,
|
(meterThickness + 1) -
|
||||||
tickPaintCacheSize.height() - 11);
|
1,
|
||||||
|
0, height - (INDICATOR_THICKNESS + 3));
|
||||||
} else {
|
} else {
|
||||||
paintHTicks(tickPainter, 6, 0,
|
paintHTicks(painter, INDICATOR_THICKNESS + 3,
|
||||||
tickPaintCacheSize.width() - 6,
|
displayNrAudioChannels *
|
||||||
tickPaintCacheSize.height());
|
(meterThickness + 1) -
|
||||||
|
1,
|
||||||
|
width - (INDICATOR_THICKNESS + 3));
|
||||||
}
|
}
|
||||||
tickPainter.end();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual painting of the widget starts here.
|
|
||||||
QPainter painter(this);
|
|
||||||
if (vertical) {
|
if (vertical) {
|
||||||
// Invert the Y axis to ease the math
|
// Invert the Y axis to ease the math
|
||||||
painter.translate(0, height);
|
painter.translate(0, height + METER_PADDING);
|
||||||
painter.scale(1, -1);
|
painter.scale(1, -1);
|
||||||
painter.drawPixmap(displayNrAudioChannels * 4 - 1, 7,
|
|
||||||
*tickPaintCache);
|
|
||||||
} else {
|
|
||||||
painter.drawPixmap(0, height - 9, *tickPaintCache);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int channelNr = 0; channelNr < displayNrAudioChannels;
|
for (int channelNr = 0; channelNr < displayNrAudioChannels;
|
||||||
@@ -961,12 +1172,17 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
|
|||||||
: channelNr;
|
: channelNr;
|
||||||
|
|
||||||
if (vertical)
|
if (vertical)
|
||||||
paintVMeter(painter, channelNr * 4, 8, 3, height - 10,
|
paintVMeter(painter, channelNr * (meterThickness + 1),
|
||||||
|
INDICATOR_THICKNESS + 2, meterThickness,
|
||||||
|
height - (INDICATOR_THICKNESS + 2),
|
||||||
displayMagnitude[channelNrFixed],
|
displayMagnitude[channelNrFixed],
|
||||||
displayPeak[channelNrFixed],
|
displayPeak[channelNrFixed],
|
||||||
displayPeakHold[channelNrFixed]);
|
displayPeakHold[channelNrFixed]);
|
||||||
else
|
else
|
||||||
paintHMeter(painter, 5, channelNr * 4, width - 5, 3,
|
paintHMeter(painter, INDICATOR_THICKNESS + 2,
|
||||||
|
channelNr * (meterThickness + 1),
|
||||||
|
width - (INDICATOR_THICKNESS + 2),
|
||||||
|
meterThickness,
|
||||||
displayMagnitude[channelNrFixed],
|
displayMagnitude[channelNrFixed],
|
||||||
displayPeak[channelNrFixed],
|
displayPeak[channelNrFixed],
|
||||||
displayPeakHold[channelNrFixed]);
|
displayPeakHold[channelNrFixed]);
|
||||||
@@ -978,16 +1194,40 @@ void VolumeMeter::paintEvent(QPaintEvent *event)
|
|||||||
// see that the audio stream has been stopped, without
|
// see that the audio stream has been stopped, without
|
||||||
// having too much visual impact.
|
// having too much visual impact.
|
||||||
if (vertical)
|
if (vertical)
|
||||||
paintInputMeter(painter, channelNr * 4, 3, 3, 3,
|
paintInputMeter(painter,
|
||||||
|
channelNr * (meterThickness + 1), 0,
|
||||||
|
meterThickness, INDICATOR_THICKNESS,
|
||||||
displayInputPeakHold[channelNrFixed]);
|
displayInputPeakHold[channelNrFixed]);
|
||||||
else
|
else
|
||||||
paintInputMeter(painter, 0, channelNr * 4, 3, 3,
|
paintInputMeter(painter, 0,
|
||||||
|
channelNr * (meterThickness + 1),
|
||||||
|
INDICATOR_THICKNESS, meterThickness,
|
||||||
displayInputPeakHold[channelNrFixed]);
|
displayInputPeakHold[channelNrFixed]);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastRedrawTime = ts;
|
lastRedrawTime = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect VolumeMeter::getBarRect() const
|
||||||
|
{
|
||||||
|
QRect rec = rect();
|
||||||
|
if (vertical)
|
||||||
|
rec.setWidth(displayNrAudioChannels * (meterThickness + 1) - 1);
|
||||||
|
else
|
||||||
|
rec.setHeight(displayNrAudioChannels * (meterThickness + 1) -
|
||||||
|
1);
|
||||||
|
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeMeter::changeEvent(QEvent *e)
|
||||||
|
{
|
||||||
|
if (e->type() == QEvent::StyleChange)
|
||||||
|
recalculateLayout = true;
|
||||||
|
|
||||||
|
QWidget::changeEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
void VolumeMeterTimer::AddVolControl(VolumeMeter *meter)
|
void VolumeMeterTimer::AddVolControl(VolumeMeter *meter)
|
||||||
{
|
{
|
||||||
volumeMeters.push_back(meter);
|
volumeMeters.push_back(meter);
|
||||||
@@ -1000,6 +1240,13 @@ void VolumeMeterTimer::RemoveVolControl(VolumeMeter *meter)
|
|||||||
|
|
||||||
void VolumeMeterTimer::timerEvent(QTimerEvent *)
|
void VolumeMeterTimer::timerEvent(QTimerEvent *)
|
||||||
{
|
{
|
||||||
for (VolumeMeter *meter : volumeMeters)
|
for (VolumeMeter *meter : volumeMeters) {
|
||||||
meter->update();
|
if (meter->needLayoutChange()) {
|
||||||
|
// Tell paintEvent to update layout and paint everything
|
||||||
|
meter->update();
|
||||||
|
} else {
|
||||||
|
// Tell paintEvent to paint only the bars
|
||||||
|
meter->update(meter->getBarRect());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QSlider>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class VolumeMeterTimer;
|
class VolumeMeterTimer;
|
||||||
@@ -25,6 +27,32 @@ class VolumeMeter : public QWidget {
|
|||||||
WRITE setForegroundWarningColor DESIGNABLE true)
|
WRITE setForegroundWarningColor DESIGNABLE true)
|
||||||
Q_PROPERTY(QColor foregroundErrorColor READ getForegroundErrorColor
|
Q_PROPERTY(QColor foregroundErrorColor READ getForegroundErrorColor
|
||||||
WRITE setForegroundErrorColor DESIGNABLE true)
|
WRITE setForegroundErrorColor DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColorDisabled READ
|
||||||
|
getBackgroundNominalColorDisabled WRITE
|
||||||
|
setBackgroundNominalColorDisabled
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColorDisabled READ
|
||||||
|
getBackgroundWarningColorDisabled WRITE
|
||||||
|
setBackgroundWarningColorDisabled
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor backgroundErrorColorDisabled READ
|
||||||
|
getBackgroundErrorColorDisabled WRITE
|
||||||
|
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColorDisabled READ
|
||||||
|
getForegroundNominalColorDisabled WRITE
|
||||||
|
setForegroundNominalColorDisabled
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColorDisabled READ
|
||||||
|
getForegroundWarningColorDisabled WRITE
|
||||||
|
setForegroundWarningColorDisabled
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor foregroundErrorColorDisabled READ
|
||||||
|
getForegroundErrorColorDisabled WRITE
|
||||||
|
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
|
||||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor
|
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor
|
||||||
DESIGNABLE true)
|
DESIGNABLE true)
|
||||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE
|
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE
|
||||||
@@ -33,6 +61,10 @@ class VolumeMeter : public QWidget {
|
|||||||
setMajorTickColor DESIGNABLE true)
|
setMajorTickColor DESIGNABLE true)
|
||||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE
|
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE
|
||||||
setMinorTickColor DESIGNABLE true)
|
setMinorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE
|
||||||
|
setMeterThickness DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE
|
||||||
|
setMeterFontScaling DESIGNABLE true)
|
||||||
|
|
||||||
// Levels are denoted in dBFS.
|
// Levels are denoted in dBFS.
|
||||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel
|
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel
|
||||||
@@ -61,6 +93,8 @@ class VolumeMeter : public QWidget {
|
|||||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration
|
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration
|
||||||
WRITE setInputPeakHoldDuration DESIGNABLE true)
|
WRITE setInputPeakHoldDuration DESIGNABLE true)
|
||||||
|
|
||||||
|
friend class VolControl;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ClipEnding();
|
void ClipEnding();
|
||||||
|
|
||||||
@@ -70,7 +104,7 @@ private:
|
|||||||
QSharedPointer<VolumeMeterTimer> updateTimerRef;
|
QSharedPointer<VolumeMeterTimer> updateTimerRef;
|
||||||
|
|
||||||
inline void resetLevels();
|
inline void resetLevels();
|
||||||
inline void handleChannelCofigurationChange();
|
inline void doLayout();
|
||||||
inline bool detectIdle(uint64_t ts);
|
inline bool detectIdle(uint64_t ts);
|
||||||
inline void calculateBallistics(uint64_t ts,
|
inline void calculateBallistics(uint64_t ts,
|
||||||
qreal timeSinceLastRedraw = 0.0);
|
qreal timeSinceLastRedraw = 0.0);
|
||||||
@@ -81,20 +115,19 @@ private:
|
|||||||
int height, float peakHold);
|
int height, float peakHold);
|
||||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height,
|
void paintHMeter(QPainter &painter, int x, int y, int width, int height,
|
||||||
float magnitude, float peak, float peakHold);
|
float magnitude, float peak, float peakHold);
|
||||||
void paintHTicks(QPainter &painter, int x, int y, int width,
|
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||||
int height);
|
|
||||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height,
|
void paintVMeter(QPainter &painter, int x, int y, int width, int height,
|
||||||
float magnitude, float peak, float peakHold);
|
float magnitude, float peak, float peakHold);
|
||||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||||
|
|
||||||
QMutex dataMutex;
|
QMutex dataMutex;
|
||||||
|
|
||||||
|
bool recalculateLayout = true;
|
||||||
uint64_t currentLastUpdateTime = 0;
|
uint64_t currentLastUpdateTime = 0;
|
||||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
QPixmap *tickPaintCache = nullptr;
|
|
||||||
int displayNrAudioChannels = 0;
|
int displayNrAudioChannels = 0;
|
||||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||||
@@ -110,10 +143,22 @@ private:
|
|||||||
QColor foregroundNominalColor;
|
QColor foregroundNominalColor;
|
||||||
QColor foregroundWarningColor;
|
QColor foregroundWarningColor;
|
||||||
QColor foregroundErrorColor;
|
QColor foregroundErrorColor;
|
||||||
|
|
||||||
|
QColor backgroundNominalColorDisabled;
|
||||||
|
QColor backgroundWarningColorDisabled;
|
||||||
|
QColor backgroundErrorColorDisabled;
|
||||||
|
QColor foregroundNominalColorDisabled;
|
||||||
|
QColor foregroundWarningColorDisabled;
|
||||||
|
QColor foregroundErrorColorDisabled;
|
||||||
|
|
||||||
QColor clipColor;
|
QColor clipColor;
|
||||||
QColor magnitudeColor;
|
QColor magnitudeColor;
|
||||||
QColor majorTickColor;
|
QColor majorTickColor;
|
||||||
QColor minorTickColor;
|
QColor minorTickColor;
|
||||||
|
|
||||||
|
int meterThickness;
|
||||||
|
qreal meterFontScaling;
|
||||||
|
|
||||||
qreal minimumLevel;
|
qreal minimumLevel;
|
||||||
qreal warningLevel;
|
qreal warningLevel;
|
||||||
qreal errorLevel;
|
qreal errorLevel;
|
||||||
@@ -124,6 +169,13 @@ private:
|
|||||||
qreal peakHoldDuration;
|
qreal peakHoldDuration;
|
||||||
qreal inputPeakHoldDuration;
|
qreal inputPeakHoldDuration;
|
||||||
|
|
||||||
|
QColor p_backgroundNominalColor;
|
||||||
|
QColor p_backgroundWarningColor;
|
||||||
|
QColor p_backgroundErrorColor;
|
||||||
|
QColor p_foregroundNominalColor;
|
||||||
|
QColor p_foregroundWarningColor;
|
||||||
|
QColor p_foregroundErrorColor;
|
||||||
|
|
||||||
uint64_t lastRedrawTime = 0;
|
uint64_t lastRedrawTime = 0;
|
||||||
int channels = 0;
|
int channels = 0;
|
||||||
bool clipping = false;
|
bool clipping = false;
|
||||||
@@ -138,6 +190,8 @@ public:
|
|||||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
|
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
|
||||||
const float peak[MAX_AUDIO_CHANNELS],
|
const float peak[MAX_AUDIO_CHANNELS],
|
||||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
QRect getBarRect() const;
|
||||||
|
bool needLayoutChange();
|
||||||
|
|
||||||
QColor getBackgroundNominalColor() const;
|
QColor getBackgroundNominalColor() const;
|
||||||
void setBackgroundNominalColor(QColor c);
|
void setBackgroundNominalColor(QColor c);
|
||||||
@@ -151,6 +205,20 @@ public:
|
|||||||
void setForegroundWarningColor(QColor c);
|
void setForegroundWarningColor(QColor c);
|
||||||
QColor getForegroundErrorColor() const;
|
QColor getForegroundErrorColor() const;
|
||||||
void setForegroundErrorColor(QColor c);
|
void setForegroundErrorColor(QColor c);
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColorDisabled() const;
|
||||||
|
void setBackgroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundWarningColorDisabled() const;
|
||||||
|
void setBackgroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundErrorColorDisabled() const;
|
||||||
|
void setBackgroundErrorColorDisabled(QColor c);
|
||||||
|
QColor getForegroundNominalColorDisabled() const;
|
||||||
|
void setForegroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getForegroundWarningColorDisabled() const;
|
||||||
|
void setForegroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getForegroundErrorColorDisabled() const;
|
||||||
|
void setForegroundErrorColorDisabled(QColor c);
|
||||||
|
|
||||||
QColor getClipColor() const;
|
QColor getClipColor() const;
|
||||||
void setClipColor(QColor c);
|
void setClipColor(QColor c);
|
||||||
QColor getMagnitudeColor() const;
|
QColor getMagnitudeColor() const;
|
||||||
@@ -159,6 +227,10 @@ public:
|
|||||||
void setMajorTickColor(QColor c);
|
void setMajorTickColor(QColor c);
|
||||||
QColor getMinorTickColor() const;
|
QColor getMinorTickColor() const;
|
||||||
void setMinorTickColor(QColor c);
|
void setMinorTickColor(QColor c);
|
||||||
|
int getMeterThickness() const;
|
||||||
|
void setMeterThickness(int v);
|
||||||
|
qreal getMeterFontScaling() const;
|
||||||
|
void setMeterFontScaling(qreal v);
|
||||||
qreal getMinimumLevel() const;
|
qreal getMinimumLevel() const;
|
||||||
void setMinimumLevel(qreal v);
|
void setMinimumLevel(qreal v);
|
||||||
qreal getWarningLevel() const;
|
qreal getWarningLevel() const;
|
||||||
@@ -182,6 +254,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void changeEvent(QEvent *e) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VolumeMeterTimer : public QTimer {
|
class VolumeMeterTimer : public QTimer {
|
||||||
@@ -200,7 +273,6 @@ protected:
|
|||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QSlider;
|
class QSlider;
|
||||||
class MuteCheckBox;
|
|
||||||
|
|
||||||
class VolControl : public QWidget {
|
class VolControl : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -211,11 +283,13 @@ private:
|
|||||||
QLabel *volLabel;
|
QLabel *volLabel;
|
||||||
VolumeMeter *volMeter;
|
VolumeMeter *volMeter;
|
||||||
QSlider *slider;
|
QSlider *slider;
|
||||||
|
QPushButton *config = nullptr;
|
||||||
float levelTotal;
|
float levelTotal;
|
||||||
float levelCount;
|
float levelCount;
|
||||||
obs_fader_t *obs_fader;
|
obs_fader_t *obs_fader;
|
||||||
obs_volmeter_t *obs_volmeter;
|
obs_volmeter_t *obs_volmeter;
|
||||||
bool vertical;
|
bool vertical;
|
||||||
|
QMenu *contextMenu;
|
||||||
|
|
||||||
static void OBSVolumeChanged(void *param, float db);
|
static void OBSVolumeChanged(void *param, float db);
|
||||||
static void OBSVolumeLevel(void *data,
|
static void OBSVolumeLevel(void *data,
|
||||||
@@ -223,12 +297,20 @@ private:
|
|||||||
const float peak[MAX_AUDIO_CHANNELS],
|
const float peak[MAX_AUDIO_CHANNELS],
|
||||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
|
||||||
|
void EmitConfigClicked();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void VolumeChanged();
|
||||||
|
|
||||||
void SliderChanged(int vol);
|
void SliderChanged(int vol);
|
||||||
void updateText();
|
void updateText();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ConfigClicked();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VolControl(OBSSource source, bool vertical = false);
|
explicit VolControl(OBSSource source, bool showConfig = false,
|
||||||
|
bool vertical = false);
|
||||||
~VolControl();
|
~VolControl();
|
||||||
|
|
||||||
inline obs_source_t *GetSource() const { return source; }
|
inline obs_source_t *GetSource() const { return source; }
|
||||||
@@ -240,6 +322,19 @@ public:
|
|||||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
|
||||||
void EnableSlider(bool enable);
|
void EnableSlider(bool enable);
|
||||||
|
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||||
|
|
||||||
QSlider *GetSlider() const;
|
void refreshColors();
|
||||||
|
QSlider *GetSlider() const { return slider; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeSlider : public QSlider {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
obs_fader_t *fad;
|
||||||
|
|
||||||
|
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||||
|
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,25 @@ using websocketpp::lib::bind;
|
|||||||
|
|
||||||
obs_websocket_vendor vendor;
|
obs_websocket_vendor vendor;
|
||||||
|
|
||||||
|
void ClearWebsocketMessages()
|
||||||
|
{
|
||||||
|
switcher->websocketMessages.clear();
|
||||||
|
for (auto &connection : switcher->connections) {
|
||||||
|
Connection *c = dynamic_cast<Connection *>(connection.get());
|
||||||
|
if (c) {
|
||||||
|
c->Events().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendWebsocketEvent(const std::string &eventMsg)
|
||||||
|
{
|
||||||
|
auto data = obs_data_create();
|
||||||
|
obs_data_set_string(data, "message", eventMsg.c_str());
|
||||||
|
obs_websocket_vendor_emit_event(vendor, VendorEvent, data);
|
||||||
|
obs_data_release(data);
|
||||||
|
}
|
||||||
|
|
||||||
void ReceiveWebsocketMessage(obs_data_t *request_data, obs_data_t *, void *)
|
void ReceiveWebsocketMessage(obs_data_t *request_data, obs_data_t *, void *)
|
||||||
{
|
{
|
||||||
if (!obs_data_has_user_value(request_data, "message")) {
|
if (!obs_data_has_user_value(request_data, "message")) {
|
||||||
@@ -24,7 +43,7 @@ void ReceiveWebsocketMessage(obs_data_t *request_data, obs_data_t *, void *)
|
|||||||
auto msg = obs_data_get_string(request_data, "message");
|
auto msg = obs_data_get_string(request_data, "message");
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->websocketMessages.emplace_back(msg);
|
switcher->websocketMessages.emplace_back(msg);
|
||||||
vblog(LOG_INFO, "received messsage: %s", msg);
|
vblog(LOG_INFO, "received message: %s", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void RegisterWebsocketVendor()
|
extern "C" void RegisterWebsocketVendor()
|
||||||
@@ -232,6 +251,31 @@ void WSConnection::HandleHello(obs_data_t *helloMsg)
|
|||||||
Send(response);
|
Send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WSConnection::HandleEvent(obs_data_t *msg)
|
||||||
|
{
|
||||||
|
auto d = obs_data_get_obj(msg, "d");
|
||||||
|
auto eventData = obs_data_get_obj(d, "eventData");
|
||||||
|
if (strcmp(obs_data_get_string(eventData, "vendorName"), VendorName) !=
|
||||||
|
0) {
|
||||||
|
vblog(LOG_INFO, "ignoring vendor event from \"%s\"",
|
||||||
|
obs_data_get_string(eventData, "vendorName"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(obs_data_get_string(eventData, "eventType"), VendorEvent) !=
|
||||||
|
0) {
|
||||||
|
vblog(LOG_INFO, "ignoring event type\"%s\"",
|
||||||
|
obs_data_get_string(eventData, "eventType"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto eventDataNested = obs_data_get_obj(eventData, "eventData");
|
||||||
|
_messages.emplace_back(obs_data_get_string(eventDataNested, "message"));
|
||||||
|
vblog(LOG_INFO, "received event msg \"%s\"",
|
||||||
|
obs_data_get_string(eventDataNested, "message"));
|
||||||
|
obs_data_release(eventDataNested);
|
||||||
|
obs_data_release(eventData);
|
||||||
|
obs_data_release(d);
|
||||||
|
}
|
||||||
|
|
||||||
void WSConnection::HandleResponse(obs_data_t *response)
|
void WSConnection::HandleResponse(obs_data_t *response)
|
||||||
{
|
{
|
||||||
auto data = obs_data_get_obj(response, "d");
|
auto data = obs_data_get_obj(response, "d");
|
||||||
@@ -278,11 +322,14 @@ void WSConnection::OnMessage(connection_hdl, client::message_ptr message)
|
|||||||
case 2: // Identified
|
case 2: // Identified
|
||||||
_status = Status::AUTHENTICATED;
|
_status = Status::AUTHENTICATED;
|
||||||
break;
|
break;
|
||||||
|
case 5: // Event (Vendor)
|
||||||
|
HandleEvent(json);
|
||||||
|
break;
|
||||||
case 7: // RequestResponse
|
case 7: // RequestResponse
|
||||||
HandleResponse(json);
|
HandleResponse(json);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
vblog(LOG_INFO, "ignoring unkown opcode %d", opcode);
|
vblog(LOG_INFO, "ignoring unknown opcode %d", opcode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
obs_data_release(json);
|
obs_data_release(json);
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ typedef websocketpp::client<websocketpp::config::asio_client> client;
|
|||||||
|
|
||||||
constexpr char VendorName[] = "AdvancedSceneSwitcher";
|
constexpr char VendorName[] = "AdvancedSceneSwitcher";
|
||||||
constexpr char VendorRequest[] = "AdvancedSceneSwitcherMessage";
|
constexpr char VendorRequest[] = "AdvancedSceneSwitcherMessage";
|
||||||
|
constexpr char VendorEvent[] = "AdvancedSceneSwitcherEvent";
|
||||||
|
|
||||||
|
void ClearWebsocketMessages();
|
||||||
|
void SendWebsocketEvent(const std::string &);
|
||||||
|
|
||||||
class WSConnection : public QObject {
|
class WSConnection : public QObject {
|
||||||
public:
|
public:
|
||||||
@@ -34,6 +38,7 @@ public:
|
|||||||
bool _reconnect, int reconnectDelay = 10);
|
bool _reconnect, int reconnectDelay = 10);
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
void SendRequest(const std::string &msg);
|
void SendRequest(const std::string &msg);
|
||||||
|
std::vector<std::string> &Events() { return _messages; }
|
||||||
std::string GetFail() { return _failMsg; }
|
std::string GetFail() { return _failMsg; }
|
||||||
|
|
||||||
enum class Status {
|
enum class Status {
|
||||||
@@ -51,6 +56,7 @@ private:
|
|||||||
void Send(const std::string &);
|
void Send(const std::string &);
|
||||||
void ConnectThread();
|
void ConnectThread();
|
||||||
void HandleHello(obs_data_t *helloMsg);
|
void HandleHello(obs_data_t *helloMsg);
|
||||||
|
void HandleEvent(obs_data_t *event);
|
||||||
void HandleResponse(obs_data_t *response);
|
void HandleResponse(obs_data_t *response);
|
||||||
|
|
||||||
client _client;
|
client _client;
|
||||||
@@ -66,4 +72,6 @@ private:
|
|||||||
std::string _failMsg = "";
|
std::string _failMsg = "";
|
||||||
std::atomic<Status> _status = {Status::DISCONNECTED};
|
std::atomic<Status> _status = {Status::DISCONNECTED};
|
||||||
std::atomic_bool _disconnect{false};
|
std::atomic_bool _disconnect{false};
|
||||||
|
|
||||||
|
std::vector<std::string> _messages;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user