mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 17:16:00 -05:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e2c466c2d | ||
|
|
cc68e2366c | ||
|
|
7a0e08b0d8 | ||
|
|
be8744f0d0 | ||
|
|
d4425df694 | ||
|
|
70e5f6002d | ||
|
|
ff98ec36d6 | ||
|
|
4966802f14 | ||
|
|
b23a90557f | ||
|
|
d6ea815b85 | ||
|
|
f35ef558ee | ||
|
|
d7ff9088f8 | ||
|
|
7048925d6e | ||
|
|
fcb3ea50d3 | ||
|
|
87c45e2b32 | ||
|
|
eb5046a9d6 | ||
|
|
58c05f3f6e | ||
|
|
0b284da3de |
@@ -166,6 +166,7 @@ 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)
|
||||||
|
|
||||||
@@ -203,6 +204,8 @@ 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
|
||||||
@@ -453,14 +456,29 @@ 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(
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ 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?"
|
||||||
@@ -291,14 +290,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.condition.date.anyDay="Beliebiger Tag"
|
AdvSceneSwitcher.day.any="Beliebiger Tag"
|
||||||
AdvSceneSwitcher.condition.date.monday="Montag"
|
AdvSceneSwitcher.day.monday="Montag"
|
||||||
AdvSceneSwitcher.condition.date.tuesday="Dienstag"
|
AdvSceneSwitcher.day.tuesday="Dienstag"
|
||||||
AdvSceneSwitcher.condition.date.wednesday="Mittwoch"
|
AdvSceneSwitcher.day.wednesday="Mittwoch"
|
||||||
AdvSceneSwitcher.condition.date.thursday="Donnerstag"
|
AdvSceneSwitcher.day.thursday="Donnerstag"
|
||||||
AdvSceneSwitcher.condition.date.friday="Freitag"
|
AdvSceneSwitcher.day.friday="Freitag"
|
||||||
AdvSceneSwitcher.condition.date.saturday="Samstag"
|
AdvSceneSwitcher.day.saturday="Samstag"
|
||||||
AdvSceneSwitcher.condition.date.sunday="Sonntag"
|
AdvSceneSwitcher.day.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"
|
||||||
@@ -309,12 +308,13 @@ 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.entry.simple="Am {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
|
AdvSceneSwitcher.condition.date.layout.simple.day="Am{{dayOfWeek}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
|
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Wiederholen alle {{duration}} bei Datumsübereinstimmung"
|
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.pattern="Aktuelles Datum \"{{currentDate}}\" entspricht dem Muster {{pattern}}"
|
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}} Wiederholen alle {{duration}} bei Datumsübereinstimmung"
|
||||||
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Nächster Treffer bei: %1"
|
AdvSceneSwitcher.condition.date.layout.pattern="Aktuelles Datum \"{{currentDate}}\" entspricht dem Muster {{pattern}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren"
|
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Nächster Treffer bei: %1"
|
||||||
|
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"
|
||||||
@@ -834,8 +834,6 @@ 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"
|
||||||
|
|||||||
@@ -177,7 +177,12 @@ 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.onChange="Perform actions only on condition change"
|
AdvSceneSwitcher.macroTab.actionTriggerMode.label="Perform actions:"
|
||||||
|
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."
|
||||||
@@ -589,14 +594,6 @@ 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"
|
||||||
@@ -607,12 +604,13 @@ 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.entry.simple="On{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
|
AdvSceneSwitcher.condition.date.layout.simple.day="On{{dayOfWeek}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
|
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}}Repeat every{{duration}}on date match"
|
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.pattern="Current date \"{{currentDate}}\" matches pattern{{pattern}}"
|
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}}Repeat every{{duration}}on date match"
|
||||||
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
|
AdvSceneSwitcher.condition.date.layout.pattern="Current date \"{{currentDate}}\" matches pattern{{pattern}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}On repeat update selected date to repeat date"
|
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Next match at: %1"
|
||||||
|
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"
|
||||||
@@ -2203,6 +2201,8 @@ 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,8 +2443,6 @@ 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"
|
||||||
@@ -2474,6 +2472,47 @@ 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
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ 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"
|
||||||
@@ -241,14 +240,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.condition.date.anyDay="Cualquier día"
|
AdvSceneSwitcher.day.any="Cualquier día"
|
||||||
AdvSceneSwitcher.condition.date.monday="Lunes"
|
AdvSceneSwitcher.day.monday="Lunes"
|
||||||
AdvSceneSwitcher.condition.date.tuesday="Martes"
|
AdvSceneSwitcher.day.tuesday="Martes"
|
||||||
AdvSceneSwitcher.condition.date.wednesday="Miércoles"
|
AdvSceneSwitcher.day.wednesday="Miércoles"
|
||||||
AdvSceneSwitcher.condition.date.thursday="Jueves"
|
AdvSceneSwitcher.day.thursday="Jueves"
|
||||||
AdvSceneSwitcher.condition.date.friday="Viernes"
|
AdvSceneSwitcher.day.friday="Viernes"
|
||||||
AdvSceneSwitcher.condition.date.saturday="Sábado"
|
AdvSceneSwitcher.day.saturday="Sábado"
|
||||||
AdvSceneSwitcher.condition.date.sunday="Domingo"
|
AdvSceneSwitcher.day.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"
|
||||||
@@ -258,11 +257,12 @@ 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.entry.simple="El {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
|
AdvSceneSwitcher.condition.date.layout.simple.day="El{{dayOfWeek}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
|
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Repetir cada {{duration}} en la coincidencia de fechas"
|
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima coincidencia en: %1"
|
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}} Repetir cada {{duration}} en la coincidencia de fechas"
|
||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
|
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Próxima coincidencia en: %1"
|
||||||
|
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"
|
||||||
@@ -680,8 +680,6 @@ 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"
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ 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\" ?"
|
||||||
@@ -356,14 +355,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.condition.date.anyDay="N'importe quel jour"
|
AdvSceneSwitcher.day.any="N'importe quel jour"
|
||||||
AdvSceneSwitcher.condition.date.monday="Lundi"
|
AdvSceneSwitcher.day.monday="Lundi"
|
||||||
AdvSceneSwitcher.condition.date.tuesday="Mardi"
|
AdvSceneSwitcher.day.tuesday="Mardi"
|
||||||
AdvSceneSwitcher.condition.date.wednesday="Mercredi"
|
AdvSceneSwitcher.day.wednesday="Mercredi"
|
||||||
AdvSceneSwitcher.condition.date.thursday="Jeudi"
|
AdvSceneSwitcher.day.thursday="Jeudi"
|
||||||
AdvSceneSwitcher.condition.date.friday="Vendredi"
|
AdvSceneSwitcher.day.friday="Vendredi"
|
||||||
AdvSceneSwitcher.condition.date.saturday="Samedi"
|
AdvSceneSwitcher.day.saturday="Samedi"
|
||||||
AdvSceneSwitcher.condition.date.sunday="Dimanche"
|
AdvSceneSwitcher.day.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"
|
||||||
@@ -374,11 +373,12 @@ 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.entry.simple="Le{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
|
AdvSceneSwitcher.condition.date.layout.simple.day="Le{{dayOfWeek}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}}Répéter toutes les{{duration}}lorsque la date correspond"
|
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.pattern="La date actuelle \"{{currentDate}}\" correspond au motif{{pattern}}"
|
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}}Répéter toutes les{{duration}}lorsque la date correspond"
|
||||||
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Prochaine correspondance à : %1"
|
AdvSceneSwitcher.condition.date.layout.pattern="La date actuelle \"{{currentDate}}\" correspond au motif{{pattern}}"
|
||||||
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.nextMatchDate="Prochaine correspondance à : %1"
|
||||||
|
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"
|
||||||
@@ -1122,8 +1122,6 @@ 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"
|
||||||
|
|||||||
@@ -164,7 +164,6 @@ 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\" は既にマクロで使用されています。"
|
||||||
@@ -555,14 +554,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.condition.date.anyDay="いつでも"
|
AdvSceneSwitcher.day.any="いつでも"
|
||||||
AdvSceneSwitcher.condition.date.monday="月曜日"
|
AdvSceneSwitcher.day.monday="月曜日"
|
||||||
AdvSceneSwitcher.condition.date.tuesday="火曜日"
|
AdvSceneSwitcher.day.tuesday="火曜日"
|
||||||
AdvSceneSwitcher.condition.date.wednesday="水曜日"
|
AdvSceneSwitcher.day.wednesday="水曜日"
|
||||||
AdvSceneSwitcher.condition.date.thursday="木曜日"
|
AdvSceneSwitcher.day.thursday="木曜日"
|
||||||
AdvSceneSwitcher.condition.date.friday="金曜日"
|
AdvSceneSwitcher.day.friday="金曜日"
|
||||||
AdvSceneSwitcher.condition.date.saturday="土曜日"
|
AdvSceneSwitcher.day.saturday="土曜日"
|
||||||
AdvSceneSwitcher.condition.date.sunday="日曜日"
|
AdvSceneSwitcher.day.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="前"
|
||||||
@@ -573,12 +572,9 @@ 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.entry.simple="On{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
|
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}}日付の一致ごとに{{duration}}繰り返します"
|
||||||
; AdvSceneSwitcher.condition.date.entry.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
|
AdvSceneSwitcher.condition.date.layout.pattern="現在の日付「{{currentDate}}」はパターン{{pattern}}と一致します"
|
||||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}}日付の一致ごとに{{duration}}繰り返します"
|
AdvSceneSwitcher.condition.date.layout.updateOnRepeat="{{updateOnRepeat}}繰り返しの場合、選択した日付を繰り返しの日付に更新します"
|
||||||
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="現在の値を取得"
|
||||||
@@ -2255,8 +2251,6 @@ 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="ミリ秒"
|
||||||
|
|||||||
@@ -147,7 +147,6 @@ 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."
|
||||||
@@ -502,14 +501,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.condition.date.anyDay="Qualquer dia"
|
AdvSceneSwitcher.day.any="Qualquer dia"
|
||||||
AdvSceneSwitcher.condition.date.monday="Segunda-feira"
|
AdvSceneSwitcher.day.monday="Segunda-feira"
|
||||||
AdvSceneSwitcher.condition.date.tuesday="Terça-feira"
|
AdvSceneSwitcher.day.tuesday="Terça-feira"
|
||||||
AdvSceneSwitcher.condition.date.wednesday="Quarta-feira"
|
AdvSceneSwitcher.day.wednesday="Quarta-feira"
|
||||||
AdvSceneSwitcher.condition.date.thursday="Quinta-feira"
|
AdvSceneSwitcher.day.thursday="Quinta-feira"
|
||||||
AdvSceneSwitcher.condition.date.friday="Sexta-feira"
|
AdvSceneSwitcher.day.friday="Sexta-feira"
|
||||||
AdvSceneSwitcher.condition.date.saturday="Sábado"
|
AdvSceneSwitcher.day.saturday="Sábado"
|
||||||
AdvSceneSwitcher.condition.date.sunday="Domingo"
|
AdvSceneSwitcher.day.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"
|
||||||
@@ -520,12 +519,13 @@ 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.entry.simple="Em{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
|
AdvSceneSwitcher.condition.date.layout.simple.day="Em{{dayOfWeek}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
|
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}}Repetir a cada{{duration}}em correspondência de data"
|
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.pattern="Data atual \"{{currentDate}}\" corresponde ao padrão{{pattern}}"
|
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}}Repetir a cada{{duration}}em correspondência de data"
|
||||||
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima correspondência em: %1"
|
AdvSceneSwitcher.condition.date.layout.pattern="Data atual \"{{currentDate}}\" corresponde ao padrão{{pattern}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}Ao repetir, atualize a data selecionada para a data de repetição"
|
AdvSceneSwitcher.condition.date.layout.nextMatchDate="Próxima correspondência em: %1"
|
||||||
|
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"
|
||||||
@@ -1871,8 +1871,6 @@ 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"
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ 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"
|
||||||
@@ -587,8 +586,6 @@ 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"
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ 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\" 已被宏使用."
|
||||||
@@ -530,14 +529,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.condition.date.anyDay="每一天"
|
AdvSceneSwitcher.day.any="每一天"
|
||||||
AdvSceneSwitcher.condition.date.monday="周一"
|
AdvSceneSwitcher.day.monday="周一"
|
||||||
AdvSceneSwitcher.condition.date.tuesday="周二"
|
AdvSceneSwitcher.day.tuesday="周二"
|
||||||
AdvSceneSwitcher.condition.date.wednesday="周三"
|
AdvSceneSwitcher.day.wednesday="周三"
|
||||||
AdvSceneSwitcher.condition.date.thursday="周四"
|
AdvSceneSwitcher.day.thursday="周四"
|
||||||
AdvSceneSwitcher.condition.date.friday="周五"
|
AdvSceneSwitcher.day.friday="周五"
|
||||||
AdvSceneSwitcher.condition.date.saturday="周六"
|
AdvSceneSwitcher.day.saturday="周六"
|
||||||
AdvSceneSwitcher.condition.date.sunday="周日"
|
AdvSceneSwitcher.day.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="之前"
|
||||||
@@ -548,12 +547,13 @@ 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.entry.simple="{{dayOfWeek}}{{weekCondition}}{{ignoreWeekTime}}{{weekTime}}"
|
AdvSceneSwitcher.condition.date.layout.simple.day="{{dayOfWeek}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
|
AdvSceneSwitcher.condition.date.layout.simple.time="{{ignoreWeekTime}}{{weekCondition}}{{weekTime}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} 每 {{duration}} 重复一次时间/日期匹配"
|
AdvSceneSwitcher.condition.date.layout.advanced="{{condition}}{{ignoreDate}}{{date}}{{ignoreTime}}{{time}}{{separator}}{{date2}}{{time2}}"
|
||||||
AdvSceneSwitcher.condition.date.entry.pattern="当前日期 \"{{currentDate}}\" 与 {{pattern}} 模式匹配 , 例子:2025 03 18 24 00 00 = .... .. .. .. .. .. ,难崩,我测试了一段时间才明白"
|
AdvSceneSwitcher.condition.date.layout.repeat="{{repeat}} 每 {{duration}} 重复一次时间/日期匹配"
|
||||||
AdvSceneSwitcher.condition.date.entry.nextMatchDate="下一次匹配 在: %1"
|
AdvSceneSwitcher.condition.date.layout.pattern="当前日期 \"{{currentDate}}\" 与 {{pattern}} 模式匹配 , 例子:2025 03 18 24 00 00 = .... .. .. .. .. .. ,难崩,我测试了一段时间才明白"
|
||||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 在重复时将选定日期更新为重复日期"
|
AdvSceneSwitcher.condition.date.layout.nextMatchDate="下一次匹配 在: %1"
|
||||||
|
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="获取当前值"
|
||||||
@@ -2115,8 +2115,6 @@ 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="毫秒"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>962</width>
|
<width>962</width>
|
||||||
<height>1160</height>
|
<height>1190</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||||
@@ -565,6 +565,13 @@
|
|||||||
</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>
|
||||||
@@ -786,25 +793,19 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="runMacroOnChange">
|
<widget class="QLabel" name="label_18">
|
||||||
<property name="toolTip">
|
|
||||||
<string>AdvSceneSwitcher.macroTab.onChange</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string>AdvSceneSwitcher.macroTab.actionTriggerMode.label</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_18">
|
<widget class="QComboBox" name="actionTriggerMode">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>AdvSceneSwitcher.macroTab.onChange</string>
|
<string>AdvSceneSwitcher.macroTab.actionTriggerMode.tooltip</string>
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>AdvSceneSwitcher.macroTab.onChange</string>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -4118,7 +4119,7 @@
|
|||||||
<tabstop>macroName</tabstop>
|
<tabstop>macroName</tabstop>
|
||||||
<tabstop>runMacro</tabstop>
|
<tabstop>runMacro</tabstop>
|
||||||
<tabstop>runMacroInParallel</tabstop>
|
<tabstop>runMacroInParallel</tabstop>
|
||||||
<tabstop>runMacroOnChange</tabstop>
|
<tabstop>actionTriggerMode</tabstop>
|
||||||
<tabstop>macroSettings</tabstop>
|
<tabstop>macroSettings</tabstop>
|
||||||
<tabstop>macroEdit</tabstop>
|
<tabstop>macroEdit</tabstop>
|
||||||
<tabstop>sceneGroups</tabstop>
|
<tabstop>sceneGroups</tabstop>
|
||||||
|
|||||||
@@ -192,17 +192,7 @@ 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)) {
|
||||||
auto json = obs_data_get_json(data);
|
AskForBackup(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);
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ 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 --- */
|
||||||
|
|
||||||
@@ -107,7 +108,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_runMacroOnChange_stateChanged(int value) const;
|
void on_actionTriggerMode_currentIndexChanged(int index) const;
|
||||||
void MacroSelectionChanged();
|
void MacroSelectionChanged();
|
||||||
void ShowMacroContextMenu(const QPoint &);
|
void ShowMacroContextMenu(const QPoint &);
|
||||||
void CopyMacro();
|
void CopyMacro();
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
#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"
|
||||||
|
|
||||||
@@ -361,14 +360,46 @@ void AdvSceneSwitcher::RestoreWindowGeo()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void renameMacroIfNecessary(const std::shared_ptr<Macro> ¯o)
|
||||||
|
{
|
||||||
|
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 (switcher->firstBoot && !switcher->disableHints) {
|
if (!IsFirstRun() || !GetTopLevelMacros().empty()) {
|
||||||
switcher->firstBoot = false;
|
return;
|
||||||
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)
|
||||||
|
|||||||
@@ -7,6 +7,17 @@ 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);
|
||||||
|
|||||||
@@ -4,13 +4,19 @@
|
|||||||
#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;
|
||||||
|
|
||||||
@@ -28,9 +34,15 @@ 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 {
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ 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
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,11 @@ 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) {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#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;
|
||||||
@@ -69,6 +68,7 @@ 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();
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ 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;
|
||||||
@@ -61,6 +62,7 @@ void MacroSelection::HideSelectedMacro()
|
|||||||
}
|
}
|
||||||
|
|
||||||
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
|
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
|
||||||
|
#endif // !UNIT_TEST
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroSelection::HideGroups()
|
void MacroSelection::HideGroups()
|
||||||
|
|||||||
@@ -474,14 +474,17 @@ void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value) const
|
|||||||
macro->SetRunInParallel(value);
|
macro->SetRunInParallel(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value) const
|
void AdvSceneSwitcher::on_actionTriggerMode_currentIndexChanged(int index) const
|
||||||
{
|
{
|
||||||
auto macro = GetSelectedMacro();
|
auto macro = GetSelectedMacro();
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock = LockContext();
|
auto lock = LockContext();
|
||||||
macro->SetMatchOnChange(value);
|
const auto mode = static_cast<Macro::ActionTriggerMode>(
|
||||||
|
ui->actionTriggerMode->itemData(index).toInt());
|
||||||
|
macro->SetActionTriggerMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
|
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
|
||||||
@@ -489,7 +492,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->runMacroOnChange->setDisabled(disable);
|
ui->actionTriggerMode->setDisabled(disable);
|
||||||
ui->macroEdit->SetControlsDisabled(disable);
|
ui->macroEdit->SetControlsDisabled(disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,10 +522,12 @@ 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->runMacroOnChange);
|
const QSignalBlocker b3(ui->actionTriggerMode);
|
||||||
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->runMacroOnChange->setChecked(macro->MatchOnChange());
|
ui->actionTriggerMode->setCurrentIndex(
|
||||||
|
ui->actionTriggerMode->findData(static_cast<int>(
|
||||||
|
macro->GetActionTriggerMode())));
|
||||||
}
|
}
|
||||||
|
|
||||||
macro->ResetUIHelpers();
|
macro->ResetUIHelpers();
|
||||||
@@ -552,9 +557,9 @@ void AdvSceneSwitcher::HighlightOnChange() const
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (macro->OnChangePreventedActionsSince(
|
if (macro->ActionTriggerModePreventedActionsSince(
|
||||||
lastOnChangeHighlightCheckTime)) {
|
lastOnChangeHighlightCheckTime)) {
|
||||||
HighlightWidget(ui->runMacroOnChange, Qt::yellow,
|
HighlightWidget(ui->actionTriggerMode, Qt::yellow,
|
||||||
Qt::transparent, true);
|
Qt::transparent, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,6 +687,24 @@ 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)
|
||||||
|
|||||||
@@ -224,7 +224,8 @@ void MacroTreeItem::HighlightIfExecuted()
|
|||||||
|
|
||||||
if (!wasHighlighted &&
|
if (!wasHighlighted &&
|
||||||
_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
|
_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
|
||||||
_macro->OnChangePreventedActionsSince(_lastHighlightCheckTime)) {
|
_macro->ActionTriggerModePreventedActionsSince(
|
||||||
|
_lastHighlightCheckTime)) {
|
||||||
HighlightWidget(this, Qt::yellow, QColor(0, 0, 0, 0), true);
|
HighlightWidget(this, Qt::yellow, QColor(0, 0, 0, 0), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
111
lib/macro/macro-websocket-trigger.cpp
Normal file
111
lib/macro/macro-websocket-trigger.cpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#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
|
||||||
@@ -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->CheckCondition();
|
conditionMatched = condition->EvaluateCondition();
|
||||||
});
|
});
|
||||||
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,12 +241,36 @@ 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);
|
||||||
|
|
||||||
_conditionSateChanged = _lastMatched != _matched;
|
_actionModeMatch = false;
|
||||||
|
switch (_actionTriggerMode) {
|
||||||
|
case Macro::ActionTriggerMode::ALWAYS:
|
||||||
|
_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)
|
const bool hasActionsToExecute = _matched ? (_actions.size() > 0)
|
||||||
: (_elseActions.size() > 0);
|
: (_elseActions.size() > 0);
|
||||||
if (!_conditionSateChanged && _performActionsOnChange &&
|
if (!_actionModeMatch && hasActionsToExecute) {
|
||||||
hasActionsToExecute) {
|
_lastActionRunModePreventTime =
|
||||||
_lastOnChangeActionsPreventedTime =
|
|
||||||
std::chrono::high_resolution_clock::now();
|
std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,9 +330,9 @@ bool Macro::WasExecutedSince(const TimePoint &time) const
|
|||||||
return _lastExecutionTime > time;
|
return _lastExecutionTime > time;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Macro::OnChangePreventedActionsSince(const TimePoint &time) const
|
bool Macro::ActionTriggerModePreventedActionsSince(const TimePoint &time) const
|
||||||
{
|
{
|
||||||
return _lastOnChangeActionsPreventedTime > time;
|
return _lastActionRunModePreventTime > time;
|
||||||
}
|
}
|
||||||
|
|
||||||
Macro::TimePoint Macro::GetLastExecutionTime() const
|
Macro::TimePoint Macro::GetLastExecutionTime() const
|
||||||
@@ -342,10 +366,9 @@ bool Macro::ShouldRunActions() const
|
|||||||
|
|
||||||
const bool hasActionsToExecute =
|
const bool hasActionsToExecute =
|
||||||
!_paused && (_matched || _elseActions.size() > 0) &&
|
!_paused && (_matched || _elseActions.size() > 0) &&
|
||||||
(!_performActionsOnChange || _conditionSateChanged);
|
_actionModeMatch;
|
||||||
|
|
||||||
if (VerboseLoggingEnabled() && _performActionsOnChange &&
|
if (VerboseLoggingEnabled() && !_actionModeMatch) {
|
||||||
!_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());
|
||||||
@@ -376,10 +399,24 @@ 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;
|
||||||
@@ -429,11 +466,6 @@ 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;
|
||||||
@@ -754,7 +786,8 @@ 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_bool(obj, "onChange", _performActionsOnChange);
|
obs_data_set_int(obj, "actionTriggerMode",
|
||||||
|
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",
|
||||||
@@ -840,7 +873,15 @@ 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");
|
||||||
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
|
if (obs_data_has_user_value(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 =
|
||||||
@@ -1100,9 +1141,11 @@ 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
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ 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);
|
||||||
@@ -54,8 +64,8 @@ public:
|
|||||||
bool GetStop() const { return _stop; }
|
bool GetStop() const { return _stop; }
|
||||||
void ResetTimers();
|
void ResetTimers();
|
||||||
|
|
||||||
void SetMatchOnChange(bool onChange);
|
void SetActionTriggerMode(ActionTriggerMode);
|
||||||
bool MatchOnChange() const { return _performActionsOnChange; }
|
ActionTriggerMode GetActionTriggerMode() const;
|
||||||
|
|
||||||
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
|
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
|
||||||
bool SkipExecOnStart() const { return _skipExecOnStart; }
|
bool SkipExecOnStart() const { return _skipExecOnStart; }
|
||||||
@@ -137,7 +147,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 OnChangePreventedActionsSince(const TimePoint &) const;
|
bool ActionTriggerModePreventedActionsSince(const TimePoint &) const;
|
||||||
TimePoint GetLastExecutionTime() const;
|
TimePoint GetLastExecutionTime() const;
|
||||||
void ResetUIHelpers();
|
void ResetUIHelpers();
|
||||||
|
|
||||||
@@ -169,7 +179,7 @@ private:
|
|||||||
TimePoint _lastCheckTime{};
|
TimePoint _lastCheckTime{};
|
||||||
TimePoint _lastUnpauseTime{};
|
TimePoint _lastUnpauseTime{};
|
||||||
TimePoint _lastExecutionTime{};
|
TimePoint _lastExecutionTime{};
|
||||||
TimePoint _lastOnChangeActionsPreventedTime{};
|
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;
|
||||||
@@ -184,14 +194,13 @@ private:
|
|||||||
bool _useShortCircuitEvaluation = false;
|
bool _useShortCircuitEvaluation = false;
|
||||||
bool _useCustomConditionCheckInterval = false;
|
bool _useCustomConditionCheckInterval = false;
|
||||||
Duration _customConditionCheckInterval = 0.3;
|
Duration _customConditionCheckInterval = 0.3;
|
||||||
bool _conditionSateChanged = false;
|
bool _actionModeMatch = 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;
|
||||||
@@ -201,6 +210,9 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ 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;
|
||||||
|
|||||||
@@ -8,13 +8,18 @@
|
|||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <util/config-file.h>
|
#include <util/config-file.h>
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
void AskForBackup(const QString &json)
|
static void showBackupDialogs(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);
|
||||||
@@ -42,6 +47,27 @@ void AskForBackup(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();
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <QString>
|
#include <obs-data.h>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
void AskForBackup(const QString &json);
|
void AskForBackup(obs_data_t *settings);
|
||||||
void BackupSettingsOfCurrentVersion();
|
void BackupSettingsOfCurrentVersion();
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@@ -104,10 +106,13 @@ static bool wasUncleanShutdown()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool askForStartupSkip()
|
static void askForStartupSkip()
|
||||||
{
|
{
|
||||||
return DisplayMessage(obs_module_text("AdvSceneSwitcher.crashDetected"),
|
bool skipStart = DisplayMessage(
|
||||||
true, false);
|
obs_module_text("AdvSceneSwitcher.crashDetected"), true, false);
|
||||||
|
if (!skipStart) {
|
||||||
|
StartPlugin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShouldSkipPluginStartOnUncleanShutdown()
|
bool ShouldSkipPluginStartOnUncleanShutdown()
|
||||||
@@ -116,18 +121,18 @@ bool ShouldSkipPluginStartOnUncleanShutdown()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread t([]() {
|
// This function is called while the plugin settings are being loaded.
|
||||||
obs_queue_task(
|
// Blocking at this stage can cause issues such as OBS failing to start
|
||||||
OBS_TASK_UI,
|
// or crashing.
|
||||||
[](void *) {
|
// Therefore, we ask the user whether they want to start the plugin
|
||||||
const bool skipStart = askForStartupSkip();
|
// asynchronously.
|
||||||
if (!skipStart) {
|
static const auto showDialogWrapper = [](void *) {
|
||||||
StartPlugin();
|
askForStartupSkip();
|
||||||
}
|
};
|
||||||
},
|
|
||||||
nullptr, false);
|
AddFinishedLoadingStep([]() {
|
||||||
|
obs_queue_task(OBS_TASK_UI, showDialogWrapper, nullptr, false);
|
||||||
});
|
});
|
||||||
t.detach();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
#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
|
||||||
@@ -19,5 +12,3 @@
|
|||||||
#else
|
#else
|
||||||
#define ADVSS_EXPORT Q_DECL_IMPORT
|
#define ADVSS_EXPORT Q_DECL_IMPORT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // UNIT_TEST
|
|
||||||
|
|||||||
465
lib/utils/first-run-wizard.cpp
Normal file
465
lib/utils/first-run-wizard.cpp
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
#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> ¯o)
|
||||||
|
: 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> ¯o,
|
||||||
|
const std::string ¯oName,
|
||||||
|
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
|
||||||
128
lib/utils/first-run-wizard.hpp
Normal file
128
lib/utils/first-run-wizard.hpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#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> ¯o);
|
||||||
|
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> ¯o, const std::string ¯oName,
|
||||||
|
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
|
||||||
@@ -4,9 +4,11 @@
|
|||||||
#include "ui-helpers.hpp"
|
#include "ui-helpers.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QMenu>
|
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(advss::Item *);
|
Q_DECLARE_METATYPE(advss::Item *);
|
||||||
|
|
||||||
@@ -330,6 +332,13 @@ 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()) {
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ 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;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#ifndef UNIT_TEST
|
|
||||||
#include <util/base.h>
|
#include <util/base.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
@@ -9,6 +7,7 @@ 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
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,42 @@
|
|||||||
#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;
|
||||||
@@ -63,6 +93,12 @@ 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);
|
||||||
@@ -136,7 +172,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(initMutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
getPluginPostLoadSteps().emplace_back(step);
|
getPluginPostLoadSteps().emplace_back(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +214,16 @@ 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);
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ 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();
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ void CenterSplitterPosition(QSplitter *splitter)
|
|||||||
|
|
||||||
void SetSplitterPositionByFraction(QSplitter *splitter, double fraction)
|
void SetSplitterPositionByFraction(QSplitter *splitter, double fraction)
|
||||||
{
|
{
|
||||||
int value1 = (double)QWIDGETSIZE_MAX * fraction;
|
int value1 = (int)((double)QWIDGETSIZE_MAX * fraction);
|
||||||
int value2 = (double)QWIDGETSIZE_MAX * (1.0 - fraction);
|
int value2 = (int)((double)QWIDGETSIZE_MAX * (1.0 - fraction));
|
||||||
splitter->setSizes(QList<int>() << value1 << value2);
|
splitter->setSizes(QList<int>() << value1 << value2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,22 @@
|
|||||||
|
|
||||||
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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#include "export-symbol-helper.hpp"
|
#include "export-symbol-helper.hpp"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|||||||
@@ -81,13 +81,13 @@ void GenericVariableSpinbox::DisableVariableSelection()
|
|||||||
|
|
||||||
void GenericVariableSpinbox::setMinimum(double value)
|
void GenericVariableSpinbox::setMinimum(double value)
|
||||||
{
|
{
|
||||||
_fixedValueInt->setMinimum(value);
|
_fixedValueInt->setMinimum((int)value);
|
||||||
_fixedValueDouble->setMinimum(value);
|
_fixedValueDouble->setMinimum(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericVariableSpinbox::setMaximum(double value)
|
void GenericVariableSpinbox::setMaximum(double value)
|
||||||
{
|
{
|
||||||
_fixedValueInt->setMaximum(value);
|
_fixedValueInt->setMaximum((int)value);
|
||||||
_fixedValueDouble->setMaximum(value);
|
_fixedValueDouble->setMaximum(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ 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
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ void MacroActionFileEdit::UpdateEntryData()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||||
_filePath->SetPath(QString::fromStdString(_entryData->_file));
|
_filePath->SetPath(_entryData->_file);
|
||||||
_text->setPlainText(_entryData->_text);
|
_text->setPlainText(_entryData->_text);
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#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 "transition-helpers.hpp"
|
||||||
|
|
||||||
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
const std::string MacroActionSceneVisibility::id = "scene_visibility";
|
const std::string MacroActionSceneVisibility::id = "scene_visibility";
|
||||||
@@ -24,43 +27,137 @@ const static std::map<MacroActionSceneVisibility::Action, std::string>
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
struct TransitionRestoreContext;
|
||||||
|
static std::unordered_map<obs_sceneitem_t *, TransitionRestoreContext *>
|
||||||
|
restoreContexts;
|
||||||
|
static std::mutex restoreMutex;
|
||||||
|
|
||||||
struct TransitionRestoreContext {
|
struct TransitionRestoreContext {
|
||||||
|
public:
|
||||||
obs_sceneitem_t *item;
|
obs_sceneitem_t *item;
|
||||||
bool wasVisible;
|
bool wasVisible;
|
||||||
|
|
||||||
bool restoreTransition;
|
OBSSource originalTransition = nullptr;
|
||||||
OBSSource originalTransition;
|
|
||||||
bool restoreDuration;
|
|
||||||
uint32_t originalDuration;
|
uint32_t originalDuration;
|
||||||
|
|
||||||
|
obs_source_t *transition = nullptr;
|
||||||
signal_handler_t *sh = nullptr;
|
signal_handler_t *sh = nullptr;
|
||||||
|
|
||||||
~TransitionRestoreContext()
|
|
||||||
{
|
|
||||||
if (!sh) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
signal_handler_disconnect(
|
|
||||||
sh, "transition_stop",
|
|
||||||
&TransitionRestoreContext::ResetSceneItemTransition,
|
|
||||||
this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ResetSceneItemTransition(void *param, calldata_t *)
|
|
||||||
{
|
|
||||||
auto *ctx = static_cast<TransitionRestoreContext *>(param);
|
|
||||||
SetSceneItemTransition(ctx->item, ctx->originalTransition,
|
|
||||||
!ctx->wasVisible);
|
|
||||||
|
|
||||||
obs_sceneitem_set_transition_duration(
|
|
||||||
ctx->item, !ctx->wasVisible, ctx->originalDuration);
|
|
||||||
delete ctx;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // 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 bool setTransition,
|
||||||
const OBSWeakSource &transitionWeak,
|
const OBSWeakSource &transitionWeak,
|
||||||
@@ -76,7 +173,7 @@ static void setSceneItemVisibility(obs_sceneitem_t *item,
|
|||||||
const uint32_t currentTransitionDuration =
|
const uint32_t currentTransitionDuration =
|
||||||
obs_sceneitem_get_transition_duration(item, !itemIsVisible);
|
obs_sceneitem_get_transition_duration(item, !itemIsVisible);
|
||||||
|
|
||||||
obs_source_t *privateTransitionSource = nullptr;
|
OBSSource privateTransitionSource = nullptr;
|
||||||
if (setTransition) {
|
if (setTransition) {
|
||||||
privateTransitionSource = SetSceneItemTransition(
|
privateTransitionSource = SetSceneItemTransition(
|
||||||
item, transition, !itemIsVisible);
|
item, transition, !itemIsVisible);
|
||||||
@@ -102,6 +199,10 @@ static void setSceneItemVisibility(obs_sceneitem_t *item,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!setTransition && !setDuration) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!privateTransitionSource) {
|
if (!privateTransitionSource) {
|
||||||
if (setTransition) {
|
if (setTransition) {
|
||||||
SetSceneItemTransition(item, currentTransition,
|
SetSceneItemTransition(item, currentTransition,
|
||||||
@@ -120,17 +221,8 @@ static void setSceneItemVisibility(obs_sceneitem_t *item,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = new TransitionRestoreContext{item,
|
attachRestoreContext(item, privateTransitionSource, itemIsVisible,
|
||||||
itemIsVisible,
|
currentTransition, currentTransitionDuration);
|
||||||
setTransition,
|
|
||||||
currentTransition,
|
|
||||||
setDuration,
|
|
||||||
currentTransitionDuration,
|
|
||||||
sh};
|
|
||||||
|
|
||||||
signal_handler_connect(
|
|
||||||
sh, "transition_stop",
|
|
||||||
&TransitionRestoreContext::ResetSceneItemTransition, ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroActionSceneVisibility::PerformAction()
|
bool MacroActionSceneVisibility::PerformAction()
|
||||||
|
|||||||
@@ -38,32 +38,13 @@ 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 (_dayOfWeek != Day::ANY &&
|
if (!_days.contains(static_cast<DayOfWeekSelector::Day>(
|
||||||
cur.date().dayOfWeek() != static_cast<int>(_dayOfWeek)) {
|
cur.date().dayOfWeek()))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (_ignoreTime) {
|
if (_ignoreTime) {
|
||||||
@@ -201,7 +182,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);
|
||||||
obs_data_set_int(obj, "dayOfWeek", static_cast<int>(_dayOfWeek));
|
SaveSelectedDays(obj, _days);
|
||||||
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;
|
||||||
@@ -216,13 +197,14 @@ 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);
|
||||||
_dayOfWeek = static_cast<Day>(obs_data_get_int(obj, "dayOfWeek"));
|
_days = LoadSelectedDays(obj);
|
||||||
_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(
|
||||||
@@ -245,17 +227,64 @@ 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) {
|
||||||
auto it = dayOfWeekNames.find(_dayOfWeek);
|
const auto dayName =
|
||||||
if (it == dayOfWeekNames.end()) {
|
DayOfWeekSelector::ToString(_days).toStdString();
|
||||||
return "";
|
|
||||||
}
|
|
||||||
std::string dayName = obs_module_text(it->second.c_str());
|
|
||||||
if (_ignoreTime) {
|
if (_ignoreTime) {
|
||||||
return dayName;
|
return dayName;
|
||||||
}
|
}
|
||||||
@@ -347,13 +376,6 @@ 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) {
|
||||||
@@ -372,7 +394,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()),
|
||||||
_dayOfWeek(new QComboBox()),
|
_days(new DayOfWeekSelector(this)),
|
||||||
_ignoreWeekTime(new QCheckBox()),
|
_ignoreWeekTime(new QCheckBox()),
|
||||||
_weekTime(new QTimeEdit()),
|
_weekTime(new QTimeEdit()),
|
||||||
_condition(new QComboBox()),
|
_condition(new QComboBox()),
|
||||||
@@ -392,7 +414,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 QHBoxLayout()),
|
_simpleLayout(new QVBoxLayout()),
|
||||||
_advancedLayout(new QHBoxLayout()),
|
_advancedLayout(new QHBoxLayout()),
|
||||||
_repeatLayout(new QVBoxLayout()),
|
_repeatLayout(new QVBoxLayout()),
|
||||||
_repeatUpdateLayout(new QHBoxLayout()),
|
_repeatUpdateLayout(new QHBoxLayout()),
|
||||||
@@ -418,8 +440,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(_dayOfWeek, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_days, &DayOfWeekSelector::SelectionChanged, this,
|
||||||
SLOT(DayOfWeekChanged(int)));
|
&MacroConditionDateEdit::DaysChanged);
|
||||||
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,
|
||||||
@@ -449,13 +471,12 @@ 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}}", _dayOfWeek},
|
{"{{dayOfWeek}}", _days},
|
||||||
{"{{ignoreWeekTime}}", _ignoreWeekTime},
|
{"{{ignoreWeekTime}}", _ignoreWeekTime},
|
||||||
{"{{weekTime}}", _weekTime},
|
{"{{weekTime}}", _weekTime},
|
||||||
{"{{condition}}", _condition},
|
{"{{condition}}", _condition},
|
||||||
@@ -472,28 +493,39 @@ MacroConditionDateEdit::MacroConditionDateEdit(
|
|||||||
{"{{pattern}}", _pattern},
|
{"{{pattern}}", _pattern},
|
||||||
{"{{currentDate}}", _currentDate},
|
{"{{currentDate}}", _currentDate},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto dayLayout = new QHBoxLayout;
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text("AdvSceneSwitcher.condition.date.entry.simple"),
|
obs_module_text(
|
||||||
_simpleLayout, widgetPlaceholders);
|
"AdvSceneSwitcher.condition.date.layout.simple.day"),
|
||||||
|
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.entry.advanced"),
|
"AdvSceneSwitcher.condition.date.layout.advanced"),
|
||||||
_advancedLayout, widgetPlaceholders);
|
_advancedLayout, widgetPlaceholders);
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.date.entry.updateOnRepeat"),
|
"AdvSceneSwitcher.condition.date.layout.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.entry.pattern"),
|
"AdvSceneSwitcher.condition.date.layout.repeat"),
|
||||||
|
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);
|
||||||
@@ -515,10 +547,11 @@ MacroConditionDateEdit::MacroConditionDateEdit(
|
|||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionDateEdit::DayOfWeekChanged(int day)
|
void MacroConditionDateEdit::DaysChanged(
|
||||||
|
const QSet<DayOfWeekSelector::Day> &days)
|
||||||
{
|
{
|
||||||
GUARD_LOADING_AND_LOCK();
|
GUARD_LOADING_AND_LOCK();
|
||||||
_entryData->_dayOfWeek = static_cast<MacroConditionDate::Day>(day);
|
_entryData->_days = days;
|
||||||
emit HeaderInfoChanged(
|
emit HeaderInfoChanged(
|
||||||
QString::fromStdString(_entryData->GetShortDesc()));
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
}
|
}
|
||||||
@@ -632,7 +665,7 @@ void MacroConditionDateEdit::ShowNextMatch()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString format(obs_module_text(
|
QString format(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.date.entry.nextMatchDate"));
|
"AdvSceneSwitcher.condition.date.layout.nextMatchDate"));
|
||||||
_nextMatchDate->setText(
|
_nextMatchDate->setText(
|
||||||
format.arg(_entryData->GetNextMatchDateTime().toString()));
|
format.arg(_entryData->GetNextMatchDateTime().toString()));
|
||||||
}
|
}
|
||||||
@@ -658,8 +691,7 @@ void MacroConditionDateEdit::UpdateEntryData()
|
|||||||
}
|
}
|
||||||
_weekCondition->setCurrentIndex(
|
_weekCondition->setCurrentIndex(
|
||||||
static_cast<int>(_entryData->_condition));
|
static_cast<int>(_entryData->_condition));
|
||||||
_dayOfWeek->setCurrentIndex(
|
_days->SetSelectedDays(_entryData->_days);
|
||||||
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));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#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>
|
||||||
@@ -30,17 +31,6 @@ 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,
|
||||||
@@ -49,7 +39,11 @@ public:
|
|||||||
PATTERN,
|
PATTERN,
|
||||||
};
|
};
|
||||||
|
|
||||||
Day _dayOfWeek = Day::ANY;
|
QSet<DayOfWeekSelector::Day> _days = {
|
||||||
|
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;
|
||||||
@@ -94,7 +88,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void DayOfWeekChanged(int day);
|
void DaysChanged(const QSet<DayOfWeekSelector::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);
|
||||||
@@ -114,7 +108,7 @@ signals:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
QComboBox *_weekCondition;
|
QComboBox *_weekCondition;
|
||||||
QComboBox *_dayOfWeek;
|
DayOfWeekSelector *_days;
|
||||||
QCheckBox *_ignoreWeekTime;
|
QCheckBox *_ignoreWeekTime;
|
||||||
QTimeEdit *_weekTime;
|
QTimeEdit *_weekTime;
|
||||||
|
|
||||||
@@ -134,7 +128,7 @@ protected:
|
|||||||
QLabel *_currentDate;
|
QLabel *_currentDate;
|
||||||
|
|
||||||
QPushButton *_advancedSettingsTooggle;
|
QPushButton *_advancedSettingsTooggle;
|
||||||
QHBoxLayout *_simpleLayout;
|
QVBoxLayout *_simpleLayout;
|
||||||
QHBoxLayout *_advancedLayout;
|
QHBoxLayout *_advancedLayout;
|
||||||
QVBoxLayout *_repeatLayout;
|
QVBoxLayout *_repeatLayout;
|
||||||
QHBoxLayout *_repeatUpdateLayout;
|
QHBoxLayout *_repeatUpdateLayout;
|
||||||
|
|||||||
121
plugins/base/utils/day-of-week-selector.cpp
Normal file
121
plugins/base/utils/day-of-week-selector.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#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
|
||||||
45
plugins/base/utils/day-of-week-selector.hpp
Normal file
45
plugins/base/utils/day-of-week-selector.hpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#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
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
#include "text-helpers.hpp"
|
#include "text-helpers.hpp"
|
||||||
|
|
||||||
#include <regex>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
QString EscapeForRegex(const QString &s)
|
QString EscapeForRegex(const QString &input)
|
||||||
{
|
{
|
||||||
static std::regex specialChars{R"([-[\]{}()*+?.,\^$|#\s])"};
|
return QRegularExpression::escape(input);
|
||||||
std::string input = s.toStdString();
|
|
||||||
return QString::fromStdString(
|
|
||||||
std::regex_replace(input, specialChars, R"(\$&)"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -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 && !message.keyDown));
|
(!message.keyDown && !_pattern.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);
|
||||||
|
|||||||
@@ -7,7 +7,11 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
@@ -24,7 +28,7 @@ static bool setup()
|
|||||||
AddSetupTabCallback("twitchConnectionTab",
|
AddSetupTabCallback("twitchConnectionTab",
|
||||||
TwitchConnectionsTable::Create, setupTab);
|
TwitchConnectionsTable::Create, setupTab);
|
||||||
|
|
||||||
static const auto showInvalidWarnings = []() {
|
static const auto showInvalidWarnings = [](void *) {
|
||||||
const auto invalidTokens = getInvalidTokens();
|
const auto invalidTokens = getInvalidTokens();
|
||||||
for (const auto &token : invalidTokens) {
|
for (const auto &token : invalidTokens) {
|
||||||
QueueUITask(
|
QueueUITask(
|
||||||
@@ -38,8 +42,14 @@ static bool setup()
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
AddLoadStep([](obs_data_t *) {
|
// This function is called while the plugin settings are being loaded.
|
||||||
AddPostLoadStep([]() { showInvalidWarnings(); });
|
// Blocking at this stage can cause issues such as OBS failing to start
|
||||||
|
// 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;
|
||||||
|
|||||||
@@ -273,15 +273,18 @@ void MacroConditionVideo::SetCondition(VideoCondition condition)
|
|||||||
bool MacroConditionVideo::ScreenshotContainsPattern()
|
bool MacroConditionVideo::ScreenshotContainsPattern()
|
||||||
{
|
{
|
||||||
cv::Mat result;
|
cv::Mat result;
|
||||||
MatchPattern(_screenshotData.GetImage(), _patternImageData,
|
double bestMatchValue =
|
||||||
_patternMatchParameters.threshold, result, nullptr,
|
MatchPattern(_screenshotData.GetImage(), _patternImageData,
|
||||||
_patternMatchParameters.useAlphaAsMask,
|
_patternMatchParameters.threshold, result,
|
||||||
_patternMatchParameters.matchMode);
|
_patternMatchParameters.useAlphaAsMask,
|
||||||
|
_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;
|
||||||
}
|
}
|
||||||
@@ -304,10 +307,12 @@ bool MacroConditionVideo::OutputChanged()
|
|||||||
|
|
||||||
cv::Mat result;
|
cv::Mat result;
|
||||||
_patternImageData = CreatePatternData(_matchImage);
|
_patternImageData = CreatePatternData(_matchImage);
|
||||||
MatchPattern(_screenshotData.GetImage(), _patternImageData,
|
double bestMatchValue =
|
||||||
_patternMatchParameters.threshold, result, nullptr,
|
MatchPattern(_screenshotData.GetImage(), _patternImageData,
|
||||||
_patternMatchParameters.useAlphaAsMask,
|
_patternMatchParameters.threshold, result,
|
||||||
_patternMatchParameters.matchMode);
|
_patternMatchParameters.useAlphaAsMask,
|
||||||
|
_patternMatchParameters.matchMode);
|
||||||
|
SetTempVarValue("similarity", std::to_string(bestMatchValue));
|
||||||
if (result.total() == 0) {
|
if (result.total() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -411,7 +416,25 @@ 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(
|
||||||
@@ -451,8 +474,6 @@ 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;
|
||||||
@@ -1477,6 +1498,7 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ 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");
|
||||||
@@ -89,8 +90,6 @@ 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;
|
||||||
|
|||||||
@@ -48,20 +48,18 @@ static void preprocessPatternMatchResult(cv::Mat &mat, bool invert)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatchPattern(QImage &img, const PatternImageData &patternData,
|
double MatchPattern(QImage &img, const PatternImageData &patternData,
|
||||||
double threshold, cv::Mat &result, double *pBestFitValue,
|
double threshold, cv::Mat &result, bool useAlphaAsMask,
|
||||||
bool useAlphaAsMask, cv::TemplateMatchModes matchMode)
|
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;
|
return bestFitValue;
|
||||||
}
|
}
|
||||||
if (img.height() < patternData.rgbaPattern.rows ||
|
if (img.height() < patternData.rgbaPattern.rows ||
|
||||||
img.width() < patternData.rgbaPattern.cols) {
|
img.width() < patternData.rgbaPattern.cols) {
|
||||||
return;
|
return bestFitValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto input = QImageToMat(img);
|
auto input = QImageToMat(img);
|
||||||
@@ -94,20 +92,18 @@ void 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);
|
||||||
|
|
||||||
if (pBestFitValue) {
|
cv::minMaxLoc(result, nullptr, &bestFitValue);
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatchPattern(QImage &img, QImage &pattern, double threshold,
|
double MatchPattern(QImage &img, QImage &pattern, double threshold,
|
||||||
cv::Mat &result, double *pBestFitValue, bool useAlphaAsMask,
|
cv::Mat &result, bool useAlphaAsMask,
|
||||||
cv::TemplateMatchModes matchColor)
|
cv::TemplateMatchModes matchColor)
|
||||||
{
|
{
|
||||||
auto data = CreatePatternData(pattern);
|
auto data = CreatePatternData(pattern);
|
||||||
MatchPattern(img, data, threshold, result, pBestFitValue,
|
return MatchPattern(img, data, threshold, result, useAlphaAsMask,
|
||||||
useAlphaAsMask, matchColor);
|
matchColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade,
|
std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade,
|
||||||
|
|||||||
@@ -62,12 +62,12 @@ struct PatternImageData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PatternImageData CreatePatternData(const QImage &pattern);
|
PatternImageData CreatePatternData(const QImage &pattern);
|
||||||
void MatchPattern(QImage &img, const PatternImageData &patternData,
|
double MatchPattern(QImage &img, const PatternImageData &patternData,
|
||||||
double threshold, cv::Mat &result, double *pBestFitValue,
|
double threshold, cv::Mat &result, bool useAlphaAsMask,
|
||||||
bool useAlphaAsMask, cv::TemplateMatchModes matchMode);
|
cv::TemplateMatchModes matchMode);
|
||||||
void MatchPattern(QImage &img, QImage &pattern, double threshold,
|
double MatchPattern(QImage &img, QImage &pattern, double threshold,
|
||||||
cv::Mat &result, double *pBestFitValue, bool useAlphaAsMask,
|
cv::Mat &result, 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,
|
||||||
|
|||||||
@@ -329,10 +329,10 @@ void PreviewImage::MarkPatternMatch(
|
|||||||
const PatternImageData &patternImageData)
|
const PatternImageData &patternImageData)
|
||||||
{
|
{
|
||||||
cv::Mat result;
|
cv::Mat result;
|
||||||
double matchValue = std::numeric_limits<double>::signaling_NaN();
|
double matchValue = MatchPattern(screenshot, patternImageData,
|
||||||
MatchPattern(screenshot, patternImageData, patternMatchParams.threshold,
|
patternMatchParams.threshold, result,
|
||||||
result, &matchValue, patternMatchParams.useAlphaAsMask,
|
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(
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
cmake_minimum_required(VERSION 3.14)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
project(advanced-scene-switcher-tests)
|
project(advanced-scene-switcher-tests)
|
||||||
|
|
||||||
get_target_property(ADVSS_SOURCE_DIR advanced-scene-switcher-lib SOURCE_DIR)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME})
|
add_executable(${PROJECT_NAME})
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE UNIT_TEST)
|
target_compile_definitions(${PROJECT_NAME} PRIVATE UNIT_TEST
|
||||||
|
ADVSS_EXPORT_SYMBOLS=1)
|
||||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
|
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
|
||||||
|
|
||||||
target_sources(
|
target_sources(${PROJECT_NAME} PRIVATE test-main.cpp)
|
||||||
${PROJECT_NAME} PRIVATE test-main.cpp mocks/obs-data.cpp
|
|
||||||
mocks/path-helpers.cpp mocks/ui-helpers.cpp)
|
get_target_property(ADVSS_SOURCE_DIR advanced-scene-switcher-lib SOURCE_DIR)
|
||||||
|
get_target_property(ADVSS_BINARY_DIR advanced-scene-switcher-lib BINARY_DIR)
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
PRIVATE mocks ${ADVSS_SOURCE_DIR}/lib/utils ${ADVSS_SOURCE_DIR}/lib/variables
|
PRIVATE ${ADVSS_SOURCE_DIR}/lib ${ADVSS_SOURCE_DIR}/lib/macro
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils ${ADVSS_SOURCE_DIR}/lib/variables
|
||||||
${ADVSS_SOURCE_DIR}/plugins/base/utils)
|
${ADVSS_SOURCE_DIR}/plugins/base/utils)
|
||||||
|
|
||||||
# --- Qt --- #
|
setup_obs_lib_dependency(${PROJECT_NAME})
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC Qt::Core Qt::Widgets)
|
# --- Qt --- #
|
||||||
|
|
||||||
if(TARGET Qt6::Core)
|
if(TARGET Qt6::Core)
|
||||||
set(_QT_VERSION
|
set(_QT_VERSION
|
||||||
@@ -28,25 +33,26 @@ elseif(TARGET Qt5::Core)
|
|||||||
CACHE INTERNAL "")
|
CACHE INTERNAL "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
macro(add_qt_lib lib_name)
|
# Include autogen headers so that generated ui_*.h files are found
|
||||||
add_custom_command(
|
target_include_directories(
|
||||||
TARGET ${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
POST_BUILD
|
PRIVATE "${ADVSS_BINARY_DIR}/advanced-scene-switcher-lib_autogen/include")
|
||||||
COMMAND
|
foreach(_conf Release RelWithDebInfo Debug MinSizeRe)
|
||||||
${CMAKE_COMMAND} -E copy_if_different
|
target_include_directories(
|
||||||
$<TARGET_FILE:Qt${_QT_VERSION}::${lib_name}>
|
${PROJECT_NAME}
|
||||||
$<TARGET_FILE_DIR:${PROJECT_NAME}>)
|
PRIVATE
|
||||||
endmacro()
|
"${ADVSS_BINARY_DIR}/advanced-scene-switcher-lib_autogen/include_${_conf}"
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
add_qt_lib(Core)
|
target_link_libraries(${PROJECT_NAME} PUBLIC Qt::Core Qt::Widgets)
|
||||||
add_qt_lib(Gui)
|
|
||||||
add_qt_lib(Widgets)
|
|
||||||
|
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
PROPERTIES AUTOMOC ON
|
PROPERTIES AUTOMOC ON
|
||||||
AUTOUIC ON
|
AUTOUIC ON
|
||||||
AUTORCC ON)
|
AUTORCC ON
|
||||||
|
AUTOUIC_SEARCH_PATHS "${ADVSS_SOURCE_DIR}/forms")
|
||||||
|
|
||||||
# --- condition-logic --- #
|
# --- condition-logic --- #
|
||||||
|
|
||||||
@@ -83,12 +89,11 @@ target_include_directories(${PROJECT_NAME}
|
|||||||
PRIVATE ${ADVSS_SOURCE_DIR}/deps/exprtk)
|
PRIVATE ${ADVSS_SOURCE_DIR}/deps/exprtk)
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_compile_options(${PROJECT_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267
|
target_compile_options(${PROJECT_NAME} PUBLIC /MP /d2FH4- /wd4267 /bigobj)
|
||||||
/bigobj)
|
|
||||||
else()
|
else()
|
||||||
target_compile_options(
|
target_compile_options(
|
||||||
${PROJECT_NAME} PUBLIC -Wno-error=unused-parameter -Wno-error=conversion
|
${PROJECT_NAME} PUBLIC -Wno-error=unused-parameter -Wno-error=conversion
|
||||||
-Wno-error=unused-value)
|
-Wno-error=unused-value -Wno-error=unused-variable)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# --- regex --- #
|
# --- regex --- #
|
||||||
@@ -122,6 +127,107 @@ target_sources(
|
|||||||
${ADVSS_SOURCE_DIR}/lib/utils/resizing-text-edit.cpp
|
${ADVSS_SOURCE_DIR}/lib/utils/resizing-text-edit.cpp
|
||||||
${ADVSS_SOURCE_DIR}/lib/variables/variable.cpp)
|
${ADVSS_SOURCE_DIR}/lib/variables/variable.cpp)
|
||||||
|
|
||||||
# --- #
|
# --- macro / macro-condition --- #
|
||||||
|
|
||||||
|
target_sources(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
PRIVATE test-macro.cpp
|
||||||
|
test-macro-condition.cpp
|
||||||
|
stubs/path-helpers.cpp
|
||||||
|
stubs/ui-helpers.cpp
|
||||||
|
stubs/plugin-state-helpers.cpp
|
||||||
|
stubs/macro-action-macro.cpp
|
||||||
|
stubs/macro-edit.cpp
|
||||||
|
stubs/macro-dock-settings.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/temp-variable.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/sync-helpers.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/help-icon.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/auto-update-tooltip-label.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/duration-control.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/duration-control.hpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/duration-modifier.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/duration-modifier.hpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/duration.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/duration.hpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/list-controls.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/list-editor.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/layout-helpers.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/log-helper.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/mouse-wheel-guard.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/section.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/splitter-helpers.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/resizable-widget.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/utils/string-list.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-action.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-action-macro.hpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-action-factory.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-condition.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-condition-factory.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-edit.hpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-helpers.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-input.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-ref.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-segment.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-selection.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro-signals.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/macro/macro.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/variables/variable-line-edit.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/variables/variable-spinbox.cpp
|
||||||
|
${ADVSS_SOURCE_DIR}/lib/variables/variable-string.cpp)
|
||||||
|
|
||||||
|
# --- Testing --- #
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
# Copy all CMake-known transitive DLLs (Qt, OBS, etc.) next to the binary.
|
||||||
|
# This makes the test executable runnable directly without PATH changes.
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT_NAME}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND
|
||||||
|
${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
$<TARGET_RUNTIME_DLLS:${PROJECT_NAME}> $<TARGET_FILE_DIR:${PROJECT_NAME}>
|
||||||
|
COMMAND_EXPAND_LISTS)
|
||||||
|
|
||||||
|
# Copy remaining OBS deps (zlib, srt, librist, ffmpeg, etc.) that are not
|
||||||
|
# CMake targets but are required at runtime. Derive the path from ZLIB::ZLIB
|
||||||
|
# which lives in the same deps directory.
|
||||||
|
get_target_property(_zlib_lib ZLIB::ZLIB IMPORTED_LOCATION_RELWITHDEBINFO)
|
||||||
|
if(NOT _zlib_lib)
|
||||||
|
get_target_property(_zlib_lib ZLIB::ZLIB IMPORTED_LOCATION_RELEASE)
|
||||||
|
endif()
|
||||||
|
if(_zlib_lib)
|
||||||
|
get_filename_component(_obs_deps_bin "${_zlib_lib}" DIRECTORY)
|
||||||
|
get_filename_component(_obs_deps_bin "${_obs_deps_bin}/../bin" ABSOLUTE)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT_NAME}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_obs_deps_bin}"
|
||||||
|
$<TARGET_FILE_DIR:${PROJECT_NAME}>)
|
||||||
|
else()
|
||||||
|
message(
|
||||||
|
WARNING "Could not determine OBS deps bin directory from ZLIB::ZLIB - "
|
||||||
|
"some DLLs may be missing at runtime")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
elseif(APPLE)
|
||||||
|
# On macOS, binaries use RPATH so copying is usually not needed. Fall back to
|
||||||
|
# DYLD_LIBRARY_PATH in case RPATH is not set correctly.
|
||||||
|
set_tests_properties(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
PROPERTIES
|
||||||
|
ENVIRONMENT
|
||||||
|
"DYLD_LIBRARY_PATH=$<TARGET_FILE_DIR:OBS::libobs>:$<TARGET_FILE_DIR:Qt${_QT_VERSION}::Core>"
|
||||||
|
)
|
||||||
|
|
||||||
|
else()
|
||||||
|
# On Linux, same approach using LD_LIBRARY_PATH.
|
||||||
|
set_tests_properties(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
PROPERTIES
|
||||||
|
ENVIRONMENT
|
||||||
|
"LD_LIBRARY_PATH=$<TARGET_FILE_DIR:OBS::libobs>:$<TARGET_FILE_DIR:Qt${_QT_VERSION}::Core>"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -1,502 +0,0 @@
|
|||||||
#include "obs-data.h"
|
|
||||||
|
|
||||||
struct obs_data_item {};
|
|
||||||
|
|
||||||
struct obs_data {};
|
|
||||||
|
|
||||||
struct obs_data_array {};
|
|
||||||
|
|
||||||
struct obs_data_number {};
|
|
||||||
|
|
||||||
obs_data_t *obs_data_create()
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_create_from_json(const char *json_string)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_create_from_json_file(const char *json_file)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_create_from_json_file_safe(const char *json_file,
|
|
||||||
const char *backup_ext)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_addref(obs_data_t *data) {}
|
|
||||||
|
|
||||||
void obs_data_release(obs_data_t *data) {}
|
|
||||||
|
|
||||||
const char *obs_data_get_json(obs_data_t *data)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_get_json_pretty(obs_data_t *data)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_get_last_json(obs_data_t *data)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_save_json(obs_data_t *data, const char *file)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_save_json_safe(obs_data_t *data, const char *file,
|
|
||||||
const char *temp_ext, const char *backup_ext)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_save_json_pretty_safe(obs_data_t *data, const char *file,
|
|
||||||
const char *temp_ext,
|
|
||||||
const char *backup_ext)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_get_defaults(obs_data_t *data)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_apply(obs_data_t *target, obs_data_t *apply_data) {}
|
|
||||||
|
|
||||||
void obs_data_erase(obs_data_t *data, const char *name) {}
|
|
||||||
|
|
||||||
void obs_data_clear(obs_data_t *target) {}
|
|
||||||
|
|
||||||
void obs_data_set_string(obs_data_t *data, const char *name, const char *val) {}
|
|
||||||
|
|
||||||
void obs_data_set_int(obs_data_t *data, const char *name, long long val) {}
|
|
||||||
|
|
||||||
void obs_data_set_double(obs_data_t *data, const char *name, double val) {}
|
|
||||||
|
|
||||||
void obs_data_set_bool(obs_data_t *data, const char *name, bool val) {}
|
|
||||||
|
|
||||||
void obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj) {}
|
|
||||||
|
|
||||||
void obs_data_set_array(obs_data_t *data, const char *name,
|
|
||||||
obs_data_array_t *array)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_default_string(obs_data_t *data, const char *name,
|
|
||||||
const char *val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_default_int(obs_data_t *data, const char *name, long long val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_default_double(obs_data_t *data, const char *name, double val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_default_bool(obs_data_t *data, const char *name, bool val) {}
|
|
||||||
|
|
||||||
void obs_data_set_default_obj(obs_data_t *data, const char *name,
|
|
||||||
obs_data_t *obj)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_default_array(obs_data_t *data, const char *name,
|
|
||||||
obs_data_array_t *arr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_autoselect_string(obs_data_t *data, const char *name,
|
|
||||||
const char *val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_autoselect_int(obs_data_t *data, const char *name,
|
|
||||||
long long val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_autoselect_double(obs_data_t *data, const char *name,
|
|
||||||
double val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_autoselect_bool(obs_data_t *data, const char *name, bool val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_autoselect_obj(obs_data_t *data, const char *name,
|
|
||||||
obs_data_t *obj)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_set_autoselect_array(obs_data_t *data, const char *name,
|
|
||||||
obs_data_array_t *arr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_get_string(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
long long obs_data_get_int(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double obs_data_get_double(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_get_bool(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_get_obj(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_get_default_string(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
long long obs_data_get_default_int(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double obs_data_get_default_double(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_get_default_bool(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_get_default_obj(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t *obs_data_get_default_array(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_get_autoselect_string(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
long long obs_data_get_autoselect_int(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double obs_data_get_autoselect_double(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_get_autoselect_bool(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_get_autoselect_obj(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t *obs_data_get_autoselect_array(obs_data_t *data,
|
|
||||||
const char *name)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t *obs_data_array_create()
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_array_addref(obs_data_array_t *array) {}
|
|
||||||
|
|
||||||
void obs_data_array_release(obs_data_array_t *array) {}
|
|
||||||
|
|
||||||
size_t obs_data_array_count(obs_data_array_t *array)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_array_push_back_array(obs_data_array_t *array,
|
|
||||||
obs_data_array_t *array2)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_array_erase(obs_data_array_t *array, size_t idx) {}
|
|
||||||
|
|
||||||
void obs_data_array_enum(obs_data_array_t *array,
|
|
||||||
void (*cb)(obs_data_t *data, void *param), void *param)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
/* Item status inspection */
|
|
||||||
|
|
||||||
bool obs_data_has_user_value(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_has_default_value(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_has_autoselect_value(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_item_has_user_value(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_item_has_default_value(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_item_has_autoselect_value(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
/* Clearing data values */
|
|
||||||
|
|
||||||
void obs_data_unset_user_value(obs_data_t *data, const char *name) {}
|
|
||||||
|
|
||||||
void obs_data_unset_default_value(obs_data_t *data, const char *name) {}
|
|
||||||
|
|
||||||
void obs_data_unset_autoselect_value(obs_data_t *data, const char *name) {}
|
|
||||||
|
|
||||||
void obs_data_item_unset_user_value(obs_data_item_t *item) {}
|
|
||||||
|
|
||||||
void obs_data_item_unset_default_value(obs_data_item_t *item) {}
|
|
||||||
|
|
||||||
void obs_data_item_unset_autoselect_value(obs_data_item_t *item) {}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
/* Item iteration */
|
|
||||||
|
|
||||||
obs_data_item_t *obs_data_first(obs_data_t *data)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_item_t *obs_data_item_byname(obs_data_t *data, const char *name)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_item_next(obs_data_item_t **item)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_item_release(obs_data_item_t **item) {}
|
|
||||||
|
|
||||||
void obs_data_item_remove(obs_data_item_t **item) {}
|
|
||||||
|
|
||||||
enum obs_data_type obs_data_item_gettype(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return OBS_DATA_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum obs_data_number_type obs_data_item_numtype(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return OBS_DATA_NUM_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_item_get_name(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_item_set_string(obs_data_item_t **item, const char *val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_int(obs_data_item_t **item, long long val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_double(obs_data_item_t **item, double val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_bool(obs_data_item_t **item, bool val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_obj(obs_data_item_t **item, obs_data_t *val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_array(obs_data_item_t **item, obs_data_array_t *val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_default_string(obs_data_item_t **item, const char *val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_item_set_default_int(obs_data_item_t **item, long long val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_default_double(obs_data_item_t **item, double val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_default_bool(obs_data_item_t **item, bool val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_default_obj(obs_data_item_t **item, obs_data_t *val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_default_array(obs_data_item_t **item,
|
|
||||||
obs_data_array_t *val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_item_set_autoselect_string(obs_data_item_t **item,
|
|
||||||
const char *val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_item_set_autoselect_int(obs_data_item_t **item, long long val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_autoselect_double(obs_data_item_t **item, double val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_autoselect_bool(obs_data_item_t **item, bool val) {}
|
|
||||||
|
|
||||||
void obs_data_item_set_autoselect_obj(obs_data_item_t **item, obs_data_t *val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void obs_data_item_set_autoselect_array(obs_data_item_t **item,
|
|
||||||
obs_data_array_t *val)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_item_get_string(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
long long obs_data_item_get_int(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double obs_data_item_get_double(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_item_get_bool(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_item_get_obj(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t *obs_data_item_get_array(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_item_get_default_string(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
long long obs_data_item_get_default_int(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double obs_data_item_get_default_double(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_item_get_default_bool(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_item_get_default_obj(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t *obs_data_item_get_default_array(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *obs_data_item_get_autoselect_string(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
long long obs_data_item_get_autoselect_int(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double obs_data_item_get_autoselect_double(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_data_item_get_autoselect_bool(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *obs_data_item_get_autoselect_obj(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t *obs_data_item_get_autoselect_array(obs_data_item_t *item)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "export-symbol-helper.hpp"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct obs_data;
|
|
||||||
struct obs_data_item;
|
|
||||||
struct obs_data_array;
|
|
||||||
typedef struct obs_data obs_data_t;
|
|
||||||
typedef struct obs_data_item obs_data_item_t;
|
|
||||||
typedef struct obs_data_array obs_data_array_t;
|
|
||||||
|
|
||||||
enum obs_data_type {
|
|
||||||
OBS_DATA_NULL,
|
|
||||||
OBS_DATA_STRING,
|
|
||||||
OBS_DATA_NUMBER,
|
|
||||||
OBS_DATA_BOOLEAN,
|
|
||||||
OBS_DATA_OBJECT,
|
|
||||||
OBS_DATA_ARRAY
|
|
||||||
};
|
|
||||||
|
|
||||||
enum obs_data_number_type {
|
|
||||||
OBS_DATA_NUM_INVALID,
|
|
||||||
OBS_DATA_NUM_INT,
|
|
||||||
OBS_DATA_NUM_DOUBLE
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
/* Main usage functions */
|
|
||||||
|
|
||||||
EXPORT obs_data_t *obs_data_create();
|
|
||||||
EXPORT obs_data_t *obs_data_create_from_json(const char *json_string);
|
|
||||||
EXPORT obs_data_t *obs_data_create_from_json_file(const char *json_file);
|
|
||||||
EXPORT obs_data_t *obs_data_create_from_json_file_safe(const char *json_file,
|
|
||||||
const char *backup_ext);
|
|
||||||
EXPORT void obs_data_addref(obs_data_t *data);
|
|
||||||
EXPORT void obs_data_release(obs_data_t *data);
|
|
||||||
|
|
||||||
EXPORT const char *obs_data_get_json(obs_data_t *data);
|
|
||||||
EXPORT const char *obs_data_get_json_pretty(obs_data_t *data);
|
|
||||||
EXPORT const char *obs_data_get_last_json(obs_data_t *data);
|
|
||||||
EXPORT bool obs_data_save_json(obs_data_t *data, const char *file);
|
|
||||||
EXPORT bool obs_data_save_json_safe(obs_data_t *data, const char *file,
|
|
||||||
const char *temp_ext,
|
|
||||||
const char *backup_ext);
|
|
||||||
EXPORT bool obs_data_save_json_pretty_safe(obs_data_t *data, const char *file,
|
|
||||||
const char *temp_ext,
|
|
||||||
const char *backup_ext);
|
|
||||||
|
|
||||||
EXPORT void obs_data_apply(obs_data_t *target, obs_data_t *apply_data);
|
|
||||||
|
|
||||||
EXPORT void obs_data_erase(obs_data_t *data, const char *name);
|
|
||||||
EXPORT void obs_data_clear(obs_data_t *data);
|
|
||||||
|
|
||||||
/* Set functions */
|
|
||||||
EXPORT void obs_data_set_string(obs_data_t *data, const char *name,
|
|
||||||
const char *val);
|
|
||||||
EXPORT void obs_data_set_int(obs_data_t *data, const char *name, long long val);
|
|
||||||
EXPORT void obs_data_set_double(obs_data_t *data, const char *name, double val);
|
|
||||||
EXPORT void obs_data_set_bool(obs_data_t *data, const char *name, bool val);
|
|
||||||
EXPORT void obs_data_set_obj(obs_data_t *data, const char *name,
|
|
||||||
obs_data_t *obj);
|
|
||||||
EXPORT void obs_data_set_array(obs_data_t *data, const char *name,
|
|
||||||
obs_data_array_t *array);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creates an obs_data_t * filled with all default values.
|
|
||||||
*/
|
|
||||||
EXPORT obs_data_t *obs_data_get_defaults(obs_data_t *data);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Default value functions.
|
|
||||||
*/
|
|
||||||
EXPORT void obs_data_set_default_string(obs_data_t *data, const char *name,
|
|
||||||
const char *val);
|
|
||||||
EXPORT void obs_data_set_default_int(obs_data_t *data, const char *name,
|
|
||||||
long long val);
|
|
||||||
EXPORT void obs_data_set_default_double(obs_data_t *data, const char *name,
|
|
||||||
double val);
|
|
||||||
EXPORT void obs_data_set_default_bool(obs_data_t *data, const char *name,
|
|
||||||
bool val);
|
|
||||||
EXPORT void obs_data_set_default_obj(obs_data_t *data, const char *name,
|
|
||||||
obs_data_t *obj);
|
|
||||||
EXPORT void obs_data_set_default_array(obs_data_t *data, const char *name,
|
|
||||||
obs_data_array_t *arr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Application overrides
|
|
||||||
* Use these to communicate the actual values of settings in case the user
|
|
||||||
* settings aren't appropriate
|
|
||||||
*/
|
|
||||||
EXPORT void obs_data_set_autoselect_string(obs_data_t *data, const char *name,
|
|
||||||
const char *val);
|
|
||||||
EXPORT void obs_data_set_autoselect_int(obs_data_t *data, const char *name,
|
|
||||||
long long val);
|
|
||||||
EXPORT void obs_data_set_autoselect_double(obs_data_t *data, const char *name,
|
|
||||||
double val);
|
|
||||||
EXPORT void obs_data_set_autoselect_bool(obs_data_t *data, const char *name,
|
|
||||||
bool val);
|
|
||||||
EXPORT void obs_data_set_autoselect_obj(obs_data_t *data, const char *name,
|
|
||||||
obs_data_t *obj);
|
|
||||||
EXPORT void obs_data_set_autoselect_array(obs_data_t *data, const char *name,
|
|
||||||
obs_data_array_t *arr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get functions
|
|
||||||
*/
|
|
||||||
EXPORT const char *obs_data_get_string(obs_data_t *data, const char *name);
|
|
||||||
EXPORT long long obs_data_get_int(obs_data_t *data, const char *name);
|
|
||||||
EXPORT double obs_data_get_double(obs_data_t *data, const char *name);
|
|
||||||
EXPORT bool obs_data_get_bool(obs_data_t *data, const char *name);
|
|
||||||
EXPORT obs_data_t *obs_data_get_obj(obs_data_t *data, const char *name);
|
|
||||||
EXPORT obs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name);
|
|
||||||
|
|
||||||
EXPORT const char *obs_data_get_default_string(obs_data_t *data,
|
|
||||||
const char *name);
|
|
||||||
EXPORT long long obs_data_get_default_int(obs_data_t *data, const char *name);
|
|
||||||
EXPORT double obs_data_get_default_double(obs_data_t *data, const char *name);
|
|
||||||
EXPORT bool obs_data_get_default_bool(obs_data_t *data, const char *name);
|
|
||||||
EXPORT obs_data_t *obs_data_get_default_obj(obs_data_t *data, const char *name);
|
|
||||||
EXPORT obs_data_array_t *obs_data_get_default_array(obs_data_t *data,
|
|
||||||
const char *name);
|
|
||||||
|
|
||||||
EXPORT const char *obs_data_get_autoselect_string(obs_data_t *data,
|
|
||||||
const char *name);
|
|
||||||
EXPORT long long obs_data_get_autoselect_int(obs_data_t *data,
|
|
||||||
const char *name);
|
|
||||||
EXPORT double obs_data_get_autoselect_double(obs_data_t *data,
|
|
||||||
const char *name);
|
|
||||||
EXPORT bool obs_data_get_autoselect_bool(obs_data_t *data, const char *name);
|
|
||||||
EXPORT obs_data_t *obs_data_get_autoselect_obj(obs_data_t *data,
|
|
||||||
const char *name);
|
|
||||||
EXPORT obs_data_array_t *obs_data_get_autoselect_array(obs_data_t *data,
|
|
||||||
const char *name);
|
|
||||||
|
|
||||||
/* Array functions */
|
|
||||||
EXPORT obs_data_array_t *obs_data_array_create();
|
|
||||||
EXPORT void obs_data_array_addref(obs_data_array_t *array);
|
|
||||||
EXPORT void obs_data_array_release(obs_data_array_t *array);
|
|
||||||
|
|
||||||
EXPORT size_t obs_data_array_count(obs_data_array_t *array);
|
|
||||||
EXPORT obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx);
|
|
||||||
EXPORT size_t obs_data_array_push_back(obs_data_array_t *array,
|
|
||||||
obs_data_t *obj);
|
|
||||||
EXPORT void obs_data_array_insert(obs_data_array_t *array, size_t idx,
|
|
||||||
obs_data_t *obj);
|
|
||||||
EXPORT void obs_data_array_push_back_array(obs_data_array_t *array,
|
|
||||||
obs_data_array_t *array2);
|
|
||||||
EXPORT void obs_data_array_erase(obs_data_array_t *array, size_t idx);
|
|
||||||
EXPORT void obs_data_array_enum(obs_data_array_t *array,
|
|
||||||
void (*cb)(obs_data_t *data, void *param),
|
|
||||||
void *param);
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
/* Item status inspection */
|
|
||||||
|
|
||||||
EXPORT bool obs_data_has_user_value(obs_data_t *data, const char *name);
|
|
||||||
EXPORT bool obs_data_has_default_value(obs_data_t *data, const char *name);
|
|
||||||
EXPORT bool obs_data_has_autoselect_value(obs_data_t *data, const char *name);
|
|
||||||
|
|
||||||
EXPORT bool obs_data_item_has_user_value(obs_data_item_t *data);
|
|
||||||
EXPORT bool obs_data_item_has_default_value(obs_data_item_t *data);
|
|
||||||
EXPORT bool obs_data_item_has_autoselect_value(obs_data_item_t *data);
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
/* Clearing data values */
|
|
||||||
|
|
||||||
EXPORT void obs_data_unset_user_value(obs_data_t *data, const char *name);
|
|
||||||
EXPORT void obs_data_unset_default_value(obs_data_t *data, const char *name);
|
|
||||||
EXPORT void obs_data_unset_autoselect_value(obs_data_t *data, const char *name);
|
|
||||||
|
|
||||||
EXPORT void obs_data_item_unset_user_value(obs_data_item_t *data);
|
|
||||||
EXPORT void obs_data_item_unset_default_value(obs_data_item_t *data);
|
|
||||||
EXPORT void obs_data_item_unset_autoselect_value(obs_data_item_t *data);
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
/* Item iteration */
|
|
||||||
|
|
||||||
EXPORT obs_data_item_t *obs_data_first(obs_data_t *data);
|
|
||||||
EXPORT obs_data_item_t *obs_data_item_byname(obs_data_t *data,
|
|
||||||
const char *name);
|
|
||||||
EXPORT bool obs_data_item_next(obs_data_item_t **item);
|
|
||||||
EXPORT void obs_data_item_release(obs_data_item_t **item);
|
|
||||||
EXPORT void obs_data_item_remove(obs_data_item_t **item);
|
|
||||||
|
|
||||||
/* Gets Item type */
|
|
||||||
EXPORT enum obs_data_type obs_data_item_gettype(obs_data_item_t *item);
|
|
||||||
EXPORT enum obs_data_number_type obs_data_item_numtype(obs_data_item_t *item);
|
|
||||||
EXPORT const char *obs_data_item_get_name(obs_data_item_t *item);
|
|
||||||
|
|
||||||
/* Item set functions */
|
|
||||||
EXPORT void obs_data_item_set_string(obs_data_item_t **item, const char *val);
|
|
||||||
EXPORT void obs_data_item_set_int(obs_data_item_t **item, long long val);
|
|
||||||
EXPORT void obs_data_item_set_double(obs_data_item_t **item, double val);
|
|
||||||
EXPORT void obs_data_item_set_bool(obs_data_item_t **item, bool val);
|
|
||||||
EXPORT void obs_data_item_set_obj(obs_data_item_t **item, obs_data_t *val);
|
|
||||||
EXPORT void obs_data_item_set_array(obs_data_item_t **item,
|
|
||||||
obs_data_array_t *val);
|
|
||||||
|
|
||||||
EXPORT void obs_data_item_set_default_string(obs_data_item_t **item,
|
|
||||||
const char *val);
|
|
||||||
EXPORT void obs_data_item_set_default_int(obs_data_item_t **item,
|
|
||||||
long long val);
|
|
||||||
EXPORT void obs_data_item_set_default_double(obs_data_item_t **item,
|
|
||||||
double val);
|
|
||||||
EXPORT void obs_data_item_set_default_bool(obs_data_item_t **item, bool val);
|
|
||||||
EXPORT void obs_data_item_set_default_obj(obs_data_item_t **item,
|
|
||||||
obs_data_t *val);
|
|
||||||
EXPORT void obs_data_item_set_default_array(obs_data_item_t **item,
|
|
||||||
obs_data_array_t *val);
|
|
||||||
|
|
||||||
EXPORT void obs_data_item_set_autoselect_string(obs_data_item_t **item,
|
|
||||||
const char *val);
|
|
||||||
EXPORT void obs_data_item_set_autoselect_int(obs_data_item_t **item,
|
|
||||||
long long val);
|
|
||||||
EXPORT void obs_data_item_set_autoselect_double(obs_data_item_t **item,
|
|
||||||
double val);
|
|
||||||
EXPORT void obs_data_item_set_autoselect_bool(obs_data_item_t **item, bool val);
|
|
||||||
EXPORT void obs_data_item_set_autoselect_obj(obs_data_item_t **item,
|
|
||||||
obs_data_t *val);
|
|
||||||
EXPORT void obs_data_item_set_autoselect_array(obs_data_item_t **item,
|
|
||||||
obs_data_array_t *val);
|
|
||||||
|
|
||||||
/* Item get functions */
|
|
||||||
EXPORT const char *obs_data_item_get_string(obs_data_item_t *item);
|
|
||||||
EXPORT long long obs_data_item_get_int(obs_data_item_t *item);
|
|
||||||
EXPORT double obs_data_item_get_double(obs_data_item_t *item);
|
|
||||||
EXPORT bool obs_data_item_get_bool(obs_data_item_t *item);
|
|
||||||
EXPORT obs_data_t *obs_data_item_get_obj(obs_data_item_t *item);
|
|
||||||
EXPORT obs_data_array_t *obs_data_item_get_array(obs_data_item_t *item);
|
|
||||||
|
|
||||||
EXPORT const char *obs_data_item_get_default_string(obs_data_item_t *item);
|
|
||||||
EXPORT long long obs_data_item_get_default_int(obs_data_item_t *item);
|
|
||||||
EXPORT double obs_data_item_get_default_double(obs_data_item_t *item);
|
|
||||||
EXPORT bool obs_data_item_get_default_bool(obs_data_item_t *item);
|
|
||||||
EXPORT obs_data_t *obs_data_item_get_default_obj(obs_data_item_t *item);
|
|
||||||
EXPORT obs_data_array_t *obs_data_item_get_default_array(obs_data_item_t *item);
|
|
||||||
|
|
||||||
EXPORT const char *obs_data_item_get_autoselect_string(obs_data_item_t *item);
|
|
||||||
EXPORT long long obs_data_item_get_autoselect_int(obs_data_item_t *item);
|
|
||||||
EXPORT double obs_data_item_get_autoselect_double(obs_data_item_t *item);
|
|
||||||
EXPORT bool obs_data_item_get_autoselect_bool(obs_data_item_t *item);
|
|
||||||
EXPORT obs_data_t *obs_data_item_get_autoselect_obj(obs_data_item_t *item);
|
|
||||||
EXPORT obs_data_array_t *
|
|
||||||
obs_data_item_get_autoselect_array(obs_data_item_t *item);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
123
tests/stubs/macro-action-macro.cpp
Normal file
123
tests/stubs/macro-action-macro.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include "macro-action-macro.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
bool MacroActionMacro::_registered = false;
|
||||||
|
const std::string MacroActionMacro::id = "macro";
|
||||||
|
|
||||||
|
bool MacroActionMacro::PerformAction()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionMacro::LogAction() const {}
|
||||||
|
|
||||||
|
bool MacroActionMacro::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionMacro::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionMacro::PostLoad()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MacroActionMacro::GetShortDesc() const
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroAction> MacroActionMacro::Create(Macro *m)
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionMacro>(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroAction> MacroActionMacro::Copy() const
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionMacro>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionMacro::ResolveVariablesToFixedValues() {}
|
||||||
|
|
||||||
|
void MacroActionMacro::RunOptions::Save(obs_data_t *obj) const {}
|
||||||
|
|
||||||
|
void MacroActionMacro::RunOptions::Load(obs_data_t *obj) {}
|
||||||
|
|
||||||
|
void MacroActionMacro::RunActions(Macro *actionMacro) const {}
|
||||||
|
|
||||||
|
void MacroActionMacro::AdjustActionState(Macro *macro) const {}
|
||||||
|
|
||||||
|
MacroActionMacroEdit::MacroActionMacroEdit(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
|
||||||
|
: ResizableWidget(parent),
|
||||||
|
_actions(new QComboBox(this)),
|
||||||
|
_macros(new MacroSelection(this)),
|
||||||
|
_actionSelectionType(new QComboBox(this)),
|
||||||
|
_label(new VariableLineEdit(this)),
|
||||||
|
_actionTypes(new FilterComboBox(this)),
|
||||||
|
_regex(new RegexConfigWidget(this)),
|
||||||
|
_conditionMacros(new MacroSelection(this)),
|
||||||
|
_conditionBehaviors(new QComboBox(this)),
|
||||||
|
_reevaluateConditionState(new QCheckBox(this)),
|
||||||
|
_actionSections(new QComboBox(this)),
|
||||||
|
_skipWhenPaused(new QCheckBox(this)),
|
||||||
|
_setInputs(new QCheckBox(this)),
|
||||||
|
_inputs(new MacroInputEdit(this)),
|
||||||
|
_entryLayout(new QHBoxLayout()),
|
||||||
|
_conditionLayout(new QHBoxLayout()),
|
||||||
|
_reevaluateConditionStateLayout(new QHBoxLayout()),
|
||||||
|
_setInputsLayout(new QHBoxLayout()),
|
||||||
|
_nestedMacro(new MacroEdit(this)),
|
||||||
|
_entryData(entryData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroActionMacroEdit::~MacroActionMacroEdit() {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::UpdateEntryData() {}
|
||||||
|
|
||||||
|
QWidget *MacroActionMacroEdit::Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroAction> action)
|
||||||
|
{
|
||||||
|
return new MacroActionMacroEdit(
|
||||||
|
parent, std::dynamic_pointer_cast<MacroActionMacro>(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::MacroChanged(const QString &text) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::ActionChanged(int value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::ActionSelectionTypeChanged(int value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::LabelChanged() {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::ActionTypeChanged(int value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::RegexChanged(const RegexConfig &config) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::ConditionMacroChanged(const QString &text) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::ConditionBehaviorChanged(int value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::ReevaluateConditionStateChanged(int value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::ActionSectionChanged(int value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::SkipWhenPausedChanged(int value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::SetInputsChanged(int value) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::InputsChanged(const StringList &inputs) {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::SetWidgetVisibility() {}
|
||||||
|
|
||||||
|
void MacroActionMacroEdit::SetupMacroInput(Macro *macro) const {}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
77
tests/stubs/macro-dock-settings.cpp
Normal file
77
tests/stubs/macro-dock-settings.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include "macro-dock-settings.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
MacroDockSettings::MacroDockSettings(Macro *macro) : _macro(macro) {}
|
||||||
|
MacroDockSettings::~MacroDockSettings() {}
|
||||||
|
|
||||||
|
void MacroDockSettings::Save(obs_data_t *, bool) const {}
|
||||||
|
void MacroDockSettings::Load(obs_data_t *) {}
|
||||||
|
void MacroDockSettings::EnableDock(bool value)
|
||||||
|
{
|
||||||
|
_registerDock = value;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetIsStandaloneDock(bool value)
|
||||||
|
{
|
||||||
|
_standaloneDock = value;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetDockWindowName(const std::string &name)
|
||||||
|
{
|
||||||
|
_dockWindow = name;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetHasRunButton(bool value)
|
||||||
|
{
|
||||||
|
_hasRunButton = value;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetHasPauseButton(bool value)
|
||||||
|
{
|
||||||
|
_hasPauseButton = value;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetHasStatusLabel(bool value)
|
||||||
|
{
|
||||||
|
_hasStatusLabel = value;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetHighlightEnable(bool value)
|
||||||
|
{
|
||||||
|
_highlight = value;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetRunButtonText(const std::string &text)
|
||||||
|
{
|
||||||
|
_runButtonText = text;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetPauseButtonText(const std::string &text)
|
||||||
|
{
|
||||||
|
_pauseButtonText = text;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetUnpauseButtonText(const std::string &text)
|
||||||
|
{
|
||||||
|
_unpauseButtonText = text;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetConditionsTrueStatusText(const std::string &text)
|
||||||
|
{
|
||||||
|
_conditionsTrueStatusText = text;
|
||||||
|
}
|
||||||
|
void MacroDockSettings::SetConditionsFalseStatusText(const std::string &text)
|
||||||
|
{
|
||||||
|
_conditionsFalseStatusText = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringVariable MacroDockSettings::ConditionsTrueStatusText() const
|
||||||
|
{
|
||||||
|
return _conditionsTrueStatusText;
|
||||||
|
}
|
||||||
|
StringVariable MacroDockSettings::ConditionsFalseStatusText() const
|
||||||
|
{
|
||||||
|
return _conditionsFalseStatusText;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroDockSettings::HandleMacroNameChange() {}
|
||||||
|
void MacroDockSettings::ResetDockIfEnabled() {}
|
||||||
|
void MacroDockSettings::RemoveDock() {}
|
||||||
|
|
||||||
|
std::string MacroDockSettings::GenerateId()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
134
tests/stubs/macro-edit.cpp
Normal file
134
tests/stubs/macro-edit.cpp
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#include "macro-edit.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
MacroEdit::MacroEdit(QWidget *parent, QStringList)
|
||||||
|
: QWidget(parent),
|
||||||
|
ui(std::make_unique<Ui_MacroEdit>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroEdit::SetMacro(const std::shared_ptr<Macro> &m)
|
||||||
|
{
|
||||||
|
_currentMacro = m;
|
||||||
|
}
|
||||||
|
std::shared_ptr<Macro> MacroEdit::GetMacro() const
|
||||||
|
{
|
||||||
|
return _currentMacro;
|
||||||
|
}
|
||||||
|
void MacroEdit::ClearSegmentWidgetCacheFor(Macro *) const {}
|
||||||
|
void MacroEdit::SetControlsDisabled(bool) const {}
|
||||||
|
void MacroEdit::HighlightAction(int, QColor) const {}
|
||||||
|
void MacroEdit::HighlightElseAction(int, QColor) const {}
|
||||||
|
void MacroEdit::HighlightCondition(int, QColor) const {}
|
||||||
|
void MacroEdit::ResetConditionHighlights() {}
|
||||||
|
void MacroEdit::ResetActionHighlights() {}
|
||||||
|
void MacroEdit::SetActionData(Macro &) const {}
|
||||||
|
void MacroEdit::SetElseActionData(Macro &) const {}
|
||||||
|
void MacroEdit::SetConditionData(Macro &) const {}
|
||||||
|
void MacroEdit::SwapActions(Macro *, int, int) {}
|
||||||
|
void MacroEdit::SwapElseActions(Macro *, int, int) {}
|
||||||
|
void MacroEdit::SwapConditions(Macro *, int, int) {}
|
||||||
|
void MacroEdit::CopyMacroSegment() {}
|
||||||
|
void MacroEdit::PasteMacroSegment() {}
|
||||||
|
bool MacroEdit::IsEmpty() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void MacroEdit::ShowAllMacroSections() {}
|
||||||
|
|
||||||
|
void MacroEdit::on_conditionAdd_clicked() {}
|
||||||
|
void MacroEdit::on_conditionRemove_clicked() {}
|
||||||
|
void MacroEdit::on_conditionTop_clicked() {}
|
||||||
|
void MacroEdit::on_conditionUp_clicked() {}
|
||||||
|
void MacroEdit::on_conditionDown_clicked() {}
|
||||||
|
void MacroEdit::on_conditionBottom_clicked() {}
|
||||||
|
void MacroEdit::on_actionAdd_clicked() {}
|
||||||
|
void MacroEdit::on_actionRemove_clicked() {}
|
||||||
|
void MacroEdit::on_actionTop_clicked() {}
|
||||||
|
void MacroEdit::on_actionUp_clicked() {}
|
||||||
|
void MacroEdit::on_actionDown_clicked() {}
|
||||||
|
void MacroEdit::on_actionBottom_clicked() {}
|
||||||
|
void MacroEdit::on_toggleElseActions_clicked() const {}
|
||||||
|
void MacroEdit::on_elseActionAdd_clicked() {}
|
||||||
|
void MacroEdit::on_elseActionRemove_clicked() {}
|
||||||
|
void MacroEdit::on_elseActionTop_clicked() {}
|
||||||
|
void MacroEdit::on_elseActionUp_clicked() {}
|
||||||
|
void MacroEdit::on_elseActionDown_clicked() {}
|
||||||
|
void MacroEdit::on_elseActionBottom_clicked() {}
|
||||||
|
void MacroEdit::UpMacroSegmentHotkey() {}
|
||||||
|
void MacroEdit::DownMacroSegmentHotkey() {}
|
||||||
|
void MacroEdit::DeleteMacroSegmentHotkey() {}
|
||||||
|
void MacroEdit::ShowMacroActionsContextMenu(const QPoint &) {}
|
||||||
|
void MacroEdit::ShowMacroElseActionsContextMenu(const QPoint &) {}
|
||||||
|
void MacroEdit::ShowMacroConditionsContextMenu(const QPoint &) {}
|
||||||
|
void MacroEdit::ExpandAllActions() const {}
|
||||||
|
void MacroEdit::ExpandAllElseActions() const {}
|
||||||
|
void MacroEdit::ExpandAllConditions() const {}
|
||||||
|
void MacroEdit::CollapseAllActions() const {}
|
||||||
|
void MacroEdit::CollapseAllElseActions() const {}
|
||||||
|
void MacroEdit::CollapseAllConditions() const {}
|
||||||
|
void MacroEdit::MinimizeActions() const {}
|
||||||
|
void MacroEdit::MaximizeActions() const {}
|
||||||
|
void MacroEdit::MinimizeElseActions() const {}
|
||||||
|
void MacroEdit::MaximizeElseActions() const {}
|
||||||
|
void MacroEdit::MinimizeConditions() const {}
|
||||||
|
void MacroEdit::MaximizeConditions() const {}
|
||||||
|
void MacroEdit::SetElseActionsStateToHidden() const {}
|
||||||
|
void MacroEdit::SetElseActionsStateToVisible() const {}
|
||||||
|
void MacroEdit::MacroActionSelectionChanged(int) {}
|
||||||
|
void MacroEdit::MacroActionReorder(int, int) {}
|
||||||
|
void MacroEdit::AddMacroAction(Macro *, int, const std::string &, obs_data_t *)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void MacroEdit::AddMacroAction(int) {}
|
||||||
|
void MacroEdit::RemoveMacroAction(int) {}
|
||||||
|
void MacroEdit::MoveMacroActionUp(int) {}
|
||||||
|
void MacroEdit::MoveMacroActionDown(int) {}
|
||||||
|
void MacroEdit::MacroElseActionSelectionChanged(int) {}
|
||||||
|
void MacroEdit::MacroElseActionReorder(int, int) {}
|
||||||
|
void MacroEdit::AddMacroElseAction(Macro *, int, const std::string &,
|
||||||
|
obs_data_t *)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void MacroEdit::AddMacroElseAction(int) {}
|
||||||
|
void MacroEdit::RemoveMacroElseAction(int) {}
|
||||||
|
void MacroEdit::MoveMacroElseActionUp(int) {}
|
||||||
|
void MacroEdit::MoveMacroElseActionDown(int) {}
|
||||||
|
void MacroEdit::MacroConditionSelectionChanged(int) {}
|
||||||
|
void MacroEdit::MacroConditionReorder(int, int) {}
|
||||||
|
void MacroEdit::AddMacroCondition(int) {}
|
||||||
|
void MacroEdit::AddMacroCondition(Macro *, int, const std::string &,
|
||||||
|
obs_data_t *, Logic::Type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void MacroEdit::RemoveMacroCondition(int) {}
|
||||||
|
void MacroEdit::MoveMacroConditionUp(int) {}
|
||||||
|
void MacroEdit::MoveMacroConditionDown(int) {}
|
||||||
|
void MacroEdit::HighlightControls() const {}
|
||||||
|
|
||||||
|
void MacroEdit::PopulateMacroActions(Macro &, uint32_t) {}
|
||||||
|
void MacroEdit::PopulateMacroElseActions(Macro &, uint32_t) {}
|
||||||
|
void MacroEdit::PopulateMacroConditions(Macro &, uint32_t) {}
|
||||||
|
void MacroEdit::SetupMacroSegmentSelection(MacroSection, int) {}
|
||||||
|
void MacroEdit::SetupContextMenu(const QPoint &,
|
||||||
|
const std::function<void(MacroEdit *, int)> &,
|
||||||
|
const std::function<void(MacroEdit *)> &,
|
||||||
|
const std::function<void(MacroEdit *)> &,
|
||||||
|
const std::function<void(MacroEdit *)> &,
|
||||||
|
const std::function<void(MacroEdit *)> &,
|
||||||
|
MacroSegmentList *)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void MacroEdit::RunSegmentHighlightChecks() {}
|
||||||
|
bool MacroEdit::ElseSectionIsVisible() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroEdit::eventFilter(QObject *, QEvent *)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
86
tests/stubs/plugin-state-helpers.cpp
Normal file
86
tests/stubs/plugin-state-helpers.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#include "plugin-state-helpers.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
void SavePluginSettings(obs_data_t *) {}
|
||||||
|
void LoadPluginSettings(obs_data_t *) {}
|
||||||
|
void AddSaveStep(std::function<void(obs_data_t *)>) {}
|
||||||
|
void AddLoadStep(std::function<void(obs_data_t *)>) {}
|
||||||
|
void AddPostLoadStep(std::function<void()>) {}
|
||||||
|
void AddIntervalResetStep(std::function<void()>) {}
|
||||||
|
void RunSaveSteps(obs_data_t *) {}
|
||||||
|
void RunLoadSteps(obs_data_t *) {}
|
||||||
|
void RunAndClearPostLoadSteps() {}
|
||||||
|
void ClearPostLoadSteps() {}
|
||||||
|
|
||||||
|
void AddPluginInitStep(std::function<void()>) {}
|
||||||
|
void AddPluginPostLoadStep(std::function<void()>) {}
|
||||||
|
void AddPluginCleanupStep(std::function<void()>) {}
|
||||||
|
void RunPluginInitSteps() {}
|
||||||
|
void RunPluginPostLoadSteps() {}
|
||||||
|
void RunPluginCleanupSteps() {}
|
||||||
|
|
||||||
|
void StopPlugin() {}
|
||||||
|
void StartPlugin() {}
|
||||||
|
bool PluginIsRunning()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int GetIntervalValue()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void AddStartStep(std::function<void()>) {}
|
||||||
|
void AddStopStep(std::function<void()>) {}
|
||||||
|
void RunStartSteps() {}
|
||||||
|
void RunStopSteps() {}
|
||||||
|
void RunIntervalResetSteps() {}
|
||||||
|
|
||||||
|
void SetPluginNoMatchBehavior(NoMatchBehavior) {}
|
||||||
|
NoMatchBehavior GetPluginNoMatchBehavior()
|
||||||
|
{
|
||||||
|
return NoMatchBehavior::NO_SWITCH;
|
||||||
|
}
|
||||||
|
void SetNoMatchScene(const OBSWeakSource &) {}
|
||||||
|
|
||||||
|
std::string ForegroundWindowTitle()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string PreviousForegroundWindowTitle()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
bool SettingsWindowIsOpened()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool HighlightUIElementsEnabled()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OBSIsShuttingDown()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool InitialLoadIsComplete()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool IsFirstInterval()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool IsFirstIntervalAfterStop()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMacroHighlightingEnabled(bool) {}
|
||||||
|
bool IsMacroHighlightingEnabled()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -47,4 +47,9 @@ QWidget *GetSettingsWindow()
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsCursorInWidgetArea(QWidget *widget)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
351
tests/test-macro-condition.cpp
Normal file
351
tests/test-macro-condition.cpp
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
#include <macro-condition.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class StubCondition : public advss::MacroCondition {
|
||||||
|
public:
|
||||||
|
StubCondition(bool initialValue = false)
|
||||||
|
: MacroCondition(nullptr),
|
||||||
|
_value(initialValue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetValue(bool value) { _value = value; }
|
||||||
|
bool CheckCondition() override { return _value; }
|
||||||
|
bool Save(obs_data_t *) const override { return true; }
|
||||||
|
bool Load(obs_data_t *) override { return true; }
|
||||||
|
std::string GetId() const override { return "stub"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _value;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// HasChanged
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("HasChanged is false on first evaluation", "[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond;
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE_FALSE(cond.HasChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("HasChanged is false when value stays false", "[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE_FALSE(cond.HasChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("HasChanged is false when value stays true", "[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE_FALSE(cond.HasChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("HasChanged is true on rising edge (false to true)",
|
||||||
|
"[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
cond.SetValue(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.HasChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("HasChanged is true on falling edge (true to false)",
|
||||||
|
"[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
cond.SetValue(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.HasChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("HasChanged resets to false after stable evaluation",
|
||||||
|
"[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
|
||||||
|
cond.SetValue(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.HasChanged());
|
||||||
|
|
||||||
|
cond.EvaluateCondition(); // value unchanged
|
||||||
|
REQUIRE_FALSE(cond.HasChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("EvaluateCondition returns the current condition value",
|
||||||
|
"[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
REQUIRE_FALSE(cond.EvaluateCondition());
|
||||||
|
|
||||||
|
cond.SetValue(true);
|
||||||
|
REQUIRE(cond.EvaluateCondition());
|
||||||
|
|
||||||
|
cond.SetValue(false);
|
||||||
|
REQUIRE_FALSE(cond.EvaluateCondition());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multiple alternating changes are each detected", "[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
cond.EvaluateCondition(); // baseline
|
||||||
|
|
||||||
|
cond.SetValue(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.HasChanged()); // false -> true
|
||||||
|
|
||||||
|
cond.SetValue(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.HasChanged()); // true -> false
|
||||||
|
|
||||||
|
cond.SetValue(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.HasChanged()); // false -> true again
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// IsRisingEdge
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("IsRisingEdge is false on first evaluation", "[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE_FALSE(cond.IsRisingEdge());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("IsRisingEdge is true on false-to-true transition",
|
||||||
|
"[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
cond.SetValue(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.IsRisingEdge());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("IsRisingEdge is false on true-to-false transition",
|
||||||
|
"[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
cond.SetValue(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE_FALSE(cond.IsRisingEdge());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("IsRisingEdge is false when value stays true", "[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE_FALSE(cond.IsRisingEdge());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("IsRisingEdge is false when value stays false", "[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE_FALSE(cond.IsRisingEdge());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("IsRisingEdge resets to false after stable evaluation",
|
||||||
|
"[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
|
||||||
|
cond.SetValue(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.IsRisingEdge());
|
||||||
|
|
||||||
|
cond.EvaluateCondition(); // value unchanged
|
||||||
|
REQUIRE_FALSE(cond.IsRisingEdge());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("IsRisingEdge fires again after falling then rising",
|
||||||
|
"[macro-condition]")
|
||||||
|
{
|
||||||
|
StubCondition cond(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
|
||||||
|
cond.SetValue(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.IsRisingEdge());
|
||||||
|
|
||||||
|
cond.SetValue(false);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE_FALSE(cond.IsRisingEdge());
|
||||||
|
|
||||||
|
cond.SetValue(true);
|
||||||
|
cond.EvaluateCondition();
|
||||||
|
REQUIRE(cond.IsRisingEdge()); // second rising edge is detected
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CheckDurationModifier - NONE (default, no time constraint)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier NONE passes through condition value",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
// Default modifier type is NONE
|
||||||
|
REQUIRE(cond.CheckDurationModifier(true));
|
||||||
|
REQUIRE_FALSE(cond.CheckDurationModifier(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CheckDurationModifier - MORE (true only after duration has elapsed)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier MORE returns false before duration elapses",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::MORE);
|
||||||
|
cond.SetDuration(advss::Duration(10.0)); // 10 seconds — won't elapse
|
||||||
|
|
||||||
|
REQUIRE_FALSE(cond.CheckDurationModifier(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier MORE returns true after duration elapses",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::MORE);
|
||||||
|
cond.SetDuration(
|
||||||
|
advss::Duration(0.0)); // 0 seconds — elapses immediately
|
||||||
|
|
||||||
|
// First call starts the timer; with 0s duration it should already pass
|
||||||
|
REQUIRE(cond.CheckDurationModifier(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier MORE resets when condition becomes false",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond;
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::MORE);
|
||||||
|
cond.SetDuration(advss::Duration(0.0));
|
||||||
|
|
||||||
|
cond.CheckDurationModifier(true); // start timer
|
||||||
|
cond.CheckDurationModifier(false); // reset
|
||||||
|
// After reset a fresh call with true must re-start the timer and still pass
|
||||||
|
// (duration is 0 so it should pass immediately again)
|
||||||
|
REQUIRE(cond.CheckDurationModifier(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier MORE returns false when condition is false",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond;
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::MORE);
|
||||||
|
cond.SetDuration(advss::Duration(0.0));
|
||||||
|
|
||||||
|
REQUIRE_FALSE(cond.CheckDurationModifier(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CheckDurationModifier - LESS (true only before duration elapses)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier LESS returns true before duration elapses",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::LESS);
|
||||||
|
cond.SetDuration(advss::Duration(10.0)); // won't elapse during test
|
||||||
|
|
||||||
|
REQUIRE(cond.CheckDurationModifier(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier LESS returns false after duration elapses",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::LESS);
|
||||||
|
cond.SetDuration(advss::Duration(0.0)); // elapses immediately
|
||||||
|
|
||||||
|
// First call starts timer; with 0s it is already past
|
||||||
|
REQUIRE_FALSE(cond.CheckDurationModifier(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier LESS returns false when condition is false",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond;
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::LESS);
|
||||||
|
cond.SetDuration(advss::Duration(10.0));
|
||||||
|
|
||||||
|
REQUIRE_FALSE(cond.CheckDurationModifier(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CheckDurationModifier - WITHIN (true while within window after going false)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier WITHIN returns true while condition is true",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::WITHIN);
|
||||||
|
cond.SetDuration(advss::Duration(10.0));
|
||||||
|
|
||||||
|
REQUIRE(cond.CheckDurationModifier(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"DurationModifier WITHIN returns true immediately after condition goes false",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond;
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::WITHIN);
|
||||||
|
cond.SetDuration(advss::Duration(10.0));
|
||||||
|
|
||||||
|
cond.CheckDurationModifier(
|
||||||
|
true); // condition was true — sets time remaining
|
||||||
|
REQUIRE(cond.CheckDurationModifier(false)); // still within window
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("DurationModifier WITHIN returns false before condition was ever true",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond;
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::WITHIN);
|
||||||
|
cond.SetDuration(advss::Duration(10.0));
|
||||||
|
|
||||||
|
// Timer was never started (condition never went true->false)
|
||||||
|
REQUIRE_FALSE(cond.CheckDurationModifier(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ResetDuration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("ResetDuration causes MORE modifier to restart its timer",
|
||||||
|
"[duration-modifier]")
|
||||||
|
{
|
||||||
|
StubCondition cond(true);
|
||||||
|
cond.SetDurationModifier(advss::DurationModifier::Type::MORE);
|
||||||
|
cond.SetDuration(advss::Duration(10.0));
|
||||||
|
|
||||||
|
cond.CheckDurationModifier(true); // starts timer
|
||||||
|
cond.ResetDuration(); // reset
|
||||||
|
|
||||||
|
// After reset the timer restarts — 10s duration won't have elapsed
|
||||||
|
REQUIRE_FALSE(cond.CheckDurationModifier(true));
|
||||||
|
}
|
||||||
416
tests/test-macro.cpp
Normal file
416
tests/test-macro.cpp
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
#include <macro.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Stubs
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class StubCondition : public advss::MacroCondition {
|
||||||
|
public:
|
||||||
|
StubCondition(advss::Macro *m, bool initialValue = false)
|
||||||
|
: MacroCondition(m),
|
||||||
|
_value(initialValue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetValue(bool value) { _value = value; }
|
||||||
|
bool CheckCondition() override { return _value; }
|
||||||
|
bool Save(obs_data_t *) const override { return true; }
|
||||||
|
bool Load(obs_data_t *) override { return true; }
|
||||||
|
std::string GetId() const override { return "stub"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StubAction : public advss::MacroAction {
|
||||||
|
public:
|
||||||
|
StubAction(advss::Macro *m) : MacroAction(m) {}
|
||||||
|
|
||||||
|
bool PerformAction() override
|
||||||
|
{
|
||||||
|
_performCount++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroAction> Copy() const override
|
||||||
|
{
|
||||||
|
return std::make_shared<StubAction>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Save(obs_data_t *) const override { return true; }
|
||||||
|
bool Load(obs_data_t *) override { return true; }
|
||||||
|
std::string GetId() const override { return "stub"; }
|
||||||
|
|
||||||
|
int PerformCount() const { return _performCount; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _performCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helpers to wire up conditions and actions onto a macro
|
||||||
|
std::shared_ptr<StubCondition> AddCondition(advss::Macro &m,
|
||||||
|
bool initialValue = false)
|
||||||
|
{
|
||||||
|
auto cond = std::make_shared<StubCondition>(&m, initialValue);
|
||||||
|
cond->SetLogicType(advss::Logic::Type::ROOT_NONE);
|
||||||
|
m.Conditions().push_back(cond);
|
||||||
|
return cond;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<StubAction> AddAction(advss::Macro &m)
|
||||||
|
{
|
||||||
|
auto action = std::make_shared<StubAction>(&m);
|
||||||
|
m.Actions().push_back(action);
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<StubAction> AddElseAction(advss::Macro &m)
|
||||||
|
{
|
||||||
|
auto action = std::make_shared<StubAction>(&m);
|
||||||
|
m.ElseActions().push_back(action);
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CheckConditions - basic matching
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("CheckConditions returns false with no conditions", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
REQUIRE_FALSE(m.CheckConditions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CheckConditions returns true when condition is true", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
REQUIRE(m.CheckConditions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CheckConditions returns false when condition is false", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, false);
|
||||||
|
REQUIRE_FALSE(m.CheckConditions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ConditionsMatched reflects last CheckConditions result", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
auto cond = AddCondition(m, false);
|
||||||
|
|
||||||
|
m.CheckConditions();
|
||||||
|
REQUIRE_FALSE(m.ConditionsMatched());
|
||||||
|
|
||||||
|
cond->SetValue(true);
|
||||||
|
m.CheckConditions();
|
||||||
|
REQUIRE(m.ConditionsMatched());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CheckConditions returns false for group macros", "[macro]")
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<advss::Macro>> children;
|
||||||
|
auto group = advss::Macro::CreateGroup("group", children);
|
||||||
|
REQUIRE_FALSE(group->CheckConditions());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Pause
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("CheckConditions respects pause", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
auto cond = AddCondition(m, true);
|
||||||
|
|
||||||
|
m.SetPaused(true);
|
||||||
|
// When paused, conditions are not evaluated — _matched stays false
|
||||||
|
REQUIRE_FALSE(m.CheckConditions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CheckConditions runs when ignorePause is true while paused",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
|
||||||
|
m.SetPaused(true);
|
||||||
|
REQUIRE(m.CheckConditions(/*ignorePause=*/true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ShouldRunActions - ActionTriggerMode::MACRO_RESULT_CHANGED (default)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions is false before first CheckConditions", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
// _actionModeMatch starts false, _matched starts false
|
||||||
|
REQUIRE_FALSE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions MACRO_RESULT_CHANGED: true on first match",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
// Default mode is MACRO_RESULT_CHANGED.
|
||||||
|
// _lastMatched starts false, first check sets _matched=true -> changed
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::MACRO_RESULT_CHANGED);
|
||||||
|
m.CheckConditions();
|
||||||
|
REQUIRE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions MACRO_RESULT_CHANGED: false when result unchanged",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::MACRO_RESULT_CHANGED);
|
||||||
|
m.CheckConditions(); // false -> true: changed
|
||||||
|
m.CheckConditions(); // true -> true: unchanged
|
||||||
|
REQUIRE_FALSE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"ShouldRunActions MACRO_RESULT_CHANGED: true again when result flips back",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
auto cond = AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::MACRO_RESULT_CHANGED);
|
||||||
|
m.CheckConditions(); // false -> true
|
||||||
|
REQUIRE(m.ShouldRunActions());
|
||||||
|
m.CheckConditions(); // true -> true
|
||||||
|
REQUIRE_FALSE(m.ShouldRunActions());
|
||||||
|
cond->SetValue(false);
|
||||||
|
m.CheckConditions(); // true -> false: changed
|
||||||
|
cond->SetValue(true);
|
||||||
|
m.CheckConditions(); // true -> false: changed
|
||||||
|
REQUIRE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ShouldRunActions - ActionTriggerMode::ALWAYS
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions ALWAYS: true every time conditions match",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(advss::Macro::ActionTriggerMode::ALWAYS);
|
||||||
|
m.CheckConditions();
|
||||||
|
REQUIRE(m.ShouldRunActions());
|
||||||
|
|
||||||
|
m.CheckConditions();
|
||||||
|
REQUIRE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions ALWAYS: false when conditions do not match",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, false);
|
||||||
|
// No else actions, so nothing to run
|
||||||
|
m.SetActionTriggerMode(advss::Macro::ActionTriggerMode::ALWAYS);
|
||||||
|
m.CheckConditions();
|
||||||
|
REQUIRE_FALSE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ShouldRunActions - ActionTriggerMode::ANY_CONDITION_CHANGED
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions ANY_CONDITION_CHANGED: true when condition changes",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
auto cond = AddCondition(m, false);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::ANY_CONDITION_CHANGED);
|
||||||
|
m.CheckConditions(); // baseline — no change yet
|
||||||
|
|
||||||
|
cond->SetValue(true);
|
||||||
|
m.CheckConditions(); // changed: false -> true
|
||||||
|
REQUIRE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions ANY_CONDITION_CHANGED: false when condition stable",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::ANY_CONDITION_CHANGED);
|
||||||
|
m.CheckConditions(); // baseline
|
||||||
|
m.CheckConditions(); // stable
|
||||||
|
REQUIRE_FALSE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ShouldRunActions - ActionTriggerMode::ANY_CONDITION_TRIGGERED
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions ANY_CONDITION_TRIGGERED: true on rising edge",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
auto cond = AddCondition(m, false);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED);
|
||||||
|
m.CheckConditions(); // baseline
|
||||||
|
|
||||||
|
cond->SetValue(true);
|
||||||
|
m.CheckConditions(); // rising edge
|
||||||
|
REQUIRE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions ANY_CONDITION_TRIGGERED: false on falling edge",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
auto cond = AddCondition(m, true);
|
||||||
|
AddElseAction(m); // need else actions so ShouldRunActions can be true
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED);
|
||||||
|
m.CheckConditions(); // baseline (first eval, no rising edge)
|
||||||
|
|
||||||
|
cond->SetValue(false);
|
||||||
|
m.CheckConditions(); // falling edge — not a rising edge
|
||||||
|
REQUIRE_FALSE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions ANY_CONDITION_TRIGGERED: false when stable true",
|
||||||
|
"[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED);
|
||||||
|
m.CheckConditions(); // baseline
|
||||||
|
m.CheckConditions(); // stable true — no rising edge
|
||||||
|
REQUIRE_FALSE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// RunCount
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("RunCount increments after PerformActions", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(advss::Macro::ActionTriggerMode::ALWAYS);
|
||||||
|
m.CheckConditions();
|
||||||
|
REQUIRE(m.RunCount() == 0);
|
||||||
|
|
||||||
|
m.PerformActions(true);
|
||||||
|
REQUIRE(m.RunCount() == 1);
|
||||||
|
|
||||||
|
m.PerformActions(true);
|
||||||
|
REQUIRE(m.RunCount() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ResetRunCount resets to zero", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.PerformActions(true);
|
||||||
|
m.PerformActions(true);
|
||||||
|
REQUIRE(m.RunCount() == 2);
|
||||||
|
|
||||||
|
m.ResetRunCount();
|
||||||
|
REQUIRE(m.RunCount() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Pause state
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("Paused is false by default", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
REQUIRE_FALSE(m.Paused());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("SetPaused toggles pause state", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
m.SetPaused(true);
|
||||||
|
REQUIRE(m.Paused());
|
||||||
|
m.SetPaused(false);
|
||||||
|
REQUIRE_FALSE(m.Paused());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Paused macros don't run actions", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
m.SetPaused(true);
|
||||||
|
REQUIRE(m.Paused());
|
||||||
|
auto action = AddAction(m);
|
||||||
|
m.PerformActions(true);
|
||||||
|
REQUIRE(action->PerformCount() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ShouldRunActions is false while paused", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
AddCondition(m, true);
|
||||||
|
AddAction(m);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(advss::Macro::ActionTriggerMode::ALWAYS);
|
||||||
|
m.CheckConditions(/*ignorePause=*/true);
|
||||||
|
m.SetPaused(true);
|
||||||
|
REQUIRE_FALSE(m.ShouldRunActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ActionTriggerMode getter/setter
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("GetActionTriggerMode returns the set mode", "[macro]")
|
||||||
|
{
|
||||||
|
advss::Macro m("test");
|
||||||
|
m.SetActionTriggerMode(advss::Macro::ActionTriggerMode::ALWAYS);
|
||||||
|
REQUIRE(m.GetActionTriggerMode() ==
|
||||||
|
advss::Macro::ActionTriggerMode::ALWAYS);
|
||||||
|
|
||||||
|
m.SetActionTriggerMode(
|
||||||
|
advss::Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED);
|
||||||
|
REQUIRE(m.GetActionTriggerMode() ==
|
||||||
|
advss::Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED);
|
||||||
|
}
|
||||||
@@ -80,11 +80,3 @@ TEST_CASE("Matches (PartialMatchRegexConfig)", "[regex-config]")
|
|||||||
result = regex.Matches(std::string("abc"), "a");
|
result = regex.Matches(std::string("abc"), "a");
|
||||||
REQUIRE(result == true);
|
REQUIRE(result == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("EscapeForRegex , [text-helpers]")
|
|
||||||
{
|
|
||||||
REQUIRE(advss::EscapeForRegex("") == "");
|
|
||||||
REQUIRE(advss::EscapeForRegex("abcdefg") == "abcdefg");
|
|
||||||
REQUIRE(advss::EscapeForRegex("(abcdefg)") == "\\(abcdefg\\)");
|
|
||||||
REQUIRE(advss::EscapeForRegex("\\(abcdefg)") == "\\\\(abcdefg\\)");
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user