mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 04:36:21 -05:00
Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68c6492c3f | ||
|
|
0ba7ba77d8 | ||
|
|
04b2b3474d | ||
|
|
ecbb5ebbd7 | ||
|
|
b62757b65d | ||
|
|
b1a3ab5493 | ||
|
|
abc3357180 | ||
|
|
29f9cba236 | ||
|
|
9a62522140 | ||
|
|
666c52556e | ||
|
|
1c0734d1cb | ||
|
|
1304ac7336 | ||
|
|
884fb1b637 | ||
|
|
a82bce947f | ||
|
|
d5241f0700 | ||
|
|
f24f258c9c | ||
|
|
eddd9fa8ab | ||
|
|
6cc224aeec | ||
|
|
4b0a631987 | ||
|
|
dcae0e8e8b | ||
|
|
29f810a2a0 | ||
|
|
719dfa7b72 | ||
|
|
259aac8fcd | ||
|
|
5be2cd278f | ||
|
|
211eac5313 | ||
|
|
f92ed3139d | ||
|
|
bdff84c605 | ||
|
|
08e430396a | ||
|
|
df7fc7ec01 | ||
|
|
9eef581161 | ||
|
|
52811e44f4 | ||
|
|
7b1c56877e | ||
|
|
78d73af589 | ||
|
|
d9c1ef4c22 | ||
|
|
93f96ee660 | ||
|
|
d25a8d4efb | ||
|
|
1f22278496 | ||
|
|
70b705e0fd | ||
|
|
9e1dfdc3f0 | ||
|
|
47d6d4ac06 | ||
|
|
fe3586b6f4 | ||
|
|
650881204f | ||
|
|
8c0ba112e2 | ||
|
|
a17d8f7e09 | ||
|
|
371b4ae05d | ||
|
|
1d7ce510f7 | ||
|
|
d759ded64d | ||
|
|
0b2e1b88cc | ||
|
|
72a11eedfc | ||
|
|
1f02226620 | ||
|
|
ad8c29997c | ||
|
|
2c7c40c3f0 | ||
|
|
c9c0e4db2e | ||
|
|
dd880b13c0 | ||
|
|
76c759c8f4 | ||
|
|
284c8020b2 | ||
|
|
f506600b8c | ||
|
|
339418b5f3 | ||
|
|
2eca2cc2e9 | ||
|
|
4315d309f2 | ||
|
|
8d18bb363a | ||
|
|
592f31f059 | ||
|
|
300ad15ad0 | ||
|
|
91f93169eb | ||
|
|
5d95069291 | ||
|
|
e5bb9162ef | ||
|
|
6869cc8a01 | ||
|
|
55c7ac8a32 | ||
|
|
101a85b063 | ||
|
|
e7c6afbc6a | ||
|
|
18e19fb6c8 | ||
|
|
a7ccfc3282 | ||
|
|
85eb2a4235 | ||
|
|
3e3805b542 | ||
|
|
c59acd836a | ||
|
|
1e81181312 | ||
|
|
8a796efdb3 | ||
|
|
1a07a93552 | ||
|
|
bd74629420 | ||
|
|
dabb293cc0 | ||
|
|
b6031bd29a | ||
|
|
05fa3af2ac | ||
|
|
c52166bfec | ||
|
|
6d3964fa5d | ||
|
|
65545d85ba | ||
|
|
dea4ff47cd | ||
|
|
258a5f6ab7 | ||
|
|
2048ca2816 | ||
|
|
eb9e2e8062 | ||
|
|
3229bf0150 | ||
|
|
dfa12f9807 |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -22,6 +22,7 @@ A clear and concise description of what you expected to happen.
|
||||
|
||||
**Logs**
|
||||
Please provide a log of your issue with verbose logging enabled (See General tab of the plugin).
|
||||
In case of a crash, please also include the corresponding crash log.
|
||||
See [here](https://obsproject.com/forum/threads/please-post-a-log-with-your-issue-heres-how.23074/) for a description where to find the log files and how to share them.
|
||||
Please share the currently used plugin settings by exporting them them to a file (See General tab of the plugin).
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
8
.github/scripts/Build-Windows.ps1
vendored
8
.github/scripts/Build-Windows.ps1
vendored
@@ -104,7 +104,7 @@ function Build {
|
||||
'-G', $CmakeGenerator
|
||||
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
|
||||
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
|
||||
"-DCMAKE_BUILD_TYPE=Release"
|
||||
"-DCMAKE_BUILD_TYPE=${Configuration}"
|
||||
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DepInstallPath}"
|
||||
"-DSW_BUILD=OFF"
|
||||
@@ -115,7 +115,7 @@ function Build {
|
||||
Invoke-External cmake -S ${LeptonicaPath} -B ${LeptonicaBuildPath} @LeptonicaCmakeArgs
|
||||
|
||||
$LeptonicaCmakeArgs = @(
|
||||
'--config', "Release"
|
||||
'--config', "${Configuration}"
|
||||
)
|
||||
|
||||
if ( $VerbosePreference -eq 'Continue' ) {
|
||||
@@ -140,7 +140,7 @@ function Build {
|
||||
'-G', $CmakeGenerator
|
||||
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
|
||||
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
|
||||
"-DCMAKE_BUILD_TYPE=Release"
|
||||
"-DCMAKE_BUILD_TYPE=${Configuration}"
|
||||
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DepInstallPath}"
|
||||
"-DSW_BUILD=OFF"
|
||||
@@ -154,7 +154,7 @@ function Build {
|
||||
Invoke-External cmake -S ${TesseractPath} -B ${TesseractBuildPath} @TesseractCmakeArgs
|
||||
|
||||
$TesseractCmakeArgs = @(
|
||||
'--config', "Release"
|
||||
'--config', "${Configuration}"
|
||||
)
|
||||
|
||||
if ( $VerbosePreference -eq 'Continue' ) {
|
||||
|
||||
@@ -125,6 +125,8 @@ target_sources(
|
||||
src/macro-core/macro-action-run.hpp
|
||||
src/macro-core/macro-action-scene-collection.cpp
|
||||
src/macro-core/macro-action-scene-collection.hpp
|
||||
src/macro-core/macro-action-scene-lock.cpp
|
||||
src/macro-core/macro-action-scene-lock.hpp
|
||||
src/macro-core/macro-action-scene-order.cpp
|
||||
src/macro-core/macro-action-scene-order.hpp
|
||||
src/macro-core/macro-action-scene-switch.cpp
|
||||
@@ -201,6 +203,8 @@ target_sources(
|
||||
src/macro-core/macro-condition-scene-transform.hpp
|
||||
src/macro-core/macro-condition-scene-visibility.cpp
|
||||
src/macro-core/macro-condition-scene-visibility.hpp
|
||||
src/macro-core/macro-condition-slideshow.cpp
|
||||
src/macro-core/macro-condition-slideshow.hpp
|
||||
src/macro-core/macro-condition-scene.cpp
|
||||
src/macro-core/macro-condition-scene.hpp
|
||||
src/macro-core/macro-condition-source.cpp
|
||||
@@ -255,16 +259,24 @@ target_sources(
|
||||
src/utils/log-helper.hpp
|
||||
src/utils/file-selection.cpp
|
||||
src/utils/file-selection.hpp
|
||||
src/utils/filter-combo-box.cpp
|
||||
src/utils/filter-combo-box.hpp
|
||||
src/utils/filter-selection.cpp
|
||||
src/utils/filter-selection.hpp
|
||||
src/utils/macro-export-import-dialog.cpp
|
||||
src/utils/macro-export-import-dialog.hpp
|
||||
src/utils/macro-list.cpp
|
||||
src/utils/macro-list.hpp
|
||||
src/utils/macro-segment-selection.cpp
|
||||
src/utils/macro-segment-selection.hpp
|
||||
src/utils/math-helpers.cpp
|
||||
src/utils/math-helpers.hpp
|
||||
src/utils/mouse-wheel-guard.cpp
|
||||
src/utils/mouse-wheel-guard.hpp
|
||||
src/utils/name-dialog.cpp
|
||||
src/utils/name-dialog.hpp
|
||||
src/utils/non-modal-dialog.cpp
|
||||
src/utils/non-modal-dialog.hpp
|
||||
src/utils/obs-dock.hpp
|
||||
src/utils/obs-module-helper.cpp
|
||||
src/utils/obs-module-helper.hpp
|
||||
@@ -296,6 +308,8 @@ target_sources(
|
||||
src/utils/string-list.hpp
|
||||
src/utils/striped-frame.cpp
|
||||
src/utils/striped-frame.hpp
|
||||
src/utils/switch-button.cpp
|
||||
src/utils/switch-button.hpp
|
||||
src/utils/sync-helper.cpp
|
||||
src/utils/sync-helper.hpp
|
||||
src/utils/transition-selection.cpp
|
||||
|
||||
@@ -438,7 +438,7 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Stream"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stream stoppen"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Stream starten"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Ausführen"
|
||||
AdvSceneSwitcher.action.run.arguments="Argumente:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Argument hinzufügen"
|
||||
@@ -451,7 +451,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Verstecken"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Umschalten"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Quelle"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Beliebig"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="Auf{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="Auf{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
|
||||
@@ -466,7 +466,7 @@ AdvSceneSwitcher.action.source.type.refreshSettings="Aktualisieren der Quellenei
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="Drücken der Einstellungstaste"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Kann verwendet werden, um Browser-, Medien- usw. Quellen zu aktualisieren"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="Keine Buttons gefunden!"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warnung: Das globale Aktivieren und Deaktivieren von Quellen kann nicht über die OBS-Benutzeroberfläche gesteuert werden"
|
||||
AdvSceneSwitcher.action.source.getSettings="Aktuelle Einstellungen abfragen"
|
||||
AdvSceneSwitcher.action.media="Medien"
|
||||
@@ -484,7 +484,7 @@ AdvSceneSwitcher.action.macro.type.unpause="Nicht mehr pausieren"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen"
|
||||
AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin-Status"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Ändern des Nichtübereinstimmungsverhaltens:"
|
||||
@@ -516,7 +516,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}transformieren{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="Datei"
|
||||
AdvSceneSwitcher.action.file.type.write="Schreiben"
|
||||
AdvSceneSwitcher.action.file.type.append="Anhängen"
|
||||
@@ -646,7 +646,7 @@ AdvSceneSwitcher.mediaTab.states.none="Keine Auswahl"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Abspielen"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Öffnen"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Buffern"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Pausiert"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Pausiert"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Gestoppt"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Beendet"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Fehler"
|
||||
|
||||
@@ -92,28 +92,42 @@ AdvSceneSwitcher.macroTab.group="Group Selected Macros"
|
||||
AdvSceneSwitcher.macroTab.ungroup="Ungroup Selected Groups"
|
||||
AdvSceneSwitcher.macroTab.rename="Rename"
|
||||
AdvSceneSwitcher.macroTab.remove="Remove"
|
||||
AdvSceneSwitcher.macroTab.export="Export"
|
||||
AdvSceneSwitcher.macroTab.export.info="Paste the string below into the import dialog to import the selected macros:"
|
||||
AdvSceneSwitcher.macroTab.import="Import"
|
||||
AdvSceneSwitcher.macroTab.import.info="Paste the export string into the below text box to import macros:"
|
||||
AdvSceneSwitcher.macroTab.import.invalid="Invalid import data provided!"
|
||||
AdvSceneSwitcher.macroTab.import.nameConflict="Cannot continue importing macro \"%1\" as a macro with the same name already exists!\nDo you want to continue with the import of \"%2\" and choose a new name? (Will be skipped otherwise)"
|
||||
AdvSceneSwitcher.macroTab.expandAll="Expand all"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
|
||||
AdvSceneSwitcher.macroTab.maximize="Maximize"
|
||||
AdvSceneSwitcher.macroTab.minimize="Minimize"
|
||||
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
|
||||
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
|
||||
AdvSceneSwitcher.macroTab.generalSettings="General settings"
|
||||
AdvSceneSwitcher.macroTab.dockSettings="Dock settings"
|
||||
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
|
||||
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
|
||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro"
|
||||
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Skip execution of actions of current macro on startup"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddRunButton="Add button to run the macro"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddPauseButton="Add button to pause or unpause the macro"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddStatusLabel="Add status label"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.run="Run button text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.pause="Pause button text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.unpause="Unpause button text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockStatusText.true="Conditions true text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockStatusText.false="Conditions false text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockHighlightIfExecuted="Highlight dock if macro actions were recently executed"
|
||||
|
||||
AdvSceneSwitcher.macroDock.pause="Pause"
|
||||
AdvSceneSwitcher.macroDock.unpause="Unpause"
|
||||
AdvSceneSwitcher.macroDock.run="Run"
|
||||
AdvSceneSwitcher.macroDock.statusLabel.true="Conditions are true."
|
||||
AdvSceneSwitcher.macroDock.statusLabel.false="Conditions are false."
|
||||
|
||||
; Macro List
|
||||
AdvSceneSwitcher.macroList.deleted="deleted"
|
||||
@@ -198,6 +212,7 @@ AdvSceneSwitcher.condition.video.condition.pattern="matches pattern"
|
||||
AdvSceneSwitcher.condition.video.condition.object="contains object"
|
||||
AdvSceneSwitcher.condition.video.condition.brightness="brightness"
|
||||
AdvSceneSwitcher.condition.video.condition.ocr="contains text"
|
||||
AdvSceneSwitcher.condition.video.condition.color="matches color"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
||||
@@ -225,6 +240,7 @@ AdvSceneSwitcher.condition.video.screenshotFail="Failed to get screenshot of sou
|
||||
AdvSceneSwitcher.condition.video.screenshotEmpty="Screenshot is empty - Is the source visible?"
|
||||
AdvSceneSwitcher.condition.video.patternMatchFail="Pattern was not found!"
|
||||
AdvSceneSwitcher.condition.video.patternMatchSuccess="Pattern is highlighted in red"
|
||||
AdvSceneSwitcher.condition.video.patternMatchSuccessFullImage="Full image matches the pattern"
|
||||
AdvSceneSwitcher.condition.video.objectMatchFail="Object was not found!"
|
||||
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
|
||||
AdvSceneSwitcher.condition.video.ocrMatchSuccess="Detected text:\n\n%1"
|
||||
@@ -239,6 +255,11 @@ AdvSceneSwitcher.condition.video.ocrMode.circleWord="Single word in a circle"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleChar="Single character"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.sparseText="Find text in no particular order"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.sparseTextOSD="Find text in no particular order (OSD)"
|
||||
AdvSceneSwitcher.condition.video.ocrLanguageNotFound="<html><head/><body><p>Language model for \"%1\" was not found in:<br>\"%2\"<br><br>You can find additional language models <a href=\"https://github.com/tesseract-ocr/tessdata\"><span style=\" text-decoration: underline; color:#268bd2;\">here</span></a>."
|
||||
AdvSceneSwitcher.condition.video.colorMatchThreshold="Match threshold:"
|
||||
AdvSceneSwitcher.condition.video.colorMatchThresholdDescription="How much of the image has to match the given color?\nA value of 1 requires every pixel of the input image to match the given color."
|
||||
AdvSceneSwitcher.condition.video.colorDeviationThreshold="Color deviation:"
|
||||
AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription="How similar can the clolor be to the provided input color for it to still count as a match?\nA value of 0 requires a perfect color match, while a higher value includes similar colors also."
|
||||
AdvSceneSwitcher.condition.video.type.main="OBS's main output"
|
||||
AdvSceneSwitcher.condition.video.type.source="Source"
|
||||
AdvSceneSwitcher.condition.video.type.scene="Scene"
|
||||
@@ -250,6 +271,8 @@ AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Perform check only in ar
|
||||
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
|
||||
AdvSceneSwitcher.condition.video.entry.orcColorPick="Check for text color:{{textColor}}{{selectColor}}"
|
||||
AdvSceneSwitcher.condition.video.entry.orcTextType="Check for text type:{{textType}}"
|
||||
AdvSceneSwitcher.condition.video.entry.orcLanguage="Check for language:{{languageCode}}"
|
||||
AdvSceneSwitcher.condition.video.entry.color="Check for color:{{color}}{{selectColor}}"
|
||||
AdvSceneSwitcher.condition.video.minSize="Minimum size:"
|
||||
AdvSceneSwitcher.condition.video.maxSize="Maximum size:"
|
||||
AdvSceneSwitcher.condition.video.selectArea="Select area"
|
||||
@@ -278,6 +301,7 @@ AdvSceneSwitcher.condition.pluginState.state.start="Plugin started"
|
||||
AdvSceneSwitcher.condition.pluginState.state.restart="Plugin restarted"
|
||||
AdvSceneSwitcher.condition.pluginState.state.running="Plugin is running"
|
||||
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS is shutting down"
|
||||
AdvSceneSwitcher.condition.pluginState.state.shutdown.limitation="Note that while actions performing OBS specific changes will still be executed on shutdown those changes will not be saved as OBS is already shutting down!"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneCollection="Scene collection loaded"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Scene changed by previous macro"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
@@ -292,10 +316,12 @@ AdvSceneSwitcher.condition.timer.entry.line2="Time remaining: {{remaining}} seco
|
||||
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Save time remaining {{autoReset}} Automatically reset timer after duration was reached"
|
||||
AdvSceneSwitcher.condition.timer.reset="Reset"
|
||||
AdvSceneSwitcher.condition.macro="Macro"
|
||||
AdvSceneSwitcher.condition.macro.type.count="Count"
|
||||
AdvSceneSwitcher.condition.macro.type.state="State"
|
||||
AdvSceneSwitcher.condition.macro.type.multiState="Multiple states"
|
||||
AdvSceneSwitcher.condition.macro.type.selection="Condition type: {{types}}"
|
||||
AdvSceneSwitcher.condition.macro.type.count="Macro run count"
|
||||
AdvSceneSwitcher.condition.macro.type.state="Macro condition state"
|
||||
AdvSceneSwitcher.condition.macro.type.multiState="Multiple macro condition state"
|
||||
AdvSceneSwitcher.condition.macro.type.actionDisabled="Macro action disabled"
|
||||
AdvSceneSwitcher.condition.macro.type.actionEnabled="Macro action enabled"
|
||||
AdvSceneSwitcher.condition.macro.type.selection="{{types}}"
|
||||
AdvSceneSwitcher.condition.macro.count.type.below="Less than"
|
||||
AdvSceneSwitcher.condition.macro.count.type.above="More than"
|
||||
AdvSceneSwitcher.condition.macro.count.type.equal="Exactly"
|
||||
@@ -308,14 +334,17 @@ AdvSceneSwitcher.condition.macro.state.entry="Conditions of {{macros}} are true"
|
||||
AdvSceneSwitcher.condition.macro.multistate.entry="Conditions of {{multiStateConditions}}{{multiStateCount}} of the following macros are true:"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} was executed {{conditions}} {{count}} times"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line2="Current count: {{currentCount}} {{resetCount}}"
|
||||
AdvSceneSwitcher.condition.macro.actionState.disabled.entry="Action{{actionIndex}}of{{macros}}is disabled"
|
||||
AdvSceneSwitcher.condition.macro.actionState.enabled.entry="Action{{actionIndex}}of{{macros}}is enabled"
|
||||
AdvSceneSwitcher.condition.source="Source"
|
||||
AdvSceneSwitcher.condition.source.type.active="Is active"
|
||||
AdvSceneSwitcher.condition.source.type.showing="Is showing"
|
||||
AdvSceneSwitcher.condition.source.type.settings="Settings match"
|
||||
AdvSceneSwitcher.condition.source.sceneVisibilityHint="Scene specific visibility can be checked using the \"Scene item visibility\" condition"
|
||||
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line3="{{regex}}{{getSettings}}"
|
||||
AdvSceneSwitcher.condition.virtualCamera="Virtual camera"
|
||||
AdvSceneSwitcher.condition.virtualCamera.state.start="Virtual camera started"
|
||||
AdvSceneSwitcher.condition.virtualCamera.state.stop="Virtual camera stopped"
|
||||
@@ -420,8 +449,8 @@ AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
|
||||
AdvSceneSwitcher.condition.profile="Profile"
|
||||
AdvSceneSwitcher.condition.profile.entry="Current active profile is {{profiles}}"
|
||||
AdvSceneSwitcher.condition.websocket="Websocket"
|
||||
AdvSceneSwitcher.condition.websocket.type.request="Request"
|
||||
AdvSceneSwitcher.condition.websocket.type.event="Event"
|
||||
AdvSceneSwitcher.condition.websocket.type.request="Scene Switcher Request"
|
||||
AdvSceneSwitcher.condition.websocket.type.event="Scene Switcher Event"
|
||||
AdvSceneSwitcher.condition.websocket.useRegex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.websocket.entry.request="{{type}} was received:"
|
||||
AdvSceneSwitcher.condition.websocket.entry.event="{{type}} was received from {{connection}}:"
|
||||
@@ -446,6 +475,12 @@ AdvSceneSwitcher.condition.display="Display"
|
||||
AdvSceneSwitcher.condition.display.type.displayName="Name of connected displays matches"
|
||||
AdvSceneSwitcher.condition.display.type.displayCount="Number of connected displays is"
|
||||
AdvSceneSwitcher.condition.display.entry="{{conditions}}{{displays}}{{regex}}{{displayCount}}"
|
||||
AdvSceneSwitcher.condition.slideshow="Slide Show"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slideChanged="Slide changed"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slideIndex="Current slide number is"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slidePath="Current slide path is"
|
||||
AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="Information about the slide show status will only be updated based on the configured time between slides"
|
||||
AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
||||
@@ -494,7 +529,11 @@ AdvSceneSwitcher.action.streaming="Streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stop streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Start streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.keyFrameInterval="Set keyframe interval"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.action.streaming.type.server="Set server URL"
|
||||
AdvSceneSwitcher.action.streaming.type.streamKey="Set stream key"
|
||||
AdvSceneSwitcher.action.streaming.type.username="Set username"
|
||||
AdvSceneSwitcher.action.streaming.type.password="Set password"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Run"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
|
||||
@@ -502,10 +541,11 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Toggle"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Enable"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Disable"
|
||||
AdvSceneSwitcher.action.filter.type.toggle="Toggle"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Set settings"
|
||||
AdvSceneSwitcher.action.filter.entry="On {{sources}} {{actions}} {{filters}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Get current settings"
|
||||
@@ -516,10 +556,23 @@ AdvSceneSwitcher.action.source.type.settings="Set settings"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings="Refresh source settings"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="Press settings button"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Can be used to refresh browser, media, etc. sources"
|
||||
AdvSceneSwitcher.action.source.type.deinterlaceMode="Set deinterlace mode"
|
||||
AdvSceneSwitcher.action.source.type.deinterlaceOrder="Set deinterlace field order"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="No buttons found!"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI\nYou might be looking for \"Scene item visibility\""
|
||||
AdvSceneSwitcher.action.source.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.disable="Disable"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.discard="Discard"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.retro="Retro"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.blend="Blend"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.blend2x="Blend 2x"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.linear="Linear"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.linear2x="Linear 2x"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.yadif="Yadif"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.yadif2x="Yadif 2x"
|
||||
AdvSceneSwitcher.action.source.deinterlaceOrder.topFieldFirst="Top Field First"
|
||||
AdvSceneSwitcher.action.source.deinterlaceOrder.bottomFieldFirst="Bottom Field First"
|
||||
AdvSceneSwitcher.action.media="Media"
|
||||
AdvSceneSwitcher.action.media.type.play="Play"
|
||||
AdvSceneSwitcher.action.media.type.pause="Pause"
|
||||
@@ -535,7 +588,10 @@ AdvSceneSwitcher.action.macro.type.unpause="Unpause"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
|
||||
AdvSceneSwitcher.action.macro.type.run="Run actions"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Stop actions"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
|
||||
AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
|
||||
AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Change the no-match behaviour:"
|
||||
@@ -549,6 +605,13 @@ AdvSceneSwitcher.action.virtualCamera.type.stop="Stop virtual camera"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.start="Start virtual camera"
|
||||
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.hotkey="Hotkey"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs="OBS hotkey"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.frontend="Frontend"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.soruce="Source"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.output="Output"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.encoder="Encoder"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.service="Service"
|
||||
AdvSceneSwitcher.action.hotkey.type.custom="Custom hotkey"
|
||||
AdvSceneSwitcher.action.hotkey.leftShift="Left Shift"
|
||||
AdvSceneSwitcher.action.hotkey.rightShift="Right Shift"
|
||||
AdvSceneSwitcher.action.hotkey.leftCtrl="Left Ctrl"
|
||||
@@ -559,7 +622,8 @@ AdvSceneSwitcher.action.hotkey.leftMeta="Left Meta"
|
||||
AdvSceneSwitcher.action.hotkey.rightMeta="Right Meta"
|
||||
AdvSceneSwitcher.action.hotkey.onlyOBS="Send keypress only to OBS"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate global key presses - functionality limited to sending key press to OBS!"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}} for {{duration}}"
|
||||
AdvSceneSwitcher.action.hotkey.entry.custom="Press{{actionType}}{{keys}}for{{duration}}"
|
||||
AdvSceneSwitcher.action.hotkey.entry.obs="Press{{actionType}}{{hotkeyType}}{{obsHotkeys}}"
|
||||
AdvSceneSwitcher.action.sceneOrder="Scene item order"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Move up"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"
|
||||
@@ -568,8 +632,18 @@ AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.reset="Reset transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.rotate="Rotate by"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="Flip horizontally"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.flipVertical="Flip vertically"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.fitToScreen="Fit to screen"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.stretchToScreen="Stretch to screen"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="Center to screen"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Center vertically"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Center horizontally"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}transform{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="File"
|
||||
AdvSceneSwitcher.action.file.type.write="Write"
|
||||
AdvSceneSwitcher.action.file.type.append="Append"
|
||||
@@ -622,11 +696,17 @@ AdvSceneSwitcher.action.sequence.status.none="none"
|
||||
AdvSceneSwitcher.action.sequence.restart="Restart from beginning once end of list is reached"
|
||||
AdvSceneSwitcher.action.sequence.continueFrom="Continue with selected item"
|
||||
AdvSceneSwitcher.action.websocket="Websocket"
|
||||
AdvSceneSwitcher.action.websocket.api.sceneSwitcher="Scene Switcher message"
|
||||
AdvSceneSwitcher.action.websocket.api.obsWebsocket="OBS websocket message"
|
||||
AdvSceneSwitcher.action.websocket.api.genericWebsocket="Generic websocket message"
|
||||
AdvSceneSwitcher.action.websocket.type.request="Request"
|
||||
AdvSceneSwitcher.action.websocket.type.event="Event"
|
||||
AdvSceneSwitcher.action.websocket.entry.request="Send scene switcher {{type}} via {{connection}}"
|
||||
AdvSceneSwitcher.action.websocket.entry.event="Send scene switcher {{type}} to connected clients"
|
||||
AdvSceneSwitcher.action.http="Http"
|
||||
AdvSceneSwitcher.action.websocket.settingsConflictGeneric="Possible settings conflict: Generic websocket message selection with connection which follows the OBS protocol!"
|
||||
AdvSceneSwitcher.action.websocket.settingsConflictOBS="Possible settings conflict: Connection selected which only supports generic websockte messages while message type requires OBS protocol!"
|
||||
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request="Send{{api}}of type{{type}}via{{connection}}"
|
||||
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients"
|
||||
AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}"
|
||||
AdvSceneSwitcher.action.http="HTTP"
|
||||
AdvSceneSwitcher.action.http.setHeaders="Set headers"
|
||||
AdvSceneSwitcher.action.http.headers="Headers:"
|
||||
AdvSceneSwitcher.action.http.addHeader="Add header"
|
||||
@@ -646,6 +726,9 @@ AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer"
|
||||
AdvSceneSwitcher.action.variable.type.subString="Set to substring of current value"
|
||||
AdvSceneSwitcher.action.variable.type.findAndReplace="Find and replace in current value"
|
||||
AdvSceneSwitcher.action.variable.type.mathExpression="Mathematical expression"
|
||||
AdvSceneSwitcher.action.variable.type.askForValue="Get user input"
|
||||
AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":"
|
||||
AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:"
|
||||
AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4"
|
||||
AdvSceneSwitcher.action.variable.findAndReplace.find="Text to find"
|
||||
AdvSceneSwitcher.action.variable.findAndReplace.replace="Text to replace with"
|
||||
@@ -659,6 +742,7 @@ AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{
|
||||
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}} Substring size:{{subStringSize}}"
|
||||
AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
|
||||
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}"
|
||||
AdvSceneSwitcher.action.variable.entry.userInput="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
|
||||
AdvSceneSwitcher.action.projector="Projector"
|
||||
AdvSceneSwitcher.action.projector.type.source="Source"
|
||||
AdvSceneSwitcher.action.projector.type.scene="Scene"
|
||||
@@ -674,6 +758,11 @@ AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="Send message to {{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on {{listenDevices}}: {{listenButton}}"
|
||||
AdvSceneSwitcher.action.osc="Open Sound Control"
|
||||
AdvSceneSwitcher.action.sceneLock="Scene item lock"
|
||||
AdvSceneSwitcher.action.sceneLock.type.lock="lock"
|
||||
AdvSceneSwitcher.action.sceneLock.type.unlock="unlock"
|
||||
AdvSceneSwitcher.action.sceneLock.type.toggle="toggle lock of"
|
||||
AdvSceneSwitcher.action.sceneLock.entry="On{{scenes}}{{actions}}{{sources}}"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
@@ -732,7 +821,7 @@ AdvSceneSwitcher.mediaTab.states.none="None"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Playing"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Opening"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Buffering"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Paused"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Paused"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Stopped"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Ended"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Error"
|
||||
@@ -935,8 +1024,11 @@ AdvSceneSwitcher.item.emptyName="Empty name not allowed!"
|
||||
AdvSceneSwitcher.item.nameNotAvailable="Name not available!"
|
||||
AdvSceneSwitcher.item.nameReserved="Name reserved!"
|
||||
|
||||
AdvSceneSwitcher.macroSegmentSelection.invalid="Invalid selection!"
|
||||
|
||||
AdvSceneSwitcher.variable.select="--select variable--"
|
||||
AdvSceneSwitcher.variable.add="Add new variable"
|
||||
AdvSceneSwitcher.variable.configure="Configure variable settings"
|
||||
AdvSceneSwitcher.variable.name="Name:"
|
||||
AdvSceneSwitcher.variable.value="Current value:"
|
||||
AdvSceneSwitcher.variable.save="Save / load behavior"
|
||||
@@ -946,7 +1038,10 @@ AdvSceneSwitcher.variable.save.default="Set to value"
|
||||
|
||||
AdvSceneSwitcher.connection.select="--select connection--"
|
||||
AdvSceneSwitcher.connection.add="Add new connection"
|
||||
AdvSceneSwitcher.connection.configure="Configure connection settings"
|
||||
AdvSceneSwitcher.connection.name="Name:"
|
||||
AdvSceneSwitcher.connection.useCustomURI="Use custom URI"
|
||||
AdvSceneSwitcher.connection.customURI="Address:"
|
||||
AdvSceneSwitcher.connection.address="Address:"
|
||||
AdvSceneSwitcher.connection.port="Port:"
|
||||
AdvSceneSwitcher.connection.password="Password:"
|
||||
@@ -961,6 +1056,7 @@ AdvSceneSwitcher.connection.status.connected="Connected, but not authenticated"
|
||||
AdvSceneSwitcher.connection.status.authenticated="Connected and authenticated"
|
||||
|
||||
AdvSceneSwitcher.regex.enable="Enable regular expressions"
|
||||
AdvSceneSwitcher.regex.configure="Configure regular expression settings"
|
||||
AdvSceneSwitcher.regex.partialMatch="Allow partial match"
|
||||
AdvSceneSwitcher.regex.caseInsensitive="Match case insensitive"
|
||||
AdvSceneSwitcher.regex.dotMatchNewline=". matches newlines"
|
||||
@@ -1052,8 +1148,25 @@ AdvSceneSwitcher.selectSceneCollection="--select scene collection--"
|
||||
AdvSceneSwitcher.enterPath="--enter path--"
|
||||
AdvSceneSwitcher.enterText="--enter text--"
|
||||
AdvSceneSwitcher.enterURL="--enter URL--"
|
||||
AdvSceneSwitcher.selectHotkey="--select hotkey--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
|
||||
AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB"
|
||||
|
||||
AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceName="Source name"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="Variable name"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="Source name matches pattern"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}matching{{pattern}}{{regex}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="Sources of type"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="{{nameConflictIndex}}Source type{{sourceGroups}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.index="Source with index"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.index.entry="{{index}}source"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.indexRange="Sources in index range"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry="Sources from{{index}}to{{indexEnd}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.all="All sources"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.all.entry="All sources"
|
||||
AdvSceneSwitcher.sceneItemSelection.all="All"
|
||||
AdvSceneSwitcher.sceneItemSelection.any="Any"
|
||||
|
||||
|
||||
@@ -360,7 +360,7 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Ejecutar"
|
||||
AdvSceneSwitcher.action.run.arguments="Argumentos:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Agregar argumento"
|
||||
@@ -371,7 +371,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Mostrar"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Fuente"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Cualquiera"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filtro"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
|
||||
@@ -382,7 +382,7 @@ AdvSceneSwitcher.action.source="Fuente"
|
||||
AdvSceneSwitcher.action.source.type.enable="Habilitar"
|
||||
AdvSceneSwitcher.action.source.type.disable="Deshabilitar"
|
||||
AdvSceneSwitcher.action.source.type.settings="Establecer configuración"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="Advertencia: la IU de OBS no puede controlar la habilitación y deshabilitación global de fuentes"
|
||||
AdvSceneSwitcher.action.source.getSettings="Obtener la configuración actual"
|
||||
AdvSceneSwitcher.action.media="Medios"
|
||||
@@ -400,7 +400,7 @@ AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
|
||||
AdvSceneSwitcher.action.macro.type.run="Ejecutar"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Detener"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Estado del complemento"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:"
|
||||
@@ -432,7 +432,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}transformar{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="Archivo"
|
||||
AdvSceneSwitcher.action.file.type.write="Escribir"
|
||||
AdvSceneSwitcher.action.file.type.append="Agregar"
|
||||
@@ -531,7 +531,7 @@ AdvSceneSwitcher.mediaTab.states.none="Ninguno"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Reproduciéndose"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Abriendo"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Almacenando en búfer"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Pausado"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Pausado"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Detenido"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Finalizado"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Error"
|
||||
|
||||
@@ -144,7 +144,7 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Запустить"
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ AdvSceneSwitcher.mediaTab.states.none="Нет"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Играет"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Открытие"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Буферизация"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Приостановлено"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Приостановлено"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Остановлено"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Закончилось"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Ошибка"
|
||||
|
||||
@@ -290,7 +290,7 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Yayın"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Çalıştır"
|
||||
AdvSceneSwitcher.action.run.arguments="Argümanlar:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Argüman ekle"
|
||||
@@ -301,7 +301,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Göster"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filtrele"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Etkin"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
|
||||
@@ -312,7 +312,7 @@ AdvSceneSwitcher.action.source="Kaynak"
|
||||
AdvSceneSwitcher.action.source.type.enable="Etkin"
|
||||
AdvSceneSwitcher.action.source.type.disable="Etkisiz"
|
||||
AdvSceneSwitcher.action.source.type.settings="Ayarları yap"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="Uyarı: Kaynakların global olarak etkinleştirilmesi ve devre dışı bırakılması, OBS UI tarafından kontrol edilemez"
|
||||
AdvSceneSwitcher.action.source.getSettings="Mevcut Ayarları yap"
|
||||
AdvSceneSwitcher.action.media="Medya"
|
||||
@@ -328,7 +328,7 @@ AdvSceneSwitcher.action.macro.type.pause="Duraklat"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
|
||||
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"
|
||||
@@ -360,7 +360,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}transform{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="Dosya"
|
||||
AdvSceneSwitcher.action.file.type.write="Yaz"
|
||||
AdvSceneSwitcher.action.file.type.append="Ekle"
|
||||
@@ -442,7 +442,7 @@ AdvSceneSwitcher.mediaTab.states.none="Yok"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Oynamak"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Açmak"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Arabelleğe alma"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Duraklatıldı"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Duraklatıldı"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Durduruldu"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Bitti"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Hata"
|
||||
|
||||
@@ -494,7 +494,7 @@ AdvSceneSwitcher.action.streaming="推流"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="停止推流"
|
||||
AdvSceneSwitcher.action.streaming.type.start="开始推流"
|
||||
AdvSceneSwitcher.action.streaming.type.keyFrameInterval="设置关键帧间隔"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="运行"
|
||||
AdvSceneSwitcher.action.sceneVisibility="场景项目可见性"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="显示"
|
||||
@@ -502,7 +502,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切换"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="来源"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="全部"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="置{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="置{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="滤镜"
|
||||
AdvSceneSwitcher.action.filter.type.enable="开启"
|
||||
AdvSceneSwitcher.action.filter.type.disable="关闭"
|
||||
@@ -517,7 +517,7 @@ AdvSceneSwitcher.action.source.type.refreshSettings="刷新源设置"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="按下设置按钮"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="可用于刷新浏览器、媒体、源、etc"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="未找到按钮!"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="警告:OBS UI无法控制全局启用和禁用源,注意,通过这个设置禁用,我测试,你必须通过这个在启用才行,不然点击小眼睛你无法显示!"
|
||||
AdvSceneSwitcher.action.source.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.media="媒体"
|
||||
@@ -535,7 +535,7 @@ AdvSceneSwitcher.action.macro.type.unpause="取消暂停"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器"
|
||||
AdvSceneSwitcher.action.macro.type.run="运行操作"
|
||||
AdvSceneSwitcher.action.macro.type.stop="停止操作"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="插件状态"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项时:"
|
||||
@@ -569,7 +569,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="场景项目转换"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}} 上转换 {{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="文件"
|
||||
AdvSceneSwitcher.action.file.type.write="写入"
|
||||
AdvSceneSwitcher.action.file.type.append="追加写入"
|
||||
@@ -732,7 +732,7 @@ AdvSceneSwitcher.mediaTab.states.none="无"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="播放中"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="打开中"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="缓冲中"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="暂停"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="暂停"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="停止"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="结束"
|
||||
AdvSceneSwitcher.mediaTab.states.error="错误"
|
||||
|
||||
9
data/res/images/DarkDoubleDown.svg
Normal file
9
data/res/images/DarkDoubleDown.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m1,2c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#fefefe" id="svg_1" transform="matrix(1, 0, 0, 1, 0, 0)"/>
|
||||
<path d="m1,8c0,-0.27 0.09,-0.52 0.28,-0.71c0.38,-0.39 1.03,-0.39 1.41,0l5.31,5.3l5.28,-5.3c0.38,-0.39 1.03,-0.39 1.41,0c0.19,0.19 0.31,0.44 0.31,0.71s-0.13,0.52 -0.31,0.71l-6,6c-0.38,0.39 -1.03,0.39 -1.41,0l-6,-6c-0.19,-0.19 -0.28,-0.44 -0.28,-0.71zm0,0" fill="#fefefe" id="svg_2"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 791 B |
9
data/res/images/DarkDoubleUp.svg
Normal file
9
data/res/images/DarkDoubleUp.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#fefefe" id="svg_1"/>
|
||||
<path d="m1,14c0,-0.27 0.11,-0.52 0.28,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.19,0.19 0.3,0.44 0.3,0.71s-0.11,0.52 -0.3,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.28,-5.3l-5.3,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.17,-0.19 -0.28,-0.44 -0.28,-0.71zm0,0" fill="#fefefe" id="svg_2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 753 B |
9
data/res/images/LightDoubleDown.svg
Normal file
9
data/res/images/LightDoubleDown.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m1,2c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_1"/>
|
||||
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_4" transform="matrix(1, 0, 0, 1, 0, 0)"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 791 B |
9
data/res/images/LightDoubleUp.svg
Normal file
9
data/res/images/LightDoubleUp.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_1"/>
|
||||
<path d="m1,14c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 757 B |
@@ -988,6 +988,19 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionTop">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionUp">
|
||||
<property name="maximumSize">
|
||||
@@ -1023,6 +1036,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionBottom">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_15">
|
||||
<property name="orientation">
|
||||
@@ -1130,6 +1156,19 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionTop">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionUp">
|
||||
<property name="maximumSize">
|
||||
@@ -1162,6 +1201,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionBottom">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_14">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -123,6 +123,10 @@ static void AskForBackup(obs_data_t *obj);
|
||||
|
||||
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
{
|
||||
if (!switcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (saving) {
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->Prune();
|
||||
@@ -439,6 +443,8 @@ void SwitcherData::Start()
|
||||
}
|
||||
}
|
||||
|
||||
bool CloseAllInputDialogs();
|
||||
|
||||
void SwitcherData::Stop()
|
||||
{
|
||||
if (th && th->isRunning()) {
|
||||
@@ -447,10 +453,23 @@ void SwitcherData::Stop()
|
||||
abortMacroWait = true;
|
||||
macroWaitCv.notify_all();
|
||||
macroTransitionCv.notify_all();
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
|
||||
// Not waiting if a dialog was closed is a workaround to avoid
|
||||
// deadlocks when a variable input dialog is opened while Stop()
|
||||
// is called.
|
||||
//
|
||||
// The QEventLoop handling the dialog would not exit until Stop()
|
||||
// would return, which itself would wait for the switcher thread
|
||||
// to complete, which itself is blocked by the variable input
|
||||
// dialog.
|
||||
//
|
||||
// Must be reworked in the future, as this will not go well when
|
||||
// OBS is shutting down and a dialog is still opened.
|
||||
if (!CloseAllInputDialogs()) {
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
}
|
||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
||||
}
|
||||
|
||||
@@ -599,7 +618,7 @@ static void setStreamStopping()
|
||||
std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
static void handleExit()
|
||||
static void handleShutdown()
|
||||
{
|
||||
if (!switcher) {
|
||||
return;
|
||||
@@ -609,8 +628,14 @@ static void handleExit()
|
||||
switcher->Stop();
|
||||
switcher->CheckMacros();
|
||||
switcher->RunMacros();
|
||||
|
||||
// Unfortunately this will not work as OBS will now allow saving
|
||||
// the scene collection data at this point, So any OBS specific
|
||||
// changes done during shutdown will be lost
|
||||
//
|
||||
// TODO: Look for a way to possibly resolve this
|
||||
obs_frontend_save();
|
||||
}
|
||||
FreeSceneSwitcher();
|
||||
}
|
||||
|
||||
static void handleSceneCollectionChanging()
|
||||
@@ -634,8 +659,12 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case OBS_FRONTEND_EVENT_EXIT:
|
||||
handleExit();
|
||||
case OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN:
|
||||
// Note: We are intentionally not listening for
|
||||
// OBS_FRONTEND_EVENT_EXIT here as at that point all scene
|
||||
// collection data will already have been cleared and thus all
|
||||
// macros will have already been cleared
|
||||
handleShutdown();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||
handleSceneChange();
|
||||
@@ -673,6 +702,9 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
|
||||
handleSceneCollectionChanging();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:
|
||||
SaveSceneSwitcher(nullptr, false, nullptr);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -102,14 +102,17 @@ public slots:
|
||||
void on_runMacroOnChange_stateChanged(int value);
|
||||
void on_conditionAdd_clicked();
|
||||
void on_conditionRemove_clicked();
|
||||
void on_conditionTop_clicked();
|
||||
void on_conditionUp_clicked();
|
||||
void on_conditionDown_clicked();
|
||||
void on_conditionBottom_clicked();
|
||||
void on_actionAdd_clicked();
|
||||
void on_actionRemove_clicked();
|
||||
void on_actionTop_clicked();
|
||||
void on_actionUp_clicked();
|
||||
void on_actionDown_clicked();
|
||||
void MacroSelectionChanged(const QItemSelection &,
|
||||
const QItemSelection &);
|
||||
void on_actionBottom_clicked();
|
||||
void MacroSelectionChanged();
|
||||
void UpMacroSegementHotkey();
|
||||
void DownMacroSegementHotkey();
|
||||
void DeleteMacroSegementHotkey();
|
||||
@@ -118,6 +121,8 @@ public slots:
|
||||
void ShowMacroConditionsContextMenu(const QPoint &);
|
||||
void CopyMacro();
|
||||
void RenameCurrentMacro();
|
||||
void ExportMacros();
|
||||
void ImportMacros();
|
||||
void ExpandAllActions();
|
||||
void ExpandAllConditions();
|
||||
void CollapseAllActions();
|
||||
@@ -161,6 +166,7 @@ signals:
|
||||
void VariableRemoved(const QString &);
|
||||
|
||||
private:
|
||||
bool ResolveMacroImportNameConflict(std::shared_ptr<Macro> &);
|
||||
bool MacroTabIsInFocus();
|
||||
|
||||
MacroSegmentList *conditionsList = nullptr;
|
||||
|
||||
@@ -516,6 +516,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
|
||||
|
||||
// Reset on startup and scene collection change
|
||||
switcher->lastOpenedTab = -1;
|
||||
startupLoadDone = true;
|
||||
}
|
||||
|
||||
void SwitcherData::SaveSettings(obs_data_t *obj)
|
||||
|
||||
@@ -437,12 +437,6 @@ void MacroActionAudioEdit::SetWidgetVisibility()
|
||||
_balance->setVisible(_entryData->_action ==
|
||||
MacroActionAudio::Action::BALANCE);
|
||||
|
||||
_fadeTypes->setDisabled(!_entryData->_fade);
|
||||
_wait->setDisabled(!_entryData->_fade);
|
||||
_abortActiveFade->setDisabled(!_entryData->_fade);
|
||||
_duration->setDisabled(!_entryData->_fade);
|
||||
_rate->setDisabled(!_entryData->_fade);
|
||||
|
||||
_fadeTypeLayout->removeWidget(_fade);
|
||||
_fadeTypeLayout->removeWidget(_fadeTypes);
|
||||
_fadeTypeLayout->removeWidget(_duration);
|
||||
@@ -474,6 +468,12 @@ void MacroActionAudioEdit::SetWidgetVisibility()
|
||||
hasVolumeControl(_entryData->_action));
|
||||
SetLayoutVisible(_fadeOptionsLayout,
|
||||
hasVolumeControl(_entryData->_action));
|
||||
_abortActiveFade->setVisible(hasVolumeControl(_entryData->_action) &&
|
||||
_entryData->_fade);
|
||||
_wait->setVisible(hasVolumeControl(_entryData->_action) &&
|
||||
_entryData->_fade);
|
||||
|
||||
updateGeometry();
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-action-scene-switch.hpp"
|
||||
#include "section.hpp"
|
||||
#include "switch-button.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QGraphicsOpacityEffect>
|
||||
|
||||
namespace advss {
|
||||
|
||||
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
||||
@@ -78,26 +81,32 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> *entryData,
|
||||
const std::string &id)
|
||||
: MacroSegmentEdit(switcher->macroProperties._highlightActions, parent),
|
||||
_actionSelection(new QComboBox()),
|
||||
_actionSelection(new FilterComboBox()),
|
||||
_enable(new SwitchButton()),
|
||||
_entryData(entryData)
|
||||
{
|
||||
QWidget::connect(_actionSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ActionSelectionChanged(const QString &)));
|
||||
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
|
||||
SLOT(ActionEnableChanged(bool)));
|
||||
QWidget::connect(window(), SIGNAL(HighlightActionsChanged(bool)), this,
|
||||
SLOT(EnableHighlight(bool)));
|
||||
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateActionState()));
|
||||
|
||||
populateActionSelection(_actionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_enable);
|
||||
_section->AddHeaderWidget(_actionSelection);
|
||||
_section->AddHeaderWidget(_headerInfo);
|
||||
|
||||
QVBoxLayout *actionLayout = new QVBoxLayout;
|
||||
auto actionLayout = new QVBoxLayout;
|
||||
actionLayout->setContentsMargins({7, 7, 7, 7});
|
||||
actionLayout->addWidget(_section);
|
||||
_contentLayout->addLayout(actionLayout);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
auto mainLayout = new QHBoxLayout;
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout->addWidget(_frame);
|
||||
@@ -106,6 +115,7 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
_entryData = entryData;
|
||||
UpdateEntryData(id);
|
||||
|
||||
_actionStateTimer.start(300);
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
@@ -115,10 +125,14 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id = MacroActionFactory::GetIdByName(text);
|
||||
if (id.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HeaderInfoChanged("");
|
||||
auto idx = _entryData->get()->GetIndex();
|
||||
auto macro = _entryData->get()->GetMacro();
|
||||
std::string id = MacroActionFactory::GetIdByName(text);
|
||||
HeaderInfoChanged("");
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->reset();
|
||||
@@ -136,6 +150,9 @@ void MacroActionEdit::UpdateEntryData(const std::string &id)
|
||||
{
|
||||
_actionSelection->setCurrentText(
|
||||
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
|
||||
const bool enabled = (*_entryData)->Enabled();
|
||||
_enable->setChecked(enabled);
|
||||
SetDisableEffect(!enabled);
|
||||
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
|
||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||
@@ -150,6 +167,43 @@ void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
|
||||
_entryData = data;
|
||||
}
|
||||
|
||||
void MacroActionEdit::SetDisableEffect(bool value)
|
||||
{
|
||||
if (value) {
|
||||
auto effect = new QGraphicsOpacityEffect(this);
|
||||
effect->setOpacity(0.5);
|
||||
_section->setGraphicsEffect(effect);
|
||||
} else {
|
||||
_section->setGraphicsEffect(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionEdit::ActionEnableChanged(bool value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
(*_entryData)->SetEnabled(value);
|
||||
SetDisableEffect(!value);
|
||||
}
|
||||
|
||||
void MacroActionEdit::UpdateActionState()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetEnableAppearance((*_entryData)->Enabled());
|
||||
}
|
||||
|
||||
void MacroActionEdit::SetEnableAppearance(bool value)
|
||||
{
|
||||
_enable->setChecked(value);
|
||||
SetDisableEffect(!value);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroSegment> MacroActionEdit::Data()
|
||||
{
|
||||
return *_entryData;
|
||||
@@ -251,23 +305,43 @@ void AdvSceneSwitcher::on_actionRemove_clicked()
|
||||
MacroActionSelectionChanged(-1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionUp_clicked()
|
||||
void AdvSceneSwitcher::on_actionTop_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1) {
|
||||
return;
|
||||
}
|
||||
MacroActionReorder(0, currentActionIdx);
|
||||
MacroActionSelectionChanged(0);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionUp_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1 || currentActionIdx == 0) {
|
||||
return;
|
||||
}
|
||||
MoveMacroActionUp(currentActionIdx);
|
||||
MacroActionSelectionChanged(currentActionIdx - 1);
|
||||
}
|
||||
void AdvSceneSwitcher::on_actionDown_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1) {
|
||||
if (currentActionIdx == -1 ||
|
||||
currentActionIdx == actionsList->ContentLayout()->count() - 1) {
|
||||
return;
|
||||
}
|
||||
MoveMacroActionDown(currentActionIdx);
|
||||
MacroActionSelectionChanged(currentActionIdx + 1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionBottom_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1) {
|
||||
return;
|
||||
}
|
||||
const int newIdx = actionsList->ContentLayout()->count() - 1;
|
||||
MacroActionReorder(newIdx, currentActionIdx);
|
||||
MacroActionSelectionChanged(newIdx);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
|
||||
{
|
||||
if (pos1 == pos2) {
|
||||
@@ -347,8 +421,8 @@ void AdvSceneSwitcher::MacroActionReorder(int to, int from)
|
||||
return;
|
||||
}
|
||||
|
||||
if (from < 0 || from > (int)macro->Actions().size() || to < 0 ||
|
||||
to > (int)macro->Actions().size()) {
|
||||
if (to == from || from < 0 || from > (int)macro->Actions().size() ||
|
||||
to < 0 || to > (int)macro->Actions().size()) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
#include "macro-action.hpp"
|
||||
#include "filter-combo-box.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <memory>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class SwitchButton;
|
||||
|
||||
struct MacroActionInfo {
|
||||
using TCreateMethod = std::shared_ptr<MacroAction> (*)(Macro *m);
|
||||
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
|
||||
@@ -44,13 +46,19 @@ public:
|
||||
|
||||
private slots:
|
||||
void ActionSelectionChanged(const QString &text);
|
||||
void ActionEnableChanged(bool);
|
||||
void UpdateActionState();
|
||||
|
||||
private:
|
||||
std::shared_ptr<MacroSegment> Data();
|
||||
void SetDisableEffect(bool);
|
||||
void SetEnableAppearance(bool);
|
||||
|
||||
QComboBox *_actionSelection;
|
||||
FilterComboBox *_actionSelection;
|
||||
SwitchButton *_enable;
|
||||
|
||||
std::shared_ptr<MacroAction> *_entryData;
|
||||
QTimer _actionStateTimer;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ const static std::map<MacroActionFilter::Action, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.filter.type.enable"},
|
||||
{MacroActionFilter::Action::DISABLE,
|
||||
"AdvSceneSwitcher.action.filter.type.disable"},
|
||||
{MacroActionFilter::Action::TOGGLE,
|
||||
"AdvSceneSwitcher.action.filter.type.toggle"},
|
||||
{MacroActionFilter::Action::SETTINGS,
|
||||
"AdvSceneSwitcher.action.filter.type.settings"},
|
||||
};
|
||||
@@ -29,6 +31,9 @@ bool MacroActionFilter::PerformAction()
|
||||
case Action::DISABLE:
|
||||
obs_source_set_enabled(s, false);
|
||||
break;
|
||||
case Action::TOGGLE:
|
||||
obs_source_set_enabled(s, !obs_source_enabled(s));
|
||||
break;
|
||||
case Action::SETTINGS:
|
||||
SetSourceSettings(s, _settings);
|
||||
break;
|
||||
@@ -60,6 +65,7 @@ bool MacroActionFilter::Save(obs_data_t *obj) const
|
||||
_filter.Save(obj, "filter");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
_settings.Save(obj, "settings");
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,7 +74,17 @@ bool MacroActionFilter::Load(obs_data_t *obj)
|
||||
MacroAction::Load(obj);
|
||||
_source.Load(obj);
|
||||
_filter.Load(obj, _source, "filter");
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
// TODO: Remove this fallback in future version
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
const auto value = obs_data_get_int(obj, "action");
|
||||
if (value == 2) {
|
||||
_action = Action::SETTINGS;
|
||||
} else {
|
||||
_action = static_cast<Action>(value);
|
||||
}
|
||||
} else {
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
}
|
||||
_settings.Load(obj, "settings");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
enum class Action {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
TOGGLE,
|
||||
SETTINGS,
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,26 @@ bool MacroActionHotkey::_registered = MacroActionFactory::Register(
|
||||
{MacroActionHotkey::Create, MacroActionHotkeyEdit::Create,
|
||||
"AdvSceneSwitcher.action.hotkey"});
|
||||
|
||||
const static std::map<MacroActionHotkey::Action, std::string> actionTypes = {
|
||||
{MacroActionHotkey::Action::OBS_HOTKEY,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs"},
|
||||
{MacroActionHotkey::Action::CUSTOM,
|
||||
"AdvSceneSwitcher.action.hotkey.type.custom"},
|
||||
};
|
||||
|
||||
const static std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = {
|
||||
{OBS_HOTKEY_REGISTERER_FRONTEND,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.frontend"},
|
||||
{OBS_HOTKEY_REGISTERER_SOURCE,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.soruce"},
|
||||
{OBS_HOTKEY_REGISTERER_OUTPUT,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.output"},
|
||||
{OBS_HOTKEY_REGISTERER_ENCODER,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.encoder"},
|
||||
{OBS_HOTKEY_REGISTERER_SERVICE,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.service"},
|
||||
};
|
||||
|
||||
static std::unordered_map<HotkeyType, long> keyTable = {
|
||||
// Chars
|
||||
{HotkeyType::Key_A, OBS_KEY_A},
|
||||
@@ -196,7 +216,92 @@ static void InjectKeys(const std::vector<HotkeyType> &keys, int duration)
|
||||
obs_hotkey_inject_event(combo, false);
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::PerformAction()
|
||||
static void addNamePrefix(std::string &name, obs_hotkey_t *hotkey)
|
||||
{
|
||||
const auto type = obs_hotkey_get_registerer_type(hotkey);
|
||||
std::string prefix;
|
||||
if (type == OBS_HOTKEY_REGISTERER_SOURCE) {
|
||||
auto source = reinterpret_cast<obs_weak_source_t *>(
|
||||
obs_hotkey_get_registerer(hotkey));
|
||||
prefix = "[" + GetWeakSourceName(source) + "] ";
|
||||
} else if (type == OBS_HOTKEY_REGISTERER_OUTPUT) {
|
||||
auto weakOutput = reinterpret_cast<obs_weak_output_t *>(
|
||||
obs_hotkey_get_registerer(hotkey));
|
||||
std::string name;
|
||||
auto output = obs_weak_output_get_output(weakOutput);
|
||||
if (output) {
|
||||
name = obs_output_get_name(output);
|
||||
obs_output_release(output);
|
||||
}
|
||||
prefix = "[" + name + "] ";
|
||||
} else if (type == OBS_HOTKEY_REGISTERER_ENCODER) {
|
||||
auto weakEncoder = reinterpret_cast<obs_weak_encoder_t *>(
|
||||
obs_hotkey_get_registerer(hotkey));
|
||||
std::string name;
|
||||
auto encoder = obs_weak_encoder_get_encoder(weakEncoder);
|
||||
if (encoder) {
|
||||
name = obs_encoder_get_name(encoder);
|
||||
obs_encoder_release(encoder);
|
||||
}
|
||||
prefix = "[" + name + "] ";
|
||||
} else if (type == OBS_HOTKEY_REGISTERER_SERVICE) {
|
||||
auto weakService = reinterpret_cast<obs_weak_service_t *>(
|
||||
obs_hotkey_get_registerer(hotkey));
|
||||
std::string name;
|
||||
auto service = obs_weak_service_get_service(weakService);
|
||||
if (service) {
|
||||
name = obs_service_get_name(service);
|
||||
obs_service_release(service);
|
||||
}
|
||||
prefix = "[" + name + "] ";
|
||||
}
|
||||
name = prefix + name;
|
||||
}
|
||||
|
||||
static obs_hotkey_id getHotkeyIdByName(const std::string &name)
|
||||
{
|
||||
struct Parameters {
|
||||
std::string name;
|
||||
obs_hotkey_id id = OBS_INVALID_HOTKEY_ID;
|
||||
} params;
|
||||
|
||||
auto func = [](void *param, obs_hotkey_id id, obs_hotkey_t *hotkey) {
|
||||
auto params = static_cast<Parameters *>(param);
|
||||
std::string name = obs_hotkey_get_name(hotkey);
|
||||
addNamePrefix(name, hotkey);
|
||||
if (name == params->name) {
|
||||
params->id = id;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
params.name = name;
|
||||
obs_enum_hotkeys(func, ¶ms);
|
||||
return params.id;
|
||||
}
|
||||
|
||||
void MacroActionHotkey::SendOBSHotkey()
|
||||
{
|
||||
auto id = getHotkeyIdByName(_hotkeyName);
|
||||
if (id == OBS_INVALID_HOTKEY_ID) {
|
||||
blog(LOG_WARNING,
|
||||
"failed to get hotkey id for \"%s\" - key will not be pressed",
|
||||
_hotkeyName.c_str());
|
||||
return;
|
||||
}
|
||||
obs_queue_task(
|
||||
OBS_TASK_UI,
|
||||
[](void *param) {
|
||||
auto id = (size_t *)param;
|
||||
obs_hotkey_trigger_routed_callback(*id, false);
|
||||
obs_hotkey_trigger_routed_callback(*id, true);
|
||||
obs_hotkey_trigger_routed_callback(*id, false);
|
||||
},
|
||||
&id, true);
|
||||
}
|
||||
|
||||
void MacroActionHotkey::SendCustomHotkey()
|
||||
{
|
||||
std::vector<HotkeyType> keys;
|
||||
if (_leftShift) {
|
||||
@@ -237,18 +342,35 @@ bool MacroActionHotkey::PerformAction()
|
||||
t.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case advss::MacroActionHotkey::Action::OBS_HOTKEY:
|
||||
SendOBSHotkey();
|
||||
break;
|
||||
case advss::MacroActionHotkey::Action::CUSTOM:
|
||||
SendCustomHotkey();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionHotkey::LogAction() const
|
||||
{
|
||||
vblog(LOG_INFO, "sent hotkey");
|
||||
vblog(LOG_INFO, "sent hotkey type %d", static_cast<int>(_action));
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "hotkeyType", _hotkeyType);
|
||||
obs_data_set_string(obj, "hotkeyName", _hotkeyName.c_str());
|
||||
obs_data_set_int(obj, "key", static_cast<int>(_key));
|
||||
obs_data_set_bool(obj, "left_shift", _leftShift);
|
||||
obs_data_set_bool(obj, "right_shift", _rightShift);
|
||||
@@ -260,13 +382,22 @@ bool MacroActionHotkey::Save(obs_data_t *obj) const
|
||||
obs_data_set_bool(obj, "right_meta", _rightMeta);
|
||||
_duration.Save(obj);
|
||||
obs_data_set_bool(obj, "onlyOBS", _onlySendToObs);
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
obs_data_set_int(obj, "version", 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
int version = obs_data_get_int(obj, "version");
|
||||
if (version != 2) {
|
||||
_action = Action::CUSTOM;
|
||||
} else {
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
}
|
||||
_hotkeyType = static_cast<obs_hotkey_registerer_type>(
|
||||
obs_data_get_int(obj, "hotkeyType"));
|
||||
_hotkeyName = obs_data_get_string(obj, "hotkeyName");
|
||||
_key = static_cast<HotkeyType>(obs_data_get_int(obj, "key"));
|
||||
_leftShift = obs_data_get_bool(obj, "left_shift");
|
||||
_rightShift = obs_data_get_bool(obj, "right_shift");
|
||||
@@ -276,7 +407,7 @@ bool MacroActionHotkey::Load(obs_data_t *obj)
|
||||
_rightAlt = obs_data_get_bool(obj, "right_alt");
|
||||
_leftMeta = obs_data_get_bool(obj, "left_meta");
|
||||
_rightMeta = obs_data_get_bool(obj, "right_meta");
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
if (version == 0) {
|
||||
_duration = obs_data_get_int(obj, "duration") / 1000.0;
|
||||
} else {
|
||||
_duration.Load(obj);
|
||||
@@ -395,9 +526,65 @@ static inline void populateKeySelection(QComboBox *list)
|
||||
list->addItem("NumpadEnter");
|
||||
}
|
||||
|
||||
static void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, value] : actionTypes) {
|
||||
list->addItem(obs_module_text(value.c_str()));
|
||||
}
|
||||
}
|
||||
static void populateHotkeyTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, value] : hotkeyTypes) {
|
||||
list->addItem(obs_module_text(value.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static void addNamePrefix(QString &name, obs_hotkey_t *hotkey)
|
||||
{
|
||||
auto temp = name.toStdString();
|
||||
addNamePrefix(temp, hotkey);
|
||||
name = QString::fromStdString(temp);
|
||||
}
|
||||
|
||||
static void populateHotkeyNames(QComboBox *list,
|
||||
obs_hotkey_registerer_type type)
|
||||
{
|
||||
struct Parameters {
|
||||
QStringList names;
|
||||
QStringList descriptions;
|
||||
obs_hotkey_registerer_type type;
|
||||
} params;
|
||||
|
||||
auto func = [](void *param, obs_hotkey_id, obs_hotkey_t *hotkey) {
|
||||
auto params = static_cast<Parameters *>(param);
|
||||
if (obs_hotkey_get_registerer_type(hotkey) == params->type) {
|
||||
QString description(obs_hotkey_get_description(hotkey));
|
||||
addNamePrefix(description, hotkey);
|
||||
params->descriptions.append(description);
|
||||
QString name(obs_hotkey_get_name(hotkey));
|
||||
addNamePrefix(name, hotkey);
|
||||
params->names.append(name);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
params.type = type;
|
||||
obs_enum_hotkeys(func, ¶ms);
|
||||
|
||||
list->clear();
|
||||
for (int i = 0; i < params.names.size(); i++) {
|
||||
list->addItem(params.descriptions.at(i), params.names.at(i));
|
||||
}
|
||||
AddSelectionEntry(list,
|
||||
obs_module_text("AdvSceneSwitcher.selectHotkey"));
|
||||
}
|
||||
|
||||
MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionHotkey> entryData)
|
||||
: QWidget(parent),
|
||||
_actionType(new QComboBox()),
|
||||
_hotkeyType(new QComboBox()),
|
||||
_obsHotkeys(new QComboBox()),
|
||||
_keys(new QComboBox()),
|
||||
_leftShift(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftShift"))),
|
||||
@@ -419,10 +606,20 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
_onlySendToOBS(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.onlyOBS"))),
|
||||
_noKeyPressSimulationWarning(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.disabled")))
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.disabled"))),
|
||||
_entryLayout(new QHBoxLayout()),
|
||||
_keyConfigLayout(new QHBoxLayout())
|
||||
{
|
||||
populateKeySelection(_keys);
|
||||
populateActionSelection(_actionType);
|
||||
populateHotkeyTypeSelection(_hotkeyType);
|
||||
|
||||
QWidget::connect(_actionType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_hotkeyType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(HotkeyTypeChanged(int)));
|
||||
QWidget::connect(_obsHotkeys, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(OBSHotkeyChanged(int)));
|
||||
QWidget::connect(_keys, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(KeyChanged(int)));
|
||||
QWidget::connect(_leftShift, SIGNAL(stateChanged(int)), this,
|
||||
@@ -446,28 +643,21 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
QWidget::connect(_onlySendToOBS, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(OnlySendToOBSChanged(int)));
|
||||
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{keys}}", _keys},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.hotkey.entry"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
_entryLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_keyConfigLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_keyConfigLayout->addWidget(_leftShift);
|
||||
_keyConfigLayout->addWidget(_rightShift);
|
||||
_keyConfigLayout->addWidget(_leftCtrl);
|
||||
_keyConfigLayout->addWidget(_rightCtrl);
|
||||
_keyConfigLayout->addWidget(_leftAlt);
|
||||
_keyConfigLayout->addWidget(_rightAlt);
|
||||
_keyConfigLayout->addWidget(_leftMeta);
|
||||
_keyConfigLayout->addWidget(_rightMeta);
|
||||
_keyConfigLayout->addStretch();
|
||||
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
line2Layout->addWidget(_leftShift);
|
||||
line2Layout->addWidget(_rightShift);
|
||||
line2Layout->addWidget(_leftCtrl);
|
||||
line2Layout->addWidget(_rightCtrl);
|
||||
line2Layout->addWidget(_leftAlt);
|
||||
line2Layout->addWidget(_rightAlt);
|
||||
line2Layout->addWidget(_leftMeta);
|
||||
line2Layout->addWidget(_rightMeta);
|
||||
line2Layout->addStretch();
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(_entryLayout);
|
||||
mainLayout->addLayout(_keyConfigLayout);
|
||||
mainLayout->addWidget(_onlySendToOBS);
|
||||
mainLayout->addWidget(_noKeyPressSimulationWarning);
|
||||
setLayout(mainLayout);
|
||||
@@ -479,10 +669,76 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::SetWarningVisibility()
|
||||
void MacroActionHotkeyEdit::RepopulateOBSHotkeySelection()
|
||||
{
|
||||
populateHotkeyNames(_obsHotkeys, _entryData->_hotkeyType);
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::SetWidgetVisibility()
|
||||
{
|
||||
_entryLayout->removeWidget(_actionType);
|
||||
_entryLayout->removeWidget(_hotkeyType);
|
||||
_entryLayout->removeWidget(_obsHotkeys);
|
||||
_entryLayout->removeWidget(_keys);
|
||||
_entryLayout->removeWidget(_duration);
|
||||
ClearLayout(_entryLayout);
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
_entryData->_action ==
|
||||
MacroActionHotkey::Action::OBS_HOTKEY
|
||||
? "AdvSceneSwitcher.action.hotkey.entry.obs"
|
||||
: "AdvSceneSwitcher.action.hotkey.entry.custom"),
|
||||
_entryLayout,
|
||||
{
|
||||
{"{{actionType}}", _actionType},
|
||||
{"{{hotkeyType}}", _hotkeyType},
|
||||
{"{{obsHotkeys}}", _obsHotkeys},
|
||||
{"{{keys}}", _keys},
|
||||
{"{{duration}}", _duration},
|
||||
});
|
||||
|
||||
_noKeyPressSimulationWarning->setVisible(!_entryData->_onlySendToObs &&
|
||||
!canSimulateKeyPresses);
|
||||
SetLayoutVisible(_keyConfigLayout,
|
||||
_entryData->_action ==
|
||||
MacroActionHotkey::Action::CUSTOM);
|
||||
_duration->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::CUSTOM);
|
||||
_keys->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::CUSTOM);
|
||||
_onlySendToOBS->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::CUSTOM);
|
||||
_hotkeyType->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::OBS_HOTKEY);
|
||||
_obsHotkeys->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::OBS_HOTKEY);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
static QString getHotkeyDescriptionByName(const std::string &name)
|
||||
{
|
||||
struct Parameters {
|
||||
std::string name;
|
||||
QString description = "";
|
||||
} params;
|
||||
|
||||
auto func = [](void *param, obs_hotkey_id id, obs_hotkey_t *hotkey) {
|
||||
auto params = static_cast<Parameters *>(param);
|
||||
std::string name = obs_hotkey_get_name(hotkey);
|
||||
addNamePrefix(name, hotkey);
|
||||
if (name == params->name) {
|
||||
params->description =
|
||||
obs_hotkey_get_description(hotkey);
|
||||
addNamePrefix(params->description, hotkey);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
params.name = name;
|
||||
obs_enum_hotkeys(func, ¶ms);
|
||||
return params.description;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::UpdateEntryData()
|
||||
@@ -491,6 +747,11 @@ void MacroActionHotkeyEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_actionType->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_hotkeyType->setCurrentIndex(static_cast<int>(_entryData->_hotkeyType));
|
||||
RepopulateOBSHotkeySelection();
|
||||
_obsHotkeys->setCurrentText(
|
||||
getHotkeyDescriptionByName(_entryData->_hotkeyName));
|
||||
_keys->setCurrentIndex(static_cast<int>(_entryData->_key));
|
||||
_leftShift->setChecked(_entryData->_leftShift);
|
||||
_rightShift->setChecked(_entryData->_rightShift);
|
||||
@@ -503,7 +764,7 @@ void MacroActionHotkeyEdit::UpdateEntryData()
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_onlySendToOBS->setChecked(_entryData->_onlySendToObs ||
|
||||
!canSimulateKeyPresses);
|
||||
SetWarningVisibility();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::LShiftChanged(int state)
|
||||
@@ -604,7 +865,7 @@ void MacroActionHotkeyEdit::OnlySendToOBSChanged(int state)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_onlySendToObs = state;
|
||||
SetWarningVisibility();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::KeyChanged(int key)
|
||||
@@ -617,4 +878,43 @@ void MacroActionHotkeyEdit::KeyChanged(int key)
|
||||
_entryData->_key = static_cast<HotkeyType>(key);
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionHotkey::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::HotkeyTypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
auto lock = LockContext();
|
||||
_entryData->_hotkeyType =
|
||||
static_cast<obs_hotkey_registerer_type>(value);
|
||||
}
|
||||
RepopulateOBSHotkeySelection();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::OBSHotkeyChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
if (idx == -1) {
|
||||
_entryData->_hotkeyName = "";
|
||||
return;
|
||||
}
|
||||
_entryData->_hotkeyName =
|
||||
_obsHotkeys->itemData(idx).toString().toStdString();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -23,7 +23,15 @@ public:
|
||||
return std::make_shared<MacroActionHotkey>(m);
|
||||
}
|
||||
|
||||
OBSWeakSource _HotkeySource;
|
||||
enum class Action {
|
||||
OBS_HOTKEY,
|
||||
CUSTOM,
|
||||
};
|
||||
Action _action = Action::OBS_HOTKEY;
|
||||
|
||||
obs_hotkey_registerer_type _hotkeyType = OBS_HOTKEY_REGISTERER_FRONTEND;
|
||||
std::string _hotkeyName;
|
||||
|
||||
HotkeyType _key = HotkeyType::Key_NoKey;
|
||||
bool _leftShift = false;
|
||||
bool _rightShift = false;
|
||||
@@ -41,6 +49,9 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
void SendOBSHotkey();
|
||||
void SendCustomHotkey();
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -62,6 +73,9 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int key);
|
||||
void HotkeyTypeChanged(int key);
|
||||
void OBSHotkeyChanged(int);
|
||||
void KeyChanged(int key);
|
||||
void LShiftChanged(int state);
|
||||
void RShiftChanged(int state);
|
||||
@@ -75,6 +89,9 @@ private slots:
|
||||
void OnlySendToOBSChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_actionType;
|
||||
QComboBox *_hotkeyType;
|
||||
QComboBox *_obsHotkeys;
|
||||
QComboBox *_keys;
|
||||
QCheckBox *_leftShift;
|
||||
QCheckBox *_rightShift;
|
||||
@@ -91,8 +108,11 @@ protected:
|
||||
std::shared_ptr<MacroActionHotkey> _entryData;
|
||||
|
||||
private:
|
||||
void SetWarningVisibility();
|
||||
QHBoxLayout *_mainLayout;
|
||||
void RepopulateOBSHotkeySelection();
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QHBoxLayout *_entryLayout;
|
||||
QHBoxLayout *_keyConfigLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ MacroActionHttpEdit::MacroActionHttpEdit(
|
||||
_timeout(new DurationSelection(this, false))
|
||||
{
|
||||
populateMethodSelection(_methods);
|
||||
_headerList->SetMaxStringSize(4096);
|
||||
|
||||
QWidget::connect(_url, SIGNAL(editingFinished()), this,
|
||||
SLOT(URLChanged()));
|
||||
|
||||
@@ -11,14 +11,23 @@ bool MacroActionMacro::_registered = MacroActionFactory::Register(
|
||||
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
|
||||
"AdvSceneSwitcher.action.macro"});
|
||||
|
||||
const static std::map<PerformMacroAction, std::string> actionTypes = {
|
||||
{PerformMacroAction::PAUSE, "AdvSceneSwitcher.action.macro.type.pause"},
|
||||
{PerformMacroAction::UNPAUSE,
|
||||
const static std::map<MacroActionMacro::Action, std::string> actionTypes = {
|
||||
{MacroActionMacro::Action::PAUSE,
|
||||
"AdvSceneSwitcher.action.macro.type.pause"},
|
||||
{MacroActionMacro::Action::UNPAUSE,
|
||||
"AdvSceneSwitcher.action.macro.type.unpause"},
|
||||
{PerformMacroAction::RESET_COUNTER,
|
||||
{MacroActionMacro::Action::RESET_COUNTER,
|
||||
"AdvSceneSwitcher.action.macro.type.resetCounter"},
|
||||
{PerformMacroAction::RUN, "AdvSceneSwitcher.action.macro.type.run"},
|
||||
{PerformMacroAction::STOP, "AdvSceneSwitcher.action.macro.type.stop"},
|
||||
{MacroActionMacro::Action::RUN,
|
||||
"AdvSceneSwitcher.action.macro.type.run"},
|
||||
{MacroActionMacro::Action::STOP,
|
||||
"AdvSceneSwitcher.action.macro.type.stop"},
|
||||
{MacroActionMacro::Action::DISABLE_ACTION,
|
||||
"AdvSceneSwitcher.action.macro.type.disableAction"},
|
||||
{MacroActionMacro::Action::ENABLE_ACTION,
|
||||
"AdvSceneSwitcher.action.macro.type.enableAction"},
|
||||
{MacroActionMacro::Action::TOGGLE_ACTION,
|
||||
"AdvSceneSwitcher.action.macro.type.toggleAction"},
|
||||
};
|
||||
|
||||
bool MacroActionMacro::PerformAction()
|
||||
@@ -29,23 +38,42 @@ bool MacroActionMacro::PerformAction()
|
||||
}
|
||||
|
||||
switch (_action) {
|
||||
case PerformMacroAction::PAUSE:
|
||||
case Action::PAUSE:
|
||||
macro->SetPaused();
|
||||
break;
|
||||
case PerformMacroAction::UNPAUSE:
|
||||
case Action::UNPAUSE:
|
||||
macro->SetPaused(false);
|
||||
break;
|
||||
case PerformMacroAction::RESET_COUNTER:
|
||||
case Action::RESET_COUNTER:
|
||||
macro->ResetRunCount();
|
||||
break;
|
||||
case PerformMacroAction::RUN:
|
||||
case Action::RUN:
|
||||
if (!macro->Paused()) {
|
||||
macro->PerformActions();
|
||||
}
|
||||
break;
|
||||
case PerformMacroAction::STOP:
|
||||
case Action::STOP:
|
||||
macro->Stop();
|
||||
break;
|
||||
case Action::DISABLE_ACTION:
|
||||
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
|
||||
false)) {
|
||||
macro->Actions().at(_actionIndex - 1)->SetEnabled(false);
|
||||
}
|
||||
break;
|
||||
case Action::ENABLE_ACTION:
|
||||
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
|
||||
false)) {
|
||||
macro->Actions().at(_actionIndex - 1)->SetEnabled(true);
|
||||
}
|
||||
break;
|
||||
case Action::TOGGLE_ACTION:
|
||||
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
|
||||
false)) {
|
||||
auto action = macro->Actions().at(_actionIndex - 1);
|
||||
action->SetEnabled(!action->Enabled());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -59,23 +87,35 @@ void MacroActionMacro::LogAction() const
|
||||
return;
|
||||
}
|
||||
switch (_action) {
|
||||
case PerformMacroAction::PAUSE:
|
||||
case Action::PAUSE:
|
||||
vblog(LOG_INFO, "paused \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::UNPAUSE:
|
||||
case Action::UNPAUSE:
|
||||
vblog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::RESET_COUNTER:
|
||||
case Action::RESET_COUNTER:
|
||||
vblog(LOG_INFO, "reset counter for \"%s\"",
|
||||
macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::RUN:
|
||||
case Action::RUN:
|
||||
vblog(LOG_INFO, "run nested macro \"%s\"",
|
||||
macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::STOP:
|
||||
case Action::STOP:
|
||||
vblog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case Action::DISABLE_ACTION:
|
||||
vblog(LOG_INFO, "disabled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
case Action::ENABLE_ACTION:
|
||||
vblog(LOG_INFO, "enabled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
case Action::TOGGLE_ACTION:
|
||||
vblog(LOG_INFO, "toggled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -85,6 +125,7 @@ bool MacroActionMacro::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_macro.Save(obj);
|
||||
_actionIndex.Save(obj, "actionIndex");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
@@ -93,7 +134,8 @@ bool MacroActionMacro::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_macro.Load(obj);
|
||||
_action = static_cast<PerformMacroAction>(
|
||||
_actionIndex.Load(obj, "actionIndex");
|
||||
_action = static_cast<MacroActionMacro::Action>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
@@ -112,25 +154,28 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
|
||||
MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_macros(new MacroSelection(parent)),
|
||||
_actionIndex(new MacroSegmentSelection(
|
||||
this, MacroSegmentSelection::Type::ACTION)),
|
||||
_actions(new QComboBox())
|
||||
{
|
||||
_macros = new MacroSelection(parent);
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(MacroChanged(const QString &)));
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_actionIndex,
|
||||
SIGNAL(SelectionChanged(const IntVariable &)), this,
|
||||
SLOT(ActionIndexChanged(const IntVariable &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{macros}}", _macros},
|
||||
};
|
||||
auto mainLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
mainLayout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{actionIndex}}", _actionIndex},
|
||||
{"{{macros}}", _macros}});
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -144,11 +189,10 @@ void MacroActionMacroEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_actionIndex->SetValue(_entryData->_actionIndex);
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
_macros->SetCurrentMacro(_entryData->_macro);
|
||||
if (_entryData->_action == PerformMacroAction::RUN ||
|
||||
_entryData->_action == PerformMacroAction::STOP) {
|
||||
_macros->HideSelectedMacro();
|
||||
}
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::MacroChanged(const QString &text)
|
||||
@@ -159,6 +203,7 @@ void MacroActionMacroEdit::MacroChanged(const QString &text)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_macro = text;
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -170,14 +215,36 @@ void MacroActionMacroEdit::ActionChanged(int value)
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<PerformMacroAction>(value);
|
||||
_entryData->_action = static_cast<MacroActionMacro::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
if (_entryData->_action == PerformMacroAction::RUN ||
|
||||
_entryData->_action == PerformMacroAction::STOP) {
|
||||
void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_actionIndex = value;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (_entryData->_action == MacroActionMacro::Action::RUN ||
|
||||
_entryData->_action == MacroActionMacro::Action::STOP) {
|
||||
_macros->HideSelectedMacro();
|
||||
} else {
|
||||
_macros->ShowAllMacros();
|
||||
}
|
||||
|
||||
const bool isModifyingActionState =
|
||||
_entryData->_action ==
|
||||
MacroActionMacro::Action::DISABLE_ACTION ||
|
||||
_entryData->_action ==
|
||||
MacroActionMacro::Action::ENABLE_ACTION ||
|
||||
_entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION;
|
||||
_actionIndex->setVisible(isModifyingActionState);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
#include "macro-segment-selection.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class PerformMacroAction {
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
RESET_COUNTER,
|
||||
RUN,
|
||||
STOP,
|
||||
};
|
||||
|
||||
class MacroActionMacro : public MacroRefAction {
|
||||
public:
|
||||
MacroActionMacro(Macro *m) : MacroAction(m), MacroRefAction(m) {}
|
||||
@@ -28,7 +21,18 @@ public:
|
||||
return std::make_shared<MacroActionMacro>(m);
|
||||
}
|
||||
|
||||
PerformMacroAction _action = PerformMacroAction::PAUSE;
|
||||
enum class Action {
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
RESET_COUNTER,
|
||||
RUN,
|
||||
STOP,
|
||||
DISABLE_ACTION,
|
||||
ENABLE_ACTION,
|
||||
TOGGLE_ACTION,
|
||||
};
|
||||
Action _action = Action::PAUSE;
|
||||
IntVariable _actionIndex = 1;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -54,16 +58,19 @@ public:
|
||||
private slots:
|
||||
void MacroChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
void ActionIndexChanged(const IntVariable &value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
MacroSelection *_macros;
|
||||
MacroSegmentSelection *_actionIndex;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionMacro> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
176
src/macro-core/macro-action-scene-lock.cpp
Normal file
176
src/macro-core/macro-action-scene-lock.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "macro-action-scene-lock.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionSceneLock::id = "scene_lock";
|
||||
|
||||
bool MacroActionSceneLock::_registered = MacroActionFactory::Register(
|
||||
MacroActionSceneLock::id,
|
||||
{MacroActionSceneLock::Create, MacroActionSceneLockEdit::Create,
|
||||
"AdvSceneSwitcher.action.sceneLock"});
|
||||
|
||||
const static std::map<MacroActionSceneLock::Action, std::string> actionTypes = {
|
||||
{MacroActionSceneLock::Action::LOCK,
|
||||
"AdvSceneSwitcher.action.sceneLock.type.lock"},
|
||||
{MacroActionSceneLock::Action::UNLOCK,
|
||||
"AdvSceneSwitcher.action.sceneLock.type.unlock"},
|
||||
{MacroActionSceneLock::Action::TOGGLE,
|
||||
"AdvSceneSwitcher.action.sceneLock.type.toggle"},
|
||||
};
|
||||
|
||||
static void setSceneItemLock(obs_sceneitem_t *item,
|
||||
MacroActionSceneLock::Action action)
|
||||
{
|
||||
switch (action) {
|
||||
case MacroActionSceneLock::Action::LOCK:
|
||||
obs_sceneitem_set_locked(item, true);
|
||||
break;
|
||||
case MacroActionSceneLock::Action::UNLOCK:
|
||||
obs_sceneitem_set_locked(item, false);
|
||||
break;
|
||||
case MacroActionSceneLock::Action::TOGGLE:
|
||||
obs_sceneitem_set_locked(item, !obs_sceneitem_locked(item));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSceneLock::PerformAction()
|
||||
{
|
||||
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
for (const auto &item : items) {
|
||||
setSceneItemLock(item, _action);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionSceneLock::LogAction() const
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed action \"%s\" for source \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(), _source.ToString(true).c_str(),
|
||||
_scene.ToString(true).c_str());
|
||||
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown scene lock action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSceneLock::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
_source.Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSceneLock::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionSceneLock::GetShortDesc() const
|
||||
{
|
||||
if (!_source.ToString().empty()) {
|
||||
return _scene.ToString() + " - " + _source.ToString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : actionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionSceneLockEdit::MacroActionSceneLockEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneLock> entryData)
|
||||
: QWidget(parent),
|
||||
_scenes(new SceneSelectionWidget(window(), true, false, true, true)),
|
||||
_sources(new SceneItemSelectionWidget(parent)),
|
||||
_actions(new QComboBox())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
|
||||
auto layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.sceneLock.entry"),
|
||||
layout, widgetPlaceholders);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionSceneLockEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_sources->SetSceneItem((_entryData->_source));
|
||||
}
|
||||
|
||||
void MacroActionSceneLockEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroActionSceneLockEdit::SourceChanged(const SceneItemSelection &item)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSceneLockEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionSceneLock::Action>(value);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
69
src/macro-core/macro-action-scene-lock.hpp
Normal file
69
src/macro-core/macro-action-scene-lock.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionSceneLock : public MacroAction {
|
||||
public:
|
||||
MacroActionSceneLock(Macro *m) : MacroAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionSceneLock>(m);
|
||||
}
|
||||
|
||||
enum class Action {
|
||||
LOCK,
|
||||
UNLOCK,
|
||||
TOGGLE,
|
||||
};
|
||||
Action _action = Action::LOCK;
|
||||
SceneSelection _scene;
|
||||
SceneItemSelection _source;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionSceneLockEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionSceneLockEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionSceneLock> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionSceneLockEdit(
|
||||
parent, std::dynamic_pointer_cast<MacroActionSceneLock>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void ActionChanged(int value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionSceneLock> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -23,7 +23,7 @@ const static std::map<SceneOrderAction, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.sceneOrder.type.movePosition"},
|
||||
};
|
||||
|
||||
static void moveSceneItemsUp(std::vector<obs_scene_item *> &items)
|
||||
static void moveSceneItemsUp(std::vector<OBSSceneItem> &items)
|
||||
{
|
||||
// In the case of the same source being in two sequential positions
|
||||
// moving the sources up will cause the sources to swap positions due to
|
||||
@@ -33,41 +33,36 @@ static void moveSceneItemsUp(std::vector<obs_scene_item *> &items)
|
||||
// will be moved up.
|
||||
std::reverse(items.begin(), items.end());
|
||||
|
||||
for (auto &i : items) {
|
||||
for (const auto &i : items) {
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_UP);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void moveSceneItemsDown(std::vector<obs_scene_item *> &items)
|
||||
static void moveSceneItemsDown(std::vector<OBSSceneItem> &items)
|
||||
{
|
||||
for (auto &i : items) {
|
||||
for (const auto &i : items) {
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_DOWN);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void moveSceneItemsTop(std::vector<obs_scene_item *> &items)
|
||||
static void moveSceneItemsTop(std::vector<OBSSceneItem> &items)
|
||||
{
|
||||
for (auto &i : items) {
|
||||
for (const auto &i : items) {
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_TOP);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void moveSceneItemsBottom(std::vector<obs_scene_item *> &items)
|
||||
static void moveSceneItemsBottom(std::vector<OBSSceneItem> &items)
|
||||
{
|
||||
for (auto &i : items) {
|
||||
for (const auto &i : items) {
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_BOTTOM);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void moveSceneItemsPos(std::vector<obs_scene_item *> &items, int pos)
|
||||
static void moveSceneItemsPos(std::vector<OBSSceneItem> &items, int pos)
|
||||
{
|
||||
for (auto &i : items) {
|
||||
for (const auto &i : items) {
|
||||
obs_sceneitem_set_order_position(i, pos);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +144,7 @@ std::string MacroActionSceneOrder::GetShortDesc() const
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
for (const auto &entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
@@ -228,6 +223,8 @@ void MacroActionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSceneOrderEdit::ActionChanged(int value)
|
||||
|
||||
@@ -20,6 +20,9 @@ static void waitForTransitionChange(OBSWeakSource &transition,
|
||||
{
|
||||
const auto time = 100ms;
|
||||
obs_source_t *source = obs_weak_source_get_source(transition);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool stillTransitioning = true;
|
||||
while (stillTransitioning && !switcher->abortMacroWait &&
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "macro-action-scene-transform.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <graphics/matrix4.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionSceneTransform::id = "scene_transform";
|
||||
@@ -11,18 +13,242 @@ bool MacroActionSceneTransform::_registered = MacroActionFactory::Register(
|
||||
MacroActionSceneTransformEdit::Create,
|
||||
"AdvSceneSwitcher.action.sceneTransform"});
|
||||
|
||||
bool MacroActionSceneTransform::PerformAction()
|
||||
{
|
||||
ApplySettings(_settings); // Resolves variables
|
||||
const static std::map<MacroActionSceneTransform::Action, std::string> actions = {
|
||||
{MacroActionSceneTransform::Action::MANUAL_TRANSFORM,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.manual"},
|
||||
{MacroActionSceneTransform::Action::RESET,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.reset"},
|
||||
{MacroActionSceneTransform::Action::ROTATE,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.rotate"},
|
||||
{MacroActionSceneTransform::Action::FLIP_HORIZONTAL,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal"},
|
||||
{MacroActionSceneTransform::Action::FLIP_VERTICAL,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.flipVertical"},
|
||||
{MacroActionSceneTransform::Action::FIT_TO_SCREEN,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.fitToScreen"},
|
||||
{MacroActionSceneTransform::Action::STRETCH_TO_SCREEN,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.stretchToScreen"},
|
||||
{MacroActionSceneTransform::Action::CENTER_TO_SCREEN,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.centerToScreen"},
|
||||
{MacroActionSceneTransform::Action::CENTER_VERTICALLY,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.centerVertically"},
|
||||
{MacroActionSceneTransform::Action::CENTER_HORIZONTALLY,
|
||||
"AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally"},
|
||||
};
|
||||
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
for (auto &item : items) {
|
||||
/* --------------- Helpers based on OBS window-basic-main.cpp --------------- */
|
||||
|
||||
static void getItemBox(obs_sceneitem_t *item, vec3 &tl, vec3 &br)
|
||||
{
|
||||
matrix4 boxTransform;
|
||||
obs_sceneitem_get_box_transform(item, &boxTransform);
|
||||
|
||||
vec3_set(&tl, M_INFINITE, M_INFINITE, 0.0f);
|
||||
vec3_set(&br, -M_INFINITE, -M_INFINITE, 0.0f);
|
||||
|
||||
auto GetMinPos = [&](float x, float y) {
|
||||
vec3 pos;
|
||||
vec3_set(&pos, x, y, 0.0f);
|
||||
vec3_transform(&pos, &pos, &boxTransform);
|
||||
vec3_min(&tl, &tl, &pos);
|
||||
vec3_max(&br, &br, &pos);
|
||||
};
|
||||
|
||||
GetMinPos(0.0f, 0.0f);
|
||||
GetMinPos(1.0f, 0.0f);
|
||||
GetMinPos(0.0f, 1.0f);
|
||||
GetMinPos(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
static vec3 getItemTL(obs_sceneitem_t *item)
|
||||
{
|
||||
vec3 tl, br;
|
||||
getItemBox(item, tl, br);
|
||||
return tl;
|
||||
}
|
||||
|
||||
static void setItemTL(obs_sceneitem_t *item, const vec3 &tl)
|
||||
{
|
||||
vec3 newTL;
|
||||
vec2 pos;
|
||||
|
||||
obs_sceneitem_get_pos(item, &pos);
|
||||
newTL = getItemTL(item);
|
||||
pos.x += tl.x - newTL.x;
|
||||
pos.y += tl.y - newTL.y;
|
||||
obs_sceneitem_set_pos(item, &pos);
|
||||
}
|
||||
|
||||
static void reset(obs_sceneitem_t *item)
|
||||
{
|
||||
obs_transform_info info;
|
||||
vec2_set(&info.pos, 0.0f, 0.0f);
|
||||
vec2_set(&info.scale, 1.0f, 1.0f);
|
||||
info.rot = 0.0f;
|
||||
info.alignment = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
|
||||
info.bounds_type = OBS_BOUNDS_NONE;
|
||||
info.bounds_alignment = OBS_ALIGN_CENTER;
|
||||
vec2_set(&info.bounds, 0.0f, 0.0f);
|
||||
obs_sceneitem_set_info(item, &info);
|
||||
|
||||
obs_sceneitem_crop crop = {};
|
||||
obs_sceneitem_set_crop(item, &crop);
|
||||
}
|
||||
|
||||
static void rotate(obs_sceneitem_t *item, float rot)
|
||||
{
|
||||
vec3 tl = getItemTL(item);
|
||||
rot += obs_sceneitem_get_rot(item);
|
||||
if (rot >= 360.0f) {
|
||||
rot -= 360.0f;
|
||||
} else if (rot <= -360.0f) {
|
||||
rot += 360.0f;
|
||||
}
|
||||
obs_sceneitem_set_rot(item, rot);
|
||||
obs_sceneitem_force_update_transform(item);
|
||||
setItemTL(item, tl);
|
||||
};
|
||||
|
||||
static void flip(obs_sceneitem_t *item, const vec2 &mul)
|
||||
{
|
||||
vec3 tl = getItemTL(item);
|
||||
vec2 scale;
|
||||
obs_sceneitem_get_scale(item, &scale);
|
||||
vec2_mul(&scale, &scale, &mul);
|
||||
obs_sceneitem_set_scale(item, &scale);
|
||||
obs_sceneitem_force_update_transform(item);
|
||||
setItemTL(item, tl);
|
||||
}
|
||||
|
||||
static void centerAlign(obs_sceneitem_t *item, obs_bounds_type boundsType)
|
||||
{
|
||||
obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
|
||||
obs_transform_info itemInfo;
|
||||
vec2_set(&itemInfo.pos, 0.0f, 0.0f);
|
||||
vec2_set(&itemInfo.scale, 1.0f, 1.0f);
|
||||
itemInfo.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;
|
||||
itemInfo.rot = 0.0f;
|
||||
|
||||
vec2_set(&itemInfo.bounds, float(ovi.base_width),
|
||||
float(ovi.base_height));
|
||||
itemInfo.bounds_type = boundsType;
|
||||
itemInfo.bounds_alignment = OBS_ALIGN_CENTER;
|
||||
|
||||
obs_sceneitem_set_info(item, &itemInfo);
|
||||
}
|
||||
|
||||
enum class CenterType {
|
||||
SCREEN,
|
||||
VERTICAL,
|
||||
HORIZONTAL,
|
||||
};
|
||||
|
||||
static void center(obs_sceneitem_t *item, const CenterType ¢erType)
|
||||
{
|
||||
// Get center x, y coordinates of items
|
||||
vec3 tl, br;
|
||||
getItemBox(item, tl, br);
|
||||
|
||||
float left = tl.x;
|
||||
float top = tl.y;
|
||||
float right = br.x;
|
||||
float bottom = br.y;
|
||||
|
||||
vec3 center;
|
||||
center.x = (right + left) / 2.0f;
|
||||
center.y = (top + bottom) / 2.0f;
|
||||
center.z = 0.0f;
|
||||
|
||||
// Get coordinates of screen center
|
||||
obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
|
||||
vec3 screenCenter;
|
||||
vec3_set(&screenCenter, float(ovi.base_width), float(ovi.base_height),
|
||||
0.0f);
|
||||
|
||||
vec3_mulf(&screenCenter, &screenCenter, 0.5f);
|
||||
|
||||
// Calculate difference between screen center and item center
|
||||
vec3 offset;
|
||||
vec3_sub(&offset, &screenCenter, ¢er);
|
||||
|
||||
// Shift item by offset
|
||||
vec3_add(&tl, &tl, &offset);
|
||||
|
||||
vec3 itemTL = getItemTL(item);
|
||||
|
||||
if (centerType == CenterType::VERTICAL) {
|
||||
tl.x = itemTL.x;
|
||||
} else if (centerType == CenterType::HORIZONTAL) {
|
||||
tl.y = itemTL.y;
|
||||
}
|
||||
|
||||
setItemTL(item, tl);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void MacroActionSceneTransform::Transform(obs_scene_item *item)
|
||||
{
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_action) {
|
||||
case MacroActionSceneTransform::Action::RESET:
|
||||
obs_sceneitem_defer_update_begin(item);
|
||||
reset(item);
|
||||
obs_sceneitem_defer_update_end(item);
|
||||
|
||||
break;
|
||||
case MacroActionSceneTransform::Action::ROTATE:
|
||||
rotate(item, _rotation);
|
||||
break;
|
||||
case MacroActionSceneTransform::Action::FLIP_HORIZONTAL: {
|
||||
vec2 scale;
|
||||
vec2_set(&scale, -1.0f, 1.0f);
|
||||
flip(item, scale);
|
||||
break;
|
||||
}
|
||||
case MacroActionSceneTransform::Action::FLIP_VERTICAL: {
|
||||
vec2 scale;
|
||||
vec2_set(&scale, 1.0f, -1.0f);
|
||||
flip(item, scale);
|
||||
break;
|
||||
}
|
||||
case MacroActionSceneTransform::Action::FIT_TO_SCREEN:
|
||||
centerAlign(item, OBS_BOUNDS_SCALE_INNER);
|
||||
break;
|
||||
case MacroActionSceneTransform::Action::STRETCH_TO_SCREEN:
|
||||
centerAlign(item, OBS_BOUNDS_STRETCH);
|
||||
break;
|
||||
case MacroActionSceneTransform::Action::CENTER_TO_SCREEN:
|
||||
center(item, CenterType::SCREEN);
|
||||
break;
|
||||
case MacroActionSceneTransform::Action::CENTER_VERTICALLY:
|
||||
center(item, CenterType::VERTICAL);
|
||||
break;
|
||||
case MacroActionSceneTransform::Action::CENTER_HORIZONTALLY:
|
||||
center(item, CenterType::HORIZONTAL);
|
||||
break;
|
||||
case MacroActionSceneTransform::Action::MANUAL_TRANSFORM:
|
||||
ApplySettings(_settings); // Resolves variables
|
||||
obs_sceneitem_defer_update_begin(item);
|
||||
obs_sceneitem_set_info(item, &_info);
|
||||
obs_sceneitem_set_crop(item, &_crop);
|
||||
obs_sceneitem_defer_update_end(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
obs_sceneitem_release(item);
|
||||
bool MacroActionSceneTransform::PerformAction()
|
||||
{
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
for (const auto &item : items) {
|
||||
Transform(item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -30,15 +256,18 @@ bool MacroActionSceneTransform::PerformAction()
|
||||
void MacroActionSceneTransform::LogAction() const
|
||||
{
|
||||
vblog(LOG_INFO,
|
||||
"performed transform action for source \"%s\" on scene \"%s\"",
|
||||
_source.ToString(true).c_str(), _scene.ToString(true).c_str());
|
||||
"performed transform action %d for source \"%s\" on scene \"%s\"",
|
||||
static_cast<int>(_action), _source.ToString(true).c_str(),
|
||||
_scene.ToString(true).c_str());
|
||||
}
|
||||
|
||||
bool MacroActionSceneTransform::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
_scene.Save(obj);
|
||||
_source.Save(obj);
|
||||
_rotation.Save(obj, "rotation");
|
||||
_settings.Save(obj, "settings");
|
||||
return true;
|
||||
}
|
||||
@@ -53,8 +282,15 @@ bool MacroActionSceneTransform::Load(obs_data_t *obj)
|
||||
}
|
||||
|
||||
MacroAction::Load(obj);
|
||||
if (!obs_data_has_user_value(obj, "action")) {
|
||||
_action =
|
||||
Action::MANUAL_TRANSFORM; // TODO: Remove fallback in future
|
||||
} else {
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
}
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
_rotation.Load(obj, "rotation");
|
||||
_settings.Load(obj, "settings");
|
||||
|
||||
// Convert old data format
|
||||
@@ -107,21 +343,34 @@ void MacroActionSceneTransform::ApplySettings(const std::string &settings)
|
||||
}
|
||||
obs_data_release(obj);
|
||||
}
|
||||
for (auto item : items) {
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[type, name] : actions) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(type));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneTransform> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_scenes(new SceneSelectionWidget(window(), true, false, false, true)),
|
||||
_sources(new SceneItemSelectionWidget(parent)),
|
||||
_action(new QComboBox()),
|
||||
_rotation(new VariableDoubleSpinBox()),
|
||||
_getSettings(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneTransform.getTransform"))),
|
||||
_settings(new VariableTextEdit(this)),
|
||||
_buttonLayout(new QHBoxLayout())
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
|
||||
_settings = new VariableTextEdit(this);
|
||||
_rotation->setMinimum(-360);
|
||||
_rotation->setMaximum(360);
|
||||
_rotation->setSuffix("°");
|
||||
|
||||
populateActionSelection(_action);
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
@@ -130,6 +379,12 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
QWidget::connect(_action, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(
|
||||
_rotation,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
||||
this, SLOT(RotationChanged(const NumberVariable<double> &)));
|
||||
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
@@ -138,23 +393,21 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{settings}}", _settings},
|
||||
{"{{getSettings}}", _getSettings},
|
||||
{"{{scenes}}", _scenes}, {"{{rotation}}", _rotation},
|
||||
{"{{sources}}", _sources}, {"{{action}}", _action},
|
||||
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
|
||||
};
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.sceneTransform.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addWidget(_getSettings);
|
||||
buttonLayout->addStretch();
|
||||
_buttonLayout->addWidget(_getSettings);
|
||||
_buttonLayout->addStretch();
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(_settings);
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
mainLayout->addLayout(_buttonLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -170,10 +423,11 @@ void MacroActionSceneTransformEdit::UpdateEntryData()
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_sources->SetSceneItem(_entryData->_source);
|
||||
_action->setCurrentIndex(
|
||||
_action->findData(static_cast<int>(_entryData->_action)));
|
||||
_rotation->SetValue(_entryData->_rotation);
|
||||
_settings->setPlainText(_entryData->_settings);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
||||
@@ -196,6 +450,30 @@ void MacroActionSceneTransformEdit::SourceChanged(const SceneItemSelection &item
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::ActionChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionSceneTransform::Action>(
|
||||
_action->itemData(idx).toInt());
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::RotationChanged(const DoubleVariable &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_rotation = value;
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::GetSettingsClicked()
|
||||
@@ -211,9 +489,6 @@ void MacroActionSceneTransformEdit::GetSettingsClicked()
|
||||
|
||||
auto settings = GetSceneItemTransform(items[0]);
|
||||
_settings->setPlainText(FormatJsonString(settings));
|
||||
for (auto item : items) {
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::SettingsChanged()
|
||||
@@ -229,4 +504,19 @@ void MacroActionSceneTransformEdit::SettingsChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::SetWidgetVisibility()
|
||||
{
|
||||
SetLayoutVisible(
|
||||
_buttonLayout,
|
||||
_entryData->_action ==
|
||||
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
|
||||
_settings->setVisible(
|
||||
_entryData->_action ==
|
||||
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
|
||||
_rotation->setVisible(_entryData->_action ==
|
||||
MacroActionSceneTransform::Action::ROTATE);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
|
||||
@@ -22,11 +23,27 @@ public:
|
||||
return std::make_shared<MacroActionSceneTransform>(m);
|
||||
}
|
||||
|
||||
enum class Action {
|
||||
RESET,
|
||||
ROTATE,
|
||||
FLIP_HORIZONTAL,
|
||||
FLIP_VERTICAL,
|
||||
FIT_TO_SCREEN,
|
||||
STRETCH_TO_SCREEN,
|
||||
CENTER_TO_SCREEN,
|
||||
CENTER_VERTICALLY,
|
||||
CENTER_HORIZONTALLY,
|
||||
MANUAL_TRANSFORM = 100,
|
||||
};
|
||||
|
||||
Action _action = Action::RESET;
|
||||
SceneSelection _scene;
|
||||
SceneItemSelection _source;
|
||||
StringVariable _settings = "";
|
||||
DoubleVariable _rotation = 90.0;
|
||||
|
||||
private:
|
||||
void Transform(obs_scene_item *);
|
||||
void ApplySettings(const std::string &);
|
||||
std::string ConvertSettings();
|
||||
|
||||
@@ -57,19 +74,25 @@ public:
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void ActionChanged(int);
|
||||
void RotationChanged(const NumberVariable<double> &value);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
SceneSelectionWidget *_scenes;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QComboBox *_action;
|
||||
VariableDoubleSpinBox *_rotation;
|
||||
QPushButton *_getSettings;
|
||||
VariableTextEdit *_settings;
|
||||
std::shared_ptr<MacroActionSceneTransform> _entryData;
|
||||
QHBoxLayout *_buttonLayout;
|
||||
|
||||
private:
|
||||
std::shared_ptr<MacroActionSceneTransform> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,85 +11,38 @@ bool MacroActionSceneVisibility::_registered = MacroActionFactory::Register(
|
||||
MacroActionSceneVisibilityEdit::Create,
|
||||
"AdvSceneSwitcher.action.sceneVisibility"});
|
||||
|
||||
const static std::map<SceneVisibilityAction, std::string> actionTypes = {
|
||||
{SceneVisibilityAction::SHOW,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.show"},
|
||||
{SceneVisibilityAction::HIDE,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
|
||||
{SceneVisibilityAction::TOGGLE,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.toggle"},
|
||||
};
|
||||
|
||||
const static std::map<SceneItemSourceType, std::string> sourceItemSourceTypes = {
|
||||
{SceneItemSourceType::SOURCE,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.source"},
|
||||
{SceneItemSourceType::SOURCE_GROUP,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup"},
|
||||
};
|
||||
|
||||
struct VisibilityData {
|
||||
std::string name;
|
||||
SceneVisibilityAction action;
|
||||
const static std::map<MacroActionSceneVisibility::Action, std::string>
|
||||
actionTypes = {
|
||||
{MacroActionSceneVisibility::Action::SHOW,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.show"},
|
||||
{MacroActionSceneVisibility::Action::HIDE,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
|
||||
{MacroActionSceneVisibility::Action::TOGGLE,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.toggle"},
|
||||
};
|
||||
|
||||
static void setSceneItemVisibility(obs_sceneitem_t *item,
|
||||
SceneVisibilityAction action)
|
||||
MacroActionSceneVisibility::Action action)
|
||||
{
|
||||
switch (action) {
|
||||
case SceneVisibilityAction::SHOW:
|
||||
case MacroActionSceneVisibility::Action::SHOW:
|
||||
obs_sceneitem_set_visible(item, true);
|
||||
break;
|
||||
case SceneVisibilityAction::HIDE:
|
||||
case MacroActionSceneVisibility::Action::HIDE:
|
||||
obs_sceneitem_set_visible(item, false);
|
||||
break;
|
||||
case SceneVisibilityAction::TOGGLE:
|
||||
case MacroActionSceneVisibility::Action::TOGGLE:
|
||||
obs_sceneitem_set_visible(item, !obs_sceneitem_visible(item));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool visibilitySourceTypeEnum(obs_scene_t *, obs_sceneitem_t *item,
|
||||
void *ptr)
|
||||
{
|
||||
VisibilityData *vInfo = reinterpret_cast<VisibilityData *>(ptr);
|
||||
|
||||
auto sourceTypeName = obs_source_get_display_name(
|
||||
obs_source_get_id(obs_sceneitem_get_source(item)));
|
||||
if (sourceTypeName && vInfo->name == sourceTypeName) {
|
||||
setSceneItemVisibility(item, vInfo->action);
|
||||
}
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, visibilitySourceTypeEnum, ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSceneVisibility::PerformAction()
|
||||
{
|
||||
switch (_sourceType) {
|
||||
case SceneItemSourceType::SOURCE: {
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
for (auto item : items) {
|
||||
setSceneItemVisibility(item, _action);
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
break;
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
for (const auto &item : items) {
|
||||
setSceneItemVisibility(item, _action);
|
||||
}
|
||||
case SceneItemSourceType::SOURCE_GROUP: {
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene());
|
||||
auto scene = obs_scene_from_source(s);
|
||||
VisibilityData vInfo = {_sourceGroup, _action};
|
||||
obs_scene_enum_items(scene, visibilitySourceTypeEnum, &vInfo);
|
||||
obs_source_release(s);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -97,18 +50,11 @@ void MacroActionSceneVisibility::LogAction() const
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
if (_sourceType == SceneItemSourceType::SOURCE) {
|
||||
vblog(LOG_INFO,
|
||||
"performed visibility action \"%s\" for source \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(),
|
||||
_source.ToString(true).c_str(),
|
||||
_scene.ToString(true).c_str());
|
||||
} else {
|
||||
vblog(LOG_INFO,
|
||||
"performed visibility action \"%s\" for any source type \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(), _sourceGroup.c_str(),
|
||||
_scene.ToString(true).c_str());
|
||||
}
|
||||
vblog(LOG_INFO,
|
||||
"performed visibility action \"%s\" for source \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(), _source.ToString(true).c_str(),
|
||||
_scene.ToString(true).c_str());
|
||||
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown SceneVisibility action %d",
|
||||
static_cast<int>(_action));
|
||||
@@ -119,13 +65,8 @@ bool MacroActionSceneVisibility::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
if (_sourceType == SceneItemSourceType::SOURCE) {
|
||||
_source.Save(obj);
|
||||
} else {
|
||||
obs_data_set_string(obj, "sourceGroup", _sourceGroup.c_str());
|
||||
}
|
||||
_source.Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "sourceType", static_cast<int>(_sourceType));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,89 +77,64 @@ bool MacroActionSceneVisibility::Load(obs_data_t *obj)
|
||||
if (obs_data_has_user_value(obj, "source")) {
|
||||
auto sourceName = obs_data_get_string(obj, "source");
|
||||
obs_data_set_string(obj, "sceneItem", sourceName);
|
||||
obs_data_set_string(obj, "sourceGroup", sourceName);
|
||||
}
|
||||
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
_sourceType = static_cast<SceneItemSourceType>(
|
||||
obs_data_get_int(obj, "sourceType"));
|
||||
_sourceGroup = obs_data_get_string(obj, "sourceGroup");
|
||||
_action = static_cast<SceneVisibilityAction>(
|
||||
_action = static_cast<MacroActionSceneVisibility::Action>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_get_int(obj, "sourceType") != 0) {
|
||||
auto sourceGroup = obs_data_get_string(obj, "sourceGroup");
|
||||
_source.SetSourceTypeSelection(sourceGroup);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionSceneVisibility::GetShortDesc() const
|
||||
{
|
||||
if (_sourceType == SceneItemSourceType::SOURCE &&
|
||||
!_source.ToString().empty()) {
|
||||
if (!_source.ToString().empty()) {
|
||||
return _scene.ToString() + " - " + _source.ToString();
|
||||
}
|
||||
if (_sourceType == SceneItemSourceType::SOURCE_GROUP &&
|
||||
!_sourceGroup.empty()) {
|
||||
return _scene.ToString() + " - " +
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup") +
|
||||
" " + _sourceGroup;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateSourceItemTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : sourceItemSourceTypes) {
|
||||
for (const auto &entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_scenes(new SceneSelectionWidget(window(), true, false, true, true)),
|
||||
_sources(new SceneItemSelectionWidget(parent)),
|
||||
_actions(new QComboBox())
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), true, false, true, true);
|
||||
_sourceTypes = new QComboBox();
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_sourceGroups = new QComboBox();
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateSourceItemTypeSelection(_sourceTypes);
|
||||
PopulateSourceGroupSelection(_sourceGroups);
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sourceTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(SourceTypeChanged(int)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
QWidget::connect(_sourceGroups,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceGroupChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes}, {"{{sourceTypes}}", _sourceTypes},
|
||||
{"{{sources}}", _sources}, {"{{sourceGroups}}", _sourceGroups},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
auto layout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneVisibility.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
layout,
|
||||
{{"{{scenes}}", _scenes},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{actions}}", _actions}});
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
@@ -232,13 +148,8 @@ void MacroActionSceneVisibilityEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_sourceTypes->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_sourceType));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_sources->SetSceneItem((_entryData->_source));
|
||||
_sourceGroups->setCurrentText(
|
||||
QString::fromStdString(_entryData->_sourceGroup));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
@@ -251,17 +162,6 @@ void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceTypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_sourceType = static_cast<SceneItemSourceType>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceChanged(
|
||||
const SceneItemSelection &item)
|
||||
{
|
||||
@@ -273,22 +173,8 @@ void MacroActionSceneVisibilityEdit::SourceChanged(
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceGroupChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
if (text == obs_module_text("AdvSceneSwitcher.selectItem")) {
|
||||
_entryData->_sourceGroup = "";
|
||||
} else {
|
||||
_entryData->_sourceGroup = text.toStdString();
|
||||
}
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::ActionChanged(int value)
|
||||
@@ -298,19 +184,8 @@ void MacroActionSceneVisibilityEdit::ActionChanged(int value)
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<SceneVisibilityAction>(value);
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_sources->setVisible(_entryData->_sourceType ==
|
||||
SceneItemSourceType::SOURCE);
|
||||
_sourceGroups->setVisible(_entryData->_sourceType ==
|
||||
SceneItemSourceType::SOURCE_GROUP);
|
||||
adjustSize();
|
||||
_entryData->_action =
|
||||
static_cast<MacroActionSceneVisibility::Action>(value);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -7,17 +7,6 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class SceneVisibilityAction {
|
||||
SHOW,
|
||||
HIDE,
|
||||
TOGGLE,
|
||||
};
|
||||
|
||||
enum class SceneItemSourceType {
|
||||
SOURCE,
|
||||
SOURCE_GROUP,
|
||||
};
|
||||
|
||||
class MacroActionSceneVisibility : public MacroAction {
|
||||
public:
|
||||
MacroActionSceneVisibility(Macro *m) : MacroAction(m) {}
|
||||
@@ -33,10 +22,14 @@ public:
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
SceneItemSourceType _sourceType = SceneItemSourceType::SOURCE;
|
||||
SceneItemSelection _source;
|
||||
std::string _sourceGroup = "";
|
||||
SceneVisibilityAction _action = SceneVisibilityAction::SHOW;
|
||||
|
||||
enum class Action {
|
||||
SHOW,
|
||||
HIDE,
|
||||
TOGGLE,
|
||||
};
|
||||
Action _action = Action::SHOW;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -62,25 +55,18 @@ public:
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceTypeChanged(int value);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void SourceGroupChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sourceTypes;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QComboBox *_sourceGroups;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionSceneVisibility> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,15 +12,50 @@ bool MacroActionSource::_registered = MacroActionFactory::Register(
|
||||
{MacroActionSource::Create, MacroActionSourceEdit::Create,
|
||||
"AdvSceneSwitcher.action.source"});
|
||||
|
||||
const static std::map<SourceAction, std::string> actionTypes = {
|
||||
{SourceAction::ENABLE, "AdvSceneSwitcher.action.source.type.enable"},
|
||||
{SourceAction::DISABLE, "AdvSceneSwitcher.action.source.type.disable"},
|
||||
{SourceAction::SETTINGS,
|
||||
const static std::map<MacroActionSource::Action, std::string> actionTypes = {
|
||||
{MacroActionSource::Action::ENABLE,
|
||||
"AdvSceneSwitcher.action.source.type.enable"},
|
||||
{MacroActionSource::Action::DISABLE,
|
||||
"AdvSceneSwitcher.action.source.type.disable"},
|
||||
{MacroActionSource::Action::SETTINGS,
|
||||
"AdvSceneSwitcher.action.source.type.settings"},
|
||||
{SourceAction::REFRESH_SETTINGS,
|
||||
{MacroActionSource::Action::REFRESH_SETTINGS,
|
||||
"AdvSceneSwitcher.action.source.type.refreshSettings"},
|
||||
{SourceAction::SETTINGS_BUTTON,
|
||||
{MacroActionSource::Action::SETTINGS_BUTTON,
|
||||
"AdvSceneSwitcher.action.source.type.pressSettingsButton"},
|
||||
{MacroActionSource::Action::DEINTERLACE_MODE,
|
||||
"AdvSceneSwitcher.action.source.type.deinterlaceMode"},
|
||||
{MacroActionSource::Action::DEINTERLACE_FIELD_ORDER,
|
||||
"AdvSceneSwitcher.action.source.type.deinterlaceOrder"},
|
||||
};
|
||||
|
||||
const static std::map<obs_deinterlace_mode, std::string> deinterlaceModes = {
|
||||
{OBS_DEINTERLACE_MODE_DISABLE,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.disable"},
|
||||
{OBS_DEINTERLACE_MODE_DISCARD,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.disable"},
|
||||
{OBS_DEINTERLACE_MODE_RETRO,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.retro"},
|
||||
{OBS_DEINTERLACE_MODE_BLEND,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.blend"},
|
||||
{OBS_DEINTERLACE_MODE_BLEND_2X,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.blend2x"},
|
||||
{OBS_DEINTERLACE_MODE_LINEAR,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.linear"},
|
||||
{OBS_DEINTERLACE_MODE_LINEAR_2X,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.linear2x"},
|
||||
{OBS_DEINTERLACE_MODE_YADIF,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.yadif"},
|
||||
{OBS_DEINTERLACE_MODE_YADIF_2X,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceMode.yadif2x"},
|
||||
};
|
||||
|
||||
const static std::map<obs_deinterlace_field_order, std::string>
|
||||
deinterlaceFieldOrders = {
|
||||
{OBS_DEINTERLACE_FIELD_ORDER_TOP,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceOrder.topFieldFirst"},
|
||||
{OBS_DEINTERLACE_FIELD_ORDER_BOTTOM,
|
||||
"AdvSceneSwitcher.action.source.deinterlaceOrder.bottomFieldFirst"},
|
||||
};
|
||||
|
||||
static std::vector<SourceSettingButton> getSourceButtons(OBSWeakSource source)
|
||||
@@ -79,21 +114,27 @@ bool MacroActionSource::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_source.GetSource());
|
||||
switch (_action) {
|
||||
case SourceAction::ENABLE:
|
||||
case Action::ENABLE:
|
||||
obs_source_set_enabled(s, true);
|
||||
break;
|
||||
case SourceAction::DISABLE:
|
||||
case Action::DISABLE:
|
||||
obs_source_set_enabled(s, false);
|
||||
break;
|
||||
case SourceAction::SETTINGS:
|
||||
case Action::SETTINGS:
|
||||
SetSourceSettings(s, _settings);
|
||||
break;
|
||||
case SourceAction::REFRESH_SETTINGS:
|
||||
case Action::REFRESH_SETTINGS:
|
||||
refreshSourceSettings(s);
|
||||
break;
|
||||
case SourceAction::SETTINGS_BUTTON:
|
||||
case Action::SETTINGS_BUTTON:
|
||||
pressSourceButton(_button, s);
|
||||
break;
|
||||
case Action::DEINTERLACE_MODE:
|
||||
obs_source_set_deinterlace_mode(s, _deinterlaceMode);
|
||||
break;
|
||||
case Action::DEINTERLACE_FIELD_ORDER:
|
||||
obs_source_set_deinterlace_field_order(s, _deinterlaceOrder);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -120,6 +161,10 @@ bool MacroActionSource::Save(obs_data_t *obj) const
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
_button.Save(obj);
|
||||
_settings.Save(obj, "settings");
|
||||
obs_data_set_int(obj, "deinterlaceMode",
|
||||
static_cast<int>(_deinterlaceMode));
|
||||
obs_data_set_int(obj, "deinterlaceOrder",
|
||||
static_cast<int>(_deinterlaceOrder));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -127,9 +172,13 @@ bool MacroActionSource::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_source.Load(obj);
|
||||
_action = static_cast<SourceAction>(obs_data_get_int(obj, "action"));
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
_button.Load(obj);
|
||||
_settings.Load(obj, "settings");
|
||||
_deinterlaceMode = static_cast<obs_deinterlace_mode>(
|
||||
obs_data_get_int(obj, "deinterlaceMode"));
|
||||
_deinterlaceOrder = static_cast<obs_deinterlace_field_order>(
|
||||
obs_data_get_int(obj, "deinterlaceOrder"));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -143,7 +192,7 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto &[actionType, name] : actionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
if (actionType == SourceAction::REFRESH_SETTINGS) {
|
||||
if (actionType == MacroActionSource::Action::REFRESH_SETTINGS) {
|
||||
list->setItemData(
|
||||
list->count() - 1,
|
||||
obs_module_text(
|
||||
@@ -170,23 +219,44 @@ static inline void populateSourceButtonSelection(QComboBox *list,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateDeinterlaceModeSelection(QComboBox *list)
|
||||
{
|
||||
list->clear();
|
||||
for (const auto &[value, name] : deinterlaceModes) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(value));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateDeinterlaceFieldOrderSelection(QComboBox *list)
|
||||
{
|
||||
list->clear();
|
||||
for (const auto &[value, name] : deinterlaceFieldOrders) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(value));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSource> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||
_actions(new QComboBox()),
|
||||
_settingsButtons(new QComboBox()),
|
||||
_getSettings(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.action.source.getSettings"))),
|
||||
_settings(new VariableTextEdit(this)),
|
||||
_deinterlaceMode(new QComboBox()),
|
||||
_deinterlaceOrder(new QComboBox()),
|
||||
_warning(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.source.warning")))
|
||||
{
|
||||
_sources = new SourceSelectionWidget(this, QStringList(), true);
|
||||
_actions = new QComboBox();
|
||||
_settingsButtons = new QComboBox();
|
||||
_getSettings = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.action.source.getSettings"));
|
||||
_settings = new VariableTextEdit(this);
|
||||
_warning = new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.source.warning"));
|
||||
|
||||
populateActionSelection(_actions);
|
||||
auto sources = GetSourceNames();
|
||||
sources.sort();
|
||||
_sources->SetSourceNameList(sources);
|
||||
populateDeinterlaceModeSelection(_deinterlaceMode);
|
||||
populateDeinterlaceFieldOrderSelection(_deinterlaceOrder);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
@@ -199,6 +269,10 @@ MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
SLOT(SettingsChanged()));
|
||||
QWidget::connect(_deinterlaceMode, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(DeinterlaceModeChanged(int)));
|
||||
QWidget::connect(_deinterlaceOrder, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(DeinterlaceOrderChanged(int)));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
@@ -209,6 +283,8 @@ MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
{"{{settings}}", _settings},
|
||||
{"{{getSettings}}", _getSettings},
|
||||
{"{{settingsButtons}}", _settingsButtons},
|
||||
{"{{deinterlaceMode}}", _deinterlaceMode},
|
||||
{"{{deinterlaceOrder}}", _deinterlaceOrder},
|
||||
};
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.source.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
@@ -238,10 +314,11 @@ void MacroActionSourceEdit::UpdateEntryData()
|
||||
_settingsButtons->setCurrentText(
|
||||
QString::fromStdString(_entryData->_button.ToString()));
|
||||
_settings->setPlainText(_entryData->_settings);
|
||||
_deinterlaceMode->setCurrentIndex(_deinterlaceMode->findData(
|
||||
static_cast<int>(_entryData->_deinterlaceMode)));
|
||||
_deinterlaceOrder->setCurrentIndex(_deinterlaceOrder->findData(
|
||||
static_cast<int>(_entryData->_deinterlaceOrder)));
|
||||
SetWidgetVisibility();
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
|
||||
@@ -267,7 +344,7 @@ void MacroActionSourceEdit::ActionChanged(int value)
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<SourceAction>(value);
|
||||
_entryData->_action = static_cast<MacroActionSource::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -305,17 +382,50 @@ void MacroActionSourceEdit::SettingsChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::DeinterlaceModeChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_deinterlaceMode = static_cast<obs_deinterlace_mode>(
|
||||
_deinterlaceMode->itemData(idx).toInt());
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::DeinterlaceOrderChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_deinterlaceOrder =
|
||||
static_cast<obs_deinterlace_field_order>(
|
||||
_deinterlaceOrder->itemData(idx).toInt());
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::SetWidgetVisibility()
|
||||
{
|
||||
const bool showSettings = _entryData->_action == SourceAction::SETTINGS;
|
||||
const bool showWarning = _entryData->_action == SourceAction::ENABLE ||
|
||||
_entryData->_action == SourceAction::DISABLE;
|
||||
const bool showSettings = _entryData->_action ==
|
||||
MacroActionSource::Action::SETTINGS;
|
||||
const bool showWarning =
|
||||
_entryData->_action == MacroActionSource::Action::ENABLE ||
|
||||
_entryData->_action == MacroActionSource::Action::DISABLE;
|
||||
_settings->setVisible(showSettings);
|
||||
_getSettings->setVisible(showSettings);
|
||||
_warning->setVisible(showWarning);
|
||||
_settingsButtons->setVisible(_entryData->_action ==
|
||||
SourceAction::SETTINGS_BUTTON);
|
||||
_settingsButtons->setVisible(
|
||||
_entryData->_action ==
|
||||
MacroActionSource::Action::SETTINGS_BUTTON);
|
||||
_deinterlaceMode->setVisible(
|
||||
_entryData->_action ==
|
||||
MacroActionSource::Action::DEINTERLACE_MODE);
|
||||
_deinterlaceOrder->setVisible(
|
||||
_entryData->_action ==
|
||||
MacroActionSource::Action::DEINTERLACE_FIELD_ORDER);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
bool SourceSettingButton::Save(obs_data_t *obj) const
|
||||
|
||||
@@ -10,14 +10,6 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class SourceAction {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
SETTINGS,
|
||||
REFRESH_SETTINGS,
|
||||
SETTINGS_BUTTON,
|
||||
};
|
||||
|
||||
struct SourceSettingButton {
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
@@ -44,7 +36,20 @@ public:
|
||||
SourceSelection _source;
|
||||
SourceSettingButton _button;
|
||||
StringVariable _settings = "";
|
||||
SourceAction _action = SourceAction::ENABLE;
|
||||
obs_deinterlace_mode _deinterlaceMode = OBS_DEINTERLACE_MODE_DISABLE;
|
||||
obs_deinterlace_field_order _deinterlaceOrder =
|
||||
OBS_DEINTERLACE_FIELD_ORDER_TOP;
|
||||
|
||||
enum class Action {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
SETTINGS,
|
||||
REFRESH_SETTINGS,
|
||||
SETTINGS_BUTTON,
|
||||
DEINTERLACE_MODE,
|
||||
DEINTERLACE_FIELD_ORDER,
|
||||
};
|
||||
Action _action = Action::ENABLE;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -73,6 +78,8 @@ private slots:
|
||||
void ButtonChanged(int idx);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
void DeinterlaceModeChanged(int);
|
||||
void DeinterlaceOrderChanged(int);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -82,6 +89,8 @@ protected:
|
||||
QComboBox *_settingsButtons;
|
||||
QPushButton *_getSettings;
|
||||
VariableTextEdit *_settings;
|
||||
QComboBox *_deinterlaceMode;
|
||||
QComboBox *_deinterlaceOrder;
|
||||
QLabel *_warning;
|
||||
std::shared_ptr<MacroActionSource> _entryData;
|
||||
|
||||
|
||||
@@ -19,26 +19,65 @@ const static std::map<MacroActionStream::Action, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.streaming.type.start"},
|
||||
{MacroActionStream::Action::KEYFRAME_INTERVAL,
|
||||
"AdvSceneSwitcher.action.streaming.type.keyFrameInterval"},
|
||||
{MacroActionStream::Action::SERVER,
|
||||
"AdvSceneSwitcher.action.streaming.type.server"},
|
||||
{MacroActionStream::Action::STREAM_KEY,
|
||||
"AdvSceneSwitcher.action.streaming.type.streamKey"},
|
||||
{MacroActionStream::Action::USERNAME,
|
||||
"AdvSceneSwitcher.action.streaming.type.username"},
|
||||
{MacroActionStream::Action::PASSWORD,
|
||||
"AdvSceneSwitcher.action.streaming.type.password"},
|
||||
};
|
||||
|
||||
constexpr int streamStartCooldown = 5;
|
||||
std::chrono::high_resolution_clock::time_point MacroActionStream::s_lastAttempt =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
|
||||
void MacroActionStream::SetKeyFrameInterval()
|
||||
void MacroActionStream::SetKeyFrameInterval() const
|
||||
{
|
||||
const auto configPath = GetPathInProfileDir("streamEncoder.json");
|
||||
obs_data_t *settings =
|
||||
auto settings =
|
||||
obs_data_create_from_json_file_safe(configPath.c_str(), "bak");
|
||||
obs_data_set_int(settings, "keyint_sec", _keyFrameInterval);
|
||||
if (settings) {
|
||||
obs_data_save_json_safe(settings, configPath.c_str(), "tmp",
|
||||
"bak");
|
||||
obs_data_release(settings);
|
||||
if (!settings) {
|
||||
blog(LOG_WARNING, "failed to set key frame interval");
|
||||
return;
|
||||
}
|
||||
obs_data_set_int(settings, "keyint_sec", _keyFrameInterval);
|
||||
obs_data_save_json_safe(settings, configPath.c_str(), "tmp", "bak");
|
||||
obs_data_release(settings);
|
||||
}
|
||||
|
||||
bool MacroActionStream::CooldownDurationReached()
|
||||
void MacroActionStream::SetStreamSettingsValue(const char *name,
|
||||
const std::string &value,
|
||||
bool enableAuth) const
|
||||
{
|
||||
const auto configPath = GetPathInProfileDir("service.json");
|
||||
auto data =
|
||||
obs_data_create_from_json_file_safe(configPath.c_str(), "bak");
|
||||
if (!data) {
|
||||
blog(LOG_WARNING, "failed to set %s", name);
|
||||
return;
|
||||
}
|
||||
auto settings = obs_data_get_obj(data, "settings");
|
||||
if (!settings) {
|
||||
blog(LOG_WARNING, "failed to set %s", name);
|
||||
obs_data_release(data);
|
||||
return;
|
||||
}
|
||||
obs_data_set_string(settings, name, value.c_str());
|
||||
if (enableAuth) {
|
||||
obs_data_set_bool(settings, "use_auth", true);
|
||||
}
|
||||
obs_data_set_obj(data, "settings", settings);
|
||||
auto service = obs_frontend_get_streaming_service();
|
||||
obs_service_update(service, settings);
|
||||
obs_frontend_save_streaming_service();
|
||||
obs_frontend_set_streaming_service(service);
|
||||
obs_data_release(settings);
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
bool MacroActionStream::CooldownDurationReached() const
|
||||
{
|
||||
auto timePassed = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::high_resolution_clock::now() - s_lastAttempt);
|
||||
@@ -64,6 +103,18 @@ bool MacroActionStream::PerformAction()
|
||||
case Action::KEYFRAME_INTERVAL:
|
||||
SetKeyFrameInterval();
|
||||
break;
|
||||
case Action::SERVER:
|
||||
SetStreamSettingsValue("server", _stringValue);
|
||||
break;
|
||||
case Action::STREAM_KEY:
|
||||
SetStreamSettingsValue("key", _stringValue);
|
||||
break;
|
||||
case Action::USERNAME:
|
||||
SetStreamSettingsValue("username", _stringValue, true);
|
||||
break;
|
||||
case Action::PASSWORD:
|
||||
SetStreamSettingsValue("password", _stringValue, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -86,6 +137,7 @@ bool MacroActionStream::Save(obs_data_t *obj) const
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
_keyFrameInterval.Save(obj, "keyFrameInterval");
|
||||
_stringValue.Save(obj, "stringValue");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,6 +146,7 @@ bool MacroActionStream::Load(obs_data_t *obj)
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
_keyFrameInterval.Load(obj, "keyFrameInterval");
|
||||
_stringValue.Load(obj, "stringValue");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -108,11 +161,19 @@ MacroActionStreamEdit::MacroActionStreamEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionStream> entryData)
|
||||
: QWidget(parent),
|
||||
_actions(new QComboBox()),
|
||||
_keyFrameInterval(new VariableSpinBox())
|
||||
_keyFrameInterval(new VariableSpinBox()),
|
||||
_stringValue(new VariableLineEdit(this)),
|
||||
_showPassword(new QPushButton()),
|
||||
_layout(new QHBoxLayout())
|
||||
{
|
||||
_keyFrameInterval->setMinimum(0);
|
||||
_keyFrameInterval->setMaximum(25);
|
||||
|
||||
_showPassword->setMaximumWidth(22);
|
||||
_showPassword->setFlat(true);
|
||||
_showPassword->setStyleSheet(
|
||||
"QPushButton { background-color: transparent; border: 0px }");
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
@@ -122,13 +183,20 @@ MacroActionStreamEdit::MacroActionStreamEdit(
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this,
|
||||
SLOT(KeyFrameIntervalChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_stringValue, SIGNAL(editingFinished()), this,
|
||||
SLOT(StringValueChanged()));
|
||||
QWidget::connect(_showPassword, SIGNAL(pressed()), this,
|
||||
SLOT(ShowPassword()));
|
||||
QWidget::connect(_showPassword, SIGNAL(released()), this,
|
||||
SLOT(HidePassword()));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.streaming.entry"),
|
||||
mainLayout,
|
||||
_layout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{keyFrameInterval}}", _keyFrameInterval}});
|
||||
setLayout(mainLayout);
|
||||
{"{{keyFrameInterval}}", _keyFrameInterval},
|
||||
{"{{stringValue}}", _stringValue},
|
||||
{"{{showPassword}}", _showPassword}});
|
||||
setLayout(_layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
@@ -142,6 +210,7 @@ void MacroActionStreamEdit::UpdateEntryData()
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_keyFrameInterval->SetValue(_entryData->_keyFrameInterval);
|
||||
_stringValue->setText(_entryData->_stringValue);
|
||||
SetWidgetVisiblity();
|
||||
}
|
||||
|
||||
@@ -156,15 +225,59 @@ void MacroActionStreamEdit::KeyFrameIntervalChanged(
|
||||
_entryData->_keyFrameInterval = value;
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::StringValueChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_stringValue = _stringValue->text().toStdString();
|
||||
SetWidgetVisiblity();
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::ShowPassword()
|
||||
{
|
||||
SetButtonIcon(_showPassword, ":res/images/visible.svg");
|
||||
_stringValue->setEchoMode(QLineEdit::Normal);
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::HidePassword()
|
||||
{
|
||||
SetButtonIcon(_showPassword, ":res/images/invisible.svg");
|
||||
_stringValue->setEchoMode(QLineEdit::PasswordEchoOnEdit);
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::SetWidgetVisiblity()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = _entryData->_action;
|
||||
_keyFrameInterval->setVisible(
|
||||
_entryData->_action ==
|
||||
MacroActionStream::Action::KEYFRAME_INTERVAL);
|
||||
action == MacroActionStream::Action::KEYFRAME_INTERVAL);
|
||||
const bool modifyingStringValue =
|
||||
action == MacroActionStream::Action::SERVER ||
|
||||
action == MacroActionStream::Action::STREAM_KEY ||
|
||||
action == MacroActionStream::Action::USERNAME ||
|
||||
action == MacroActionStream::Action::PASSWORD;
|
||||
_stringValue->setVisible(modifyingStringValue);
|
||||
if (modifyingStringValue) {
|
||||
// Give as much space to string value line edit as posssible
|
||||
RemoveStretchIfPresent(_layout);
|
||||
} else {
|
||||
AddStretchIfNecessary(_layout);
|
||||
}
|
||||
if (action == MacroActionStream::Action::PASSWORD ||
|
||||
action == MacroActionStream::Action::STREAM_KEY) {
|
||||
_stringValue->setEchoMode(QLineEdit::PasswordEchoOnEdit);
|
||||
_showPassword->show();
|
||||
HidePassword();
|
||||
} else {
|
||||
_stringValue->setEchoMode(QLineEdit::Normal);
|
||||
_showPassword->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::ActionChanged(int value)
|
||||
@@ -172,9 +285,12 @@ void MacroActionStreamEdit::ActionChanged(int value)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionStream::Action>(value);
|
||||
{
|
||||
auto lock = LockContext();
|
||||
_entryData->_action =
|
||||
static_cast<MacroActionStream::Action>(value);
|
||||
}
|
||||
_stringValue->setText(QString(""));
|
||||
SetWidgetVisiblity();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QComboBox>
|
||||
@@ -26,13 +27,20 @@ public:
|
||||
STOP,
|
||||
START,
|
||||
KEYFRAME_INTERVAL,
|
||||
SERVER,
|
||||
STREAM_KEY,
|
||||
USERNAME,
|
||||
PASSWORD,
|
||||
};
|
||||
Action _action = Action::STOP;
|
||||
NumberVariable<int> _keyFrameInterval = 0;
|
||||
IntVariable _keyFrameInterval = 0;
|
||||
StringVariable _stringValue;
|
||||
|
||||
private:
|
||||
void SetKeyFrameInterval();
|
||||
bool CooldownDurationReached();
|
||||
void SetKeyFrameInterval() const;
|
||||
void SetStreamSettingsValue(const char *name, const std::string &value,
|
||||
bool enableAuth = false) const;
|
||||
bool CooldownDurationReached() const;
|
||||
static std::chrono::high_resolution_clock::time_point s_lastAttempt;
|
||||
|
||||
static bool _registered;
|
||||
@@ -58,16 +66,21 @@ public:
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
void KeyFrameIntervalChanged(const NumberVariable<int> &);
|
||||
void StringValueChanged();
|
||||
void ShowPassword();
|
||||
void HidePassword();
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
VariableSpinBox *_keyFrameInterval;
|
||||
VariableLineEdit *_stringValue;
|
||||
QPushButton *_showPassword;
|
||||
std::shared_ptr<MacroActionStream> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisiblity();
|
||||
|
||||
QHBoxLayout *_mainLayout;
|
||||
QHBoxLayout *_layout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ bool MacroActionTimer::_registered = MacroActionFactory::Register(
|
||||
{MacroActionTimer::Create, MacroActionTimerEdit::Create,
|
||||
"AdvSceneSwitcher.action.timer"});
|
||||
|
||||
static std::map<TimerAction, std::string> timerActions = {
|
||||
const static std::map<TimerAction, std::string> timerActions = {
|
||||
{TimerAction::PAUSE, "AdvSceneSwitcher.action.timer.type.pause"},
|
||||
{TimerAction::CONTINUE, "AdvSceneSwitcher.action.timer.type.continue"},
|
||||
{TimerAction::RESET, "AdvSceneSwitcher.action.timer.type.reset"},
|
||||
|
||||
@@ -88,7 +88,7 @@ void MacroActionTransition::SetSourceTransition(bool show)
|
||||
obs_source_release(transition);
|
||||
|
||||
const auto items = _source.GetSceneItems(_scene);
|
||||
for (auto &item : items) {
|
||||
for (const auto &item : items) {
|
||||
if (_setTransitionType) {
|
||||
obs_sceneitem_set_transition(item, show, t);
|
||||
}
|
||||
@@ -96,7 +96,6 @@ void MacroActionTransition::SetSourceTransition(bool show)
|
||||
obs_sceneitem_set_transition_duration(
|
||||
item, show, _duration.Milliseconds());
|
||||
}
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
|
||||
obs_source_release(t);
|
||||
@@ -200,7 +199,7 @@ std::string MacroActionTransition::GetShortDesc() const
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
for (const auto &entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
@@ -298,6 +297,8 @@ void MacroActionTransitionEdit::SourceChanged(const SceneItemSelection &item)
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionTransitionEdit::ActionChanged(int value)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "math-helpers.hpp"
|
||||
#include "non-modal-dialog.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
@@ -15,7 +16,7 @@ bool MacroActionVariable::_registered = MacroActionFactory::Register(
|
||||
{MacroActionVariable::Create, MacroActionVariableEdit::Create,
|
||||
"AdvSceneSwitcher.action.variable"});
|
||||
|
||||
static std::map<MacroActionVariable::Type, std::string> actionTypes = {
|
||||
const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
|
||||
{MacroActionVariable::Type::SET_FIXED_VALUE,
|
||||
"AdvSceneSwitcher.action.variable.type.set"},
|
||||
{MacroActionVariable::Type::APPEND,
|
||||
@@ -38,6 +39,8 @@ static std::map<MacroActionVariable::Type, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.variable.type.findAndReplace"},
|
||||
{MacroActionVariable::Type::MATH_EXPRESSION,
|
||||
"AdvSceneSwitcher.action.variable.type.mathExpression"},
|
||||
{MacroActionVariable::Type::USER_INPUT,
|
||||
"AdvSceneSwitcher.action.variable.type.askForValue"},
|
||||
};
|
||||
|
||||
static void apppend(Variable &var, const std::string &value)
|
||||
@@ -121,6 +124,19 @@ void MacroActionVariable::HandleMathExpression(Variable *var)
|
||||
var->SetValue(std::get<double>(result));
|
||||
}
|
||||
|
||||
struct AskForInputParams {
|
||||
QString prompt;
|
||||
std::optional<std::string> result;
|
||||
};
|
||||
|
||||
static void askForInput(void *param)
|
||||
{
|
||||
auto parameters = static_cast<AskForInputParams *>(param);
|
||||
auto dialog = new NonModalMessageDialog(
|
||||
parameters->prompt, NonModalMessageDialog::Type::INPUT);
|
||||
parameters->result = dialog->GetInput();
|
||||
}
|
||||
|
||||
bool MacroActionVariable::PerformAction()
|
||||
{
|
||||
auto var = _variable.lock();
|
||||
@@ -189,6 +205,22 @@ bool MacroActionVariable::PerformAction()
|
||||
HandleMathExpression(var.get());
|
||||
return true;
|
||||
}
|
||||
case Type::USER_INPUT: {
|
||||
AskForInputParams params{
|
||||
_useCustomPrompt
|
||||
? QString::fromStdString(_inputPrompt)
|
||||
: QString(obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.askForValuePromptDefault"))
|
||||
.arg(QString::fromStdString(
|
||||
var->Name())),
|
||||
{}};
|
||||
obs_queue_task(OBS_TASK_UI, askForInput, ¶ms, true);
|
||||
if (!params.result.has_value()) {
|
||||
return false;
|
||||
}
|
||||
var->SetValue(*params.result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -213,6 +245,8 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
|
||||
obs_data_set_string(obj, "replaceStr", _replaceStr.c_str());
|
||||
_regex.Save(obj);
|
||||
_mathExpression.Save(obj, "mathExpression");
|
||||
obs_data_set_bool(obj, "useCustomPrompt", _useCustomPrompt);
|
||||
_inputPrompt.Save(obj, "inputPrompt");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -235,6 +269,8 @@ bool MacroActionVariable::Load(obs_data_t *obj)
|
||||
_findStr = obs_data_get_string(obj, "findStr");
|
||||
_replaceStr = obs_data_get_string(obj, "replaceStr");
|
||||
_mathExpression.Load(obj, "mathExpression");
|
||||
_useCustomPrompt = obs_data_get_bool(obj, "useCustomPrompt");
|
||||
_inputPrompt.Load(obj, "inputPrompt");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -337,7 +373,8 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_actions(new QComboBox()),
|
||||
_strValue(new ResizingPlainTextEdit(this, 5, 1, 1)),
|
||||
_numValue(new QDoubleSpinBox()),
|
||||
_segmentIdx(new QSpinBox()),
|
||||
_segmentIdx(new MacroSegmentSelection(
|
||||
this, MacroSegmentSelection::Type::CONDITION, false)),
|
||||
_segmentValueStatus(new QLabel()),
|
||||
_segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)),
|
||||
_substringLayout(new QVBoxLayout()),
|
||||
@@ -352,13 +389,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_findStr(new ResizingPlainTextEdit(this, 10, 1, 1)),
|
||||
_replaceStr(new ResizingPlainTextEdit(this, 10, 1, 1)),
|
||||
_mathExpression(new VariableLineEdit(this)),
|
||||
_mathExpressionResult(new QLabel())
|
||||
_mathExpressionResult(new QLabel()),
|
||||
_promptLayout(new QHBoxLayout()),
|
||||
_useCustomPrompt(new QCheckBox()),
|
||||
_inputPrompt(new VariableLineEdit(this))
|
||||
{
|
||||
_numValue->setMinimum(-9999999999);
|
||||
_numValue->setMaximum(9999999999);
|
||||
_segmentIdx->setMinimum(0);
|
||||
_segmentIdx->setMaximum(99);
|
||||
_segmentIdx->setSpecialValueText("-");
|
||||
_segmentValue->setReadOnly(true);
|
||||
_subStringStart->setMinimum(1);
|
||||
_subStringStart->setMaximum(99999);
|
||||
@@ -371,6 +408,8 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_regexMatchIdx->setMinimum(1);
|
||||
_regexMatchIdx->setMaximum(999);
|
||||
_regexMatchIdx->setSuffix(".");
|
||||
_inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
populateTypeSelection(_actions);
|
||||
|
||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||
@@ -383,8 +422,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
SLOT(StrValueChanged()));
|
||||
QWidget::connect(_numValue, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(NumValueChanged(double)));
|
||||
QWidget::connect(_segmentIdx, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(SegmentIndexChanged(int)));
|
||||
QWidget::connect(_segmentIdx,
|
||||
SIGNAL(SelectionChanged(const IntVariable &)), this,
|
||||
SLOT(SegmentIndexChanged(const IntVariable &)));
|
||||
QWidget::connect(window(), SIGNAL(MacroSegmentOrderChanged()), this,
|
||||
SLOT(MacroSegmentOrderChanged()));
|
||||
QWidget::connect(_subStringStart, SIGNAL(valueChanged(int)), this,
|
||||
@@ -403,6 +443,10 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
SLOT(ReplaceStrValueChanged()));
|
||||
QWidget::connect(_mathExpression, SIGNAL(editingFinished()), this,
|
||||
SLOT(MathExpressionChanged()));
|
||||
QWidget::connect(_useCustomPrompt, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseCustomPromptChanged(int)));
|
||||
QWidget::connect(_inputPrompt, SIGNAL(editingFinished()), this,
|
||||
SLOT(InputPromptChanged()));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{variables}}", _variables},
|
||||
@@ -417,6 +461,8 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
{"{{findStr}}", _findStr},
|
||||
{"{{replaceStr}}", _replaceStr},
|
||||
{"{{mathExpression}}", _mathExpression},
|
||||
{"{{useCustomPrompt}}", _useCustomPrompt},
|
||||
{"{{inputPrompt}}", _inputPrompt},
|
||||
};
|
||||
auto entryLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
|
||||
@@ -437,6 +483,11 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
"AdvSceneSwitcher.action.variable.entry.findAndReplace"),
|
||||
_findReplaceLayout, widgetPlaceholders, false);
|
||||
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.userInput"),
|
||||
_promptLayout, widgetPlaceholders);
|
||||
|
||||
auto regexConfigLayout = new QHBoxLayout;
|
||||
regexConfigLayout->addWidget(_regex);
|
||||
regexConfigLayout->addStretch();
|
||||
@@ -453,6 +504,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
layout->addWidget(_segmentValue);
|
||||
layout->addLayout(_findReplaceLayout);
|
||||
layout->addWidget(_mathExpressionResult);
|
||||
layout->addLayout(_promptLayout);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -476,7 +528,13 @@ void MacroActionVariableEdit::UpdateEntryData()
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_strValue->setPlainText(QString::fromStdString(_entryData->_strValue));
|
||||
_numValue->setValue(_entryData->_numValue);
|
||||
_segmentIdx->setValue(_entryData->GetSegmentIndexValue() + 1);
|
||||
_segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1);
|
||||
_segmentIdx->SetMacro(_entryData->GetMacro());
|
||||
_segmentIdx->SetType(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE
|
||||
? MacroSegmentSelection::Type::CONDITION
|
||||
: MacroSegmentSelection::Type::ACTION);
|
||||
_subStringStart->setValue(_entryData->_subStringStart + 1);
|
||||
_subStringSize->setValue(_entryData->_subStringSize);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
@@ -487,6 +545,8 @@ void MacroActionVariableEdit::UpdateEntryData()
|
||||
_replaceStr->setPlainText(
|
||||
QString::fromStdString(_entryData->_replaceStr));
|
||||
_mathExpression->setText(_entryData->_mathExpression);
|
||||
_useCustomPrompt->setChecked(_entryData->_useCustomPrompt);
|
||||
_inputPrompt->setText(_entryData->_inputPrompt);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -518,13 +578,14 @@ void MacroActionVariableEdit::ActionChanged(int value)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_type = static_cast<MacroActionVariable::Type>(value);
|
||||
SetWidgetVisibility();
|
||||
|
||||
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE ||
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE) {
|
||||
MarkSelectedSegment();
|
||||
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE) {
|
||||
_segmentIdx->SetType(MacroSegmentSelection::Type::ACTION);
|
||||
} else if (_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_CONDITION_VALUE) {
|
||||
_segmentIdx->SetType(MacroSegmentSelection::Type::CONDITION);
|
||||
}
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::StrValueChanged()
|
||||
@@ -549,7 +610,7 @@ void MacroActionVariableEdit::NumValueChanged(double val)
|
||||
_entryData->_numValue = val;
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SegmentIndexChanged(int val)
|
||||
void MacroActionVariableEdit::SegmentIndexChanged(const IntVariable &val)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
@@ -557,7 +618,6 @@ void MacroActionVariableEdit::SegmentIndexChanged(int val)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->SetSegmentIndexValue(val - 1);
|
||||
MarkSelectedSegment();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SetSegmentValueError(const QString &text)
|
||||
@@ -590,7 +650,7 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
|
||||
SetSegmentValueError(obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.invalidSelection"));
|
||||
const QSignalBlocker b(_segmentIdx);
|
||||
_segmentIdx->setValue(index);
|
||||
_segmentIdx->SetValue(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -649,7 +709,7 @@ void MacroActionVariableEdit::UpdateSegmentVariableValue()
|
||||
void MacroActionVariableEdit::MacroSegmentOrderChanged()
|
||||
{
|
||||
const QSignalBlocker b(_segmentIdx);
|
||||
_segmentIdx->setValue(_entryData->GetSegmentIndexValue() + 1);
|
||||
_segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1);
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SubStringStartChanged(int val)
|
||||
@@ -752,40 +812,31 @@ void MacroActionVariableEdit::MathExpressionChanged()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::MarkSelectedSegment()
|
||||
void MacroActionVariableEdit::UseCustomPromptChanged(int value)
|
||||
{
|
||||
if (switcher->disableHints) {
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto m = _entryData->GetMacro();
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = _entryData->GetSegmentIndexValue();
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE) {
|
||||
const auto &actions = m->Actions();
|
||||
if (index >= (int)actions.size()) {
|
||||
return;
|
||||
}
|
||||
AdvSceneSwitcher::window->HighlightAction(
|
||||
index, QColor(Qt::lightGray));
|
||||
_inputPrompt->setVisible(value);
|
||||
if (value) {
|
||||
RemoveStretchIfPresent(_promptLayout);
|
||||
} else {
|
||||
const auto &conditions = m->Conditions();
|
||||
if (index >= (int)conditions.size()) {
|
||||
return;
|
||||
}
|
||||
AdvSceneSwitcher::window->HighlightCondition(
|
||||
index, QColor(Qt::lightGray));
|
||||
AddStretchIfNecessary(_promptLayout);
|
||||
}
|
||||
|
||||
PulseWidget(_segmentIdx, QColor(Qt::lightGray), QColor(0, 0, 0, 0),
|
||||
true);
|
||||
auto lock = LockContext();
|
||||
_entryData->_useCustomPrompt = value;
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::InputPromptChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_inputPrompt = _inputPrompt->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
@@ -833,7 +884,10 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
_mathExpression->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::MATH_EXPRESSION);
|
||||
_mathExpressionResult->hide();
|
||||
|
||||
SetLayoutVisible(_promptLayout,
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::USER_INPUT);
|
||||
_inputPrompt->setVisible(_entryData->_useCustomPrompt);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-segment-selection.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
@@ -35,6 +36,7 @@ public:
|
||||
SUBSTRING,
|
||||
FIND_AND_REPLACE,
|
||||
MATH_EXPRESSION,
|
||||
USER_INPUT,
|
||||
};
|
||||
|
||||
Type _type = Type::SET_FIXED_VALUE;
|
||||
@@ -53,6 +55,9 @@ public:
|
||||
"AdvSceneSwitcher.action.variable.findAndReplace.replace");
|
||||
StringVariable _mathExpression = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.mathExpression.example");
|
||||
bool _useCustomPrompt = false;
|
||||
StringVariable _inputPrompt = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.askForValuePrompt");
|
||||
|
||||
private:
|
||||
void DecrementCurrentSegmentVariableRef();
|
||||
@@ -89,7 +94,7 @@ private slots:
|
||||
void ActionChanged(int);
|
||||
void StrValueChanged();
|
||||
void NumValueChanged(double);
|
||||
void SegmentIndexChanged(int val);
|
||||
void SegmentIndexChanged(const IntVariable &);
|
||||
void UpdateSegmentVariableValue();
|
||||
void MacroSegmentOrderChanged();
|
||||
void SubStringStartChanged(int val);
|
||||
@@ -100,6 +105,8 @@ private slots:
|
||||
void FindStrValueChanged();
|
||||
void ReplaceStrValueChanged();
|
||||
void MathExpressionChanged();
|
||||
void UseCustomPromptChanged(int);
|
||||
void InputPromptChanged();
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -110,7 +117,7 @@ protected:
|
||||
QComboBox *_actions;
|
||||
ResizingPlainTextEdit *_strValue;
|
||||
QDoubleSpinBox *_numValue;
|
||||
QSpinBox *_segmentIdx;
|
||||
MacroSegmentSelection *_segmentIdx;
|
||||
QLabel *_segmentValueStatus;
|
||||
ResizingPlainTextEdit *_segmentValue;
|
||||
QVBoxLayout *_substringLayout;
|
||||
@@ -126,10 +133,12 @@ protected:
|
||||
ResizingPlainTextEdit *_replaceStr;
|
||||
VariableLineEdit *_mathExpression;
|
||||
QLabel *_mathExpressionResult;
|
||||
QHBoxLayout *_promptLayout;
|
||||
QCheckBox *_useCustomPrompt;
|
||||
VariableLineEdit *_inputPrompt;
|
||||
std::shared_ptr<MacroActionVariable> _entryData;
|
||||
|
||||
private:
|
||||
void MarkSelectedSegment();
|
||||
void SetWidgetVisibility();
|
||||
void SetSegmentValueError(const QString &);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ bool MacroActionWait::_registered = MacroActionFactory::Register(
|
||||
{MacroActionWait::Create, MacroActionWaitEdit::Create,
|
||||
"AdvSceneSwitcher.action.wait"});
|
||||
|
||||
static std::map<WaitType, std::string> waitTypes = {
|
||||
const static std::map<WaitType, std::string> waitTypes = {
|
||||
{WaitType::FIXED, "AdvSceneSwitcher.action.wait.type.fixed"},
|
||||
{WaitType::RANDOM, "AdvSceneSwitcher.action.wait.type.random"},
|
||||
};
|
||||
|
||||
@@ -10,47 +10,75 @@ bool MacroActionWebsocket::_registered = MacroActionFactory::Register(
|
||||
{MacroActionWebsocket::Create, MacroActionWebsocketEdit::Create,
|
||||
"AdvSceneSwitcher.action.websocket"});
|
||||
|
||||
static std::map<MacroActionWebsocket::Type, std::string> actionTypes = {
|
||||
{MacroActionWebsocket::Type::REQUEST,
|
||||
"AdvSceneSwitcher.action.websocket.type.request"},
|
||||
{MacroActionWebsocket::Type::EVENT,
|
||||
"AdvSceneSwitcher.action.websocket.type.event"},
|
||||
const static std::map<MacroActionWebsocket::API, std::string> apiTypes = {
|
||||
{MacroActionWebsocket::API::SCENE_SWITCHER,
|
||||
"AdvSceneSwitcher.action.websocket.api.sceneSwitcher"},
|
||||
{MacroActionWebsocket::API::OBS_WEBSOCKET,
|
||||
"AdvSceneSwitcher.action.websocket.api.obsWebsocket"},
|
||||
{MacroActionWebsocket::API::GENERIC_WEBSOCKET,
|
||||
"AdvSceneSwitcher.action.websocket.api.genericWebsocket"},
|
||||
};
|
||||
|
||||
void MacroActionWebsocket::SendRequest()
|
||||
const static std::map<MacroActionWebsocket::MessageType, std::string>
|
||||
messageTypes = {
|
||||
{MacroActionWebsocket::MessageType::REQUEST,
|
||||
"AdvSceneSwitcher.action.websocket.type.request"},
|
||||
{MacroActionWebsocket::MessageType::EVENT,
|
||||
"AdvSceneSwitcher.action.websocket.type.event"},
|
||||
};
|
||||
|
||||
void MacroActionWebsocket::SendRequest(const std::string &msg)
|
||||
{
|
||||
auto connection = _connection.lock();
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
connection->SendMsg(_message);
|
||||
|
||||
connection->SendMsg(msg);
|
||||
}
|
||||
|
||||
bool MacroActionWebsocket::PerformAction()
|
||||
{
|
||||
switch (_type) {
|
||||
case MacroActionWebsocket::Type::REQUEST:
|
||||
SendRequest();
|
||||
break;
|
||||
case MacroActionWebsocket::Type::EVENT:
|
||||
SendWebsocketEvent(_message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (_api != MacroActionWebsocket::API::SCENE_SWITCHER) {
|
||||
SendRequest(_message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_type == MacroActionWebsocket::MessageType::REQUEST) {
|
||||
auto vendorRequest = ConstructVendorRequestMessage(_message);
|
||||
SendRequest(vendorRequest);
|
||||
} else {
|
||||
SendWebsocketEvent(_message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionWebsocket::LogAction() const
|
||||
{
|
||||
if (_api == API::GENERIC_WEBSOCKET) {
|
||||
vblog(LOG_INFO,
|
||||
"sent generic websocket message \"%s\" via \"%s\"",
|
||||
_message.c_str(),
|
||||
GetWeakConnectionName(_connection).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (_api == API::OBS_WEBSOCKET) {
|
||||
vblog(LOG_INFO, "sent obs websocket message \"%s\" via \"%s\"",
|
||||
_message.c_str(),
|
||||
GetWeakConnectionName(_connection).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_type) {
|
||||
case MacroActionWebsocket::Type::REQUEST:
|
||||
vblog(LOG_INFO, "sent msg \"%s\" via \"%s\"", _message.c_str(),
|
||||
case MacroActionWebsocket::MessageType::REQUEST:
|
||||
vblog(LOG_INFO, "sent scene switcher message \"%s\" via \"%s\"",
|
||||
_message.c_str(),
|
||||
GetWeakConnectionName(_connection).c_str());
|
||||
break;
|
||||
case MacroActionWebsocket::Type::EVENT:
|
||||
vblog(LOG_INFO, "sent event \"%s\" to connected clients",
|
||||
case MacroActionWebsocket::MessageType::EVENT:
|
||||
vblog(LOG_INFO,
|
||||
"sent scene switcher event \"%s\" to connected clients",
|
||||
_message.c_str());
|
||||
break;
|
||||
default:
|
||||
@@ -61,6 +89,7 @@ void MacroActionWebsocket::LogAction() const
|
||||
bool MacroActionWebsocket::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "api", static_cast<int>(_api));
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
_message.Save(obj, "message");
|
||||
obs_data_set_string(obj, "connection",
|
||||
@@ -71,7 +100,8 @@ bool MacroActionWebsocket::Save(obs_data_t *obj) const
|
||||
bool MacroActionWebsocket::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||
_api = static_cast<API>(obs_data_get_int(obj, "api"));
|
||||
_type = static_cast<MessageType>(obs_data_get_int(obj, "type"));
|
||||
_message.Load(obj, "message");
|
||||
_connection =
|
||||
GetWeakConnectionByName(obs_data_get_string(obj, "connection"));
|
||||
@@ -80,40 +110,54 @@ bool MacroActionWebsocket::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroActionWebsocket::GetShortDesc() const
|
||||
{
|
||||
if (_type == Type::REQUEST) {
|
||||
if (_type == MessageType::REQUEST) {
|
||||
return GetWeakConnectionName(_connection);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
static inline void populateAPISelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
for (const auto &[_, name] : apiTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateMessageTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : messageTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionWebsocketEdit::MacroActionWebsocketEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionWebsocket> entryData)
|
||||
: QWidget(parent),
|
||||
_actions(new QComboBox(this)),
|
||||
_apiType(new QComboBox(this)),
|
||||
_messageType(new QComboBox(this)),
|
||||
_message(new VariableTextEdit(this)),
|
||||
_connection(new ConnectionSelection(this)),
|
||||
_editLayout(new QHBoxLayout())
|
||||
_editLayout(new QHBoxLayout()),
|
||||
_settingsConflict(new QLabel())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
populateAPISelection(_apiType);
|
||||
populateMessageTypeSelection(_messageType);
|
||||
_settingsConflict->setWordWrap(true);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_apiType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(APITypeChanged(int)));
|
||||
QWidget::connect(_messageType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(MessageTypeChanged(int)));
|
||||
QWidget::connect(_message, SIGNAL(textChanged()), this,
|
||||
SLOT(MessageChanged()));
|
||||
QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)),
|
||||
this,
|
||||
SLOT(ConnectionSelectionChanged(const QString &)));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(_editLayout);
|
||||
mainLayout->addWidget(_message);
|
||||
mainLayout->addWidget(_settingsConflict);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -121,34 +165,111 @@ MacroActionWebsocketEdit::MacroActionWebsocketEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::CheckForSettingsConflict()
|
||||
{
|
||||
auto connection = _entryData->_connection.lock();
|
||||
if (!connection) {
|
||||
_settingsConflict->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entryData->_api == MacroActionWebsocket::API::GENERIC_WEBSOCKET &&
|
||||
connection->IsUsingOBSProtocol()) {
|
||||
_settingsConflict->show();
|
||||
_settingsConflict->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.settingsConflictGeneric"));
|
||||
} else if (_entryData->_api !=
|
||||
MacroActionWebsocket::API::GENERIC_WEBSOCKET &&
|
||||
!connection->IsUsingOBSProtocol()) {
|
||||
_settingsConflict->show();
|
||||
_settingsConflict->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.settingsConflictOBS"));
|
||||
} else {
|
||||
_settingsConflict->hide();
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::SetupWidgetVisibility()
|
||||
{
|
||||
_messageType->setVisible(_entryData->_api ==
|
||||
MacroActionWebsocket::API::SCENE_SWITCHER);
|
||||
switch (_entryData->_api) {
|
||||
case MacroActionWebsocket::API::SCENE_SWITCHER:
|
||||
if (_entryData->_type ==
|
||||
MacroActionWebsocket::MessageType::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
} else {
|
||||
SetupEventEdit();
|
||||
}
|
||||
break;
|
||||
case MacroActionWebsocket::API::OBS_WEBSOCKET:
|
||||
case MacroActionWebsocket::API::GENERIC_WEBSOCKET:
|
||||
SetupGenericEdit();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CheckForSettingsConflict();
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::SetupRequestEdit()
|
||||
{
|
||||
_editLayout->removeWidget(_actions);
|
||||
_editLayout->removeWidget(_connection);
|
||||
ClearLayout(_editLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", _actions},
|
||||
{"{{connection}}", _connection},
|
||||
};
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.entry.request"),
|
||||
_editLayout, widgetPlaceholders);
|
||||
ClearWidgets();
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request"),
|
||||
_editLayout,
|
||||
{{"{{api}}", _apiType},
|
||||
{"{{type}}", _messageType},
|
||||
{"{{connection}}", _connection}});
|
||||
_connection->show();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::SetupEventEdit()
|
||||
{
|
||||
_editLayout->removeWidget(_actions);
|
||||
ClearWidgets();
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event"),
|
||||
_editLayout,
|
||||
{{"{{api}}", _apiType},
|
||||
{"{{type}}", _messageType},
|
||||
{"{{connection}}", _connection}});
|
||||
|
||||
// Add widget to layout but hide it
|
||||
_editLayout->addWidget(_connection);
|
||||
_connection->hide();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::SetupGenericEdit()
|
||||
{
|
||||
ClearWidgets();
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.entry.generic"),
|
||||
_editLayout,
|
||||
{{"{{api}}", _apiType},
|
||||
{"{{type}}", _messageType},
|
||||
{"{{connection}}", _connection}});
|
||||
_connection->show();
|
||||
|
||||
// Add widget to layout but hide it
|
||||
_editLayout->addWidget(_messageType);
|
||||
_messageType->hide();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::ClearWidgets()
|
||||
{
|
||||
_editLayout->removeWidget(_apiType);
|
||||
_editLayout->removeWidget(_messageType);
|
||||
_editLayout->removeWidget(_connection);
|
||||
ClearLayout(_editLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", _actions},
|
||||
{"{{connection}}", _connection},
|
||||
};
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.websocket.entry.event"),
|
||||
_editLayout, widgetPlaceholders);
|
||||
_connection->hide();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::UpdateEntryData()
|
||||
@@ -157,33 +278,37 @@ void MacroActionWebsocketEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_apiType->setCurrentIndex(static_cast<int>(_entryData->_api));
|
||||
_messageType->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_message->setPlainText(_entryData->_message);
|
||||
_connection->SetConnection(_entryData->_connection);
|
||||
|
||||
if (_entryData->_type == MacroActionWebsocket::Type::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
} else {
|
||||
SetupEventEdit();
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
SetupWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::ActionChanged(int index)
|
||||
void MacroActionWebsocketEdit::APITypeChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_type = static_cast<MacroActionWebsocket::Type>(index);
|
||||
if (_entryData->_type == MacroActionWebsocket::Type::REQUEST) {
|
||||
SetupRequestEdit();
|
||||
} else {
|
||||
SetupEventEdit();
|
||||
_entryData->_api = static_cast<MacroActionWebsocket::API>(index);
|
||||
SetupWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionWebsocketEdit::MessageTypeChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_type =
|
||||
static_cast<MacroActionWebsocket::MessageType>(index);
|
||||
SetupWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -210,6 +335,7 @@ void MacroActionWebsocketEdit::ConnectionSelectionChanged(
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_connection = GetWeakConnectionByQString(connection);
|
||||
CheckForSettingsConflict();
|
||||
emit(HeaderInfoChanged(connection));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,17 +24,24 @@ public:
|
||||
return std::make_shared<MacroActionWebsocket>(m);
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
enum class API {
|
||||
SCENE_SWITCHER,
|
||||
OBS_WEBSOCKET,
|
||||
GENERIC_WEBSOCKET,
|
||||
};
|
||||
|
||||
enum class MessageType {
|
||||
REQUEST,
|
||||
EVENT,
|
||||
};
|
||||
|
||||
Type _type = Type::REQUEST;
|
||||
API _api = API::SCENE_SWITCHER;
|
||||
MessageType _type = MessageType::REQUEST;
|
||||
StringVariable _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
std::weak_ptr<Connection> _connection;
|
||||
|
||||
private:
|
||||
void SendRequest();
|
||||
void SendRequest(const std::string &msg);
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
@@ -57,7 +64,8 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int);
|
||||
void APITypeChanged(int);
|
||||
void MessageTypeChanged(int);
|
||||
void MessageChanged();
|
||||
void ConnectionSelectionChanged(const QString &);
|
||||
signals:
|
||||
@@ -67,13 +75,20 @@ protected:
|
||||
std::shared_ptr<MacroActionWebsocket> _entryData;
|
||||
|
||||
private:
|
||||
void CheckForSettingsConflict();
|
||||
void SetupWidgetVisibility();
|
||||
void SetupRequestEdit();
|
||||
void SetupEventEdit();
|
||||
void SetupGenericEdit();
|
||||
void ClearWidgets();
|
||||
|
||||
QComboBox *_actions;
|
||||
QComboBox *_apiType;
|
||||
QComboBox *_messageType;
|
||||
VariableTextEdit *_message;
|
||||
ConnectionSelection *_connection;
|
||||
QHBoxLayout *_editLayout;
|
||||
QLabel *_settingsConflict;
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,12 +11,15 @@ bool MacroAction::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroSegment::Save(obj);
|
||||
obs_data_set_string(obj, "id", GetId().c_str());
|
||||
obs_data_set_bool(obj, "enabled", _enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroAction::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroSegment::Load(obj);
|
||||
obs_data_set_default_bool(obj, "enabled", true);
|
||||
_enabled = obs_data_get_bool(obj, "enabled");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -25,6 +28,16 @@ void MacroAction::LogAction() const
|
||||
vblog(LOG_INFO, "performed action %s", GetId().c_str());
|
||||
}
|
||||
|
||||
void MacroAction::SetEnabled(bool value)
|
||||
{
|
||||
_enabled = value;
|
||||
}
|
||||
|
||||
bool MacroAction::Enabled() const
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
MacroRefAction::MacroRefAction(Macro *m, bool supportsVariableValue)
|
||||
: MacroAction(m, supportsVariableValue)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,11 @@ public:
|
||||
virtual bool Save(obs_data_t *obj) const = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
virtual void LogAction() const;
|
||||
void SetEnabled(bool);
|
||||
bool Enabled() const;
|
||||
|
||||
private:
|
||||
bool _enabled = true;
|
||||
};
|
||||
|
||||
class MacroRefAction : virtual public MacroAction {
|
||||
|
||||
@@ -12,7 +12,7 @@ bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionAudio::Create, MacroConditionAudioEdit::Create,
|
||||
"AdvSceneSwitcher.condition.audio"});
|
||||
|
||||
static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
|
||||
const static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
|
||||
{MacroConditionAudio::Type::OUTPUT_VOLUME,
|
||||
"AdvSceneSwitcher.condition.audio.type.output"},
|
||||
{MacroConditionAudio::Type::CONFIGURED_VOLUME,
|
||||
@@ -25,7 +25,7 @@ static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
|
||||
"AdvSceneSwitcher.condition.audio.type.balance"},
|
||||
};
|
||||
|
||||
static std::map<MacroConditionAudio::OutputCondition, std::string>
|
||||
const static std::map<MacroConditionAudio::OutputCondition, std::string>
|
||||
audioOutputConditionTypes = {
|
||||
{MacroConditionAudio::OutputCondition::ABOVE,
|
||||
"AdvSceneSwitcher.condition.audio.state.above"},
|
||||
@@ -33,7 +33,7 @@ static std::map<MacroConditionAudio::OutputCondition, std::string>
|
||||
"AdvSceneSwitcher.condition.audio.state.below"},
|
||||
};
|
||||
|
||||
static std::map<MacroConditionAudio::VolumeCondition, std::string>
|
||||
const static std::map<MacroConditionAudio::VolumeCondition, std::string>
|
||||
audioVolumeConditionTypes = {
|
||||
{MacroConditionAudio::VolumeCondition::ABOVE,
|
||||
"AdvSceneSwitcher.condition.audio.state.above"},
|
||||
|
||||
@@ -15,7 +15,7 @@ bool MacroConditionCursor::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionCursor::Create, MacroConditionCursorEdit::Create,
|
||||
"AdvSceneSwitcher.condition.cursor"});
|
||||
|
||||
static std::map<MacroConditionCursor::Condition, std::string>
|
||||
const static std::map<MacroConditionCursor::Condition, std::string>
|
||||
cursorConditionTypes = {
|
||||
{MacroConditionCursor::Condition::REGION,
|
||||
"AdvSceneSwitcher.condition.cursor.type.region"},
|
||||
@@ -27,7 +27,7 @@ static std::map<MacroConditionCursor::Condition, std::string>
|
||||
#endif
|
||||
};
|
||||
|
||||
static std::map<MacroConditionCursor::Button, std::string> buttonTypes = {
|
||||
const static std::map<MacroConditionCursor::Button, std::string> buttonTypes = {
|
||||
{MacroConditionCursor::Button::LEFT,
|
||||
"AdvSceneSwitcher.condition.cursor.button.left"},
|
||||
{MacroConditionCursor::Button::MIDDLE,
|
||||
|
||||
@@ -14,8 +14,8 @@ bool MacroConditionDate::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionDate::Create, MacroConditionDateEdit::Create,
|
||||
"AdvSceneSwitcher.condition.date", false});
|
||||
|
||||
static std::map<MacroConditionDate::Condition, std::string> dateConditionTypes =
|
||||
{
|
||||
const static std::map<MacroConditionDate::Condition, std::string>
|
||||
dateConditionTypes = {
|
||||
{MacroConditionDate::Condition::AT,
|
||||
"AdvSceneSwitcher.condition.date.state.at"},
|
||||
{MacroConditionDate::Condition::AFTER,
|
||||
@@ -28,8 +28,8 @@ static std::map<MacroConditionDate::Condition, std::string> dateConditionTypes =
|
||||
"AdvSceneSwitcher.condition.date.state.pattern"},
|
||||
};
|
||||
|
||||
static std::map<MacroConditionDate::Condition, std::string> weekConditionTypes =
|
||||
{
|
||||
const static std::map<MacroConditionDate::Condition, std::string>
|
||||
weekConditionTypes = {
|
||||
{MacroConditionDate::Condition::AT,
|
||||
"AdvSceneSwitcher.condition.date.state.at"},
|
||||
{MacroConditionDate::Condition::AFTER,
|
||||
@@ -38,7 +38,7 @@ static std::map<MacroConditionDate::Condition, std::string> weekConditionTypes =
|
||||
"AdvSceneSwitcher.condition.date.state.before"},
|
||||
};
|
||||
|
||||
static std::map<MacroConditionDate::Day, std::string> dayOfWeekNames = {
|
||||
const static std::map<MacroConditionDate::Day, std::string> dayOfWeekNames = {
|
||||
{MacroConditionDate::Day::ANY,
|
||||
"AdvSceneSwitcher.condition.date.anyDay"},
|
||||
{MacroConditionDate::Day::MONDAY,
|
||||
|
||||
@@ -12,11 +12,12 @@ bool MacroConditionDisplay::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionDisplay::Create, MacroConditionDisplayEdit::Create,
|
||||
"AdvSceneSwitcher.condition.display"});
|
||||
|
||||
static std::map<MacroConditionDisplay::Condition, std::string> conditionTypes = {
|
||||
{MacroConditionDisplay::Condition::DISPLAY_NAME,
|
||||
"AdvSceneSwitcher.condition.display.type.displayName"},
|
||||
{MacroConditionDisplay::Condition::DISPLAY_COUNT,
|
||||
"AdvSceneSwitcher.condition.display.type.displayCount"},
|
||||
const static std::map<MacroConditionDisplay::Condition, std::string>
|
||||
conditionTypes = {
|
||||
{MacroConditionDisplay::Condition::DISPLAY_NAME,
|
||||
"AdvSceneSwitcher.condition.display.type.displayName"},
|
||||
{MacroConditionDisplay::Condition::DISPLAY_COUNT,
|
||||
"AdvSceneSwitcher.condition.display.type.displayCount"},
|
||||
};
|
||||
|
||||
static bool matchDisplayNames(const std::string &name, const RegexConfig ®ex)
|
||||
|
||||
@@ -182,7 +182,7 @@ MacroConditionEdit::MacroConditionEdit(
|
||||
: MacroSegmentEdit(switcher->macroProperties._highlightConditions,
|
||||
parent),
|
||||
_logicSelection(new QComboBox()),
|
||||
_conditionSelection(new QComboBox()),
|
||||
_conditionSelection(new FilterComboBox()),
|
||||
_dur(new DurationModifierEdit()),
|
||||
_entryData(entryData),
|
||||
_isRoot(root)
|
||||
@@ -295,13 +295,16 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
auto idx = _entryData->get()->GetIndex();
|
||||
auto macro = _entryData->get()->GetMacro();
|
||||
std::string id = MacroConditionFactory::GetIdByName(text);
|
||||
if (id.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto temp = DurationModifier();
|
||||
_dur->SetValue(temp);
|
||||
HeaderInfoChanged("");
|
||||
auto idx = _entryData->get()->GetIndex();
|
||||
auto macro = _entryData->get()->GetMacro();
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto logic = (*_entryData)->GetLogicType();
|
||||
@@ -454,24 +457,45 @@ void AdvSceneSwitcher::on_conditionRemove_clicked()
|
||||
MacroConditionSelectionChanged(-1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionUp_clicked()
|
||||
void AdvSceneSwitcher::on_conditionTop_clicked()
|
||||
{
|
||||
if (currentConditionIdx == -1) {
|
||||
return;
|
||||
}
|
||||
MacroConditionReorder(0, currentConditionIdx);
|
||||
MacroConditionSelectionChanged(0);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionUp_clicked()
|
||||
{
|
||||
if (currentConditionIdx == -1 || currentConditionIdx == 0) {
|
||||
return;
|
||||
}
|
||||
MoveMacroConditionUp(currentConditionIdx);
|
||||
MacroConditionSelectionChanged(currentConditionIdx - 1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionDown_clicked()
|
||||
{
|
||||
if (currentConditionIdx == -1) {
|
||||
if (currentConditionIdx == -1 ||
|
||||
currentConditionIdx ==
|
||||
conditionsList->ContentLayout()->count() - 1) {
|
||||
return;
|
||||
}
|
||||
MoveMacroConditionDown(currentConditionIdx);
|
||||
MacroConditionSelectionChanged(currentConditionIdx + 1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionBottom_clicked()
|
||||
{
|
||||
if (currentConditionIdx == -1) {
|
||||
return;
|
||||
}
|
||||
const int newIdx = conditionsList->ContentLayout()->count() - 1;
|
||||
MacroConditionReorder(newIdx, currentConditionIdx);
|
||||
MacroConditionSelectionChanged(newIdx);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SwapConditions(Macro *m, int pos1, int pos2)
|
||||
{
|
||||
if (pos1 == pos2) {
|
||||
@@ -565,8 +589,8 @@ void AdvSceneSwitcher::MacroConditionReorder(int to, int from)
|
||||
return;
|
||||
}
|
||||
|
||||
if (from < 0 || from > (int)macro->Conditions().size() || to < 0 ||
|
||||
to > (int)macro->Conditions().size()) {
|
||||
if (to == from || from < 0 || from > (int)macro->Conditions().size() ||
|
||||
to < 0 || to > (int)macro->Conditions().size()) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "macro-condition.hpp"
|
||||
#include "filter-combo-box.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -77,7 +78,7 @@ private:
|
||||
std::shared_ptr<MacroSegment> Data();
|
||||
|
||||
QComboBox *_logicSelection;
|
||||
QComboBox *_conditionSelection;
|
||||
FilterComboBox *_conditionSelection;
|
||||
DurationModifierEdit *_dur;
|
||||
|
||||
std::shared_ptr<MacroCondition> *_entryData;
|
||||
|
||||
@@ -12,7 +12,7 @@ bool MacroConditionFilter::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionFilter::Create, MacroConditionFilterEdit::Create,
|
||||
"AdvSceneSwitcher.condition.filter"});
|
||||
|
||||
static std::map<MacroConditionFilter::Condition, std::string>
|
||||
const static std::map<MacroConditionFilter::Condition, std::string>
|
||||
filterConditionTypes = {
|
||||
{MacroConditionFilter::Condition::ENABLED,
|
||||
"AdvSceneSwitcher.condition.filter.type.active"},
|
||||
|
||||
@@ -8,7 +8,7 @@ const std::string MacroConditionHotkey::id = "hotkey";
|
||||
bool MacroConditionHotkey::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionHotkey::id,
|
||||
{MacroConditionHotkey::Create, MacroConditionHotkeyEdit::Create,
|
||||
"AdvSceneSwitcher.condition.hotkey", false});
|
||||
"AdvSceneSwitcher.condition.hotkey"});
|
||||
|
||||
static uint32_t count = 1;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "macro-condition-macro.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
@@ -11,16 +12,21 @@ bool MacroConditionMacro::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionMacro::Create, MacroConditionMacroEdit::Create,
|
||||
"AdvSceneSwitcher.condition.macro"});
|
||||
|
||||
static std::map<MacroConditionMacro::Type, std::string> macroConditionTypes = {
|
||||
{MacroConditionMacro::Type::COUNT,
|
||||
"AdvSceneSwitcher.condition.macro.type.count"},
|
||||
{MacroConditionMacro::Type::STATE,
|
||||
"AdvSceneSwitcher.condition.macro.type.state"},
|
||||
{MacroConditionMacro::Type::MULTI_STATE,
|
||||
"AdvSceneSwitcher.condition.macro.type.multiState"},
|
||||
const static std::map<MacroConditionMacro::Type, std::string>
|
||||
macroConditionTypes = {
|
||||
{MacroConditionMacro::Type::COUNT,
|
||||
"AdvSceneSwitcher.condition.macro.type.count"},
|
||||
{MacroConditionMacro::Type::STATE,
|
||||
"AdvSceneSwitcher.condition.macro.type.state"},
|
||||
{MacroConditionMacro::Type::MULTI_STATE,
|
||||
"AdvSceneSwitcher.condition.macro.type.multiState"},
|
||||
{MacroConditionMacro::Type::ACTION_DISABLED,
|
||||
"AdvSceneSwitcher.condition.macro.type.actionDisabled"},
|
||||
{MacroConditionMacro::Type::ACTION_ENABLED,
|
||||
"AdvSceneSwitcher.condition.macro.type.actionEnabled"},
|
||||
};
|
||||
|
||||
static std::map<MacroConditionMacro::CounterCondition, std::string>
|
||||
const static std::map<MacroConditionMacro::CounterCondition, std::string>
|
||||
counterConditionTypes = {
|
||||
{MacroConditionMacro::CounterCondition::BELOW,
|
||||
"AdvSceneSwitcher.condition.macro.count.type.below"},
|
||||
@@ -30,7 +36,7 @@ static std::map<MacroConditionMacro::CounterCondition, std::string>
|
||||
"AdvSceneSwitcher.condition.macro.count.type.equal"},
|
||||
};
|
||||
|
||||
static std::map<MacroConditionMacro::MultiStateCondition, std::string>
|
||||
const static std::map<MacroConditionMacro::MultiStateCondition, std::string>
|
||||
multiStateConditionTypes = {
|
||||
{MacroConditionMacro::MultiStateCondition::BELOW,
|
||||
"AdvSceneSwitcher.condition.macro.state.type.below"},
|
||||
@@ -83,6 +89,24 @@ bool MacroConditionMacro::CheckMultiStateCondition()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionMacro::CheckActionStateCondition()
|
||||
{
|
||||
auto macro = _macro.GetMacro();
|
||||
if (!macro) {
|
||||
return false;
|
||||
}
|
||||
if (!IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1, false)) {
|
||||
return false;
|
||||
}
|
||||
if (_type == Type::ACTION_DISABLED) {
|
||||
return !macro->Actions().at(_actionIndex - 1)->Enabled();
|
||||
} else if (_type == Type::ACTION_ENABLED) {
|
||||
return macro->Actions().at(_actionIndex - 1)->Enabled();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionMacro::CheckCountCondition()
|
||||
{
|
||||
auto macro = _macro.GetMacro();
|
||||
@@ -113,6 +137,9 @@ bool MacroConditionMacro::CheckCondition()
|
||||
return CheckMultiStateCondition();
|
||||
case Type::COUNT:
|
||||
return CheckCountCondition();
|
||||
case Type::ACTION_DISABLED:
|
||||
case Type::ACTION_ENABLED:
|
||||
return CheckActionStateCondition();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -131,6 +158,7 @@ bool MacroConditionMacro::Save(obs_data_t *obj) const
|
||||
_multiSateCount.Save(obj, "multiStateCount");
|
||||
obs_data_set_int(obj, "multiStateCondition",
|
||||
static_cast<int>(_multiSateCondition));
|
||||
_actionIndex.Save(obj, "actionIndex");
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
return true;
|
||||
}
|
||||
@@ -143,6 +171,7 @@ bool MacroConditionMacro::Load(obs_data_t *obj)
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||
_counterCondition = static_cast<CounterCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_actionIndex.Load(obj, "actionIndex");
|
||||
// TODO: Remove this fallback in future version
|
||||
if (!obs_data_has_user_value(obj, "multiStateCondition")) {
|
||||
_multiSateCondition = MultiStateCondition::ABOVE;
|
||||
@@ -210,7 +239,9 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
|
||||
_settingsLine2(new QHBoxLayout()),
|
||||
_macroList(new MacroList(this, false, false)),
|
||||
_multiStateConditions(new QComboBox()),
|
||||
_multiStateCount(new VariableSpinBox())
|
||||
_multiStateCount(new VariableSpinBox()),
|
||||
_actionIndex(new MacroSegmentSelection(
|
||||
this, MacroSegmentSelection::Type::ACTION))
|
||||
{
|
||||
_count->setMaximum(10000000);
|
||||
populateTypeSelection(_types);
|
||||
@@ -245,6 +276,9 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this,
|
||||
SLOT(MultiStateCountChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_actionIndex,
|
||||
SIGNAL(SelectionChanged(const IntVariable &)), this,
|
||||
SLOT(ActionIndexChanged(const IntVariable &)));
|
||||
|
||||
auto typesLayout = new QHBoxLayout();
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
@@ -254,6 +288,14 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
|
||||
"AdvSceneSwitcher.condition.macro.type.selection"),
|
||||
typesLayout, widgetPlaceholders);
|
||||
|
||||
_settingsLine1->addWidget(_macros);
|
||||
_settingsLine1->addWidget(_counterConditions);
|
||||
_settingsLine1->addWidget(_count);
|
||||
_settingsLine2->addWidget(_currentCount);
|
||||
_settingsLine2->addWidget(_resetCount);
|
||||
_settingsLine1->addWidget(_multiStateConditions);
|
||||
_settingsLine1->addWidget(_multiStateCount);
|
||||
|
||||
auto mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addLayout(typesLayout);
|
||||
mainLayout->addLayout(_settingsLine1);
|
||||
@@ -286,10 +328,34 @@ void MacroConditionMacroEdit::ClearLayouts()
|
||||
_settingsLine2->removeWidget(_resetCount);
|
||||
_settingsLine1->removeWidget(_multiStateConditions);
|
||||
_settingsLine1->removeWidget(_multiStateCount);
|
||||
_settingsLine1->removeWidget(_actionIndex);
|
||||
ClearLayout(_settingsLine1);
|
||||
ClearLayout(_settingsLine2);
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::SetupWidgets()
|
||||
{
|
||||
switch (_entryData->_type) {
|
||||
case MacroConditionMacro::Type::COUNT:
|
||||
SetupCountWidgets();
|
||||
break;
|
||||
case MacroConditionMacro::Type::STATE:
|
||||
SetupStateWidgets();
|
||||
break;
|
||||
case MacroConditionMacro::Type::MULTI_STATE:
|
||||
SetupMultiStateWidgets();
|
||||
break;
|
||||
case MacroConditionMacro::Type::ACTION_DISABLED:
|
||||
SetupActionStateWidgets(false);
|
||||
break;
|
||||
case MacroConditionMacro::Type::ACTION_ENABLED:
|
||||
SetupActionStateWidgets(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::SetupStateWidgets()
|
||||
{
|
||||
SetWidgetVisibility();
|
||||
@@ -340,43 +406,51 @@ void MacroConditionMacroEdit::SetupCountWidgets()
|
||||
_settingsLine2, widgetPlaceholders);
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::SetupActionStateWidgets(bool enable)
|
||||
{
|
||||
SetWidgetVisibility();
|
||||
ClearLayouts();
|
||||
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
enable ? "AdvSceneSwitcher.condition.macro.actionState.enabled.entry"
|
||||
: "AdvSceneSwitcher.condition.macro.actionState.disabled.entry"),
|
||||
_settingsLine1,
|
||||
{{"{{macros}}", _macros}, {"{{actionIndex}}", _actionIndex}});
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::SetWidgetVisibility()
|
||||
{
|
||||
switch (_entryData->_type) {
|
||||
case MacroConditionMacro::Type::COUNT:
|
||||
_macros->show();
|
||||
_counterConditions->show();
|
||||
_count->show();
|
||||
_currentCount->show();
|
||||
_resetCount->show();
|
||||
_macroList->hide();
|
||||
_multiStateConditions->hide();
|
||||
_multiStateCount->hide();
|
||||
break;
|
||||
case MacroConditionMacro::Type::STATE:
|
||||
_macros->show();
|
||||
_counterConditions->hide();
|
||||
_count->hide();
|
||||
_currentCount->hide();
|
||||
_resetCount->hide();
|
||||
_macroList->hide();
|
||||
_multiStateConditions->hide();
|
||||
_multiStateCount->hide();
|
||||
break;
|
||||
case MacroConditionMacro::Type::MULTI_STATE:
|
||||
_macros->hide();
|
||||
_counterConditions->hide();
|
||||
_count->hide();
|
||||
_currentCount->hide();
|
||||
_macros->setVisible(
|
||||
_entryData->_type == MacroConditionMacro::Type::COUNT ||
|
||||
_entryData->_type == MacroConditionMacro::Type::STATE ||
|
||||
_entryData->_type ==
|
||||
MacroConditionMacro::Type::ACTION_DISABLED ||
|
||||
_entryData->_type == MacroConditionMacro::Type::ACTION_ENABLED);
|
||||
_counterConditions->setVisible(_entryData->_type ==
|
||||
MacroConditionMacro::Type::COUNT);
|
||||
_count->setVisible(_entryData->_type ==
|
||||
MacroConditionMacro::Type::COUNT);
|
||||
_currentCount->setVisible(_entryData->_type ==
|
||||
MacroConditionMacro::Type::COUNT);
|
||||
_resetCount->setVisible(_entryData->_type ==
|
||||
MacroConditionMacro::Type::COUNT);
|
||||
_macroList->setVisible(_entryData->_type ==
|
||||
MacroConditionMacro::Type::MULTI_STATE);
|
||||
_multiStateConditions->setVisible(
|
||||
_entryData->_type == MacroConditionMacro::Type::MULTI_STATE);
|
||||
_multiStateCount->setVisible(_entryData->_type ==
|
||||
MacroConditionMacro::Type::MULTI_STATE);
|
||||
_actionIndex->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroConditionMacro::Type::ACTION_DISABLED ||
|
||||
_entryData->_type == MacroConditionMacro::Type::ACTION_ENABLED);
|
||||
if (_entryData->_type == MacroConditionMacro::Type::MULTI_STATE ||
|
||||
_entryData->_type == MacroConditionMacro::Type::ACTION_DISABLED ||
|
||||
_entryData->_type == MacroConditionMacro::Type::ACTION_ENABLED) {
|
||||
_pausedWarning->hide();
|
||||
_resetCount->hide();
|
||||
_macroList->show();
|
||||
_multiStateConditions->show();
|
||||
_multiStateCount->show();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
@@ -387,20 +461,7 @@ void MacroConditionMacroEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_entryData->_type) {
|
||||
case MacroConditionMacro::Type::COUNT:
|
||||
SetupCountWidgets();
|
||||
break;
|
||||
case MacroConditionMacro::Type::STATE:
|
||||
SetupStateWidgets();
|
||||
break;
|
||||
case MacroConditionMacro::Type::MULTI_STATE:
|
||||
SetupMultiStateWidgets();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SetupWidgets();
|
||||
_macros->SetCurrentMacro(_entryData->_macro);
|
||||
_types->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_counterConditions->setCurrentIndex(
|
||||
@@ -410,6 +471,9 @@ void MacroConditionMacroEdit::UpdateEntryData()
|
||||
_multiStateConditions->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_multiSateCondition));
|
||||
_multiStateCount->SetValue(_entryData->_multiSateCount);
|
||||
_actionIndex->SetValue(_entryData->_actionIndex);
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::MacroChanged(const QString &text)
|
||||
@@ -420,6 +484,7 @@ void MacroConditionMacroEdit::MacroChanged(const QString &text)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_macro = text;
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -460,6 +525,7 @@ void MacroConditionMacroEdit::MacroRemove(const QString &)
|
||||
}
|
||||
}
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::TypeChanged(int type)
|
||||
@@ -470,20 +536,7 @@ void MacroConditionMacroEdit::TypeChanged(int type)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_type = static_cast<MacroConditionMacro::Type>(type);
|
||||
|
||||
switch (_entryData->_type) {
|
||||
case MacroConditionMacro::Type::COUNT:
|
||||
SetupCountWidgets();
|
||||
break;
|
||||
case MacroConditionMacro::Type::STATE:
|
||||
SetupStateWidgets();
|
||||
break;
|
||||
case MacroConditionMacro::Type::MULTI_STATE:
|
||||
SetupMultiStateWidgets();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SetupWidgets();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::ResetClicked()
|
||||
@@ -521,6 +574,7 @@ void MacroConditionMacroEdit::UpdatePaused()
|
||||
_entryData->_type != MacroConditionMacro::Type::MULTI_STATE &&
|
||||
macro && macro->Paused());
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::MultiStateConditionChanged(int cond)
|
||||
@@ -555,6 +609,7 @@ void MacroConditionMacroEdit::Add(const std::string &name)
|
||||
MacroRef macro(name);
|
||||
_entryData->_macros.push_back(macro);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::Remove(int idx)
|
||||
@@ -566,6 +621,7 @@ void MacroConditionMacroEdit::Remove(int idx)
|
||||
auto lock = LockContext();
|
||||
_entryData->_macros.erase(std::next(_entryData->_macros.begin(), idx));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::Replace(int idx, const std::string &name)
|
||||
@@ -578,6 +634,17 @@ void MacroConditionMacroEdit::Replace(int idx, const std::string &name)
|
||||
auto lock = LockContext();
|
||||
_entryData->_macros[idx] = macro;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::ActionIndexChanged(const IntVariable &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_actionIndex = value;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
#include "macro-list.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
#include "macro-segment-selection.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QSpinBox>
|
||||
@@ -37,6 +37,8 @@ public:
|
||||
COUNT,
|
||||
STATE,
|
||||
MULTI_STATE,
|
||||
ACTION_DISABLED,
|
||||
ACTION_ENABLED,
|
||||
};
|
||||
Type _type = Type::STATE;
|
||||
|
||||
@@ -55,11 +57,13 @@ public:
|
||||
};
|
||||
MultiStateCondition _multiSateCondition = MultiStateCondition::ABOVE;
|
||||
NumberVariable<int> _multiSateCount = 0;
|
||||
IntVariable _actionIndex = 1;
|
||||
|
||||
private:
|
||||
bool CheckCountCondition();
|
||||
bool CheckStateCondition();
|
||||
bool CheckMultiStateCondition();
|
||||
bool CheckActionStateCondition();
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
@@ -95,6 +99,7 @@ private slots:
|
||||
void Add(const std::string &);
|
||||
void Remove(int);
|
||||
void Replace(int, const std::string &);
|
||||
void ActionIndexChanged(const IntVariable &value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -111,15 +116,18 @@ protected:
|
||||
MacroList *_macroList;
|
||||
QComboBox *_multiStateConditions;
|
||||
VariableSpinBox *_multiStateCount;
|
||||
MacroSegmentSelection *_actionIndex;
|
||||
QTimer _countTimer;
|
||||
QTimer _pausedTimer;
|
||||
std::shared_ptr<MacroConditionMacro> _entryData;
|
||||
|
||||
private:
|
||||
void ClearLayouts();
|
||||
void SetupWidgets();
|
||||
void SetupStateWidgets();
|
||||
void SetupMultiStateWidgets();
|
||||
void SetupCountWidgets();
|
||||
void SetupActionStateWidgets(bool enable);
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ bool MacroConditionStats::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionStats::Create, MacroConditionStatsEdit::Create,
|
||||
"AdvSceneSwitcher.condition.stats"});
|
||||
|
||||
static std::map<MacroConditionStats::Type, std::string> statsTypes = {
|
||||
const static std::map<MacroConditionStats::Type, std::string> statsTypes = {
|
||||
{MacroConditionStats::Type::FPS,
|
||||
"AdvSceneSwitcher.condition.stats.type.fps"},
|
||||
{MacroConditionStats::Type::CPU_USAGE,
|
||||
@@ -39,7 +39,7 @@ static std::map<MacroConditionStats::Type, std::string> statsTypes = {
|
||||
"AdvSceneSwitcher.condition.stats.type.megabytesSent.recording"},
|
||||
};
|
||||
|
||||
static std::map<MacroConditionStats::Condition, std::string>
|
||||
const static std::map<MacroConditionStats::Condition, std::string>
|
||||
statsConditionTypes = {
|
||||
{MacroConditionStats::Condition::ABOVE,
|
||||
"AdvSceneSwitcher.condition.stats.condition.above"},
|
||||
|
||||
@@ -14,7 +14,7 @@ bool MacroConditionPluginState::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionPluginStateEdit::Create,
|
||||
"AdvSceneSwitcher.condition.pluginState"});
|
||||
|
||||
static std::map<MacroConditionPluginState::Condition, std::string>
|
||||
const static std::map<MacroConditionPluginState::Condition, std::string>
|
||||
pluginStateConditionTypes = {
|
||||
{MacroConditionPluginState::Condition::PLUGIN_START,
|
||||
"AdvSceneSwitcher.condition.pluginState.state.start"},
|
||||
@@ -112,24 +112,28 @@ static inline void populateConditionSelection(QComboBox *list)
|
||||
|
||||
MacroConditionPluginStateEdit::MacroConditionPluginStateEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionPluginState> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_condition(new QComboBox()),
|
||||
_shutdownLimitation(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.pluginState.state.shutdown.limitation")))
|
||||
{
|
||||
_condition = new QComboBox();
|
||||
|
||||
_shutdownLimitation->setWordWrap(true);
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||
auto entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{condition}}", _condition},
|
||||
};
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.pluginState.entry"),
|
||||
switchLayout, widgetPlaceholders);
|
||||
entryLayout, widgetPlaceholders);
|
||||
entryLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(switchLayout);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(_shutdownLimitation);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -155,6 +159,7 @@ void MacroConditionPluginStateEdit::ConditionChanged(int idx)
|
||||
MacroConditionPluginState::Condition::OBS_SHUTDOWN) {
|
||||
switcher->shutdownConditionCount++;
|
||||
}
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionPluginStateEdit::UpdateEntryData()
|
||||
@@ -165,6 +170,16 @@ void MacroConditionPluginStateEdit::UpdateEntryData()
|
||||
|
||||
_condition->setCurrentIndex(
|
||||
_condition->findData(static_cast<int>(_entryData->_condition)));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionPluginStateEdit::SetWidgetVisibility()
|
||||
{
|
||||
_shutdownLimitation->setVisible(
|
||||
_entryData->_condition ==
|
||||
MacroConditionPluginState::Condition::OBS_SHUTDOWN);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -58,7 +58,10 @@ private slots:
|
||||
void ConditionChanged(int idx);
|
||||
|
||||
protected:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_condition;
|
||||
QLabel *_shutdownLimitation;
|
||||
std::shared_ptr<MacroConditionPluginState> _entryData;
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,7 +10,7 @@ bool MacroConditionRecord::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionRecord::Create, MacroConditionRecordEdit::Create,
|
||||
"AdvSceneSwitcher.condition.record"});
|
||||
|
||||
static std::map<RecordState, std::string> recordStates = {
|
||||
const static std::map<RecordState, std::string> recordStates = {
|
||||
{RecordState::STOP, "AdvSceneSwitcher.condition.record.state.stop"},
|
||||
{RecordState::PAUSE, "AdvSceneSwitcher.condition.record.state.pause"},
|
||||
{RecordState::START, "AdvSceneSwitcher.condition.record.state.start"},
|
||||
|
||||
@@ -11,7 +11,7 @@ bool MacroConditionReplayBuffer::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionReplayBufferEdit::Create,
|
||||
"AdvSceneSwitcher.condition.replay"});
|
||||
|
||||
static std::map<ReplayBufferState, std::string> ReplayBufferStates = {
|
||||
const static std::map<ReplayBufferState, std::string> ReplayBufferStates = {
|
||||
{ReplayBufferState::STOP,
|
||||
"AdvSceneSwitcher.condition.replay.state.stopped"},
|
||||
{ReplayBufferState::START,
|
||||
|
||||
@@ -10,7 +10,7 @@ bool MacroConditionSceneOrder::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionSceneOrder::Create, MacroConditionSceneOrderEdit::Create,
|
||||
"AdvSceneSwitcher.condition.sceneOrder"});
|
||||
|
||||
static std::map<MacroConditionSceneOrder::Condition, std::string>
|
||||
const static std::map<MacroConditionSceneOrder::Condition, std::string>
|
||||
sceneOrderConditionTypes = {
|
||||
{MacroConditionSceneOrder::Condition::ABOVE,
|
||||
"AdvSceneSwitcher.condition.sceneOrder.type.above"},
|
||||
@@ -29,16 +29,16 @@ struct PosInfo2 {
|
||||
static bool getSceneItemPositionHelper(obs_scene_t *, obs_sceneitem_t *item,
|
||||
void *ptr)
|
||||
{
|
||||
PosInfo2 *posInfo = reinterpret_cast<PosInfo2 *>(ptr);
|
||||
auto posInfo = reinterpret_cast<PosInfo2 *>(ptr);
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, getSceneItemPositionHelper, ptr);
|
||||
}
|
||||
if (posInfo->item == item) {
|
||||
posInfo->pos = posInfo->curPos;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, getSceneItemPositionHelper, ptr);
|
||||
}
|
||||
posInfo->curPos += 1;
|
||||
return true;
|
||||
}
|
||||
@@ -50,11 +50,11 @@ static PosInfo2 getSceneItemPos(obs_scene_item *item, obs_scene *scene)
|
||||
return pos;
|
||||
}
|
||||
|
||||
std::vector<int> getSceneItemPositions(std::vector<obs_scene_item *> &items,
|
||||
obs_scene *scene)
|
||||
static std::vector<int> getSceneItemPositions(std::vector<OBSSceneItem> &items,
|
||||
obs_scene *scene)
|
||||
{
|
||||
std::vector<int> positions;
|
||||
for (auto item : items) {
|
||||
for (const auto &item : items) {
|
||||
auto pos = getSceneItemPos(item, scene);
|
||||
if (pos.pos != -1) {
|
||||
positions.emplace_back(pos.pos);
|
||||
@@ -106,13 +106,6 @@ bool MacroConditionSceneOrder::CheckCondition()
|
||||
auto positions1 = getSceneItemPositions(items1, scene);
|
||||
auto positions2 = getSceneItemPositions(items2, scene);
|
||||
|
||||
for (auto i : items1) {
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
for (auto i : items2) {
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
|
||||
switch (_condition) {
|
||||
@@ -124,8 +117,9 @@ bool MacroConditionSceneOrder::CheckCondition()
|
||||
break;
|
||||
case Condition::POSITION:
|
||||
for (int p : positions1) {
|
||||
if (p == _position)
|
||||
if (p == _position) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -195,7 +189,7 @@ std::string MacroConditionSceneOrder::GetShortDesc() const
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : sceneOrderConditionTypes) {
|
||||
for (const auto &entry : sceneOrderConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
@@ -282,6 +276,8 @@ void MacroConditionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSceneOrderEdit::Source2Changed(const SceneItemSelection &item)
|
||||
@@ -294,6 +290,8 @@ void MacroConditionSceneOrderEdit::Source2Changed(const SceneItemSelection &item
|
||||
_entryData->_source2 = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSceneOrderEdit::ConditionChanged(int index)
|
||||
|
||||
@@ -18,12 +18,11 @@ bool MacroConditionSceneTransform::CheckCondition()
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
|
||||
std::string json;
|
||||
for (auto &item : items) {
|
||||
for (const auto &item : items) {
|
||||
json = GetSceneItemTransform(item);
|
||||
if (MatchJson(json, _settings, _regex)) {
|
||||
ret = true;
|
||||
}
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
SetVariableValue(json);
|
||||
return ret;
|
||||
@@ -165,6 +164,8 @@ void MacroConditionSceneTransformEdit::SourceChanged(
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
||||
@@ -184,10 +185,6 @@ void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
||||
settings = EscapeForRegex(settings);
|
||||
}
|
||||
_settings->setPlainText(settings);
|
||||
|
||||
for (auto item : items) {
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::SettingsChanged()
|
||||
|
||||
@@ -14,7 +14,7 @@ bool MacroConditionSceneVisibility::_registered =
|
||||
MacroConditionSceneVisibilityEdit::Create,
|
||||
"AdvSceneSwitcher.condition.sceneVisibility"});
|
||||
|
||||
static std::map<MacroConditionSceneVisibility::Condition, std::string>
|
||||
const static std::map<MacroConditionSceneVisibility::Condition, std::string>
|
||||
conditionTypes = {
|
||||
{MacroConditionSceneVisibility::Condition::SHOWN,
|
||||
"AdvSceneSwitcher.condition.sceneVisibility.type.shown"},
|
||||
@@ -24,32 +24,30 @@ static std::map<MacroConditionSceneVisibility::Condition, std::string>
|
||||
"AdvSceneSwitcher.condition.sceneVisibility.type.changed"},
|
||||
};
|
||||
|
||||
static bool areAllSceneItemsShown(const std::vector<obs_scene_item *> &items)
|
||||
static bool areAllSceneItemsShown(const std::vector<OBSSceneItem> &items)
|
||||
{
|
||||
bool ret = true;
|
||||
for (auto item : items) {
|
||||
for (const auto &item : items) {
|
||||
if (!obs_sceneitem_visible(item)) {
|
||||
ret = false;
|
||||
}
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool areAllSceneItemsHidden(const std::vector<obs_scene_item *> &items)
|
||||
static bool areAllSceneItemsHidden(const std::vector<OBSSceneItem> &items)
|
||||
{
|
||||
bool ret = true;
|
||||
for (auto item : items) {
|
||||
for (const auto &item : items) {
|
||||
if (obs_sceneitem_visible(item)) {
|
||||
ret = false;
|
||||
}
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
didVisibilityOfAnySceneItemsChange(const std::vector<obs_scene_item *> &items,
|
||||
didVisibilityOfAnySceneItemsChange(const std::vector<OBSSceneItem> &items,
|
||||
std::vector<bool> &previousVisibility)
|
||||
{
|
||||
std::vector<bool> currentVisibility;
|
||||
@@ -64,10 +62,6 @@ didVisibilityOfAnySceneItemsChange(const std::vector<obs_scene_item *> &items,
|
||||
ret = previousVisibility != currentVisibility;
|
||||
}
|
||||
previousVisibility = currentVisibility;
|
||||
|
||||
for (const auto &item : items) {
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -86,10 +80,10 @@ bool MacroConditionSceneVisibility::CheckCondition()
|
||||
case Condition::CHANGED:
|
||||
return didVisibilityOfAnySceneItemsChange(items,
|
||||
_previousVisibilty);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -129,7 +123,7 @@ std::string MacroConditionSceneVisibility::GetShortDesc() const
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : conditionTypes) {
|
||||
for (const auto &entry : conditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
@@ -183,6 +177,8 @@ void MacroConditionSceneVisibilityEdit::SourceChanged(
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
|
||||
@@ -11,7 +11,7 @@ bool MacroConditionScene::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionScene::Create, MacroConditionSceneEdit::Create,
|
||||
"AdvSceneSwitcher.condition.scene"});
|
||||
|
||||
static std::map<MacroConditionScene::Type, std::string> sceneTypes = {
|
||||
const static std::map<MacroConditionScene::Type, std::string> sceneTypes = {
|
||||
{MacroConditionScene::Type::CURRENT,
|
||||
"AdvSceneSwitcher.condition.scene.type.current"},
|
||||
{MacroConditionScene::Type::PREVIOUS,
|
||||
|
||||
312
src/macro-core/macro-condition-slideshow.cpp
Normal file
312
src/macro-core/macro-condition-slideshow.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
#include "macro-condition-slideshow.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroConditionSlideshow::id = "slideshow";
|
||||
|
||||
bool MacroConditionSlideshow::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionSlideshow::id,
|
||||
{MacroConditionSlideshow::Create, MacroConditionSlideshowEdit::Create,
|
||||
"AdvSceneSwitcher.condition.slideshow"});
|
||||
|
||||
static const std::map<MacroConditionSlideshow::Condition, std::string>
|
||||
conditions = {
|
||||
{MacroConditionSlideshow::Condition::SLIDE_CHANGED,
|
||||
"AdvSceneSwitcher.condition.slideshow.condition.slideChanged"},
|
||||
{MacroConditionSlideshow::Condition::SLIDE_INDEX,
|
||||
"AdvSceneSwitcher.condition.slideshow.condition.slideIndex"},
|
||||
{MacroConditionSlideshow::Condition::SLIDE_PATH,
|
||||
"AdvSceneSwitcher.condition.slideshow.condition.slidePath"},
|
||||
};
|
||||
|
||||
MacroConditionSlideshow::MacroConditionSlideshow(Macro *m)
|
||||
: MacroCondition(m, true)
|
||||
{
|
||||
}
|
||||
|
||||
MacroConditionSlideshow::~MacroConditionSlideshow()
|
||||
{
|
||||
RemoveSignalHandler();
|
||||
}
|
||||
|
||||
bool MacroConditionSlideshow::CheckCondition()
|
||||
{
|
||||
auto source = _source.GetSource();
|
||||
if (_currentSignalSource != source) {
|
||||
Reset();
|
||||
RemoveSignalHandler();
|
||||
AddSignalHandler(source);
|
||||
}
|
||||
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (_condition) {
|
||||
case MacroConditionSlideshow::Condition::SLIDE_CHANGED:
|
||||
if (!_slideChanged) {
|
||||
SetVariableValue("false");
|
||||
return false;
|
||||
}
|
||||
_slideChanged = false;
|
||||
SetVariableValue("true");
|
||||
return true;
|
||||
case MacroConditionSlideshow::Condition::SLIDE_INDEX:
|
||||
if (_currentIndex == -1) {
|
||||
SetVariableValue("-1");
|
||||
return false;
|
||||
}
|
||||
SetVariableValue(std::to_string(_currentIndex + 1));
|
||||
return _currentIndex == _index - 1;
|
||||
case MacroConditionSlideshow::Condition::SLIDE_PATH:
|
||||
if (_currentPath[0] == '\0') {
|
||||
SetVariableValue("");
|
||||
return false;
|
||||
}
|
||||
SetVariableValue(_currentPath);
|
||||
return std::string(_path) == std::string(_currentPath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionSlideshow::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
_source.Save(obj);
|
||||
_index.Save(obj, "index");
|
||||
_path.Save(obj, "path");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionSlideshow::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_condition = static_cast<Condition>(obs_data_get_int(obj, "condition"));
|
||||
_source.Load(obj);
|
||||
_index.Load(obj, "index");
|
||||
_path.Load(obj, "path");
|
||||
|
||||
auto s = _source.GetSource();
|
||||
AddSignalHandler(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionSlideshow::GetShortDesc() const
|
||||
{
|
||||
return _source.ToString();
|
||||
}
|
||||
|
||||
void MacroConditionSlideshow::SetSource(const SourceSelection &source)
|
||||
{
|
||||
Reset();
|
||||
_source = source;
|
||||
RemoveSignalHandler();
|
||||
auto s = _source.GetSource();
|
||||
AddSignalHandler(s);
|
||||
}
|
||||
|
||||
const SourceSelection &MacroConditionSlideshow::GetSource() const
|
||||
{
|
||||
return _source;
|
||||
}
|
||||
|
||||
void MacroConditionSlideshow::SlideChanged(void *c, calldata_t *data)
|
||||
{
|
||||
auto condition = static_cast<MacroConditionSlideshow *>(c);
|
||||
const auto macro = condition->GetMacro();
|
||||
if (macro && macro->Paused()) {
|
||||
return;
|
||||
}
|
||||
|
||||
long long newIndex = 0;
|
||||
if (!calldata_get_int(data, "index", &newIndex)) {
|
||||
condition->_currentIndex = -1;
|
||||
}
|
||||
|
||||
// Function will be called every time the slide_time timeout is reached
|
||||
// regardless of if there was actually a change in slide index.
|
||||
//
|
||||
// Thus we need to manually check if the slide index really changed.
|
||||
if (newIndex != condition->_currentIndex) {
|
||||
condition->_slideChanged = true;
|
||||
}
|
||||
condition->_currentIndex = newIndex;
|
||||
|
||||
if (!calldata_get_string(data, "path", &condition->_currentPath)) {
|
||||
condition->_currentPath = "";
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionSlideshow::RemoveSignalHandler()
|
||||
{
|
||||
obs_source_t *source = obs_weak_source_get_source(_currentSignalSource);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_disconnect(sh, "slide_changed", SlideChanged, this);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
void MacroConditionSlideshow::AddSignalHandler(const OBSWeakSource &s)
|
||||
{
|
||||
_currentSignalSource = s;
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(s);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_connect(sh, "slide_changed", SlideChanged, this);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
void MacroConditionSlideshow::Reset()
|
||||
{
|
||||
_slideChanged = false;
|
||||
_currentIndex = -1;
|
||||
_currentPath = "";
|
||||
}
|
||||
|
||||
static void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : conditions) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static QStringList getSlideshowNames()
|
||||
{
|
||||
auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */
|
||||
{
|
||||
QStringList *list = reinterpret_cast<QStringList *>(param);
|
||||
std::string sourceId = obs_source_get_id(source);
|
||||
if (sourceId.compare("slideshow") == 0) {
|
||||
*list << obs_source_get_name(source);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
QStringList list;
|
||||
obs_enum_sources(sourceEnum, &list);
|
||||
return list;
|
||||
}
|
||||
|
||||
MacroConditionSlideshowEdit::MacroConditionSlideshowEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionSlideshow> entryData)
|
||||
: QWidget(parent),
|
||||
_conditions(new QComboBox(this)),
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||
_index(new VariableSpinBox(this)),
|
||||
_path(new VariableLineEdit(this))
|
||||
{
|
||||
setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip"));
|
||||
_index->setMinimum(1);
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
auto sources = getSlideshowNames();
|
||||
sources.sort();
|
||||
_sources->SetSourceNameList(sources);
|
||||
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
||||
SLOT(SourceChanged(const SourceSelection &)));
|
||||
QWidget::connect(
|
||||
_index,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(IndexChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_path, SIGNAL(editingFinished()), this,
|
||||
SLOT(PathChanged()));
|
||||
|
||||
auto layout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.slideshow.entry"),
|
||||
layout,
|
||||
{{"{{conditions}}", _conditions},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{index}}", _index},
|
||||
{"{{path}}", _path}});
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_condition =
|
||||
static_cast<MacroConditionSlideshow::Condition>(index);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::SourceChanged(const SourceSelection &source)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->SetSource(source);
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::IndexChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_index = value;
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::PathChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_path = _path->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_sources->SetSource(_entryData->GetSource());
|
||||
_index->SetValue(_entryData->_index);
|
||||
_path->setText(_entryData->_path);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionSlideshowEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_index->setVisible(_entryData->_condition ==
|
||||
MacroConditionSlideshow::Condition::SLIDE_INDEX);
|
||||
_path->setVisible(_entryData->_condition ==
|
||||
MacroConditionSlideshow::Condition::SLIDE_PATH);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
88
src/macro-core/macro-condition-slideshow.hpp
Normal file
88
src/macro-core/macro-condition-slideshow.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "source-selection.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroConditionSlideshow : public MacroCondition {
|
||||
public:
|
||||
MacroConditionSlideshow(Macro *m);
|
||||
~MacroConditionSlideshow();
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionSlideshow>(m);
|
||||
}
|
||||
|
||||
void SetSource(const SourceSelection &source);
|
||||
const SourceSelection &GetSource() const;
|
||||
|
||||
enum class Condition {
|
||||
SLIDE_CHANGED,
|
||||
SLIDE_INDEX,
|
||||
SLIDE_PATH,
|
||||
};
|
||||
Condition _condition = Condition::SLIDE_CHANGED;
|
||||
IntVariable _index;
|
||||
StringVariable _path;
|
||||
|
||||
private:
|
||||
static void SlideChanged(void *condition, calldata_t *);
|
||||
void RemoveSignalHandler();
|
||||
void AddSignalHandler(const OBSWeakSource &);
|
||||
void Reset();
|
||||
|
||||
SourceSelection _source;
|
||||
OBSWeakSource _currentSignalSource;
|
||||
bool _slideChanged = false;
|
||||
long long _currentIndex = -1;
|
||||
const char *_currentPath = "";
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionSlideshowEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionSlideshowEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionSlideshow> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionSlideshowEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionSlideshow>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int index);
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void IndexChanged(const NumberVariable<int> &);
|
||||
void PathChanged();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_conditions;
|
||||
VariableSpinBox *_index;
|
||||
VariableLineEdit *_path;
|
||||
SourceSelectionWidget *_sources;
|
||||
|
||||
std::shared_ptr<MacroConditionSlideshow> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -10,7 +10,7 @@ bool MacroConditionSource::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionSource::Create, MacroConditionSourceEdit::Create,
|
||||
"AdvSceneSwitcher.condition.source"});
|
||||
|
||||
static std::map<SourceCondition, std::string> sourceConditionTypes = {
|
||||
const static std::map<SourceCondition, std::string> sourceConditionTypes = {
|
||||
{SourceCondition::ACTIVE,
|
||||
"AdvSceneSwitcher.condition.source.type.active"},
|
||||
{SourceCondition::SHOWING,
|
||||
@@ -107,7 +107,9 @@ MacroConditionSourceEdit::MacroConditionSourceEdit(
|
||||
populateConditionSelection(_conditions);
|
||||
auto sources = GetSourceNames();
|
||||
sources.sort();
|
||||
_sources->SetSourceNameList(sources);
|
||||
auto scenes = GetSceneNames();
|
||||
scenes.sort();
|
||||
_sources->SetSourceNameList(sources + scenes);
|
||||
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
||||
@@ -121,24 +123,27 @@ MacroConditionSourceEdit::MacroConditionSourceEdit(
|
||||
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
|
||||
SLOT(RegexChanged(RegexConfig)));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line3Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources}, {"{{conditions}}", _conditions},
|
||||
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
|
||||
{"{{regex}}", _regex},
|
||||
};
|
||||
auto line1Layout = new QHBoxLayout;
|
||||
line1Layout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
auto line2Layout = new QHBoxLayout;
|
||||
line2Layout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.entry.line2"),
|
||||
line2Layout, widgetPlaceholders, false);
|
||||
auto line3Layout = new QHBoxLayout;
|
||||
line3Layout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.entry.line3"),
|
||||
line3Layout, widgetPlaceholders);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
mainLayout->addLayout(line3Layout);
|
||||
@@ -169,8 +174,7 @@ void MacroConditionSourceEdit::ConditionChanged(int index)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_condition = static_cast<SourceCondition>(index);
|
||||
SetSettingsSelectionVisible(_entryData->_condition ==
|
||||
SourceCondition::SETTINGS);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::GetSettingsClicked()
|
||||
@@ -213,12 +217,23 @@ void MacroConditionSourceEdit::RegexChanged(RegexConfig conf)
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible)
|
||||
void MacroConditionSourceEdit::SetWidgetVisibility()
|
||||
{
|
||||
_settings->setVisible(visible);
|
||||
_getSettings->setVisible(visible);
|
||||
_regex->setVisible(visible);
|
||||
_settings->setVisible(_entryData->_condition ==
|
||||
SourceCondition::SETTINGS);
|
||||
_getSettings->setVisible(_entryData->_condition ==
|
||||
SourceCondition::SETTINGS);
|
||||
_regex->setVisible(_entryData->_condition == SourceCondition::SETTINGS);
|
||||
|
||||
setToolTip(
|
||||
(_entryData->_condition == SourceCondition::ACTIVE ||
|
||||
_entryData->_condition == SourceCondition::SHOWING)
|
||||
? obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.sceneVisibilityHint")
|
||||
: "");
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::UpdateEntryData()
|
||||
@@ -231,11 +246,7 @@ void MacroConditionSourceEdit::UpdateEntryData()
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_settings->setPlainText(_entryData->_settings);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
SetSettingsSelectionVisible(_entryData->_condition ==
|
||||
SourceCondition::SETTINGS);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -74,7 +74,7 @@ protected:
|
||||
std::shared_ptr<MacroConditionSource> _entryData;
|
||||
|
||||
private:
|
||||
void SetSettingsSelectionVisible(bool visible);
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,17 +11,18 @@ bool MacroConditionStream::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionStream::Create, MacroConditionStreamEdit::Create,
|
||||
"AdvSceneSwitcher.condition.stream"});
|
||||
|
||||
static std::map<MacroConditionStream::Condition, std::string> streamStates = {
|
||||
{MacroConditionStream::Condition::STOP,
|
||||
"AdvSceneSwitcher.condition.stream.state.stop"},
|
||||
{MacroConditionStream::Condition::START,
|
||||
"AdvSceneSwitcher.condition.stream.state.start"},
|
||||
{MacroConditionStream::Condition::STARTING,
|
||||
"AdvSceneSwitcher.condition.stream.state.starting"},
|
||||
{MacroConditionStream::Condition::STOPPING,
|
||||
"AdvSceneSwitcher.condition.stream.state.stopping"},
|
||||
{MacroConditionStream::Condition::KEYFRAME_INTERVAL,
|
||||
"AdvSceneSwitcher.condition.stream.state.keyFrameInterval"},
|
||||
const static std::map<MacroConditionStream::Condition, std::string>
|
||||
streamStates = {
|
||||
{MacroConditionStream::Condition::STOP,
|
||||
"AdvSceneSwitcher.condition.stream.state.stop"},
|
||||
{MacroConditionStream::Condition::START,
|
||||
"AdvSceneSwitcher.condition.stream.state.start"},
|
||||
{MacroConditionStream::Condition::STARTING,
|
||||
"AdvSceneSwitcher.condition.stream.state.starting"},
|
||||
{MacroConditionStream::Condition::STOPPING,
|
||||
"AdvSceneSwitcher.condition.stream.state.stopping"},
|
||||
{MacroConditionStream::Condition::KEYFRAME_INTERVAL,
|
||||
"AdvSceneSwitcher.condition.stream.state.keyFrameInterval"},
|
||||
};
|
||||
|
||||
int MacroConditionStream::GetKeyFrameInterval()
|
||||
|
||||
@@ -10,13 +10,14 @@ bool MacroConditionStudioMode::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionStudioMode::Create, MacroConditionStudioModeEdit::Create,
|
||||
"AdvSceneSwitcher.condition.studioMode"});
|
||||
|
||||
static std::map<StudioModeCondition, std::string> studioModeConditionTypes = {
|
||||
{StudioModeCondition::STUDIO_MODE_ACTIVE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.active"},
|
||||
{StudioModeCondition::STUDIO_MODE_NOT_ACTIVE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.notActive"},
|
||||
{StudioModeCondition::PREVIEW_SCENE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.previewScene"},
|
||||
const static std::map<StudioModeCondition, std::string>
|
||||
studioModeConditionTypes = {
|
||||
{StudioModeCondition::STUDIO_MODE_ACTIVE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.active"},
|
||||
{StudioModeCondition::STUDIO_MODE_NOT_ACTIVE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.notActive"},
|
||||
{StudioModeCondition::PREVIEW_SCENE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.previewScene"},
|
||||
};
|
||||
|
||||
bool MacroConditionStudioMode::CheckCondition()
|
||||
|
||||
@@ -10,7 +10,7 @@ bool MacroConditionTimer::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionTimer::Create, MacroConditionTimerEdit::Create,
|
||||
"AdvSceneSwitcher.condition.timer", false});
|
||||
|
||||
static std::map<TimerType, std::string> timerTypes = {
|
||||
const static std::map<TimerType, std::string> timerTypes = {
|
||||
{TimerType::FIXED, "AdvSceneSwitcher.condition.timer.type.fixed"},
|
||||
{TimerType::RANDOM, "AdvSceneSwitcher.condition.timer.type.random"},
|
||||
};
|
||||
@@ -18,8 +18,10 @@ static std::map<TimerType, std::string> timerTypes = {
|
||||
bool MacroConditionTimer::CheckCondition()
|
||||
{
|
||||
if (_paused) {
|
||||
SetVariableValue(std::to_string(_remaining));
|
||||
return _remaining == 0.;
|
||||
}
|
||||
SetVariableValue(std::to_string(_duration.TimeRemaining()));
|
||||
if (_duration.DurationReached()) {
|
||||
if (!_oneshot) {
|
||||
_duration.Reset();
|
||||
|
||||
@@ -19,7 +19,7 @@ enum class TimerType {
|
||||
|
||||
class MacroConditionTimer : public MacroCondition {
|
||||
public:
|
||||
MacroConditionTimer(Macro *m) : MacroCondition(m) {}
|
||||
MacroConditionTimer(Macro *m) : MacroCondition(m, true) {}
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
|
||||
@@ -11,7 +11,7 @@ bool MacroConditionTransition::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionTransition::Create, MacroConditionTransitionEdit::Create,
|
||||
"AdvSceneSwitcher.condition.transition"});
|
||||
|
||||
static std::map<TransitionCondition, std::string> filterConditionTypes = {
|
||||
const static std::map<TransitionCondition, std::string> filterConditionTypes = {
|
||||
{TransitionCondition::CURRENT,
|
||||
"AdvSceneSwitcher.condition.transition.type.current"},
|
||||
{TransitionCondition::DURATION,
|
||||
|
||||
@@ -10,7 +10,7 @@ bool MacroConditionVCam::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionVCam::Create, MacroConditionVCamEdit::Create,
|
||||
"AdvSceneSwitcher.condition.virtualCamera"});
|
||||
|
||||
static std::map<VCamState, std::string> VCamStates = {
|
||||
const static std::map<VCamState, std::string> VCamStates = {
|
||||
{VCamState::STOP,
|
||||
"AdvSceneSwitcher.condition.virtualCamera.state.stop"},
|
||||
{VCamState::START,
|
||||
|
||||
@@ -13,11 +13,12 @@ bool MacroConditionWebsocket::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionWebsocket::Create, MacroConditionWebsocketEdit::Create,
|
||||
"AdvSceneSwitcher.condition.websocket"});
|
||||
|
||||
static std::map<MacroConditionWebsocket::Type, std::string> conditionTypes = {
|
||||
{MacroConditionWebsocket::Type::REQUEST,
|
||||
"AdvSceneSwitcher.condition.websocket.type.request"},
|
||||
{MacroConditionWebsocket::Type::EVENT,
|
||||
"AdvSceneSwitcher.condition.websocket.type.event"},
|
||||
const static std::map<MacroConditionWebsocket::Type, std::string>
|
||||
conditionTypes = {
|
||||
{MacroConditionWebsocket::Type::REQUEST,
|
||||
"AdvSceneSwitcher.condition.websocket.type.request"},
|
||||
{MacroConditionWebsocket::Type::EVENT,
|
||||
"AdvSceneSwitcher.condition.websocket.type.event"},
|
||||
};
|
||||
|
||||
static bool matchRegex(const RegexConfig &conf, const std::string &msg,
|
||||
|
||||
@@ -40,7 +40,8 @@ static bool windowContainsText(const std::string &window,
|
||||
return text == matchText;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::WindowMatches(const std::string &window)
|
||||
bool MacroConditionWindow::WindowMatchesRequirements(
|
||||
const std::string &window) const
|
||||
{
|
||||
const bool focusCheckOK = (!_focus || window == switcher->currentTitle);
|
||||
if (!focusCheckOK) {
|
||||
@@ -60,26 +61,52 @@ bool MacroConditionWindow::WindowMatches(const std::string &window)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_checkText) {
|
||||
auto text = GetTextInWindow(window);
|
||||
SetVariableValue(text.value_or(""));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::WindowMatches(
|
||||
const std::vector<std::string> &windowList)
|
||||
{
|
||||
bool match = !_checkTitle ||
|
||||
std::find(windowList.begin(), windowList.end(),
|
||||
std::string(_window)) != windowList.end();
|
||||
match = match && WindowMatchesRequirements(_window);
|
||||
SetVariableValueBasedOnMatch(_window);
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::WindowRegexMatches(
|
||||
const std::vector<std::string> &windowList)
|
||||
{
|
||||
// No need to test if checking for window title is required as if the
|
||||
// user has disabled window title matching the option will always be
|
||||
// enabled in the backend and use the regular expression ".*".
|
||||
|
||||
for (const auto &window : windowList) {
|
||||
if (matchRegex(_windowRegex, window, _window) &&
|
||||
WindowMatches(window)) {
|
||||
WindowMatchesRequirements(window)) {
|
||||
SetVariableValueBasedOnMatch(window);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SetVariableValueBasedOnMatch("");
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacroConditionWindow::SetVariableValueBasedOnMatch(
|
||||
const std::string &matchWindow)
|
||||
{
|
||||
if (!IsReferencedInVars()) {
|
||||
return;
|
||||
}
|
||||
if (_checkText) {
|
||||
auto text = GetTextInWindow(matchWindow);
|
||||
SetVariableValue(text.value_or(""));
|
||||
} else {
|
||||
SetVariableValue(switcher->currentTitle);
|
||||
}
|
||||
}
|
||||
|
||||
static bool foregroundWindowChanged()
|
||||
{
|
||||
return switcher->currentTitle != switcher->lastTitle;
|
||||
@@ -87,19 +114,13 @@ static bool foregroundWindowChanged()
|
||||
|
||||
bool MacroConditionWindow::CheckCondition()
|
||||
{
|
||||
SetVariableValue("");
|
||||
if (!_checkText) {
|
||||
SetVariableValue(switcher->currentTitle);
|
||||
}
|
||||
|
||||
std::vector<std::string> windowList;
|
||||
GetWindowList(windowList);
|
||||
|
||||
bool match = false;
|
||||
if (_windowRegex.Enabled()) {
|
||||
match = WindowRegexMatches(windowList);
|
||||
} else {
|
||||
match = WindowMatches(_window);
|
||||
match = WindowMatches(windowList);
|
||||
}
|
||||
match = match && (!_windowFocusChanged || foregroundWindowChanged());
|
||||
return match;
|
||||
|
||||
@@ -21,10 +21,6 @@ public:
|
||||
return std::make_shared<MacroConditionWindow>(m);
|
||||
}
|
||||
|
||||
private:
|
||||
bool WindowMatches(const std::string &window);
|
||||
bool WindowRegexMatches(const std::vector<std::string> &windowList);
|
||||
|
||||
public:
|
||||
StringVariable _window;
|
||||
RegexConfig _windowRegex;
|
||||
@@ -40,6 +36,11 @@ public:
|
||||
RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig();
|
||||
|
||||
private:
|
||||
bool WindowMatchesRequirements(const std::string &window) const;
|
||||
bool WindowMatches(const std::vector<std::string> &windowList);
|
||||
bool WindowRegexMatches(const std::vector<std::string> &windowList);
|
||||
void SetVariableValueBasedOnMatch(const std::string &matchWindow);
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
@@ -6,20 +6,30 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroDock::MacroDock(Macro *m, QWidget *parent, const QString &runButtonText,
|
||||
const QString &pauseButtonText,
|
||||
const QString &unpauseButtonText)
|
||||
MacroDock::MacroDock(Macro *m, QWidget *parent,
|
||||
const StringVariable &runButtonText,
|
||||
const StringVariable &pauseButtonText,
|
||||
const StringVariable &unpauseButtonText,
|
||||
const StringVariable &conditionsTrueText,
|
||||
const StringVariable &conditionsFalseText,
|
||||
bool enableHighlight)
|
||||
: OBSDock(parent),
|
||||
_runButtonText(runButtonText),
|
||||
_pauseButtonText(pauseButtonText),
|
||||
_unpauseButtonText(unpauseButtonText),
|
||||
_run(new QPushButton(runButtonText)),
|
||||
_conditionsTrueText(conditionsTrueText),
|
||||
_conditionsFalseText(conditionsFalseText),
|
||||
_highlight(enableHighlight),
|
||||
_run(new QPushButton(runButtonText.c_str())),
|
||||
_pauseToggle(new QPushButton()),
|
||||
_statusText(new QLabel(conditionsFalseText.c_str())),
|
||||
_macro(m)
|
||||
{
|
||||
if (_macro) {
|
||||
setWindowTitle(QString::fromStdString(_macro->Name()));
|
||||
_run->setVisible(_macro->DockHasRunButton());
|
||||
_pauseToggle->setVisible(_macro->DockHasPauseButton());
|
||||
_statusText->setVisible(_macro->DockHasStatusLabel());
|
||||
} else {
|
||||
setWindowTitle("<deleted macro>");
|
||||
}
|
||||
@@ -34,12 +44,12 @@ MacroDock::MacroDock(Macro *m, QWidget *parent, const QString &runButtonText,
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(_run);
|
||||
layout->addWidget(_pauseToggle);
|
||||
layout->addWidget(_statusText);
|
||||
|
||||
QWidget::connect(&_timer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdatePauseText()));
|
||||
_timer.start(1000);
|
||||
|
||||
UpdatePauseText();
|
||||
UpdateText();
|
||||
QWidget::connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateText()));
|
||||
QWidget::connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight()));
|
||||
_timer.start(500);
|
||||
|
||||
// QFrame wrapper is necessary to avoid dock being partially
|
||||
// transparent
|
||||
@@ -63,9 +73,10 @@ void MacroDock::ShowRunButton(bool value)
|
||||
_run->setVisible(value);
|
||||
}
|
||||
|
||||
void MacroDock::SetRunButtonText(const QString &text)
|
||||
void MacroDock::SetRunButtonText(const StringVariable &text)
|
||||
{
|
||||
_run->setText(text);
|
||||
_runButtonText = text;
|
||||
_run->setText(text.c_str());
|
||||
}
|
||||
|
||||
void MacroDock::ShowPauseButton(bool value)
|
||||
@@ -73,16 +84,38 @@ void MacroDock::ShowPauseButton(bool value)
|
||||
_pauseToggle->setVisible(value);
|
||||
}
|
||||
|
||||
void MacroDock::SetPauseButtonText(const QString &text)
|
||||
void MacroDock::SetPauseButtonText(const StringVariable &text)
|
||||
{
|
||||
_pauseButtonText = text;
|
||||
UpdatePauseText();
|
||||
UpdateText();
|
||||
}
|
||||
|
||||
void MacroDock::SetUnpauseButtonText(const QString &text)
|
||||
void MacroDock::SetUnpauseButtonText(const StringVariable &text)
|
||||
{
|
||||
_unpauseButtonText = text;
|
||||
UpdatePauseText();
|
||||
UpdateText();
|
||||
}
|
||||
|
||||
void MacroDock::ShowStatusLabel(bool value)
|
||||
{
|
||||
_statusText->setVisible(value);
|
||||
}
|
||||
|
||||
void MacroDock::SetConditionsTrueText(const StringVariable &text)
|
||||
{
|
||||
_conditionsTrueText = text;
|
||||
UpdateText();
|
||||
}
|
||||
|
||||
void MacroDock::SetConditionsFalseText(const StringVariable &text)
|
||||
{
|
||||
_conditionsFalseText = text;
|
||||
UpdateText();
|
||||
}
|
||||
|
||||
void MacroDock::EnableHighlight(bool value)
|
||||
{
|
||||
_highlight = value;
|
||||
}
|
||||
|
||||
void MacroDock::RunClicked()
|
||||
@@ -106,17 +139,33 @@ void MacroDock::PauseToggleClicked()
|
||||
}
|
||||
|
||||
_macro->SetPaused(!_macro->Paused());
|
||||
UpdatePauseText();
|
||||
UpdateText();
|
||||
}
|
||||
|
||||
void MacroDock::UpdatePauseText()
|
||||
void MacroDock::UpdateText()
|
||||
{
|
||||
_run->setText(_runButtonText.c_str());
|
||||
|
||||
if (!_macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
_pauseToggle->setText(_macro->Paused() ? _unpauseButtonText
|
||||
: _pauseButtonText);
|
||||
_pauseToggle->setText(_macro->Paused() ? _unpauseButtonText.c_str()
|
||||
: _pauseButtonText.c_str());
|
||||
_statusText->setText(_macro->Matched() ? _conditionsTrueText.c_str()
|
||||
: _conditionsFalseText.c_str());
|
||||
}
|
||||
|
||||
void MacroDock::Highlight()
|
||||
{
|
||||
if (!_highlight || !_macro) {
|
||||
return;
|
||||
}
|
||||
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
|
||||
_macro->ExecutedSince(_lastHighlightCheckTime)) {
|
||||
PulseWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
|
||||
}
|
||||
_lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
#include "obs-dock.hpp"
|
||||
#include "variable-string.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QLabel>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -13,28 +16,42 @@ class MacroDock : public OBSDock {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroDock(Macro *, QWidget *parent, const QString &runButtonText,
|
||||
const QString &pauseButtonText,
|
||||
const QString &unpauseButtonText);
|
||||
MacroDock(Macro *, QWidget *parent, const StringVariable &runButtonText,
|
||||
const StringVariable &pauseButtonText,
|
||||
const StringVariable &unpauseButtonText,
|
||||
const StringVariable &conditionsTrueText,
|
||||
const StringVariable &conditionsFalseText,
|
||||
bool enableHighlight);
|
||||
void SetName(const QString &);
|
||||
void ShowRunButton(bool);
|
||||
void SetRunButtonText(const QString &);
|
||||
void SetRunButtonText(const StringVariable &);
|
||||
void ShowPauseButton(bool);
|
||||
void SetPauseButtonText(const QString &);
|
||||
void SetUnpauseButtonText(const QString &);
|
||||
void SetPauseButtonText(const StringVariable &);
|
||||
void SetUnpauseButtonText(const StringVariable &);
|
||||
void ShowStatusLabel(bool);
|
||||
void SetConditionsTrueText(const StringVariable &);
|
||||
void SetConditionsFalseText(const StringVariable &);
|
||||
void EnableHighlight(bool);
|
||||
|
||||
private slots:
|
||||
void RunClicked();
|
||||
void PauseToggleClicked();
|
||||
void UpdatePauseText();
|
||||
void UpdateText();
|
||||
void Highlight();
|
||||
|
||||
private:
|
||||
QString _pauseButtonText;
|
||||
QString _unpauseButtonText;
|
||||
StringVariable _runButtonText;
|
||||
StringVariable _pauseButtonText;
|
||||
StringVariable _unpauseButtonText;
|
||||
StringVariable _conditionsTrueText;
|
||||
StringVariable _conditionsFalseText;
|
||||
bool _highlight;
|
||||
QPushButton *_run;
|
||||
QPushButton *_pauseToggle;
|
||||
QLabel *_statusText;
|
||||
|
||||
QTimer _timer;
|
||||
std::chrono::high_resolution_clock::time_point _lastHighlightCheckTime{};
|
||||
|
||||
Macro *_macro;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -30,30 +32,6 @@ void MacroProperties::Load(obs_data_t *obj)
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
static void setGridLayoutRowVisible(QGridLayout *layout, int row, bool visible)
|
||||
{
|
||||
for (int col = 0; col < layout->columnCount(); col++) {
|
||||
auto item = layout->itemAtPosition(row, col);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto rowLayout = item->layout();
|
||||
if (rowLayout) {
|
||||
SetLayoutVisible(rowLayout, visible);
|
||||
}
|
||||
|
||||
auto widget = item->widget();
|
||||
if (widget) {
|
||||
widget->setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
if (!visible) {
|
||||
layout->setRowMinimumHeight(row, 0);
|
||||
}
|
||||
}
|
||||
|
||||
MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
const MacroProperties &prop,
|
||||
Macro *macro)
|
||||
@@ -68,15 +46,23 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
|
||||
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
|
||||
_currentSkipOnStartup(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
|
||||
_currentMacroRegisterDock(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentRegisterDock"))),
|
||||
_currentMacroDockAddRunButton(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDockAddRunButton"))),
|
||||
_currentMacroDockAddPauseButton(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDockAddPauseButton"))),
|
||||
_runButtonText(new QLineEdit()),
|
||||
_pauseButtonText(new QLineEdit()),
|
||||
_unpauseButtonText(new QLineEdit()),
|
||||
_currentMacroDockAddStatusLabel(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDockAddStatusLabel"))),
|
||||
_currentMacroDockHighlightIfConditionsTrue(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDockHighlightIfExecuted"))),
|
||||
_runButtonText(new VariableLineEdit(this)),
|
||||
_pauseButtonText(new VariableLineEdit(this)),
|
||||
_unpauseButtonText(new VariableLineEdit(this)),
|
||||
_conditionsTrueStatusText(new VariableLineEdit(this)),
|
||||
_conditionsFalseStatusText(new VariableLineEdit(this)),
|
||||
_dockOptions(new QGroupBox(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.dockSettings"))),
|
||||
_dockLayout(new QGridLayout())
|
||||
@@ -100,6 +86,12 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
hotkeyLayout->addWidget(_currentMacroRegisterHotkeys);
|
||||
hotkeyOptions->setLayout(hotkeyLayout);
|
||||
|
||||
auto generalOptions = new QGroupBox(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
|
||||
auto generalLayout = new QVBoxLayout;
|
||||
generalLayout->addWidget(_currentSkipOnStartup);
|
||||
generalOptions->setLayout(generalLayout);
|
||||
|
||||
int row = 0;
|
||||
_dockLayout->addWidget(_currentMacroRegisterDock, row, 1, 1, 2);
|
||||
row++;
|
||||
@@ -127,6 +119,26 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
row, 1);
|
||||
_dockLayout->addWidget(_unpauseButtonText, row, 2);
|
||||
_unpauseButtonTextRow = row;
|
||||
row++;
|
||||
_dockLayout->addWidget(_currentMacroDockAddStatusLabel, row, 1, 1, 2);
|
||||
row++;
|
||||
_dockLayout->addWidget(
|
||||
new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDockStatusText.true")),
|
||||
row, 1);
|
||||
_dockLayout->addWidget(_conditionsTrueStatusText, row, 2);
|
||||
_conditionsTrueTextRow = row;
|
||||
row++;
|
||||
_dockLayout->addWidget(
|
||||
new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDockStatusText.false")),
|
||||
row, 1);
|
||||
_dockLayout->addWidget(_conditionsFalseStatusText, row, 2);
|
||||
_conditionsFalseTextRow = row;
|
||||
row++;
|
||||
_dockLayout->addWidget(_currentMacroDockHighlightIfConditionsTrue, row,
|
||||
1, 1, 2);
|
||||
|
||||
_dockOptions->setLayout(_dockLayout);
|
||||
|
||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||
@@ -141,13 +153,26 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
&MacroPropertiesDialog::RunButtonEnableChanged);
|
||||
connect(_currentMacroDockAddPauseButton, &QCheckBox::stateChanged, this,
|
||||
&MacroPropertiesDialog::PauseButtonEnableChanged);
|
||||
connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this,
|
||||
&MacroPropertiesDialog::StatusLabelEnableChanged);
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
auto scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
auto contentWidget = new QWidget(scrollArea);
|
||||
auto layout = new QVBoxLayout(contentWidget);
|
||||
layout->addWidget(highlightOptions);
|
||||
layout->addWidget(hotkeyOptions);
|
||||
layout->addWidget(generalOptions);
|
||||
layout->addWidget(_dockOptions);
|
||||
layout->addWidget(buttonbox);
|
||||
setLayout(layout);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
scrollArea->setWidget(contentWidget);
|
||||
|
||||
auto dialogLayout = new QVBoxLayout();
|
||||
dialogLayout->addWidget(scrollArea);
|
||||
dialogLayout->addWidget(buttonbox);
|
||||
setLayout(dialogLayout);
|
||||
|
||||
_executed->setChecked(prop._highlightExecuted);
|
||||
_conditions->setChecked(prop._highlightConditions);
|
||||
@@ -155,53 +180,100 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys);
|
||||
if (!macro || macro->IsGroup()) {
|
||||
hotkeyOptions->hide();
|
||||
generalOptions->hide();
|
||||
_dockOptions->hide();
|
||||
return;
|
||||
}
|
||||
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
|
||||
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
|
||||
const bool dockEnabled = macro->DockEnabled();
|
||||
_currentMacroRegisterDock->setChecked(dockEnabled);
|
||||
_currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton());
|
||||
_currentMacroDockAddPauseButton->setChecked(
|
||||
macro->DockHasPauseButton());
|
||||
_runButtonText->setText(QString::fromStdString(macro->RunButtonText()));
|
||||
_pauseButtonText->setText(
|
||||
QString::fromStdString(macro->PauseButtonText()));
|
||||
_unpauseButtonText->setText(
|
||||
QString::fromStdString(macro->UnpauseButtonText()));
|
||||
_currentMacroDockAddStatusLabel->setChecked(
|
||||
macro->DockHasStatusLabel());
|
||||
_currentMacroDockHighlightIfConditionsTrue->setChecked(
|
||||
macro->DockHighlightEnabled());
|
||||
_runButtonText->setText(macro->RunButtonText());
|
||||
_pauseButtonText->setText(macro->PauseButtonText());
|
||||
_unpauseButtonText->setText(macro->UnpauseButtonText());
|
||||
_conditionsTrueStatusText->setText(macro->ConditionsTrueStatusText());
|
||||
_conditionsFalseStatusText->setText(macro->ConditionsFalseStatusText());
|
||||
|
||||
_currentMacroDockAddRunButton->setVisible(dockEnabled);
|
||||
_currentMacroDockAddPauseButton->setVisible(dockEnabled);
|
||||
setGridLayoutRowVisible(_dockLayout, _runButtonTextRow,
|
||||
_currentMacroDockAddStatusLabel->setVisible(dockEnabled);
|
||||
_currentMacroDockHighlightIfConditionsTrue->setVisible(dockEnabled);
|
||||
SetGridLayoutRowVisible(_dockLayout, _runButtonTextRow,
|
||||
dockEnabled && macro->DockHasRunButton());
|
||||
setGridLayoutRowVisible(_dockLayout, _pauseButtonTextRow,
|
||||
SetGridLayoutRowVisible(_dockLayout, _pauseButtonTextRow,
|
||||
dockEnabled && macro->DockHasPauseButton());
|
||||
setGridLayoutRowVisible(_dockLayout, _unpauseButtonTextRow,
|
||||
SetGridLayoutRowVisible(_dockLayout, _unpauseButtonTextRow,
|
||||
dockEnabled && macro->DockHasPauseButton());
|
||||
SetGridLayoutRowVisible(_dockLayout, _conditionsTrueTextRow,
|
||||
dockEnabled && macro->DockHasStatusLabel());
|
||||
SetGridLayoutRowVisible(_dockLayout, _conditionsFalseTextRow,
|
||||
dockEnabled && macro->DockHasStatusLabel());
|
||||
MinimizeSizeOfColumn(_dockLayout, 0);
|
||||
Resize();
|
||||
|
||||
// Try to set sensible initial size for the dialog window
|
||||
QSize contentSize = contentWidget->sizeHint();
|
||||
resize(contentSize.width() + layout->contentsMargins().left() +
|
||||
layout->contentsMargins().right() +
|
||||
dialogLayout->contentsMargins().left() +
|
||||
dialogLayout->contentsMargins().right() +
|
||||
scrollArea->verticalScrollBar()->sizeHint().width() + 20,
|
||||
contentSize.height() + dialogLayout->spacing() +
|
||||
buttonbox->sizeHint().height() +
|
||||
dialogLayout->contentsMargins().top() +
|
||||
dialogLayout->contentsMargins().bottom() +
|
||||
scrollArea->horizontalScrollBar()->sizeHint().height() +
|
||||
20);
|
||||
}
|
||||
|
||||
void MacroPropertiesDialog::DockEnableChanged(int enabled)
|
||||
{
|
||||
_currentMacroDockAddRunButton->setVisible(enabled);
|
||||
_currentMacroDockAddPauseButton->setVisible(enabled);
|
||||
setGridLayoutRowVisible(_dockLayout, _runButtonTextRow, enabled);
|
||||
setGridLayoutRowVisible(_dockLayout, _pauseButtonTextRow, enabled);
|
||||
setGridLayoutRowVisible(_dockLayout, _unpauseButtonTextRow, enabled);
|
||||
_currentMacroDockAddStatusLabel->setVisible(enabled);
|
||||
_currentMacroDockHighlightIfConditionsTrue->setVisible(enabled);
|
||||
SetGridLayoutRowVisible(
|
||||
_dockLayout, _runButtonTextRow,
|
||||
enabled && _currentMacroDockAddRunButton->isChecked());
|
||||
SetGridLayoutRowVisible(
|
||||
_dockLayout, _pauseButtonTextRow,
|
||||
enabled && _currentMacroDockAddPauseButton->isChecked());
|
||||
SetGridLayoutRowVisible(
|
||||
_dockLayout, _unpauseButtonTextRow,
|
||||
enabled && _currentMacroDockAddPauseButton->isChecked());
|
||||
SetGridLayoutRowVisible(
|
||||
_dockLayout, _conditionsTrueTextRow,
|
||||
enabled && _currentMacroDockAddStatusLabel->isChecked());
|
||||
SetGridLayoutRowVisible(
|
||||
_dockLayout, _conditionsFalseTextRow,
|
||||
enabled && _currentMacroDockAddStatusLabel->isChecked());
|
||||
Resize();
|
||||
}
|
||||
|
||||
void MacroPropertiesDialog::RunButtonEnableChanged(int enabled)
|
||||
{
|
||||
setGridLayoutRowVisible(_dockLayout, _runButtonTextRow, enabled);
|
||||
SetGridLayoutRowVisible(_dockLayout, _runButtonTextRow, enabled);
|
||||
Resize();
|
||||
}
|
||||
|
||||
void MacroPropertiesDialog::PauseButtonEnableChanged(int enabled)
|
||||
{
|
||||
setGridLayoutRowVisible(_dockLayout, _pauseButtonTextRow, enabled);
|
||||
setGridLayoutRowVisible(_dockLayout, _unpauseButtonTextRow, enabled);
|
||||
SetGridLayoutRowVisible(_dockLayout, _pauseButtonTextRow, enabled);
|
||||
SetGridLayoutRowVisible(_dockLayout, _unpauseButtonTextRow, enabled);
|
||||
Resize();
|
||||
}
|
||||
|
||||
void MacroPropertiesDialog::StatusLabelEnableChanged(int enabled)
|
||||
{
|
||||
SetGridLayoutRowVisible(_dockLayout, _conditionsTrueTextRow, enabled);
|
||||
SetGridLayoutRowVisible(_dockLayout, _conditionsFalseTextRow, enabled);
|
||||
Resize();
|
||||
}
|
||||
|
||||
@@ -209,8 +281,6 @@ void MacroPropertiesDialog::Resize()
|
||||
{
|
||||
_dockOptions->adjustSize();
|
||||
_dockOptions->updateGeometry();
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
|
||||
@@ -233,16 +303,25 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
|
||||
|
||||
macro->EnablePauseHotkeys(
|
||||
dialog._currentMacroRegisterHotkeys->isChecked());
|
||||
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
|
||||
macro->EnableDock(dialog._currentMacroRegisterDock->isChecked());
|
||||
macro->SetDockHasRunButton(
|
||||
dialog._currentMacroDockAddRunButton->isChecked());
|
||||
macro->SetDockHasPauseButton(
|
||||
dialog._currentMacroDockAddPauseButton->isChecked());
|
||||
macro->SetDockHasStatusLabel(
|
||||
dialog._currentMacroDockAddStatusLabel->isChecked());
|
||||
macro->SetHighlightEnable(
|
||||
dialog._currentMacroDockHighlightIfConditionsTrue->isChecked());
|
||||
macro->SetRunButtonText(dialog._runButtonText->text().toStdString());
|
||||
macro->SetPauseButtonText(
|
||||
dialog._pauseButtonText->text().toStdString());
|
||||
macro->SetUnpauseButtonText(
|
||||
dialog._unpauseButtonText->text().toStdString());
|
||||
macro->SetConditionsTrueStatusText(
|
||||
dialog._conditionsTrueStatusText->text().toStdString());
|
||||
macro->SetConditionsFalseStatusText(
|
||||
dialog._conditionsFalseStatusText->text().toStdString());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QDialog>
|
||||
@@ -35,6 +36,7 @@ private slots:
|
||||
void DockEnableChanged(int);
|
||||
void RunButtonEnableChanged(int);
|
||||
void PauseButtonEnableChanged(int);
|
||||
void StatusLabelEnableChanged(int);
|
||||
|
||||
private:
|
||||
void Resize();
|
||||
@@ -45,18 +47,25 @@ private:
|
||||
QCheckBox *_newMacroRegisterHotkeys;
|
||||
// Current macro specific settings
|
||||
QCheckBox *_currentMacroRegisterHotkeys;
|
||||
QCheckBox *_currentSkipOnStartup;
|
||||
QCheckBox *_currentMacroRegisterDock;
|
||||
QCheckBox *_currentMacroDockAddRunButton;
|
||||
QCheckBox *_currentMacroDockAddPauseButton;
|
||||
QLineEdit *_runButtonText;
|
||||
QLineEdit *_pauseButtonText;
|
||||
QLineEdit *_unpauseButtonText;
|
||||
QCheckBox *_currentMacroDockAddStatusLabel;
|
||||
QCheckBox *_currentMacroDockHighlightIfConditionsTrue;
|
||||
VariableLineEdit *_runButtonText;
|
||||
VariableLineEdit *_pauseButtonText;
|
||||
VariableLineEdit *_unpauseButtonText;
|
||||
VariableLineEdit *_conditionsTrueStatusText;
|
||||
VariableLineEdit *_conditionsFalseStatusText;
|
||||
QGroupBox *_dockOptions;
|
||||
QGridLayout *_dockLayout;
|
||||
|
||||
int _runButtonTextRow = -1;
|
||||
int _pauseButtonTextRow = -1;
|
||||
int _unpauseButtonTextRow = -1;
|
||||
int _conditionsTrueTextRow = -1;
|
||||
int _conditionsFalseTextRow = -1;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -8,18 +8,10 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroSelection::MacroSelection(QWidget *parent) : QComboBox(parent)
|
||||
MacroSelection::MacroSelection(QWidget *parent)
|
||||
: FilterComboBox(parent,
|
||||
obs_module_text("AdvSceneSwitcher.selectMacro"))
|
||||
{
|
||||
addItem(obs_module_text("AdvSceneSwitcher.selectMacro"));
|
||||
|
||||
QStandardItemModel *model =
|
||||
qobject_cast<QStandardItemModel *>(this->model());
|
||||
QModelIndex firstIndex =
|
||||
model->index(0, modelColumn(), rootModelIndex());
|
||||
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
|
||||
firstItem->setSelectable(false);
|
||||
firstItem->setEnabled(false);
|
||||
|
||||
for (const auto &m : switcher->macros) {
|
||||
if (m->IsGroup()) {
|
||||
continue;
|
||||
@@ -46,7 +38,7 @@ void MacroSelection::SetCurrentMacro(const MacroRef ¯o)
|
||||
{
|
||||
auto m = macro.GetMacro();
|
||||
if (!m) {
|
||||
this->setCurrentIndex(0);
|
||||
this->setCurrentIndex(-1);
|
||||
} else {
|
||||
this->setCurrentText(QString::fromStdString(m->Name()));
|
||||
}
|
||||
@@ -86,7 +78,7 @@ void MacroSelection::MacroRemove(const QString &name)
|
||||
return;
|
||||
}
|
||||
removeItem(idx);
|
||||
setCurrentIndex(0);
|
||||
setCurrentIndex(-1);
|
||||
}
|
||||
|
||||
void MacroSelection::MacroRename(const QString &oldName, const QString &newName)
|
||||
@@ -139,8 +131,9 @@ bool MacroSelectionDialog::AskForMacro(QWidget *parent, std::string ¯oName)
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
macroName = dialog._macroSelection->currentText().toUtf8().constData();
|
||||
if (macroName == obs_module_text("AdvSceneSwitcher.selectMacro")) {
|
||||
macroName = dialog._macroSelection->currentText().toStdString();
|
||||
if (macroName == obs_module_text("AdvSceneSwitcher.selectMacro") ||
|
||||
macroName.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <QComboBox>
|
||||
#include "filter-combo-box.hpp"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace advss {
|
||||
@@ -7,7 +8,7 @@ namespace advss {
|
||||
class Macro;
|
||||
class MacroRef;
|
||||
|
||||
class MacroSelection : public QComboBox {
|
||||
class MacroSelection : public FilterComboBox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
#include "switcher-data.hpp"
|
||||
#include "name-dialog.hpp"
|
||||
#include "macro-properties.hpp"
|
||||
#include "macro-export-import-dialog.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "version.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QMenu>
|
||||
@@ -18,7 +20,7 @@ namespace advss {
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QTimer onChangeHighlightTimer;
|
||||
|
||||
static bool macroNameExists(std::string name)
|
||||
static bool macroNameExists(const std::string &name)
|
||||
{
|
||||
return !!GetMacroByName(name.c_str());
|
||||
}
|
||||
@@ -60,12 +62,8 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
res = std::make_shared<Macro>(
|
||||
name,
|
||||
switcher->macroProperties._newMacroRegisterHotkeys);
|
||||
}
|
||||
res = std::make_shared<Macro>(
|
||||
name, switcher->macroProperties._newMacroRegisterHotkeys);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -77,11 +75,7 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
ui->macros->Add(newMacro);
|
||||
}
|
||||
|
||||
ui->macros->Add(newMacro);
|
||||
ui->macroAdd->disconnect(addPulse);
|
||||
emit MacroAdded(QString::fromStdString(name));
|
||||
}
|
||||
@@ -101,11 +95,7 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> ¯o)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
ui->macros->Remove(macro);
|
||||
}
|
||||
|
||||
ui->macros->Remove(macro);
|
||||
emit MacroRemoved(name);
|
||||
}
|
||||
|
||||
@@ -152,7 +142,6 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
|
||||
|
||||
void AdvSceneSwitcher::on_macroUp_clicked()
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
@@ -162,7 +151,6 @@ void AdvSceneSwitcher::on_macroUp_clicked()
|
||||
|
||||
void AdvSceneSwitcher::on_macroDown_clicked()
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
@@ -202,6 +190,179 @@ void AdvSceneSwitcher::RenameCurrentMacro()
|
||||
ui->macroName->setText(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> ¯os,
|
||||
const std::shared_ptr<Macro> &group)
|
||||
{
|
||||
std::vector<std::shared_ptr<Macro>> subitems;
|
||||
subitems.reserve(group->GroupSize());
|
||||
|
||||
// Find all subitems
|
||||
for (auto it = switcher->macros.begin(); it < switcher->macros.end();
|
||||
it++) {
|
||||
if ((*it)->Name() == group->Name()) {
|
||||
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
|
||||
subitems.emplace_back(*std::next(it, i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove subitems which were already selected to avoid duplicates
|
||||
for (const auto &subitem : subitems) {
|
||||
auto it = std::find(macros.begin(), macros.end(), subitem);
|
||||
if (it == macros.end()) {
|
||||
continue;
|
||||
}
|
||||
macros.erase(it);
|
||||
}
|
||||
|
||||
// Add group subitems
|
||||
auto it = std::find(macros.begin(), macros.end(), group);
|
||||
if (it == macros.end()) {
|
||||
return;
|
||||
}
|
||||
it = std::next(it);
|
||||
macros.insert(it, subitems.begin(), subitems.end());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ExportMacros()
|
||||
{
|
||||
auto selectedMacros = GetSelectedMacros();
|
||||
auto macros = selectedMacros;
|
||||
for (const auto ¯o : selectedMacros) {
|
||||
if (macro->IsGroup() && macro->GroupSize() > 0) {
|
||||
addGroupSubitems(macros, macro);
|
||||
}
|
||||
}
|
||||
|
||||
auto data = obs_data_create();
|
||||
auto macroArray = obs_data_array_create();
|
||||
for (const auto ¯o : macros) {
|
||||
obs_data_t *obj = obs_data_create();
|
||||
macro->Save(obj);
|
||||
obs_data_array_push_back(macroArray, obj);
|
||||
obs_data_release(obj);
|
||||
}
|
||||
obs_data_set_array(data, "macros", macroArray);
|
||||
obs_data_array_release(macroArray);
|
||||
obs_data_set_string(data, "version", g_GIT_TAG);
|
||||
auto json = obs_data_get_json(data);
|
||||
QString exportString(json);
|
||||
obs_data_release(data);
|
||||
|
||||
MacroExportImportDialog::ExportMacros(exportString);
|
||||
}
|
||||
|
||||
bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
|
||||
std::shared_ptr<Macro> ¯o)
|
||||
{
|
||||
QString errorMesg = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.import.nameConflict");
|
||||
errorMesg = errorMesg.arg(QString::fromStdString(macro->Name()),
|
||||
QString::fromStdString(macro->Name()));
|
||||
bool continueResolve = DisplayMessage(errorMesg, true);
|
||||
if (!continueResolve) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString format = QString::fromStdString(macro->Name()) + " %1";
|
||||
int i = 2;
|
||||
|
||||
QString placeHolderText = format.arg(i);
|
||||
while ((macroNameExists(placeHolderText.toUtf8().constData()))) {
|
||||
placeHolderText = format.arg(++i);
|
||||
}
|
||||
|
||||
std::string newName;
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.macroTab.add"),
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.name"), newName,
|
||||
placeHolderText);
|
||||
|
||||
if (!accepted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newName.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (macroNameExists(newName)) {
|
||||
DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
|
||||
return ResolveMacroImportNameConflict(macro);
|
||||
}
|
||||
|
||||
macro->SetName(newName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ImportMacros()
|
||||
{
|
||||
QString json;
|
||||
if (!MacroExportImportDialog::ImportMacros(json)) {
|
||||
return;
|
||||
}
|
||||
auto data = obs_data_create_from_json(json.toStdString().c_str());
|
||||
if (!data) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.import.invalid"));
|
||||
ImportMacros();
|
||||
return;
|
||||
}
|
||||
|
||||
auto version = obs_data_get_string(data, "version");
|
||||
if (strcmp(version, g_GIT_TAG) != 0) {
|
||||
blog(LOG_WARNING,
|
||||
"importing macros from non matching plugin version \"%s\"",
|
||||
version);
|
||||
}
|
||||
|
||||
auto array = obs_data_get_array(data, "macros");
|
||||
size_t count = obs_data_array_count(array);
|
||||
|
||||
int groupSize = 0;
|
||||
std::shared_ptr<Macro> group;
|
||||
|
||||
auto lock = LockContext();
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(array, i);
|
||||
auto macro = std::make_shared<Macro>();
|
||||
macro->Load(array_obj);
|
||||
macro->PostLoad();
|
||||
|
||||
if (macroNameExists(macro->Name()) &&
|
||||
!ResolveMacroImportNameConflict(macro)) {
|
||||
obs_data_release(array_obj);
|
||||
groupSize--;
|
||||
continue;
|
||||
}
|
||||
|
||||
switcher->macros.emplace_back(macro);
|
||||
if (groupSize > 0 && !macro->IsGroup()) {
|
||||
Macro::PrepareMoveToGroup(group, macro);
|
||||
groupSize--;
|
||||
}
|
||||
|
||||
if (macro->IsGroup()) {
|
||||
group = macro;
|
||||
groupSize = macro->GroupSize();
|
||||
// We are not sure if all elements will be added so we
|
||||
// have to reset the group size to zero and add elements
|
||||
// to the group as they come up.
|
||||
macro->ResetGroupSize();
|
||||
}
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(array);
|
||||
obs_data_release(data);
|
||||
|
||||
ui->macros->Reset(switcher->macros,
|
||||
switcher->macroProperties._highlightExecuted);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroName_editingFinished()
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
@@ -372,8 +533,7 @@ std::vector<std::shared_ptr<Macro>> AdvSceneSwitcher::GetSelectedMacros()
|
||||
return ui->macros->GetCurrentMacros();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::MacroSelectionChanged(const QItemSelection &,
|
||||
const QItemSelection &)
|
||||
void AdvSceneSwitcher::MacroSelectionChanged()
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
@@ -445,12 +605,8 @@ void AdvSceneSwitcher::SetupMacroTab()
|
||||
}
|
||||
ui->macros->Reset(switcher->macros,
|
||||
switcher->macroProperties._highlightExecuted);
|
||||
connect(ui->macros->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection &,
|
||||
const QItemSelection &)),
|
||||
this,
|
||||
SLOT(MacroSelectionChanged(const QItemSelection &,
|
||||
const QItemSelection &)));
|
||||
connect(ui->macros, SIGNAL(MacroSelectionChanged()), this,
|
||||
SLOT(MacroSelectionChanged()));
|
||||
|
||||
delete conditionsList;
|
||||
conditionsList = new MacroSegmentList(this);
|
||||
@@ -503,6 +659,16 @@ void AdvSceneSwitcher::SetupMacroTab()
|
||||
ui->macroActionConditionSplitter->setStyleSheet(
|
||||
"QSplitter::handle {background: transparent;}");
|
||||
|
||||
// Set action and condition control icons
|
||||
const std::string pathPrefix =
|
||||
GetDataFilePath("res/images/" + GetThemeTypeName());
|
||||
SetButtonIcon(ui->actionTop, (pathPrefix + "DoubleUp.svg").c_str());
|
||||
SetButtonIcon(ui->actionBottom,
|
||||
(pathPrefix + "DoubleDown.svg").c_str());
|
||||
SetButtonIcon(ui->conditionTop, (pathPrefix + "DoubleUp.svg").c_str());
|
||||
SetButtonIcon(ui->conditionBottom,
|
||||
(pathPrefix + "DoubleDown.svg").c_str());
|
||||
|
||||
// Reserve more space for macro edit area than for the macro list
|
||||
ui->macroListMacroEditSplitter->setStretchFactor(0, 1);
|
||||
ui->macroListMacroEditSplitter->setStretchFactor(1, 4);
|
||||
@@ -559,6 +725,15 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.remove"), this,
|
||||
&AdvSceneSwitcher::on_macroRemove_clicked);
|
||||
remove->setDisabled(ui->macros->SelectionEmpty());
|
||||
menu.addSeparator();
|
||||
|
||||
auto exportAction = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.export"), this,
|
||||
&AdvSceneSwitcher::ExportMacros);
|
||||
exportAction->setDisabled(ui->macros->SelectionEmpty());
|
||||
auto import = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.import"), this,
|
||||
&AdvSceneSwitcher::ImportMacros);
|
||||
|
||||
menu.exec(globalPos);
|
||||
}
|
||||
@@ -844,32 +1019,40 @@ void AdvSceneSwitcher::FadeOutActionControls()
|
||||
{
|
||||
fade(ui->actionAdd, true);
|
||||
fade(ui->actionRemove, true);
|
||||
fade(ui->actionTop, true);
|
||||
fade(ui->actionUp, true);
|
||||
fade(ui->actionDown, true);
|
||||
fade(ui->actionBottom, true);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::FadeOutConditionControls()
|
||||
{
|
||||
fade(ui->conditionAdd, true);
|
||||
fade(ui->conditionRemove, true);
|
||||
fade(ui->conditionTop, true);
|
||||
fade(ui->conditionUp, true);
|
||||
fade(ui->conditionDown, true);
|
||||
fade(ui->conditionBottom, true);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ResetOpacityActionControls()
|
||||
{
|
||||
fade(ui->actionAdd, false);
|
||||
fade(ui->actionRemove, false);
|
||||
fade(ui->actionTop, false);
|
||||
fade(ui->actionUp, false);
|
||||
fade(ui->actionDown, false);
|
||||
fade(ui->actionBottom, false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ResetOpacityConditionControls()
|
||||
{
|
||||
fade(ui->conditionAdd, false);
|
||||
fade(ui->conditionRemove, false);
|
||||
fade(ui->conditionTop, false);
|
||||
fade(ui->conditionUp, false);
|
||||
fade(ui->conditionDown, false);
|
||||
fade(ui->conditionBottom, false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::HighlightControls()
|
||||
|
||||
@@ -96,15 +96,15 @@ void MacroTreeItem::UpdatePaused()
|
||||
|
||||
void MacroTreeItem::HighlightIfExecuted()
|
||||
{
|
||||
if (!_highlight) {
|
||||
// Run check regardless to reset "_wasExecutedRecently"
|
||||
(void)_macro->WasExecutedRecently();
|
||||
if (!_highlight || !_macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_macro && _macro->WasExecutedRecently()) {
|
||||
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
|
||||
_macro->ExecutedSince(_lastHighlightCheckTime)) {
|
||||
PulseWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
|
||||
}
|
||||
_lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void MacroTreeItem::MacroRenamed(const QString &oldName, const QString &newName)
|
||||
@@ -277,6 +277,7 @@ void MacroTreeModel::MoveItemBefore(const std::shared_ptr<Macro> &item,
|
||||
blog(LOG_WARNING,
|
||||
"error while trying to move item \"%s\" before \"%s\"",
|
||||
item->Name().c_str(), before->Name().c_str());
|
||||
assert(IsInValidState());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -293,6 +294,7 @@ void MacroTreeModel::MoveItemBefore(const std::shared_ptr<Macro> &item,
|
||||
_macros.erase(it);
|
||||
_macros.insert(std::next(_macros.begin(), macroTo), tmp);
|
||||
endMoveRows();
|
||||
assert(IsInValidState());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -309,6 +311,7 @@ void MacroTreeModel::MoveItemBefore(const std::shared_ptr<Macro> &item,
|
||||
_macros.insert(std::next(_macros.begin(), macroTo + i), tmp);
|
||||
}
|
||||
endMoveRows();
|
||||
assert(IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTreeModel::MoveItemAfter(const std::shared_ptr<Macro> &item,
|
||||
@@ -318,34 +321,39 @@ void MacroTreeModel::MoveItemAfter(const std::shared_ptr<Macro> &item,
|
||||
return;
|
||||
}
|
||||
|
||||
int modelFromIdx = 0, modelFromEndIdx = 0, modelTo = 0, macroFrom = 0,
|
||||
macroTo = 0;
|
||||
auto moveAfter = after;
|
||||
int modelFromIdx = 0, modelFromEndIdx = 0, modelTo = 0;
|
||||
try {
|
||||
modelFromIdx = GetItemModelIndex(item);
|
||||
modelFromEndIdx = modelFromIdx;
|
||||
modelTo = GetItemModelIndex(after);
|
||||
macroFrom = GetItemMacroIndex(item);
|
||||
macroTo = GetItemMacroIndex(after);
|
||||
} catch (const std::out_of_range &) {
|
||||
blog(LOG_WARNING,
|
||||
"error while trying to move item \"%s\" after \"%s\"",
|
||||
item->Name().c_str(), after->Name().c_str());
|
||||
assert(IsInValidState());
|
||||
return;
|
||||
}
|
||||
|
||||
if (after->IsGroup()) {
|
||||
modelTo += after->IsCollapsed() ? 0 : after->GroupSize();
|
||||
macroTo += after->GroupSize();
|
||||
moveAfter = FindEndOfGroup(after, false);
|
||||
}
|
||||
|
||||
if (!item->IsGroup()) {
|
||||
beginMoveRows(QModelIndex(), modelFromIdx, modelFromEndIdx,
|
||||
QModelIndex(), modelTo + 1);
|
||||
auto it = std::next(_macros.begin(), macroFrom);
|
||||
auto it = std::find(_macros.begin(), _macros.end(), item);
|
||||
std::shared_ptr<Macro> tmp = *it;
|
||||
_macros.erase(it);
|
||||
_macros.insert(std::next(_macros.begin(), macroTo), tmp);
|
||||
it = std::find(_macros.begin(), _macros.end(), moveAfter);
|
||||
if (it == _macros.end()) {
|
||||
_macros.insert(it, tmp);
|
||||
} else {
|
||||
_macros.insert(std::next(it), tmp);
|
||||
}
|
||||
endMoveRows();
|
||||
assert(IsInValidState());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -355,13 +363,22 @@ void MacroTreeModel::MoveItemAfter(const std::shared_ptr<Macro> &item,
|
||||
|
||||
beginMoveRows(QModelIndex(), modelFromIdx, modelFromEndIdx,
|
||||
QModelIndex(), modelTo + 1);
|
||||
for (uint32_t i = 0; i <= item->GroupSize(); i++) {
|
||||
auto it = std::next(_macros.begin(), macroFrom);
|
||||
auto tmp = *it;
|
||||
auto groupStart = std::find(_macros.begin(), _macros.end(), item);
|
||||
auto groupEnd = std::next(groupStart, item->GroupSize() + 1);
|
||||
std::deque<std::shared_ptr<Macro>> elementsToMove(groupStart, groupEnd);
|
||||
for (const auto &element : elementsToMove) {
|
||||
auto it = std::find(_macros.begin(), _macros.end(), element);
|
||||
_macros.erase(it);
|
||||
_macros.insert(std::next(_macros.begin(), macroTo), tmp);
|
||||
it = std::find(_macros.begin(), _macros.end(), moveAfter);
|
||||
if (it == _macros.end()) {
|
||||
_macros.insert(it, element);
|
||||
} else {
|
||||
_macros.insert(std::next(it), element);
|
||||
}
|
||||
moveAfter = element;
|
||||
}
|
||||
endMoveRows();
|
||||
assert(IsInValidState());
|
||||
}
|
||||
|
||||
static inline int
|
||||
@@ -378,6 +395,7 @@ CountItemsVisibleInModel(const std::deque<std::shared_ptr<Macro>> ¯os)
|
||||
|
||||
void MacroTreeModel::Add(std::shared_ptr<Macro> item)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto idx = CountItemsVisibleInModel(_macros);
|
||||
beginInsertRows(QModelIndex(), idx, idx);
|
||||
_macros.emplace_back(item);
|
||||
@@ -390,6 +408,7 @@ void MacroTreeModel::Add(std::shared_ptr<Macro> item)
|
||||
|
||||
void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto uiStartIdx = GetItemModelIndex(item);
|
||||
if (uiStartIdx == -1) {
|
||||
return;
|
||||
@@ -419,6 +438,7 @@ void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
|
||||
if (isGroup) {
|
||||
UpdateGroupState(true);
|
||||
}
|
||||
assert(IsInValidState());
|
||||
}
|
||||
|
||||
std::shared_ptr<Macro> MacroTreeModel::Neighbor(const std::shared_ptr<Macro> &m,
|
||||
@@ -586,6 +606,37 @@ bool MacroTreeModel::IsLastItem(std::shared_ptr<Macro> item) const
|
||||
return GetItemModelIndex(item) + 1 == (int)_macros.size();
|
||||
}
|
||||
|
||||
bool MacroTreeModel::IsInValidState()
|
||||
{
|
||||
// Check for reordering erros
|
||||
for (int i = 0, j = 0; i < _macros.size(); i++) {
|
||||
const auto &m = _macros[i];
|
||||
if (QString::fromStdString(m->Name()) !=
|
||||
data(index(j, 0), Qt::AccessibleTextRole)) {
|
||||
return false;
|
||||
}
|
||||
if (m->IsGroup() && m->IsCollapsed()) {
|
||||
i += m->GroupSize();
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
// Check for group errors
|
||||
for (int i = 0; i < _macros.size(); i++) {
|
||||
const auto &m = _macros[i];
|
||||
if (!m->IsGroup()) {
|
||||
continue;
|
||||
}
|
||||
const auto groupSize = m->GroupSize();
|
||||
assert(i + groupSize < _macros.size());
|
||||
for (uint32_t j = 1; j <= groupSize; j++) {
|
||||
assert(_macros[i + j]->IsSubitem());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
|
||||
{
|
||||
if (indices.count() == 0) {
|
||||
@@ -608,14 +659,15 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
|
||||
itemsToGroup.emplace_back(item);
|
||||
}
|
||||
|
||||
std::shared_ptr<Macro> item =
|
||||
std::shared_ptr<Macro> group =
|
||||
Macro::CreateGroup(name.toStdString(), itemsToGroup);
|
||||
if (!item) {
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A new list entry for group
|
||||
_macros.insert(_macros.begin() + insertGroupAt, item);
|
||||
// Add new list entry for group
|
||||
insertGroupAt = ModelIndexToMacroIndex(insertGroupAt, _macros);
|
||||
_macros.insert(_macros.begin() + insertGroupAt, group);
|
||||
|
||||
// Move all selected items after new group entry
|
||||
int offset = 1;
|
||||
@@ -630,6 +682,7 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
|
||||
_mt->selectionModel()->clear();
|
||||
|
||||
Reset(_macros);
|
||||
assert(IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
|
||||
@@ -650,6 +703,7 @@ void MacroTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
|
||||
_mt->selectionModel()->clear();
|
||||
|
||||
Reset(_macros);
|
||||
assert(IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTreeModel::ExpandGroup(std::shared_ptr<Macro> item)
|
||||
@@ -664,6 +718,7 @@ void MacroTreeModel::ExpandGroup(std::shared_ptr<Macro> item)
|
||||
Reset(_macros);
|
||||
|
||||
_mt->selectionModel()->clear();
|
||||
assert(IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTreeModel::CollapseGroup(std::shared_ptr<Macro> item)
|
||||
@@ -678,6 +733,7 @@ void MacroTreeModel::CollapseGroup(std::shared_ptr<Macro> item)
|
||||
Reset(_macros);
|
||||
|
||||
_mt->selectionModel()->clear();
|
||||
assert(IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTreeModel::UpdateGroupState(bool update)
|
||||
@@ -705,6 +761,12 @@ void MacroTree::Reset(std::deque<std::shared_ptr<Macro>> ¯os,
|
||||
MacroTreeModel *mtm = new MacroTreeModel(this, macros);
|
||||
setModel(mtm);
|
||||
GetModel()->Reset(macros);
|
||||
connect(selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection &,
|
||||
const QItemSelection &)),
|
||||
this,
|
||||
SLOT(SelectionChangedHelper(const QItemSelection &,
|
||||
const QItemSelection &)));
|
||||
}
|
||||
|
||||
void MacroTree::Add(std::shared_ptr<Macro> item,
|
||||
@@ -714,6 +776,7 @@ void MacroTree::Add(std::shared_ptr<Macro> item,
|
||||
if (after) {
|
||||
MoveItemAfter(item, after);
|
||||
}
|
||||
assert(GetModel()->IsInValidState());
|
||||
}
|
||||
|
||||
std::shared_ptr<Macro> MacroTree::GetCurrentMacro() const
|
||||
@@ -759,6 +822,7 @@ void MacroTree::ResetWidgets()
|
||||
}
|
||||
modelIdx++;
|
||||
}
|
||||
assert(GetModel()->IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTree::UpdateWidget(const QModelIndex &idx,
|
||||
@@ -1043,6 +1107,7 @@ void MacroTree::dropEvent(QDropEvent *event)
|
||||
event->setDropAction(Qt::CopyAction);
|
||||
|
||||
QListView::dropEvent(event);
|
||||
assert(GetModel()->IsInValidState());
|
||||
}
|
||||
|
||||
bool MacroTree::GroupsSelected() const
|
||||
@@ -1116,6 +1181,7 @@ void MacroTree::Remove(std::shared_ptr<Macro> item) const
|
||||
|
||||
void MacroTree::Up(std::shared_ptr<Macro> item) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto above = GetModel()->Neighbor(item, true);
|
||||
if (!above) {
|
||||
return;
|
||||
@@ -1141,6 +1207,7 @@ void MacroTree::Up(std::shared_ptr<Macro> item) const
|
||||
|
||||
void MacroTree::Down(std::shared_ptr<Macro> item) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto below = GetModel()->Neighbor(item, false);
|
||||
if (!below) {
|
||||
return;
|
||||
@@ -1180,12 +1247,20 @@ void MacroTree::GroupSelectedItems()
|
||||
QModelIndexList indices = selectedIndexes();
|
||||
std::sort(indices.begin(), indices.end());
|
||||
GetModel()->GroupSelectedItems(indices);
|
||||
assert(GetModel()->IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTree::UngroupSelectedGroups()
|
||||
{
|
||||
QModelIndexList indices = selectedIndexes();
|
||||
GetModel()->UngroupSelectedGroups(indices);
|
||||
assert(GetModel()->IsInValidState());
|
||||
}
|
||||
|
||||
void MacroTree::SelectionChangedHelper(const QItemSelection &,
|
||||
const QItemSelection &)
|
||||
{
|
||||
emit MacroSelectionChanged();
|
||||
}
|
||||
|
||||
inline MacroTreeItem *MacroTree::GetItemWidget(int idx) const
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <deque>
|
||||
#include <chrono>
|
||||
|
||||
class QLabel;
|
||||
class QSpacerItem;
|
||||
@@ -61,6 +62,7 @@ private:
|
||||
QLabel *_label = nullptr;
|
||||
MacroTree *_tree;
|
||||
bool _highlight;
|
||||
std::chrono::high_resolution_clock::time_point _lastHighlightCheckTime{};
|
||||
QTimer _timer;
|
||||
std::shared_ptr<Macro> _macro;
|
||||
|
||||
@@ -105,6 +107,7 @@ private:
|
||||
int GetItemMacroIndex(const std::shared_ptr<Macro> &item) const;
|
||||
int GetItemModelIndex(const std::shared_ptr<Macro> &item) const;
|
||||
bool IsLastItem(std::shared_ptr<Macro> item) const;
|
||||
bool IsInValidState();
|
||||
|
||||
MacroTree *_mt;
|
||||
std::deque<std::shared_ptr<Macro>> &_macros;
|
||||
@@ -135,6 +138,11 @@ public:
|
||||
public slots:
|
||||
void GroupSelectedItems();
|
||||
void UngroupSelectedGroups();
|
||||
void SelectionChangedHelper(const QItemSelection &,
|
||||
const QItemSelection &);
|
||||
|
||||
signals:
|
||||
void MacroSelectionChanged();
|
||||
|
||||
protected:
|
||||
virtual void dropEvent(QDropEvent *event) override;
|
||||
|
||||
@@ -30,7 +30,12 @@ Macro::~Macro()
|
||||
_die = true;
|
||||
Stop();
|
||||
ClearHotkeys();
|
||||
RemoveDock();
|
||||
|
||||
// Keep the dock widgets in case of shutdown so they can be restored by
|
||||
// OBS on startup
|
||||
if (!switcher->obsIsShuttingDown) {
|
||||
RemoveDock();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Macro>
|
||||
@@ -171,21 +176,14 @@ bool Macro::CeckMatch()
|
||||
}
|
||||
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
||||
|
||||
bool newLastMatched = _matched;
|
||||
if (_matched && _matchOnChange && _lastMatched == _matched) {
|
||||
bool matchedBeforeOnChangeCheck = _matched;
|
||||
if (_matched && _matchOnChange && _lastMatched) {
|
||||
vblog(LOG_INFO, "ignore match for Macro %s (on change)",
|
||||
_name.c_str());
|
||||
_matched = false;
|
||||
SetOnChangeHighlight();
|
||||
}
|
||||
_lastMatched = newLastMatched;
|
||||
|
||||
// TODO: Move back to PerformAction() once new scene collection frontend
|
||||
// events are available - see:
|
||||
// https://github.com/obsproject/obs-studio/commit/feda1aaa283e8a99f6ba1159cfe6b9c1f2934a61
|
||||
if (_matched && _runCount != std::numeric_limits<int>::max()) {
|
||||
_runCount++;
|
||||
}
|
||||
_lastMatched = matchedBeforeOnChangeCheck;
|
||||
_lastCheckTime = std::chrono::high_resolution_clock::now();
|
||||
return _matched;
|
||||
}
|
||||
@@ -208,14 +206,23 @@ bool Macro::PerformActions(bool forceParallel, bool ignorePause)
|
||||
} else {
|
||||
RunActions(ret, ignorePause);
|
||||
}
|
||||
_wasExecutedRecently = true;
|
||||
_lastExecutionTime = std::chrono::high_resolution_clock::now();
|
||||
auto group = _parent.lock();
|
||||
if (group) {
|
||||
group->_wasExecutedRecently = true;
|
||||
group->_lastExecutionTime = _lastExecutionTime;
|
||||
}
|
||||
if (_runCount != std::numeric_limits<int>::max()) {
|
||||
_runCount++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Macro::ExecutedSince(
|
||||
const std::chrono::high_resolution_clock::time_point &time)
|
||||
{
|
||||
return _lastExecutionTime > time;
|
||||
}
|
||||
|
||||
int64_t Macro::MsSinceLastCheck() const
|
||||
{
|
||||
if (_lastCheckTime.time_since_epoch().count() == 0) {
|
||||
@@ -241,19 +248,27 @@ void Macro::ResetTimers()
|
||||
c->ResetDuration();
|
||||
}
|
||||
_lastCheckTime = {};
|
||||
_lastExecutionTime = {};
|
||||
}
|
||||
|
||||
void Macro::RunActions(bool &retVal, bool ignorePause)
|
||||
{
|
||||
bool ret = true;
|
||||
for (auto &a : _actions) {
|
||||
a->LogAction();
|
||||
ret = ret && a->PerformAction();
|
||||
if (a->Enabled()) {
|
||||
a->LogAction();
|
||||
ret = ret && a->PerformAction();
|
||||
} else {
|
||||
vblog(LOG_INFO, "skipping disabled action %s",
|
||||
a->GetId().c_str());
|
||||
}
|
||||
if (!ret || (_paused && !ignorePause) || _stop || _die) {
|
||||
retVal = ret;
|
||||
break;
|
||||
}
|
||||
a->SetHighlight();
|
||||
if (a->Enabled()) {
|
||||
a->SetHighlight();
|
||||
}
|
||||
}
|
||||
_done = true;
|
||||
}
|
||||
@@ -346,6 +361,7 @@ bool Macro::Save(obs_data_t *obj) const
|
||||
obs_data_set_bool(obj, "pause", _paused);
|
||||
obs_data_set_bool(obj, "parallel", _runInParallel);
|
||||
obs_data_set_bool(obj, "onChange", _matchOnChange);
|
||||
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
|
||||
|
||||
obs_data_set_bool(obj, "group", _isGroup);
|
||||
if (_isGroup) {
|
||||
@@ -438,6 +454,7 @@ bool Macro::Load(obs_data_t *obj)
|
||||
_paused = obs_data_get_bool(obj, "pause");
|
||||
_runInParallel = obs_data_get_bool(obj, "parallel");
|
||||
_matchOnChange = obs_data_get_bool(obj, "onChange");
|
||||
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
|
||||
|
||||
_isGroup = obs_data_get_bool(obj, "group");
|
||||
if (_isGroup) {
|
||||
@@ -543,15 +560,6 @@ bool Macro::SwitchesScene() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Macro::WasExecutedRecently()
|
||||
{
|
||||
if (_wasExecutedRecently) {
|
||||
_wasExecutedRecently = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Macro::OnChangePreventedActionsRecently()
|
||||
{
|
||||
if (_onChangeTriggered) {
|
||||
@@ -601,12 +609,16 @@ void Macro::SaveDockSettings(obs_data_t *obj) const
|
||||
}
|
||||
obs_data_set_bool(dockSettings, "hasRunButton", _dockHasRunButton);
|
||||
obs_data_set_bool(dockSettings, "hasPauseButton", _dockHasPauseButton);
|
||||
obs_data_set_string(dockSettings, "runButtonText",
|
||||
_runButtonText.c_str());
|
||||
obs_data_set_string(dockSettings, "pauseButtonText",
|
||||
_pauseButtonText.c_str());
|
||||
obs_data_set_string(dockSettings, "unpauseButtonText",
|
||||
_unpauseButtonText.c_str());
|
||||
obs_data_set_bool(dockSettings, "hasStatusLabel", _dockHasStatusLabel);
|
||||
obs_data_set_bool(dockSettings, "highlightIfConditionsTrue",
|
||||
_dockHighlight);
|
||||
_runButtonText.Save(dockSettings, "runButtonText");
|
||||
_pauseButtonText.Save(dockSettings, "pauseButtonText");
|
||||
_unpauseButtonText.Save(dockSettings, "unpauseButtonText");
|
||||
_conditionsTrueStatusText.Save(dockSettings,
|
||||
"conditionsTrueStatusText");
|
||||
_conditionsFalseStatusText.Save(dockSettings,
|
||||
"conditionsFalseStatusText");
|
||||
if (_dock) {
|
||||
auto window = static_cast<QMainWindow *>(
|
||||
obs_frontend_get_main_window());
|
||||
@@ -648,16 +660,22 @@ void Macro::LoadDockSettings(obs_data_t *obj)
|
||||
obs_data_set_default_string(
|
||||
dockSettings, "unpauseButtonText",
|
||||
obs_module_text("AdvSceneSwitcher.macroDock.unpause"));
|
||||
|
||||
_runButtonText = obs_data_get_string(dockSettings, "runButtonText");
|
||||
_pauseButtonText = obs_data_get_string(dockSettings, "pauseButtonText");
|
||||
_unpauseButtonText =
|
||||
obs_data_get_string(dockSettings, "unpauseButtonText");
|
||||
_runButtonText.Load(dockSettings, "runButtonText");
|
||||
_pauseButtonText.Load(dockSettings, "pauseButtonText");
|
||||
_unpauseButtonText.Load(dockSettings, "unpauseButtonText");
|
||||
_conditionsTrueStatusText.Load(dockSettings,
|
||||
"conditionsTrueStatusText");
|
||||
_conditionsFalseStatusText.Load(dockSettings,
|
||||
"conditionsFalseStatusText");
|
||||
if (dockEnabled) {
|
||||
_dockHasRunButton =
|
||||
obs_data_get_bool(dockSettings, "hasRunButton");
|
||||
_dockHasPauseButton =
|
||||
obs_data_get_bool(dockSettings, "hasPauseButton");
|
||||
_dockHasStatusLabel =
|
||||
obs_data_get_bool(dockSettings, "hasStatusLabel");
|
||||
_dockHighlight = obs_data_get_bool(dockSettings,
|
||||
"highlightIfConditionsTrue");
|
||||
_dockIsFloating = obs_data_get_bool(dockSettings, "isFloating");
|
||||
_dockArea = static_cast<Qt::DockWidgetArea>(
|
||||
obs_data_get_int(dockSettings, "area"));
|
||||
@@ -692,10 +710,9 @@ void Macro::EnableDock(bool value)
|
||||
// Create new dock widget
|
||||
auto window =
|
||||
static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||
_dock = new MacroDock(this, window,
|
||||
QString::fromStdString(_runButtonText),
|
||||
QString::fromStdString(_pauseButtonText),
|
||||
QString::fromStdString(_unpauseButtonText));
|
||||
_dock = new MacroDock(this, window, _runButtonText, _pauseButtonText,
|
||||
_unpauseButtonText, _conditionsTrueStatusText,
|
||||
_conditionsFalseStatusText, _dockHighlight);
|
||||
SetDockWidgetName(); // Used by OBS to restore position
|
||||
|
||||
// Register new dock
|
||||
@@ -705,14 +722,16 @@ void Macro::EnableDock(bool value)
|
||||
// geometry set here.
|
||||
// The function calls here are only intended to attempt to restore the
|
||||
// dock status when switching scene collections.
|
||||
_dock->setVisible(_dockIsVisible);
|
||||
if (window->dockWidgetArea(_dock) != _dockArea) {
|
||||
window->addDockWidget(_dockArea, _dock);
|
||||
if (switcher->startupLoadDone) {
|
||||
_dock->setVisible(_dockIsVisible);
|
||||
if (window->dockWidgetArea(_dock) != _dockArea) {
|
||||
window->addDockWidget(_dockArea, _dock);
|
||||
}
|
||||
if (_dock->isFloating() != _dockIsFloating) {
|
||||
_dock->setFloating(_dockIsFloating);
|
||||
}
|
||||
_dock->restoreGeometry(_dockGeo);
|
||||
}
|
||||
if (_dock->isFloating() != _dockIsFloating) {
|
||||
_dock->setFloating(_dockIsFloating);
|
||||
}
|
||||
_dock->restoreGeometry(_dockGeo);
|
||||
_registerDock = value;
|
||||
}
|
||||
|
||||
@@ -734,13 +753,31 @@ void Macro::SetDockHasPauseButton(bool value)
|
||||
_dock->ShowPauseButton(value);
|
||||
}
|
||||
|
||||
void Macro::SetDockHasStatusLabel(bool value)
|
||||
{
|
||||
_dockHasStatusLabel = value;
|
||||
if (!_dock) {
|
||||
return;
|
||||
}
|
||||
_dock->ShowStatusLabel(value);
|
||||
}
|
||||
|
||||
void Macro::SetHighlightEnable(bool value)
|
||||
{
|
||||
_dockHighlight = value;
|
||||
if (!_dock) {
|
||||
return;
|
||||
}
|
||||
_dock->EnableHighlight(value);
|
||||
}
|
||||
|
||||
void Macro::SetRunButtonText(const std::string &text)
|
||||
{
|
||||
_runButtonText = text;
|
||||
if (!_dock) {
|
||||
return;
|
||||
}
|
||||
_dock->SetRunButtonText(QString::fromStdString(text));
|
||||
_dock->SetRunButtonText(text);
|
||||
}
|
||||
|
||||
void Macro::SetPauseButtonText(const std::string &text)
|
||||
@@ -749,7 +786,7 @@ void Macro::SetPauseButtonText(const std::string &text)
|
||||
if (!_dock) {
|
||||
return;
|
||||
}
|
||||
_dock->SetPauseButtonText(QString::fromStdString(text));
|
||||
_dock->SetPauseButtonText(text);
|
||||
}
|
||||
|
||||
void Macro::SetUnpauseButtonText(const std::string &text)
|
||||
@@ -758,7 +795,35 @@ void Macro::SetUnpauseButtonText(const std::string &text)
|
||||
if (!_dock) {
|
||||
return;
|
||||
}
|
||||
_dock->SetUnpauseButtonText(QString::fromStdString(text));
|
||||
_dock->SetUnpauseButtonText(text);
|
||||
}
|
||||
|
||||
void Macro::SetConditionsTrueStatusText(const std::string &text)
|
||||
{
|
||||
_conditionsTrueStatusText = text;
|
||||
if (!_dock) {
|
||||
return;
|
||||
}
|
||||
_dock->SetConditionsTrueText(text);
|
||||
}
|
||||
|
||||
StringVariable Macro::ConditionsTrueStatusText() const
|
||||
{
|
||||
return _conditionsTrueStatusText;
|
||||
}
|
||||
|
||||
void Macro::SetConditionsFalseStatusText(const std::string &text)
|
||||
{
|
||||
_conditionsFalseStatusText = text;
|
||||
if (!_dock) {
|
||||
return;
|
||||
}
|
||||
_dock->SetConditionsFalseText(text);
|
||||
}
|
||||
|
||||
StringVariable Macro::ConditionsFalseStatusText() const
|
||||
{
|
||||
return _conditionsFalseStatusText;
|
||||
}
|
||||
|
||||
void Macro::RemoveDock()
|
||||
@@ -963,7 +1028,7 @@ bool SwitcherData::CheckMacros()
|
||||
|
||||
bool SwitcherData::RunMacros()
|
||||
{
|
||||
// Create copy of macor list as elements might be removed, inserted, or
|
||||
// Create copy of macro list as elements might be removed, inserted, or
|
||||
// reordered while macros are currently being executed.
|
||||
// For example, this can happen if a macro is performing a wait action,
|
||||
// as the main lock will be unlocked during this time.
|
||||
@@ -984,12 +1049,18 @@ bool SwitcherData::RunMacros()
|
||||
}
|
||||
|
||||
for (auto &m : runPhaseMacros) {
|
||||
if (m && m->Matched()) {
|
||||
vblog(LOG_INFO, "running macro: %s", m->Name().c_str());
|
||||
if (!m->PerformActions()) {
|
||||
blog(LOG_WARNING, "abort macro: %s",
|
||||
m->Name().c_str());
|
||||
}
|
||||
if (!m || !m->Matched()) {
|
||||
continue;
|
||||
}
|
||||
if (firstInterval && m->SkipExecOnStart()) {
|
||||
blog(LOG_INFO,
|
||||
"skip execution of macro \"%s\" at startup",
|
||||
m->Name().c_str());
|
||||
continue;
|
||||
}
|
||||
vblog(LOG_INFO, "running macro: %s", m->Name().c_str());
|
||||
if (!m->PerformActions()) {
|
||||
blog(LOG_WARNING, "abort macro: %s", m->Name().c_str());
|
||||
}
|
||||
}
|
||||
if (GetLock()) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "macro-action.hpp"
|
||||
#include "macro-condition.hpp"
|
||||
#include "macro-ref.hpp"
|
||||
#include "variable-string.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
@@ -36,6 +37,8 @@ public:
|
||||
bool Paused() const { return _paused; }
|
||||
void SetMatchOnChange(bool onChange) { _matchOnChange = onChange; }
|
||||
bool MatchOnChange() const { return _matchOnChange; }
|
||||
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
|
||||
bool SkipExecOnStart() const { return _skipExecOnStart; }
|
||||
int RunCount() const { return _runCount; };
|
||||
void ResetRunCount() { _runCount = 0; };
|
||||
void ResetTimers();
|
||||
@@ -59,6 +62,7 @@ public:
|
||||
std::shared_ptr<Macro> item);
|
||||
bool IsGroup() const { return _isGroup; }
|
||||
uint32_t GroupSize() const { return _groupSize; }
|
||||
void ResetGroupSize() { _groupSize = 0; }
|
||||
bool IsSubitem() const { return !_parent.expired(); }
|
||||
void SetCollapsed(bool val) { _isCollapsed = val; }
|
||||
bool IsCollapsed() const { return _isCollapsed; }
|
||||
@@ -76,7 +80,8 @@ public:
|
||||
bool SwitchesScene() const;
|
||||
|
||||
// UI helpers
|
||||
bool WasExecutedRecently();
|
||||
bool
|
||||
ExecutedSince(const std::chrono::high_resolution_clock::time_point &);
|
||||
bool OnChangePreventedActionsRecently();
|
||||
void ResetUIHelpers();
|
||||
|
||||
@@ -89,12 +94,20 @@ public:
|
||||
bool DockHasRunButton() const { return _dockHasRunButton; }
|
||||
void SetDockHasPauseButton(bool value);
|
||||
bool DockHasPauseButton() const { return _dockHasPauseButton; }
|
||||
std::string RunButtonText() const { return _runButtonText; }
|
||||
void SetDockHasStatusLabel(bool value);
|
||||
bool DockHasStatusLabel() const { return _dockHasStatusLabel; }
|
||||
void SetHighlightEnable(bool value);
|
||||
bool DockHighlightEnabled() const { return _dockHighlight; }
|
||||
StringVariable RunButtonText() const { return _runButtonText; }
|
||||
void SetRunButtonText(const std::string &text);
|
||||
std::string PauseButtonText() const { return _pauseButtonText; }
|
||||
StringVariable PauseButtonText() const { return _pauseButtonText; }
|
||||
void SetPauseButtonText(const std::string &text);
|
||||
std::string UnpauseButtonText() const { return _unpauseButtonText; }
|
||||
StringVariable UnpauseButtonText() const { return _unpauseButtonText; }
|
||||
void SetUnpauseButtonText(const std::string &text);
|
||||
void SetConditionsTrueStatusText(const std::string &text);
|
||||
StringVariable ConditionsTrueStatusText() const;
|
||||
void SetConditionsFalseStatusText(const std::string &text);
|
||||
StringVariable ConditionsFalseStatusText() const;
|
||||
|
||||
private:
|
||||
void SetupHotkeys();
|
||||
@@ -114,6 +127,7 @@ private:
|
||||
bool _stop = false;
|
||||
bool _done = true;
|
||||
std::chrono::high_resolution_clock::time_point _lastCheckTime{};
|
||||
std::chrono::high_resolution_clock::time_point _lastExecutionTime{};
|
||||
std::thread _backgroundThread;
|
||||
std::vector<std::thread> _helperThreads;
|
||||
|
||||
@@ -128,7 +142,8 @@ private:
|
||||
bool _runInParallel = false;
|
||||
bool _matched = false;
|
||||
bool _lastMatched = false;
|
||||
bool _matchOnChange = false;
|
||||
bool _matchOnChange = true;
|
||||
bool _skipExecOnStart = false;
|
||||
bool _paused = false;
|
||||
int _runCount = 0;
|
||||
bool _registerHotkeys = true;
|
||||
@@ -136,19 +151,23 @@ private:
|
||||
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
|
||||
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
|
||||
|
||||
// UI helpers for the macro tab
|
||||
bool _wasExecutedRecently = false;
|
||||
bool _onChangeTriggered = false;
|
||||
|
||||
bool _registerDock = false;
|
||||
bool _dockHasRunButton = true;
|
||||
bool _dockHasPauseButton = true;
|
||||
std::string _runButtonText =
|
||||
bool _dockHasStatusLabel = false;
|
||||
bool _dockHighlight = false;
|
||||
StringVariable _runButtonText =
|
||||
obs_module_text("AdvSceneSwitcher.macroDock.run");
|
||||
std::string _pauseButtonText =
|
||||
StringVariable _pauseButtonText =
|
||||
obs_module_text("AdvSceneSwitcher.macroDock.pause");
|
||||
std::string _unpauseButtonText =
|
||||
StringVariable _unpauseButtonText =
|
||||
obs_module_text("AdvSceneSwitcher.macroDock.unpause");
|
||||
StringVariable _conditionsTrueStatusText =
|
||||
obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.true");
|
||||
StringVariable _conditionsFalseStatusText =
|
||||
obs_module_text("AdvSceneSwitcher.macroDock.statusLabel.false");
|
||||
bool _dockIsFloating = true;
|
||||
bool _dockIsVisible = false;
|
||||
Qt::DockWidgetArea _dockArea;
|
||||
|
||||
@@ -17,6 +17,17 @@ install_advss_plugin_dependency(...)
|
||||
... to install the plugin and its dependencies.
|
||||
#]]
|
||||
|
||||
add_subdirectory(midi)
|
||||
add_subdirectory(openvr)
|
||||
add_subdirectory(video)
|
||||
option(ENABLE_MIDI_PLUGIN "Enable the midi plugin" ON)
|
||||
if(ENABLE_MIDI_PLUGIN)
|
||||
add_subdirectory(midi)
|
||||
endif()
|
||||
|
||||
option(ENABLE_OPENVR_PLUGIN "Enable the openvr plugin" ON)
|
||||
if(ENABLE_OPENVR_PLUGIN)
|
||||
add_subdirectory(openvr)
|
||||
endif()
|
||||
|
||||
option(ENABLE_VIDEO_PLUGIN "Enable the video plugin" ON)
|
||||
if(ENABLE_VIDEO_PLUGIN)
|
||||
add_subdirectory(video)
|
||||
endif()
|
||||
|
||||
@@ -12,6 +12,10 @@ if(NOT EXISTS "${LIBREMIDI_DIR}")
|
||||
endif()
|
||||
add_subdirectory("${LIBREMIDI_DIR}" "${LIBREMIDI_DIR}/build" EXCLUDE_FROM_ALL)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(libremidi PRIVATE /wd4251 /wd4267 /wd4275)
|
||||
endif(MSVC)
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
add_library(${PROJECT_NAME} MODULE)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
#include <screenshot-helper.hpp>
|
||||
#include <slider-spinbox.hpp>
|
||||
#include <variable-text-edit.hpp>
|
||||
#include <variable-line-edit.hpp>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
@@ -42,6 +43,7 @@ public:
|
||||
void ResetLastMatch() { _lastMatchResult = false; }
|
||||
double GetCurrentBrightness() const { return _currentBrightness; }
|
||||
void SetPageSegMode(tesseract::PageSegMode);
|
||||
bool SetLanguage(const std::string &);
|
||||
|
||||
VideoInput _video;
|
||||
VideoCondition _condition = VideoCondition::MATCH;
|
||||
@@ -56,8 +58,9 @@ public:
|
||||
NumberVariable<double> _brightnessThreshold = 0.5;
|
||||
PatternMatchParameters _patternMatchParameters;
|
||||
ObjDetectParameters _objMatchParameters;
|
||||
OCRParameters _ocrParamters;
|
||||
AreaParamters _areaParameters;
|
||||
OCRParameters _ocrParameters;
|
||||
ColorParameters _colorParameters;
|
||||
AreaParameters _areaParameters;
|
||||
bool _throttleEnabled = false;
|
||||
int _throttleCount = 3;
|
||||
|
||||
@@ -67,6 +70,7 @@ private:
|
||||
bool ScreenshotContainsObject();
|
||||
bool CheckBrightnessThreshold();
|
||||
bool CheckOCR();
|
||||
bool CheckColor();
|
||||
bool Compare();
|
||||
bool CheckShouldBeSkipped();
|
||||
|
||||
@@ -84,6 +88,139 @@ private:
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class BrightnessEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BrightnessEdit(QWidget *parent,
|
||||
const std::shared_ptr<MacroConditionVideo> &);
|
||||
|
||||
private slots:
|
||||
void BrightnessThresholdChanged(const NumberVariable<double> &);
|
||||
void UpdateCurrentBrightness();
|
||||
|
||||
private:
|
||||
SliderSpinBox *_threshold;
|
||||
QLabel *_current;
|
||||
QTimer _timer;
|
||||
|
||||
std::shared_ptr<MacroConditionVideo> _data;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
class OCREdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OCREdit(QWidget *parent, PreviewDialog *,
|
||||
const std::shared_ptr<MacroConditionVideo> &);
|
||||
|
||||
private slots:
|
||||
void SelectColorClicked();
|
||||
void ColorThresholdChanged(const NumberVariable<double> &);
|
||||
void MatchTextChanged();
|
||||
void RegexChanged(RegexConfig conf);
|
||||
void PageSegModeChanged(int);
|
||||
void LanguageChanged();
|
||||
|
||||
private:
|
||||
void SetupColorLabel(const QColor &);
|
||||
|
||||
VariableTextEdit *_matchText;
|
||||
RegexConfigWidget *_regex;
|
||||
QLabel *_textColor;
|
||||
QPushButton *_selectColor;
|
||||
SliderSpinBox *_colorThreshold;
|
||||
QComboBox *_pageSegMode;
|
||||
VariableLineEdit *_languageCode;
|
||||
|
||||
PreviewDialog *_previewDialog;
|
||||
|
||||
std::shared_ptr<MacroConditionVideo> _data;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
class ObjectDetectEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ObjectDetectEdit(QWidget *parent, PreviewDialog *,
|
||||
const std::shared_ptr<MacroConditionVideo> &);
|
||||
|
||||
private slots:
|
||||
void ModelPathChanged(const QString &text);
|
||||
void ObjectScaleThresholdChanged(const NumberVariable<double> &);
|
||||
void MinNeighborsChanged(int value);
|
||||
void MinSizeChanged(Size value);
|
||||
void MaxSizeChanged(Size value);
|
||||
|
||||
private:
|
||||
FileSelection *_modelDataPath;
|
||||
SliderSpinBox *_objectScaleThreshold;
|
||||
QSpinBox *_minNeighbors;
|
||||
QLabel *_minNeighborsDescription;
|
||||
SizeSelection *_minSize;
|
||||
SizeSelection *_maxSize;
|
||||
|
||||
PreviewDialog *_previewDialog;
|
||||
|
||||
std::shared_ptr<MacroConditionVideo> _data;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
class ColorEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ColorEdit(QWidget *parent,
|
||||
const std::shared_ptr<MacroConditionVideo> &);
|
||||
|
||||
private slots:
|
||||
void SelectColorClicked();
|
||||
void MatchThresholdChanged(const NumberVariable<double> &);
|
||||
void ColorThresholdChanged(const NumberVariable<double> &);
|
||||
|
||||
private:
|
||||
void SetupColorLabel(const QColor &);
|
||||
|
||||
SliderSpinBox *_matchThreshold;
|
||||
SliderSpinBox *_colorThreshold;
|
||||
QLabel *_color;
|
||||
QPushButton *_selectColor;
|
||||
|
||||
std::shared_ptr<MacroConditionVideo> _data;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
class AreaEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AreaEdit(QWidget *parent, PreviewDialog *,
|
||||
const std::shared_ptr<MacroConditionVideo> &);
|
||||
|
||||
private slots:
|
||||
void CheckAreaEnableChanged(int value);
|
||||
void CheckAreaChanged(Area);
|
||||
void CheckAreaChanged(QRect area);
|
||||
void SelectAreaClicked();
|
||||
|
||||
signals:
|
||||
void Resized();
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QCheckBox *_checkAreaEnable;
|
||||
AreaSelection *_checkArea;
|
||||
QPushButton *_selectArea;
|
||||
|
||||
PreviewDialog *_previewDialog;
|
||||
|
||||
std::shared_ptr<MacroConditionVideo> _data;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
class MacroConditionVideoEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -117,38 +254,20 @@ private slots:
|
||||
void UseAlphaAsMaskChanged(int value);
|
||||
void PatternMatchModeChanged(int value);
|
||||
|
||||
void BrightnessThresholdChanged(const NumberVariable<double> &);
|
||||
|
||||
void ModelPathChanged(const QString &text);
|
||||
void ObjectScaleThresholdChanged(const NumberVariable<double> &);
|
||||
void MinNeighborsChanged(int value);
|
||||
void MinSizeChanged(Size value);
|
||||
void MaxSizeChanged(Size value);
|
||||
|
||||
void SelectColorClicked();
|
||||
void MatchTextChanged();
|
||||
void RegexChanged(RegexConfig conf);
|
||||
void PageSegModeChanged(int);
|
||||
|
||||
void CheckAreaEnableChanged(int value);
|
||||
void CheckAreaChanged(Area);
|
||||
void CheckAreaChanged(QRect area);
|
||||
void SelectAreaClicked();
|
||||
|
||||
void ThrottleEnableChanged(int value);
|
||||
void ThrottleCountChanged(int value);
|
||||
void ShowMatchClicked();
|
||||
|
||||
void UpdateCurrentBrightness();
|
||||
void SetWidgetVisibility();
|
||||
void Resize();
|
||||
|
||||
signals:
|
||||
void VideoSelectionChanged(const VideoInput &);
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
void HandleVideoInputUpdate();
|
||||
void SetupPreviewDialogParams();
|
||||
void SetupColorLabel(const QColor &);
|
||||
|
||||
QComboBox *_videoInputTypes;
|
||||
SceneSelectionWidget *_scenes;
|
||||
@@ -165,40 +284,19 @@ private:
|
||||
QHBoxLayout *_patternMatchModeLayout;
|
||||
QComboBox *_patternMatchMode;
|
||||
|
||||
SliderSpinBox *_brightnessThreshold;
|
||||
QLabel *_currentBrightness;
|
||||
QPushButton *_showMatch;
|
||||
PreviewDialog _previewDialog;
|
||||
|
||||
QVBoxLayout *_ocrLayout;
|
||||
VariableTextEdit *_matchText;
|
||||
RegexConfigWidget *_regex;
|
||||
QLabel *_textColor;
|
||||
QPushButton *_selectColor;
|
||||
QComboBox *_pageSegMode;
|
||||
|
||||
FileSelection *_modelDataPath;
|
||||
QHBoxLayout *_modelPathLayout;
|
||||
SliderSpinBox *_objectScaleThreshold;
|
||||
QHBoxLayout *_neighborsControlLayout;
|
||||
QSpinBox *_minNeighbors;
|
||||
QLabel *_minNeighborsDescription;
|
||||
QHBoxLayout *_sizeLayout;
|
||||
SizeSelection *_minSize;
|
||||
SizeSelection *_maxSize;
|
||||
|
||||
QHBoxLayout *_checkAreaControlLayout;
|
||||
QCheckBox *_checkAreaEnable;
|
||||
AreaSelection *_checkArea;
|
||||
QPushButton *_selectArea;
|
||||
BrightnessEdit *_brightness;
|
||||
OCREdit *_ocr;
|
||||
ObjectDetectEdit *_objectDetect;
|
||||
ColorEdit *_color;
|
||||
AreaEdit *_area;
|
||||
|
||||
QHBoxLayout *_throttleControlLayout;
|
||||
QCheckBox *_throttleEnable;
|
||||
QSpinBox *_throttleCount;
|
||||
|
||||
QPushButton *_showMatch;
|
||||
PreviewDialog _previewDialog;
|
||||
|
||||
QTimer _updateBrightnessTimer;
|
||||
|
||||
std::shared_ptr<MacroConditionVideo> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "opencv-helpers.hpp"
|
||||
|
||||
#include <log-helper.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
PatternImageData createPatternData(QImage &pattern)
|
||||
PatternImageData CreatePatternData(const QImage &pattern)
|
||||
{
|
||||
PatternImageData data{};
|
||||
if (pattern.isNull()) {
|
||||
@@ -24,13 +26,12 @@ static void invertPatternMatchResult(cv::Mat &mat)
|
||||
{
|
||||
for (int r = 0; r < mat.rows; r++) {
|
||||
for (int c = 0; c < mat.cols; c++) {
|
||||
float value = mat.at<float>(r, c) =
|
||||
1.0 - mat.at<float>(r, c);
|
||||
mat.at<float>(r, c) = 1.0 - mat.at<float>(r, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void matchPattern(QImage &img, const PatternImageData &patternData,
|
||||
void MatchPattern(QImage &img, const PatternImageData &patternData,
|
||||
double threshold, cv::Mat &result, bool useAlphaAsMask,
|
||||
cv::TemplateMatchModes matchMode)
|
||||
{
|
||||
@@ -74,41 +75,46 @@ void matchPattern(QImage &img, const PatternImageData &patternData,
|
||||
cv::threshold(result, result, threshold, 0.0, cv::THRESH_TOZERO);
|
||||
}
|
||||
|
||||
void matchPattern(QImage &img, QImage &pattern, double threshold,
|
||||
void MatchPattern(QImage &img, QImage &pattern, double threshold,
|
||||
cv::Mat &result, bool useAlphaAsMask,
|
||||
cv::TemplateMatchModes matchColor)
|
||||
{
|
||||
auto data = createPatternData(pattern);
|
||||
matchPattern(img, data, threshold, result, useAlphaAsMask, matchColor);
|
||||
auto data = CreatePatternData(pattern);
|
||||
MatchPattern(img, data, threshold, result, useAlphaAsMask, matchColor);
|
||||
}
|
||||
|
||||
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
|
||||
std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade,
|
||||
double scaleFactor, int minNeighbors,
|
||||
cv::Size minSize, cv::Size maxSize)
|
||||
const cv::Size &minSize,
|
||||
const cv::Size &maxSize)
|
||||
{
|
||||
if (img.isNull() || cascade.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto i = QImageToMat(img);
|
||||
auto image = QImageToMat(img);
|
||||
cv::Mat frameGray;
|
||||
cv::cvtColor(i, frameGray, cv::COLOR_RGBA2GRAY);
|
||||
equalizeHist(frameGray, frameGray);
|
||||
cv::cvtColor(image, frameGray, cv::COLOR_RGBA2GRAY);
|
||||
cv::equalizeHist(frameGray, frameGray);
|
||||
std::vector<cv::Rect> objects;
|
||||
cascade.detectMultiScale(frameGray, objects, scaleFactor, minNeighbors,
|
||||
0, minSize, maxSize);
|
||||
try {
|
||||
cascade.detectMultiScale(frameGray, objects, scaleFactor,
|
||||
minNeighbors, 0, minSize, maxSize);
|
||||
} catch (const std::exception &e) {
|
||||
vblog(LOG_INFO, "detectMultiScale failed: %s", e.what());
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
uchar getAvgBrightness(QImage &img)
|
||||
uchar GetAvgBrightness(QImage &img)
|
||||
{
|
||||
if (img.isNull()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto i = QImageToMat(img);
|
||||
auto image = QImageToMat(img);
|
||||
cv::Mat hsvImage, rgbImage;
|
||||
cv::cvtColor(i, rgbImage, cv::COLOR_RGBA2RGB);
|
||||
cv::cvtColor(image, rgbImage, cv::COLOR_RGBA2RGB);
|
||||
cv::cvtColor(rgbImage, hsvImage, cv::COLOR_RGB2HSV);
|
||||
long long brightnessSum = 0;
|
||||
for (int i = 0; i < hsvImage.rows; ++i) {
|
||||
@@ -119,21 +125,39 @@ uchar getAvgBrightness(QImage &img)
|
||||
return brightnessSum / (hsvImage.rows * hsvImage.cols);
|
||||
}
|
||||
|
||||
cv::Mat preprocessForOCR(const QImage &image, const QColor &color)
|
||||
static bool colorIsSimilar(const QColor &color1, const QColor &color2,
|
||||
int maxDiff)
|
||||
{
|
||||
const int diffRed = std::abs(color1.red() - color2.red());
|
||||
const int diffGreen = std::abs(color1.green() - color2.green());
|
||||
const int diffBlue = std::abs(color1.blue() - color2.blue());
|
||||
|
||||
return diffRed <= maxDiff && diffGreen <= maxDiff &&
|
||||
diffBlue <= maxDiff;
|
||||
}
|
||||
|
||||
cv::Mat PreprocessForOCR(const QImage &image, const QColor &textColor,
|
||||
double colorDiff)
|
||||
{
|
||||
auto mat = QImageToMat(image);
|
||||
|
||||
// Only keep the desired color
|
||||
cv::cvtColor(mat, mat, cv::COLOR_RGBA2RGB);
|
||||
cv::cvtColor(mat, mat, cv::COLOR_RGB2HSV);
|
||||
cv::inRange(mat, cv::Scalar(0, 0, 0),
|
||||
cv::Scalar(color.red(), color.green(), color.blue()), mat);
|
||||
// Tesseract works best when matching black text on a white background,
|
||||
// so everything that matches the text color will be displayed black
|
||||
// while the rest of the image should be white.
|
||||
const int diff = colorDiff * 255;
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
for (int x = 0; x < image.width(); x++) {
|
||||
if (colorIsSimilar(image.pixelColor(x, y), textColor,
|
||||
diff)) {
|
||||
mat.at<cv::Vec4b>(y, x) = {0, 0, 0, 255};
|
||||
} else {
|
||||
mat.at<cv::Vec4b>(y, x) = {255, 255, 255, 255};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invert to improve ORC detection
|
||||
cv::bitwise_not(mat, mat);
|
||||
|
||||
// Scale image up if selected area is too small
|
||||
// Results will probably still be unsatisfying
|
||||
// Scale image up if selected area is very small.
|
||||
// Results will probably still be unsatisfying.
|
||||
if (mat.rows <= 300 || mat.cols <= 300) {
|
||||
double scale = 0.;
|
||||
if (mat.rows < mat.cols) {
|
||||
@@ -141,25 +165,28 @@ cv::Mat preprocessForOCR(const QImage &image, const QColor &color)
|
||||
} else {
|
||||
scale = 300. / mat.cols;
|
||||
}
|
||||
|
||||
cv::resize(mat, mat,
|
||||
cv::Size(mat.cols * scale, mat.rows * scale),
|
||||
cv::INTER_CUBIC);
|
||||
}
|
||||
|
||||
return mat;
|
||||
cv::Mat result;
|
||||
mat.copyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string runOCR(tesseract::TessBaseAPI *ocr, const QImage &image,
|
||||
const QColor &color)
|
||||
std::string RunOCR(tesseract::TessBaseAPI *ocr, const QImage &image,
|
||||
const QColor &color, double colorDiff)
|
||||
{
|
||||
if (image.isNull()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
#ifdef OCR_SUPPORT
|
||||
auto mat = preprocessForOCR(image, color);
|
||||
ocr->SetImage(mat.data, mat.cols, mat.rows, 1, mat.step);
|
||||
auto mat = PreprocessForOCR(image, color, colorDiff);
|
||||
cv::Mat gray;
|
||||
cv::cvtColor(mat, gray, cv::COLOR_RGBA2GRAY);
|
||||
ocr->SetImage(gray.data, gray.cols, gray.rows, 1, gray.step);
|
||||
ocr->Recognize(0);
|
||||
std::unique_ptr<char[]> detectedText(ocr->GetUTF8Text());
|
||||
|
||||
@@ -173,6 +200,28 @@ std::string runOCR(tesseract::TessBaseAPI *ocr, const QImage &image,
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ContainsPixelsInColorRange(const QImage &image, const QColor &color,
|
||||
double colorDeviationThreshold,
|
||||
double totalPixelMatchThreshold)
|
||||
{
|
||||
int totalPixels = image.width() * image.height();
|
||||
int matchingPixels = 0;
|
||||
int maxColorDiff = static_cast<int>(colorDeviationThreshold * 255.0);
|
||||
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
for (int x = 0; x < image.width(); x++) {
|
||||
if (colorIsSimilar(image.pixelColor(x, y), color,
|
||||
maxColorDiff)) {
|
||||
matchingPixels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double matchPercentage =
|
||||
static_cast<double>(matchingPixels) / totalPixels;
|
||||
return matchPercentage >= totalPixelMatchThreshold;
|
||||
}
|
||||
|
||||
// Assumption is that QImage uses Format_RGBA8888.
|
||||
// Conversion from: https://github.com/dbzhang800/QtOpenCV
|
||||
cv::Mat QImageToMat(const QImage &img)
|
||||
|
||||
@@ -47,19 +47,25 @@ struct PatternImageData {
|
||||
cv::Mat1b mask;
|
||||
};
|
||||
|
||||
PatternImageData createPatternData(QImage &pattern);
|
||||
void matchPattern(QImage &img, const PatternImageData &patternData,
|
||||
PatternImageData CreatePatternData(const QImage &pattern);
|
||||
void MatchPattern(QImage &img, const PatternImageData &patternData,
|
||||
double threshold, cv::Mat &result, bool useAlphaAsMask,
|
||||
cv::TemplateMatchModes matchMode);
|
||||
void matchPattern(QImage &img, QImage &pattern, double threshold,
|
||||
void MatchPattern(QImage &img, QImage &pattern, double threshold,
|
||||
cv::Mat &result, bool useAlphaAsMask,
|
||||
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,
|
||||
cv::Size minSize, cv::Size maxSize);
|
||||
uchar getAvgBrightness(QImage &img);
|
||||
cv::Mat preprocessForOCR(const QImage &image, const QColor &color);
|
||||
std::string runOCR(tesseract::TessBaseAPI *, const QImage &, const QColor &);
|
||||
const cv::Size &minSize,
|
||||
const cv::Size &maxSize);
|
||||
uchar GetAvgBrightness(QImage &img);
|
||||
cv::Mat PreprocessForOCR(const QImage &image, const QColor &color,
|
||||
double colorDiff);
|
||||
std::string RunOCR(tesseract::TessBaseAPI *, const QImage &, const QColor &,
|
||||
double colorDiff);
|
||||
bool ContainsPixelsInColorRange(const QImage &image, const QColor &color,
|
||||
double colorDeviationThreshold,
|
||||
double totalPixelMatchThreshold);
|
||||
cv::Mat QImageToMat(const QImage &img);
|
||||
QImage MatToQImage(const cv::Mat &mat);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "paramerter-wrappers.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace advss {
|
||||
|
||||
bool PatternMatchParameters::Save(obs_data_t *obj) const
|
||||
@@ -106,7 +108,7 @@ bool ObjDetectParameters::Load(obs_data_t *obj)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AreaParamters::Save(obs_data_t *obj) const
|
||||
bool AreaParameters::Save(obs_data_t *obj) const
|
||||
{
|
||||
auto data = obs_data_create();
|
||||
obs_data_set_bool(data, "enabled", enable);
|
||||
@@ -116,7 +118,7 @@ bool AreaParamters::Save(obs_data_t *obj) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AreaParamters::Load(obs_data_t *obj)
|
||||
bool AreaParameters::Load(obs_data_t *obj)
|
||||
{
|
||||
// TODO: Remove this fallback in a future version
|
||||
if (!obs_data_has_user_value(obj, "areaData")) {
|
||||
@@ -219,6 +221,17 @@ static void SaveColor(obs_data_t *obj, const char *name, const QColor &color)
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
static QColor LoadColor(obs_data_t *obj, const char *name)
|
||||
{
|
||||
QColor color = Qt::black;
|
||||
auto data = obs_data_get_obj(obj, name);
|
||||
color.setRed(obs_data_get_int(data, "red"));
|
||||
color.setGreen(obs_data_get_int(data, "green"));
|
||||
color.setBlue(obs_data_get_int(data, "blue"));
|
||||
obs_data_release(data);
|
||||
return color;
|
||||
}
|
||||
|
||||
OCRParameters::OCRParameters()
|
||||
{
|
||||
Setup();
|
||||
@@ -236,9 +249,12 @@ OCRParameters::OCRParameters(const OCRParameters &other)
|
||||
: text(other.text),
|
||||
regex(other.regex),
|
||||
color(other.color),
|
||||
colorThreshold(other.colorThreshold),
|
||||
pageSegMode(other.pageSegMode)
|
||||
{
|
||||
Setup();
|
||||
if (!initDone) {
|
||||
Setup();
|
||||
}
|
||||
if (initDone) {
|
||||
ocr->SetPageSegMode(pageSegMode);
|
||||
}
|
||||
@@ -249,8 +265,14 @@ OCRParameters &OCRParameters::operator=(const OCRParameters &other)
|
||||
text = other.text;
|
||||
regex = other.regex;
|
||||
color = other.color;
|
||||
colorThreshold = other.colorThreshold;
|
||||
pageSegMode = other.pageSegMode;
|
||||
ocr->SetPageSegMode(pageSegMode);
|
||||
if (!initDone) {
|
||||
Setup();
|
||||
}
|
||||
if (initDone) {
|
||||
ocr->SetPageSegMode(pageSegMode);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -259,30 +281,27 @@ bool OCRParameters::Save(obs_data_t *obj) const
|
||||
auto data = obs_data_create();
|
||||
text.Save(data, "pattern");
|
||||
regex.Save(data);
|
||||
languageCode.Save(data, "language");
|
||||
SaveColor(data, "textColor", color);
|
||||
colorThreshold.Save(data, "colorThreshold");
|
||||
obs_data_set_int(data, "pageSegMode", static_cast<int>(pageSegMode));
|
||||
obs_data_set_int(data, "version", 1);
|
||||
obs_data_set_obj(obj, "ocrData", data);
|
||||
obs_data_release(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
static QColor LoadColor(obs_data_t *obj, const char *name)
|
||||
{
|
||||
QColor color = Qt::black;
|
||||
auto data = obs_data_get_obj(obj, name);
|
||||
color.setRed(obs_data_get_int(data, "red"));
|
||||
color.setGreen(obs_data_get_int(data, "green"));
|
||||
color.setBlue(obs_data_get_int(data, "blue"));
|
||||
obs_data_release(data);
|
||||
return color;
|
||||
}
|
||||
|
||||
bool OCRParameters::Load(obs_data_t *obj)
|
||||
{
|
||||
auto data = obs_data_get_obj(obj, "ocrData");
|
||||
text.Load(data, "pattern");
|
||||
regex.Load(data);
|
||||
obs_data_set_default_string(data, "language", "eng");
|
||||
languageCode.Load(data, "language");
|
||||
color = LoadColor(data, "textColor");
|
||||
if (obs_data_has_user_value(data, "version")) {
|
||||
colorThreshold.Load(data, "colorThreshold");
|
||||
}
|
||||
pageSegMode = static_cast<tesseract::PageSegMode>(
|
||||
obs_data_get_int(data, "pageSegMode"));
|
||||
obs_data_release(data);
|
||||
@@ -299,6 +318,24 @@ void OCRParameters::SetPageMode(tesseract::PageSegMode mode)
|
||||
ocr->SetPageSegMode(mode);
|
||||
}
|
||||
|
||||
bool OCRParameters::SetLanguageCode(const std::string &value)
|
||||
{
|
||||
std::string dataPath = obs_get_module_data_path(obs_current_module()) +
|
||||
std::string("/res/ocr") + "/" + value +
|
||||
".traineddata";
|
||||
if (!std::filesystem::exists(dataPath)) {
|
||||
return false;
|
||||
}
|
||||
Setup();
|
||||
ocr->SetPageSegMode(pageSegMode);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string OCRParameters::GetLanguageCode() const
|
||||
{
|
||||
return languageCode;
|
||||
}
|
||||
|
||||
void OCRParameters::Setup()
|
||||
{
|
||||
ocr = std::make_unique<tesseract::TessBaseAPI>();
|
||||
@@ -308,11 +345,32 @@ void OCRParameters::Setup()
|
||||
}
|
||||
std::string dataPath = obs_get_module_data_path(obs_current_module()) +
|
||||
std::string("/res/ocr");
|
||||
if (ocr->Init(dataPath.c_str(), "eng") != 0) {
|
||||
if (ocr->Init(dataPath.c_str(), languageCode.c_str()) != 0) {
|
||||
initDone = false;
|
||||
return;
|
||||
}
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
bool ColorParameters::Save(obs_data_t *obj) const
|
||||
{
|
||||
auto data = obs_data_create();
|
||||
SaveColor(data, "color", color);
|
||||
colorThreshold.Save(data, "colorThreshold");
|
||||
matchThreshold.Save(data, "matchThreshold");
|
||||
obs_data_set_obj(obj, "colorData", data);
|
||||
obs_data_release(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ColorParameters::Load(obs_data_t *obj)
|
||||
{
|
||||
auto data = obs_data_get_obj(obj, "colorData");
|
||||
color = LoadColor(data, "color");
|
||||
colorThreshold.Load(data, "colorThreshold");
|
||||
matchThreshold.Load(data, "matchThreshold");
|
||||
obs_data_release(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -28,6 +28,7 @@ enum class VideoCondition {
|
||||
OBJECT,
|
||||
BRIGHTNESS,
|
||||
OCR,
|
||||
COLOR,
|
||||
};
|
||||
|
||||
class VideoInput {
|
||||
@@ -89,12 +90,16 @@ public:
|
||||
|
||||
bool Initialized() const { return initDone; }
|
||||
void SetPageMode(tesseract::PageSegMode);
|
||||
bool SetLanguageCode(const std::string &);
|
||||
std::string GetLanguageCode() const;
|
||||
tesseract::PageSegMode GetPageMode() const { return pageSegMode; }
|
||||
tesseract::TessBaseAPI *GetOCR() const { return ocr.get(); }
|
||||
|
||||
StringVariable text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
RegexConfig regex = RegexConfig::PartialMatchRegexConfig();
|
||||
QColor color = Qt::black;
|
||||
DoubleVariable colorThreshold = 0.3;
|
||||
StringVariable languageCode = "eng";
|
||||
|
||||
private:
|
||||
void Setup();
|
||||
@@ -104,7 +109,17 @@ private:
|
||||
bool initDone = false;
|
||||
};
|
||||
|
||||
class AreaParamters {
|
||||
class ColorParameters {
|
||||
public:
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
|
||||
QColor color = Qt::black;
|
||||
DoubleVariable colorThreshold = 0.1;
|
||||
DoubleVariable matchThreshold = 0.8;
|
||||
};
|
||||
|
||||
class AreaParameters {
|
||||
public:
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
|
||||
@@ -117,21 +117,21 @@ void PreviewDialog::closeEvent(QCloseEvent *event)
|
||||
Stop();
|
||||
}
|
||||
|
||||
void PreviewDialog::PatternMatchParamtersChanged(
|
||||
void PreviewDialog::PatternMatchParametersChanged(
|
||||
const PatternMatchParameters ¶ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_patternMatchParams = params;
|
||||
_patternImageData = createPatternData(_patternMatchParams.image);
|
||||
_patternImageData = CreatePatternData(_patternMatchParams.image);
|
||||
}
|
||||
|
||||
void PreviewDialog::ObjDetectParamtersChanged(const ObjDetectParameters ¶ms)
|
||||
void PreviewDialog::ObjDetectParametersChanged(const ObjDetectParameters ¶ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_objDetectParams = params;
|
||||
}
|
||||
|
||||
void PreviewDialog::OCRParamtersChanged(const OCRParameters ¶ms)
|
||||
void PreviewDialog::OCRParametersChanged(const OCRParameters ¶ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_ocrParams = params;
|
||||
@@ -143,7 +143,7 @@ void PreviewDialog::VideoSelectionChanged(const VideoInput &video)
|
||||
_video = video;
|
||||
}
|
||||
|
||||
void PreviewDialog::AreaParamtersChanged(const AreaParamters ¶ms)
|
||||
void PreviewDialog::AreaParametersChanged(const AreaParameters ¶ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_areaParams = params;
|
||||
@@ -187,7 +187,7 @@ void PreviewDialog::Start()
|
||||
return;
|
||||
}
|
||||
|
||||
PreviewImage *worker = new PreviewImage();
|
||||
auto worker = new PreviewImage(_mtx);
|
||||
worker->moveToThread(&_thread);
|
||||
connect(&_thread, &QThread::finished, worker, &QObject::deleteLater);
|
||||
connect(worker, &PreviewImage::ImageReady, this,
|
||||
@@ -244,12 +244,14 @@ static void markObjects(QImage &image, std::vector<cv::Rect> &objects)
|
||||
}
|
||||
}
|
||||
|
||||
PreviewImage::PreviewImage(std::mutex &mtx) : _mtx(mtx) {}
|
||||
|
||||
void PreviewImage::CreateImage(const VideoInput &video, PreviewType type,
|
||||
const PatternMatchParameters &patternMatchParams,
|
||||
const PatternImageData &patternImageData,
|
||||
ObjDetectParameters objDetectParams,
|
||||
OCRParameters ocrParams,
|
||||
const AreaParamters &areaParams,
|
||||
const AreaParameters &areaParams,
|
||||
VideoCondition condition)
|
||||
{
|
||||
auto source = obs_weak_source_get_source(video.GetVideo());
|
||||
@@ -271,6 +273,7 @@ void PreviewImage::CreateImage(const VideoInput &video, PreviewType type,
|
||||
}
|
||||
|
||||
if (type == PreviewType::SHOW_MATCH) {
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
if (areaParams.enable) {
|
||||
screenshot.image = screenshot.image.copy(
|
||||
areaParams.area.x, areaParams.area.y,
|
||||
@@ -295,13 +298,16 @@ void PreviewImage::MarkMatch(QImage &screenshot,
|
||||
{
|
||||
if (condition == VideoCondition::PATTERN) {
|
||||
cv::Mat result;
|
||||
matchPattern(screenshot, patternImageData,
|
||||
MatchPattern(screenshot, patternImageData,
|
||||
patternMatchParams.threshold, result,
|
||||
patternMatchParams.useAlphaAsMask,
|
||||
patternMatchParams.matchMode);
|
||||
if (countNonZero(result) == 0) {
|
||||
if (result.total() == 0 || countNonZero(result) == 0) {
|
||||
emit StatusUpdate(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternMatchFail"));
|
||||
} else if (result.cols == 1 && result.rows == 1) {
|
||||
emit StatusUpdate(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternMatchSuccessFullImage"));
|
||||
} else {
|
||||
emit StatusUpdate(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternMatchSuccess"));
|
||||
@@ -309,7 +315,7 @@ void PreviewImage::MarkMatch(QImage &screenshot,
|
||||
patternImageData.rgbaPattern);
|
||||
}
|
||||
} else if (condition == VideoCondition::OBJECT) {
|
||||
auto objects = matchObject(screenshot, objDetectParams.cascade,
|
||||
auto objects = MatchObject(screenshot, objDetectParams.cascade,
|
||||
objDetectParams.scaleFactor,
|
||||
objDetectParams.minNeighbors,
|
||||
objDetectParams.minSize.CV(),
|
||||
@@ -323,13 +329,11 @@ void PreviewImage::MarkMatch(QImage &screenshot,
|
||||
markObjects(screenshot, objects);
|
||||
}
|
||||
} else if (condition == VideoCondition::OCR) {
|
||||
auto text =
|
||||
runOCR(ocrParams.GetOCR(), screenshot, ocrParams.color);
|
||||
auto text = RunOCR(ocrParams.GetOCR(), screenshot,
|
||||
ocrParams.color, ocrParams.colorThreshold);
|
||||
QString status(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.ocrMatchSuccess"));
|
||||
emit StatusUpdate(status.arg(QString::fromStdString(text)));
|
||||
|
||||
// TODO: show preprocessed image
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user