Compare commits

...

20 Commits
1.14 ... 1.15

Author SHA1 Message Date
WarmUpTill
c964ed04d3 Clean up includes 2021-07-22 09:40:31 -07:00
WarmUpTill
810235e0b7 Clean up duplicate code 2021-07-22 09:40:31 -07:00
WarmUpTill
e2b3563810 Rename files 2021-07-22 09:40:31 -07:00
WarmUpTill
76c5ec480b Add MacroSegment and MacroSegmentEdit to remove duplicate code 2021-07-22 09:40:31 -07:00
WarmUpTill
84e4b740a8 Prevent accidental edits of actions and conditions when scrolling 2021-07-22 09:40:31 -07:00
WarmUpTill
95409dfa26 Add option to expand or collapse all macro actions or conditions 2021-07-22 09:40:31 -07:00
WarmUpTill
058af0d3bb Move GetShortDesc() and GetId() to MacroSegment class 2021-07-22 09:40:31 -07:00
WarmUpTill
fd789b476b Persist collapse / expand state 2021-07-22 09:40:31 -07:00
WarmUpTill
3fc82df278 Handle macro control signals 2021-07-22 09:40:31 -07:00
WarmUpTill
d541356fd3 Add macro control widget 2021-07-22 09:40:31 -07:00
WarmUpTill
5c2adacca1 Update build instructions and readme(#267)
* Adjust build instructions

* Add install instructions

* Add note regarding QtConcurrent
2021-07-22 18:39:34 +02:00
WarmUpTill
7200668158 Add option to show frame 2021-07-19 12:13:29 -07:00
WarmUpTill
7cf7fd6112 Show current cursor position 2021-07-19 12:13:29 -07:00
WarmUpTill
41821cbeb2 Add macro condition that triggers if cursor position changed
Extend condition previously named "region"
2021-07-19 12:13:29 -07:00
WarmUpTill
9537579235 Rename macro condition "region" to "cursor" 2021-07-19 12:13:29 -07:00
WarmUpTill
40a701a934 Add "--recursive" option to "git clone" calls 2021-07-18 19:37:16 +02:00
WarmUpTill
6c8556c66c Rework cmake and readme (#264)
* Add missing include

* Rework CMakeLists.txt to support out of tree builds

* Update build instructions

* Fix windows build issue and copy locale on Linux

* Update build instructions

* Add downloads section
2021-07-17 17:17:03 +02:00
WarmUpTill
a467dffd05 Fix crash on Fedora 2021-07-13 13:14:43 -07:00
WarmUpTill
497dbd89d4 Fix warning 2021-07-13 13:14:43 -07:00
WarmUpTill
0fdbce25a1 Clean up minor UI inconsistencies 2021-07-13 13:14:43 -07:00
47 changed files with 1652 additions and 809 deletions

131
BUILDING.md Normal file
View File

@@ -0,0 +1,131 @@
# Compiling the plugin
You have the option to ...
- either add the plugin to the OBS source tree directly and build the plugin while building OBS itself. (**in tree**)
- or you can move the sources of this plugin outside of the OBS source tree and build it separately from OBS. (**out of tree**)
As both methods require you to have a working [OBS Studio development environment](https://obsproject.com/wiki/install-instructions), [Qt](https://download.qt.io/official_releases/qt/5.15/5.15.2/) 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.
Note that your Qt install must include the QtConcurrent module.
## Compiling in tree (recommended)
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
```
cd obs-studio/UI/frontend-plugins/
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
```
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt file and add an entry for the scene switcher:
add_subdirectory(SceneSwitcher)
## Compiling out of tree
### Prerequisites
You'll need [Qt](https://download.qt.io/official_releases/qt/5.15/5.15.2/), [CMake](https://cmake.org/download/) and a working [OBS Studio development environment](https://obsproject.com/wiki/install-instructions) installed on your computer.
Once you've set this up, do the following:
```
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
cd SceneSwitcher
mkdir build && cd build
```
### Windows
In cmake-gui, you'll have to set these CMake variables :
- **BUILD_OUT_OF_TREE** (bool) : true
- **LIBOBS_LIB** (filepath) : location of the obs.lib file
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
code of OBS Studio, located at [source_directory]/libobs/.
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the obs-frontend-api.lib file
(usually in the same place as LIBOBS_LIB)
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api
subfolder in the source code of OBS Studio, located at [source_directory]/UI/obs-frontend-api.
- **CURL_LIB** (filepath) : location of the libcurl.lib file
(part of the dependencies2019/win64/bin folder used to build OBS)
- **CURL_INCLUDE_DIR** (path) : location of the curl
subfolder in OBS dependencies folder: ".../dependencies2019/win64/include/"
Assuming that you set up Qt via QT installer:
- **Qt5Core_DIR** (path) : C:/Qt/5.15.2/msvc2019_64/lib/cmake/Qt5Core
- **Qt5Gui_DIR** (path): C:/Qt/5.15.2/msvc2019_64/lib/cmake/Qt5Gui
- **Qt5Widgets_DIR** (path) : C:/Qt/5.15.2/msvc2019_64/lib/cmake/Qt5Widgets
Just keep hitting configure until all the vars are filled out. Then hit generate.
### Linux
Most versions of Linux you can use cmake-gui or the command line.
**For the command line:**
```
# [...] are placeholders
cmake -DBUILD_OUT_OF_TREE=1 \
-DLIBOBS_INCLUDE_DIR=[...]/obs-studio/libobs/ \
-DLIBOBS_LIB=[...]/libobs.so \
-DLIBOBS_FRONTEND_INCLUDE_DIR=[...]/obs-studio/UI/obs-frontend-api/ \
-DLIBOBS_FRONTEND_API_LIB=[...]/libobs-frontend-api.so \
-DCMAKE_INSTALL_PREFIX=/usr ..
make -j4
sudo make install
```
For cmake-gui you'll have to set the following variables:
- **BUILD_OUT_OF_TREE** (bool) : true
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
code of OBS Studio, located at [source_directory]/libobs/.
- **LIBOBS_LIB** (filepath) : location of the libobs.so file (usually CMake finds
this, but if not it'll usually be in /usr/lib/libobs.so)
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the libobs-frontend-api.so
file (usually in the same place as LIBOBS_LIB)
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api
subfolder in the source code of OBS Studio, located at
[source_directory]/UI/obs-frontend-api.
Assuming that you installed Qt via your system package manager, it should be
found automatically. If not, then usually you'll find it in something like:
- **Qt5Core_DIR** (path) : /usr/lib64/cmake/Qt5Core
- **Qt5Gui_DIR** (path): /usr/lib64/cmake/Qt5Gui
- **Qt5Widgets_DIR** (path) : /usr/lib64/cmake/Qt5Widgets
Just keep hitting configure until all the vars are filled out. Then hit generate.
Then open a terminal in the build folder and type:
```
make -j4
sudo make install
```
NOTE: The Linux version of this plugin is dependent on libXScrnSaver, libcurl and libXtst.
### OS X
In cmake-gui, you'll have to set these CMake variables :
- **BUILD_OUT_OF_TREE** (bool) : true
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
code of OBS Studio, located at [source_directory]/libobs/.
- **LIBOBS_LIB** (filepath) : location of the libobs.0.dylib file (usually
in /Applications/OBS.app/Contents/Resources/bin/libobs.0.dylib)
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the libobs-frontend-api.0.dylib
file (usually in usually in /Applications/OBS.app/Contents/Resources/bin/libobs-frontend-api.0.dylib)
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api subfolder
in the source code of OBS Studio, located at [source_directory]/UI/obs-frontend-api.
Assuming that you installed Qt via the regular Qt App way:
- **Qt5Core_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5Core
- **Qt5Widgets_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5Widgets
- **Qt5MacExtras_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5MacExtras
Just keep hitting configure until all the vars are filled out. Then hit generate.
Open xcode (or a terminal, depending on the build type you chose), build and copy
the advanced-scene-switcher.so file to 'Library/Application Support/obs-studio/plugins/advanced-scene-switcher/bin/'
And the 'data' folder to 'Library/Application Support/obs-studio/plugins/advanced-scene-switcher/'.
Note that you might have to adjust the library search paths using the install_name_tool if you want the plugin to run on machines other than your build machine:
```
install_name_tool -change @rpath/libobs-frontend-api.dylib @executable_path/../Frameworks/libobs-frontend-api.dylib UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt5/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt5/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt5/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
```

View File

@@ -1,317 +1,347 @@
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.14)
project(advanced-scene-switcher)
add_definitions(-DASIO_STANDALONE)
# generate version info
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RELWITHDEBINFO)
endif()
# Compiler settings
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
if(CMAKE_COMPILER_IS_GNUCC
OR CMAKE_COMPILER_IS_GNUCXX
OR CMAKE_COMPILER_IS_CLANG)
set(CMAKE_CXX_FLAGS
"-Wall -Wextra -Wvla -Wno-unused-function -Wno-missing-field-initializers ${CMAKE_CXX_FLAGS} -fno-strict-aliasing"
)
set(CMAKE_C_FLAGS
"-Wall -Wextra -Wvla -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-braces -Wno-missing-field-initializers ${CMAKE_C_FLAGS} -std=gnu99 -fno-strict-aliasing"
)
option(USE_LIBC++ "Use libc++ instead of libstdc++" ${APPLE})
if(USE_LIBC++)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
elseif(MSVC)
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
# Disable pointless constant condition warnings
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} /wd4127 /wd4201 /wd4456 /wd4457 /wd4458 /wd4459 /wd4595"
)
add_definitions(-DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS
-D_CRT_NONSTDC_NO_WARNINGS)
endif()
# Generate version info
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in"
"${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
if(WIN32)
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR "${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}" ISS_MSVC_REDIST_HELPER_DIR)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in" "${CMAKE_CURRENT_BINARY_DIR}/CI/windows/setup.iss" @ONLY)
get_filename_component(ISS_PLUGIN_FILES_DIR
"${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR
"${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}"
ISS_MSVC_REDIST_HELPER_DIR)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in"
"${CMAKE_CURRENT_BINARY_DIR}/CI/windows/setup.iss" @ONLY)
endif()
# Windows installer
if(WIN32)
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR "${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}" ISS_MSVC_REDIST_HELPER_DIR)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in" "${CMAKE_CURRENT_BINARY_DIR}/CI/windows/setup.iss" @ONLY)
get_filename_component(ISS_PLUGIN_FILES_DIR
"${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR
"${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}"
ISS_MSVC_REDIST_HELPER_DIR)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in"
"${CMAKE_CURRENT_BINARY_DIR}/CI/windows/setup.iss" @ONLY)
endif()
# Out of tree specific settings
if(BUILD_OUT_OF_TREE)
set(CMAKE_PREFIX_PATH "${QTDIR}")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/external")
set(CMAKE_PREFIX_PATH "${QTDIR}")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/external")
include(external/FindLibObs.cmake)
find_package(LibObs REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_path(LIBOBS_FRONTEND_INCLUDE_DIR HINTS ${LIBOBS_INCLUDE_DIRS})
find_file(
LIBOBS_FRONTEND_API_LIB
NAMES libobs-frontend-api.*
HINTS ${LIBOBS_LIB})
include(external/FindLibObs.cmake)
find_package(LibObs REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Libcurl REQUIRED)
find_path(LIBOBS_FRONTEND_INCLUDE_DIR HINTS ${LIBOBS_INCLUDE_DIRS})
find_file(LIBOBS_FRONTEND_API_LIB NAMES libobs-frontend-api.* HINTS ${LIBOBS_LIB} )
include_directories("${LIBOBS_INCLUDE_DIRS}" "${LIBOBS_FRONTEND_INCLUDE_DIR}"
${Qt5Core_INCLUDES} ${Qt5Widgets_INCLUDES})
endif()
# Platform specific settings
if(APPLE)
find_library(COCOA Cocoa)
if(BUILD_OUT_OF_TREE)
find_package(Qt5MacExtras REQUIRED)
endif()
include_directories(${COCOA})
find_library(COCOA Cocoa)
if(BUILD_OUT_OF_TREE)
find_package(Qt5MacExtras REQUIRED)
endif()
include_directories(${COCOA})
endif()
find_package(Libcurl REQUIRED)
include_directories("${LIBCURL_INCLUDE_DIRS}")
if(UNIX AND NOT APPLE)
find_package(X11 REQUIRED)
link_libraries(${X11_LIBRARIES})
include_directories(${X11_INCLUDE_DIR})
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
link_libraries(${X11_LIBRARIES})
include_directories("${X11_INCLUDE_DIR}" "${X11_Xtst_INCLUDE_PATH}"
"${X11_Xss_INCLUDE_PATH}")
endif()
include_directories(
"${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include"
"${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp"
)
set(advanced-scene-switcher_HEADERS
${advanced-scene-switcher_HEADERS}
src/headers/advanced-scene-switcher.hpp
src/headers/switcher-data-structs.hpp
src/headers/scene-group.hpp
src/headers/scene-trigger.hpp
src/headers/switch-audio.hpp
src/headers/switch-executable.hpp
src/headers/switch-file.hpp
src/headers/switch-idle.hpp
src/headers/switch-media.hpp
src/headers/switch-network.hpp
src/headers/switch-pause.hpp
src/headers/switch-random.hpp
src/headers/switch-screen-region.hpp
src/headers/switch-time.hpp
src/headers/switch-transitions.hpp
src/headers/switch-window.hpp
src/headers/switch-sequence.hpp
src/headers/switch-video.hpp
src/headers/switch-generic.hpp
src/headers/macro-action-edit.hpp
src/headers/macro-action-audio.hpp
src/headers/macro-action-file.hpp
src/headers/macro-action-filter.hpp
src/headers/macro-action-hotkey.hpp
src/headers/macro-action-macro.hpp
src/headers/macro-action-media.hpp
src/headers/macro-action-plugin-state.hpp
src/headers/macro-action-recording.hpp
src/headers/macro-action-replay-buffer.hpp
src/headers/macro-action-run.hpp
src/headers/macro-action-scene-order.hpp
src/headers/macro-action-scene-switch.hpp
src/headers/macro-action-scene-transform.hpp
src/headers/macro-action-scene-visibility.hpp
src/headers/macro-action-source.hpp
src/headers/macro-action-streaming.hpp
src/headers/macro-action-virtual-cam.hpp
src/headers/macro-action-wait.hpp
src/headers/macro-condition-edit.hpp
src/headers/macro-condition-audio.hpp
src/headers/macro-condition-date.hpp
src/headers/macro-condition-macro.hpp
src/headers/macro-condition-file.hpp
src/headers/macro-condition-filter.hpp
src/headers/macro-condition-hotkey.hpp
src/headers/macro-condition-idle.hpp
src/headers/macro-condition-media.hpp
src/headers/macro-condition-plugin-state.hpp
src/headers/macro-condition-process.hpp
src/headers/macro-condition-recording.hpp
src/headers/macro-condition-region.hpp
src/headers/macro-condition-replay-buffer.hpp
src/headers/macro-condition-scene-order.hpp
src/headers/macro-condition-scene.hpp
src/headers/macro-condition-source.hpp
src/headers/macro-condition-streaming.hpp
src/headers/macro-condition-timer.hpp
src/headers/macro-condition-video.hpp
src/headers/macro-condition-virtual-cam.hpp
src/headers/macro-condition-window.hpp
src/headers/macro.hpp
src/headers/macro-selection.hpp
src/headers/curl-helper.hpp
src/headers/hotkey.hpp
src/headers/scene-selection.hpp
src/headers/screenshot-helper.hpp
src/headers/name-dialog.hpp
src/headers/duration-control.hpp
src/headers/section.hpp
src/headers/platform-funcs.hpp
src/headers/utility.hpp
src/headers/volume-control.hpp
src/headers/version.h
)
set(advanced-scene-switcher_SOURCES
${advanced-scene-switcher_SOURCES}
src/advanced-scene-switcher.cpp
src/advanced-scene-switcher-module.c
src/switcher-data-structs.cpp
src/scene-group.cpp
src/scene-trigger.cpp
src/switch-transitions.cpp
src/switch-screen-region.cpp
src/switch-priority.cpp
src/switch-executable.cpp
src/switch-idle.cpp
src/switch-sequence.cpp
src/switch-file.cpp
src/switch-window.cpp
src/switch-media.cpp
src/switch-network.cpp
src/hotkey.cpp
src/general.cpp
src/switch-pause.cpp
src/switch-random.cpp
src/switch-time.cpp
src/switch-audio.cpp
src/switch-video.cpp
src/switch-generic.cpp
src/macro-action-edit.cpp
src/macro-action-audio.cpp
src/macro-action-file.cpp
src/macro-action-filter.cpp
src/macro-action-hotkey.cpp
src/macro-action-macro.cpp
src/macro-action-media.cpp
src/macro-action-plugin-state.cpp
src/macro-action-recording.cpp
src/macro-action-replay-buffer.cpp
src/macro-action-run.cpp
src/macro-action-scene-order.cpp
src/macro-action-scene-switch.cpp
src/macro-action-scene-transform.cpp
src/macro-action-scene-visibility.cpp
src/macro-action-source.cpp
src/macro-action-streaming.cpp
src/macro-action-virtual-cam.cpp
src/macro-action-wait.cpp
src/macro-condition-edit.cpp
src/macro-condition-audio.cpp
src/macro-condition-date.cpp
src/macro-condition-macro.cpp
src/macro-condition-file.cpp
src/macro-condition-filter.cpp
src/macro-condition-hotkey.cpp
src/macro-condition-idle.cpp
src/macro-condition-media.cpp
src/macro-condition-plugin-state.cpp
src/macro-condition-process.cpp
src/macro-condition-recording.cpp
src/macro-condition-region.cpp
src/macro-condition-replay-buffer.cpp
src/macro-condition-scene-order.cpp
src/macro-condition-scene.cpp
src/macro-condition-source.cpp
src/macro-condition-streaming.cpp
src/macro-condition-timer.cpp
src/macro-condition-video.cpp
src/macro-condition-virtual-cam.cpp
src/macro-condition-window.cpp
src/macro.cpp
src/macro-selection.cpp
src/macro-tab.cpp
src/curl-helper.cpp
src/scene-selection.cpp
src/screenshot-helper.cpp
src/name-dialog.cpp
src/duration-control.cpp
src/section.cpp
src/utility.cpp
src/volume-control.cpp
src/version.cpp
)
set(advanced-scene-switcher_UI
${advanced-scene-switcher_UI}
forms/advanced-scene-switcher.ui
)
if(WIN32)
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
set(advanced-scene-switcher_PLATFORM_SOURCES
src/win/advanced-scene-switcher-win.cpp)
set(advanced-scene-switcher_PLATFORM_SOURCES
src/win/advanced-scene-switcher-win.cpp)
elseif(APPLE)
set(advanced-scene-switcher_PLATFORM_SOURCES
src/osx/advanced-scene-switcher-osx.mm)
set_source_files_properties(advanced-scene-switcher-osx.mm
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
set(advanced-scene-switcher_PLATFORM_LIBS
${COCOA})
set(advanced-scene-switcher_PLATFORM_SOURCES
src/osx/advanced-scene-switcher-osx.mm)
set_source_files_properties(advanced-scene-switcher-osx.mm
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
set(advanced-scene-switcher_PLATFORM_LIBS ${COCOA})
else()
set(advanced-scene-switcher_PLATFORM_SOURCES
src/linux/advanced-scene-switcher-nix.cpp)
set(advanced-scene-switcher_PLATFORM_LIBS
Xss)
set(advanced-scene-switcher_PLATFORM_SOURCES
src/linux/advanced-scene-switcher-nix.cpp)
set(advanced-scene-switcher_PLATFORM_LIBS Xss)
endif()
qt5_wrap_ui(advanced-scene-switcher_UI_HEADERS
${advanced-scene-switcher_UI}
${advanced-scene-switcher_PLATFORM_UI})
# asio and websocketpp
add_definitions(-DASIO_STANDALONE)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include"
"${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp")
if(WIN32)
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
endif()
add_library(advanced-scene-switcher MODULE
${advanced-scene-switcher_HEADERS}
${advanced-scene-switcher_SOURCES}
${advanced-scene-switcher_UI_HEADERS}
${advanced-scene-switcher_PLATFORM_SOURCES}
${advanced-scene-switcher_PLATFORM_HEADERS}
)
# Setup QT tools
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/forms")
set(advanced-scene-switcher_UI ${advanced-scene-switcher_UI}
forms/advanced-scene-switcher.ui)
qt5_wrap_ui(advanced-scene-switcher_UI_HEADERS ${advanced-scene-switcher_UI}
${advanced-scene-switcher_PLATFORM_UI})
# The plugin sources
set(advanced-scene-switcher_HEADERS
${advanced-scene-switcher_HEADERS}
src/headers/advanced-scene-switcher.hpp
src/headers/switcher-data-structs.hpp
src/headers/scene-group.hpp
src/headers/scene-trigger.hpp
src/headers/switch-audio.hpp
src/headers/switch-executable.hpp
src/headers/switch-file.hpp
src/headers/switch-idle.hpp
src/headers/switch-media.hpp
src/headers/switch-network.hpp
src/headers/switch-pause.hpp
src/headers/switch-random.hpp
src/headers/switch-screen-region.hpp
src/headers/switch-time.hpp
src/headers/switch-transitions.hpp
src/headers/switch-window.hpp
src/headers/switch-sequence.hpp
src/headers/switch-video.hpp
src/headers/switch-generic.hpp
src/headers/macro-action-edit.hpp
src/headers/macro-action-audio.hpp
src/headers/macro-action-file.hpp
src/headers/macro-action-filter.hpp
src/headers/macro-action-hotkey.hpp
src/headers/macro-action-macro.hpp
src/headers/macro-action-media.hpp
src/headers/macro-action-plugin-state.hpp
src/headers/macro-action-recording.hpp
src/headers/macro-action-replay-buffer.hpp
src/headers/macro-action-run.hpp
src/headers/macro-action-scene-order.hpp
src/headers/macro-action-scene-switch.hpp
src/headers/macro-action-scene-transform.hpp
src/headers/macro-action-scene-visibility.hpp
src/headers/macro-action-source.hpp
src/headers/macro-action-streaming.hpp
src/headers/macro-action-virtual-cam.hpp
src/headers/macro-action-wait.hpp
src/headers/macro-condition-edit.hpp
src/headers/macro-condition-audio.hpp
src/headers/macro-condition-cursor.hpp
src/headers/macro-condition-date.hpp
src/headers/macro-condition-file.hpp
src/headers/macro-condition-filter.hpp
src/headers/macro-condition-hotkey.hpp
src/headers/macro-condition-idle.hpp
src/headers/macro-condition-macro.hpp
src/headers/macro-condition-media.hpp
src/headers/macro-condition-plugin-state.hpp
src/headers/macro-condition-process.hpp
src/headers/macro-condition-recording.hpp
src/headers/macro-condition-replay-buffer.hpp
src/headers/macro-condition-scene-order.hpp
src/headers/macro-condition-scene.hpp
src/headers/macro-condition-source.hpp
src/headers/macro-condition-streaming.hpp
src/headers/macro-condition-timer.hpp
src/headers/macro-condition-video.hpp
src/headers/macro-condition-virtual-cam.hpp
src/headers/macro-condition-window.hpp
src/headers/macro.hpp
src/headers/macro-controls.hpp
src/headers/macro-segment.hpp
src/headers/macro-selection.hpp
src/headers/curl-helper.hpp
src/headers/hotkey.hpp
src/headers/scene-selection.hpp
src/headers/screenshot-helper.hpp
src/headers/name-dialog.hpp
src/headers/duration-control.hpp
src/headers/section.hpp
src/headers/platform-funcs.hpp
src/headers/utility.hpp
src/headers/volume-control.hpp
src/headers/version.h)
set(advanced-scene-switcher_SOURCES
${advanced-scene-switcher_SOURCES}
src/advanced-scene-switcher.cpp
src/advanced-scene-switcher-module.c
src/switcher-data-structs.cpp
src/scene-group.cpp
src/scene-trigger.cpp
src/switch-transitions.cpp
src/switch-screen-region.cpp
src/switch-priority.cpp
src/switch-executable.cpp
src/switch-idle.cpp
src/switch-sequence.cpp
src/switch-file.cpp
src/switch-window.cpp
src/switch-media.cpp
src/switch-network.cpp
src/hotkey.cpp
src/general.cpp
src/switch-pause.cpp
src/switch-random.cpp
src/switch-time.cpp
src/switch-audio.cpp
src/switch-video.cpp
src/switch-generic.cpp
src/macro-action-edit.cpp
src/macro-action-audio.cpp
src/macro-action-file.cpp
src/macro-action-filter.cpp
src/macro-action-hotkey.cpp
src/macro-action-macro.cpp
src/macro-action-media.cpp
src/macro-action-plugin-state.cpp
src/macro-action-recording.cpp
src/macro-action-replay-buffer.cpp
src/macro-action-run.cpp
src/macro-action-scene-order.cpp
src/macro-action-scene-switch.cpp
src/macro-action-scene-transform.cpp
src/macro-action-scene-visibility.cpp
src/macro-action-source.cpp
src/macro-action-streaming.cpp
src/macro-action-virtual-cam.cpp
src/macro-action-wait.cpp
src/macro-condition-edit.cpp
src/macro-condition-audio.cpp
src/macro-condition-cursor.cpp
src/macro-condition-date.cpp
src/macro-condition-file.cpp
src/macro-condition-filter.cpp
src/macro-condition-hotkey.cpp
src/macro-condition-idle.cpp
src/macro-condition-macro.cpp
src/macro-condition-media.cpp
src/macro-condition-plugin-state.cpp
src/macro-condition-process.cpp
src/macro-condition-recording.cpp
src/macro-condition-replay-buffer.cpp
src/macro-condition-scene-order.cpp
src/macro-condition-scene.cpp
src/macro-condition-source.cpp
src/macro-condition-streaming.cpp
src/macro-condition-timer.cpp
src/macro-condition-video.cpp
src/macro-condition-virtual-cam.cpp
src/macro-condition-window.cpp
src/macro.cpp
src/macro-controls.cpp
src/macro-segment.cpp
src/macro-selection.cpp
src/macro-tab.cpp
src/curl-helper.cpp
src/scene-selection.cpp
src/screenshot-helper.cpp
src/name-dialog.cpp
src/duration-control.cpp
src/section.cpp
src/utility.cpp
src/volume-control.cpp
src/version.cpp)
add_library(
advanced-scene-switcher MODULE
${advanced-scene-switcher_HEADERS}
${advanced-scene-switcher_SOURCES}
${advanced-scene-switcher_UI_HEADERS}
${advanced-scene-switcher_PLATFORM_SOURCES}
${advanced-scene-switcher_PLATFORM_HEADERS})
# Out of tree build
if(BUILD_OUT_OF_TREE)
include_directories(
"${LIBOBS_INCLUDE_DIRS}"
"${LIBOBS_FRONTEND_INCLUDE_DIR}"
${Qt5Core_INCLUDES}
${Qt5Widgets_INCLUDES}
${LIBCURL_INCLUDE_DIRS})
target_link_libraries(
advanced-scene-switcher ${advanced-scene-switcher_PLATFORM_LIBS}
${LIBOBS_LIB} ${LIBOBS_FRONTEND_API_LIB} Qt5::Core Qt5::Widgets)
target_link_libraries(advanced-scene-switcher
${advanced-scene-switcher_PLATFORM_LIBS}
${LIBOBS_LIB}
${LIBOBS_FRONTEND_API_LIB}
Qt5::Core
Qt5::Widgets)
# 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")
# 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"
)
# OSX
if(APPLE)
set_target_properties(advanced-scene-switcher PROPERTIES PREFIX "")
endif()
# Windows
if(WIN32)
string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
message("${CMAKE_CXX_FLAGS_RELEASE}")
find_package(w32-pthreads REQUIRED)
target_link_libraries(advanced-scene-switcher
w32-pthreads)
endif()
# OSX
if(APPLE)
set_target_properties(advanced-scene-switcher PROPERTIES PREFIX "")
endif()
# Linux
if(UNIX AND NOT APPLE)
set(CMAKE_C_FLAGS "-Wall -Wextra -Wvla -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-braces -Wno-missing-field-initializers ${CMAKE_C_FLAGS} -std=c99 -fno-strict-aliasing")
if(ARCH EQUAL 64)
set(ARCH_NAME "x86_64")
else()
set(ARCH_NAME "i686")
endif()
set_target_properties(advanced-scene-switcher PROPERTIES PREFIX "")
install(TARGETS advanced-scene-switcher
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/obs-plugins)
install(FILES ${ASS_TRANSLATION_FILES}
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/advanced-scene-switcher/locale")
endif()
# Linux
if(UNIX AND NOT APPLE)
if(ARCH EQUAL 64)
set(ARCH_NAME "x86_64")
else()
set(ARCH_NAME "i686")
endif()
set_target_properties(advanced-scene-switcher PROPERTIES PREFIX "")
install(TARGETS advanced-scene-switcher
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/obs-plugins)
install(
FILES ${ASS_TRANSLATION_FILES}
DESTINATION
"${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/advanced-scene-switcher/locale"
)
endif()
else()
target_link_libraries(advanced-scene-switcher
${advanced-scene-switcher_PLATFORM_LIBS}
obs-frontend-api
Qt5::Widgets
libobs)
install_obs_plugin_with_data(advanced-scene-switcher data)
# In tree build
target_link_libraries(
advanced-scene-switcher ${advanced-scene-switcher_PLATFORM_LIBS}
obs-frontend-api Qt5::Widgets libobs)
install_obs_plugin_with_data(advanced-scene-switcher data)
endif()

126
README.md
View File

@@ -1,119 +1,27 @@
# SceneSwitcher
An automated scene switcher for OBS Studio
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/ and see https://github.com/WarmUpTill/SceneSwitcher/blob/master/CI/release/README.txt for installing releases.
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/.
## Compiling in tree (recommended)
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
## Downloads
Binaries for Windows, MacOS, and Linux are available in the [Releases](https://github.com/WarmUpTill/SceneSwitcher/releases) section.
## Installing the plugin
For the Windows and MacOS platforms, it is recommended to run the provided installers.
For Linux the Snap package manager offers an OBS Studio installation which is bundled with the plugin:
```
cd obs-studio/UI/frontend-plugins/
git clone https://github.com/WarmUpTill/SceneSwitcher.git
sudo snap install obs-studi
```
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt file and add an entry for the scene switcher:
add_subdirectory(SceneSwitcher)
If that is not an option you will have to ...
1. Copy the library to the plugins folder of you obs installation.
2. Copy the contents of the data directory to its respective folders of your obs installation.
## Compiling out of tree
### Prerequisites
You'll need CMake and a working development environment for OBS Studio installed
on your computer. Once you've done that, do the following:
```
git clone https://github.com/WarmUpTill/SceneSwitcher.git
cd SceneSwitcher
mkdir build && cd build
```
Unfortunately the exact location of these folders may vary from system to system.
### Windows
In cmake-gui, you'll have to set these CMake variables :
- **BUILD_OUT_OF_TREE** (bool) : true
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
code of OBS Studio, located at [source_directory]/libobs/.
- **LIBOBS_LIB** (filepath) : location of the obs.dll file (usually set to
C:\Program Files\obs-studio\bin\64bit\obs.dll)
## Compiling the plugin
Assuming that you installed Qt via the regular Qt App way:
- **Qt5Core_DIR** (path) : C:/Qt/5.10.1/msvc2017_64/lib/cmake/Qt5Core
- **Qt5Gui_DIR** (path): C:/Qt/5.10.1/msvc2017_64/lib/cmake/Qt5Gui
- **Qt5Widgets_DIR** (path) : C:/Qt/5.10.1/msvc2017_64/lib/cmake/Qt5Widgets
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the libobs-frontend-api.dll
file, usually C:/Program Files/obs-studio/bin/64bit/obs-frontend-api.dll
(usually in the same place as LIBOBS_LIB)
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api
subfolder in the source code of OBS Studio, located at [source_directory]/UI/obs-frontend-api.
Just keep hitting configure until all the vars are filled out. Then hit generate.
### Linux
Most versions of Linux you can use cmake-gui or the command line.
**For the command line:**
```
cmake -DBUILD_OUT_OF_TREE=1 -DLIBOBS_INCLUDE_DIR="<path to the libobs/ sub-folder in obs-studio's source code>" \
-DLIBOBS_FRONTEND_INCLUDE_DIR="<path to the UI/obs-frontend-api/ sub-folder in obs-studio's source code>" \
-DLIBOBS_LIB="<path to the libobs.so file>" \
-DLIBOBS_FRONTEND_API_LIB="<path to the libobs-frontend-api.so file (usually in the same place as LIBOBS_LIB)>" \
-DCMAKE_INSTALL_PREFIX=/usr ..
make -j4
sudo make install
```
For cmake-gui you'll have to set the following variables:
- **BUILD_OUT_OF_TREE** (bool) : true
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
code of OBS Studio, located at [source_directory]/libobs/.
- **LIBOBS_LIB** (filepath) : location of the libobs.so file (usually CMake finds
this, but if not it'll usually be in /usr/lib/libobs.so)
Assuming that you installed Qt via your system package manager, it should be
found automatically. If not, then usually you'll find it in something like:
- **Qt5Core_DIR** (path) : /usr/lib64/cmake/Qt5Core
- **Qt5Gui_DIR** (path): /usr/lib64/cmake/Qt5Gui
- **Qt5Widgets_DIR** (path) : /usr/lib64/cmake/Qt5Widgets
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the libobs-frontend-api.so
file (usually in the same place as LIBOBS_LIB)
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api
subfolder in the source code of OBS Studio, located at
[source_directory]/UI/obs-frontend-api.
Just keep hitting configure until all the vars are filled out. Then hit generate.
Then open a terminal in the build folder and type:
```
make -j4
sudo make install
```
NOTE: The Linux version of this plugin is dependent on libXScrnSaver.
### OS X
In cmake-gui, you'll have to set these CMake variables :
- **BUILD_OUT_OF_TREE** (bool) : true
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
code of OBS Studio, located at [source_directory]/libobs/.
- **LIBOBS_LIB** (filepath) : location of the libobs.0.dylib file (usually
in /Applications/OBS.app/Contents/Resources/bin/libobs.0.dylib)
Assuming that you installed Qt via the regular Qt App way:
- **Qt5Core_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5Core
- **Qt5Widgets_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5Widgets
- **Qt5MacExtras_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5MacExtras
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the libobs-frontend-api.0.dylib
file (usually in usually in /Applications/OBS.app/Contents/Resources/bin/libobs-frontend-api.0.dylib)
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api subfolder
in the source code of OBS Studio, located at [source_directory]/UI/obs-frontend-api.
Just keep hitting configure until all the vars are filled out. Then hit generate.
Open xcode (or a terminal, depending on the build type you chose), build and copy
the advanced-scene-switcher.so file to /Applications/OBS.app/Contents/PlugIns
Note that you might have to adjust the library search paths using the install_name_tool if you want the plugin to run on machines other than your build machine:
```
install_name_tool -change @rpath/libobs-frontend-api.dylib @executable_path/../Frameworks/libobs-frontend-api.dylib UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt5/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt5/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt5/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
```
See the [build instructions](BUILDING.md).

View File

@@ -68,6 +68,8 @@ AdvSceneSwitcher.macroTab.name="Name:"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
AdvSceneSwitcher.macroTab.copy="Create copy"
AdvSceneSwitcher.macroTab.expandAll="Expand all"
AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
; Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry"
@@ -83,8 +85,13 @@ AdvSceneSwitcher.condition.audio="Audio"
AdvSceneSwitcher.condition.audio.state.below="Below"
AdvSceneSwitcher.condition.audio.state.above="Above"
AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}}"
AdvSceneSwitcher.condition.region="Screen region"
AdvSceneSwitcher.condition.region.entry="Cursor is in {{minX}} {{minY}} x {{maxX}} {{maxY}}"
AdvSceneSwitcher.condition.cursor="Cursor"
AdvSceneSwitcher.condition.cursor.type.region="is in region"
AdvSceneSwitcher.condition.cursor.type.moving="is moving"
AdvSceneSwitcher.condition.cursor.showFrame="Show frame"
AdvSceneSwitcher.condition.cursor.hideFrame="Hide frame"
AdvSceneSwitcher.condition.cursor.entry.line1="Cursor is {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="Cursor is currently at {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="Scene"
AdvSceneSwitcher.condition.scene.type.current="Current"
AdvSceneSwitcher.condition.scene.type.previous="Previous"

View File

@@ -81,8 +81,7 @@ AdvSceneSwitcher.condition.audio="Аудио"
AdvSceneSwitcher.ondition.audio.state.below="Ниже"
AdvSceneSwitcher.ondition.audio.state.above="Выше"
AdvSceneSwitcher.condition.audio.entry="Громкость {{audioSources}} равна {{condition}} {{volume}} в течении {{duration}} секунд"
AdvSceneSwitcher.condition.region="Область экрана"
AdvSceneSwitcher.condition.region.entry="Курсор находится в {{minX}} {{minY}} x {{maxX}} {{maxY}}"
AdvSceneSwitcher.condition.cursor="Область экрана"
AdvSceneSwitcher.condition.scene="Сцена"
AdvSceneSwitcher.condition.scene.type.current="Текущий"
AdvSceneSwitcher.condition.scene.type.previous="Предыдущий"

View File

@@ -693,13 +693,6 @@
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_65">
<property name="orientation">

View File

@@ -277,6 +277,12 @@ void SwitcherData::setPreconditions()
}
}
currentTitle = title;
// Cursor
std::pair<int, int> cursorPos = getCursorPos();
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||
cursorPos.second != switcher->lastCursorPos.second;
lastCursorPos = getCursorPos();
}
bool SwitcherData::checkForMatch(OBSWeakSource &scene,

View File

@@ -14,6 +14,8 @@
}
class QCloseEvent;
class MacroActionEdit;
class MacroConditionEdit;
/*******************************************************************************
* Advanced Scene Switcher window
@@ -47,6 +49,10 @@ public:
bool addNewMacro(std::string &name);
Macro *getSelectedMacro();
void SetEditMacro(Macro &m);
void ConnectControlSignals(MacroActionEdit *);
void ConnectControlSignals(MacroConditionEdit *);
void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2);
void loadUI();
void setupGeneralTab();
@@ -109,8 +115,22 @@ public slots:
void on_conditionRemove_clicked();
void on_actionAdd_clicked();
void on_actionRemove_clicked();
void showMacroContextMenu(const QPoint &);
void copyMacro();
void ShowMacroContextMenu(const QPoint &);
void ShowMacroActionsContextMenu(const QPoint &);
void ShowMacroConditionsContextMenu(const QPoint &);
void CopyMacro();
void ExpandAllActions();
void ExpandAllConditions();
void CollapseAllActions();
void CollapseAllConditions();
void AddMacroAction(int idx);
void RemoveMacroAction(int idx);
void MoveMacroActionUp(int idx);
void MoveMacroActionDown(int idx);
void AddMacroCondition(int idx);
void RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx);
void on_screenRegionSwitches_currentRowChanged(int idx);
void on_showFrame_clicked();

View File

@@ -1,5 +1,6 @@
#pragma once
#include <QSpinBox>
#include <QHBoxLayout>
#include "macro-action-edit.hpp"
enum class AudioAction {

View File

@@ -1,13 +1,5 @@
#pragma once
#include <QWidget>
#include <QComboBox>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QLabel>
#include <deque>
#include "macro.hpp"
#include "section.hpp"
struct MacroActionInfo {
using TCreateMethod = std::shared_ptr<MacroAction> (*)();
@@ -33,34 +25,23 @@ private:
static std::map<std::string, MacroActionInfo> _methods;
};
class MacroActionEdit : public QWidget {
class MacroActionEdit : public MacroSegmentEdit {
Q_OBJECT
public:
MacroActionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = "scene_switch",
bool startCollapsed = false);
void UpdateEntryData(const std::string &id, bool collapse);
const std::string &id = "scene_switch");
void UpdateEntryData(const std::string &id);
private slots:
void ActionSelectionChanged(const QString &text);
void HeaderInfoChanged(const QString &);
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString newName);
void SceneGroupAdded(const QString &name);
void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName);
protected:
QComboBox *_actionSelection;
Section *_section;
QLabel *_headerInfo;
std::shared_ptr<MacroAction> *_entryData;
private:
MacroSegment *Data();
QComboBox *_actionSelection;
std::shared_ptr<MacroAction> *_entryData;
bool _loading = true;
};

View File

@@ -3,6 +3,7 @@
#include "hotkey.hpp"
#include <QCheckBox>
#include <QHBoxLayout>
class MacroActionHotkey : public MacroAction {
public:

View File

@@ -1,8 +1,9 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "macro-selection.hpp"
#include <QHBoxLayout>
enum class PerformMacroAction {
PAUSE,
UNPAUSE,

View File

@@ -1,7 +1,9 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
#include <QSpinBox>
#include <QHBoxLayout>
enum class MediaAction {
PLAY,
PAUSE,

View File

@@ -1,7 +1,9 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include <QDoubleSpinBox>
#include <QHBoxLayout>
enum class PluginStateAction {
STOP,
NO_MATCH_BEHAVIOUR,

View File

@@ -1,8 +1,9 @@
#pragma once
#include <QDoubleSpinBox>
#include <QLabel>
#include "macro-action-edit.hpp"
#include <QComboBox>
#include <QHBoxLayout>
enum class RecordAction {
STOP,
START,

View File

@@ -1,8 +1,9 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include <QHBoxLayout>
enum class ReplayBufferAction {
STOP,
START,

View File

@@ -1,8 +1,11 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QHBoxLayout>
enum class StreamAction {
STOP,
START,

View File

@@ -1,7 +1,9 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include <QComboBox>
#include <QHBoxLayout>
enum class VCamAction {
STOP,
START,

View File

@@ -1,8 +1,10 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include <QDoubleSpinBox>
#include <QHBoxLayout>
enum class WaitType {
FIXED,
RANDOM,

View File

@@ -0,0 +1,76 @@
#pragma once
#include "macro.hpp"
#include <QSpinBox>
#include <QComboBox>
#include <QPushButton>
#include <QTimer>
#include <QFrame>
enum class CursorCondition {
REGION,
MOVING,
};
class MacroConditionCursor : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionCursor>();
}
CursorCondition _condition = CursorCondition::REGION;
int _minX = 0, _minY = 0, _maxX = 0, _maxY = 0;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionCursorEdit : public QWidget {
Q_OBJECT
public:
MacroConditionCursorEdit(
QWidget *parent,
std::shared_ptr<MacroConditionCursor> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionCursorEdit(
parent,
std::dynamic_pointer_cast<MacroConditionCursor>(cond));
}
private slots:
void ConditionChanged(int cond);
void MinXChanged(int pos);
void MinYChanged(int pos);
void MaxXChanged(int pos);
void MaxYChanged(int pos);
void UpdateCursorPos();
void ToggleFrame();
protected:
QSpinBox *_minX;
QSpinBox *_minY;
QSpinBox *_maxX;
QSpinBox *_maxY;
QComboBox *_conditions;
QPushButton *_frameToggle;
QLabel *_xPos;
QLabel *_yPos;
std::shared_ptr<MacroConditionCursor> _entryData;
private:
void SetRegionSelectionVisible(bool);
void SetupFrame();
QTimer _timer;
QFrame _frame;
bool _loading = true;
};

View File

@@ -1,10 +1,10 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QDateTimeEdit>
#include "duration-control.hpp"
#include <QCheckBox>
#include <QDateTimeEdit>
class MacroConditionDate : public MacroCondition {
public:
bool CheckCondition();

View File

@@ -1,12 +1,5 @@
#pragma once
#include "advanced-scene-switcher.hpp"
#include "macro.hpp"
#include "macro-condition-scene.hpp"
#include "section.hpp"
#include "utility.hpp"
#include <QGroupBox>
struct MacroConditionInfo {
using TCreateMethod = std::shared_ptr<MacroCondition> (*)();
@@ -34,16 +27,16 @@ private:
static std::map<std::string, MacroConditionInfo> _methods;
};
class MacroConditionEdit : public QWidget {
class MacroConditionEdit : public MacroSegmentEdit {
Q_OBJECT
public:
MacroConditionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroCondition> * = nullptr,
const std::string &id = "scene", bool root = true,
bool startCollapsed = false);
const std::string &id = "scene", bool root = true);
bool IsRootNode();
void UpdateEntryData(const std::string &id, bool collapse);
void SetRootNode(bool);
void UpdateEntryData(const std::string &id);
private slots:
void LogicSelectionChanged(int idx);
@@ -51,25 +44,15 @@ private slots:
void DurationChanged(double seconds);
void DurationConditionChanged(DurationCondition cond);
void DurationUnitChanged(DurationUnit unit);
void HeaderInfoChanged(const QString &);
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString newName);
void SceneGroupAdded(const QString &name);
void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName);
protected:
private:
MacroSegment *Data();
QComboBox *_logicSelection;
QComboBox *_conditionSelection;
Section *_section;
QLabel *_headerInfo;
DurationConstraintEdit *_dur;
std::shared_ptr<MacroCondition> *_entryData;
private:
bool _isRoot = true;
bool _loading = true;
};

View File

@@ -1,7 +1,10 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox>
enum class FilterCondition {
ENABLED,

View File

@@ -1,9 +1,11 @@
#pragma once
#include "macro.hpp"
#include "macro-selection.hpp"
#include <QWidget>
#include <QComboBox>
#include <QSpinBox>
#include <QLabel>
#include <QPushButton>
#include <QHBoxLayout>
#include <QTimer>
enum class MacroConditionMacroType {

View File

@@ -1,7 +1,8 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QCheckBox>
class MacroConditionProcess : public MacroCondition {
public:

View File

@@ -1,55 +0,0 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
class MacroConditionRegion : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionRegion>();
}
int _minX = 0, _minY = 0, _maxX = 0, _maxY = 0;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionRegionEdit : public QWidget {
Q_OBJECT
public:
MacroConditionRegionEdit(
QWidget *parent,
std::shared_ptr<MacroConditionRegion> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionRegionEdit(
parent,
std::dynamic_pointer_cast<MacroConditionRegion>(cond));
}
private slots:
void MinXChanged(int pos);
void MinYChanged(int pos);
void MaxXChanged(int pos);
void MaxYChanged(int pos);
protected:
QSpinBox *_minX;
QSpinBox *_minY;
QSpinBox *_maxX;
QSpinBox *_maxY;
std::shared_ptr<MacroConditionRegion> _entryData;
private:
bool _loading = true;
};

View File

@@ -1,7 +1,10 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox>
enum class SourceCondition {
ACTIVE,

View File

@@ -1,10 +1,11 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QTimer>
#include "duration-control.hpp"
#include <QCheckBox>
#include <QPushButton>
#include <QTimer>
class MacroConditionTimer : public MacroCondition {
public:
bool CheckCondition();

View File

@@ -1,7 +1,8 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QCheckBox>
class MacroConditionWindow : public MacroCondition {
public:

View File

@@ -0,0 +1,31 @@
#pragma once
#include <QFrame>
#include <QPropertyAnimation>
#include <QScrollArea>
#include <QPushButton>
#include <QLayout>
#include <QWidget>
class MacroEntryControls : public QWidget {
Q_OBJECT
public:
explicit MacroEntryControls(const int animationDuration = 300,
QWidget *parent = 0);
void Show(bool visible = true);
signals:
void Add();
void Remove();
void Up();
void Down();
private:
QPushButton *_add;
QPushButton *_remove;
QPushButton *_up;
QPushButton *_down;
QPropertyAnimation *_animation = nullptr;
};

View File

@@ -0,0 +1,71 @@
#pragma once
#include <QWidget>
#include <obs.hpp>
class MacroSegment {
public:
void SetIndex(int idx) { _idx = idx; }
int GetIndex() { return _idx; }
void SetCollapsed(bool collapsed) { _collapsed = collapsed; }
bool GetCollapsed() { return _collapsed; }
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual std::string GetShortDesc();
virtual std::string GetId() = 0;
protected:
int _idx;
bool _collapsed = false;
};
class Section;
class QLabel;
class MacroEntryControls;
class MacroSegmentEdit : public QWidget {
Q_OBJECT
public:
MacroSegmentEdit(QWidget *parent = nullptr);
// Use this function to avoid accidental edits when scrolling through
// list of actions and conditions
void SetFocusPolicyOfWidgets();
protected slots:
void HeaderInfoChanged(const QString &);
void Add();
void Remove();
void Up();
void Down();
void Collapsed(bool);
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString newName);
void SceneGroupAdded(const QString &name);
void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName);
void AddAt(int idx);
void RemoveAt(int idx);
void UpAt(int idx);
void DownAt(int idx);
protected:
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
Section *_section;
QLabel *_headerInfo;
MacroEntryControls *_controls;
private:
virtual MacroSegment *Data() = 0;
};
class MouseWheelWidgetAdjustmentGuard : public QObject {
public:
explicit MouseWheelWidgetAdjustmentGuard(QObject *parent);
protected:
bool eventFilter(QObject *o, QEvent *e) override;
};

View File

@@ -1,4 +1,5 @@
#pragma once
#include "macro-segment.hpp"
#include "duration-control.hpp"
#include <string>
@@ -36,14 +37,11 @@ struct LogicTypeInfo {
std::string _name;
};
class MacroCondition {
class MacroCondition : public MacroSegment {
public:
virtual bool CheckCondition() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual std::string GetShortDesc();
virtual std::string GetId() = 0;
LogicType GetLogicType() { return _logic; }
void SetLogicType(LogicType logic) { _logic = logic; }
static const std::map<LogicType, LogicTypeInfo> logicTypes;
@@ -61,13 +59,11 @@ private:
DurationConstraint _duration;
};
class MacroAction {
class MacroAction : public MacroSegment {
public:
virtual bool PerformAction() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual std::string GetShortDesc();
virtual std::string GetId() = 0;
virtual void LogAction();
};
@@ -89,6 +85,8 @@ public:
{
return _conditions;
}
void UpdateActionIndices();
void UpdateConditionIndices();
std::deque<std::shared_ptr<MacroAction>> &Actions() { return _actions; }
bool Save(obs_data_t *obj);

View File

@@ -23,6 +23,8 @@ protected:
private slots:
void AnimationFinished();
void Collapse(bool collapse);
signals:
void Collapsed(bool);
private:
void SetupAnimations();

View File

@@ -101,6 +101,8 @@ struct SwitcherData {
std::string currentTitle;
std::deque<ScreenRegionSwitch> screenRegionSwitches;
std::pair<int, int> lastCursorPos = {0, 0};
bool cursorPosChanged = false;
std::vector<std::string> ignoreWindowsSwitches;

View File

@@ -44,6 +44,7 @@ QString fromatJsonString(const char *);
void placeWidgets(std::string text, QBoxLayout *layout,
std::unordered_map<std::string, QWidget *> placeholders,
bool addStretch = true);
void deleteLayoutItem(QLayoutItem *item);
void clearLayout(QLayout *layout);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
QColor = QColor(0, 0, 0, 0),

View File

@@ -20,6 +20,7 @@
#include <util/platform.h>
#include <vector>
#include <thread>
#include <unordered_map>
#include <QStringList>
#include <QRegularExpression>
#include <QLibrary>
@@ -222,7 +223,7 @@ void GetCurrentWindowTitle(std::string &title)
int status = 0;
XTextProperty text;
if (xstatus == Success) {
if (xstatus == Success && data[0]) {
status = XGetTextProperty(disp(), data[0], &text,
XInternAtom(disp(), "_NET_WM_NAME",
true));

View File

@@ -1,7 +1,9 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/macro-action-edit.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include "headers/section.hpp"
#include "headers/macro-controls.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
std::map<std::string, MacroActionInfo> MacroActionFactory::_methods;
@@ -59,32 +61,14 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData,
const std::string &id, bool startCollapsed)
: QWidget(parent)
const std::string &id)
: MacroSegmentEdit(parent), _entryData(entryData)
{
_actionSelection = new QComboBox();
_section = new Section(300);
_headerInfo = new QLabel();
QWidget::connect(_actionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ActionSelectionChanged(const QString &)));
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
SIGNAL(MacroAdded(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SIGNAL(MacroRemoved(const QString &)));
QWidget::connect(parent,
SIGNAL(MacroRenamed(const QString &, const QString)),
this,
SIGNAL(MacroRenamed(const QString &, const QString)));
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
SIGNAL(SceneGroupAdded(const QString &)));
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
this, SIGNAL(SceneGroupRemoved(const QString &)));
QWidget::connect(
parent,
SIGNAL(SceneGroupRenamed(const QString &, const QString)), this,
SIGNAL(SceneGroupRenamed(const QString &, const QString)));
populateActionSelection(_actionSelection);
@@ -93,10 +77,11 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(_section);
mainLayout->addWidget(_controls);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData(id, startCollapsed);
UpdateEntryData(id);
_loading = false;
}
@@ -117,9 +102,10 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, false);
SetFocusPolicyOfWidgets();
}
void MacroActionEdit::UpdateEntryData(const std::string &id, bool collapse)
void MacroActionEdit::UpdateEntryData(const std::string &id)
{
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
@@ -128,13 +114,40 @@ void MacroActionEdit::UpdateEntryData(const std::string &id, bool collapse)
this, SLOT(HeaderInfoChanged(const QString &)));
HeaderInfoChanged(
QString::fromStdString((*_entryData)->GetShortDesc()));
_section->SetContent(widget, collapse);
_section->SetContent(widget, (*_entryData)->GetCollapsed());
SetFocusPolicyOfWidgets();
}
void MacroActionEdit::HeaderInfoChanged(const QString &text)
MacroSegment *MacroActionEdit::Data()
{
_headerInfo->setVisible(!text.isEmpty());
_headerInfo->setText(text);
return _entryData->get();
}
void AdvSceneSwitcher::AddMacroAction(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx > (int)macro->Actions().size()) {
return;
}
MacroActionSwitchScene temp;
std::string id = temp.GetId();
std::lock_guard<std::mutex> lock(switcher->m);
auto action = macro->Actions().emplace(macro->Actions().begin() + idx,
MacroActionFactory::Create(id));
macro->UpdateActionIndices();
// All entry pointers in existing edit widgets after the new entry will
// be invalidated by adding the new entry so we have to recreate them
// and because I am lazy I am recreating every widget.
//
// If performance should become a concern this has to be revisited.
SetEditMacro(*macro);
}
void AdvSceneSwitcher::on_actionAdd_clicked()
@@ -144,15 +157,31 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
return;
}
MacroActionSwitchScene temp;
std::string id = temp.GetId();
AddMacroAction((int)macro->Actions().size());
ui->macroEditActionHelp->setVisible(false);
}
void AdvSceneSwitcher::RemoveMacroAction(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->Actions().size()) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
macro->Actions().emplace_back(MacroActionFactory::Create(id));
auto newEntry =
new MacroActionEdit(this, &macro->Actions().back(), id, false);
ui->macroEditActionLayout->addWidget(newEntry);
ui->macroEditActionHelp->setVisible(false);
macro->Actions().erase(macro->Actions().begin() + idx);
macro->UpdateActionIndices();
// All entry pointers in existing edit widgets after the new entry will
// be invalidated by adding the new entry so we have to recreate them
// and because I am lazy I am recreating every widget.
//
// If performance should become a concern this has to be revisited.
SetEditMacro(*macro);
}
void AdvSceneSwitcher::on_actionRemove_clicked()
@@ -161,24 +190,61 @@ void AdvSceneSwitcher::on_actionRemove_clicked()
if (!macro) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
if (macro->Actions().empty()) {
RemoveMacroAction((int)macro->Actions().size() - 1);
}
void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
{
if (pos1 == pos2) {
return;
}
macro->Actions().pop_back();
int count = ui->macroEditActionLayout->count();
auto item = ui->macroEditActionLayout->takeAt(count - 1);
if (item) {
auto widget = item->widget();
if (widget) {
widget->setVisible(false);
}
delete item;
if (pos1 > pos2) {
std::swap(pos1, pos2);
}
if (count == 1) {
ui->macroEditActionHelp->setVisible(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(m->Actions().begin() + pos1, m->Actions().begin() + pos2);
m->UpdateActionIndices();
auto a1 = m->Actions().begin() + pos1;
auto a2 = m->Actions().begin() + pos2;
auto item1 = ui->macroEditActionLayout->takeAt(pos1);
auto item2 = ui->macroEditActionLayout->takeAt(pos2 - 1);
deleteLayoutItem(item1);
deleteLayoutItem(item2);
auto widget1 = new MacroActionEdit(this, &(*a1), (*a1)->GetId());
auto widget2 = new MacroActionEdit(this, &(*a2), (*a2)->GetId());
ConnectControlSignals(widget1);
ConnectControlSignals(widget2);
ui->macroEditActionLayout->insertWidget(pos1, widget1);
ui->macroEditActionLayout->insertWidget(pos2, widget2);
}
void AdvSceneSwitcher::MoveMacroActionUp(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
if (idx < 1 || idx >= (int)macro->Actions().size()) {
return;
}
SwapActions(macro, idx, idx - 1);
}
void AdvSceneSwitcher::MoveMacroActionDown(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->Actions().size() - 1) {
return;
}
SwapActions(macro, idx, idx + 1);
}

View File

@@ -73,18 +73,22 @@ MacroActionRunEdit::MacroActionRunEdit(
_addArg->setMaximumSize(QSize(22, 22));
_addArg->setProperty("themeID",
QVariant(QString::fromUtf8("addIconSmall")));
_addArg->setFlat(true);
_removeArg = new QPushButton();
_removeArg->setMaximumSize(QSize(22, 22));
_removeArg->setProperty("themeID",
QVariant(QString::fromUtf8("removeIconSmall")));
_removeArg->setFlat(true);
_argUp = new QPushButton();
_argUp->setMaximumSize(QSize(22, 22));
_argUp->setProperty("themeID",
QVariant(QString::fromUtf8("upArrowIconSmall")));
_argUp->setFlat(true);
_argDown = new QPushButton();
_argDown->setMaximumSize(QSize(22, 22));
_argDown->setProperty(
"themeID", QVariant(QString::fromUtf8("downArrowIconSmall")));
_argDown->setFlat(true);
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
SLOT(FilePathChanged()));

View File

@@ -0,0 +1,267 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-cursor.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionCursor::id = "cursor";
bool MacroConditionCursor::_registered = MacroConditionFactory::Register(
MacroConditionCursor::id,
{MacroConditionCursor::Create, MacroConditionCursorEdit::Create,
"AdvSceneSwitcher.condition.cursor"});
// TODO: Remove in future version - just added for backwards compatibility
static const std::string idOld = "region";
static bool oldRegisterd = MacroConditionFactory::Register(
idOld, {MacroConditionCursor::Create, MacroConditionCursorEdit::Create,
"AdvSceneSwitcher.condition.cursor"});
static std::map<CursorCondition, std::string> cursorConditionTypes = {
{CursorCondition::REGION,
"AdvSceneSwitcher.condition.cursor.type.region"},
{CursorCondition::MOVING,
"AdvSceneSwitcher.condition.cursor.type.moving"},
};
bool MacroConditionCursor::CheckCondition()
{
std::pair<int, int> cursorPos = getCursorPos();
switch (_condition) {
case CursorCondition::REGION:
return cursorPos.first >= _minX && cursorPos.second >= _minY &&
cursorPos.first <= _maxX && cursorPos.second <= _maxY;
case CursorCondition::MOVING:
return switcher->cursorPosChanged;
default:
break;
}
return false;
}
bool MacroConditionCursor::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_int(obj, "minX", _minX);
obs_data_set_int(obj, "minY", _minY);
obs_data_set_int(obj, "maxX", _maxX);
obs_data_set_int(obj, "maxY", _maxY);
return true;
}
bool MacroConditionCursor::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_condition = static_cast<CursorCondition>(
obs_data_get_int(obj, "condition"));
_minX = obs_data_get_int(obj, "minX");
_minY = obs_data_get_int(obj, "minY");
_maxX = obs_data_get_int(obj, "maxX");
_maxY = obs_data_get_int(obj, "maxY");
return true;
}
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : cursorConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionCursorEdit::MacroConditionCursorEdit(
QWidget *parent, std::shared_ptr<MacroConditionCursor> entryData)
: QWidget(parent)
{
_conditions = new QComboBox();
_minX = new QSpinBox();
_minY = new QSpinBox();
_maxX = new QSpinBox();
_maxY = new QSpinBox();
_frameToggle = new QPushButton(
obs_module_text("AdvSceneSwitcher.condition.cursor.showFrame"));
_xPos = new QLabel("-");
_yPos = new QLabel("-");
populateConditionSelection(_conditions);
_minX->setPrefix("Min X: ");
_minY->setPrefix("Min Y: ");
_maxX->setPrefix("Max X: ");
_maxY->setPrefix("Max Y: ");
_minX->setMinimum(-1000000);
_minY->setMinimum(-1000000);
_maxX->setMinimum(-1000000);
_maxY->setMinimum(-1000000);
_minX->setMaximum(1000000);
_minY->setMaximum(1000000);
_maxX->setMaximum(1000000);
_maxY->setMaximum(1000000);
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_minX, SIGNAL(valueChanged(int)), this,
SLOT(MinXChanged(int)));
QWidget::connect(_minY, SIGNAL(valueChanged(int)), this,
SLOT(MinYChanged(int)));
QWidget::connect(_maxX, SIGNAL(valueChanged(int)), this,
SLOT(MaxXChanged(int)));
QWidget::connect(_maxY, SIGNAL(valueChanged(int)), this,
SLOT(MaxYChanged(int)));
QWidget::connect(_frameToggle, SIGNAL(clicked()), this,
SLOT(ToggleFrame()));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{conditions}}", _conditions},
{"{{minX}}", _minX},
{"{{minY}}", _minY},
{"{{maxX}}", _maxX},
{"{{maxY}}", _maxY},
{"{{xPos}}", _xPos},
{"{{yPos}}", _yPos},
{"{{toggleFrameButton}}", _frameToggle},
};
QHBoxLayout *line1 = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.cursor.entry.line1"),
line1, widgetPlaceholders);
QHBoxLayout *line2 = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.cursor.entry.line2"),
line2, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1);
mainLayout->addLayout(line2);
setLayout(mainLayout);
connect(&_timer, &QTimer::timeout, this,
&MacroConditionCursorEdit::UpdateCursorPos);
_timer.start(1000);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionCursorEdit::ConditionChanged(int index)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<CursorCondition>(index);
SetRegionSelectionVisible(_entryData->_condition ==
CursorCondition::REGION);
}
void MacroConditionCursorEdit::MinXChanged(int pos)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_minX = pos;
SetupFrame();
}
void MacroConditionCursorEdit::MinYChanged(int pos)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_minY = pos;
SetupFrame();
}
void MacroConditionCursorEdit::MaxXChanged(int pos)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_maxX = pos;
SetupFrame();
}
void MacroConditionCursorEdit::MaxYChanged(int pos)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_maxY = pos;
SetupFrame();
}
void MacroConditionCursorEdit::UpdateCursorPos()
{
std::pair<int, int> position = getCursorPos();
_xPos->setText(QString::number(position.first));
_yPos->setText(QString::number(position.second));
}
void MacroConditionCursorEdit::ToggleFrame()
{
SetupFrame();
if (_frame.isVisible()) {
_frameToggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.cursor.showFrame"));
_frame.hide();
} else {
_frameToggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.cursor.hideFrame"));
_frame.show();
}
}
void MacroConditionCursorEdit::SetRegionSelectionVisible(bool visible)
{
_minX->setVisible(visible);
_minY->setVisible(visible);
_maxX->setVisible(visible);
_maxY->setVisible(visible);
_frameToggle->setVisible(visible);
if (_frame.isVisible()) {
ToggleFrame();
}
adjustSize();
}
void MacroConditionCursorEdit::SetupFrame()
{
_frame.setFrameStyle(QFrame::Box | QFrame::Plain);
_frame.setWindowFlags(Qt::FramelessWindowHint | Qt::Tool |
Qt::WindowTransparentForInput |
Qt::WindowDoesNotAcceptFocus |
Qt::WindowStaysOnTopHint);
_frame.setAttribute(Qt::WA_TranslucentBackground, true);
if (_entryData) {
_frame.setGeometry(_entryData->_minX, _entryData->_minY,
_entryData->_maxX - _entryData->_minX,
_entryData->_maxY - _entryData->_minY);
}
}
void MacroConditionCursorEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_minX->setValue(_entryData->_minX);
_minY->setValue(_entryData->_minY);
_maxX->setValue(_entryData->_maxX);
_maxY->setValue(_entryData->_maxY);
SetRegionSelectionVisible(_entryData->_condition ==
CursorCondition::REGION);
}

View File

@@ -1,5 +1,9 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-scene.hpp"
#include "headers/section.hpp"
#include "headers/macro-controls.hpp"
#include "headers/utility.hpp"
std::map<std::string, MacroConditionInfo> MacroConditionFactory::_methods;
@@ -87,13 +91,11 @@ static inline void populateConditionSelection(QComboBox *list)
MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root, bool startCollapsed)
: QWidget(parent)
const std::string &id, bool root)
: MacroSegmentEdit(parent), _entryData(entryData), _isRoot(root)
{
_logicSelection = new QComboBox();
_conditionSelection = new QComboBox();
_section = new Section(300);
_headerInfo = new QLabel();
_dur = new DurationConstraintEdit();
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
@@ -108,22 +110,6 @@ MacroConditionEdit::MacroConditionEdit(
QWidget::connect(_dur, SIGNAL(ConditionChanged(DurationCondition)),
this,
SLOT(DurationConditionChanged(DurationCondition)));
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
SIGNAL(MacroAdded(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SIGNAL(MacroRemoved(const QString &)));
QWidget::connect(parent,
SIGNAL(MacroRenamed(const QString &, const QString)),
this,
SIGNAL(MacroRenamed(const QString &, const QString)));
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
SIGNAL(SceneGroupAdded(const QString &)));
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
this, SIGNAL(SceneGroupRemoved(const QString &)));
QWidget::connect(
parent,
SIGNAL(SceneGroupRenamed(const QString &, const QString)), this,
SIGNAL(SceneGroupRenamed(const QString &, const QString)));
populateLogicSelection(_logicSelection, root);
populateConditionSelection(_conditionSelection);
@@ -135,11 +121,10 @@ MacroConditionEdit::MacroConditionEdit(
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(_section);
mainLayout->addWidget(_controls);
setLayout(mainLayout);
_entryData = entryData;
_isRoot = root;
UpdateEntryData(id, startCollapsed);
UpdateEntryData(id);
_loading = false;
}
@@ -165,7 +150,14 @@ bool MacroConditionEdit::IsRootNode()
return _isRoot;
}
void MacroConditionEdit::UpdateEntryData(const std::string &id, bool collapse)
void MacroConditionEdit::SetRootNode(bool root)
{
const QSignalBlocker blocker(_logicSelection);
_logicSelection->clear();
populateLogicSelection(_logicSelection, root);
}
void MacroConditionEdit::UpdateEntryData(const std::string &id)
{
_conditionSelection->setCurrentText(obs_module_text(
MacroConditionFactory::GetConditionName(id).c_str()));
@@ -182,11 +174,12 @@ void MacroConditionEdit::UpdateEntryData(const std::string &id, bool collapse)
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
logic_root_offset);
}
_section->SetContent(widget, collapse);
_section->SetContent(widget, (*_entryData)->GetCollapsed());
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
auto constraint = (*_entryData)->GetDurationConstraint();
_dur->SetValue(constraint);
SetFocusPolicyOfWidgets();
}
void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
@@ -212,6 +205,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, false);
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
SetFocusPolicyOfWidgets();
}
void MacroConditionEdit::DurationChanged(double seconds)
@@ -244,10 +238,40 @@ void MacroConditionEdit::DurationUnitChanged(DurationUnit unit)
(*_entryData)->SetDurationUnit(unit);
}
void MacroConditionEdit::HeaderInfoChanged(const QString &text)
MacroSegment *MacroConditionEdit::Data()
{
_headerInfo->setVisible(!text.isEmpty());
_headerInfo->setText(text);
return _entryData->get();
}
void AdvSceneSwitcher::AddMacroCondition(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx > (int)macro->Conditions().size()) {
return;
}
MacroConditionScene temp;
std::string id = temp.GetId();
std::lock_guard<std::mutex> lock(switcher->m);
bool root = idx == 0;
auto cond =
macro->Conditions().emplace(macro->Conditions().begin() + idx,
MacroConditionFactory::Create(id));
auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE;
(*cond)->SetLogicType(logic);
macro->UpdateConditionIndices();
// All entry pointers in existing edit widgets after the new entry will
// be invalidated by adding the new entry so we have to recreate them
// and because I am lazy I am recreating every widget.
//
// If performance should become a concern this has to be revisited.
SetEditMacro(*macro);
}
void AdvSceneSwitcher::on_conditionAdd_clicked()
@@ -256,18 +280,41 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
if (!macro) {
return;
}
MacroConditionScene temp;
std::string id = temp.GetId();
AddMacroCondition((int)macro->Conditions().size());
ui->macroEditConditionHelp->setVisible(false);
}
void AdvSceneSwitcher::RemoveMacroCondition(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->Conditions().size()) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
bool root = macro->Conditions().size() == 0;
macro->Conditions().emplace_back(MacroConditionFactory::Create(id));
auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE;
macro->Conditions().back()->SetLogicType(logic);
auto newEntry = new MacroConditionEdit(
this, &macro->Conditions().back(), id, root, false);
ui->macroEditConditionLayout->addWidget(newEntry);
ui->macroEditConditionHelp->setVisible(false);
macro->Conditions().erase(macro->Conditions().begin() + idx);
macro->UpdateConditionIndices();
if (macro->Conditions().size() == 0) {
ui->macroEditConditionHelp->setVisible(true);
}
if (idx == 0 && macro->Conditions().size() > 0) {
auto newRoot = macro->Conditions().at(0);
newRoot->SetLogicType(LogicType::ROOT_NONE);
}
// All entry pointers in existing edit widgets after the new entry will
// be invalidated by adding the new entry so we have to recreate them
// and because I am lazy I am recreating every widget.
//
// If performance should become a concern this has to be revisited.
SetEditMacro(*macro);
}
void AdvSceneSwitcher::on_conditionRemove_clicked()
@@ -276,24 +323,71 @@ void AdvSceneSwitcher::on_conditionRemove_clicked()
if (!macro) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
if (macro->Conditions().empty()) {
RemoveMacroCondition((int)macro->Conditions().size() - 1);
}
void AdvSceneSwitcher::SwapConditions(Macro *m, int pos1, int pos2)
{
if (pos1 == pos2) {
return;
}
macro->Conditions().pop_back();
int count = ui->macroEditConditionLayout->count();
auto item = ui->macroEditConditionLayout->takeAt(count - 1);
if (item) {
auto widget = item->widget();
if (widget) {
widget->setVisible(false);
}
delete item;
if (pos1 > pos2) {
std::swap(pos1, pos2);
}
if (count == 1) {
ui->macroEditConditionHelp->setVisible(true);
bool root = pos1 == 0;
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(m->Conditions().begin() + pos1,
m->Conditions().begin() + pos2);
m->UpdateConditionIndices();
auto c1 = m->Conditions().begin() + pos1;
auto c2 = m->Conditions().begin() + pos2;
if (root) {
auto logic1 = (*c1)->GetLogicType();
auto logic2 = (*c2)->GetLogicType();
(*c1)->SetLogicType(logic2);
(*c2)->SetLogicType(logic1);
}
auto item1 = ui->macroEditConditionLayout->takeAt(pos1);
auto item2 = ui->macroEditConditionLayout->takeAt(pos2 - 1);
deleteLayoutItem(item1);
deleteLayoutItem(item2);
auto widget1 =
new MacroConditionEdit(this, &(*c1), (*c1)->GetId(), root);
auto widget2 =
new MacroConditionEdit(this, &(*c2), (*c2)->GetId(), false);
ConnectControlSignals(widget1);
ConnectControlSignals(widget2);
ui->macroEditConditionLayout->insertWidget(pos1, widget1);
ui->macroEditConditionLayout->insertWidget(pos2, widget2);
}
void AdvSceneSwitcher::MoveMacroConditionUp(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
if (idx < 1 || idx >= (int)macro->Conditions().size()) {
return;
}
SwapConditions(macro, idx, idx - 1);
}
void AdvSceneSwitcher::MoveMacroConditionDown(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->Conditions().size() - 1) {
return;
}
SwapConditions(macro, idx, idx + 1);
}

View File

@@ -1,140 +0,0 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-region.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionRegion::id = "region";
bool MacroConditionRegion::_registered = MacroConditionFactory::Register(
MacroConditionRegion::id,
{MacroConditionRegion::Create, MacroConditionRegionEdit::Create,
"AdvSceneSwitcher.condition.region"});
bool MacroConditionRegion::CheckCondition()
{
std::pair<int, int> cursorPos = getCursorPos();
return cursorPos.first >= _minX && cursorPos.second >= _minY &&
cursorPos.first <= _maxX && cursorPos.second <= _maxY;
}
bool MacroConditionRegion::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "minX", _minX);
obs_data_set_int(obj, "minY", _minY);
obs_data_set_int(obj, "maxX", _maxX);
obs_data_set_int(obj, "maxY", _maxY);
return true;
}
bool MacroConditionRegion::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_minX = obs_data_get_int(obj, "minX");
_minY = obs_data_get_int(obj, "minY");
_maxX = obs_data_get_int(obj, "maxX");
_maxY = obs_data_get_int(obj, "maxY");
return true;
}
MacroConditionRegionEdit::MacroConditionRegionEdit(
QWidget *parent, std::shared_ptr<MacroConditionRegion> entryData)
: QWidget(parent)
{
_minX = new QSpinBox();
_minY = new QSpinBox();
_maxX = new QSpinBox();
_maxY = new QSpinBox();
_minX->setPrefix("Min X: ");
_minY->setPrefix("Min Y: ");
_maxX->setPrefix("Max X: ");
_maxY->setPrefix("Max Y: ");
_minX->setMinimum(-1000000);
_minY->setMinimum(-1000000);
_maxX->setMinimum(-1000000);
_maxY->setMinimum(-1000000);
_minX->setMaximum(1000000);
_minY->setMaximum(1000000);
_maxX->setMaximum(1000000);
_maxY->setMaximum(1000000);
QWidget::connect(_minX, SIGNAL(valueChanged(int)), this,
SLOT(MinXChanged(int)));
QWidget::connect(_minY, SIGNAL(valueChanged(int)), this,
SLOT(MinYChanged(int)));
QWidget::connect(_maxX, SIGNAL(valueChanged(int)), this,
SLOT(MaxXChanged(int)));
QWidget::connect(_maxY, SIGNAL(valueChanged(int)), this,
SLOT(MaxYChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{minX}}", _minX},
{"{{minY}}", _minY},
{"{{maxX}}", _maxX},
{"{{maxY}}", _maxY}};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.region.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionRegionEdit::MinXChanged(int pos)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_minX = pos;
}
void MacroConditionRegionEdit::MinYChanged(int pos)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_minY = pos;
}
void MacroConditionRegionEdit::MaxXChanged(int pos)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_maxX = pos;
}
void MacroConditionRegionEdit::MaxYChanged(int pos)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_maxY = pos;
}
void MacroConditionRegionEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_minX->setValue(_entryData->_minX);
_minY->setValue(_entryData->_minY);
_maxX->setValue(_entryData->_maxX);
_maxY->setValue(_entryData->_maxY);
}

70
src/macro-controls.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "headers/macro-controls.hpp"
MacroEntryControls::MacroEntryControls(const int animationDuration,
QWidget *parent)
: QWidget(parent)
{
_add = new QPushButton(this);
_add->setMaximumSize(QSize(22, 22));
_add->setProperty("themeID",
QVariant(QString::fromUtf8("addIconSmall")));
_add->setFlat(true);
_remove = new QPushButton(this);
_remove->setMaximumSize(QSize(22, 22));
_remove->setProperty("themeID",
QVariant(QString::fromUtf8("removeIconSmall")));
_remove->setFlat(true);
_up = new QPushButton(this);
_up->setMaximumSize(QSize(22, 22));
_up->setProperty("themeID",
QVariant(QString::fromUtf8("upArrowIconSmall")));
_up->setFlat(true);
_down = new QPushButton(this);
_down->setMaximumSize(QSize(22, 22));
_down->setProperty("themeID",
QVariant(QString::fromUtf8("downArrowIconSmall")));
_down->setFlat(true);
auto line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
auto mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(_add);
mainLayout->addWidget(_remove);
auto *separator = new QFrame(this);
separator->setFrameShape(QFrame::VLine);
separator->setFrameShadow(QFrame::Sunken);
mainLayout->addWidget(separator);
mainLayout->addWidget(_up);
mainLayout->addWidget(_down);
mainLayout->addWidget(line);
mainLayout->setContentsMargins(0, 0, 0, 0);
_animation = new QPropertyAnimation(this, "maximumHeight");
_animation->setDuration(animationDuration);
_animation->setStartValue(0);
_animation->setEndValue(mainLayout->sizeHint().height());
setMaximumHeight(0);
setLayout(mainLayout);
connect(_add, &QPushButton::clicked, this, &MacroEntryControls::Add);
connect(_remove, &QPushButton::clicked, this,
&MacroEntryControls::Remove);
connect(_up, &QPushButton::clicked, this, &MacroEntryControls::Up);
connect(_down, &QPushButton::clicked, this, &MacroEntryControls::Down);
}
void MacroEntryControls::Show(bool visible)
{
_animation->setDirection(visible ? QAbstractAnimation::Forward
: QAbstractAnimation::Backward);
_animation->start();
}

141
src/macro-segment.cpp Normal file
View File

@@ -0,0 +1,141 @@
#include "headers/macro-segment.hpp"
#include "headers/macro-controls.hpp"
#include "headers/section.hpp"
#include <obs.hpp>
#include <QEvent>
#include <QLabel>
bool MacroSegment::Save(obs_data_t *obj)
{
obs_data_set_bool(obj, "collapsed", static_cast<int>(_collapsed));
return true;
}
bool MacroSegment::Load(obs_data_t *obj)
{
_collapsed = obs_data_get_bool(obj, "collapsed");
return true;
}
std::string MacroSegment::GetShortDesc()
{
return "";
}
MouseWheelWidgetAdjustmentGuard::MouseWheelWidgetAdjustmentGuard(QObject *parent)
: QObject(parent)
{
}
bool MouseWheelWidgetAdjustmentGuard::eventFilter(QObject *o, QEvent *e)
{
const QWidget *widget = static_cast<QWidget *>(o);
if (e->type() == QEvent::Wheel && widget && !widget->hasFocus()) {
e->ignore();
return true;
}
return QObject::eventFilter(o, e);
}
MacroSegmentEdit::MacroSegmentEdit(QWidget *parent) : QWidget(parent)
{
_section = new Section(300);
_headerInfo = new QLabel();
_controls = new MacroEntryControls();
QWidget::connect(_section, &Section::Collapsed, this,
&MacroSegmentEdit::Collapsed);
// Macro signals
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
SIGNAL(MacroAdded(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SIGNAL(MacroRemoved(const QString &)));
QWidget::connect(parent,
SIGNAL(MacroRenamed(const QString &, const QString)),
this,
SIGNAL(MacroRenamed(const QString &, const QString)));
// Scene group signals
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
SIGNAL(SceneGroupAdded(const QString &)));
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
this, SIGNAL(SceneGroupRemoved(const QString &)));
QWidget::connect(
parent,
SIGNAL(SceneGroupRenamed(const QString &, const QString)), this,
SIGNAL(SceneGroupRenamed(const QString &, const QString)));
// Control signals
QWidget::connect(_controls, &MacroEntryControls::Add, this,
&MacroSegmentEdit::Add);
QWidget::connect(_controls, &MacroEntryControls::Remove, this,
&MacroSegmentEdit::Remove);
QWidget::connect(_controls, &MacroEntryControls::Up, this,
&MacroSegmentEdit::Up);
QWidget::connect(_controls, &MacroEntryControls::Down, this,
&MacroSegmentEdit::Down);
}
void MacroSegmentEdit::HeaderInfoChanged(const QString &text)
{
_headerInfo->setVisible(!text.isEmpty());
_headerInfo->setText(text);
}
void MacroSegmentEdit::Add()
{
if (Data()) {
// Insert after current entry
emit AddAt(Data()->GetIndex() + 1);
}
}
void MacroSegmentEdit::Remove()
{
if (Data()) {
emit RemoveAt(Data()->GetIndex());
}
}
void MacroSegmentEdit::Up()
{
if (Data()) {
emit UpAt(Data()->GetIndex());
}
}
void MacroSegmentEdit::Down()
{
if (Data()) {
emit DownAt(Data()->GetIndex());
}
}
void MacroSegmentEdit::Collapsed(bool collapsed)
{
if (Data()) {
Data()->SetCollapsed(collapsed);
}
}
void MacroSegmentEdit::enterEvent(QEvent *)
{
_controls->Show(true);
}
void MacroSegmentEdit::leaveEvent(QEvent *)
{
_controls->Show(false);
}
void MacroSegmentEdit::SetFocusPolicyOfWidgets()
{
QList<QWidget *> widgets = this->findChildren<QWidget *>();
for (auto w : widgets) {
w->setFocusPolicy(Qt::StrongFocus);
w->installEventFilter(new MouseWheelWidgetAdjustmentGuard(w));
}
}

View File

@@ -3,14 +3,12 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/name-dialog.hpp"
#include "headers/utility.hpp"
#include <QMenu>
static QMetaObject::Connection addPulse;
const auto conditionsCollapseThreshold = 4;
const auto actionsCollapseThreshold = 4;
bool macroNameExists(std::string name)
{
return !!GetMacroByName(name.c_str());
@@ -175,20 +173,19 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m)
clearLayout(ui->macroEditConditionLayout);
clearLayout(ui->macroEditActionLayout);
bool collapse = m.Conditions().size() > conditionsCollapseThreshold;
bool root = true;
for (auto &c : m.Conditions()) {
auto newEntry = new MacroConditionEdit(this, &c, c->GetId(),
root, collapse);
auto newEntry =
new MacroConditionEdit(this, &c, c->GetId(), root);
ConnectControlSignals(newEntry);
ui->macroEditConditionLayout->addWidget(newEntry);
ui->macroEditConditionHelp->setVisible(false);
root = false;
}
collapse = m.Actions().size() > actionsCollapseThreshold;
for (auto &a : m.Actions()) {
auto newEntry =
new MacroActionEdit(this, &a, a->GetId(), collapse);
auto newEntry = new MacroActionEdit(this, &a, a->GetId());
ConnectControlSignals(newEntry);
ui->macroEditActionLayout->addWidget(newEntry);
ui->macroEditActionHelp->setVisible(false);
}
@@ -208,6 +205,30 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m)
}
}
void AdvSceneSwitcher::ConnectControlSignals(MacroActionEdit *c)
{
connect(c, &MacroActionEdit::AddAt, this,
&AdvSceneSwitcher::AddMacroAction);
connect(c, &MacroActionEdit::RemoveAt, this,
&AdvSceneSwitcher::RemoveMacroAction);
connect(c, &MacroActionEdit::UpAt, this,
&AdvSceneSwitcher::MoveMacroActionUp);
connect(c, &MacroActionEdit::DownAt, this,
&AdvSceneSwitcher::MoveMacroActionDown);
}
void AdvSceneSwitcher::ConnectControlSignals(MacroConditionEdit *c)
{
connect(c, &MacroConditionEdit::AddAt, this,
&AdvSceneSwitcher::AddMacroCondition);
connect(c, &MacroConditionEdit::RemoveAt, this,
&AdvSceneSwitcher::RemoveMacroCondition);
connect(c, &MacroConditionEdit::UpAt, this,
&AdvSceneSwitcher::MoveMacroConditionUp);
connect(c, &MacroConditionEdit::DownAt, this,
&AdvSceneSwitcher::MoveMacroConditionDown);
}
Macro *AdvSceneSwitcher::getSelectedMacro()
{
QListWidgetItem *item = ui->macros->currentItem();
@@ -280,22 +301,52 @@ void AdvSceneSwitcher::setupMacroTab()
}
ui->macros->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->macros, SIGNAL(customContextMenuRequested(QPoint)), this,
SLOT(showMacroContextMenu(QPoint)));
connect(ui->macros, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroContextMenu);
ui->macroActions->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->macroActions, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroActionsContextMenu);
ui->macroConditions->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->macroConditions, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroConditionsContextMenu);
ui->macroEdit->setDisabled(true);
}
void AdvSceneSwitcher::showMacroContextMenu(const QPoint &pos)
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
{
QPoint globalPos = ui->macros->mapToGlobal(pos);
QMenu myMenu;
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.copy"),
this, SLOT(copyMacro()));
this, &AdvSceneSwitcher::CopyMacro);
myMenu.exec(globalPos);
}
void AdvSceneSwitcher::copyMacro()
void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
{
QPoint globalPos = ui->macroActions->mapToGlobal(pos);
QMenu myMenu;
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
this, &AdvSceneSwitcher::ExpandAllActions);
myMenu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), this,
&AdvSceneSwitcher::CollapseAllActions);
myMenu.exec(globalPos);
}
void AdvSceneSwitcher::ShowMacroConditionsContextMenu(const QPoint &pos)
{
QPoint globalPos = ui->macroConditions->mapToGlobal(pos);
QMenu myMenu;
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
this, &AdvSceneSwitcher::ExpandAllConditions);
myMenu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), this,
&AdvSceneSwitcher::CollapseAllConditions);
myMenu.exec(globalPos);
}
void AdvSceneSwitcher::CopyMacro()
{
obs_data_t *data = obs_data_create();
getSelectedMacro()->Save(data);
@@ -317,3 +368,55 @@ void AdvSceneSwitcher::copyMacro()
item->setCheckState(Qt::Checked);
ui->macros->setCurrentItem(item);
}
void AdvSceneSwitcher::ExpandAllActions()
{
auto m = getSelectedMacro();
if (!m) {
return;
}
for (auto &a : m->Actions()) {
a->SetCollapsed(false);
}
SetEditMacro(*m);
}
void AdvSceneSwitcher::ExpandAllConditions()
{
auto m = getSelectedMacro();
if (!m) {
return;
}
for (auto &c : m->Conditions()) {
c->SetCollapsed(false);
}
SetEditMacro(*m);
}
void AdvSceneSwitcher::CollapseAllActions()
{
auto m = getSelectedMacro();
if (!m) {
return;
}
for (auto &a : m->Actions()) {
a->SetCollapsed(true);
}
SetEditMacro(*m);
}
void AdvSceneSwitcher::CollapseAllConditions()
{
auto m = getSelectedMacro();
if (!m) {
return;
}
for (auto &c : m->Conditions()) {
c->SetCollapsed(true);
}
SetEditMacro(*m);
}

View File

@@ -1,12 +1,13 @@
#include "headers/macro.hpp"
#include "headers/macro-action-edit.hpp"
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include <limits>
#undef max
#include <chrono>
#include <unordered_map>
constexpr int perfLogThreshold = 300;
@@ -42,7 +43,7 @@ bool Macro::CeckMatch()
endTime - startTime);
if (ms.count() >= perfLogThreshold) {
blog(LOG_WARNING,
"spent %d ms in %s condition check of macro '%s'!",
"spent %ld ms in %s condition check of macro '%s'!",
ms.count(), c->GetId().c_str(), Name().c_str());
}
@@ -124,6 +125,24 @@ void Macro::SetName(const std::string &name)
SetHotkeysDesc();
}
void Macro::UpdateActionIndices()
{
int idx = 0;
for (auto a : _actions) {
a->SetIndex(idx);
idx++;
}
}
void Macro::UpdateConditionIndices()
{
int idx = 0;
for (auto c : _conditions) {
c->SetIndex(idx);
idx++;
}
}
bool Macro::Save(obs_data_t *obj)
{
obs_data_set_string(obj, "name", _name.c_str());
@@ -238,6 +257,7 @@ bool Macro::Load(obs_data_t *obj)
root = false;
}
obs_data_array_release(conditions);
UpdateConditionIndices();
obs_data_array_t *actions = obs_data_get_array(obj, "actions");
count = obs_data_array_count(actions);
@@ -260,6 +280,7 @@ bool Macro::Load(obs_data_t *obj)
obs_data_release(array_obj);
}
obs_data_array_release(actions);
UpdateActionIndices();
return true;
}
@@ -365,6 +386,7 @@ void Macro::SetHotkeysDesc()
bool MacroCondition::Save(obs_data_t *obj)
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
_duration.Save(obj);
@@ -373,16 +395,12 @@ bool MacroCondition::Save(obs_data_t *obj)
bool MacroCondition::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
_duration.Load(obj);
return true;
}
std::string MacroCondition::GetShortDesc()
{
return "";
}
void MacroCondition::SetDurationConstraint(const DurationConstraint &dur)
{
_duration = dur;
@@ -405,21 +423,17 @@ void MacroCondition::SetDuration(double seconds)
bool MacroAction::Save(obs_data_t *obj)
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
return true;
}
bool MacroAction::Load(obs_data_t *obj)
{
UNUSED_PARAMETER(obj);
MacroSegment::Load(obj);
return true;
}
std::string MacroAction::GetShortDesc()
{
return "";
}
void MacroAction::LogAction()
{
vblog(LOG_INFO, "performed action %s", GetId().c_str());

View File

@@ -51,6 +51,7 @@ void Section::Collapse(bool collapse)
_transitioning = true;
_collapsed = collapse;
_toggleAnimation->start();
emit Collapsed(collapse);
}
void Section::SetContent(QWidget *w, bool collapsed)

View File

@@ -174,6 +174,17 @@ void placeWidgets(std::string text, QBoxLayout *layout,
}
}
void deleteLayoutItem(QLayoutItem *item)
{
if (item) {
auto widget = item->widget();
if (widget) {
widget->setVisible(false);
}
delete item;
}
}
void clearLayout(QLayout *layout)
{
QLayoutItem *item;