Compare commits

..

2 Commits

Author SHA1 Message Date
WarmUpTill
5a69e774c2 Fix warnings 2025-12-11 18:43:49 +01:00
WarmUpTill
2389f37ed6 Add temp var for last recording save path 2025-12-11 17:22:37 +01:00
112 changed files with 1431 additions and 4123 deletions

View File

@@ -6,18 +6,18 @@ You have the option to ...
Both methods require [CMake](https://cmake.org/download/). Both methods require [CMake](https://cmake.org/download/).
The plugin can be compiled for OBS 31 and above, although using the latest version of OBS is recommended to support all features. The plugin can be compiled for OBS 27 and above, although using the latest version of OBS is recommended to support all features.
## Compiling in tree (recommended for development) ## Compiling in tree (recommended for development)
This section assumes that you have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio). This section assumes that you have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio).
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/plugins: Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
``` ```
cd obs-studio/plugins cd obs-studio/UI/frontend-plugins/
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
``` ```
Then modify the obs-studio/plugins/CMakeLists.txt and add an entry for the scene switcher: Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt Example and add an entry for the scene switcher:
``` ```
add_subdirectory(SceneSwitcher) add_subdirectory(SceneSwitcher)
``` ```

View File

@@ -166,7 +166,6 @@ target_sources(
lib/macro/macro-tab.cpp lib/macro/macro-tab.cpp
lib/macro/macro-tree.cpp lib/macro/macro-tree.cpp
lib/macro/macro-tree.hpp lib/macro/macro-tree.hpp
lib/macro/macro-websocket-trigger.cpp
lib/macro/macro.cpp lib/macro/macro.cpp
lib/macro/macro.hpp) lib/macro/macro.hpp)
@@ -185,8 +184,6 @@ target_sources(
lib/utils/canvas-helpers.hpp lib/utils/canvas-helpers.hpp
lib/utils/condition-logic.cpp lib/utils/condition-logic.cpp
lib/utils/condition-logic.hpp lib/utils/condition-logic.hpp
lib/utils/crash-handler.cpp
lib/utils/crash-handler.hpp
lib/utils/curl-helper.cpp lib/utils/curl-helper.cpp
lib/utils/curl-helper.hpp lib/utils/curl-helper.hpp
lib/utils/cursor-shape-changer.cpp lib/utils/cursor-shape-changer.cpp
@@ -204,8 +201,6 @@ target_sources(
lib/utils/file-selection.hpp lib/utils/file-selection.hpp
lib/utils/filter-combo-box.cpp lib/utils/filter-combo-box.cpp
lib/utils/filter-combo-box.hpp lib/utils/filter-combo-box.hpp
lib/utils/first-run-wizard.cpp
lib/utils/first-run-wizard.hpp
lib/utils/help-icon.hpp lib/utils/help-icon.hpp
lib/utils/help-icon.cpp lib/utils/help-icon.cpp
lib/utils/item-selection-helpers.cpp lib/utils/item-selection-helpers.cpp
@@ -456,29 +451,14 @@ else()
endif() endif()
if(PROCPS2_INCLUDE_DIR) if(PROCPS2_INCLUDE_DIR)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(libproc2 IMPORTED_TARGET libproc2<4.0.5 QUIET)
if(libproc2_FOUND)
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_USE_INFO)
endif()
pkg_check_modules(libproc2 REQUIRED IMPORTED_TARGET libproc2) pkg_check_modules(libproc2 REQUIRED IMPORTED_TARGET libproc2)
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}") set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE) target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
set(PROCESS_CONDITION_SUPPORTED 1) set(PROCESS_CONDITION_SUPPORTED 1)
# Check if PIDS_VAL takes 4 arguments (old API, pre-4.0.5) or 3 (new API)
include(CheckCSourceCompiles)
set(CMAKE_REQUIRED_INCLUDES "${PROCPS2_INCLUDE_DIR}")
set(CMAKE_REQUIRED_LIBRARIES proc2)
check_c_source_compiles(
"
#include <libproc2/pids.h>
int main(void) {
struct pids_stack *s = 0;
struct pids_info *i = 0;
(void)PIDS_VAL(0, str, s, i);
return 0;
}
"
PROCPS2_PIDS_VAL_TAKES_INFO)
if(PROCPS2_PIDS_VAL_TAKES_INFO)
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_USE_INFO)
endif()
endif() endif()
if(NOT DEFINED PROCESS_CONDITION_SUPPORTED) if(NOT DEFINED PROCESS_CONDITION_SUPPORTED)
message( message(

View File

@@ -64,7 +64,7 @@ function(_git_find_closest_git_dir _start_dir _git_dir_var)
while(NOT EXISTS "${git_dir}") while(NOT EXISTS "${git_dir}")
# .git dir not found, search parent directories # .git dir not found, search parent directories
set(git_previous_parent "${cur_dir}") set(git_previous_parent "${cur_dir}")
get_filename_component(cur_dir "${cur_dir}" DIRECTORY) get_filename_component(cur_dir ${cur_dir} DIRECTORY)
if(cur_dir STREQUAL git_previous_parent) if(cur_dir STREQUAL git_previous_parent)
# We have reached the root directory, we are not in git # We have reached the root directory, we are not in git
set(${_git_dir_var} set(${_git_dir_var}

View File

@@ -76,6 +76,7 @@ AdvSceneSwitcher.macroTab.name="Name:"
AdvSceneSwitcher.macroTab.run="Makro ausführen" AdvSceneSwitcher.macroTab.run="Makro ausführen"
AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt.\nSoll die aktuelle Ausführung gestoppt werden?" AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt.\nSoll die aktuelle Ausführung gestoppt werden?"
AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen" AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen"
AdvSceneSwitcher.macroTab.onChange="Nur bei Änderung ausführen"
AdvSceneSwitcher.macroTab.defaultname="Makro %1" AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1" AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1"
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?" AdvSceneSwitcher.macroTab.removeGroupPopup.text="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
@@ -290,14 +291,14 @@ AdvSceneSwitcher.condition.replay.state.started="Replay Buffer gestartet"
AdvSceneSwitcher.condition.replay.state.saved="Replay Buffer gespeichert" AdvSceneSwitcher.condition.replay.state.saved="Replay Buffer gespeichert"
AdvSceneSwitcher.condition.replay.entry="{{state}}" AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Datum" AdvSceneSwitcher.condition.date="Datum"
AdvSceneSwitcher.day.any="Beliebiger Tag" AdvSceneSwitcher.condition.date.anyDay="Beliebiger Tag"
AdvSceneSwitcher.day.monday="Montag" AdvSceneSwitcher.condition.date.monday="Montag"
AdvSceneSwitcher.day.tuesday="Dienstag" AdvSceneSwitcher.condition.date.tuesday="Dienstag"
AdvSceneSwitcher.day.wednesday="Mittwoch" AdvSceneSwitcher.condition.date.wednesday="Mittwoch"
AdvSceneSwitcher.day.thursday="Donnerstag" AdvSceneSwitcher.condition.date.thursday="Donnerstag"
AdvSceneSwitcher.day.friday="Freitag" AdvSceneSwitcher.condition.date.friday="Freitag"
AdvSceneSwitcher.day.saturday="Samstag" AdvSceneSwitcher.condition.date.saturday="Samstag"
AdvSceneSwitcher.day.sunday="Sonntag" AdvSceneSwitcher.condition.date.sunday="Sonntag"
AdvSceneSwitcher.condition.date.state.at="Am" AdvSceneSwitcher.condition.date.state.at="Am"
AdvSceneSwitcher.condition.date.state.after="Nach" AdvSceneSwitcher.condition.date.state.after="Nach"
AdvSceneSwitcher.condition.date.state.before="Vor" AdvSceneSwitcher.condition.date.state.before="Vor"
@@ -308,13 +309,12 @@ AdvSceneSwitcher.condition.date.ignoreDate="Wenn diese Option nicht aktiviert is
AdvSceneSwitcher.condition.date.ignoreTime="Wenn diese Option nicht aktiviert ist, wird die Zeitkomponente ignoriert" AdvSceneSwitcher.condition.date.ignoreTime="Wenn diese Option nicht aktiviert ist, wird die Zeitkomponente ignoriert"
AdvSceneSwitcher.condition.date.showAdvancedSettings="Erweiterte Einstellungen anzeigen" AdvSceneSwitcher.condition.date.showAdvancedSettings="Erweiterte Einstellungen anzeigen"
AdvSceneSwitcher.condition.date.showSimpleSettings="Einfache Einstellungen anzeigen" AdvSceneSwitcher.condition.date.showSimpleSettings="Einfache Einstellungen anzeigen"
AdvSceneSwitcher.condition.date.layout.simple.day="Am{{dayOfWeek}}" AdvSceneSwitcher.condition.date.entry.simple="Am {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}" AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}" AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Wiederholen alle {{duration}} bei Datumsübereinstimmung"
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}} Wiederholen alle {{duration}} bei Datumsübereinstimmung" AdvSceneSwitcher.condition.date.entry.pattern="Aktuelles Datum \"{{currentDate}}\" entspricht dem Muster {{pattern}}"
AdvSceneSwitcher.condition.date.layout.pattern="Aktuelles Datum \"{{currentDate}}\" entspricht dem Muster {{pattern}}" AdvSceneSwitcher.condition.date.entry.nextMatchDate="Nächster Treffer bei: %1"
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Nächster Treffer bei: %1" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren"
AdvSceneSwitcher.condition.date.layout.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren"
AdvSceneSwitcher.condition.sceneTransform="Szenenelement transformieren" AdvSceneSwitcher.condition.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Transformation erhalten" AdvSceneSwitcher.condition.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.condition.sceneTransform.condition.match="entspricht Transformation" AdvSceneSwitcher.condition.sceneTransform.condition.match="entspricht Transformation"
@@ -429,7 +429,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Verstecken"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Umschalten" AdvSceneSwitcher.action.sceneVisibility.type.toggle="Umschalten"
AdvSceneSwitcher.action.sceneVisibility.type.source="Quelle" AdvSceneSwitcher.action.sceneVisibility.type.source="Quelle"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Beliebig" AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Beliebig"
AdvSceneSwitcher.action.sceneVisibility.layout="Auf{{scenes}}{{actions}}{{sources}}" AdvSceneSwitcher.action.sceneVisibility.entry="Auf{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="Filter" AdvSceneSwitcher.action.filter="Filter"
AdvSceneSwitcher.action.filter.type.enable="Aktivieren" AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren" AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
@@ -834,6 +834,8 @@ AdvSceneSwitcher.status.inactive="Inaktiv"
AdvSceneSwitcher.running="Plugin läuft" AdvSceneSwitcher.running="Plugin läuft"
AdvSceneSwitcher.stopped="Plugin gestoppt" AdvSceneSwitcher.stopped="Plugin gestoppt"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Dies scheint das erste Mal zu sein, dass der Erweiterte Szenenwechsler gestartet wurde.<br>Bitte schaue ins <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> für eine Liste von Anleitungen und Beispielen.<br>Nicht zögern und Fragen im <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">Thread</span></a> des Plugins im OBS-Forum stellen!!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="Die Entwicklung für dieses Tab wurde gestoppt!\nBitte stattdessen zur Verwendung des Makros-Tab übergehen.\nDieser Hinweis kann im Allgemein-Tab deaktiviert werden." AdvSceneSwitcher.deprecatedTabWarning="Die Entwicklung für dieses Tab wurde gestoppt!\nBitte stattdessen zur Verwendung des Makros-Tab übergehen.\nDieser Hinweis kann im Allgemein-Tab deaktiviert werden."
AdvSceneSwitcher.unit.milliseconds="Millisekunden" AdvSceneSwitcher.unit.milliseconds="Millisekunden"

View File

@@ -44,8 +44,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html>
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!" AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros" AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch="Always show macro search" AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch="Always show macro search"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs="Always show all feature tabs"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs.tooltip="Show tabs that are normally hidden until first used in a macro (e.g. Action Queues, Variables, Twitch, Websockets)."
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior" AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
AdvSceneSwitcher.generalTab.priority="Priority" AdvSceneSwitcher.generalTab.priority="Priority"
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)" AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
@@ -160,8 +158,6 @@ AdvSceneSwitcher.macroTab.title="Macro"
AdvSceneSwitcher.macroTab.macros="Macros" AdvSceneSwitcher.macroTab.macros="Macros"
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab." AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro." AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
AdvSceneSwitcher.macroTab.macroLastExecutedTooltip="Was last executed %1 seconds ago."
AdvSceneSwitcher.macroTab.macroNotYetExecutedTooltip="Was not yet executed."
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define macro conditions.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new condition." AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define macro conditions.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new condition."
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action." AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action." AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
@@ -177,12 +173,7 @@ AdvSceneSwitcher.macroTab.run.tooltip="Run all macro actions regardless of condi
AdvSceneSwitcher.macroTab.runElse="Run macro (else)" AdvSceneSwitcher.macroTab.runElse="Run macro (else)"
AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the actions failed or the macro is running already.\nDo you want to stop it?" AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the actions failed or the macro is running already.\nDo you want to stop it?"
AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros" AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros"
AdvSceneSwitcher.macroTab.actionTriggerMode.label="Perform actions:" AdvSceneSwitcher.macroTab.onChange="Perform actions only on condition change"
AdvSceneSwitcher.macroTab.actionTriggerMode.tooltip="Controls when the actions of this macro are performed.\n\n\"Always\" - actions are performed every time the conditions are met.\n\"Macro result changed\" - actions are only performed when the macro transitions between matching and not matching.\n\"Any condition changed\" - actions are only performed when any individual condition changes its result.\n\"Any condition triggered\" - actions are only performed when any individual condition changes from false to true."
AdvSceneSwitcher.macroTab.actionTriggerMode.always="always"
AdvSceneSwitcher.macroTab.actionTriggerMode.onOverallChange="only when result changes"
AdvSceneSwitcher.macroTab.actionTriggerMode.onAnyConditionChange="only when any condition changes"
AdvSceneSwitcher.macroTab.actionTriggerMode.onAnyConditionTriggered="only when any condition becomes true"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Group %1" AdvSceneSwitcher.macroTab.defaultGroupName="Group %1"
AdvSceneSwitcher.macroTab.macroNameExists="The name \"%1\" is already used by a macro." AdvSceneSwitcher.macroTab.macroNameExists="The name \"%1\" is already used by a macro."
@@ -594,6 +585,14 @@ AdvSceneSwitcher.condition.replay.state.started="Replay buffer started"
AdvSceneSwitcher.condition.replay.state.saved="Replay buffer saved" AdvSceneSwitcher.condition.replay.state.saved="Replay buffer saved"
AdvSceneSwitcher.condition.replay.entry="{{state}}" AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Date" AdvSceneSwitcher.condition.date="Date"
AdvSceneSwitcher.condition.date.anyDay="Any day"
AdvSceneSwitcher.condition.date.monday="Monday"
AdvSceneSwitcher.condition.date.tuesday="Tuesday"
AdvSceneSwitcher.condition.date.wednesday="Wednesday"
AdvSceneSwitcher.condition.date.thursday="Thursday"
AdvSceneSwitcher.condition.date.friday="Friday"
AdvSceneSwitcher.condition.date.saturday="Saturday"
AdvSceneSwitcher.condition.date.sunday="Sunday"
AdvSceneSwitcher.condition.date.state.at="At" AdvSceneSwitcher.condition.date.state.at="At"
AdvSceneSwitcher.condition.date.state.after="After" AdvSceneSwitcher.condition.date.state.after="After"
AdvSceneSwitcher.condition.date.state.before="Before" AdvSceneSwitcher.condition.date.state.before="Before"
@@ -604,13 +603,12 @@ AdvSceneSwitcher.condition.date.ignoreDate="If unchecked the date component will
AdvSceneSwitcher.condition.date.ignoreTime="If unchecked the time component will be ignored" AdvSceneSwitcher.condition.date.ignoreTime="If unchecked the time component will be ignored"
AdvSceneSwitcher.condition.date.showAdvancedSettings="Show advanced settings" AdvSceneSwitcher.condition.date.showAdvancedSettings="Show advanced settings"
AdvSceneSwitcher.condition.date.showSimpleSettings="Show simple settings" AdvSceneSwitcher.condition.date.showSimpleSettings="Show simple settings"
AdvSceneSwitcher.condition.date.layout.simple.day="On{{dayOfWeek}}" AdvSceneSwitcher.condition.date.entry.simple="On{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}" AdvSceneSwitcher.condition.date.entry.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}" AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}}Repeat every{{duration}}on date match"
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}}Repeat every{{duration}}on date match" AdvSceneSwitcher.condition.date.entry.pattern="Current date \"{{currentDate}}\" matches pattern{{pattern}}"
AdvSceneSwitcher.condition.date.layout.pattern="Current date \"{{currentDate}}\" matches pattern{{pattern}}" AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Next match at: %1" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}On repeat update selected date to repeat date"
AdvSceneSwitcher.condition.date.layout.updateOnRepeat="{{updateOnRepeat}}On repeat update selected date to repeat date"
AdvSceneSwitcher.condition.sceneTransform="Scene item transform" AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform" AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="Get current value" AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="Get current value"
@@ -916,9 +914,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Toggle" AdvSceneSwitcher.action.sceneVisibility.type.toggle="Toggle"
AdvSceneSwitcher.action.sceneVisibility.type.source="Source" AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any" AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
AdvSceneSwitcher.action.sceneVisibility.layout="On{{scenes}}{{actions}}{{sources}}" AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.sceneVisibility.layout.transition="{{updateTransition}}Set transition to{{transitions}}"
AdvSceneSwitcher.action.sceneVisibility.layout.duration="{{updateDuration}}Set transition duration to{{duration}}seconds"
AdvSceneSwitcher.action.filter="Filter" AdvSceneSwitcher.action.filter="Filter"
AdvSceneSwitcher.action.filter.type.enable="Enable" AdvSceneSwitcher.action.filter.type.enable="Enable"
AdvSceneSwitcher.action.filter.type.disable="Disable" AdvSceneSwitcher.action.filter.type.disable="Disable"
@@ -1194,8 +1190,6 @@ AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values"
AdvSceneSwitcher.action.variable.type.trim="Trim whitespace" AdvSceneSwitcher.action.variable.type.trim="Trim whitespace"
AdvSceneSwitcher.action.variable.type.changeCase="Change case" AdvSceneSwitcher.action.variable.type.changeCase="Change case"
AdvSceneSwitcher.action.variable.type.randomNumber="Generate random number" AdvSceneSwitcher.action.variable.type.randomNumber="Generate random number"
AdvSceneSwitcher.action.variable.type.randomListValue="Select random value"
AdvSceneSwitcher.action.variable.type.allowRepeat="Allow repeat values"
AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase" AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase"
AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE" AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE"
AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized" AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized"
@@ -1218,8 +1212,8 @@ AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}" AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}" AdvSceneSwitcher.action.variable.layout.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}" AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}" AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}" AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}" AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
@@ -1262,14 +1256,12 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message" AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information" AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information" AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
AdvSceneSwitcher.action.twitch.type.channel.getInfo="Get channel information"
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control" AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.action.twitch.layout.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}" AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="Using account{{account}}{{actions}}on{{channel}}" AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.layout.channel.getInfo="Using account{{account}}{{actions}}of{{channel}}" AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}" AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="Enter title" AdvSceneSwitcher.action.twitch.title.title="Enter title"
AdvSceneSwitcher.action.twitch.marker.description="Describe marker" AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip" AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
@@ -1394,8 +1386,6 @@ AdvSceneSwitcher.hotkey.macro.segment.remove="Remove selected macro segment"
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?" AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
AdvSceneSwitcher.askForMacro="Select macro{{macroSelection}}" AdvSceneSwitcher.askForMacro="Select macro{{macroSelection}}"
AdvSceneSwitcher.crashDetected="OBS did not shut down cleanly (for example, due to a crash or freeze).\n\nThe Advanced Scene Switcher plugin would normally start automatically.\nWould you like to keep it stopped for now?"
AdvSceneSwitcher.close="Close" AdvSceneSwitcher.close="Close"
AdvSceneSwitcher.browse="Browse" AdvSceneSwitcher.browse="Browse"
@@ -2155,8 +2145,6 @@ AdvSceneSwitcher.tempVar.window.window="Window title"
AdvSceneSwitcher.tempVar.window.window.description="The window title of the matching window." AdvSceneSwitcher.tempVar.window.window.description="The window title of the matching window."
AdvSceneSwitcher.tempVar.window.windowClass="Window class" AdvSceneSwitcher.tempVar.window.windowClass="Window class"
AdvSceneSwitcher.tempVar.window.windowClass.description="The window class of the matching window." AdvSceneSwitcher.tempVar.window.windowClass.description="The window class of the matching window."
AdvSceneSwitcher.tempVar.window.windowText="Window text"
AdvSceneSwitcher.tempVar.window.windowText.description="Text contained within the window (won't work on all window types)."
AdvSceneSwitcher.tempVar.timer.seconds="Seconds" AdvSceneSwitcher.tempVar.timer.seconds="Seconds"
AdvSceneSwitcher.tempVar.timer.minutes="Minutes" AdvSceneSwitcher.tempVar.timer.minutes="Minutes"
@@ -2201,8 +2189,6 @@ AdvSceneSwitcher.tempVar.recording.durationSeconds.description="Recording durati
AdvSceneSwitcher.tempVar.recording.lastSavePath="Last save path" AdvSceneSwitcher.tempVar.recording.lastSavePath="Last save path"
AdvSceneSwitcher.tempVar.recording.lastSavePath.description="The file path of the last saved recording." AdvSceneSwitcher.tempVar.recording.lastSavePath.description="The file path of the last saved recording."
AdvSceneSwitcher.tempVar.video.similarity="Similarity Rating"
AdvSceneSwitcher.tempVar.video.similarity.description="Values range from 0 to 1, where 0 means dissimilar and 1 means highly similar."
AdvSceneSwitcher.tempVar.video.patternCount="Pattern count" AdvSceneSwitcher.tempVar.video.patternCount="Pattern count"
AdvSceneSwitcher.tempVar.video.patternCount.description="The number of times the given pattern has been found in a given video input frame." AdvSceneSwitcher.tempVar.video.patternCount.description="The number of times the given pattern has been found in a given video input frame."
AdvSceneSwitcher.tempVar.video.objectCount="Object count" AdvSceneSwitcher.tempVar.video.objectCount="Object count"
@@ -2443,6 +2429,8 @@ AdvSceneSwitcher.status.inactive="Inactive"
AdvSceneSwitcher.running="Plugin running" AdvSceneSwitcher.running="Plugin running"
AdvSceneSwitcher.stopped="Plugin stopped" AdvSceneSwitcher.stopped="Plugin stopped"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>This seems to be the first time the Advanced Scene Switcher was started.<br>Please have a look at the <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> for a list of guides and examples.<br>Do not hesitate to ask questions in the plugin's <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">thread</span></a> on the OBS forums!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="Development for this tab stopped!\nPlease consider transitioning to using Macros instead.\nThis hint can be disabled on the General tab." AdvSceneSwitcher.deprecatedTabWarning="Development for this tab stopped!\nPlease consider transitioning to using Macros instead.\nThis hint can be disabled on the General tab."
AdvSceneSwitcher.unit.milliseconds="milliseconds" AdvSceneSwitcher.unit.milliseconds="milliseconds"
@@ -2472,47 +2460,6 @@ AdvSceneSwitcher.clearBufferOnMatch="Clear message buffer when matching message
AdvSceneSwitcher.script.settings="Settings" AdvSceneSwitcher.script.settings="Settings"
AdvSceneSwitcher.script.timeout="Script timeout:{{timeout}}" AdvSceneSwitcher.script.timeout="Script timeout:{{timeout}}"
AdvSceneSwitcher.day.monday="Monday"
AdvSceneSwitcher.day.tuesday="Tuesday"
AdvSceneSwitcher.day.wednesday="Wednesday"
AdvSceneSwitcher.day.thursday="Thursday"
AdvSceneSwitcher.day.friday="Friday"
AdvSceneSwitcher.day.saturday="Saturday"
AdvSceneSwitcher.day.sunday="Sunday"
AdvSceneSwitcher.day.none="No day"
AdvSceneSwitcher.day.any="Any day"
# First run wizard
FirstRunWizard.windowTitle="Advanced Scene Switcher - Setup Wizard"
FirstRunWizard.welcome.title="Welcome to Advanced Scene Switcher"
FirstRunWizard.welcome.subtitle="Let's create your first automation in a few easy steps."
FirstRunWizard.welcome.body="<p>Advanced Scene Switcher lets you build <b>Macros</b> - rules of the form:</p><blockquote><i>When [condition] -> perform [action]</i></blockquote><p>This wizard creates a macro that <b>switches to a chosen OBS scene whenever a specific window comes into focus</b>.</p><p>You can skip at any time. Re-open this wizard later from the <b>General</b> tab inside the Advanced Scene Switcher dialog.</p>"
FirstRunWizard.scene.title="Choose a Target Scene"
FirstRunWizard.scene.subtitle="Which OBS scene should become active when your chosen window comes into focus?"
FirstRunWizard.scene.label="Switch to scene:"
FirstRunWizard.window.title="Choose a Trigger Window"
FirstRunWizard.window.subtitle="Enter the title of the window that should trigger the scene switch. Partial matches are fine - \"Firefox\" will match any Firefox window."
FirstRunWizard.window.label="Window title contains:"
FirstRunWizard.window.placeholder="e.g. \"Firefox\", \"Visual Studio Code\""
FirstRunWizard.window.autoDetect="Auto-detect focused window"
FirstRunWizard.window.autoDetectTooltip="Click, then switch to the target window. The title will be captured automatically after 3 seconds."
FirstRunWizard.window.autoDetectCountdown="Detecting in %1 s - focus your window now..."
FirstRunWizard.window.hint="<small>Tip: a short partial title is more robust than the full title, which often changes when you switch tabs.</small>"
FirstRunWizard.review.title="Review Your Macro"
FirstRunWizard.review.subtitle="Click Back to make changes, or Finish to create the macro."
FirstRunWizard.review.summary="<b>Macro name:</b> Window -> %1<br><br><b>Condition:</b> Focused window title contains <i>\"%2\"</i> (case-insensitive)<br><br><b>Action:</b> Switch to scene <i>\"%1\"</i>"
FirstRunWizard.review.errorTitle="Macro Creation Failed"
FirstRunWizard.review.errorBody="The macro could not be created automatically.\n\nYou can create it manually on the Macros tab:\n Condition: Window -> contains \"%1\"\n Action: Switch scene -> \"%2\""
FirstRunWizard.done.title="You're all set!"
FirstRunWizard.done.subtitle="Your first macro has been created and is now active."
FirstRunWizard.done.body="<p>You can view and edit it on the <b>Macros</b> tab of the Advanced Scene Switcher dialog.</p><p>To learn more:</p><ul><li><a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\">Plugin wiki</a></li><li><a href=\"https://obsproject.com/forum/resources/automatic-scene-switching.395/\">OBS forum thread</a></li></ul>"
FirstRunWizard.openButton="Open Setup Wizard..."
# This secion is copied from the OBS locale files # This secion is copied from the OBS locale files
# OBS commonly shared locale # OBS commonly shared locale

View File

@@ -72,6 +72,7 @@ AdvSceneSwitcher.macroTab.add="Agregar nueva macro"
AdvSceneSwitcher.macroTab.name="Nombre:" AdvSceneSwitcher.macroTab.name="Nombre:"
AdvSceneSwitcher.macroTab.run="Ejecutar macro" AdvSceneSwitcher.macroTab.run="Ejecutar macro"
AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros" AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros"
AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.copy="Crear copia" AdvSceneSwitcher.macroTab.copy="Crear copia"
AdvSceneSwitcher.macroTab.expandAll="Expandir todo" AdvSceneSwitcher.macroTab.expandAll="Expandir todo"
@@ -240,14 +241,14 @@ AdvSceneSwitcher.condition.replay.state.started="Búfer de reproducción iniciad
AdvSceneSwitcher.condition.replay.state.saved="Búfer de reproducción guardado" AdvSceneSwitcher.condition.replay.state.saved="Búfer de reproducción guardado"
AdvSceneSwitcher.condition.replay.entry="{{state}}" AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Fecha" AdvSceneSwitcher.condition.date="Fecha"
AdvSceneSwitcher.day.any="Cualquier día" AdvSceneSwitcher.condition.date.anyDay="Cualquier día"
AdvSceneSwitcher.day.monday="Lunes" AdvSceneSwitcher.condition.date.monday="Lunes"
AdvSceneSwitcher.day.tuesday="Martes" AdvSceneSwitcher.condition.date.tuesday="Martes"
AdvSceneSwitcher.day.wednesday="Miércoles" AdvSceneSwitcher.condition.date.wednesday="Miércoles"
AdvSceneSwitcher.day.thursday="Jueves" AdvSceneSwitcher.condition.date.thursday="Jueves"
AdvSceneSwitcher.day.friday="Viernes" AdvSceneSwitcher.condition.date.friday="Viernes"
AdvSceneSwitcher.day.saturday="Sábado" AdvSceneSwitcher.condition.date.saturday="Sábado"
AdvSceneSwitcher.day.sunday="Domingo" AdvSceneSwitcher.condition.date.sunday="Domingo"
AdvSceneSwitcher.condition.date.state.at="A las" AdvSceneSwitcher.condition.date.state.at="A las"
AdvSceneSwitcher.condition.date.state.after="Después" AdvSceneSwitcher.condition.date.state.after="Después"
AdvSceneSwitcher.condition.date.state.before="Antes" AdvSceneSwitcher.condition.date.state.before="Antes"
@@ -257,12 +258,11 @@ AdvSceneSwitcher.condition.date.ignoreDate="Si no se marca, se ignorará el comp
AdvSceneSwitcher.condition.date.ignoreTime="Si no se marca, se ignorará el componente de tiempo" AdvSceneSwitcher.condition.date.ignoreTime="Si no se marca, se ignorará el componente de tiempo"
AdvSceneSwitcher.condition.date.showAdvancedSettings="Mostrar configuración avanzada" AdvSceneSwitcher.condition.date.showAdvancedSettings="Mostrar configuración avanzada"
AdvSceneSwitcher.condition.date.showSimpleSettings="Mostrar configuración simple" AdvSceneSwitcher.condition.date.showSimpleSettings="Mostrar configuración simple"
AdvSceneSwitcher.condition.date.layout.simple.day="El{{dayOfWeek}}" AdvSceneSwitcher.condition.date.entry.simple="El {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}" AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}" AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Repetir cada {{duration}} en la coincidencia de fechas"
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}} Repetir cada {{duration}} en la coincidencia de fechas" AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima coincidencia en: %1"
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Próxima coincidencia en: %1" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
AdvSceneSwitcher.condition.date.layout.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena" AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación" AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.condition.sceneTransform.condition.match="coincide con la transformación" AdvSceneSwitcher.condition.sceneTransform.condition.match="coincide con la transformación"
@@ -350,7 +350,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Mostrar"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar" AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
AdvSceneSwitcher.action.sceneVisibility.type.source="Fuente" AdvSceneSwitcher.action.sceneVisibility.type.source="Fuente"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Cualquiera" AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Cualquiera"
AdvSceneSwitcher.action.sceneVisibility.layout="En{{scenes}}{{actions}}{{sources}}" AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="Filtro" AdvSceneSwitcher.action.filter="Filtro"
AdvSceneSwitcher.action.filter.type.enable="Habilitar" AdvSceneSwitcher.action.filter.type.enable="Habilitar"
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar" AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
@@ -680,6 +680,8 @@ AdvSceneSwitcher.status.inactive="Inactivo"
AdvSceneSwitcher.running="Iniciar complemento" AdvSceneSwitcher.running="Iniciar complemento"
AdvSceneSwitcher.stopped="Complemento de detención" AdvSceneSwitcher.stopped="Complemento de detención"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Esta parece ser la primera vez que se inicia el conmutador de escena avanzado.<br>Por favor, eche un vistazo a <a href=\"https:/ /github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> para obtener una lista de guías y ejemplos.<br>No dude en hacer preguntas en el complemento <a href=\"https://obsproject.com /forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">hilo</span></a> en los foros de OBS!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="¡Se detuvo el desarrollo de esta pestaña!\nConsidere cambiar a macros en su lugar.\nEsta sugerencia se puede desactivar en la pestaña General". AdvSceneSwitcher.deprecatedTabWarning="¡Se detuvo el desarrollo de esta pestaña!\nConsidere cambiar a macros en su lugar.\nEsta sugerencia se puede desactivar en la pestaña General".
AdvSceneSwitcher.unit.milliseconds="milisegundos" AdvSceneSwitcher.unit.milliseconds="milisegundos"

View File

@@ -78,6 +78,7 @@ AdvSceneSwitcher.macroTab.add="Ajouter une nouvelle macro"
AdvSceneSwitcher.macroTab.name="Nom :" AdvSceneSwitcher.macroTab.name="Nom :"
AdvSceneSwitcher.macroTab.run="Exécuter la macro" AdvSceneSwitcher.macroTab.run="Exécuter la macro"
AdvSceneSwitcher.macroTab.runInParallel="Exécuter la macro en parallèle avec d'autres macros" AdvSceneSwitcher.macroTab.runInParallel="Exécuter la macro en parallèle avec d'autres macros"
AdvSceneSwitcher.macroTab.onChange="Exécuter des actions uniquement en cas de changement de condition"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1" AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1"
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" ?" AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
@@ -355,14 +356,14 @@ AdvSceneSwitcher.condition.replay.state.stopped="Tampon de répétition arrêté
AdvSceneSwitcher.condition.replay.state.started="Tampon de répétition démarré" AdvSceneSwitcher.condition.replay.state.started="Tampon de répétition démarré"
AdvSceneSwitcher.condition.replay.state.saved="Tampon de répétition enregistré" AdvSceneSwitcher.condition.replay.state.saved="Tampon de répétition enregistré"
AdvSceneSwitcher.condition.date="Date" AdvSceneSwitcher.condition.date="Date"
AdvSceneSwitcher.day.any="N'importe quel jour" AdvSceneSwitcher.condition.date.anyDay="N'importe quel jour"
AdvSceneSwitcher.day.monday="Lundi" AdvSceneSwitcher.condition.date.monday="Lundi"
AdvSceneSwitcher.day.tuesday="Mardi" AdvSceneSwitcher.condition.date.tuesday="Mardi"
AdvSceneSwitcher.day.wednesday="Mercredi" AdvSceneSwitcher.condition.date.wednesday="Mercredi"
AdvSceneSwitcher.day.thursday="Jeudi" AdvSceneSwitcher.condition.date.thursday="Jeudi"
AdvSceneSwitcher.day.friday="Vendredi" AdvSceneSwitcher.condition.date.friday="Vendredi"
AdvSceneSwitcher.day.saturday="Samedi" AdvSceneSwitcher.condition.date.saturday="Samedi"
AdvSceneSwitcher.day.sunday="Dimanche" AdvSceneSwitcher.condition.date.sunday="Dimanche"
AdvSceneSwitcher.condition.date.state.at="À" AdvSceneSwitcher.condition.date.state.at="À"
AdvSceneSwitcher.condition.date.state.after="Après" AdvSceneSwitcher.condition.date.state.after="Après"
AdvSceneSwitcher.condition.date.state.before="Avant" AdvSceneSwitcher.condition.date.state.before="Avant"
@@ -373,12 +374,11 @@ AdvSceneSwitcher.condition.date.ignoreDate="Si non cochée, la composante date s
AdvSceneSwitcher.condition.date.ignoreTime="Si non cochée, la composante heure sera ignorée" AdvSceneSwitcher.condition.date.ignoreTime="Si non cochée, la composante heure sera ignorée"
AdvSceneSwitcher.condition.date.showAdvancedSettings="Afficher les paramètres avancés" AdvSceneSwitcher.condition.date.showAdvancedSettings="Afficher les paramètres avancés"
AdvSceneSwitcher.condition.date.showSimpleSettings="Afficher les paramètres simples" AdvSceneSwitcher.condition.date.showSimpleSettings="Afficher les paramètres simples"
AdvSceneSwitcher.condition.date.layout.simple.day="Le{{dayOfWeek}}" AdvSceneSwitcher.condition.date.entry.simple="Le{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}" AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}}Répéter toutes les{{duration}}lorsque la date correspond"
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}}Répéter toutes les{{duration}}lorsque la date correspond" AdvSceneSwitcher.condition.date.entry.pattern="La date actuelle \"{{currentDate}}\" correspond au motif{{pattern}}"
AdvSceneSwitcher.condition.date.layout.pattern="La date actuelle \"{{currentDate}}\" correspond au motif{{pattern}}" AdvSceneSwitcher.condition.date.entry.nextMatchDate="Prochaine correspondance à : %1"
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Prochaine correspondance à : %1" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}À la répétition, mettre à jour la date sélectionnée pour la date de répétition"
AdvSceneSwitcher.condition.date.layout.updateOnRepeat="{{updateOnRepeat}}À la répétition, mettre à jour la date sélectionnée pour la date de répétition"
AdvSceneSwitcher.condition.sceneTransform="Transformation de l'élément de la scène" AdvSceneSwitcher.condition.sceneTransform="Transformation de l'élément de la scène"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtenir la transformation" AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtenir la transformation"
AdvSceneSwitcher.condition.sceneTransform.condition.match="correspond à la transformation" AdvSceneSwitcher.condition.sceneTransform.condition.match="correspond à la transformation"
@@ -510,7 +510,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Masquer"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Basculer" AdvSceneSwitcher.action.sceneVisibility.type.toggle="Basculer"
AdvSceneSwitcher.action.sceneVisibility.type.source="Source" AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="N'importe" AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="N'importe"
AdvSceneSwitcher.action.sceneVisibility.layout="Sur{{scenes}}{{actions}}{{sources}}" AdvSceneSwitcher.action.sceneVisibility.entry="Sur{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="Filtre" AdvSceneSwitcher.action.filter="Filtre"
AdvSceneSwitcher.action.filter.type.enable="Activer" AdvSceneSwitcher.action.filter.type.enable="Activer"
AdvSceneSwitcher.action.filter.type.disable="Désactiver" AdvSceneSwitcher.action.filter.type.disable="Désactiver"
@@ -701,8 +701,8 @@ AdvSceneSwitcher.action.variable.invalidSelection="Sélection invalide !"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !" AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !"
AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :" AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :"
AdvSceneSwitcher.action.variable.layout.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière:{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière :"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}" AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}" AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
AdvSceneSwitcher.action.projector="Projecteur" AdvSceneSwitcher.action.projector="Projecteur"
@@ -1122,6 +1122,8 @@ AdvSceneSwitcher.status.inactive="Inactif"
AdvSceneSwitcher.running="Plugin en cours d'exécution" AdvSceneSwitcher.running="Plugin en cours d'exécution"
AdvSceneSwitcher.stopped="Plugin arrêté" AdvSceneSwitcher.stopped="Plugin arrêté"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Il semble que ce soit la première fois que l'Advanced Scene Switcher est démarré.<br>Veuillez consulter le <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> pour obtenir une liste de guides et d'exemples.<br>N'hésitez pas à poser des questions dans le <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">fil de discussion</span></a> du plugin sur les forums OBS !</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="Le développement de cet onglet est terminé !\nVeuillez envisager de passer à l'utilisation des Macros à la place.\nCette astuce peut être désactivée dans l'onglet Général." AdvSceneSwitcher.deprecatedTabWarning="Le développement de cet onglet est terminé !\nVeuillez envisager de passer à l'utilisation des Macros à la place.\nCette astuce peut être désactivée dans l'onglet Général."
AdvSceneSwitcher.unit.milliseconds="millisecondes" AdvSceneSwitcher.unit.milliseconds="millisecondes"

View File

@@ -164,6 +164,7 @@ AdvSceneSwitcher.macroTab.run.tooltip="条件に関係なくすべてのマク
AdvSceneSwitcher.macroTab.runElse="マクロ実行else" AdvSceneSwitcher.macroTab.runElse="マクロ実行else"
AdvSceneSwitcher.macroTab.runFail="\"%1\" の実行に失敗しました!\nいずれかのアクションが失敗したか、マクロがすでに実行されています。\n停止しますか" AdvSceneSwitcher.macroTab.runFail="\"%1\" の実行に失敗しました!\nいずれかのアクションが失敗したか、マクロがすでに実行されています。\n停止しますか"
AdvSceneSwitcher.macroTab.runInParallel="他のマクロと並行してマクロを実行する" AdvSceneSwitcher.macroTab.runInParallel="他のマクロと並行してマクロを実行する"
AdvSceneSwitcher.macroTab.onChange="条件変更時のみアクションを実行"
AdvSceneSwitcher.macroTab.defaultname="マクロ %1" AdvSceneSwitcher.macroTab.defaultname="マクロ %1"
AdvSceneSwitcher.macroTab.defaultGroupName="グループ %1" AdvSceneSwitcher.macroTab.defaultGroupName="グループ %1"
AdvSceneSwitcher.macroTab.macroNameExists="名前 \"%1\" は既にマクロで使用されています。" AdvSceneSwitcher.macroTab.macroNameExists="名前 \"%1\" は既にマクロで使用されています。"
@@ -554,14 +555,14 @@ AdvSceneSwitcher.condition.replay.state.started="リプレイバッファを開
AdvSceneSwitcher.condition.replay.state.saved="リプレイバッファを保存" AdvSceneSwitcher.condition.replay.state.saved="リプレイバッファを保存"
; AdvSceneSwitcher.condition.replay.entry="{{state}}" ; AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="日付条件" AdvSceneSwitcher.condition.date="日付条件"
AdvSceneSwitcher.day.any="いつでも" AdvSceneSwitcher.condition.date.anyDay="いつでも"
AdvSceneSwitcher.day.monday="月曜日" AdvSceneSwitcher.condition.date.monday="月曜日"
AdvSceneSwitcher.day.tuesday="火曜日" AdvSceneSwitcher.condition.date.tuesday="火曜日"
AdvSceneSwitcher.day.wednesday="水曜日" AdvSceneSwitcher.condition.date.wednesday="水曜日"
AdvSceneSwitcher.day.thursday="木曜日" AdvSceneSwitcher.condition.date.thursday="木曜日"
AdvSceneSwitcher.day.friday="金曜日" AdvSceneSwitcher.condition.date.friday="金曜日"
AdvSceneSwitcher.day.saturday="土曜日" AdvSceneSwitcher.condition.date.saturday="土曜日"
AdvSceneSwitcher.day.sunday="日曜日" AdvSceneSwitcher.condition.date.sunday="日曜日"
; AdvSceneSwitcher.condition.date.state.at="At" ; AdvSceneSwitcher.condition.date.state.at="At"
AdvSceneSwitcher.condition.date.state.after="アフター" AdvSceneSwitcher.condition.date.state.after="アフター"
AdvSceneSwitcher.condition.date.state.before="前" AdvSceneSwitcher.condition.date.state.before="前"
@@ -572,9 +573,12 @@ AdvSceneSwitcher.condition.date.ignoreDate="チェックを外すと日付部分
AdvSceneSwitcher.condition.date.ignoreTime="チェックを外すと時間要素は無視されます" AdvSceneSwitcher.condition.date.ignoreTime="チェックを外すと時間要素は無視されます"
AdvSceneSwitcher.condition.date.showAdvancedSettings="詳細設定を表示" AdvSceneSwitcher.condition.date.showAdvancedSettings="詳細設定を表示"
AdvSceneSwitcher.condition.date.showSimpleSettings="簡単設定を表示" AdvSceneSwitcher.condition.date.showSimpleSettings="簡単設定を表示"
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}}日付の一致ごとに{{duration}}繰り返します" ; AdvSceneSwitcher.condition.date.entry.simple="On{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.layout.pattern="現在の日付「{{currentDate}}」はパターン{{pattern}}と一致します" ; AdvSceneSwitcher.condition.date.entry.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
AdvSceneSwitcher.condition.date.layout.updateOnRepeat="{{updateOnRepeat}}繰り返しの場合、選択した日付を繰り返しの日付に更新します" AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}}日付の一致ごとに{{duration}}繰り返します"
AdvSceneSwitcher.condition.date.entry.pattern="現在の日付「{{currentDate}}」はパターン{{pattern}}と一致します"
; AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}繰り返しの場合、選択した日付を繰り返しの日付に更新します"
; AdvSceneSwitcher.condition.sceneTransform="Scene item transform" ; AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
; AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform" ; AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="現在の値を取得" AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="現在の値を取得"
@@ -866,6 +870,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="非表示"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切り替え" AdvSceneSwitcher.action.sceneVisibility.type.toggle="切り替え"
; AdvSceneSwitcher.action.sceneVisibility.type.source="Source" ; AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
; AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any" ; AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
; AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="フィルタ" AdvSceneSwitcher.action.filter="フィルタ"
AdvSceneSwitcher.action.filter.type.enable="有効にする" AdvSceneSwitcher.action.filter.type.enable="有効にする"
AdvSceneSwitcher.action.filter.type.disable="無効化" AdvSceneSwitcher.action.filter.type.disable="無効化"
@@ -1144,8 +1149,8 @@ AdvSceneSwitcher.action.variable.currentSegmentValue="現在値:"
; AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}" ; AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。" AdvSceneSwitcher.action.variable.layout.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。" AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。"
AdvSceneSwitcher.action.variable.layout.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:"
; AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}" ; AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}" AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}" AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}"
@@ -1188,10 +1193,10 @@ AdvSceneSwitcher.action.twitch.type.user.getInfo="ユーザー情報を取得"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="チャンネルポイントリワード情報を取得" AdvSceneSwitcher.action.twitch.type.reward.getInfo="チャンネルポイントリワード情報を取得"
AdvSceneSwitcher.action.twitch.reward.toggleControl="リワード名/変数選択コントロールの切り替え" AdvSceneSwitcher.action.twitch.reward.toggleControl="リワード名/変数選択コントロールの切り替え"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません"
; AdvSceneSwitcher.action.twitch.layout.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}" ; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.layout.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています" AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="{{userInfoQueryType}}{{userLogin}}{{userId}}でアカウント{{account}}{{actions}}を使用" AdvSceneSwitcher.action.twitch.entry.user.getInfo="{{userInfoQueryType}}{{userLogin}}{{userId}}でアカウント{{account}}{{actions}}を使用"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="チャンネル{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}でアカウント{{account}}{{actions}}を使用" AdvSceneSwitcher.action.twitch.entry.reward.getInfo="チャンネル{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}でアカウント{{account}}{{actions}}を使用"
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力" AdvSceneSwitcher.action.twitch.title.title="タイトルを入力"
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明" AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します" AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"
@@ -2251,6 +2256,8 @@ AdvSceneSwitcher.status.inactive="停止中"
AdvSceneSwitcher.running="プラグイン実行中" AdvSceneSwitcher.running="プラグイン実行中"
AdvSceneSwitcher.stopped="プラグイン停止しました" AdvSceneSwitcher.stopped="プラグイン停止しました"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>高機能シーンスイッチャーが初めて起動したようです。<br>ガイドと使用例については <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> をご覧ください。<br>質問は <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">OBSフォーラムのプラグインのスレッド</span></a> で遠慮なくどうぞ!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="このタブの開発は停止しました!\n代わりにマクロの使用に移行することを検討してください。\nこのヒントは [全般] タブで無効にすることができます。" AdvSceneSwitcher.deprecatedTabWarning="このタブの開発は停止しました!\n代わりにマクロの使用に移行することを検討してください。\nこのヒントは [全般] タブで無効にすることができます。"
AdvSceneSwitcher.unit.milliseconds="ミリ秒" AdvSceneSwitcher.unit.milliseconds="ミリ秒"

View File

@@ -147,6 +147,7 @@ AdvSceneSwitcher.macroTab.run.tooltip="Execute todas as ações da macro indepen
AdvSceneSwitcher.macroTab.runElse="Executar macro (alternativa)" AdvSceneSwitcher.macroTab.runElse="Executar macro (alternativa)"
AdvSceneSwitcher.macroTab.runFail="Falha ao executar \"%1\"!\nUma das ações falhou ou a macro já está em execução.\nDeseja interrompê-la?" AdvSceneSwitcher.macroTab.runFail="Falha ao executar \"%1\"!\nUma das ações falhou ou a macro já está em execução.\nDeseja interrompê-la?"
AdvSceneSwitcher.macroTab.runInParallel="Executar macro em paralelo com outras macros" AdvSceneSwitcher.macroTab.runInParallel="Executar macro em paralelo com outras macros"
AdvSceneSwitcher.macroTab.onChange="Executar ações apenas quando houver mudança na condição"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Grupo %1" AdvSceneSwitcher.macroTab.defaultGroupName="Grupo %1"
AdvSceneSwitcher.macroTab.macroNameExists="O nome \"%1\" já está em uso por uma macro." AdvSceneSwitcher.macroTab.macroNameExists="O nome \"%1\" já está em uso por uma macro."
@@ -501,14 +502,14 @@ AdvSceneSwitcher.condition.replay.state.started="Buffer de replay iniciado"
AdvSceneSwitcher.condition.replay.state.saved="Buffer de replay salvo" AdvSceneSwitcher.condition.replay.state.saved="Buffer de replay salvo"
AdvSceneSwitcher.condition.replay.entry="{{state}}" AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Data" AdvSceneSwitcher.condition.date="Data"
AdvSceneSwitcher.day.any="Qualquer dia" AdvSceneSwitcher.condition.date.anyDay="Qualquer dia"
AdvSceneSwitcher.day.monday="Segunda-feira" AdvSceneSwitcher.condition.date.monday="Segunda-feira"
AdvSceneSwitcher.day.tuesday="Terça-feira" AdvSceneSwitcher.condition.date.tuesday="Terça-feira"
AdvSceneSwitcher.day.wednesday="Quarta-feira" AdvSceneSwitcher.condition.date.wednesday="Quarta-feira"
AdvSceneSwitcher.day.thursday="Quinta-feira" AdvSceneSwitcher.condition.date.thursday="Quinta-feira"
AdvSceneSwitcher.day.friday="Sexta-feira" AdvSceneSwitcher.condition.date.friday="Sexta-feira"
AdvSceneSwitcher.day.saturday="Sábado" AdvSceneSwitcher.condition.date.saturday="Sábado"
AdvSceneSwitcher.day.sunday="Domingo" AdvSceneSwitcher.condition.date.sunday="Domingo"
AdvSceneSwitcher.condition.date.state.at="Às" AdvSceneSwitcher.condition.date.state.at="Às"
AdvSceneSwitcher.condition.date.state.after="Após" AdvSceneSwitcher.condition.date.state.after="Após"
AdvSceneSwitcher.condition.date.state.before="Antes" AdvSceneSwitcher.condition.date.state.before="Antes"
@@ -519,13 +520,12 @@ AdvSceneSwitcher.condition.date.ignoreDate="Se desmarcado, o componente de data
AdvSceneSwitcher.condition.date.ignoreTime="Se desmarcado, o componente de tempo será ignorado" AdvSceneSwitcher.condition.date.ignoreTime="Se desmarcado, o componente de tempo será ignorado"
AdvSceneSwitcher.condition.date.showAdvancedSettings="Mostrar configurações avançadas" AdvSceneSwitcher.condition.date.showAdvancedSettings="Mostrar configurações avançadas"
AdvSceneSwitcher.condition.date.showSimpleSettings="Mostrar configurações simples" AdvSceneSwitcher.condition.date.showSimpleSettings="Mostrar configurações simples"
AdvSceneSwitcher.condition.date.layout.simple.day="Em{{dayOfWeek}}" AdvSceneSwitcher.condition.date.entry.simple="Em{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}" AdvSceneSwitcher.condition.date.entry.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}" AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}}Repetir a cada{{duration}}em correspondência de data"
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}}Repetir a cada{{duration}}em correspondência de data" AdvSceneSwitcher.condition.date.entry.pattern="Data atual \"{{currentDate}}\" corresponde ao padrão{{pattern}}"
AdvSceneSwitcher.condition.date.layout.pattern="Data atual \"{{currentDate}}\" corresponde ao padrão{{pattern}}" AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima correspondência em: %1"
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Próxima correspondência em: %1" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}Ao repetir, atualize a data selecionada para a data de repetição"
AdvSceneSwitcher.condition.date.layout.updateOnRepeat="{{updateOnRepeat}}Ao repetir, atualize a data selecionada para a data de repetição"
AdvSceneSwitcher.condition.sceneTransform="Transformação do item de cena" AdvSceneSwitcher.condition.sceneTransform="Transformação do item de cena"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obter transformação" AdvSceneSwitcher.condition.sceneTransform.getTransform="Obter transformação"
AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="Obter valor atual" AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="Obter valor atual"
@@ -781,7 +781,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Alternar" AdvSceneSwitcher.action.sceneVisibility.type.toggle="Alternar"
AdvSceneSwitcher.action.sceneVisibility.type.source="Fonte" AdvSceneSwitcher.action.sceneVisibility.type.source="Fonte"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Qualquer" AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Qualquer"
AdvSceneSwitcher.action.sceneVisibility.layout="Em{{scenes}}{{actions}}{{sources}}" AdvSceneSwitcher.action.sceneVisibility.entry="Em{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="Filtro" AdvSceneSwitcher.action.filter="Filtro"
AdvSceneSwitcher.action.filter.type.enable="Habilitar" AdvSceneSwitcher.action.filter.type.enable="Habilitar"
AdvSceneSwitcher.action.filter.type.disable="Desabilitar" AdvSceneSwitcher.action.filter.type.disable="Desabilitar"
@@ -1030,8 +1030,8 @@ AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Obter valores de va
AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:" AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}" AdvSceneSwitcher.action.variable.layout.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}" AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}" AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}" AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}" AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
@@ -1065,8 +1065,8 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat" AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat" AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.layout.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}" AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="Usando conta{{account}}{{actions}}em{{channel}}" AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título" AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador" AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe" AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"
@@ -1871,6 +1871,8 @@ AdvSceneSwitcher.status.inactive="Inativo"
AdvSceneSwitcher.running="Plugin em execução" AdvSceneSwitcher.running="Plugin em execução"
AdvSceneSwitcher.stopped="Plugin parado" AdvSceneSwitcher.stopped="Plugin parado"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Parece que esta é a primeira vez que o Advanced Scene Switcher está sendo iniciado.<br>Por favor, consulte o <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> para uma lista de guias e exemplos.<br>Não hesite em fazer perguntas no <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">tópico</span></a> do plugin nos fóruns do OBS!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="Desenvolvimento para esta aba interrompido!\nPor favor, considere a transição para o uso de Macros em vez disso.\nEsta dica pode ser desativada na aba Geral." AdvSceneSwitcher.deprecatedTabWarning="Desenvolvimento para esta aba interrompido!\nPor favor, considere a transição para o uso de Macros em vez disso.\nEsta dica pode ser desativada na aba Geral."
AdvSceneSwitcher.unit.milliseconds="milissegundos" AdvSceneSwitcher.unit.milliseconds="milissegundos"

View File

@@ -72,6 +72,7 @@ AdvSceneSwitcher.macroTab.add="Yeni Makro ekle"
AdvSceneSwitcher.macroTab.name="İsim:" AdvSceneSwitcher.macroTab.name="İsim:"
AdvSceneSwitcher.macroTab.run="Makro Çalıştırma" AdvSceneSwitcher.macroTab.run="Makro Çalıştırma"
AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın" AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın"
AdvSceneSwitcher.macroTab.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin"
AdvSceneSwitcher.macroTab.defaultname="Makro %1" AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.copy="Kopya oluştur" AdvSceneSwitcher.macroTab.copy="Kopya oluştur"
AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet" AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
@@ -280,7 +281,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Göster"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle" AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle"
AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt" AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi" AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi"
AdvSceneSwitcher.action.sceneVisibility.layout="Açık{{scenes}}{{actions}}{{sources}}" AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="Filtrele" AdvSceneSwitcher.action.filter="Filtrele"
AdvSceneSwitcher.action.filter.type.enable="Etkin" AdvSceneSwitcher.action.filter.type.enable="Etkin"
AdvSceneSwitcher.action.filter.type.disable="Etkisiz" AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
@@ -586,6 +587,8 @@ AdvSceneSwitcher.status.inactive="İnaktif"
AdvSceneSwitcher.running="Eklenti çalışıyor" AdvSceneSwitcher.running="Eklenti çalışıyor"
AdvSceneSwitcher.stopped="Eklenti durdu" AdvSceneSwitcher.stopped="Eklenti durdu"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Gelişmiş Sahne Değiştirici ilk kez başlatılıyor gibi görünüyor.<br>Lütfen <a href=\"https://github.com/ adresine bir göz atın. WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> için kılavuzlar ve örnekler listesi.<br>Yapmayın. eklentinin <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:# sayfasında soru sormaktan çekinmeyin OBS forumlarında 268bd2;\">konu</span></a>!</p></body></html>"
AdvSceneSwitcher.unit.milliseconds="millisaniye" AdvSceneSwitcher.unit.milliseconds="millisaniye"
AdvSceneSwitcher.unit.seconds="saniye" AdvSceneSwitcher.unit.seconds="saniye"
AdvSceneSwitcher.unit.minutes="dakika" AdvSceneSwitcher.unit.minutes="dakika"

View File

@@ -151,6 +151,7 @@ AdvSceneSwitcher.macroTab.run.tooltip="无论条件如何,都运行所有宏
AdvSceneSwitcher.macroTab.runElse="运行宏(不满足条件)" AdvSceneSwitcher.macroTab.runElse="运行宏(不满足条件)"
AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n要么其中一个操作失败要么宏已在运行中.\n你想停止它吗?" AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n要么其中一个操作失败要么宏已在运行中.\n你想停止它吗?"
AdvSceneSwitcher.macroTab.runInParallel="与其他宏并行运行宏" AdvSceneSwitcher.macroTab.runInParallel="与其他宏并行运行宏"
AdvSceneSwitcher.macroTab.onChange="仅在条件结果发生变化时执行操作(条件结果不变时只执行一次操作)"
AdvSceneSwitcher.macroTab.defaultname="宏 %1" AdvSceneSwitcher.macroTab.defaultname="宏 %1"
AdvSceneSwitcher.macroTab.defaultGroupName="分组 %1" AdvSceneSwitcher.macroTab.defaultGroupName="分组 %1"
AdvSceneSwitcher.macroTab.macroNameExists="名称 \"%1\" 已被宏使用." AdvSceneSwitcher.macroTab.macroNameExists="名称 \"%1\" 已被宏使用."
@@ -529,14 +530,14 @@ AdvSceneSwitcher.condition.replay.state.started="回放缓存已启动"
AdvSceneSwitcher.condition.replay.state.saved="已保存回放缓存" AdvSceneSwitcher.condition.replay.state.saved="已保存回放缓存"
AdvSceneSwitcher.condition.replay.entry="{{state}}" AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="日期" AdvSceneSwitcher.condition.date="日期"
AdvSceneSwitcher.day.any="每一天" AdvSceneSwitcher.condition.date.anyDay="每一天"
AdvSceneSwitcher.day.monday="周一" AdvSceneSwitcher.condition.date.monday="周一"
AdvSceneSwitcher.day.tuesday="周二" AdvSceneSwitcher.condition.date.tuesday="周二"
AdvSceneSwitcher.day.wednesday="周三" AdvSceneSwitcher.condition.date.wednesday="周三"
AdvSceneSwitcher.day.thursday="周四" AdvSceneSwitcher.condition.date.thursday="周四"
AdvSceneSwitcher.day.friday="周五" AdvSceneSwitcher.condition.date.friday="周五"
AdvSceneSwitcher.day.saturday="周六" AdvSceneSwitcher.condition.date.saturday="周六"
AdvSceneSwitcher.day.sunday="周日" AdvSceneSwitcher.condition.date.sunday="周日"
AdvSceneSwitcher.condition.date.state.at="现在" AdvSceneSwitcher.condition.date.state.at="现在"
AdvSceneSwitcher.condition.date.state.after="之后" AdvSceneSwitcher.condition.date.state.after="之后"
AdvSceneSwitcher.condition.date.state.before="之前" AdvSceneSwitcher.condition.date.state.before="之前"
@@ -547,13 +548,12 @@ AdvSceneSwitcher.condition.date.ignoreDate="如果未选中,日期组件将被
AdvSceneSwitcher.condition.date.ignoreTime="如果未选中,时间组件将被忽略" AdvSceneSwitcher.condition.date.ignoreTime="如果未选中,时间组件将被忽略"
AdvSceneSwitcher.condition.date.showAdvancedSettings="显示高级设置" AdvSceneSwitcher.condition.date.showAdvancedSettings="显示高级设置"
AdvSceneSwitcher.condition.date.showSimpleSettings="显示简单设置" AdvSceneSwitcher.condition.date.showSimpleSettings="显示简单设置"
AdvSceneSwitcher.condition.date.layout.simple.day="{{dayOfWeek}}" AdvSceneSwitcher.condition.date.entry.simple="{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}" AdvSceneSwitcher.condition.date.entry.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}" AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} 每 {{duration}} 重复一次时间/日期匹配"
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}} 每 {{duration}} 重复一次时间/日期匹配" AdvSceneSwitcher.condition.date.entry.pattern="当前日期 \"{{currentDate}}\" 与 {{pattern}} 模式匹配 , 例子:2025 03 18 24 00 00 = .... .. .. .. .. .. ,难崩,我测试了一段时间才明白"
AdvSceneSwitcher.condition.date.layout.pattern="当前日期 \"{{currentDate}}\" 与 {{pattern}} 模式匹配 , 例子:2025 03 18 24 00 00 = .... .. .. .. .. .. ,难崩,我测试了一段时间才明白" AdvSceneSwitcher.condition.date.entry.nextMatchDate="下一次匹配 在: %1"
AdvSceneSwitcher.condition.date.layout.nextMatchDate="下一次匹配 在: %1" AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 在重复时将选定日期更新为重复日期"
AdvSceneSwitcher.condition.date.layout.updateOnRepeat="{{updateOnRepeat}} 在重复时将选定日期更新为重复日期"
AdvSceneSwitcher.condition.sceneTransform="场景项目变换" AdvSceneSwitcher.condition.sceneTransform="场景项目变换"
AdvSceneSwitcher.condition.sceneTransform.getTransform="获取变换" AdvSceneSwitcher.condition.sceneTransform.getTransform="获取变换"
AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="获取当前值" AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="获取当前值"
@@ -840,7 +840,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切换可见性" AdvSceneSwitcher.action.sceneVisibility.type.toggle="切换可见性"
AdvSceneSwitcher.action.sceneVisibility.type.source="源" AdvSceneSwitcher.action.sceneVisibility.type.source="源"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="任何" AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="任何"
AdvSceneSwitcher.action.sceneVisibility.layout="{{scenes}}{{actions}}{{sources}}" AdvSceneSwitcher.action.sceneVisibility.entry="{{scenes}}{{actions}}{{sources}}"
AdvSceneSwitcher.action.filter="滤镜" AdvSceneSwitcher.action.filter="滤镜"
AdvSceneSwitcher.action.filter.type.enable="开启" AdvSceneSwitcher.action.filter.type.enable="开启"
AdvSceneSwitcher.action.filter.type.disable="关闭" AdvSceneSwitcher.action.filter.type.disable="关闭"
@@ -1100,8 +1100,8 @@ AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条
AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:" AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}于{{variables}}的{{direction}}起,内容为{{paddingCharSelection}}将其变量值长度增加至{{stringLength}}." AdvSceneSwitcher.action.variable.layout.pad="{{actions}}于{{variables}}的{{direction}}起,内容为{{paddingCharSelection}}将其变量值长度增加至{{stringLength}}."
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}于{{variables}}的{{direction}}起,将变量长度缩减至{{stringLength}}" AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}于{{variables}}的{{direction}}起,将变量长度缩减至{{stringLength}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="截取字符串开始位置:{{subStringStart}} 截取字符串长度:{{subStringSize}}{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringIndex="截取字符串开始位置:{{subStringStart}} 截取字符串长度:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:{{subStringRegex}}" AdvSceneSwitcher.action.variable.layout.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}" AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}使用自定义提示{{inputPrompt}}" AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}使用自定义提示{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}用占位符填充{{inputPlaceholder}}" AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}用占位符填充{{inputPlaceholder}}"
@@ -1139,10 +1139,10 @@ AdvSceneSwitcher.action.twitch.type.user.getInfo="获取用户信息"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="获取频道积分奖励信息" AdvSceneSwitcher.action.twitch.type.reward.getInfo="获取频道积分奖励信息"
AdvSceneSwitcher.action.twitch.reward.toggleControl="切换奖励名称/变量选择控制" AdvSceneSwitcher.action.twitch.reward.toggleControl="切换奖励名称/变量选择控制"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="在未选择 Twitch 帐户的情况下无法选择类别!" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="在未选择 Twitch 帐户的情况下无法选择类别!"
AdvSceneSwitcher.action.twitch.layout.default="{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}" AdvSceneSwitcher.action.twitch.entry.default="{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="使用账户{{account}}{{actions}}{{channel}}" AdvSceneSwitcher.action.twitch.entry.chat="使用账户{{account}}{{actions}}{{channel}}"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="使用账户{{account}}{{actions}}{{userInfoQueryType}}{{userLogin}}{{userId}}" AdvSceneSwitcher.action.twitch.entry.user.getInfo="使用账户{{account}}{{actions}}{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="使用账户{{account}}{{actions}} 频道{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}" AdvSceneSwitcher.action.twitch.entry.reward.getInfo="使用账户{{account}}{{actions}} 频道{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="输入标题" AdvSceneSwitcher.action.twitch.title.title="输入标题"
AdvSceneSwitcher.action.twitch.marker.description="标记描述" AdvSceneSwitcher.action.twitch.marker.description="标记描述"
AdvSceneSwitcher.action.twitch.clip.hasDelay="在捕捉视频片段前稍加延迟" AdvSceneSwitcher.action.twitch.clip.hasDelay="在捕捉视频片段前稍加延迟"
@@ -2115,6 +2115,8 @@ AdvSceneSwitcher.status.inactive="已停止"
AdvSceneSwitcher.running="插件正在运行" AdvSceneSwitcher.running="插件正在运行"
AdvSceneSwitcher.stopped="插件已停止" AdvSceneSwitcher.stopped="插件已停止"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>这似乎是您第一次启动高级场景切换器.<br>请看一下 <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> 查看指南和示例列表.<br>如果有问题在在OBS论坛插件帖子内提问 <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">thread</span></a></p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="此选项卡的开发已停止!请考虑转换为使用宏来代替。\n可以在“常规”选项卡上禁用此提示." AdvSceneSwitcher.deprecatedTabWarning="此选项卡的开发已停止!请考虑转换为使用宏来代替。\n可以在“常规”选项卡上禁用此提示."
AdvSceneSwitcher.unit.milliseconds="毫秒" AdvSceneSwitcher.unit.milliseconds="毫秒"

View File

@@ -68,7 +68,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>962</width> <width>962</width>
<height>1190</height> <height>1132</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_19"> <layout class="QVBoxLayout" name="verticalLayout_19">
@@ -268,19 +268,9 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="alwaysShowMacroSearch"> <widget class="QCheckBox" name="uiHintsDisable">
<property name="text"> <property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch</string> <string>AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="alwaysShowFeatureTabs">
<property name="toolTip">
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs.tooltip</string>
</property>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -292,9 +282,9 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="uiHintsDisable"> <widget class="QCheckBox" name="alwaysShowMacroSearch">
<property name="text"> <property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints</string> <string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -565,13 +555,6 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="openSetupWizard">
<property name="text">
<string>FirstRunWizard.openButton</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>
@@ -793,19 +776,25 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_18"> <widget class="QCheckBox" name="runMacroOnChange">
<property name="text"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.actionTriggerMode.label</string> <string>AdvSceneSwitcher.macroTab.onChange</string>
</property> </property>
<property name="wordWrap"> <property name="text">
<bool>true</bool> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="actionTriggerMode"> <widget class="QLabel" name="label_18">
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.actionTriggerMode.tooltip</string> <string>AdvSceneSwitcher.macroTab.onChange</string>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.onChange</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@@ -4119,7 +4108,7 @@
<tabstop>macroName</tabstop> <tabstop>macroName</tabstop>
<tabstop>runMacro</tabstop> <tabstop>runMacro</tabstop>
<tabstop>runMacroInParallel</tabstop> <tabstop>runMacroInParallel</tabstop>
<tabstop>actionTriggerMode</tabstop> <tabstop>runMacroOnChange</tabstop>
<tabstop>macroSettings</tabstop> <tabstop>macroSettings</tabstop>
<tabstop>macroEdit</tabstop> <tabstop>macroEdit</tabstop>
<tabstop>sceneGroups</tabstop> <tabstop>sceneGroups</tabstop>

View File

@@ -1,6 +1,5 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "backup.hpp" #include "backup.hpp"
#include "crash-handler.hpp"
#include "log-helper.hpp" #include "log-helper.hpp"
#include "macro-helpers.hpp" #include "macro-helpers.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
@@ -192,21 +191,25 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->m.lock(); switcher->m.lock();
if (switcher->VersionChanged(data, g_GIT_SHA1)) { if (switcher->VersionChanged(data, g_GIT_SHA1)) {
AskForBackup(data); auto json = obs_data_get_json(data);
static QString jsonQString = json ? json : "";
std::thread t([]() {
obs_queue_task(
OBS_TASK_UI,
[](void *) {
AskForBackup(jsonQString);
},
nullptr, false);
});
t.detach();
} }
switcher->LoadSettings(data); switcher->LoadSettings(data);
switcher->m.unlock(); switcher->m.unlock();
if (switcher->stop) { if (!switcher->stop) {
return; switcher->Start();
} }
if (ShouldSkipPluginStartOnUncleanShutdown()) {
return;
}
switcher->Start();
} }
} }

View File

@@ -70,7 +70,6 @@ public slots:
void on_priorityUp_clicked(); void on_priorityUp_clicked();
void on_priorityDown_clicked(); void on_priorityDown_clicked();
void on_threadPriority_currentTextChanged(const QString &text); void on_threadPriority_currentTextChanged(const QString &text);
void on_openSetupWizard_clicked();
/* --- End of legacy tab section --- */ /* --- End of legacy tab section --- */
@@ -108,7 +107,7 @@ public slots:
void on_macroDown_clicked() const; void on_macroDown_clicked() const;
void on_macroName_editingFinished(); void on_macroName_editingFinished();
void on_runMacroInParallel_stateChanged(int value) const; void on_runMacroInParallel_stateChanged(int value) const;
void on_actionTriggerMode_currentIndexChanged(int index) const; void on_runMacroOnChange_stateChanged(int value) const;
void MacroSelectionChanged(); void MacroSelectionChanged();
void ShowMacroContextMenu(const QPoint &); void ShowMacroContextMenu(const QPoint &);
void CopyMacro(); void CopyMacro();

View File

@@ -1,18 +1,19 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "file-selection.hpp" #include "file-selection.hpp"
#include "filter-combo-box.hpp" #include "filter-combo-box.hpp"
#include "first-run-wizard.hpp"
#include "layout-helpers.hpp" #include "layout-helpers.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "macro-search.hpp" #include "macro-search.hpp"
#include "macro-settings.hpp" #include "macro-settings.hpp"
#include "path-helpers.hpp" #include "path-helpers.hpp"
#include "selection-helpers.hpp"
#include "source-helpers.hpp" #include "source-helpers.hpp"
#include "splitter-helpers.hpp" #include "splitter-helpers.hpp"
#include "status-control.hpp" #include "status-control.hpp"
#include "switcher-data.hpp" #include "switcher-data.hpp"
#include "tab-helpers.hpp" #include "tab-helpers.hpp"
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include "utility.hpp"
#include "variable.hpp" #include "variable.hpp"
#include "version.h" #include "version.h"
@@ -360,46 +361,14 @@ void AdvSceneSwitcher::RestoreWindowGeo()
} }
} }
static void renameMacroIfNecessary(const std::shared_ptr<Macro> &macro)
{
if (!GetMacroByName(macro->Name().c_str())) {
return;
}
auto name = macro->Name();
int i = 2;
while (GetMacroByName((name + " " + std::to_string(i)).c_str())) {
i++;
}
macro->SetName(name + " " + std::to_string(i));
}
void AdvSceneSwitcher::CheckFirstTimeSetup() void AdvSceneSwitcher::CheckFirstTimeSetup()
{ {
if (!IsFirstRun() || !GetTopLevelMacros().empty()) { if (switcher->firstBoot && !switcher->disableHints) {
return; switcher->firstBoot = false;
DisplayMessage(
obs_module_text("AdvSceneSwitcher.firstBootMessage"));
switcher->Start();
} }
auto macro = FirstRunWizard::ShowWizard(this);
if (macro) {
renameMacroIfNecessary(macro);
QTimer::singleShot(0, this,
[this, macro]() { ui->macros->Add(macro); });
}
switcher->Start();
}
void AdvSceneSwitcher::on_openSetupWizard_clicked()
{
auto macro = FirstRunWizard::ShowWizard(this);
if (!macro) {
return;
}
renameMacroIfNecessary(macro);
ui->macros->Add(macro);
ui->tabWidget->setCurrentWidget(ui->macroTab);
} }
void AdvSceneSwitcher::on_tabWidget_currentChanged(int) void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
@@ -1060,8 +1029,6 @@ void AdvSceneSwitcher::SetupGeneralTab()
setTabOrder(ui->importSettings, ui->cooldownTime); setTabOrder(ui->importSettings, ui->cooldownTime);
setTabOrder(ui->cooldownTime, ui->noMatchDontSwitch); setTabOrder(ui->cooldownTime, ui->noMatchDontSwitch);
SetupShowAllTabsCheckBox(ui->alwaysShowFeatureTabs, ui->tabWidget);
MinimizeSizeOfColumn(ui->statusLayout, 0); MinimizeSizeOfColumn(ui->statusLayout, 0);
setWindowTitle(windowTitle() + " - " + g_GIT_TAG); setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
} }

View File

@@ -93,46 +93,33 @@ bool MacroActionMacro::PerformAction()
return true; return true;
} }
const auto performActionForMacro = [this](Macro *macro) { switch (_action) {
switch (_action) { case Action::PAUSE:
case Action::PAUSE: macro->SetPaused();
macro->SetPaused(); break;
break; case Action::UNPAUSE:
case Action::UNPAUSE: macro->SetPaused(false);
macro->SetPaused(false); break;
break; case Action::TOGGLE_PAUSE:
case Action::TOGGLE_PAUSE: macro->SetPaused(!macro->Paused());
macro->SetPaused(!macro->Paused()); break;
break; case Action::RESET_COUNTER:
case Action::RESET_COUNTER: macro->ResetRunCount();
macro->ResetRunCount(); break;
break; case Action::RUN_ACTIONS:
case Action::RUN_ACTIONS: RunActions(macro.get());
RunActions(macro); break;
break; case Action::STOP:
case Action::STOP: macro->Stop();
macro->Stop(); break;
break; case Action::DISABLE_ACTION:
case Action::DISABLE_ACTION: case Action::ENABLE_ACTION:
case Action::ENABLE_ACTION: case Action::TOGGLE_ACTION:
case Action::TOGGLE_ACTION: AdjustActionState(macro.get());
AdjustActionState(macro); break;
break; default:
default: break;
break;
}
};
if (!IsGroupMacro(macro.get())) {
performActionForMacro(macro.get());
return true;
} }
auto macros = GetGroupMacroEntries(macro.get());
for (const auto &macro : macros) {
performActionForMacro(macro.get());
}
return true; return true;
} }
@@ -417,7 +404,6 @@ MacroActionMacroEdit::MacroActionMacroEdit(
populateActionTypes(_actionTypes); populateActionTypes(_actionTypes);
_conditionMacros->HideSelectedMacro(); _conditionMacros->HideSelectedMacro();
_conditionMacros->HideGroups();
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)), QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &))); this, SLOT(MacroChanged(const QString &)));

View File

@@ -219,43 +219,6 @@ void MacroActionVariable::GenerateRandomNumber(Variable *var)
} }
} }
void MacroActionVariable::PickRandomValue(Variable *var)
{
static std::random_device rd;
static std::mt19937 gen(rd());
static const uint8_t maxIter = 255;
if (_randomValues.isEmpty()) {
return;
}
if (_randomValues.size() == 1) {
const auto &value = _randomValues.at(0);
var->SetValue(value);
_lastRandomValue = value;
}
std::uniform_int_distribution<int> dis(0, _randomValues.size() - 1);
StringVariable value = _randomValues.at(dis(gen));
uint8_t iter = 0;
for (; !_allowRepeatValues && iter < maxIter &&
_lastRandomValue.has_value() &&
std::string(value) == _lastRandomValue.value();
iter++) {
value = _randomValues.at(dis(gen));
}
if (iter == maxIter) {
blog(LOG_INFO,
"giving up picking non-repeat random value after %d tries",
maxIter);
}
var->SetValue(value);
_lastRandomValue = value;
}
struct AskForInputParams { struct AskForInputParams {
AskForInputParams(const QString &prompt_, const QString &placeholder_) AskForInputParams(const QString &prompt_, const QString &placeholder_)
: prompt(prompt_), : prompt(prompt_),
@@ -495,9 +458,6 @@ bool MacroActionVariable::PerformAction()
case Action::RANDOM_NUMBER: case Action::RANDOM_NUMBER:
GenerateRandomNumber(var.get()); GenerateRandomNumber(var.get());
return true; return true;
case Action::RANDOM_LIST_VALUE:
PickRandomValue(var.get());
return true;
} }
return true; return true;
@@ -538,8 +498,6 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
_randomNumberStart.Save(obj, "randomNumberStart"); _randomNumberStart.Save(obj, "randomNumberStart");
_randomNumberEnd.Save(obj, "randomNumberEnd"); _randomNumberEnd.Save(obj, "randomNumberEnd");
obs_data_set_bool(obj, "generateInteger", _generateInteger); obs_data_set_bool(obj, "generateInteger", _generateInteger);
_randomValues.Save(obj, "randomValues", "value");
obs_data_set_bool(obj, "allowRepeatValues", _allowRepeatValues);
_jsonQuery.Save(obj, "jsonQuery"); _jsonQuery.Save(obj, "jsonQuery");
_jsonIndex.Save(obj, "jsonIndex"); _jsonIndex.Save(obj, "jsonIndex");
@@ -598,8 +556,6 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_randomNumberStart.Load(obj, "randomNumberStart"); _randomNumberStart.Load(obj, "randomNumberStart");
_randomNumberEnd.Load(obj, "randomNumberEnd"); _randomNumberEnd.Load(obj, "randomNumberEnd");
_generateInteger = obs_data_get_bool(obj, "generateInteger"); _generateInteger = obs_data_get_bool(obj, "generateInteger");
_randomValues.Load(obj, "randomValues", "value");
_allowRepeatValues = obs_data_get_bool(obj, "allowRepeatValues");
_jsonQuery.Load(obj, "jsonQuery"); _jsonQuery.Load(obj, "jsonQuery");
_jsonIndex.Load(obj, "jsonIndex"); _jsonIndex.Load(obj, "jsonIndex");
@@ -768,8 +724,6 @@ static inline void populateActionSelection(QComboBox *list)
"AdvSceneSwitcher.action.variable.type.roundToInt"}, "AdvSceneSwitcher.action.variable.type.roundToInt"},
{MacroActionVariable::Action::RANDOM_NUMBER, {MacroActionVariable::Action::RANDOM_NUMBER,
"AdvSceneSwitcher.action.variable.type.randomNumber"}, "AdvSceneSwitcher.action.variable.type.randomNumber"},
{MacroActionVariable::Action::RANDOM_LIST_VALUE,
"AdvSceneSwitcher.action.variable.type.randomListValue"},
{true, ""}, // Separator {true, ""}, // Separator
{MacroActionVariable::Action::USER_INPUT, {MacroActionVariable::Action::USER_INPUT,
@@ -847,10 +801,11 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_segmentValueStatus(new QLabel()), _segmentValueStatus(new QLabel()),
_segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)), _segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)),
_substringLayout(new QVBoxLayout()), _substringLayout(new QVBoxLayout()),
_subStringControlsLayout(new QHBoxLayout()), _subStringIndexEntryLayout(new QHBoxLayout()),
_subStringRegexEntryLayout(new QHBoxLayout()),
_subStringStart(new VariableSpinBox(this)), _subStringStart(new VariableSpinBox(this)),
_subStringSize(new VariableSpinBox(this)), _subStringSize(new VariableSpinBox(this)),
_subStringRegex(new RegexConfigWidget(parent)), _substringRegex(new RegexConfigWidget(parent)),
_regexPattern(new ResizingPlainTextEdit(this, 10, 1, 1)), _regexPattern(new ResizingPlainTextEdit(this, 10, 1, 1)),
_regexMatchIdx(new VariableSpinBox(this)), _regexMatchIdx(new VariableSpinBox(this)),
_findReplaceLayout(new QHBoxLayout()), _findReplaceLayout(new QHBoxLayout()),
@@ -882,13 +837,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
obs_module_text( obs_module_text(
"AdvSceneSwitcher.action.variable.generateInteger"), "AdvSceneSwitcher.action.variable.generateInteger"),
this)), this)),
_randomNumberLayout(new QVBoxLayout()), _randomLayout(new QVBoxLayout()),
_randomValues(new StringListEdit(this)),
_allowRepeatValues(new QCheckBox(
obs_module_text(
"AdvSceneSwitcher.action.variable.type.allowRepeat"),
this)),
_randomValueLayout(new QVBoxLayout()),
_jsonQuery(new VariableLineEdit(this)), _jsonQuery(new VariableLineEdit(this)),
_jsonQueryHelp(new HelpIcon( _jsonQueryHelp(new HelpIcon(
obs_module_text( obs_module_text(
@@ -924,7 +873,6 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_randomNumberStart->setMaximum(9999999999); _randomNumberStart->setMaximum(9999999999);
_randomNumberEnd->setMinimum(-9999999999); _randomNumberEnd->setMinimum(-9999999999);
_randomNumberEnd->setMaximum(9999999999); _randomNumberEnd->setMaximum(9999999999);
_randomValues->SetMaxStringSize(99999999);
_jsonIndex->setMaximum(999); _jsonIndex->setMaximum(999);
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)), QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
@@ -953,7 +901,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_subStringSize, _subStringSize,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)), SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(SubStringSizeChanged(const NumberVariable<int> &))); this, SLOT(SubStringSizeChanged(const NumberVariable<int> &)));
QWidget::connect(_subStringRegex, QWidget::connect(_substringRegex,
SIGNAL(RegexConfigChanged(const RegexConfig &)), this, SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
SLOT(SubStringRegexChanged(const RegexConfig &))); SLOT(SubStringRegexChanged(const RegexConfig &)));
QWidget::connect(_regexPattern, SIGNAL(textChanged()), this, QWidget::connect(_regexPattern, SIGNAL(textChanged()), this,
@@ -1013,11 +961,6 @@ MacroActionVariableEdit::MacroActionVariableEdit(
SLOT(RandomNumberEndChanged(const NumberVariable<double> &))); SLOT(RandomNumberEndChanged(const NumberVariable<double> &)));
QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this, QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this,
SLOT(GenerateIntegerChanged(int))); SLOT(GenerateIntegerChanged(int)));
QWidget::connect(_randomValues,
SIGNAL(StringListChanged(const StringList &)), this,
SLOT(RandomValueListChanged(const StringList &)));
QWidget::connect(_allowRepeatValues, SIGNAL(stateChanged(int)), this,
SLOT(AllowRepeatValuesChanged(int)));
QWidget::connect(_jsonQuery, SIGNAL(editingFinished()), this, QWidget::connect(_jsonQuery, SIGNAL(editingFinished()), this,
SLOT(JsonQueryChanged())); SLOT(JsonQueryChanged()));
QWidget::connect( QWidget::connect(
@@ -1032,6 +975,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
{"{{strValue}}", _strValue}, {"{{strValue}}", _strValue},
{"{{numValue}}", _numValue}, {"{{numValue}}", _numValue},
{"{{segmentIndex}}", _segmentIdx}, {"{{segmentIndex}}", _segmentIdx},
{"{{subStringStart}}", _subStringStart},
{"{{subStringSize}}", _subStringSize},
{"{{regexMatchIdx}}", _regexMatchIdx},
{"{{findRegex}}", _findRegex}, {"{{findRegex}}", _findRegex},
{"{{findStr}}", _findStr}, {"{{findStr}}", _findStr},
{"{{replaceStr}}", _replaceStr}, {"{{replaceStr}}", _replaceStr},
@@ -1063,7 +1009,12 @@ MacroActionVariableEdit::MacroActionVariableEdit(
PlaceWidgets( PlaceWidgets(
obs_module_text( obs_module_text(
"AdvSceneSwitcher.action.variable.layout.substringIndex"), "AdvSceneSwitcher.action.variable.layout.substringIndex"),
_subStringControlsLayout, widgetPlaceholders); _subStringIndexEntryLayout, widgetPlaceholders);
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.variable.layout.substringRegex"),
_subStringRegexEntryLayout, widgetPlaceholders);
PlaceWidgets( PlaceWidgets(
obs_module_text( obs_module_text(
@@ -1084,17 +1035,15 @@ MacroActionVariableEdit::MacroActionVariableEdit(
obs_module_text( obs_module_text(
"AdvSceneSwitcher.action.variable.layout.randomNumber"), "AdvSceneSwitcher.action.variable.layout.randomNumber"),
randomLayout, widgetPlaceholders); randomLayout, widgetPlaceholders);
_randomNumberLayout->addLayout(randomLayout); _randomLayout->addLayout(randomLayout);
_randomNumberLayout->addWidget(_generateInteger); _randomLayout->addWidget(_generateInteger);
_randomValueLayout->addWidget(_randomValues);
_randomValueLayout->addWidget(_allowRepeatValues);
auto regexConfigLayout = new QHBoxLayout; auto regexConfigLayout = new QHBoxLayout;
regexConfigLayout->addWidget(_subStringRegex); regexConfigLayout->addWidget(_substringRegex);
regexConfigLayout->addStretch(); regexConfigLayout->addStretch();
_substringLayout->addLayout(_subStringControlsLayout); _substringLayout->addLayout(_subStringIndexEntryLayout);
_substringLayout->addLayout(_subStringRegexEntryLayout);
_substringLayout->addWidget(_regexPattern); _substringLayout->addWidget(_regexPattern);
_substringLayout->addLayout(regexConfigLayout); _substringLayout->addLayout(regexConfigLayout);
@@ -1107,8 +1056,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
layout->addWidget(_mathExpressionResult); layout->addWidget(_mathExpressionResult);
layout->addLayout(_promptLayout); layout->addLayout(_promptLayout);
layout->addLayout(_placeholderLayout); layout->addLayout(_placeholderLayout);
layout->addLayout(_randomNumberLayout); layout->addLayout(_randomLayout);
layout->addLayout(_randomValueLayout);
setLayout(layout); setLayout(layout);
_entryData = entryData; _entryData = entryData;
@@ -1142,7 +1090,7 @@ void MacroActionVariableEdit::UpdateEntryData()
: MacroSegmentSelection::Type::ACTION); : MacroSegmentSelection::Type::ACTION);
_subStringStart->SetValue(_entryData->_subStringStart); _subStringStart->SetValue(_entryData->_subStringStart);
_subStringSize->SetValue(_entryData->_subStringSize); _subStringSize->SetValue(_entryData->_subStringSize);
_subStringRegex->SetRegexConfig(_entryData->_subStringRegex); _substringRegex->SetRegexConfig(_entryData->_subStringRegex);
_findRegex->SetRegexConfig(_entryData->_findRegex); _findRegex->SetRegexConfig(_entryData->_findRegex);
_regexPattern->setPlainText( _regexPattern->setPlainText(
QString::fromStdString(_entryData->_regexPattern)); QString::fromStdString(_entryData->_regexPattern));
@@ -1167,8 +1115,6 @@ void MacroActionVariableEdit::UpdateEntryData()
_randomNumberStart->SetValue(_entryData->_randomNumberStart); _randomNumberStart->SetValue(_entryData->_randomNumberStart);
_randomNumberEnd->SetValue(_entryData->_randomNumberEnd); _randomNumberEnd->SetValue(_entryData->_randomNumberEnd);
_generateInteger->setChecked(_entryData->_generateInteger); _generateInteger->setChecked(_entryData->_generateInteger);
_allowRepeatValues->setChecked(_entryData->_allowRepeatValues);
_randomValues->SetStringList(_entryData->_randomValues);
_jsonQuery->setText(_entryData->_jsonQuery); _jsonQuery->setText(_entryData->_jsonQuery);
_jsonIndex->SetValue(_entryData->_jsonIndex); _jsonIndex->SetValue(_entryData->_jsonIndex);
@@ -1511,18 +1457,6 @@ void MacroActionVariableEdit::GenerateIntegerChanged(int value)
_entryData->_generateInteger = value; _entryData->_generateInteger = value;
} }
void MacroActionVariableEdit::RandomValueListChanged(const StringList &values)
{
GUARD_LOADING_AND_LOCK();
_entryData->_randomValues = values;
}
void MacroActionVariableEdit::AllowRepeatValuesChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_allowRepeatValues = value;
}
void MacroActionVariableEdit::JsonQueryChanged() void MacroActionVariableEdit::JsonQueryChanged()
{ {
GUARD_LOADING_AND_LOCK(); GUARD_LOADING_AND_LOCK();
@@ -1561,20 +1495,8 @@ void MacroActionVariableEdit::SetWidgetVisibility()
{"{{jsonQuery}}", _jsonQuery}, {"{{jsonQuery}}", _jsonQuery},
{"{{jsonQueryHelp}}", _jsonQueryHelp}, {"{{jsonQueryHelp}}", _jsonQueryHelp},
{"{{jsonIndex}}", _jsonIndex}, {"{{jsonIndex}}", _jsonIndex},
{"{{subStringRegex}}", _subStringRegex},
{"{{subStringStart}}", _subStringStart},
{"{{subStringSize}}", _subStringSize},
{"{{regexMatchIdx}}", _regexMatchIdx},
}; };
for (const auto &[_, widget] : widgetPlaceholders) {
_entryLayout->removeWidget(widget);
_subStringControlsLayout->removeWidget(widget);
}
ClearLayout(_entryLayout);
ClearLayout(_subStringControlsLayout);
const char *layoutString = ""; const char *layoutString = "";
if (_entryData->_action == MacroActionVariable::Action::PAD) { if (_entryData->_action == MacroActionVariable::Action::PAD) {
layoutString = obs_module_text( layoutString = obs_module_text(
@@ -1588,6 +1510,11 @@ void MacroActionVariableEdit::SetWidgetVisibility()
"AdvSceneSwitcher.action.variable.layout.other"); "AdvSceneSwitcher.action.variable.layout.other");
} }
for (const auto &[_, widget] : widgetPlaceholders) {
_entryLayout->removeWidget(widget);
}
ClearLayout(_entryLayout);
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders); PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
if (_entryData->_action == MacroActionVariable::Action::SET_VALUE || if (_entryData->_action == MacroActionVariable::Action::SET_VALUE ||
@@ -1634,25 +1561,15 @@ void MacroActionVariableEdit::SetWidgetVisibility()
MacroActionVariable::Action::SET_ACTION_VALUE || MacroActionVariable::Action::SET_ACTION_VALUE ||
_entryData->_action == _entryData->_action ==
MacroActionVariable::Action::SET_CONDITION_VALUE); MacroActionVariable::Action::SET_CONDITION_VALUE);
bool showRegex = _entryData->_subStringRegex.Enabled();
layoutString =
showRegex
? "AdvSceneSwitcher.action.variable.layout.substringRegex"
: "AdvSceneSwitcher.action.variable.layout.substringIndex";
PlaceWidgets(obs_module_text(layoutString), _subStringControlsLayout,
widgetPlaceholders);
_subStringStart->setVisible(!showRegex);
_subStringSize->setVisible(!showRegex);
_regexMatchIdx->setVisible(showRegex);
SetLayoutVisible(_substringLayout, SetLayoutVisible(_substringLayout,
_entryData->_action == _entryData->_action ==
MacroActionVariable::Action::SUBSTRING); MacroActionVariable::Action::SUBSTRING);
_regexPattern->setVisible( if (_entryData->_action == MacroActionVariable::Action::SUBSTRING) {
showRegex && bool showRegex = _entryData->_subStringRegex.Enabled();
_entryData->_action == MacroActionVariable::Action::SUBSTRING); SetLayoutVisible(_subStringIndexEntryLayout, !showRegex);
SetLayoutVisible(_subStringRegexEntryLayout, showRegex);
_regexPattern->setVisible(showRegex);
}
SetLayoutVisible(_findReplaceLayout, SetLayoutVisible(_findReplaceLayout,
_entryData->_action == _entryData->_action ==
MacroActionVariable::Action::FIND_AND_REPLACE); MacroActionVariable::Action::FIND_AND_REPLACE);
@@ -1717,13 +1634,9 @@ void MacroActionVariableEdit::SetWidgetVisibility()
MacroActionVariable::Action::PAD); MacroActionVariable::Action::PAD);
_caseType->setVisible(_entryData->_action == _caseType->setVisible(_entryData->_action ==
MacroActionVariable::Action::CHANGE_CASE); MacroActionVariable::Action::CHANGE_CASE);
SetLayoutVisible(_randomNumberLayout, SetLayoutVisible(_randomLayout,
_entryData->_action == _entryData->_action ==
MacroActionVariable::Action::RANDOM_NUMBER); MacroActionVariable::Action::RANDOM_NUMBER);
SetLayoutVisible(
_randomValueLayout,
_entryData->_action ==
MacroActionVariable::Action::RANDOM_LIST_VALUE);
_jsonQuery->setVisible(_entryData->_action == _jsonQuery->setVisible(_entryData->_action ==
MacroActionVariable::Action::QUERY_JSON); MacroActionVariable::Action::QUERY_JSON);
_jsonQueryHelp->setVisible(_entryData->_action == _jsonQueryHelp->setVisible(_entryData->_action ==

View File

@@ -6,7 +6,6 @@
#include "resizing-text-edit.hpp" #include "resizing-text-edit.hpp"
#include "scene-selection.hpp" #include "scene-selection.hpp"
#include "single-char-selection.hpp" #include "single-char-selection.hpp"
#include "string-list.hpp"
#include "variable-line-edit.hpp" #include "variable-line-edit.hpp"
#include "variable-text-edit.hpp" #include "variable-text-edit.hpp"
#include "variable-spinbox.hpp" #include "variable-spinbox.hpp"
@@ -57,7 +56,6 @@ public:
QUERY_JSON, QUERY_JSON,
ARRAY_JSON, ARRAY_JSON,
COPY_VAR, COPY_VAR,
RANDOM_LIST_VALUE,
}; };
Action _action = Action::SET_VALUE; Action _action = Action::SET_VALUE;
@@ -109,10 +107,6 @@ public:
DoubleVariable _randomNumberEnd = 100; DoubleVariable _randomNumberEnd = 100;
bool _generateInteger = true; bool _generateInteger = true;
StringList _randomValues = {"value1", "value2", "value3"};
bool _allowRepeatValues = true;
std::optional<std::string> _lastRandomValue;
StringVariable _jsonQuery = "$.some.nested.value"; StringVariable _jsonQuery = "$.some.nested.value";
IntVariable _jsonIndex = 0; IntVariable _jsonIndex = 0;
@@ -125,7 +119,6 @@ private:
void HandleCaseChange(Variable *); void HandleCaseChange(Variable *);
void SetToSceneItemName(Variable *); void SetToSceneItemName(Variable *);
void GenerateRandomNumber(Variable *); void GenerateRandomNumber(Variable *);
void PickRandomValue(Variable *);
std::weak_ptr<MacroSegment> _macroSegment; std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1; int _segmentIdxLoadValue = -1;
@@ -182,8 +175,6 @@ private slots:
void RandomNumberStartChanged(const NumberVariable<double> &); void RandomNumberStartChanged(const NumberVariable<double> &);
void RandomNumberEndChanged(const NumberVariable<double> &); void RandomNumberEndChanged(const NumberVariable<double> &);
void GenerateIntegerChanged(int); void GenerateIntegerChanged(int);
void RandomValueListChanged(const StringList &);
void AllowRepeatValuesChanged(int);
void JsonQueryChanged(); void JsonQueryChanged();
void JsonIndexChanged(const NumberVariable<int> &); void JsonIndexChanged(const NumberVariable<int> &);
@@ -203,10 +194,11 @@ private:
QLabel *_segmentValueStatus; QLabel *_segmentValueStatus;
ResizingPlainTextEdit *_segmentValue; ResizingPlainTextEdit *_segmentValue;
QVBoxLayout *_substringLayout; QVBoxLayout *_substringLayout;
QHBoxLayout *_subStringControlsLayout; QHBoxLayout *_subStringIndexEntryLayout;
QHBoxLayout *_subStringRegexEntryLayout;
VariableSpinBox *_subStringStart; VariableSpinBox *_subStringStart;
VariableSpinBox *_subStringSize; VariableSpinBox *_subStringSize;
RegexConfigWidget *_subStringRegex; RegexConfigWidget *_substringRegex;
ResizingPlainTextEdit *_regexPattern; ResizingPlainTextEdit *_regexPattern;
VariableSpinBox *_regexMatchIdx; VariableSpinBox *_regexMatchIdx;
QHBoxLayout *_findReplaceLayout; QHBoxLayout *_findReplaceLayout;
@@ -233,10 +225,7 @@ private:
VariableDoubleSpinBox *_randomNumberStart; VariableDoubleSpinBox *_randomNumberStart;
VariableDoubleSpinBox *_randomNumberEnd; VariableDoubleSpinBox *_randomNumberEnd;
QCheckBox *_generateInteger; QCheckBox *_generateInteger;
QVBoxLayout *_randomNumberLayout; QVBoxLayout *_randomLayout;
StringListEdit *_randomValues;
QCheckBox *_allowRepeatValues;
QVBoxLayout *_randomValueLayout;
VariableLineEdit *_jsonQuery; VariableLineEdit *_jsonQuery;
QLabel *_jsonQueryHelp; QLabel *_jsonQueryHelp;
VariableSpinBox *_jsonIndex; VariableSpinBox *_jsonIndex;

View File

@@ -315,7 +315,6 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
_actionIndex(new MacroSegmentSelection( _actionIndex(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::ACTION)) this, MacroSegmentSelection::Type::ACTION))
{ {
_macros->HideGroups();
_count->setMaximum(10000000); _count->setMaximum(10000000);
populateTypeSelection(_types); populateTypeSelection(_types);
populateCounterConditionSelection(_counterConditions); populateCounterConditionSelection(_counterConditions);

View File

@@ -7,17 +7,6 @@ MacroCondition::MacroCondition(Macro *m, bool supportsVariableValue)
{ {
} }
bool MacroCondition::EvaluateCondition()
{
bool newValue = CheckCondition();
_changed = _previousValue.has_value() && (*_previousValue != newValue);
const bool negate = _logic.IsNegationType(GetLogicType());
_risingEdge = _changed &&
((!negate && newValue) || (negate && !newValue));
_previousValue = newValue;
return newValue;
}
bool MacroCondition::Save(obs_data_t *obj) const bool MacroCondition::Save(obs_data_t *obj) const
{ {
MacroSegment::Save(obj); MacroSegment::Save(obj);

View File

@@ -4,19 +4,13 @@
#include "duration-modifier.hpp" #include "duration-modifier.hpp"
#include "macro-ref.hpp" #include "macro-ref.hpp"
#include <optional>
namespace advss { namespace advss {
class EXPORT MacroCondition : public MacroSegment { class EXPORT MacroCondition : public MacroSegment {
public: public:
MacroCondition(Macro *m, bool supportsVariableValue = false); MacroCondition(Macro *m, bool supportsVariableValue = false);
virtual ~MacroCondition() = default; virtual ~MacroCondition() = default;
virtual bool CheckCondition() = 0;
bool EvaluateCondition();
bool HasChanged() const { return _changed; }
bool IsRisingEdge() const { return _risingEdge; }
virtual bool Save(obs_data_t *obj) const = 0; virtual bool Save(obs_data_t *obj) const = 0;
virtual bool Load(obs_data_t *obj) = 0; virtual bool Load(obs_data_t *obj) = 0;
@@ -34,15 +28,9 @@ public:
static std::string_view GetDefaultID(); static std::string_view GetDefaultID();
protected:
virtual bool CheckCondition() = 0;
private: private:
Logic _logic = Logic(Logic::Type::ROOT_NONE); Logic _logic = Logic(Logic::Type::ROOT_NONE);
DurationModifier _durationModifier; DurationModifier _durationModifier;
std::optional<bool> _previousValue;
bool _changed = false;
bool _risingEdge = false;
}; };
class EXPORT MacroRefCondition : virtual public MacroCondition { class EXPORT MacroRefCondition : virtual public MacroCondition {

View File

@@ -8,6 +8,9 @@ namespace advss {
class Macro; class Macro;
class MacroSegment; class MacroSegment;
/*******************************************************************************
* Advanced Scene Switcher window
*******************************************************************************/
class MacroEdit : public QWidget { class MacroEdit : public QWidget {
Q_OBJECT Q_OBJECT

View File

@@ -117,32 +117,9 @@ GetMacroConditions(Macro *macro)
return macro->Conditions(); return macro->Conditions();
} }
bool IsGroupMacro(Macro *macro) std::string_view GetSceneSwitchActionId()
{ {
return macro && macro->IsGroup(); return MacroAction::GetDefaultID();
}
std::vector<std::shared_ptr<Macro>> GetGroupMacroEntries(Macro *macro)
{
if (!macro || !macro->IsGroup()) {
return {};
}
std::vector<std::shared_ptr<Macro>> entries;
entries.reserve(macro->GroupSize());
const auto &macros = GetTopLevelMacros();
for (auto it = macros.begin(); it < macros.end(); it++) {
if ((*it)->Name() != macro->Name()) {
continue;
}
for (uint32_t i = 1; i <= macro->GroupSize(); i++) {
entries.emplace_back(*std::next(it, i));
}
break;
}
return entries;
} }
std::condition_variable &GetMacroWaitCV() std::condition_variable &GetMacroWaitCV()
@@ -242,11 +219,6 @@ bool RunMacroActions(Macro *macro)
return macro && macro->PerformActions(true); return macro && macro->PerformActions(true);
} }
bool RunMacroElseActions(Macro *macro)
{
return macro && macro->PerformActions(false);
}
void ResetMacroConditionTimers(Macro *macro) void ResetMacroConditionTimers(Macro *macro)
{ {
if (!macro) { if (!macro) {

View File

@@ -8,6 +8,7 @@
#include <condition_variable> #include <condition_variable>
#include <deque> #include <deque>
#include <optional> #include <optional>
#include <string_view>
#include <thread> #include <thread>
struct obs_data; struct obs_data;
@@ -19,8 +20,6 @@ class Macro;
class MacroAction; class MacroAction;
class MacroCondition; class MacroCondition;
static const int macro_func = 10;
EXPORT std::deque<std::shared_ptr<Macro>> &GetTopLevelMacros(); EXPORT std::deque<std::shared_ptr<Macro>> &GetTopLevelMacros();
std::deque<std::shared_ptr<Macro>> &GetTemporaryMacros(); std::deque<std::shared_ptr<Macro>> &GetTemporaryMacros();
EXPORT std::deque<std::shared_ptr<Macro>> GetAllMacros(); EXPORT std::deque<std::shared_ptr<Macro>> GetAllMacros();
@@ -36,8 +35,9 @@ GetMacroElseActions(Macro *);
EXPORT std::optional<std::deque<std::shared_ptr<MacroCondition>>> EXPORT std::optional<std::deque<std::shared_ptr<MacroCondition>>>
GetMacroConditions(Macro *); GetMacroConditions(Macro *);
EXPORT bool IsGroupMacro(Macro *); std::string_view GetSceneSwitchActionId();
EXPORT std::vector<std::shared_ptr<Macro>> GetGroupMacroEntries(Macro *);
constexpr auto macro_func = 10;
EXPORT std::condition_variable &GetMacroWaitCV(); EXPORT std::condition_variable &GetMacroWaitCV();
EXPORT std::condition_variable &GetMacroTransitionCV(); EXPORT std::condition_variable &GetMacroTransitionCV();
@@ -68,7 +68,6 @@ EXPORT void AddMacroHelperThread(Macro *, std::thread &&);
EXPORT bool CheckMacros(); EXPORT bool CheckMacros();
EXPORT bool RunMacroActions(Macro *); EXPORT bool RunMacroActions(Macro *);
bool RunMacroElseActions(Macro *);
EXPORT bool RunMacros(); EXPORT bool RunMacros();
void StopAllMacros(); void StopAllMacros();

View File

@@ -244,9 +244,22 @@ bool MacroMatchesSearchFilter(Macro *macro)
} }
if (macro->IsGroup()) { if (macro->IsGroup()) {
const auto groupEntries = GetGroupMacroEntries(macro); std::vector<std::shared_ptr<Macro>> subItems;
for (const auto &entry : groupEntries) { subItems.reserve(macro->GroupSize());
if (MacroMatchesSearchFilter(entry.get())) {
const auto &macros = GetTopLevelMacros();
for (auto it = macros.begin(); it < macros.end(); it++) {
if ((*it)->Name() != macro->Name()) {
continue;
}
for (uint32_t i = 1; i <= macro->GroupSize(); i++) {
subItems.emplace_back(*std::next(it, i));
}
break;
}
for (const auto &item : subItems) {
if (MacroMatchesSearchFilter(item.get())) {
return true; return true;
} }
} }

View File

@@ -15,6 +15,9 @@ MacroSelection::MacroSelection(QWidget *parent)
obs_module_text("AdvSceneSwitcher.selectMacro")) obs_module_text("AdvSceneSwitcher.selectMacro"))
{ {
for (const auto &m : GetTopLevelMacros()) { for (const auto &m : GetTopLevelMacros()) {
if (m->IsGroup()) {
continue;
}
addItem(QString::fromStdString(m->Name())); addItem(QString::fromStdString(m->Name()));
} }
@@ -51,7 +54,6 @@ void MacroSelection::HideSelectedMacro()
return; return;
} }
#ifndef UNIT_TEST
const auto m = ssWindow->ui->macros->GetCurrentMacro(); const auto m = ssWindow->ui->macros->GetCurrentMacro();
if (!m) { if (!m) {
return; return;
@@ -62,23 +64,6 @@ void MacroSelection::HideSelectedMacro()
} }
qobject_cast<QListView *>(view())->setRowHidden(idx, true); qobject_cast<QListView *>(view())->setRowHidden(idx, true);
#endif // !UNIT_TEST
}
void MacroSelection::HideGroups()
{
for (const auto &macro : GetTopLevelMacros()) {
if (!macro->IsGroup()) {
continue;
}
int idx = findText(QString::fromStdString(macro->Name()));
if (idx == -1) {
continue;
}
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
}
} }
void MacroSelection::ShowAllMacros() void MacroSelection::ShowAllMacros()

View File

@@ -15,7 +15,6 @@ public:
MacroSelection(QWidget *parent); MacroSelection(QWidget *parent);
void SetCurrentMacro(const MacroRef &); void SetCurrentMacro(const MacroRef &);
void HideSelectedMacro(); // Macro currently being edited void HideSelectedMacro(); // Macro currently being edited
void HideGroups();
void ShowAllMacros(); void ShowAllMacros();
private slots: private slots:

View File

@@ -25,8 +25,6 @@ namespace advss {
static QObject *addPulse = nullptr; static QObject *addPulse = nullptr;
static QTimer onChangeHighlightTimer; static QTimer onChangeHighlightTimer;
static std::chrono::high_resolution_clock::time_point
lastOnChangeHighlightCheckTime{};
static void disableAddButtonHighlight() static void disableAddButtonHighlight()
{ {
@@ -149,7 +147,20 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros, static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros,
const std::shared_ptr<Macro> &group) const std::shared_ptr<Macro> &group)
{ {
auto subitems = GetGroupMacroEntries(group.get()); std::vector<std::shared_ptr<Macro>> subitems;
subitems.reserve(group->GroupSize());
// Find all subitems
auto allMacros = GetTopLevelMacros();
for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
if ((*it)->Name() != group->Name()) {
continue;
}
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
subitems.emplace_back(*std::next(it, i));
}
break;
}
// Remove subitems which were already selected to avoid duplicates // Remove subitems which were already selected to avoid duplicates
for (const auto &subitem : subitems) { for (const auto &subitem : subitems) {
@@ -474,17 +485,14 @@ void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value) const
macro->SetRunInParallel(value); macro->SetRunInParallel(value);
} }
void AdvSceneSwitcher::on_actionTriggerMode_currentIndexChanged(int index) const void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value) const
{ {
auto macro = GetSelectedMacro(); auto macro = GetSelectedMacro();
if (!macro) { if (!macro) {
return; return;
} }
auto lock = LockContext(); auto lock = LockContext();
const auto mode = static_cast<Macro::ActionTriggerMode>( macro->SetMatchOnChange(value);
ui->actionTriggerMode->itemData(index).toInt());
macro->SetActionTriggerMode(mode);
} }
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
@@ -492,7 +500,7 @@ void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
ui->macroName->setDisabled(disable); ui->macroName->setDisabled(disable);
ui->runMacro->setDisabled(disable); ui->runMacro->setDisabled(disable);
ui->runMacroInParallel->setDisabled(disable); ui->runMacroInParallel->setDisabled(disable);
ui->actionTriggerMode->setDisabled(disable); ui->runMacroOnChange->setDisabled(disable);
ui->macroEdit->SetControlsDisabled(disable); ui->macroEdit->SetControlsDisabled(disable);
} }
@@ -522,12 +530,10 @@ void AdvSceneSwitcher::MacroSelectionChanged()
{ {
const QSignalBlocker b1(ui->macroName); const QSignalBlocker b1(ui->macroName);
const QSignalBlocker b2(ui->runMacroInParallel); const QSignalBlocker b2(ui->runMacroInParallel);
const QSignalBlocker b3(ui->actionTriggerMode); const QSignalBlocker b3(ui->runMacroOnChange);
ui->macroName->setText(macro->Name().c_str()); ui->macroName->setText(macro->Name().c_str());
ui->runMacroInParallel->setChecked(macro->RunInParallel()); ui->runMacroInParallel->setChecked(macro->RunInParallel());
ui->actionTriggerMode->setCurrentIndex( ui->runMacroOnChange->setChecked(macro->MatchOnChange());
ui->actionTriggerMode->findData(static_cast<int>(
macro->GetActionTriggerMode())));
} }
macro->ResetUIHelpers(); macro->ResetUIHelpers();
@@ -557,14 +563,10 @@ void AdvSceneSwitcher::HighlightOnChange() const
return; return;
} }
if (macro->ActionTriggerModePreventedActionsSince( if (macro->OnChangePreventedActionsRecently()) {
lastOnChangeHighlightCheckTime)) { HighlightWidget(ui->runMacroOnChange, Qt::yellow,
HighlightWidget(ui->actionTriggerMode, Qt::yellow,
Qt::transparent, true); Qt::transparent, true);
} }
lastOnChangeHighlightCheckTime =
std::chrono::high_resolution_clock::now();
} }
void AdvSceneSwitcher::on_macroSettings_clicked() void AdvSceneSwitcher::on_macroSettings_clicked()
@@ -663,8 +665,6 @@ void AdvSceneSwitcher::SetupMacroTab()
ui->macroPriorityWarning->setVisible( ui->macroPriorityWarning->setVisible(
switcher->functionNamesByPriority[0] != macro_func); switcher->functionNamesByPriority[0] != macro_func);
lastOnChangeHighlightCheckTime =
std::chrono::high_resolution_clock::now();
onChangeHighlightTimer.setInterval(1500); onChangeHighlightTimer.setInterval(1500);
connect(&onChangeHighlightTimer, SIGNAL(timeout()), this, connect(&onChangeHighlightTimer, SIGNAL(timeout()), this,
SLOT(HighlightOnChange())); SLOT(HighlightOnChange()));
@@ -687,24 +687,6 @@ void AdvSceneSwitcher::SetupMacroTab()
ui->macroSearchRegex, ui->macroSearchRegex,
ui->macroSearchShowSettings, ui->macroSearchShowSettings,
[this]() { ui->macros->RefreshFilter(); }); [this]() { ui->macros->RefreshFilter(); });
static const std::vector<
std::pair<Macro::ActionTriggerMode, const char *>>
actionTriggerModes = {
{Macro::ActionTriggerMode::ALWAYS,
"AdvSceneSwitcher.macroTab.actionTriggerMode.always"},
{Macro::ActionTriggerMode::MACRO_RESULT_CHANGED,
"AdvSceneSwitcher.macroTab.actionTriggerMode.onOverallChange"},
{Macro::ActionTriggerMode::ANY_CONDITION_CHANGED,
"AdvSceneSwitcher.macroTab.actionTriggerMode.onAnyConditionChange"},
{Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED,
"AdvSceneSwitcher.macroTab.actionTriggerMode.onAnyConditionTriggered"},
};
for (const auto &[mode, name] : actionTriggerModes) {
ui->actionTriggerMode->addItem(obs_module_text(name),
static_cast<int>(mode));
}
} }
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos) void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)

View File

@@ -5,17 +5,18 @@
#include "path-helpers.hpp" #include "path-helpers.hpp"
#include "sync-helpers.hpp" #include "sync-helpers.hpp"
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include "utility.hpp"
#include <obs.h> #include <obs.h>
#include <string> #include <string>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QSpacerItem> #include <QSpacerItem>
#include <QPushButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QMouseEvent> #include <QMouseEvent>
#include <QStylePainter> #include <QStylePainter>
#include <QToolTip>
Q_DECLARE_METATYPE(std::shared_ptr<advss::Macro>); Q_DECLARE_METATYPE(std::shared_ptr<advss::Macro>);
@@ -61,11 +62,12 @@ MacroTreeItem::MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macroItem,
_boxLayout = new QHBoxLayout(); _boxLayout = new QHBoxLayout();
_boxLayout->setContentsMargins(0, 0, 0, 0); _boxLayout->setContentsMargins(0, 0, 0, 0);
_boxLayout->addWidget(_running);
if (isGroup) { if (isGroup) {
_boxLayout->addWidget(_iconLabel); _boxLayout->addWidget(_iconLabel);
_boxLayout->addSpacing(2); _boxLayout->addSpacing(2);
_running->hide();
} }
_boxLayout->addWidget(_running);
_boxLayout->addWidget(_label); _boxLayout->addWidget(_label);
#ifdef __APPLE__ #ifdef __APPLE__
/* Hack: Fixes a bug where scrollbars would be above the lock icon */ /* Hack: Fixes a bug where scrollbars would be above the lock icon */
@@ -75,138 +77,29 @@ MacroTreeItem::MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macroItem,
Update(true); Update(true);
setLayout(_boxLayout); setLayout(_boxLayout);
connect(_running, SIGNAL(clicked(bool)), this, auto setRunning = [this](bool val) {
SLOT(RunningClicked(bool))); _macro->SetPaused(!val);
};
connect(_running, &QAbstractButton::clicked, setRunning);
connect(MacroSignalManager::Instance(), SIGNAL(HighlightChanged(bool)), connect(MacroSignalManager::Instance(), SIGNAL(HighlightChanged(bool)),
this, SLOT(EnableHighlight(bool))); this, SLOT(EnableHighlight(bool)));
connect(MacroSignalManager::Instance(), connect(MacroSignalManager::Instance(),
SIGNAL(Rename(const QString &, const QString &)), this, SIGNAL(Rename(const QString &, const QString &)), this,
SLOT(MacroRenamed(const QString &, const QString &))); SLOT(MacroRenamed(const QString &, const QString &)));
connect(&_timer, SIGNAL(timeout()), this, SLOT(HighlightIfExecuted())); connect(&_timer, SIGNAL(timeout()), this, SLOT(HighlightIfExecuted()));
connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateRunning())); connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdatePaused()));
UpdateRunning();
_timer.start(1500); _timer.start(1500);
} }
bool MacroTreeItem::event(QEvent *event)
{
if (event->type() != QEvent::ToolTip) {
return QFrame::event(event);
}
if (_macro->IsGroup()) {
return true;
}
QString text;
if (!_macro->WasExecutedSince({})) {
text = obs_module_text(
"AdvSceneSwitcher.macroTab.macroNotYetExecutedTooltip");
} else {
const QString formatStr = obs_module_text(
"AdvSceneSwitcher.macroTab.macroLastExecutedTooltip");
const auto secondsSinceLastRun =
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::high_resolution_clock::now() -
_macro->GetLastExecutionTime());
text = formatStr.arg(secondsSinceLastRun.count());
}
auto helpEvent = static_cast<QHelpEvent *>(event);
QToolTip::showText(helpEvent->globalPos(), text, this);
return true;
}
void MacroTreeItem::EnableHighlight(bool enable) void MacroTreeItem::EnableHighlight(bool enable)
{ {
_highlight = enable; _highlight = enable;
} }
void MacroTreeItem::RunningClicked(bool running) void MacroTreeItem::UpdatePaused()
{
const auto updateWidget = [this](Macro *macro) {
if (!macro) {
return;
}
const auto &macros = GetTopLevelMacros();
bool found = false;
int idx = 0;
for (const auto &m : macros) {
if (m.get() == macro) {
found = true;
break;
}
idx++;
}
if (!found) {
return;
}
const auto widget = _tree->GetItemWidget(idx);
if (widget) {
widget->UpdateRunning();
}
};
if (!_macro->IsGroup()) {
_macro->SetPaused(!running);
if (!_macro->IsSubitem()) {
return;
}
const auto group = _macro->Parent();
updateWidget(group.get());
return;
}
const auto macros = GetGroupMacroEntries(_macro.get());
for (const auto &macro : macros) {
macro->SetPaused(!running);
}
// Update backend values before updating UI to prevent flickering in
// running state of the group
for (const auto &macro : macros) {
updateWidget(macro.get());
}
UpdateRunning();
}
void MacroTreeItem::UpdateRunning()
{ {
const QSignalBlocker blocker(_running); const QSignalBlocker blocker(_running);
_running->setChecked(!_macro->Paused());
if (!_macro->IsGroup()) {
_running->setChecked(!_macro->Paused());
return;
}
const auto macros = GetGroupMacroEntries(_macro.get());
bool allRunning = true;
bool allPaused = true;
for (const auto &macro : macros) {
if (macro->Paused()) {
allRunning = false;
} else {
allPaused = false;
}
}
if (allRunning) {
_running->setCheckState(Qt::Checked);
return;
}
if (allPaused) {
_running->setCheckState(Qt::Unchecked);
return;
}
_running->setCheckState(Qt::PartiallyChecked);
} }
void MacroTreeItem::HighlightIfExecuted() void MacroTreeItem::HighlightIfExecuted()
@@ -215,20 +108,10 @@ void MacroTreeItem::HighlightIfExecuted()
return; return;
} }
bool wasHighlighted = false;
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 && if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
_macro->WasExecutedSince(_lastHighlightCheckTime)) { _macro->WasExecutedSince(_lastHighlightCheckTime)) {
HighlightWidget(this, Qt::green, QColor(0, 0, 0, 0), true); HighlightWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
wasHighlighted = true;
} }
if (!wasHighlighted &&
_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
_macro->ActionTriggerModePreventedActionsSince(
_lastHighlightCheckTime)) {
HighlightWidget(this, Qt::yellow, QColor(0, 0, 0, 0), true);
}
_lastHighlightCheckTime = std::chrono::high_resolution_clock::now(); _lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
} }
@@ -305,7 +188,7 @@ void MacroTreeItem::Update(bool force)
_expand->blockSignals(true); _expand->blockSignals(true);
_expand->setChecked(_macro->IsCollapsed()); _expand->setChecked(_macro->IsCollapsed());
_expand->blockSignals(false); _expand->blockSignals(false);
connect(_expand, &QCheckBox::toggled, this, connect(_expand, &QPushButton::toggled, this,
&MacroTreeItem::ExpandClicked); &MacroTreeItem::ExpandClicked);
} else { } else {
_spacer = new QSpacerItem(3, 1); _spacer = new QSpacerItem(3, 1);
@@ -331,6 +214,7 @@ void MacroTreeModel::Reset(std::deque<std::shared_ptr<Macro>> &newItems)
_macros = newItems; _macros = newItems;
endResetModel(); endResetModel();
UpdateGroupState(false);
_mt->ResetWidgets(); _mt->ResetWidgets();
} }
@@ -575,6 +459,9 @@ void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
_mt->selectionModel()->clear(); _mt->selectionModel()->clear();
if (isGroup) {
UpdateGroupState(true);
}
assert(IsInValidState()); assert(IsInValidState());
} }
@@ -663,6 +550,7 @@ MacroTreeModel::MacroTreeModel(MacroTree *st_,
_mt(st_), _mt(st_),
_macros(macros) _macros(macros)
{ {
UpdateGroupState(false);
} }
int MacroTreeModel::rowCount(const QModelIndex &parent) const int MacroTreeModel::rowCount(const QModelIndex &parent) const
@@ -816,6 +704,7 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
offset++; offset++;
} }
_hasGroups = true;
_mt->selectionModel()->clear(); _mt->selectionModel()->clear();
Reset(_macros); Reset(_macros);
@@ -873,6 +762,24 @@ void MacroTreeModel::CollapseGroup(std::shared_ptr<Macro> item)
assert(IsInValidState()); assert(IsInValidState());
} }
void MacroTreeModel::UpdateGroupState(bool update)
{
bool nowHasGroups = false;
for (auto &item : _macros) {
if (item->IsGroup()) {
nowHasGroups = true;
break;
}
}
if (nowHasGroups != _hasGroups) {
_hasGroups = nowHasGroups;
if (update) {
_mt->UpdateWidgets(true);
}
}
}
void MacroTree::Reset(std::deque<std::shared_ptr<Macro>> &macros, void MacroTree::Reset(std::deque<std::shared_ptr<Macro>> &macros,
bool highlight) bool highlight)
{ {
@@ -935,6 +842,7 @@ MacroTree::MacroTree(QWidget *parent_) : QListView(parent_)
void MacroTree::ResetWidgets() void MacroTree::ResetWidgets()
{ {
MacroTreeModel *mtm = GetModel(); MacroTreeModel *mtm = GetModel();
mtm->UpdateGroupState(false);
int modelIdx = 0; int modelIdx = 0;
for (int i = 0; i < (int)mtm->_macros.size(); i++) { for (int i = 0; i < (int)mtm->_macros.size(); i++) {
QModelIndex index = mtm->createIndex(modelIdx, 0, nullptr); QModelIndex index = mtm->createIndex(modelIdx, 0, nullptr);

View File

@@ -34,14 +34,10 @@ public:
explicit MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macro, explicit MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macro,
bool highlight); bool highlight);
protected:
bool event(QEvent *) override;
private slots: private slots:
void ExpandClicked(bool checked); void ExpandClicked(bool checked);
void EnableHighlight(bool enable); void EnableHighlight(bool enable);
void RunningClicked(bool); void UpdatePaused();
void UpdateRunning();
void HighlightIfExecuted(); void HighlightIfExecuted();
void MacroRenamed(const QString &, const QString &); void MacroRenamed(const QString &, const QString &);
@@ -110,6 +106,7 @@ private:
void UngroupSelectedGroups(QModelIndexList &indices); void UngroupSelectedGroups(QModelIndexList &indices);
void ExpandGroup(std::shared_ptr<Macro> item); void ExpandGroup(std::shared_ptr<Macro> item);
void CollapseGroup(std::shared_ptr<Macro> item); void CollapseGroup(std::shared_ptr<Macro> item);
void UpdateGroupState(bool update);
int GetItemMacroIndex(const std::shared_ptr<Macro> &item) const; int GetItemMacroIndex(const std::shared_ptr<Macro> &item) const;
int GetItemModelIndex(const std::shared_ptr<Macro> &item) const; int GetItemModelIndex(const std::shared_ptr<Macro> &item) const;
bool IsLastItem(std::shared_ptr<Macro> item) const; bool IsLastItem(std::shared_ptr<Macro> item) const;
@@ -117,6 +114,7 @@ private:
MacroTree *_mt; MacroTree *_mt;
std::deque<std::shared_ptr<Macro>> &_macros; std::deque<std::shared_ptr<Macro>> &_macros;
bool _hasGroups = false;
friend class MacroTree; friend class MacroTree;
friend class MacroTreeItem; friend class MacroTreeItem;

View File

@@ -1,111 +0,0 @@
#include "websocket-api.hpp"
#include "log-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "variable.hpp"
#include "macro-helpers.hpp"
#include <obs.hpp>
namespace advss {
static bool setup();
static bool setupDone = setup();
static void receiveRunMacroMessage(obs_data_t *data, obs_data_t *);
static void receiveSetVariablesMessage(obs_data_t *data, obs_data_t *);
static const char *runMacroMessage = "AdvancedSceneSwitcherRunMacro";
static const char *setVariablesMessage = "AdvancedSceneSwitcherSetVariables";
static bool setup()
{
AddPluginInitStep([]() {
RegisterWebsocketRequest(runMacroMessage,
receiveRunMacroMessage);
RegisterWebsocketRequest(setVariablesMessage,
receiveSetVariablesMessage);
});
return true;
}
static void setVariables(obs_data_t *data)
{
OBSDataArrayAutoRelease variables =
obs_data_get_array(data, "variables");
size_t count = obs_data_array_count(variables);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease item = obs_data_array_item(variables, i);
if (!obs_data_has_user_value(item, "name")) {
blog(LOG_INFO,
"ignoring invalid variable entry in \"%s\" websocket message (missing \"name\")",
runMacroMessage);
continue;
}
if (!obs_data_has_user_value(item, "value")) {
blog(LOG_INFO,
"ignoring invalid variable entry in \"%s\" websocket message (missing \"value\")",
runMacroMessage);
continue;
}
auto varName = obs_data_get_string(item, "name");
auto value = obs_data_get_string(item, "value");
auto weakVar = GetWeakVariableByName(varName);
auto var = weakVar.lock();
if (!var) {
blog(LOG_INFO,
"ignoring invalid variable name \"%s\" in \"%s\" websocket message",
varName, runMacroMessage);
continue;
}
var->SetValue(value);
}
}
static void receiveRunMacroMessage(obs_data_t *data, obs_data_t *)
{
if (!obs_data_has_user_value(data, "name")) {
blog(LOG_INFO,
"ignoring invalid \"%s\" websocket message (missing \"name\")",
runMacroMessage);
return;
}
auto name = obs_data_get_string(data, "name");
const auto weakMacro =
GetWeakMacroByName(obs_data_get_string(data, "name"));
const auto macro = weakMacro.lock();
if (!macro) {
blog(LOG_INFO,
"ignoring invalid \"%s\" websocket message (macro \"%s\") not found",
runMacroMessage, name);
return;
}
if (obs_data_has_user_value(data, "variables")) {
setVariables(data);
}
const bool runElse = obs_data_get_bool(data, "runElseActions");
if (runElse) {
RunMacroElseActions(macro.get());
} else {
RunMacroActions(macro.get());
}
}
static void receiveSetVariablesMessage(obs_data_t *data, obs_data_t *)
{
if (!obs_data_has_user_value(data, "variables")) {
blog(LOG_INFO,
"ignoring invalid \"%s\" websocket message (missing \"variables\")",
setVariablesMessage);
return;
}
setVariables(data);
}
} // namespace advss

View File

@@ -111,7 +111,7 @@ static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
const auto startTime = std::chrono::high_resolution_clock::now(); const auto startTime = std::chrono::high_resolution_clock::now();
bool conditionMatched = false; bool conditionMatched = false;
condition->WithLock([&condition, &conditionMatched]() { condition->WithLock([&condition, &conditionMatched]() {
conditionMatched = condition->EvaluateCondition(); conditionMatched = condition->CheckCondition();
}); });
const auto endTime = std::chrono::high_resolution_clock::now(); const auto endTime = std::chrono::high_resolution_clock::now();
const auto timeSpent = endTime - startTime; const auto timeSpent = endTime - startTime;
@@ -241,37 +241,9 @@ bool Macro::CheckConditions(bool ignorePause)
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched); vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
_actionModeMatch = false; _conditionSateChanged = _lastMatched != _matched;
switch (_actionTriggerMode) { if (!_conditionSateChanged && _performActionsOnChange) {
case Macro::ActionTriggerMode::ALWAYS: _onPreventedActionExecution = true;
_actionModeMatch = true;
break;
case Macro::ActionTriggerMode::MACRO_RESULT_CHANGED:
_actionModeMatch = _lastMatched != _matched;
break;
case Macro::ActionTriggerMode::ANY_CONDITION_CHANGED:
for (const auto &condition : _conditions) {
if (condition->HasChanged()) {
_actionModeMatch = true;
}
}
break;
case Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED:
for (const auto &condition : _conditions) {
if (condition->IsRisingEdge()) {
_actionModeMatch = true;
}
}
break;
default:
break;
}
const bool hasActionsToExecute = _matched ? (_actions.size() > 0)
: (_elseActions.size() > 0);
if (!_actionModeMatch && hasActionsToExecute) {
_lastActionRunModePreventTime =
std::chrono::high_resolution_clock::now();
} }
_lastMatched = _matched; _lastMatched = _matched;
@@ -330,16 +302,6 @@ bool Macro::WasExecutedSince(const TimePoint &time) const
return _lastExecutionTime > time; return _lastExecutionTime > time;
} }
bool Macro::ActionTriggerModePreventedActionsSince(const TimePoint &time) const
{
return _lastActionRunModePreventTime > time;
}
Macro::TimePoint Macro::GetLastExecutionTime() const
{
return _lastExecutionTime;
}
bool Macro::ConditionsShouldBeChecked() const bool Macro::ConditionsShouldBeChecked() const
{ {
if (!_useCustomConditionCheckInterval) { if (!_useCustomConditionCheckInterval) {
@@ -366,9 +328,10 @@ bool Macro::ShouldRunActions() const
const bool hasActionsToExecute = const bool hasActionsToExecute =
!_paused && (_matched || _elseActions.size() > 0) && !_paused && (_matched || _elseActions.size() > 0) &&
_actionModeMatch; (!_performActionsOnChange || _conditionSateChanged);
if (VerboseLoggingEnabled() && !_actionModeMatch) { if (VerboseLoggingEnabled() && _performActionsOnChange &&
!_conditionSateChanged) {
if (_matched && _actions.size() > 0) { if (_matched && _actions.size() > 0) {
blog(LOG_INFO, "skip actions for Macro %s (on change)", blog(LOG_INFO, "skip actions for Macro %s (on change)",
_name.c_str()); _name.c_str());
@@ -399,24 +362,10 @@ void Macro::ResetTimers()
_lastExecutionTime = {}; _lastExecutionTime = {};
} }
void Macro::SetActionTriggerMode(ActionTriggerMode mode)
{
_actionTriggerMode = mode;
}
Macro::ActionTriggerMode Macro::GetActionTriggerMode() const
{
return _actionTriggerMode;
}
bool Macro::RunActionsHelper( bool Macro::RunActionsHelper(
const std::deque<std::shared_ptr<MacroAction>> &actionsToRun, const std::deque<std::shared_ptr<MacroAction>> &actionsToRun,
bool ignorePause) bool ignorePause)
{ {
if (_paused && !ignorePause) {
return true;
}
// Create copy of action list as elements might be removed, inserted, or // Create copy of action list as elements might be removed, inserted, or
// reordered while actions are currently being executed. // reordered while actions are currently being executed.
auto actions = actionsToRun; auto actions = actionsToRun;
@@ -466,6 +415,11 @@ bool Macro::WasPausedSince(const TimePoint &time) const
return _lastUnpauseTime > time; return _lastUnpauseTime > time;
} }
void Macro::SetMatchOnChange(bool onChange)
{
_performActionsOnChange = onChange;
}
void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone) void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone)
{ {
_stopActionsIfNotDone = stopActionsIfNotDone; _stopActionsIfNotDone = stopActionsIfNotDone;
@@ -786,8 +740,7 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
obs_data_set_bool(obj, "pause", _paused); obs_data_set_bool(obj, "pause", _paused);
obs_data_set_bool(obj, "parallel", _runInParallel); obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "checkConditionsInParallel", _checkInParallel); obs_data_set_bool(obj, "checkConditionsInParallel", _checkInParallel);
obs_data_set_int(obj, "actionTriggerMode", obs_data_set_bool(obj, "onChange", _performActionsOnChange);
static_cast<int>(_actionTriggerMode));
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart); obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone); obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
obs_data_set_bool(obj, "useShortCircuitEvaluation", obs_data_set_bool(obj, "useShortCircuitEvaluation",
@@ -873,15 +826,7 @@ bool Macro::Load(obs_data_t *obj)
} }
_runInParallel = obs_data_get_bool(obj, "parallel"); _runInParallel = obs_data_get_bool(obj, "parallel");
_checkInParallel = obs_data_get_bool(obj, "checkConditionsInParallel"); _checkInParallel = obs_data_get_bool(obj, "checkConditionsInParallel");
if (obs_data_has_user_value(obj, "onChange")) { _performActionsOnChange = obs_data_get_bool(obj, "onChange");
const bool onChange = obs_data_get_bool(obj, "onChange");
_actionTriggerMode =
onChange ? ActionTriggerMode::MACRO_RESULT_CHANGED
: ActionTriggerMode::ALWAYS;
} else {
_actionTriggerMode = static_cast<ActionTriggerMode>(
obs_data_get_int(obj, "actionTriggerMode"));
}
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart"); _skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone"); _stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
_useShortCircuitEvaluation = _useShortCircuitEvaluation =
@@ -1003,12 +948,12 @@ bool Macro::PostLoad()
bool Macro::SwitchesScene() const bool Macro::SwitchesScene() const
{ {
for (const auto &a : _actions) { for (const auto &a : _actions) {
if (a->GetId() == MacroAction::GetDefaultID()) { if (a->GetId() == GetSceneSwitchActionId()) {
return true; return true;
} }
} }
for (const auto &a : _elseActions) { for (const auto &a : _elseActions) {
if (a->GetId() == MacroAction::GetDefaultID()) { if (a->GetId() == GetSceneSwitchActionId()) {
return true; return true;
} }
} }
@@ -1041,8 +986,18 @@ bool Macro::HasValidSplitterPositions() const
!_elseActionSplitterPosition.empty(); !_elseActionSplitterPosition.empty();
} }
bool Macro::OnChangePreventedActionsRecently()
{
if (_onPreventedActionExecution) {
_onPreventedActionExecution = false;
return _matched ? _actions.size() > 0 : _elseActions.size() > 0;
}
return false;
}
void Macro::ResetUIHelpers() void Macro::ResetUIHelpers()
{ {
_onPreventedActionExecution = false;
for (auto c : _conditions) { for (auto c : _conditions) {
c->GetHighlightAndReset(); c->GetHighlightAndReset();
} }
@@ -1141,11 +1096,9 @@ void Macro::ClearHotkeys() const
void setHotkeyDescriptionHelper(const char *formatModuleText, void setHotkeyDescriptionHelper(const char *formatModuleText,
const std::string name, const obs_hotkey_id id) const std::string name, const obs_hotkey_id id)
{ {
#ifndef UNIT_TEST
QString format{obs_module_text(formatModuleText)}; QString format{obs_module_text(formatModuleText)};
QString hotkeyDesc = format.arg(QString::fromStdString(name)); QString hotkeyDesc = format.arg(QString::fromStdString(name));
obs_hotkey_set_description(id, hotkeyDesc.toStdString().c_str()); obs_hotkey_set_description(id, hotkeyDesc.toStdString().c_str());
#endif // !UNIT_TEST
} }
void Macro::SetHotkeysDesc() const void Macro::SetHotkeysDesc() const

View File

@@ -26,16 +26,6 @@ class Macro {
public: public:
enum class PauseStateSaveBehavior { PERSIST, PAUSE, UNPAUSE }; enum class PauseStateSaveBehavior { PERSIST, PAUSE, UNPAUSE };
enum class ActionTriggerMode {
// Trigger always
ALWAYS,
// Trigger when macro match result flips
MACRO_RESULT_CHANGED,
// Trigger when any individual condition changes
ANY_CONDITION_CHANGED,
// Trigger when any individual condition evaluates to true
ANY_CONDITION_TRIGGERED,
};
Macro(const std::string &name = ""); Macro(const std::string &name = "");
Macro(const std::string &name, const GlobalMacroSettings &settings); Macro(const std::string &name, const GlobalMacroSettings &settings);
@@ -64,8 +54,8 @@ public:
bool GetStop() const { return _stop; } bool GetStop() const { return _stop; }
void ResetTimers(); void ResetTimers();
void SetActionTriggerMode(ActionTriggerMode); void SetMatchOnChange(bool onChange);
ActionTriggerMode GetActionTriggerMode() const; bool MatchOnChange() const { return _performActionsOnChange; }
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; } void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
bool SkipExecOnStart() const { return _skipExecOnStart; } bool SkipExecOnStart() const { return _skipExecOnStart; }
@@ -147,8 +137,7 @@ public:
const QList<int> &GetElseActionSplitterPosition() const; const QList<int> &GetElseActionSplitterPosition() const;
bool HasValidSplitterPositions() const; bool HasValidSplitterPositions() const;
bool WasExecutedSince(const TimePoint &) const; bool WasExecutedSince(const TimePoint &) const;
bool ActionTriggerModePreventedActionsSince(const TimePoint &) const; bool OnChangePreventedActionsRecently();
TimePoint GetLastExecutionTime() const;
void ResetUIHelpers(); void ResetUIHelpers();
// Hotkeys // Hotkeys
@@ -179,7 +168,6 @@ private:
TimePoint _lastCheckTime{}; TimePoint _lastCheckTime{};
TimePoint _lastUnpauseTime{}; TimePoint _lastUnpauseTime{};
TimePoint _lastExecutionTime{}; TimePoint _lastExecutionTime{};
TimePoint _lastActionRunModePreventTime{};
std::vector<std::thread> _helperThreads; std::vector<std::thread> _helperThreads;
std::deque<std::shared_ptr<MacroCondition>> _conditions; std::deque<std::shared_ptr<MacroCondition>> _conditions;
@@ -194,13 +182,14 @@ private:
bool _useShortCircuitEvaluation = false; bool _useShortCircuitEvaluation = false;
bool _useCustomConditionCheckInterval = false; bool _useCustomConditionCheckInterval = false;
Duration _customConditionCheckInterval = 0.3; Duration _customConditionCheckInterval = 0.3;
bool _actionModeMatch = false; bool _conditionSateChanged = false;
bool _runInParallel = false; bool _runInParallel = false;
bool _checkInParallel = false; bool _checkInParallel = false;
bool _matched = false; bool _matched = false;
std::future<void> _conditionCheckFuture; std::future<void> _conditionCheckFuture;
bool _lastMatched = false; bool _lastMatched = false;
bool _performActionsOnChange = true;
bool _skipExecOnStart = false; bool _skipExecOnStart = false;
bool _stopActionsIfNotDone = false; bool _stopActionsIfNotDone = false;
bool _paused = false; bool _paused = false;
@@ -210,14 +199,14 @@ private:
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
ActionTriggerMode _actionTriggerMode =
ActionTriggerMode::MACRO_RESULT_CHANGED;
PauseStateSaveBehavior _pauseSaveBehavior = PauseStateSaveBehavior _pauseSaveBehavior =
PauseStateSaveBehavior::PERSIST; PauseStateSaveBehavior::PERSIST;
MacroInputVariables _inputVariables; MacroInputVariables _inputVariables;
// UI helpers
bool _onPreventedActionExecution = false;
QList<int> _actionConditionSplitterPosition; QList<int> _actionConditionSplitterPosition;
QList<int> _elseActionSplitterPosition; QList<int> _elseActionSplitterPosition;

View File

@@ -152,7 +152,7 @@ bool SwitcherData::VersionChanged(obs_data_t *obj, std::string currentVersion)
if (!obs_data_has_user_value(obj, "version")) { if (!obs_data_has_user_value(obj, "version")) {
return false; return false;
} }
switcher->firstBoot = false;
std::string previousVersion = obs_data_get_string(obj, "version"); std::string previousVersion = obs_data_get_string(obj, "version");
return previousVersion != currentVersion; return previousVersion != currentVersion;
} }

View File

@@ -94,6 +94,7 @@ public:
bool stop = false; bool stop = false;
std::condition_variable cv; std::condition_variable cv;
bool firstBoot = true;
bool transitionActive = false; bool transitionActive = false;
bool sceneCollectionStop = false; bool sceneCollectionStop = false;
bool obsIsShuttingDown = false; bool obsIsShuttingDown = false;

View File

@@ -1,7 +1,5 @@
#include "auto-update-tooltip-label.hpp" #include "auto-update-tooltip-label.hpp"
#include <QToolTip>
namespace advss { namespace advss {
AutoUpdateHelpIcon::AutoUpdateHelpIcon( AutoUpdateHelpIcon::AutoUpdateHelpIcon(
@@ -18,7 +16,6 @@ AutoUpdateHelpIcon::AutoUpdateHelpIcon(
void AutoUpdateHelpIcon::enterEvent(QEnterEvent *event) void AutoUpdateHelpIcon::enterEvent(QEnterEvent *event)
{ {
UpdateTooltip();
_timer->start(_updateIntervalMs); _timer->start(_updateIntervalMs);
QLabel::enterEvent(event); QLabel::enterEvent(event);
} }
@@ -31,12 +28,7 @@ void AutoUpdateHelpIcon::leaveEvent(QEvent *event)
void AutoUpdateHelpIcon::UpdateTooltip() void AutoUpdateHelpIcon::UpdateTooltip()
{ {
if (!underMouse()) { setToolTip(_callback());
return;
}
const QString text = _callback();
QToolTip::showText(QCursor::pos(), text, this);
} }
} // namespace advss } // namespace advss

View File

@@ -8,18 +8,13 @@
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <obs-module.h> #include <obs-module.h>
#include <util/config-file.h>
#include <QFileDialog> #include <QFileDialog>
#include <QMainWindow>
#include <QTextStream> #include <QTextStream>
#include <QTimer> #include <util/config-file.h>
#include <thread>
namespace advss { namespace advss {
static void showBackupDialogs(const QString &json) void AskForBackup(const QString &json)
{ {
const bool backupWasConfirmed = DisplayMessage( const bool backupWasConfirmed = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true, false); obs_module_text("AdvSceneSwitcher.askBackup"), true, false);
@@ -47,27 +42,6 @@ static void showBackupDialogs(const QString &json)
out << json; out << json;
} }
void AskForBackup(obs_data_t *settings)
{
// This function is called while the plugin settings are being loaded.
// Blocking at this stage can cause issues such as OBS failing to start
// or crashing.
// Therefore, we ask the user whether they want to back up the settings
// asynchronously.
auto json = obs_data_get_json(settings);
static QString jsonQString = json ? json : "";
static const auto askForBackupWrapper = [](void *) {
showBackupDialogs(jsonQString);
};
AddFinishedLoadingStep([]() {
obs_queue_task(OBS_TASK_UI, askForBackupWrapper, nullptr,
false);
});
}
void BackupSettingsOfCurrentVersion() void BackupSettingsOfCurrentVersion()
{ {
auto sceneCollectionName = obs_frontend_get_current_scene_collection(); auto sceneCollectionName = obs_frontend_get_current_scene_collection();

View File

@@ -1,9 +1,9 @@
#pragma once #pragma once
#include <obs-data.h> #include <QString>
namespace advss { namespace advss {
void AskForBackup(obs_data_t *settings); void AskForBackup(const QString &json);
void BackupSettingsOfCurrentVersion(); void BackupSettingsOfCurrentVersion();
} // namespace advss } // namespace advss

View File

@@ -1,140 +0,0 @@
#include "crash-handler.hpp"
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp"
#include <obs-frontend-api.h>
#include <obs-module.h>
#include <QDir>
#include <QFile>
#include <QMainWindow>
#include <QTimer>
#include <thread>
namespace advss {
static constexpr std::string_view sentinel = ".running";
#ifndef NDEBUG
static constexpr bool handleUncleanShutdown = false;
#else
static constexpr bool handleUncleanShutdown = true;
#endif
static bool wasCleanShutdown = false;
static void setup();
static bool setupDone = []() {
AddPluginInitStep(setup);
return true;
}();
static void handleShutdown(enum obs_frontend_event event, void *)
{
if (event != OBS_FRONTEND_EVENT_EXIT) {
return;
}
char *sentinelFile = obs_module_config_path(sentinel.data());
if (!sentinelFile) {
return;
}
QFile file(sentinelFile);
if (!file.exists()) {
bfree(sentinelFile);
return;
}
if (!file.remove()) {
blog(LOG_WARNING, "failed to remove sentinel file");
}
bfree(sentinelFile);
}
static void setup()
{
char *sentinelFile = obs_module_config_path(sentinel.data());
if (!sentinelFile) {
return;
}
QString dirPath = QFileInfo(sentinelFile).absolutePath();
QDir dir(dirPath);
if (!dir.exists()) {
if (!dir.mkpath(dirPath)) {
blog(LOG_WARNING,
"failed to create directory for sentinel file");
bfree(sentinelFile);
return;
}
}
QFile file(sentinelFile);
wasCleanShutdown = file.exists();
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
blog(LOG_WARNING, "failed to create sentinel file");
bfree(sentinelFile);
return;
}
file.write("running");
file.close();
bfree(sentinelFile);
obs_frontend_add_event_callback(handleShutdown, nullptr);
return;
}
static bool wasUncleanShutdown()
{
static bool alreadyHandled = false;
if (!handleUncleanShutdown || !wasCleanShutdown || alreadyHandled) {
alreadyHandled = true;
return false;
}
alreadyHandled = true;
blog(LOG_WARNING, "unclean shutdown detected");
return true;
}
static void askForStartupSkip()
{
bool skipStart = DisplayMessage(
obs_module_text("AdvSceneSwitcher.crashDetected"), true, false);
if (!skipStart) {
StartPlugin();
}
}
bool ShouldSkipPluginStartOnUncleanShutdown()
{
if (!wasUncleanShutdown()) {
return false;
}
// This function is called while the plugin settings are being loaded.
// Blocking at this stage can cause issues such as OBS failing to start
// or crashing.
// Therefore, we ask the user whether they want to start the plugin
// asynchronously.
static const auto showDialogWrapper = [](void *) {
askForStartupSkip();
};
AddFinishedLoadingStep([]() {
obs_queue_task(OBS_TASK_UI, showDialogWrapper, nullptr, false);
});
return true;
}
} // namespace advss

View File

@@ -1,7 +0,0 @@
#pragma once
namespace advss {
bool ShouldSkipPluginStartOnUncleanShutdown();
} // namespace advss

View File

@@ -1,5 +1,12 @@
#pragma once #pragma once
#ifdef UNIT_TEST
#define EXPORT
#define ADVSS_EXPORT
#else
#ifdef _MSC_VER #ifdef _MSC_VER
#define EXPORT __declspec(dllexport) #define EXPORT __declspec(dllexport)
#else #else
@@ -12,3 +19,5 @@
#else #else
#define ADVSS_EXPORT Q_DECL_IMPORT #define ADVSS_EXPORT Q_DECL_IMPORT
#endif #endif
#endif // UNIT_TEST

View File

@@ -1,465 +0,0 @@
#include "first-run-wizard.hpp"
#include "log-helper.hpp"
#include "macro.hpp"
#include "macro-action-factory.hpp"
#include "macro-condition-factory.hpp"
#include "macro-settings.hpp"
#include "platform-funcs.hpp"
#include "selection-helpers.hpp"
#include <obs-frontend-api.h>
#include <obs-data.h>
#include <util/config-file.h>
#include <QFrame>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QRegularExpression>
#include <QVBoxLayout>
namespace advss {
static constexpr char kConditionIdWindow[] = "window";
static constexpr char kActionIdSceneSwitch[] = "scene_switch";
// ---------------------------------------------------------------------------
// OBS global config helpers
// ---------------------------------------------------------------------------
static constexpr char kConfigSection[] = "AdvancedSceneSwitcher";
static constexpr char kFirstRunKey[] = "firstRun";
bool IsFirstRun()
{
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(31, 0, 0)
config_t *cfg = obs_frontend_get_user_config();
#else
config_t *cfg = obs_frontend_get_global_config();
#endif
if (!config_has_user_value(cfg, kConfigSection, kFirstRunKey)) {
return true;
}
return config_get_bool(cfg, kConfigSection, kFirstRunKey);
}
static void WriteFirstRun(bool value)
{
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(31, 0, 0)
config_t *cfg = obs_frontend_get_user_config();
#else
config_t *cfg = obs_frontend_get_global_config();
#endif
config_set_bool(cfg, kConfigSection, kFirstRunKey, value);
config_save_safe(cfg, "tmp", nullptr);
}
static QString DetectFocusedWindow()
{
std::string title;
GetCurrentWindowTitle(title);
return QString::fromStdString(title);
}
// ===========================================================================
// WelcomePage
// ===========================================================================
WelcomePage::WelcomePage(QWidget *parent) : QWizardPage(parent)
{
setTitle(obs_module_text("FirstRunWizard.welcome.title"));
setSubTitle(obs_module_text("FirstRunWizard.welcome.subtitle"));
auto body = new QLabel(obs_module_text("FirstRunWizard.welcome.body"),
this);
body->setWordWrap(true);
body->setTextFormat(Qt::RichText);
auto layout = new QVBoxLayout(this);
layout->addWidget(body);
layout->addStretch();
}
// ===========================================================================
// SceneSelectionPage
// ===========================================================================
SceneSelectionPage::SceneSelectionPage(QWidget *parent) : QWizardPage(parent)
{
setTitle(obs_module_text("FirstRunWizard.scene.title"));
setSubTitle(obs_module_text("FirstRunWizard.scene.subtitle"));
auto label =
new QLabel(obs_module_text("FirstRunWizard.scene.label"), this);
_sceneCombo = new QComboBox(this);
_sceneCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// registerField with * suffix means the field is mandatory for Next
registerField("targetScene*", _sceneCombo, "currentText",
SIGNAL(currentTextChanged(QString)));
connect(_sceneCombo, &QComboBox::currentTextChanged, this,
&QWizardPage::completeChanged);
auto row = new QHBoxLayout;
row->addWidget(label);
row->addWidget(_sceneCombo, 1);
auto layout = new QVBoxLayout(this);
layout->addLayout(row);
layout->addStretch();
}
void SceneSelectionPage::initializePage()
{
_sceneCombo->clear();
for (const QString &name : GetSceneNames())
_sceneCombo->addItem(name);
}
bool SceneSelectionPage::isComplete() const
{
return _sceneCombo->count() > 0 &&
!_sceneCombo->currentText().isEmpty();
}
// ===========================================================================
// WindowConditionPage
// ===========================================================================
WindowConditionPage::WindowConditionPage(QWidget *parent)
: QWizardPage(parent),
_detectTimer(new QTimer(this))
{
setTitle(obs_module_text("FirstRunWizard.window.title"));
setSubTitle(obs_module_text("FirstRunWizard.window.subtitle"));
auto label = new QLabel(obs_module_text("FirstRunWizard.window.label"),
this);
_windowEdit = new QLineEdit(this);
_windowEdit->setPlaceholderText(
obs_module_text("FirstRunWizard.window.placeholder"));
_autoDetect = new QPushButton(
obs_module_text("FirstRunWizard.window.autoDetect"), this);
_autoDetect->setToolTip(
obs_module_text("FirstRunWizard.window.autoDetectTooltip"));
registerField("windowTitle*", _windowEdit);
connect(_windowEdit, &QLineEdit::textChanged, this,
&QWizardPage::completeChanged);
connect(_autoDetect, &QPushButton::clicked, this,
&WindowConditionPage::onAutoDetectClicked);
connect(_detectTimer, &QTimer::timeout, this,
&WindowConditionPage::onCountdownTick);
auto row = new QHBoxLayout;
row->addWidget(label);
row->addWidget(_windowEdit, 1);
auto hint =
new QLabel(obs_module_text("FirstRunWizard.window.hint"), this);
hint->setTextFormat(Qt::RichText);
hint->setWordWrap(true);
auto layout = new QVBoxLayout(this);
layout->addLayout(row);
layout->addWidget(_autoDetect, 0, Qt::AlignLeft);
layout->addWidget(hint);
layout->addStretch();
}
void WindowConditionPage::initializePage()
{
if (_windowEdit->text().isEmpty()) {
QString detected = DetectFocusedWindow();
if (!detected.isEmpty()) {
_windowEdit->setText(detected);
}
}
}
bool WindowConditionPage::isComplete() const
{
return !_windowEdit->text().trimmed().isEmpty();
}
void WindowConditionPage::onAutoDetectClicked()
{
_countdown = 3;
_autoDetect->setEnabled(false);
_autoDetect->setText(
QString(obs_module_text(
"FirstRunWizard.window.autoDetectCountdown"))
.arg(_countdown));
_detectTimer->start(1000);
}
void WindowConditionPage::onCountdownTick()
{
--_countdown;
if (_countdown > 0) {
_autoDetect->setText(
QString(obs_module_text(
"FirstRunWizard.window.autoDetectCountdown"))
.arg(_countdown));
return;
}
_detectTimer->stop();
QString title = DetectFocusedWindow();
if (!title.isEmpty()) {
_windowEdit->setText(title);
}
_autoDetect->setEnabled(true);
_autoDetect->setText(
obs_module_text("FirstRunWizard.window.autoDetect"));
}
// ===========================================================================
// ReviewPage
// ===========================================================================
ReviewPage::ReviewPage(QWidget *parent, std::shared_ptr<Macro> &macro)
: QWizardPage(parent),
_macro(macro)
{
setTitle(obs_module_text("FirstRunWizard.review.title"));
setSubTitle(obs_module_text("FirstRunWizard.review.subtitle"));
_summary = new QLabel(this);
_summary->setWordWrap(true);
_summary->setTextFormat(Qt::RichText);
_summary->setFrameShape(QFrame::StyledPanel);
_summary->setContentsMargins(12, 12, 12, 12);
auto layout = new QVBoxLayout(this);
layout->addWidget(_summary);
layout->addStretch();
}
void ReviewPage::initializePage()
{
const QString scene = field("targetScene").toString();
const QString window = field("windowTitle").toString();
_summary->setText(
QString(obs_module_text("FirstRunWizard.review.summary"))
.arg(scene.toHtmlEscaped(), window.toHtmlEscaped()));
}
static QString escapeForRegex(const QString &input)
{
return QRegularExpression::escape(input);
}
bool ReviewPage::validatePage()
{
const QString scene = field("targetScene").toString();
const QString window = escapeForRegex(field("windowTitle").toString());
const std::string name = ("Window -> " + scene).toStdString();
// Build condition data blob
// ---------------------------------------------------------------
// Condition blob — mirrors MacroConditionWindow::Save() output:
//
// {
// "segmentSettings": { "enabled": true, "version": 1 },
// "id": "window",
// "checkTitle": true,
// "window": "<user input>",
// "windowRegexConfig": {
// "enable": true, // use regex-style partial matching
// "partial": true, // match anywhere in the title
// "options": 3 // case-insensitive (QRegularExpression flags)
// },
// "focus": true, // only trigger when window is focused
// "version": 1
// }
// ---------------------------------------------------------------
OBSDataAutoRelease condSegment = obs_data_create();
obs_data_set_bool(condSegment, "enabled", true);
obs_data_set_int(condSegment, "version", 1);
OBSDataAutoRelease condRegex = obs_data_create();
obs_data_set_bool(condRegex, "enable", true);
obs_data_set_bool(condRegex, "partial", true);
obs_data_set_int(condRegex, "options", 3); // CaseInsensitiveOption
OBSDataAutoRelease condData = obs_data_create();
obs_data_set_obj(condData, "segmentSettings", condSegment);
obs_data_set_string(condData, "id", "window");
obs_data_set_bool(condData, "checkTitle", true);
obs_data_set_string(condData, "window", window.toUtf8().constData());
obs_data_set_obj(condData, "windowRegexConfig", condRegex);
obs_data_set_bool(condData, "focus", true);
obs_data_set_int(condData, "version", 1);
// Build action data blob
// ---------------------------------------------------------------
// Action blob — mirrors MacroActionSwitchScene::Save() output:
//
// {
// "segmentSettings": { "enabled": true, "version": 1 },
// "id": "scene_switch",
// "action": 0, // 0 = switch scene
// "sceneSelection": {
// "type": 0, // 0 = scene by name
// "name": "<scene>",
// "canvasSelection": "Main"
// },
// "transitionType": 1, // 1 = use scene's default transition
// "blockUntilTransitionDone": false,
// "sceneType": 0
// }
// ---------------------------------------------------------------
OBSDataAutoRelease actionSegment = obs_data_create();
obs_data_set_bool(actionSegment, "enabled", true);
obs_data_set_int(actionSegment, "version", 1);
OBSDataAutoRelease sceneSelection = obs_data_create();
obs_data_set_int(sceneSelection, "type", 0);
obs_data_set_string(sceneSelection, "name", scene.toUtf8().constData());
obs_data_set_string(sceneSelection, "canvasSelection", "Main");
OBSDataAutoRelease actionData = obs_data_create();
obs_data_set_obj(actionData, "segmentSettings", actionSegment);
obs_data_set_string(actionData, "id", "scene_switch");
obs_data_set_int(actionData, "action", 0);
obs_data_set_obj(actionData, "sceneSelection", sceneSelection);
obs_data_set_int(actionData, "transitionType", 1);
obs_data_set_bool(actionData, "blockUntilTransitionDone", false);
obs_data_set_int(actionData, "sceneType", 0);
if (!FirstRunWizard::CreateMacro(_macro, name, kConditionIdWindow,
condData, kActionIdSceneSwitch,
actionData)) {
QMessageBox::warning(
this,
obs_module_text("FirstRunWizard.review.errorTitle"),
QString(obs_module_text(
"FirstRunWizard.review.errorBody"))
.arg(window, scene));
_macro.reset();
// Still advance so the user is not stuck.
}
return true;
}
// ===========================================================================
// DonePage
// ===========================================================================
DonePage::DonePage(QWidget *parent) : QWizardPage(parent)
{
setTitle(obs_module_text("FirstRunWizard.done.title"));
setSubTitle(obs_module_text("FirstRunWizard.done.subtitle"));
auto body =
new QLabel(obs_module_text("FirstRunWizard.done.body"), this);
body->setWordWrap(true);
body->setTextFormat(Qt::RichText);
body->setOpenExternalLinks(true);
auto layout = new QVBoxLayout(this);
layout->addWidget(body);
layout->addStretch();
}
// ===========================================================================
// FirstRunWizard
// ===========================================================================
FirstRunWizard::FirstRunWizard(QWidget *parent) : QWizard(parent)
{
setWindowTitle(obs_module_text("FirstRunWizard.windowTitle"));
setWizardStyle(QWizard::ModernStyle);
setMinimumSize(540, 420);
setPage(PAGE_WELCOME, new WelcomePage(this));
setPage(PAGE_SCENE, new SceneSelectionPage(this));
setPage(PAGE_WINDOW, new WindowConditionPage(this));
setPage(PAGE_REVIEW, new ReviewPage(this, _macro));
setPage(PAGE_DONE, new DonePage(this));
setStartId(PAGE_WELCOME);
setOption(QWizard::NoBackButtonOnLastPage, true);
setOption(QWizard::NoCancelButtonOnLastPage, true);
// Mark done on both Accept (Finish) and Reject (Cancel / close)
connect(this, &QWizard::accepted, this,
&FirstRunWizard::markFirstRunComplete);
connect(this, &QWizard::rejected, this,
&FirstRunWizard::markFirstRunComplete);
}
void FirstRunWizard::markFirstRunComplete()
{
WriteFirstRun(false);
}
// static
std::shared_ptr<Macro> FirstRunWizard::ShowWizard(QWidget *parent)
{
auto wizard = new FirstRunWizard(parent);
wizard->exec();
wizard->deleteLater();
return wizard->_macro;
}
// static
bool FirstRunWizard::CreateMacro(std::shared_ptr<Macro> &macro,
const std::string &macroName,
const std::string &conditionId,
obs_data_t *conditionData,
const std::string &actionId,
obs_data_t *actionData)
{
// 1. Create and register the Macro
macro = std::make_shared<Macro>(macroName, GetGlobalMacroSettings());
if (!macro) {
blog(LOG_WARNING, "FirstRunWizard: Macro allocation failed");
return false;
}
// 2. Instantiate condition via factory, then hydrate via Load()
auto condition =
MacroConditionFactory::Create(conditionId, macro.get());
if (!condition) {
blog(LOG_WARNING,
"FirstRunWizard: condition factory returned null "
"for id '%s' — is the base plugin loaded?",
conditionId.c_str());
return false;
}
if (!condition->Load(conditionData)) {
blog(LOG_WARNING,
"FirstRunWizard: condition Load() failed for id '%s'",
conditionId.c_str());
return false;
}
macro->Conditions().emplace_back(condition);
// 3. Instantiate action via factory, then hydrate via Load()
auto action = MacroActionFactory::Create(actionId, macro.get());
if (!action) {
blog(LOG_WARNING,
"FirstRunWizard: action factory returned null "
"for id '%s' — is the base plugin loaded?",
actionId.c_str());
return false;
}
if (!action->Load(actionData)) {
blog(LOG_WARNING,
"FirstRunWizard: action Load() failed for id '%s'",
actionId.c_str());
return false;
}
macro->Actions().emplace_back(action);
blog(LOG_INFO, "FirstRunWizard: created macro '%s'", macroName.c_str());
return true;
}
} // namespace advss

View File

@@ -1,128 +0,0 @@
#pragma once
#include <obs-data.h>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QTimer>
#include <QWizard>
#include <QWizardPage>
#include <string>
namespace advss {
class Macro;
bool IsFirstRun();
// ---------------------------------------------------------------------------
// Page IDs
// ---------------------------------------------------------------------------
enum WizardPageId {
PAGE_WELCOME = 0,
PAGE_SCENE,
PAGE_WINDOW,
PAGE_REVIEW,
PAGE_DONE,
};
// ---------------------------------------------------------------------------
// WelcomePage
// ---------------------------------------------------------------------------
class WelcomePage : public QWizardPage {
Q_OBJECT
public:
explicit WelcomePage(QWidget *parent = nullptr);
int nextId() const override { return PAGE_SCENE; }
};
// ---------------------------------------------------------------------------
// SceneSelectionPage
// Registers wizard field "targetScene" (QString).
// ---------------------------------------------------------------------------
class SceneSelectionPage : public QWizardPage {
Q_OBJECT
public:
explicit SceneSelectionPage(QWidget *parent = nullptr);
void initializePage() override;
bool isComplete() const override;
int nextId() const override { return PAGE_WINDOW; }
private:
QComboBox *_sceneCombo;
};
// ---------------------------------------------------------------------------
// WindowConditionPage
// Registers wizard field "windowTitle" (QString).
// Auto-detect button samples the focused window after a countdown.
// ---------------------------------------------------------------------------
class WindowConditionPage : public QWizardPage {
Q_OBJECT
public:
explicit WindowConditionPage(QWidget *parent = nullptr);
void initializePage() override;
bool isComplete() const override;
int nextId() const override { return PAGE_REVIEW; }
private slots:
void onAutoDetectClicked();
void onCountdownTick();
private:
QLineEdit *_windowEdit;
QPushButton *_autoDetect;
QTimer *_detectTimer;
int _countdown = 3;
};
// ---------------------------------------------------------------------------
// ReviewPage
// Displays a summary and calls FirstRunWizard::CreateMacro() on Finish.
// ---------------------------------------------------------------------------
class ReviewPage : public QWizardPage {
Q_OBJECT
public:
explicit ReviewPage(QWidget *parent, std::shared_ptr<Macro> &macro);
void initializePage() override;
bool validatePage() override;
int nextId() const override { return PAGE_DONE; }
private:
QLabel *_summary;
std::shared_ptr<Macro> &_macro;
};
// ---------------------------------------------------------------------------
// DonePage
// ---------------------------------------------------------------------------
class DonePage : public QWizardPage {
Q_OBJECT
public:
explicit DonePage(QWidget *parent = nullptr);
int nextId() const override { return -1; }
};
// ---------------------------------------------------------------------------
// FirstRunWizard
// ---------------------------------------------------------------------------
class FirstRunWizard : public QWizard {
Q_OBJECT
public:
explicit FirstRunWizard(QWidget *parent = nullptr);
static std::shared_ptr<Macro> ShowWizard(QWidget *parent);
static bool
CreateMacro(std::shared_ptr<Macro> &macro, const std::string &macroName,
const std::string &conditionId, obs_data_t *conditionData,
const std::string &actionId, obs_data_t *actionData);
private:
void markFirstRunComplete();
std::shared_ptr<Macro> _macro;
};
} // namespace advss

View File

@@ -4,11 +4,9 @@
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include <algorithm> #include <algorithm>
#include <QAction> #include <QAction>
#include <QLayout>
#include <QMenu> #include <QMenu>
#include <QTimer> #include <QLayout>
Q_DECLARE_METATYPE(advss::Item *); Q_DECLARE_METATYPE(advss::Item *);
@@ -332,13 +330,6 @@ void ItemSettingsDialog::NameChanged(const QString &text)
SetNameWarning(""); SetNameWarning("");
} }
void ItemSettingsDialog::showEvent(QShowEvent *)
{
if (_showNameEmptyWarning && _name->text().isEmpty()) {
_name->setFocus(Qt::OtherFocusReason);
}
}
void ItemSettingsDialog::SetNameWarning(const QString warn) void ItemSettingsDialog::SetNameWarning(const QString warn)
{ {
if (warn.isEmpty()) { if (warn.isEmpty()) {

View File

@@ -52,7 +52,6 @@ private slots:
void NameChanged(const QString &); void NameChanged(const QString &);
protected: protected:
virtual void showEvent(QShowEvent *) override;
void SetNameWarning(const QString); void SetNameWarning(const QString);
QLineEdit *_name; QLineEdit *_name;

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#ifndef UNIT_TEST
#include <util/base.h> #include <util/base.h>
#endif
namespace advss { namespace advss {
@@ -7,7 +9,6 @@ namespace advss {
#define blog(level, msg, ...) #define blog(level, msg, ...)
#define vblog(level, msg, ...) #define vblog(level, msg, ...)
#define ablog(level, msg, ...) #define ablog(level, msg, ...)
#define mblog(level, msg, ...)
#else #else
// Print log with "[adv-ss] " prefix // Print log with "[adv-ss] " prefix

View File

@@ -1,12 +1,14 @@
#include "non-modal-dialog.hpp" #include "non-modal-dialog.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include <atomic>
#include <mutex> #include <mutex>
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLabel> #include <QLabel>
#include <QLayout> #include <QLayout>
#include <QMainWindow> #include <QMainWindow>
#include <QCoreApplication>
namespace advss { namespace advss {

View File

@@ -3,42 +3,12 @@
#include "macro-signals.hpp" #include "macro-signals.hpp"
#include "switcher-data.hpp" #include "switcher-data.hpp"
#include "obs-frontend-api.h"
namespace advss { namespace advss {
static std::mutex initMutex; static std::mutex initMutex;
static std::mutex postLoadMutex; static std::mutex postLoadMutex;
static std::mutex finishLoadMutex;
static std::mutex mutex; static std::mutex mutex;
static bool setup();
static bool setupDonw = setup();
bool loadingFinished = false;
static std::vector<std::function<void()>> &getFinishLoadSteps();
static bool setup()
{
static auto handleEvent = [](enum obs_frontend_event event, void *) {
switch (event) {
case OBS_FRONTEND_EVENT_FINISHED_LOADING: {
std::lock_guard<std::mutex> lock(finishLoadMutex);
for (const auto &step : getFinishLoadSteps()) {
step();
}
getFinishLoadSteps().clear();
loadingFinished = true;
break;
}
default:
break;
};
};
obs_frontend_add_event_callback(handleEvent, nullptr);
return true;
}
static std::vector<std::function<void()>> &getPluginInitSteps() static std::vector<std::function<void()>> &getPluginInitSteps()
{ {
static std::vector<std::function<void()>> steps; static std::vector<std::function<void()>> steps;
@@ -93,12 +63,6 @@ static std::vector<std::function<void()>> &getPostLoadSteps()
return steps; return steps;
} }
static std::vector<std::function<void()>> &getFinishLoadSteps()
{
static std::vector<std::function<void()>> steps;
return steps;
}
void SavePluginSettings(obs_data_t *obj) void SavePluginSettings(obs_data_t *obj)
{ {
GetSwitcher()->SaveSettings(obj); GetSwitcher()->SaveSettings(obj);
@@ -172,7 +136,7 @@ void AddPluginInitStep(std::function<void()> step)
void AddPluginPostLoadStep(std::function<void()> step) void AddPluginPostLoadStep(std::function<void()> step)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(initMutex);
getPluginPostLoadSteps().emplace_back(step); getPluginPostLoadSteps().emplace_back(step);
} }
@@ -214,16 +178,6 @@ void RunIntervalResetSteps()
} }
} }
void AddFinishedLoadingStep(std::function<void()> step)
{
std::lock_guard<std::mutex> lock(finishLoadMutex);
if (loadingFinished) {
return;
}
getFinishLoadSteps().emplace_back(step);
}
void AddStartStep(std::function<void()> step) void AddStartStep(std::function<void()> step)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);

View File

@@ -34,9 +34,6 @@ void RunStartSteps();
void RunStopSteps(); void RunStopSteps();
void RunIntervalResetSteps(); void RunIntervalResetSteps();
// Steps are executed after OBS_FRONTEND_EVENT_FINISHED_LOADING is fired
EXPORT void AddFinishedLoadingStep(std::function<void()>);
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 }; enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior); EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior);
EXPORT NoMatchBehavior GetPluginNoMatchBehavior(); EXPORT NoMatchBehavior GetPluginNoMatchBehavior();

View File

@@ -65,16 +65,13 @@ ResourceTable::ResourceTable(QWidget *parent, const QString &help,
[openSettings]() { openSettings(); }); [openSettings]() { openSettings(); });
auto settingsShortcut = new QShortcut(QKeySequence("F2"), this); auto settingsShortcut = new QShortcut(QKeySequence("F2"), this);
settingsShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(settingsShortcut, &QShortcut::activated, this, QWidget::connect(settingsShortcut, &QShortcut::activated, this,
openSettings); openSettings);
auto removeShortcut = new QShortcut(QKeySequence("Del"), this); auto removeShortcut = new QShortcut(QKeySequence("Del"), this);
removeShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(removeShortcut, &QShortcut::activated, this, QWidget::connect(removeShortcut, &QShortcut::activated, this,
[this]() { Remove(); }); [this]() { Remove(); });
auto newShortcut = new QShortcut(QKeySequence("Ctrl+N"), this); auto newShortcut = new QShortcut(QKeySequence("Ctrl+N"), this);
newShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(newShortcut, &QShortcut::activated, this, QWidget::connect(newShortcut, &QShortcut::activated, this,
[this]() { Add(); }); [this]() { Add(); });
} }

View File

@@ -1,5 +1,4 @@
#include "source-helpers.hpp" #include "source-helpers.hpp"
#include "canvas-helpers.hpp"
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
@@ -29,43 +28,15 @@ std::string GetWeakSourceName(obs_weak_source_t *weak_source)
OBSWeakSource GetWeakSourceByName(const char *name) OBSWeakSource GetWeakSourceByName(const char *name)
{ {
OBSSourceAutoRelease source = obs_get_source_by_name(name); OBSWeakSource weak;
obs_source_t *source = obs_get_source_by_name(name);
if (source) { if (source) {
return OBSGetWeakRef(source); weak = obs_source_get_weak_source(source);
obs_weak_source_release(weak);
obs_source_release(source);
} }
#if LIBOBS_API_VER > MAKE_SEMANTIC_VERSION(31, 1, 0) return weak;
struct SearchData {
const char *name;
OBSWeakSource &scene;
};
static const auto enumCanvasScenes = [](void *dataPtr,
obs_source_t *source) -> bool {
auto data = static_cast<SearchData *>(dataPtr);
if (strcmp(data->name, obs_source_get_name(source)) == 0) {
data->scene = OBSGetWeakRef(source);
return false;
}
return true;
};
static const auto enumCanvases = [](void *dataPtr,
obs_canvas_t *canvas) -> bool {
obs_canvas_enum_scenes(canvas, enumCanvasScenes, dataPtr);
auto data = static_cast<SearchData *>(dataPtr);
return data->scene == nullptr;
};
OBSWeakSource weakScene;
SearchData searchData{name, weakScene};
obs_enum_canvases(enumCanvases, &searchData);
return weakScene;
#else
return nullptr;
#endif
} }
OBSWeakSource GetWeakSourceByQString(const QString &name) OBSWeakSource GetWeakSourceByQString(const QString &name)

View File

@@ -37,8 +37,8 @@ void CenterSplitterPosition(QSplitter *splitter)
void SetSplitterPositionByFraction(QSplitter *splitter, double fraction) void SetSplitterPositionByFraction(QSplitter *splitter, double fraction)
{ {
int value1 = (int)((double)QWIDGETSIZE_MAX * fraction); int value1 = (double)QWIDGETSIZE_MAX * fraction;
int value2 = (int)((double)QWIDGETSIZE_MAX * (1.0 - fraction)); int value2 = (double)QWIDGETSIZE_MAX * (1.0 - fraction);
splitter->setSizes(QList<int>() << value1 << value2); splitter->setSizes(QList<int>() << value1 << value2);
} }

View File

@@ -58,9 +58,6 @@ StringListEdit::StringListEdit(
_filterCallback(filter), _filterCallback(filter),
_preprocessCallback(preprocess) _preprocessCallback(preprocess)
{ {
if (_addString.isEmpty()) {
_addString = obs_module_text("AdvSceneSwitcher.windowTitle");
}
} }
void StringListEdit::SetStringList(const StringList &list) void StringListEdit::SetStringList(const StringList &list)

View File

@@ -10,8 +10,6 @@ namespace advss {
class StringList : public QList<StringVariable> { class StringList : public QList<StringVariable> {
public: public:
using QList<StringVariable>::QList;
EXPORT bool Save(obs_data_t *obj, const char *name, EXPORT bool Save(obs_data_t *obj, const char *name,
const char *elementName = "string") const; const char *elementName = "string") const;
EXPORT bool Load(obs_data_t *obj, const char *name, EXPORT bool Load(obs_data_t *obj, const char *name,

View File

@@ -2,22 +2,8 @@
namespace advss { namespace advss {
#ifdef UNIT_TEST
std::mutex *GetSwitcherMutex()
{
static std::mutex m;
return &m;
}
std::unique_lock<std::mutex> *GetSwitcherLoopLock()
{
static std::mutex m;
static std::unique_lock<std::mutex> lock(m);
return &lock;
}
#else
std::mutex *GetSwitcherMutex(); std::mutex *GetSwitcherMutex();
std::unique_lock<std::mutex> *GetSwitcherLoopLock(); std::unique_lock<std::mutex> *GetSwitcherLoopLock();
#endif
std::mutex *GetMutex() std::mutex *GetMutex()
{ {

View File

@@ -2,6 +2,7 @@
#include "export-symbol-helper.hpp" #include "export-symbol-helper.hpp"
#include <functional> #include <functional>
#include <memory>
#include <mutex> #include <mutex>
namespace advss { namespace advss {

View File

@@ -1,7 +1,6 @@
#include "tab-helpers.hpp" #include "tab-helpers.hpp"
#include "log-helper.hpp" #include "log-helper.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include <obs.hpp> #include <obs.hpp>
#include <QTabBar> #include <QTabBar>
@@ -34,21 +33,6 @@ struct TabCallbacks {
static std::unordered_map<const char *, TabCallbacks> createTabCallbacks; static std::unordered_map<const char *, TabCallbacks> createTabCallbacks;
static int lastOpenedTab = -1; static int lastOpenedTab = -1;
static bool alwaysShowTabs = false;
[[maybe_unused]] static bool _ = []() {
AddPluginInitStep([]() {
AddSaveStep([](obs_data_t *data) {
obs_data_set_bool(data, "alwaysShowTabs",
alwaysShowTabs);
});
AddLoadStep([](obs_data_t *data) {
alwaysShowTabs =
obs_data_get_bool(data, "alwaysShowTabs");
});
});
return true;
}();
void SetTabVisibleByName(QTabWidget *tabWidget, bool visible, void SetTabVisibleByName(QTabWidget *tabWidget, bool visible,
const QString &name) const QString &name)
@@ -213,41 +197,7 @@ void SetupOtherTabs(QTabWidget *tabWidget)
.c_str()); .c_str());
tabWidget->insertTab(0, widget, tabText); tabWidget->insertTab(0, widget, tabText);
callbacks.setupTab(tabWidget); callbacks.setupTab(tabWidget);
if (alwaysShowTabs) {
tabWidget->setTabVisible(0, true);
}
} }
} }
void SetupShowAllTabsCheckBox(QCheckBox *checkBox, QTabWidget *tabWidget)
{
const auto stateChanged = [tabWidget](int state) {
const bool newState = (state != 0);
if (newState == alwaysShowTabs) {
return;
}
alwaysShowTabs = newState;
// Only show currently hidden tabs, but don't hide tabs which
// are already visible
if (!alwaysShowTabs) {
return;
}
for (const auto &[name, _] : createTabCallbacks) {
const auto localeKey =
std::string("AdvSceneSwitcher.") + name +
".title";
auto tabText = obs_module_text(localeKey.c_str());
SetTabVisibleByName(tabWidget, true, tabText);
}
};
checkBox->setChecked(alwaysShowTabs);
QWidget::connect(checkBox, &QCheckBox::stateChanged, checkBox,
stateChanged);
}
} // namespace advss } // namespace advss

View File

@@ -2,8 +2,6 @@
#include "export-symbol-helper.hpp" #include "export-symbol-helper.hpp"
#include <obs-data.h> #include <obs-data.h>
#include <QCheckBox>
#include <QTabWidget> #include <QTabWidget>
namespace advss { namespace advss {
@@ -21,6 +19,5 @@ void SetupOtherTabs(QTabWidget *tabWidget);
void SaveTabOrder(obs_data_t *obj); void SaveTabOrder(obs_data_t *obj);
void LoadTabOrder(obs_data_t *obj); void LoadTabOrder(obs_data_t *obj);
bool MacroTabIsInFocus(); bool MacroTabIsInFocus();
void SetupShowAllTabsCheckBox(QCheckBox *checkBox, QTabWidget *tabWidget);
} // namespace advss } // namespace advss

View File

@@ -81,13 +81,13 @@ void GenericVariableSpinbox::DisableVariableSelection()
void GenericVariableSpinbox::setMinimum(double value) void GenericVariableSpinbox::setMinimum(double value)
{ {
_fixedValueInt->setMinimum((int)value); _fixedValueInt->setMinimum(value);
_fixedValueDouble->setMinimum(value); _fixedValueDouble->setMinimum(value);
} }
void GenericVariableSpinbox::setMaximum(double value) void GenericVariableSpinbox::setMaximum(double value)
{ {
_fixedValueInt->setMaximum((int)value); _fixedValueInt->setMaximum(value);
_fixedValueDouble->setMaximum(value); _fixedValueDouble->setMaximum(value);
} }

View File

@@ -261,14 +261,6 @@ std::optional<std::string> GetTextInWindow(const std::string &window)
return {}; return {};
} }
DWORD pid = 0;
DWORD thid = 0;
thid = GetWindowThreadProcessId(hwnd, &pid);
// Calling CoCreateInstance() on the OBS windows might cause a deadlock
if (GetCurrentProcessId() == pid) {
return {};
}
IUIAutomation *automation = nullptr; IUIAutomation *automation = nullptr;
auto hr = CoCreateInstance(__uuidof(CUIAutomation), nullptr, auto hr = CoCreateInstance(__uuidof(CUIAutomation), nullptr,
CLSCTX_INPROC_SERVER, CLSCTX_INPROC_SERVER,

View File

@@ -150,8 +150,6 @@ target_sources(
utils/connection-manager.hpp utils/connection-manager.hpp
utils/cursor-helpers.cpp utils/cursor-helpers.cpp
utils/cursor-helpers.hpp utils/cursor-helpers.hpp
utils/day-of-week-selector.cpp
utils/day-of-week-selector.hpp
utils/filter-selection.cpp utils/filter-selection.cpp
utils/filter-selection.hpp utils/filter-selection.hpp
utils/hotkey-helpers.cpp utils/hotkey-helpers.cpp
@@ -168,8 +166,6 @@ target_sources(
utils/scene-item-selection.hpp utils/scene-item-selection.hpp
utils/scene-item-transform-helpers.cpp utils/scene-item-transform-helpers.cpp
utils/scene-item-transform-helpers.hpp utils/scene-item-transform-helpers.hpp
utils/transition-helpers.cpp
utils/transition-helpers.hpp
utils/source-properties-button.cpp utils/source-properties-button.cpp
utils/source-properties-button.hpp utils/source-properties-button.hpp
utils/source-settings-helpers.cpp utils/source-settings-helpers.cpp

View File

@@ -144,7 +144,7 @@ void MacroActionFileEdit::UpdateEntryData()
} }
_actions->setCurrentIndex(static_cast<int>(_entryData->_action)); _actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_filePath->SetPath(_entryData->_file); _filePath->SetPath(QString::fromStdString(_entryData->_file));
_text->setPlainText(_entryData->_text); _text->setPlainText(_entryData->_text);
adjustSize(); adjustSize();

View File

@@ -4,7 +4,6 @@
#include "plugin-state-helpers.hpp" #include "plugin-state-helpers.hpp"
#include "scene-switch-helpers.hpp" #include "scene-switch-helpers.hpp"
#include "source-helpers.hpp" #include "source-helpers.hpp"
#include "transition-helpers.hpp"
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
@@ -69,6 +68,14 @@ static int getTransitionOverrideDuration(OBSWeakSource &scene)
return duration; return duration;
} }
static bool isUsingFixedLengthTransition(const OBSWeakSource &transition)
{
obs_source_t *source = obs_weak_source_get_source(transition);
bool ret = obs_transition_fixed(source);
obs_source_release(source);
return ret;
}
static OBSWeakSource getOverrideTransition(OBSWeakSource &scene) static OBSWeakSource getOverrideTransition(OBSWeakSource &scene)
{ {
OBSWeakSource transition; OBSWeakSource transition;
@@ -94,13 +101,13 @@ static int getExpectedTransitionDuration(OBSWeakSource &scene,
auto overrideTransition = getOverrideTransition(scene); auto overrideTransition = getOverrideTransition(scene);
if (overrideTransition) { if (overrideTransition) {
transition = overrideTransition; transition = overrideTransition;
if (!IsFixedLengthTransition(transition)) { if (!isUsingFixedLengthTransition(transition)) {
return getTransitionOverrideDuration(scene); return getTransitionOverrideDuration(scene);
} }
} }
} }
if (IsFixedLengthTransition(transition)) { if (isUsingFixedLengthTransition(transition)) {
return -1; // no API is available to access the fixed duration return -1; // no API is available to access the fixed duration
} }
if (duration != 0) { if (duration != 0) {
@@ -496,7 +503,7 @@ shouldShowDurationInTransitionLayout(MacroActionSwitchScene::SceneType type,
return true; return true;
} }
if (IsFixedLengthTransition(transition.GetTransition())) { if (isUsingFixedLengthTransition(transition.GetTransition())) {
return false; return false;
} }

View File

@@ -1,9 +1,5 @@
#include "macro-action-scene-visibility.hpp" #include "macro-action-scene-visibility.hpp"
#include "layout-helpers.hpp" #include "layout-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "transition-helpers.hpp"
#include <obs-frontend-api.h>
namespace advss { namespace advss {
@@ -25,168 +21,9 @@ const static std::map<MacroActionSceneVisibility::Action, std::string>
"AdvSceneSwitcher.action.sceneVisibility.type.toggle"}, "AdvSceneSwitcher.action.sceneVisibility.type.toggle"},
}; };
namespace {
struct TransitionRestoreContext;
static std::unordered_map<obs_sceneitem_t *, TransitionRestoreContext *>
restoreContexts;
static std::mutex restoreMutex;
struct TransitionRestoreContext {
public:
obs_sceneitem_t *item;
bool wasVisible;
OBSSource originalTransition = nullptr;
uint32_t originalDuration;
obs_source_t *transition = nullptr;
signal_handler_t *sh = nullptr;
};
} // namespace
static void handleShutdown(enum obs_frontend_event event, void *private_data)
{
if (event != OBS_FRONTEND_EVENT_EXIT) {
return;
}
std::lock_guard<std::mutex> lock(restoreMutex);
for (const auto &[_, ctx] : restoreContexts) {
obs_source_release(ctx->transition);
}
restoreContexts.clear();
}
static bool setup()
{
obs_frontend_add_event_callback(handleShutdown, nullptr);
return true;
}
static bool setupDone = setup();
static void handleSourceDestroyed(void *param, calldata_t *)
{
if (OBSIsShuttingDown()) {
return;
}
auto ctx = static_cast<TransitionRestoreContext *>(param);
ctx->sh = nullptr;
ctx->originalTransition = nullptr;
delete ctx;
}
static void resetSceneItemTransition(void *param, calldata_t *)
{
if (OBSIsShuttingDown()) {
return;
}
auto ctx = static_cast<TransitionRestoreContext *>(param);
if (!ctx) {
return;
}
SetSceneItemTransition(ctx->item, ctx->originalTransition,
!ctx->wasVisible);
obs_sceneitem_set_transition_duration(ctx->item, !ctx->wasVisible,
ctx->originalDuration);
signal_handler_disconnect(ctx->sh, "transition_stop",
resetSceneItemTransition, ctx);
signal_handler_disconnect(ctx->sh, "destroy", handleSourceDestroyed,
ctx);
{
std::lock_guard<std::mutex> lock(restoreMutex);
restoreContexts.erase(ctx->item);
}
obs_source_release(ctx->transition);
delete ctx;
}
static void attachRestoreContext(obs_sceneitem_t *item,
obs_source_t *transition, bool itemWasVisible,
OBSSource originalTransition,
uint32_t originalDuration)
{
signal_handler_t *sh = obs_source_get_signal_handler(transition);
if (!sh) {
return;
}
std::lock_guard<std::mutex> lock(restoreMutex);
auto ctx = new TransitionRestoreContext{
item,
itemWasVisible,
originalTransition,
originalDuration,
obs_source_get_ref(transition),
sh,
};
auto it = restoreContexts.find(item);
if (it != restoreContexts.end()) {
auto *oldCtx = it->second;
signal_handler_disconnect(oldCtx->sh, "transition_stop",
resetSceneItemTransition, oldCtx);
signal_handler_disconnect(oldCtx->sh, "destroy",
handleSourceDestroyed, oldCtx);
ctx->originalTransition = oldCtx->originalTransition;
ctx->originalDuration = oldCtx->originalDuration;
obs_source_release(oldCtx->transition);
delete oldCtx;
restoreContexts.erase(it);
}
restoreContexts[item] = ctx;
signal_handler_connect(sh, "transition_stop", resetSceneItemTransition,
ctx);
signal_handler_connect(sh, "destroy", handleSourceDestroyed, ctx);
}
static void setSceneItemVisibility(obs_sceneitem_t *item, static void setSceneItemVisibility(obs_sceneitem_t *item,
const bool setTransition,
const OBSWeakSource &transitionWeak,
const bool setDuration,
const Duration &duration,
MacroActionSceneVisibility::Action action) MacroActionSceneVisibility::Action action)
{ {
const OBSSourceAutoRelease transition = OBSGetStrongRef(transitionWeak);
const bool itemIsVisible = obs_sceneitem_visible(item);
const OBSSource currentTransition =
obs_sceneitem_get_transition(item, !itemIsVisible);
const uint32_t currentTransitionDuration =
obs_sceneitem_get_transition_duration(item, !itemIsVisible);
OBSSource privateTransitionSource = nullptr;
if (setTransition) {
privateTransitionSource = SetSceneItemTransition(
item, transition, !itemIsVisible);
} else {
privateTransitionSource =
obs_sceneitem_get_transition(item, !itemIsVisible);
}
if (setDuration) {
obs_sceneitem_set_transition_duration(item, !itemIsVisible,
duration.Milliseconds());
}
switch (action) { switch (action) {
case MacroActionSceneVisibility::Action::SHOW: case MacroActionSceneVisibility::Action::SHOW:
obs_sceneitem_set_visible(item, true); obs_sceneitem_set_visible(item, true);
@@ -195,43 +32,16 @@ static void setSceneItemVisibility(obs_sceneitem_t *item,
obs_sceneitem_set_visible(item, false); obs_sceneitem_set_visible(item, false);
break; break;
case MacroActionSceneVisibility::Action::TOGGLE: case MacroActionSceneVisibility::Action::TOGGLE:
obs_sceneitem_set_visible(item, !itemIsVisible); obs_sceneitem_set_visible(item, !obs_sceneitem_visible(item));
break; break;
} }
if (!setTransition && !setDuration) {
return;
}
if (!privateTransitionSource) {
if (setTransition) {
SetSceneItemTransition(item, currentTransition,
!itemIsVisible);
}
if (setDuration) {
obs_sceneitem_set_transition_duration(
item, !itemIsVisible,
currentTransitionDuration);
}
return;
}
auto sh = obs_source_get_signal_handler(privateTransitionSource);
if (!sh) {
return;
}
attachRestoreContext(item, privateTransitionSource, itemIsVisible,
currentTransition, currentTransitionDuration);
} }
bool MacroActionSceneVisibility::PerformAction() bool MacroActionSceneVisibility::PerformAction()
{ {
auto items = _source.GetSceneItems(_scene); auto items = _source.GetSceneItems(_scene);
for (const auto &item : items) { for (const auto &item : items) {
setSceneItemVisibility(item, _updateTransition, setSceneItemVisibility(item, _action);
_transition.GetTransition(),
_updateDuration, _duration, _action);
} }
return true; return true;
} }
@@ -256,10 +66,6 @@ bool MacroActionSceneVisibility::Save(obs_data_t *obj) const
MacroAction::Save(obj); MacroAction::Save(obj);
_scene.Save(obj); _scene.Save(obj);
_source.Save(obj); _source.Save(obj);
obs_data_set_bool(obj, "updateTransition", _updateTransition);
_transition.Save(obj);
obs_data_set_bool(obj, "updateDuration", _updateDuration);
_duration.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action)); obs_data_set_int(obj, "action", static_cast<int>(_action));
return true; return true;
} }
@@ -276,10 +82,6 @@ bool MacroActionSceneVisibility::Load(obs_data_t *obj)
MacroAction::Load(obj); MacroAction::Load(obj);
_scene.Load(obj); _scene.Load(obj);
_source.Load(obj); _source.Load(obj);
_updateTransition = obs_data_get_bool(obj, "updateTransition");
_transition.Load(obj);
_updateDuration = obs_data_get_bool(obj, "updateDuration");
_duration.Load(obj);
_action = static_cast<MacroActionSceneVisibility::Action>( _action = static_cast<MacroActionSceneVisibility::Action>(
obs_data_get_int(obj, "action")); obs_data_get_int(obj, "action"));
@@ -339,17 +141,10 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
SceneItemSelection::Type::ALL, SceneItemSelection::Type::ALL,
}, },
SceneItemSelectionWidget::NameClashMode::ALL)), SceneItemSelectionWidget::NameClashMode::ALL)),
_updateTransition(new QCheckBox(this)),
_transitions(new TransitionSelectionWidget(this, false, false)),
_updateDuration(new QCheckBox(this)),
_duration(new DurationSelection(this, false)),
_durationLayout(new QHBoxLayout),
_actions(new QComboBox()) _actions(new QComboBox())
{ {
populateActionSelection(_actions); populateActionSelection(_actions);
_duration->SpinBox()->setSpecialValueText("-");
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int))); SLOT(ActionChanged(int)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
@@ -359,49 +154,18 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
QWidget::connect(_sources, QWidget::connect(_sources,
SIGNAL(SceneItemChanged(const SceneItemSelection &)), SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this, SLOT(SourceChanged(const SceneItemSelection &))); this, SLOT(SourceChanged(const SceneItemSelection &)));
QWidget::connect(_updateTransition, SIGNAL(stateChanged(int)), this,
SLOT(UpdateTransitionChanged(int)));
QWidget::connect(_transitions,
SIGNAL(TransitionChanged(const TransitionSelection &)),
this,
SLOT(TransitionChanged(const TransitionSelection &)));
QWidget::connect(_updateDuration, SIGNAL(stateChanged(int)), this,
SLOT(UpdateDurationChanged(int)));
QWidget::connect(_duration, SIGNAL(DurationChanged(const Duration &)),
this, SLOT(DurationChanged(const Duration &)));
auto sceneItemLayout = new QHBoxLayout; auto layout = new QHBoxLayout;
PlaceWidgets(obs_module_text( PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.sceneVisibility.layout"), "AdvSceneSwitcher.action.sceneVisibility.entry"),
sceneItemLayout, layout,
{{"{{scenes}}", _scenes}, {{"{{scenes}}", _scenes},
{"{{sources}}", _sources}, {"{{sources}}", _sources},
{"{{actions}}", _actions}}); {"{{actions}}", _actions}});
auto transitionLayout = new QHBoxLayout;
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.sceneVisibility.layout.transition"),
transitionLayout,
{{"{{updateTransition}}", _updateTransition},
{"{{transitions}}", _transitions}});
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.sceneVisibility.layout.duration"),
_durationLayout,
{{"{{updateDuration}}", _updateDuration},
{"{{duration}}", _duration}});
auto layout = new QVBoxLayout;
layout->addLayout(sceneItemLayout);
layout->addLayout(transitionLayout);
layout->addLayout(_durationLayout);
setLayout(layout); setLayout(layout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
SetWidgetVisibility();
_loading = false; _loading = false;
} }
@@ -413,11 +177,7 @@ void MacroActionSceneVisibilityEdit::UpdateEntryData()
_actions->setCurrentIndex(static_cast<int>(_entryData->_action)); _actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_scenes->SetScene(_entryData->_scene); _scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem(_entryData->_source); _sources->SetSceneItem((_entryData->_source));
_updateTransition->setChecked(_entryData->_updateTransition);
_transitions->SetTransition(_entryData->_transition);
_updateDuration->setChecked(_entryData->_updateDuration);
_duration->SetDuration(_entryData->_duration);
} }
void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s) void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
@@ -437,34 +197,6 @@ void MacroActionSceneVisibilityEdit::SourceChanged(
updateGeometry(); updateGeometry();
} }
void MacroActionSceneVisibilityEdit::UpdateTransitionChanged(int state)
{
GUARD_LOADING_AND_LOCK();
_entryData->_updateTransition = state;
SetWidgetVisibility();
}
void MacroActionSceneVisibilityEdit::TransitionChanged(
const TransitionSelection &t)
{
GUARD_LOADING_AND_LOCK();
_entryData->_transition = t;
SetWidgetVisibility();
}
void MacroActionSceneVisibilityEdit::UpdateDurationChanged(int state)
{
GUARD_LOADING_AND_LOCK();
_entryData->_updateDuration = state;
SetWidgetVisibility();
}
void MacroActionSceneVisibilityEdit::DurationChanged(const Duration &dur)
{
GUARD_LOADING_AND_LOCK();
_entryData->_duration = dur;
}
void MacroActionSceneVisibilityEdit::ActionChanged(int value) void MacroActionSceneVisibilityEdit::ActionChanged(int value)
{ {
GUARD_LOADING_AND_LOCK(); GUARD_LOADING_AND_LOCK();
@@ -472,20 +204,4 @@ void MacroActionSceneVisibilityEdit::ActionChanged(int value)
static_cast<MacroActionSceneVisibility::Action>(value); static_cast<MacroActionSceneVisibility::Action>(value);
} }
void MacroActionSceneVisibilityEdit::SetWidgetVisibility()
{
const bool hideDurationSelection =
_entryData->_updateTransition &&
IsFixedLengthTransition(
_entryData->_transition.GetTransition());
SetLayoutVisible(_durationLayout, !hideDurationSelection);
_transitions->setEnabled(_entryData->_updateTransition);
_duration->setEnabled(_entryData->_updateDuration);
adjustSize();
updateGeometry();
}
} // namespace advss } // namespace advss

View File

@@ -1,9 +1,7 @@
#pragma once #pragma once
#include "duration-control.hpp"
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "scene-selection.hpp" #include "scene-selection.hpp"
#include "scene-item-selection.hpp" #include "scene-item-selection.hpp"
#include "transition-selection.hpp"
namespace advss { namespace advss {
@@ -22,10 +20,6 @@ public:
SceneSelection _scene; SceneSelection _scene;
SceneItemSelection _source; SceneItemSelection _source;
bool _updateTransition = false;
TransitionSelection _transition;
bool _updateDuration = false;
Duration _duration;
enum class Action { enum class Action {
SHOW, SHOW,
@@ -59,27 +53,17 @@ public:
private slots: private slots:
void SceneChanged(const SceneSelection &); void SceneChanged(const SceneSelection &);
void SourceChanged(const SceneItemSelection &); void SourceChanged(const SceneItemSelection &);
void UpdateTransitionChanged(int);
void TransitionChanged(const TransitionSelection &);
void UpdateDurationChanged(int);
void DurationChanged(const Duration &seconds);
void ActionChanged(int value); void ActionChanged(int value);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
private: protected:
void SetWidgetVisibility();
SceneSelectionWidget *_scenes; SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources; SceneItemSelectionWidget *_sources;
QCheckBox *_updateTransition;
TransitionSelectionWidget *_transitions;
QCheckBox *_updateDuration;
DurationSelection *_duration;
QHBoxLayout *_durationLayout;
QComboBox *_actions; QComboBox *_actions;
std::shared_ptr<MacroActionSceneVisibility> _entryData; std::shared_ptr<MacroActionSceneVisibility> _entryData;
private:
bool _loading = true; bool _loading = true;
}; };

View File

@@ -194,7 +194,6 @@ MacroActionSequenceEdit::MacroActionSequenceEdit(
_resetIndex(new VariableSpinBox()), _resetIndex(new VariableSpinBox()),
_layout(new QHBoxLayout()) _layout(new QHBoxLayout())
{ {
_macros->HideGroups();
populateActionSelection(_actions); populateActionSelection(_actions);
_resetIndex->setMinimum(1); _resetIndex->setMinimum(1);

View File

@@ -31,16 +31,15 @@ bool MacroActionTimer::PerformAction()
if (!macro) { if (!macro) {
return true; return true;
} }
auto conditions = *GetMacroConditions(macro.get());
const auto updateCondition = [this](MacroCondition *condition) { for (auto condition : conditions) {
if (condition->GetId() != id) { if (condition->GetId() != id) {
return; continue;
} }
auto timerCondition = auto timerCondition =
dynamic_cast<MacroConditionTimer *>(condition); dynamic_cast<MacroConditionTimer *>(condition.get());
if (!timerCondition) { if (!timerCondition) {
return; continue;
} }
switch (_actionType) { switch (_actionType) {
@@ -60,23 +59,6 @@ bool MacroActionTimer::PerformAction()
default: default:
break; break;
} }
};
const auto updateMacro = [&](Macro *macro) {
auto conditions = *GetMacroConditions(macro);
for (auto condition : conditions) {
updateCondition(condition.get());
}
};
if (!IsGroupMacro(macro.get())) {
updateMacro(macro.get());
return true;
}
auto macros = GetGroupMacroEntries(macro.get());
for (const auto &macro : macros) {
updateMacro(macro.get());
} }
return true; return true;
} }
@@ -149,7 +131,7 @@ void MacroActionTimer::ResolveVariablesToFixedValues()
_duration.ResolveVariables(); _duration.ResolveVariables();
} }
static void populateTypeSelection(QComboBox *list) static inline void populateTypeSelection(QComboBox *list)
{ {
for (const auto &[_, name] : timerActions) { for (const auto &[_, name] : timerActions) {
list->addItem(obs_module_text(name.c_str())); list->addItem(obs_module_text(name.c_str()));
@@ -158,11 +140,12 @@ static void populateTypeSelection(QComboBox *list)
MacroActionTimerEdit::MacroActionTimerEdit( MacroActionTimerEdit::MacroActionTimerEdit(
QWidget *parent, std::shared_ptr<MacroActionTimer> entryData) QWidget *parent, std::shared_ptr<MacroActionTimer> entryData)
: QWidget(parent), : QWidget(parent)
_macros(new MacroSelection(parent)),
_duration(new DurationSelection(this)),
_timerAction(new QComboBox(this))
{ {
_macros = new MacroSelection(parent);
_duration = new DurationSelection();
_timerAction = new QComboBox();
populateTypeSelection(_timerAction); populateTypeSelection(_timerAction);
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)), QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
@@ -172,13 +155,15 @@ MacroActionTimerEdit::MacroActionTimerEdit(
QWidget::connect(_timerAction, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_timerAction, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionTypeChanged(int))); SLOT(ActionTypeChanged(int)));
auto layout = new QHBoxLayout; _mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{macros}}", _macros},
{"{{duration}}", _duration},
{"{{timerAction}}", _timerAction},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.timer.entry"), PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.timer.entry"),
layout, _mainLayout, widgetPlaceholders);
{{"{{macros}}", _macros}, setLayout(_mainLayout);
{"{{duration}}", _duration},
{"{{timerAction}}", _timerAction}});
setLayout(layout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();

View File

@@ -58,14 +58,16 @@ private slots:
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
private: protected:
void SetWidgetVisibility(); void SetWidgetVisibility();
MacroSelection *_macros; MacroSelection *_macros;
DurationSelection *_duration; DurationSelection *_duration;
QComboBox *_timerAction; QComboBox *_timerAction;
std::shared_ptr<MacroActionTimer> _entryData; std::shared_ptr<MacroActionTimer> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true; bool _loading = true;
}; };

View File

@@ -1,6 +1,5 @@
#include "macro-action-transition.hpp" #include "macro-action-transition.hpp"
#include "layout-helpers.hpp" #include "layout-helpers.hpp"
#include "transition-helpers.hpp"
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
@@ -76,6 +75,26 @@ static void obs_sceneitem_set_transition_duration(obs_sceneitem_t *item,
} }
#endif #endif
static void setSceneItemTransition(const OBSSceneItem &item,
const OBSSourceAutoRelease &transition,
bool show)
{
OBSDataAutoRelease settings = obs_source_get_settings(transition);
if (!transition || !settings) {
// Set transition to "None"
obs_sceneitem_set_transition(item, show, nullptr);
return;
}
// We cannot share the transition source between
// scene items without introducing strange graphical
// artifacts so we have to create new ones here
OBSSourceAutoRelease transitionSource = obs_source_create_private(
obs_source_get_id(transition), obs_source_get_name(transition),
settings);
obs_sceneitem_set_transition(item, show, transitionSource);
}
void MacroActionTransition::SetSourceTransition(bool show) void MacroActionTransition::SetSourceTransition(bool show)
{ {
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0) #if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
@@ -84,7 +103,7 @@ void MacroActionTransition::SetSourceTransition(bool show)
const auto items = _source.GetSceneItems(_scene); const auto items = _source.GetSceneItems(_scene);
for (const auto &item : items) { for (const auto &item : items) {
if (_setTransitionType) { if (_setTransitionType) {
SetSceneItemTransition(item, transition, show); setSceneItemTransition(item, transition, show);
} }
if (_setDuration) { if (_setDuration) {
obs_sceneitem_set_transition_duration( obs_sceneitem_set_transition_duration(

View File

@@ -38,13 +38,32 @@ const static std::map<MacroConditionDate::Condition, std::string>
"AdvSceneSwitcher.condition.date.state.before"}, "AdvSceneSwitcher.condition.date.state.before"},
}; };
const static std::map<MacroConditionDate::Day, std::string> dayOfWeekNames = {
{MacroConditionDate::Day::ANY,
"AdvSceneSwitcher.condition.date.anyDay"},
{MacroConditionDate::Day::MONDAY,
"AdvSceneSwitcher.condition.date.monday"},
{MacroConditionDate::Day::TUESDAY,
"AdvSceneSwitcher.condition.date.tuesday"},
{MacroConditionDate::Day::WEDNESDAY,
"AdvSceneSwitcher.condition.date.wednesday"},
{MacroConditionDate::Day::THURSDAY,
"AdvSceneSwitcher.condition.date.thursday"},
{MacroConditionDate::Day::FRIDAY,
"AdvSceneSwitcher.condition.date.friday"},
{MacroConditionDate::Day::SATURDAY,
"AdvSceneSwitcher.condition.date.saturday"},
{MacroConditionDate::Day::SUNDAY,
"AdvSceneSwitcher.condition.date.sunday"},
};
bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck) bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck)
{ {
QDateTime cur = QDateTime::currentDateTime(); QDateTime cur = QDateTime::currentDateTime();
SetVariables(cur); SetVariables(cur);
if (!_days.contains(static_cast<DayOfWeekSelector::Day>( if (_dayOfWeek != Day::ANY &&
cur.date().dayOfWeek()))) { cur.date().dayOfWeek() != static_cast<int>(_dayOfWeek)) {
return false; return false;
} }
if (_ignoreTime) { if (_ignoreTime) {
@@ -182,7 +201,7 @@ bool MacroConditionDate::CheckCondition()
bool MacroConditionDate::Save(obs_data_t *obj) const bool MacroConditionDate::Save(obs_data_t *obj) const
{ {
MacroCondition::Save(obj); MacroCondition::Save(obj);
SaveSelectedDays(obj, _days); obs_data_set_int(obj, "dayOfWeek", static_cast<int>(_dayOfWeek));
obs_data_set_int(obj, "condition", static_cast<int>(_condition)); obs_data_set_int(obj, "condition", static_cast<int>(_condition));
const auto &dateToSave = _updateOnRepeat ? _dateTime : _origDateTime; const auto &dateToSave = _updateOnRepeat ? _dateTime : _origDateTime;
const auto &dateToSave2 = _updateOnRepeat ? _dateTime2 : _origDateTime2; const auto &dateToSave2 = _updateOnRepeat ? _dateTime2 : _origDateTime2;
@@ -197,14 +216,13 @@ bool MacroConditionDate::Save(obs_data_t *obj) const
_duration.Save(obj); _duration.Save(obj);
obs_data_set_bool(obj, "dayOfWeekCheck", _dayOfWeekCheck); obs_data_set_bool(obj, "dayOfWeekCheck", _dayOfWeekCheck);
obs_data_set_string(obj, "pattern", _pattern.c_str()); obs_data_set_string(obj, "pattern", _pattern.c_str());
obs_data_set_int(obj, "version", 1);
return true; return true;
} }
bool MacroConditionDate::Load(obs_data_t *obj) bool MacroConditionDate::Load(obs_data_t *obj)
{ {
MacroCondition::Load(obj); MacroCondition::Load(obj);
_days = LoadSelectedDays(obj); _dayOfWeek = static_cast<Day>(obs_data_get_int(obj, "dayOfWeek"));
_condition = static_cast<MacroConditionDate::Condition>( _condition = static_cast<MacroConditionDate::Condition>(
obs_data_get_int(obj, "condition")); obs_data_get_int(obj, "condition"));
_dateTime = QDateTime::fromString( _dateTime = QDateTime::fromString(
@@ -227,64 +245,17 @@ bool MacroConditionDate::Load(obs_data_t *obj)
_condition == MacroConditionDate::Condition::BETWEEN) { _condition == MacroConditionDate::Condition::BETWEEN) {
_condition = MacroConditionDate::Condition::AT; _condition = MacroConditionDate::Condition::AT;
} }
if (!obs_data_has_user_value(obj, "version")) {
enum class Day {
ANY = 0,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY,
};
auto oldDay =
static_cast<Day>(obs_data_get_int(obj, "dayOfWeek"));
switch (oldDay) {
case Day::ANY:
_days = {DayOfWeekSelector::Monday,
DayOfWeekSelector::Tuesday,
DayOfWeekSelector::Wednesday,
DayOfWeekSelector::Thursday,
DayOfWeekSelector::Friday,
DayOfWeekSelector::Saturday,
DayOfWeekSelector::Sunday};
break;
case Day::MONDAY:
_days = {DayOfWeekSelector::Monday};
break;
case Day::TUESDAY:
_days = {DayOfWeekSelector::Tuesday};
break;
case Day::WEDNESDAY:
_days = {DayOfWeekSelector::Wednesday};
break;
case Day::THURSDAY:
_days = {DayOfWeekSelector::Thursday};
break;
case Day::FRIDAY:
_days = {DayOfWeekSelector::Friday};
break;
case Day::SATURDAY:
_days = {DayOfWeekSelector::Saturday};
break;
case Day::SUNDAY:
_days = {DayOfWeekSelector::Sunday};
break;
default:
break;
}
}
return true; return true;
} }
std::string MacroConditionDate::GetShortDesc() const std::string MacroConditionDate::GetShortDesc() const
{ {
if (_dayOfWeekCheck) { if (_dayOfWeekCheck) {
const auto dayName = auto it = dayOfWeekNames.find(_dayOfWeek);
DayOfWeekSelector::ToString(_days).toStdString(); if (it == dayOfWeekNames.end()) {
return "";
}
std::string dayName = obs_module_text(it->second.c_str());
if (_ignoreTime) { if (_ignoreTime) {
return dayName; return dayName;
} }
@@ -376,6 +347,13 @@ void MacroConditionDate::SetupTempVars()
obs_module_text("AdvSceneSwitcher.tempVar.date.dayOfWeek")); obs_module_text("AdvSceneSwitcher.tempVar.date.dayOfWeek"));
} }
static inline void populateDaySelection(QComboBox *list)
{
for (auto entry : dayOfWeekNames) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
static inline void populateConditionSelection(QComboBox *list) static inline void populateConditionSelection(QComboBox *list)
{ {
for (auto entry : dateConditionTypes) { for (auto entry : dateConditionTypes) {
@@ -394,7 +372,7 @@ MacroConditionDateEdit::MacroConditionDateEdit(
QWidget *parent, std::shared_ptr<MacroConditionDate> entryData) QWidget *parent, std::shared_ptr<MacroConditionDate> entryData)
: QWidget(parent), : QWidget(parent),
_weekCondition(new QComboBox()), _weekCondition(new QComboBox()),
_days(new DayOfWeekSelector(this)), _dayOfWeek(new QComboBox()),
_ignoreWeekTime(new QCheckBox()), _ignoreWeekTime(new QCheckBox()),
_weekTime(new QTimeEdit()), _weekTime(new QTimeEdit()),
_condition(new QComboBox()), _condition(new QComboBox()),
@@ -414,7 +392,7 @@ MacroConditionDateEdit::MacroConditionDateEdit(
_currentDate(new QLabel()), _currentDate(new QLabel()),
_advancedSettingsTooggle(new QPushButton(obs_module_text( _advancedSettingsTooggle(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"))), "AdvSceneSwitcher.condition.date.showAdvancedSettings"))),
_simpleLayout(new QVBoxLayout()), _simpleLayout(new QHBoxLayout()),
_advancedLayout(new QHBoxLayout()), _advancedLayout(new QHBoxLayout()),
_repeatLayout(new QVBoxLayout()), _repeatLayout(new QVBoxLayout()),
_repeatUpdateLayout(new QHBoxLayout()), _repeatUpdateLayout(new QHBoxLayout()),
@@ -440,8 +418,8 @@ MacroConditionDateEdit::MacroConditionDateEdit(
QWidget::connect(_weekCondition, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_weekCondition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int))); SLOT(ConditionChanged(int)));
QWidget::connect(_days, &DayOfWeekSelector::SelectionChanged, this, QWidget::connect(_dayOfWeek, SIGNAL(currentIndexChanged(int)), this,
&MacroConditionDateEdit::DaysChanged); SLOT(DayOfWeekChanged(int)));
QWidget::connect(_ignoreWeekTime, SIGNAL(stateChanged(int)), this, QWidget::connect(_ignoreWeekTime, SIGNAL(stateChanged(int)), this,
SLOT(IgnoreTimeChanged(int))); SLOT(IgnoreTimeChanged(int)));
QWidget::connect(_weekTime, SIGNAL(timeChanged(const QTime &)), this, QWidget::connect(_weekTime, SIGNAL(timeChanged(const QTime &)), this,
@@ -471,12 +449,13 @@ MacroConditionDateEdit::MacroConditionDateEdit(
QWidget::connect(_pattern, SIGNAL(editingFinished()), this, QWidget::connect(_pattern, SIGNAL(editingFinished()), this,
SLOT(PatternChanged())); SLOT(PatternChanged()));
populateDaySelection(_dayOfWeek);
populateConditionSelection(_condition); populateConditionSelection(_condition);
populateWeekConditionSelection(_weekCondition); populateWeekConditionSelection(_weekCondition);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{weekCondition}}", _weekCondition}, {"{{weekCondition}}", _weekCondition},
{"{{dayOfWeek}}", _days}, {"{{dayOfWeek}}", _dayOfWeek},
{"{{ignoreWeekTime}}", _ignoreWeekTime}, {"{{ignoreWeekTime}}", _ignoreWeekTime},
{"{{weekTime}}", _weekTime}, {"{{weekTime}}", _weekTime},
{"{{condition}}", _condition}, {"{{condition}}", _condition},
@@ -493,39 +472,28 @@ MacroConditionDateEdit::MacroConditionDateEdit(
{"{{pattern}}", _pattern}, {"{{pattern}}", _pattern},
{"{{currentDate}}", _currentDate}, {"{{currentDate}}", _currentDate},
}; };
auto dayLayout = new QHBoxLayout;
PlaceWidgets( PlaceWidgets(
obs_module_text( obs_module_text("AdvSceneSwitcher.condition.date.entry.simple"),
"AdvSceneSwitcher.condition.date.layout.simple.day"), _simpleLayout, widgetPlaceholders);
dayLayout, widgetPlaceholders);
auto timeLayout = new QHBoxLayout;
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.date.layout.simple.time"),
timeLayout, widgetPlaceholders);
_simpleLayout->addLayout(dayLayout);
_simpleLayout->addLayout(timeLayout);
PlaceWidgets(obs_module_text( PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.date.layout.advanced"), "AdvSceneSwitcher.condition.date.entry.advanced"),
_advancedLayout, widgetPlaceholders); _advancedLayout, widgetPlaceholders);
PlaceWidgets( PlaceWidgets(
obs_module_text( obs_module_text(
"AdvSceneSwitcher.condition.date.layout.updateOnRepeat"), "AdvSceneSwitcher.condition.date.entry.updateOnRepeat"),
_repeatUpdateLayout, widgetPlaceholders); _repeatUpdateLayout, widgetPlaceholders);
auto repeatLayout = new QHBoxLayout; auto repeatLayout = new QHBoxLayout;
PlaceWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.repeat"),
repeatLayout, widgetPlaceholders);
PlaceWidgets(obs_module_text( PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.date.layout.repeat"), "AdvSceneSwitcher.condition.date.entry.pattern"),
repeatLayout, widgetPlaceholders);
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.date.layout.pattern"),
_patternLayout, widgetPlaceholders); _patternLayout, widgetPlaceholders);
_repeatLayout->addLayout(repeatLayout); _repeatLayout->addLayout(repeatLayout);
_repeatLayout->addWidget(_nextMatchDate); _repeatLayout->addWidget(_nextMatchDate);
_repeatLayout->addLayout(_repeatUpdateLayout); _repeatLayout->addLayout(_repeatUpdateLayout);
auto mainLayout = new QVBoxLayout; auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(_simpleLayout); mainLayout->addLayout(_simpleLayout);
mainLayout->addLayout(_advancedLayout); mainLayout->addLayout(_advancedLayout);
mainLayout->addLayout(_patternLayout); mainLayout->addLayout(_patternLayout);
@@ -547,11 +515,10 @@ MacroConditionDateEdit::MacroConditionDateEdit(
_loading = false; _loading = false;
} }
void MacroConditionDateEdit::DaysChanged( void MacroConditionDateEdit::DayOfWeekChanged(int day)
const QSet<DayOfWeekSelector::Day> &days)
{ {
GUARD_LOADING_AND_LOCK(); GUARD_LOADING_AND_LOCK();
_entryData->_days = days; _entryData->_dayOfWeek = static_cast<MacroConditionDate::Day>(day);
emit HeaderInfoChanged( emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc())); QString::fromStdString(_entryData->GetShortDesc()));
} }
@@ -665,7 +632,7 @@ void MacroConditionDateEdit::ShowNextMatch()
return; return;
} }
QString format(obs_module_text( QString format(obs_module_text(
"AdvSceneSwitcher.condition.date.layout.nextMatchDate")); "AdvSceneSwitcher.condition.date.entry.nextMatchDate"));
_nextMatchDate->setText( _nextMatchDate->setText(
format.arg(_entryData->GetNextMatchDateTime().toString())); format.arg(_entryData->GetNextMatchDateTime().toString()));
} }
@@ -691,7 +658,8 @@ void MacroConditionDateEdit::UpdateEntryData()
} }
_weekCondition->setCurrentIndex( _weekCondition->setCurrentIndex(
static_cast<int>(_entryData->_condition)); static_cast<int>(_entryData->_condition));
_days->SetSelectedDays(_entryData->_days); _dayOfWeek->setCurrentIndex(
static_cast<int>(static_cast<int>(_entryData->_dayOfWeek)));
_ignoreWeekTime->setChecked(!_entryData->_ignoreTime); _ignoreWeekTime->setChecked(!_entryData->_ignoreTime);
_weekTime->setTime(_entryData->GetDateTime1().time()); _weekTime->setTime(_entryData->GetDateTime1().time());
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition)); _condition->setCurrentIndex(static_cast<int>(_entryData->_condition));

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "day-of-week-selector.hpp"
#include "duration-control.hpp" #include "duration-control.hpp"
#include <QCheckBox> #include <QCheckBox>
@@ -31,6 +30,17 @@ public:
QDateTime GetDateTime2() const; QDateTime GetDateTime2() const;
QDateTime GetNextMatchDateTime() const; QDateTime GetNextMatchDateTime() const;
enum class Day {
ANY = 0,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY,
};
enum class Condition { enum class Condition {
AT, AT,
AFTER, AFTER,
@@ -39,11 +49,7 @@ public:
PATTERN, PATTERN,
}; };
QSet<DayOfWeekSelector::Day> _days = { Day _dayOfWeek = Day::ANY;
DayOfWeekSelector::Day::Monday,
DayOfWeekSelector::Day::Tuesday,
DayOfWeekSelector::Day::Wednesday,
};
bool _ignoreDate = false; bool _ignoreDate = false;
bool _ignoreTime = false; bool _ignoreTime = false;
bool _repeat = false; bool _repeat = false;
@@ -88,7 +94,7 @@ public:
} }
private slots: private slots:
void DaysChanged(const QSet<DayOfWeekSelector::Day> &); void DayOfWeekChanged(int day);
void ConditionChanged(int cond); void ConditionChanged(int cond);
void DateChanged(const QDate &date); void DateChanged(const QDate &date);
void TimeChanged(const QTime &time); void TimeChanged(const QTime &time);
@@ -108,7 +114,7 @@ signals:
protected: protected:
QComboBox *_weekCondition; QComboBox *_weekCondition;
DayOfWeekSelector *_days; QComboBox *_dayOfWeek;
QCheckBox *_ignoreWeekTime; QCheckBox *_ignoreWeekTime;
QTimeEdit *_weekTime; QTimeEdit *_weekTime;
@@ -128,7 +134,7 @@ protected:
QLabel *_currentDate; QLabel *_currentDate;
QPushButton *_advancedSettingsTooggle; QPushButton *_advancedSettingsTooggle;
QVBoxLayout *_simpleLayout; QHBoxLayout *_simpleLayout;
QHBoxLayout *_advancedLayout; QHBoxLayout *_advancedLayout;
QVBoxLayout *_repeatLayout; QVBoxLayout *_repeatLayout;
QHBoxLayout *_repeatUpdateLayout; QHBoxLayout *_repeatUpdateLayout;

View File

@@ -55,17 +55,17 @@ void MacroConditionGameCapture::GetCalldataInfo(calldata_t *cd)
if (!calldata_get_string(cd, "title", &title)) { if (!calldata_get_string(cd, "title", &title)) {
blog(LOG_WARNING, "%s failed to get title", __func__); blog(LOG_WARNING, "%s failed to get title", __func__);
} }
_title = title ? title : ""; _title = title;
const char *className = ""; const char *className = "";
if (!calldata_get_string(cd, "class", &className)) { if (!calldata_get_string(cd, "class", &className)) {
blog(LOG_WARNING, "%s failed to get class", __func__); blog(LOG_WARNING, "%s failed to get class", __func__);
} }
_class = className ? className : ""; _class = className;
const char *executable = ""; const char *executable = "";
if (!calldata_get_string(cd, "executable", &executable)) { if (!calldata_get_string(cd, "executable", &executable)) {
blog(LOG_WARNING, "%s failed to get executable", __func__); blog(LOG_WARNING, "%s failed to get executable", __func__);
} }
_executable = executable ? executable : ""; _executable = executable;
} }
void MacroConditionGameCapture::HookedSignalReceived(void *data, calldata_t *cd) void MacroConditionGameCapture::HookedSignalReceived(void *data, calldata_t *cd)

View File

@@ -29,22 +29,6 @@ static bool windowContainsText(const std::string &window,
return text == matchText; return text == matchText;
} }
void MacroConditionWindow::SetCheckText(bool value)
{
#ifdef _WIN32
_checkText = value;
#else
(void)value;
_checkText = false;
#endif
SetupTempVars();
}
bool MacroConditionWindow::GetCheckText()
{
return _checkText;
}
bool MacroConditionWindow::WindowMatchesRequirements( bool MacroConditionWindow::WindowMatchesRequirements(
const std::string &window) const const std::string &window) const
{ {
@@ -110,18 +94,12 @@ void MacroConditionWindow::SetVariableValueBasedOnMatch(
#ifdef _WIN32 #ifdef _WIN32
SetTempVarValue("windowClass", SetTempVarValue("windowClass",
GetWindowClassByWindowTitle(matchWindow)); GetWindowClassByWindowTitle(matchWindow));
if (_checkText) {
const auto text = GetTextInWindow(matchWindow);
if (text) {
SetTempVarValue("windowText", *text);
}
}
#endif #endif
if (!IsReferencedInVars()) { if (!IsReferencedInVars()) {
return; return;
} }
if (_checkText) { if (_checkText) {
const auto text = GetTextInWindow(matchWindow); auto text = GetTextInWindow(matchWindow);
SetVariableValue(text.value_or("")); SetVariableValue(text.value_or(""));
} else { } else {
SetVariableValue(ForegroundWindowTitle()); SetVariableValue(ForegroundWindowTitle());
@@ -208,16 +186,6 @@ void MacroConditionWindow::SetupTempVars()
obs_module_text("AdvSceneSwitcher.tempVar.window.windowClass"), obs_module_text("AdvSceneSwitcher.tempVar.window.windowClass"),
obs_module_text( obs_module_text(
"AdvSceneSwitcher.tempVar.window.windowClass.description")); "AdvSceneSwitcher.tempVar.window.windowClass.description"));
if (!_checkText) {
return;
}
AddTempvar(
"windowText",
obs_module_text("AdvSceneSwitcher.tempVar.window.windowText"),
obs_module_text(
"AdvSceneSwitcher.tempVar.window.windowText.description"));
#endif #endif
} }
@@ -381,7 +349,7 @@ void MacroConditionWindowEdit::CheckTitleChanged(int state)
void MacroConditionWindowEdit::CheckTextChanged(int state) void MacroConditionWindowEdit::CheckTextChanged(int state)
{ {
GUARD_LOADING_AND_LOCK(); GUARD_LOADING_AND_LOCK();
_entryData->SetCheckText(state); _entryData->_checkText = state;
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -441,8 +409,8 @@ void MacroConditionWindowEdit::SetWidgetVisibility()
_entryData->_focus || _entryData->_windowFocusChanged); _entryData->_focus || _entryData->_windowFocusChanged);
_windowSelection->setVisible(_entryData->_checkTitle); _windowSelection->setVisible(_entryData->_checkTitle);
_windowRegex->setVisible(_entryData->_checkTitle); _windowRegex->setVisible(_entryData->_checkTitle);
_textRegex->setVisible(_entryData->GetCheckText()); _textRegex->setVisible(_entryData->_checkText);
_text->setVisible(_entryData->GetCheckText()); _text->setVisible(_entryData->_checkText);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();
} }
@@ -461,7 +429,7 @@ void MacroConditionWindowEdit::UpdateEntryData()
_maximized->setChecked(_entryData->_maximized); _maximized->setChecked(_entryData->_maximized);
_focused->setChecked(_entryData->_focus); _focused->setChecked(_entryData->_focus);
_windowFocusChanged->setChecked(_entryData->_windowFocusChanged); _windowFocusChanged->setChecked(_entryData->_windowFocusChanged);
_checkText->setChecked(_entryData->GetCheckText()); _checkText->setChecked(_entryData->_checkText);
_text->setPlainText(_entryData->_text); _text->setPlainText(_entryData->_text);
_textRegex->SetRegexConfig(_entryData->_textRegex); _textRegex->SetRegexConfig(_entryData->_textRegex);
SetWidgetVisibility(); SetWidgetVisibility();

View File

@@ -22,9 +22,6 @@ public:
return std::make_shared<MacroConditionWindow>(m); return std::make_shared<MacroConditionWindow>(m);
} }
void SetCheckText(bool value);
bool GetCheckText();
StringVariable _window; StringVariable _window;
RegexConfig _windowRegex; RegexConfig _windowRegex;
bool _checkTitle = true; bool _checkTitle = true;
@@ -33,6 +30,8 @@ public:
bool _focus = true; bool _focus = true;
bool _windowFocusChanged = false; bool _windowFocusChanged = false;
// For now only supported on Windows
bool _checkText = false;
StringVariable _text; StringVariable _text;
RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig(); RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig();
@@ -43,9 +42,6 @@ private:
void SetVariableValueBasedOnMatch(const std::string &matchWindow); void SetVariableValueBasedOnMatch(const std::string &matchWindow);
void SetupTempVars(); void SetupTempVars();
// For now only supported on Windows
bool _checkText = false;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };

View File

@@ -1,121 +0,0 @@
#include "day-of-week-selector.hpp"
#include "obs-module-helper.hpp"
#include <QPushButton>
#include <QHBoxLayout>
namespace advss {
DayOfWeekSelector::DayOfWeekSelector(QWidget *parent) : QWidget(parent)
{
auto layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(4);
const QVector<QPair<Day, QString>> days = {
{Monday, obs_module_text("AdvSceneSwitcher.day.monday")},
{Tuesday, obs_module_text("AdvSceneSwitcher.day.tuesday")},
{Wednesday, obs_module_text("AdvSceneSwitcher.day.wednesday")},
{Thursday, obs_module_text("AdvSceneSwitcher.day.thursday")},
{Friday, obs_module_text("AdvSceneSwitcher.day.friday")},
{Saturday, obs_module_text("AdvSceneSwitcher.day.saturday")},
{Sunday, obs_module_text("AdvSceneSwitcher.day.sunday")}};
for (const auto &[day, name] : days) {
auto btn = new QPushButton(this);
btn->setText(name);
btn->setCheckable(true);
connect(btn, &QPushButton::toggled, this,
&DayOfWeekSelector::OnButtonToggled);
layout->addWidget(btn);
m_buttons.insert(day, btn);
}
setLayout(layout);
}
void DayOfWeekSelector::OnButtonToggled(bool)
{
emit SelectionChanged(SelectedDays());
}
QSet<DayOfWeekSelector::Day> DayOfWeekSelector::SelectedDays() const
{
QSet<Day> result;
for (auto it = m_buttons.begin(); it != m_buttons.end(); ++it) {
if (it.value()->isChecked()) {
result.insert(it.key());
}
}
return result;
}
void DayOfWeekSelector::SetSelectedDays(const QSet<Day> &days)
{
for (auto it = m_buttons.begin(); it != m_buttons.end(); ++it) {
it.value()->setChecked(days.contains(it.key()));
}
}
QString DayOfWeekSelector::ToString(const QSet<Day> &days)
{
const QVector<QPair<Day, QString>> dayNames = {
{Monday, obs_module_text("AdvSceneSwitcher.day.monday")},
{Tuesday, obs_module_text("AdvSceneSwitcher.day.tuesday")},
{Wednesday, obs_module_text("AdvSceneSwitcher.day.wednesday")},
{Thursday, obs_module_text("AdvSceneSwitcher.day.thursday")},
{Friday, obs_module_text("AdvSceneSwitcher.day.friday")},
{Saturday, obs_module_text("AdvSceneSwitcher.day.saturday")},
{Sunday, obs_module_text("AdvSceneSwitcher.day.sunday")}};
if (days.isEmpty()) {
return obs_module_text("AdvSceneSwitcher.day.none");
}
if (days.size() == 7) {
return obs_module_text("AdvSceneSwitcher.day.any");
}
QStringList parts;
for (const auto &[day, name] : dayNames) {
if (days.contains(day)) {
parts << name;
}
}
return parts.join(", ");
}
void SaveSelectedDays(obs_data_t *settings,
const QSet<DayOfWeekSelector::Day> &days)
{
uint64_t mask = 0;
for (DayOfWeekSelector::Day day : days) {
mask |= (1ULL << static_cast<int>(day));
}
obs_data_set_int(settings, "selectedDays",
static_cast<long long>(mask));
}
QSet<DayOfWeekSelector::Day> LoadSelectedDays(obs_data_t *settings)
{
QSet<DayOfWeekSelector::Day> result;
uint64_t mask = static_cast<uint64_t>(
obs_data_get_int(settings, "selectedDays"));
for (int i = DayOfWeekSelector::Monday; i <= DayOfWeekSelector::Sunday;
++i) {
if (mask & (1ULL << i)) {
result.insert(static_cast<DayOfWeekSelector::Day>(i));
}
}
return result;
}
} // namespace advss

View File

@@ -1,45 +0,0 @@
#pragma once
#include <QWidget>
#include <QSet>
#include <obs-data.h>
class QPushButton;
namespace advss {
class DayOfWeekSelector : public QWidget {
Q_OBJECT
public:
enum Day {
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
Q_ENUM(Day)
explicit DayOfWeekSelector(QWidget *parent = nullptr);
QSet<Day> SelectedDays() const;
void SetSelectedDays(const QSet<Day> &days);
static QString ToString(const QSet<Day> &days);
signals:
void SelectionChanged(const QSet<Day> &days);
private:
void OnButtonToggled(bool checked);
QMap<Day, QPushButton *> m_buttons;
};
void SaveSelectedDays(obs_data_t *settings,
const QSet<DayOfWeekSelector::Day> &days);
QSet<DayOfWeekSelector::Day> LoadSelectedDays(obs_data_t *settings);
} // namespace advss

View File

@@ -10,7 +10,7 @@ namespace advss {
enum class HotkeyType; enum class HotkeyType;
bool CanSimulateKeyPresses(); bool CanSimulateKeyPresses();
void PressKeys(const std::vector<HotkeyType> &keys, int duration); void PressKeys(const std::vector<HotkeyType> keys, int duration);
class Hotkey { class Hotkey {
public: public:

View File

@@ -188,7 +188,7 @@ static const std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_NumpadEnter, XK_KP_Enter}, {HotkeyType::Key_NumpadEnter, XK_KP_Enter},
}; };
void PressKeys(const std::vector<HotkeyType> &keys, int duration) void PressKeys(const std::vector<HotkeyType> keys, int duration)
{ {
if (!canSimulateKeyPresses) { if (!canSimulateKeyPresses) {
return; return;

View File

@@ -9,7 +9,7 @@ bool CanSimulateKeyPresses()
return canSimulateKeyPresses; return canSimulateKeyPresses;
} }
void PressKeys(const std::vector<HotkeyType> &, int) void PressKeys(const std::vector<HotkeyType> keys, int duration)
{ {
// Not supported on MacOS // Not supported on MacOS
return; return;

View File

@@ -210,7 +210,7 @@ findListPropertyById(const obs_source_t *source, const std::string &id)
} }
auto property = obs_properties_first(properties); auto property = obs_properties_first(properties);
return {findListPropertyByIdHelper(property, id), return {findListPropertyByIdHelper(property, id),
properties_t(properties, obs_properties_destroy)}; properties_t(nullptr, obs_properties_destroy)};
} }
static std::pair<obs_property_t *, properties_t> static std::pair<obs_property_t *, properties_t>
@@ -335,12 +335,6 @@ void SetSourceSetting(obs_source_t *source, const SourceSetting &setting,
case OBS_DATA_OBJECT: { case OBS_DATA_OBJECT: {
OBSDataAutoRelease json = OBSDataAutoRelease json =
obs_data_create_from_json(value.c_str()); obs_data_create_from_json(value.c_str());
if (!json) {
blog(LOG_WARNING,
"not setting invalid data object settings value! (%s)",
value.c_str());
break;
}
obs_data_set_obj(data, id.c_str(), json); obs_data_set_obj(data, id.c_str(), json);
break; break;
} }

View File

@@ -1,12 +1,15 @@
#include "text-helpers.hpp" #include "text-helpers.hpp"
#include <QRegularExpression> #include <regex>
namespace advss { namespace advss {
QString EscapeForRegex(const QString &input) QString EscapeForRegex(const QString &s)
{ {
return QRegularExpression::escape(input); static std::regex specialChars{R"([-[\]{}()*+?.,\^$|#\s])"};
std::string input = s.toStdString();
return QString::fromStdString(
std::regex_replace(input, specialChars, R"(\$&)"));
} }
} // namespace advss } // namespace advss

View File

@@ -1,32 +0,0 @@
#include "transition-helpers.hpp"
namespace advss {
bool IsFixedLengthTransition(const OBSWeakSource &transition)
{
OBSSourceAutoRelease source = obs_weak_source_get_source(transition);
return obs_transition_fixed(source);
}
obs_source_t *SetSceneItemTransition(const OBSSceneItem &item,
const OBSSourceAutoRelease &transition,
bool show)
{
OBSDataAutoRelease settings = obs_source_get_settings(transition);
if (!transition || !settings) {
// Set transition to "None"
obs_sceneitem_set_transition(item, show, nullptr);
return nullptr;
}
// We cannot share the transition source between
// scene items without introducing strange graphical
// artifacts so we have to create new ones here
OBSSourceAutoRelease transitionSource = obs_source_create_private(
obs_source_get_id(transition), obs_source_get_name(transition),
settings);
obs_sceneitem_set_transition(item, show, transitionSource);
return transitionSource.Get();
}
} // namespace advss

View File

@@ -1,11 +0,0 @@
#pragma once
#include <obs.hpp>
namespace advss {
bool IsFixedLengthTransition(const OBSWeakSource &transition);
obs_source_t *SetSceneItemTransition(const OBSSceneItem &item,
const OBSSourceAutoRelease &transition,
bool show);
} // namespace advss

View File

@@ -138,7 +138,7 @@ static const std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_NumpadEnter, VK_RETURN}, {HotkeyType::Key_NumpadEnter, VK_RETURN},
}; };
void PressKeys(const std::vector<HotkeyType> &keys, int duration) void PressKeys(const std::vector<HotkeyType> keys, int duration)
{ {
const int repeatInterval = 100; const int repeatInterval = 100;

View File

@@ -70,7 +70,7 @@ struct URLInfo {
std::string path; std::string path;
}; };
static URLInfo getURLInfo(const std::string &input, bool keepParams) static URLInfo getURLInfo(const std::string &input)
{ {
if (input.empty()) { if (input.empty()) {
return {}; return {};
@@ -84,35 +84,21 @@ static URLInfo getURLInfo(const std::string &input, bool keepParams)
const QUrl url(urlInput); const QUrl url(urlInput);
auto host = auto host =
url.scheme().toStdString() + "://" + url.host().toStdString(); url.scheme().toStdString() + "://" + url.host().toStdString();
int port = url.port();
const int port = url.port();
if (port != -1) { if (port != -1) {
host += ":" + std::to_string(port); host += ":" + std::to_string(port);
} }
auto path = url.path().toStdString(); auto path = url.path().toStdString();
if (path.empty()) { if (path.empty()) {
path = "/"; path = "/";
} }
const auto query = url.query().toStdString();
if (!query.empty()) {
if (keepParams) {
path += "?" + query;
} else {
blog(LOG_WARNING,
"ignoring query parameters \"%s\" in URL field. "
"Using parameter field values instead.",
query.c_str());
}
}
return {host, path}; return {host, path};
} }
bool MacroActionHttp::PerformAction() bool MacroActionHttp::PerformAction()
{ {
const auto [host, path] = getURLInfo(_url, !_setParams); const auto [host, path] = getURLInfo(_url);
httplib::Client cli(host); httplib::Client cli(host);
setTimeout(cli, _timeout); setTimeout(cli, _timeout);

View File

@@ -513,7 +513,7 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
RETURN_SUCCESS(); RETURN_SUCCESS();
} }
void ScriptHandler::GetRunningStatus(void *, calldata_t *data) void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
{ {
calldata_set_bool(data, "is_running", PluginIsRunning()); calldata_set_bool(data, "is_running", PluginIsRunning());
} }

View File

@@ -93,7 +93,7 @@ bool MacroConditionStreamdeck::MessageMatches(const StreamDeckMessage &message)
{ {
const bool keyStateMatches = !_pattern.checkKeyState || const bool keyStateMatches = !_pattern.checkKeyState ||
((message.keyDown && _pattern.keyDown) || ((message.keyDown && _pattern.keyDown) ||
(!message.keyDown && !_pattern.keyDown)); (!message.keyDown && !message.keyDown));
const bool positionMatches = !_pattern.checkPosition || const bool positionMatches = !_pattern.checkPosition ||
(message.row == _pattern.row && (message.row == _pattern.row &&
message.column == _pattern.column); message.column == _pattern.column);

View File

@@ -58,16 +58,14 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const
} }
OBSDataArrayAutoRelease array = obs_data_get_array(res.data, "data"); OBSDataArrayAutoRelease array = obs_data_get_array(res.data, "data");
size_t count = obs_data_array_count(array); size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
if (count == 0) { OBSDataAutoRelease arrayObj = obs_data_array_item(array, i);
userIDCache[_name] = "invalid"; std::string id = obs_data_get_string(arrayObj, "id");
return "invalid"; userIDCache[_name] = id;
return id;
} }
userIDCache[_name] = "invalid";
OBSDataAutoRelease arrayObj = obs_data_array_item(array, 0); return "invalid";
const std::string id = obs_data_get_string(arrayObj, "id");
userIDCache[_name] = id;
return id;
} }
static QDate parseRFC3339Date(const std::string &rfc3339Date) static QDate parseRFC3339Date(const std::string &rfc3339Date)

View File

@@ -72,6 +72,7 @@ void EventSub::UnregisterInstance()
void EventSub::ConnectThread() void EventSub::ConnectThread()
{ {
_client->reset(); _client->reset();
_connected = true;
websocketpp::lib::error_code ec; websocketpp::lib::error_code ec;
EventSubWSClient::connection_ptr con = EventSubWSClient::connection_ptr con =
_client->get_connection(_url, ec); _client->get_connection(_url, ec);
@@ -89,24 +90,12 @@ void EventSub::ConnectThread()
void EventSub::WaitAndReconnect() void EventSub::WaitAndReconnect()
{ {
if (_reconnecting) {
return;
}
_reconnecting = true;
auto thread = std::thread([this]() { auto thread = std::thread([this]() {
std::unique_lock<std::mutex> lock(_waitMtx); std::unique_lock<std::mutex> lock(_waitMtx);
blog(LOG_INFO, blog(LOG_INFO,
"Twitch EventSub trying to reconnect to in %d seconds.", "Twitch EventSub trying to reconnect to in %d seconds.",
reconnectDelay); reconnectDelay);
_cv.wait_for(lock, std::chrono::seconds(reconnectDelay)); _cv.wait_for(lock, std::chrono::seconds(reconnectDelay));
_reconnecting = false;
if (_disconnect) {
return;
}
Connect(); Connect();
}); });
thread.detach(); thread.detach();
@@ -114,10 +103,6 @@ void EventSub::WaitAndReconnect()
void EventSub::Connect() void EventSub::Connect()
{ {
if (_reconnecting) {
return;
}
std::lock_guard<std::mutex> lock(_connectMtx); std::lock_guard<std::mutex> lock(_connectMtx);
if (_connected) { if (_connected) {
vblog(LOG_INFO, "Twitch EventSub connect already in progress"); vblog(LOG_INFO, "Twitch EventSub connect already in progress");
@@ -128,7 +113,6 @@ void EventSub::Connect()
_thread.join(); _thread.join();
} }
_disconnect = false; _disconnect = false;
_connected = true;
_thread = std::thread(&EventSub::ConnectThread, this); _thread = std::thread(&EventSub::ConnectThread, this);
} }
@@ -464,7 +448,7 @@ void EventSub::StartServerMigrationClient(const std::string &url)
auto client = std::make_unique<EventSubWSClient>(); auto client = std::make_unique<EventSubWSClient>();
SetupClient(*client); SetupClient(*client);
client->set_open_handler([this](connection_hdl) { client->set_open_handler([this](connection_hdl hdl) {
vblog(LOG_INFO, "Twitch EventSub migration client opened"); vblog(LOG_INFO, "Twitch EventSub migration client opened");
}); });
@@ -481,8 +465,8 @@ void EventSub::StartServerMigrationClient(const std::string &url)
const auto &data = msg->payload; const auto &data = msg->payload;
if (type == "session_welcome") { if (type == "session_welcome") {
blog(LOG_INFO, vblog(LOG_INFO,
"Twitch EventSub migration successful - switching to new connection"); "Twitch EventSub migration successful - switching to new connection");
OBSDataAutoRelease session = OBSDataAutoRelease session =
obs_data_get_obj(data, "session"); obs_data_get_obj(data, "session");
_migrationSessionID = _migrationSessionID =
@@ -581,7 +565,7 @@ void EventSub::OnClose(connection_hdl hdl)
blog(LOG_INFO, "Twitch EventSub connection closed: %s / %s (%d)", blog(LOG_INFO, "Twitch EventSub connection closed: %s / %s (%d)",
msg.c_str(), reason.c_str(), code); msg.c_str(), reason.c_str(), code);
if (!_migrating) { if (_migrating) {
ClearActiveSubscriptions(); ClearActiveSubscriptions();
} }
_connected = false; _connected = false;

View File

@@ -110,7 +110,6 @@ private:
std::condition_variable _cv; std::condition_variable _cv;
std::atomic_bool _connected{false}; std::atomic_bool _connected{false};
std::atomic_bool _disconnect{false}; std::atomic_bool _disconnect{false};
std::atomic_bool _reconnecting{false};
std::string _url; std::string _url;
std::string _sessionID; std::string _sessionID;

View File

@@ -83,8 +83,6 @@ const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
"AdvSceneSwitcher.action.twitch.type.user.getInfo"}, "AdvSceneSwitcher.action.twitch.type.user.getInfo"},
{MacroActionTwitch::Action::POINTS_REWARD_GET_INFO, {MacroActionTwitch::Action::POINTS_REWARD_GET_INFO,
"AdvSceneSwitcher.action.twitch.type.reward.getInfo"}, "AdvSceneSwitcher.action.twitch.type.reward.getInfo"},
{MacroActionTwitch::Action::CHANNEL_GET_INFO,
"AdvSceneSwitcher.action.twitch.type.channel.getInfo"},
}; };
const static std::map<MacroActionTwitch::AnnouncementColor, std::string> const static std::map<MacroActionTwitch::AnnouncementColor, std::string>
@@ -324,41 +322,6 @@ void MacroActionTwitch::GetUserInfo(const std::shared_ptr<TwitchToken> &token)
}); });
} }
void MacroActionTwitch::GetChannelInfo(const std::shared_ptr<TwitchToken> &token)
{
auto info = _channel.GetInfo(*token);
if (!info) {
return;
}
SetTempVarValue("broadcaster_user_id", info->broadcaster_id);
SetTempVarValue("broadcaster_user_login", info->broadcaster_login);
SetTempVarValue("broadcaster_user_name", info->broadcaster_name);
SetTempVarValue("language", info->broadcaster_language);
SetTempVarValue("game_id", info->game_id);
SetTempVarValue("game_name", info->game_name);
SetTempVarValue("title", info->title);
SetTempVarValue("delay", std::to_string(info->delay));
std::string tags;
for (const auto &tag : info->tags) {
tags += tag + " ";
}
if (!tags.empty()) {
tags.pop_back();
}
SetTempVarValue("tags", tags);
std::string classificationLabels;
for (const auto &label : info->content_classification_labels) {
classificationLabels += label + " ";
}
if (!classificationLabels.empty()) {
classificationLabels.pop_back();
}
SetTempVarValue("content_classification_labels", classificationLabels);
SetTempVarValue("is_branded_content",
info->is_branded_content ? "true" : "false");
}
bool MacroActionTwitch::ResolveVariableSelectionToRewardId( bool MacroActionTwitch::ResolveVariableSelectionToRewardId(
const std::shared_ptr<TwitchToken> &token) const std::shared_ptr<TwitchToken> &token)
{ {
@@ -542,16 +505,6 @@ void MacroActionTwitch::SetupTempVars()
setupTempVarHelper("offline_image_url", ".user.getInfo"); setupTempVarHelper("offline_image_url", ".user.getInfo");
setupTempVarHelper("created_at", ".user.getInfo"); setupTempVarHelper("created_at", ".user.getInfo");
break; break;
case Action::CHANNEL_GET_INFO:
setupTempVarHelper("language");
setupTempVarHelper("game_id");
setupTempVarHelper("game_name");
setupTempVarHelper("title");
setupTempVarHelper("delay");
setupTempVarHelper("tags");
setupTempVarHelper("content_classification_labels");
setupTempVarHelper("is_branded_content");
break;
default: default:
break; break;
} }
@@ -610,9 +563,6 @@ bool MacroActionTwitch::PerformAction()
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO: case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
GetRewardInfo(token); GetRewardInfo(token);
break; break;
case MacroActionTwitch::Action::CHANNEL_GET_INFO:
GetChannelInfo(token);
break;
default: default:
break; break;
} }
@@ -822,7 +772,6 @@ bool MacroActionTwitch::ActionIsSupportedByToken()
{Action::WHISPER_SEND, {{"user:manage:whispers"}}}, {Action::WHISPER_SEND, {{"user:manage:whispers"}}},
{Action::SEND_CHAT_MESSAGE, {{"chat:edit"}}}, {Action::SEND_CHAT_MESSAGE, {{"chat:edit"}}},
{Action::USER_GET_INFO, {}}, {Action::USER_GET_INFO, {}},
{Action::CHANNEL_GET_INFO, {}},
{Action::POINTS_REWARD_GET_INFO, {Action::POINTS_REWARD_GET_INFO,
{{"channel:read:redemptions"}, {{"channel:read:redemptions"},
{"channel:manage:redemptions"}}}}; {"channel:manage:redemptions"}}}};
@@ -1178,8 +1127,6 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
_entryData->GetAction() == _entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_INFO_CONTENT_LABELS_SET); MacroActionTwitch::Action::CHANNEL_INFO_CONTENT_LABELS_SET);
_channel->setVisible( _channel->setVisible(
_entryData->GetAction() ==
MacroActionTwitch::Action::CHANNEL_GET_INFO ||
_entryData->GetAction() == _entryData->GetAction() ==
MacroActionTwitch::Action::RAID_START || MacroActionTwitch::Action::RAID_START ||
_entryData->GetAction() == _entryData->GetAction() ==
@@ -1326,23 +1273,19 @@ void MacroActionTwitchEdit::SetWidgetLayout()
switch (_entryData->GetAction()) { switch (_entryData->GetAction()) {
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE: case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
layoutText = obs_module_text( layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.chat"); "AdvSceneSwitcher.action.twitch.entry.chat");
break; break;
case MacroActionTwitch::Action::USER_GET_INFO: case MacroActionTwitch::Action::USER_GET_INFO:
layoutText = obs_module_text( layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.user.getInfo"); "AdvSceneSwitcher.action.twitch.entry.user.getInfo");
break; break;
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO: case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
layoutText = obs_module_text( layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.reward.getInfo"); "AdvSceneSwitcher.action.twitch.entry.reward.getInfo");
break;
case MacroActionTwitch::Action::CHANNEL_GET_INFO:
layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.channel.getInfo");
break; break;
default: default:
layoutText = obs_module_text( layoutText = obs_module_text(
"AdvSceneSwitcher.action.twitch.layout.default"); "AdvSceneSwitcher.action.twitch.entry.default");
break; break;
} }

View File

@@ -142,8 +142,7 @@ public:
SEND_CHAT_MESSAGE = 5000, SEND_CHAT_MESSAGE = 5000,
// Get user info // Get user info
USER_GET_INFO = 6000, USER_GET_INFO = 6000
CHANNEL_GET_INFO = 6100,
}; };
enum class AnnouncementColor { enum class AnnouncementColor {
@@ -204,7 +203,6 @@ private:
void SendChatMessage(const std::shared_ptr<TwitchToken> &); void SendChatMessage(const std::shared_ptr<TwitchToken> &);
void GetUserInfo(const std::shared_ptr<TwitchToken> &); void GetUserInfo(const std::shared_ptr<TwitchToken> &);
void GetRewardInfo(const std::shared_ptr<TwitchToken> &); void GetRewardInfo(const std::shared_ptr<TwitchToken> &);
void GetChannelInfo(const std::shared_ptr<TwitchToken> &token);
bool ResolveVariableSelectionToRewardId( bool ResolveVariableSelectionToRewardId(
const std::shared_ptr<TwitchToken> &); const std::shared_ptr<TwitchToken> &);

View File

@@ -629,7 +629,7 @@ void MacroConditionTwitch::SetTempVarValues(const ChannelInfo &info)
if (!classificationLabels.empty()) { if (!classificationLabels.empty()) {
classificationLabels.pop_back(); classificationLabels.pop_back();
} }
SetTempVarValue("content_classification_labels", classificationLabels); SetTempVarValue("content_classification_labels", tags);
SetTempVarValue("is_branded_content", SetTempVarValue("is_branded_content",
info.is_branded_content ? "true" : "false"); info.is_branded_content ? "true" : "false");
} }

View File

@@ -829,7 +829,7 @@ void TokenGrabberThread::run()
if (_serverThread.joinable()) { if (_serverThread.joinable()) {
_serverThread.join(); _serverThread.join();
} }
_stopWaiting = false; _stopWaiting = {false};
// Generate URI to request token // Generate URI to request token
auto state = generateStateString(); auto state = generateStateString();

View File

@@ -7,11 +7,7 @@
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QMainWindow>
#include <QTabWidget> #include <QTabWidget>
#include <QTimer>
#include <obs-frontend-api.h>
namespace advss { namespace advss {
@@ -28,7 +24,7 @@ static bool setup()
AddSetupTabCallback("twitchConnectionTab", AddSetupTabCallback("twitchConnectionTab",
TwitchConnectionsTable::Create, setupTab); TwitchConnectionsTable::Create, setupTab);
static const auto showInvalidWarnings = [](void *) { static const auto showInvalidWarnings = []() {
const auto invalidTokens = getInvalidTokens(); const auto invalidTokens = getInvalidTokens();
for (const auto &token : invalidTokens) { for (const auto &token : invalidTokens) {
QueueUITask( QueueUITask(
@@ -42,14 +38,8 @@ static bool setup()
} }
}; };
// This function is called while the plugin settings are being loaded. AddLoadStep([](obs_data_t *) {
// Blocking at this stage can cause issues such as OBS failing to start AddPostLoadStep([]() { showInvalidWarnings(); });
// or crashing.
// Therefore, we ask the user whether they want to update their Twitch
// connections asynchronously.
AddFinishedLoadingStep([]() {
obs_queue_task(OBS_TASK_UI, showInvalidWarnings, nullptr,
false);
}); });
return true; return true;

View File

@@ -273,18 +273,15 @@ void MacroConditionVideo::SetCondition(VideoCondition condition)
bool MacroConditionVideo::ScreenshotContainsPattern() bool MacroConditionVideo::ScreenshotContainsPattern()
{ {
cv::Mat result; cv::Mat result;
double bestMatchValue = MatchPattern(_screenshotData.GetImage(), _patternImageData,
MatchPattern(_screenshotData.GetImage(), _patternImageData, _patternMatchParameters.threshold, result, nullptr,
_patternMatchParameters.threshold, result, _patternMatchParameters.useAlphaAsMask,
_patternMatchParameters.useAlphaAsMask, _patternMatchParameters.matchMode);
_patternMatchParameters.matchMode);
if (result.total() == 0) { if (result.total() == 0) {
SetTempVarValue("similarity", std::to_string(bestMatchValue));
SetTempVarValue("patternCount", "0"); SetTempVarValue("patternCount", "0");
return false; return false;
} }
const auto count = countNonZero(result); const auto count = countNonZero(result);
SetTempVarValue("similarity", std::to_string(bestMatchValue));
SetTempVarValue("patternCount", std::to_string(count)); SetTempVarValue("patternCount", std::to_string(count));
return count > 0; return count > 0;
} }
@@ -307,12 +304,10 @@ bool MacroConditionVideo::OutputChanged()
cv::Mat result; cv::Mat result;
_patternImageData = CreatePatternData(_matchImage); _patternImageData = CreatePatternData(_matchImage);
double bestMatchValue = MatchPattern(_screenshotData.GetImage(), _patternImageData,
MatchPattern(_screenshotData.GetImage(), _patternImageData, _patternMatchParameters.threshold, result, nullptr,
_patternMatchParameters.threshold, result, _patternMatchParameters.useAlphaAsMask,
_patternMatchParameters.useAlphaAsMask, _patternMatchParameters.matchMode);
_patternMatchParameters.matchMode);
SetTempVarValue("similarity", std::to_string(bestMatchValue));
if (result.total() == 0) { if (result.total() == 0) {
return false; return false;
} }
@@ -416,25 +411,7 @@ void MacroConditionVideo::SetupTempVars()
{ {
MacroCondition::SetupTempVars(); MacroCondition::SetupTempVars();
switch (_condition) { switch (_condition) {
case VideoCondition::HAS_CHANGED:
case VideoCondition::HAS_NOT_CHANGED:
if (!_patternMatchParameters.useForChangedCheck) {
break;
}
AddTempvar(
"similarity",
obs_module_text(
"AdvSceneSwitcher.tempVar.video.similarity"),
obs_module_text(
"AdvSceneSwitcher.tempVar.video.similarity.description"));
break;
case VideoCondition::PATTERN: case VideoCondition::PATTERN:
AddTempvar(
"similarity",
obs_module_text(
"AdvSceneSwitcher.tempVar.video.similarity"),
obs_module_text(
"AdvSceneSwitcher.tempVar.video.similarity.description"));
AddTempvar( AddTempvar(
"patternCount", "patternCount",
obs_module_text( obs_module_text(
@@ -474,6 +451,8 @@ void MacroConditionVideo::SetupTempVars()
break; break;
case VideoCondition::MATCH: case VideoCondition::MATCH:
case VideoCondition::DIFFER: case VideoCondition::DIFFER:
case VideoCondition::HAS_NOT_CHANGED:
case VideoCondition::HAS_CHANGED:
case VideoCondition::NO_IMAGE: case VideoCondition::NO_IMAGE:
default: default:
break; break;
@@ -1498,7 +1477,6 @@ void MacroConditionVideoEdit::UsePatternForChangedCheckChanged(int value)
{ {
GUARD_LOADING_AND_LOCK(); GUARD_LOADING_AND_LOCK();
_entryData->_patternMatchParameters.useForChangedCheck = value; _entryData->_patternMatchParameters.useForChangedCheck = value;
_entryData->SetupTempVars();
SetWidgetVisibility(); SetWidgetVisibility();
} }

View File

@@ -50,7 +50,6 @@ public:
void SetCondition(VideoCondition); void SetCondition(VideoCondition);
VideoCondition GetCondition() const { return _condition; } VideoCondition GetCondition() const { return _condition; }
void SetupTempVars();
VideoInput _video; VideoInput _video;
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath"); std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
@@ -90,6 +89,8 @@ private:
bool Compare(); bool Compare();
bool CheckShouldBeSkipped(); bool CheckShouldBeSkipped();
void SetupTempVars();
VideoCondition _condition = VideoCondition::MATCH; VideoCondition _condition = VideoCondition::MATCH;
bool _getNextScreenshot = true; bool _getNextScreenshot = true;

View File

@@ -48,18 +48,20 @@ static void preprocessPatternMatchResult(cv::Mat &mat, bool invert)
} }
} }
double MatchPattern(QImage &img, const PatternImageData &patternData, void MatchPattern(QImage &img, const PatternImageData &patternData,
double threshold, cv::Mat &result, bool useAlphaAsMask, double threshold, cv::Mat &result, double *pBestFitValue,
cv::TemplateMatchModes matchMode) bool useAlphaAsMask, cv::TemplateMatchModes matchMode)
{ {
double bestFitValue = std::numeric_limits<double>::signaling_NaN();
result = cv::Mat(0, 0, CV_32F); result = cv::Mat(0, 0, CV_32F);
if (pBestFitValue) {
*pBestFitValue = std::numeric_limits<double>::signaling_NaN();
}
if (img.isNull() || patternData.rgbaPattern.empty()) { if (img.isNull() || patternData.rgbaPattern.empty()) {
return bestFitValue; return;
} }
if (img.height() < patternData.rgbaPattern.rows || if (img.height() < patternData.rgbaPattern.rows ||
img.width() < patternData.rgbaPattern.cols) { img.width() < patternData.rgbaPattern.cols) {
return bestFitValue; return;
} }
auto input = QImageToMat(img); auto input = QImageToMat(img);
@@ -92,18 +94,20 @@ double MatchPattern(QImage &img, const PatternImageData &patternData,
// -> Invert TM_SQDIFF_NORMED in the preprocess step // -> Invert TM_SQDIFF_NORMED in the preprocess step
preprocessPatternMatchResult(result, matchMode == cv::TM_SQDIFF_NORMED); preprocessPatternMatchResult(result, matchMode == cv::TM_SQDIFF_NORMED);
cv::minMaxLoc(result, nullptr, &bestFitValue); if (pBestFitValue) {
cv::minMaxLoc(result, nullptr, pBestFitValue);
}
cv::threshold(result, result, threshold, 0.0, cv::THRESH_TOZERO); cv::threshold(result, result, threshold, 0.0, cv::THRESH_TOZERO);
return bestFitValue;
} }
double MatchPattern(QImage &img, QImage &pattern, double threshold, void MatchPattern(QImage &img, QImage &pattern, double threshold,
cv::Mat &result, bool useAlphaAsMask, cv::Mat &result, double *pBestFitValue, bool useAlphaAsMask,
cv::TemplateMatchModes matchColor) cv::TemplateMatchModes matchColor)
{ {
auto data = CreatePatternData(pattern); auto data = CreatePatternData(pattern);
return MatchPattern(img, data, threshold, result, useAlphaAsMask, MatchPattern(img, data, threshold, result, pBestFitValue,
matchColor); useAlphaAsMask, matchColor);
} }
std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade, std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade,

View File

@@ -62,12 +62,12 @@ struct PatternImageData {
}; };
PatternImageData CreatePatternData(const QImage &pattern); PatternImageData CreatePatternData(const QImage &pattern);
double MatchPattern(QImage &img, const PatternImageData &patternData, void MatchPattern(QImage &img, const PatternImageData &patternData,
double threshold, cv::Mat &result, bool useAlphaAsMask, double threshold, cv::Mat &result, double *pBestFitValue,
cv::TemplateMatchModes matchMode); bool useAlphaAsMask, cv::TemplateMatchModes matchMode);
double MatchPattern(QImage &img, QImage &pattern, double threshold, void MatchPattern(QImage &img, QImage &pattern, double threshold,
cv::Mat &result, bool useAlphaAsMask, cv::Mat &result, double *pBestFitValue, bool useAlphaAsMask,
cv::TemplateMatchModes matchMode); cv::TemplateMatchModes matchMode);
std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade, std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors, double scaleFactor, int minNeighbors,
const cv::Size &minSize, const cv::Size &minSize,

View File

@@ -329,10 +329,10 @@ void PreviewImage::MarkPatternMatch(
const PatternImageData &patternImageData) const PatternImageData &patternImageData)
{ {
cv::Mat result; cv::Mat result;
double matchValue = MatchPattern(screenshot, patternImageData, double matchValue = std::numeric_limits<double>::signaling_NaN();
patternMatchParams.threshold, result, MatchPattern(screenshot, patternImageData, patternMatchParams.threshold,
patternMatchParams.useAlphaAsMask, result, &matchValue, patternMatchParams.useAlphaAsMask,
patternMatchParams.matchMode); patternMatchParams.matchMode);
emit ValueUpdate(matchValue); emit ValueUpdate(matchValue);
if (result.total() == 0 || countNonZero(result) == 0) { if (result.total() == 0 || countNonZero(result) == 0) {
emit StatusUpdate(obs_module_text( emit StatusUpdate(obs_module_text(

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