Compare commits

..

1 Commits

Author SHA1 Message Date
WarmUpTill
3f2eb40c06 Improve "Studio mode" action 2025-10-28 19:21:29 +01:00
99 changed files with 1328 additions and 2959 deletions

View File

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

View File

@@ -146,8 +146,6 @@ target_sources(
lib/macro/macro-ref.hpp
lib/macro/macro-run-button.cpp
lib/macro/macro-run-button.hpp
lib/macro/macro-search.cpp
lib/macro/macro-search.hpp
lib/macro/macro-segment-copy-paste.cpp
lib/macro/macro-segment-copy-paste.hpp
lib/macro/macro-segment-list.cpp
@@ -184,8 +182,6 @@ target_sources(
lib/utils/canvas-helpers.hpp
lib/utils/condition-logic.cpp
lib/utils/condition-logic.hpp
lib/utils/crash-handler.cpp
lib/utils/crash-handler.hpp
lib/utils/curl-helper.cpp
lib/utils/curl-helper.hpp
lib/utils/cursor-shape-changer.cpp

View File

@@ -180,46 +180,24 @@ function(_check_dependencies)
set(url ${url}/${version}/${file})
endif()
set(MAX_DOWNLOAD_RETRIES 3)
set(RETRY_DELAY 60) # seconds
if(NOT EXISTS "${dependencies_dir}/${file}")
message(STATUS "Downloading ${url}")
file(
DOWNLOAD "${url}" "${dependencies_dir}/${file}"
STATUS download_status
EXPECTED_HASH SHA256=${hash})
set(download_success FALSE)
foreach(i RANGE 1 ${MAX_DOWNLOAD_RETRIES})
message(STATUS "Attempt ${i}/${MAX_DOWNLOAD_RETRIES} for ${url}")
file(
DOWNLOAD "${url}" "${dependencies_dir}/${file}"
STATUS download_status
EXPECTED_HASH SHA256=${hash})
list(GET download_status 0 error_code)
list(GET download_status 1 error_message)
if(error_code EQUAL 0)
message(STATUS "Downloading ${url} - success on attempt ${i}")
set(download_success TRUE)
break()
else()
message(WARNING "Download failed (attempt ${i}): ${error_message}")
file(REMOVE "${dependencies_dir}/${file}")
if(NOT i EQUAL MAX_DOWNLOAD_RETRIES)
message(STATUS "Retrying in ${RETRY_DELAY} seconds...")
execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${RETRY_DELAY})
endif()
endif()
endforeach()
if(NOT download_success)
list(GET download_status 0 error_code)
list(GET download_status 1 error_message)
if(error_code GREATER 0)
message(STATUS "Downloading ${url} - Failure")
message(
FATAL_ERROR
"Unable to download ${url} after ${MAX_DOWNLOAD_RETRIES} attempts")
"Unable to download ${url}, failed with error: ${error_message}")
file(REMOVE "${dependencies_dir}/${file}")
else()
message(STATUS "Downloading ${url} - done")
endif()
message(STATUS "Downloading ${url} - done")
endif()
if(NOT EXISTS "${dependencies_dir}/${destination}")

View File

@@ -495,7 +495,7 @@ AdvSceneSwitcher.action.file.type.write="Schreiben"
AdvSceneSwitcher.action.file.type.append="Anhängen"
AdvSceneSwitcher.action.file.entry="{{actions}} in {{filePath}}:"
AdvSceneSwitcher.action.studioMode="Studio-Modus"
AdvSceneSwitcher.action.studioMode.type.swap="Vorschau- und Programm-Szene tauschen"
AdvSceneSwitcher.action.studioMode.type.transitionWithSwap="Vorschau- und Programm-Szene tauschen"
AdvSceneSwitcher.action.studioMode.type.setScene="Vorschau-Szene einstellen auf"
AdvSceneSwitcher.action.studioMode.type.enable="Studio-Modus aktivieren"
AdvSceneSwitcher.action.studioMode.type.disable="Studio-Modus deaktivieren"

View File

@@ -43,9 +43,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="Display warni
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>Loading of the following plugin libraries was unsuccessful, which could result in some Advanced Scene Switcher functions not being available:%1Check the OBS logs for details.<br>This message can be disabled on the General tab.</body></html>"
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch="Always show macro search"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs="Always show all feature tabs"
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs.tooltip="Show tabs that are normally hidden until first used in a macro (e.g. Action Queues, Variables, Twitch, Websockets)."
AdvSceneSwitcher.generalTab.generalBehavior.disableMacroWidgetCache="Disable macro widget caching"
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
AdvSceneSwitcher.generalTab.priority="Priority"
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
@@ -76,7 +74,6 @@ AdvSceneSwitcher.generalTab.setTransitionBy="When changing transitions:"
AdvSceneSwitcher.generalTab.transitionOverride="Set transition overrides"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
AdvSceneSwitcher.generalTab.uiBehavior="UI settings"
# Variables Tab
AdvSceneSwitcher.variableTab.title="Variables"
@@ -94,12 +91,6 @@ AdvSceneSwitcher.variableTab.lastUsed.text.never="Never"
AdvSceneSwitcher.variableTab.lastChanged.header="Last changed"
AdvSceneSwitcher.variableTab.lastChanged.text.none="No change since launch"
AdvSceneSwitcher.variableTab.lastChanged.tooltip="Times changed: %1\n\nPrevious value: %2"
AdvSceneSwitcher.variableTab.search.placeholder="Search ..."
AdvSceneSwitcher.variableTab.search.all="All columns"
AdvSceneSwitcher.variableTab.search.name="Name column"
AdvSceneSwitcher.variableTab.search.value="Value column"
AdvSceneSwitcher.variableTab.addDock="Add dock"
AdvSceneSwitcher.variableTab.clear="Clear"
# Action Queue Tab
AdvSceneSwitcher.actionQueueTab.title="Action Queues"
@@ -157,11 +148,13 @@ AdvSceneSwitcher.mqttConnectionTab.removeMultipleConnectionsPopup.text="Are you
# Macro Tab
AdvSceneSwitcher.macroTab.title="Macro"
AdvSceneSwitcher.macroTab.macros="Macros"
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define macro conditions.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new condition."
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.edit="Edit macro"
AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
AdvSceneSwitcher.macroTab.edit.action="Action type:"
@@ -277,13 +270,6 @@ AdvSceneSwitcher.macroTab.tooltip.elseActionUpButton="Move selected action up in
AdvSceneSwitcher.macroTab.tooltip.elseActionDownButton="Move selected action down in the else section"
AdvSceneSwitcher.macroTab.tooltip.elseActionBottomButton="Move selected action to the bottom in the else section"
AdvSceneSwitcher.macroTab.tooltip.openMacroSettingsButton="Open macro settings"
AdvSceneSwitcher.macroTab.search.placeholder="Search ..."
AdvSceneSwitcher.macroTab.search.showSettings="Show search settings"
AdvSceneSwitcher.macroTab.search.name="Name"
AdvSceneSwitcher.macroTab.search.allSegments="Segment type"
AdvSceneSwitcher.macroTab.search.conditions="Condition type"
AdvSceneSwitcher.macroTab.search.actions="Action type"
AdvSceneSwitcher.macroTab.search.label="Segment label"
AdvSceneSwitcher.macroDock.pause="Pause"
AdvSceneSwitcher.macroDock.unpause="Unpause"
@@ -942,9 +928,6 @@ AdvSceneSwitcher.action.source.type.deinterlaceOrder="Set deinterlace field orde
AdvSceneSwitcher.action.source.type.openInteractionDialog="Open interaction dialog"
AdvSceneSwitcher.action.source.type.openFilterDialog="Open filter dialog"
AdvSceneSwitcher.action.source.type.openPropertiesDialog="Open properties dialog"
AdvSceneSwitcher.action.source.type.closeInteractionDialog="Close interaction dialog"
AdvSceneSwitcher.action.source.type.closeFilterDialog="Close filter dialog"
AdvSceneSwitcher.action.source.type.closePropertiesDialog="Close properties dialog"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}{{refresh}}"
AdvSceneSwitcher.action.source.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
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\""
@@ -966,7 +949,6 @@ AdvSceneSwitcher.action.source.inputMethod.individualTempvar="Set to macro prope
AdvSceneSwitcher.action.source.inputMethod.json="Set setting JSON string"
AdvSceneSwitcher.action.source.refresh="Refresh"
AdvSceneSwitcher.action.source.refresh.tooltip="Repopulate the source settings selection with the settings of the source which's name matches the variable value."
AdvSceneSwitcher.action.source.dialog.accept="Accept changes"
AdvSceneSwitcher.action.media="Media"
AdvSceneSwitcher.action.media.type.play="Play"
AdvSceneSwitcher.action.media.type.pause="Pause"
@@ -1002,16 +984,12 @@ AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action"
AdvSceneSwitcher.action.macro.type.nestedMacro="Nested macro"
AdvSceneSwitcher.action.macro.actionSelectionType.index="at index"
AdvSceneSwitcher.action.macro.actionSelectionType.label="with label"
AdvSceneSwitcher.action.macro.actionSelectionType.id="of action type"
AdvSceneSwitcher.action.macro.type.nestedMacro.conditionHelp="This section allows you to define macro conditions.\n\nClick the plus button below to add a new condition."
AdvSceneSwitcher.action.macro.type.nestedMacro.actionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nClick the plus button below to add a new action."
AdvSceneSwitcher.action.macro.type.nestedMacro.elseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nClick the plus button below to add a new action."
AdvSceneSwitcher.action.macro.layout.run="{{actions}}{{actionSections}}of{{macros}}"
AdvSceneSwitcher.action.macro.layout.run.condition="{{conditionBehaviors}}of{{conditionMacros}}"
AdvSceneSwitcher.action.macro.layout.actionState="{{actions}}{{actionSelectionType}}{{actionIndex}}{{label}}{{regex}}{{actionTypes}}in{{actionSections}}section of{{macros}}"
AdvSceneSwitcher.action.macro.layout.other="{{actions}}{{macros}}"
AdvSceneSwitcher.action.macro.entry.run="{{actions}}{{actionTypes}}of{{macros}}"
AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}of{{conditionMacros}}"
AdvSceneSwitcher.action.macro.entry.other="{{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:"
@@ -1077,7 +1055,17 @@ AdvSceneSwitcher.action.file.type.write="Write"
AdvSceneSwitcher.action.file.type.append="Append"
AdvSceneSwitcher.action.file.entry="{{actions}}to{{filePath}}:"
AdvSceneSwitcher.action.studioMode="Studio mode"
AdvSceneSwitcher.action.studioMode.type.swap="Swap preview and program scene"
AdvSceneSwitcher.action.studioMode.type.transition="Transition"
AdvSceneSwitcher.action.studioMode.type.transitionWithSwap="Swap preview and program scene"
AdvSceneSwitcher.action.studioMode.type.transitionSwap.enable="Enable preview/program scene swapping after transition"
AdvSceneSwitcher.action.studioMode.type.transitionSwap.disable="Disable preview/program scene swapping after transition"
AdvSceneSwitcher.action.studioMode.type.transitionSwap.toggle="Toggle preview/program scene swapping after transition"
AdvSceneSwitcher.action.studioMode.type.duplicateScene.enable="Enable scene duplication"
AdvSceneSwitcher.action.studioMode.type.duplicateScene.disable="Disable scene duplication"
AdvSceneSwitcher.action.studioMode.type.duplicateScene.toggle="Toggle scene duplication"
AdvSceneSwitcher.action.studioMode.type.duplicateSource.enable="Enable source duplication"
AdvSceneSwitcher.action.studioMode.type.duplicateSource.disable="Disable source duplication"
AdvSceneSwitcher.action.studioMode.type.duplicateSource.toggle="Toggle source duplication"
AdvSceneSwitcher.action.studioMode.type.setScene="Set preview scene to"
AdvSceneSwitcher.action.studioMode.type.enable="Enable studio mode"
AdvSceneSwitcher.action.studioMode.type.disable="Disable studio mode"
@@ -1163,7 +1151,6 @@ AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds"
AdvSceneSwitcher.action.variable="Variable"
AdvSceneSwitcher.action.variable.type.set="Set to value"
AdvSceneSwitcher.action.variable.type.append="Append"
AdvSceneSwitcher.action.variable.type.copy="Copy variable"
AdvSceneSwitcher.action.variable.type.appendVar="Append variable"
AdvSceneSwitcher.action.variable.type.increment="Increment"
AdvSceneSwitcher.action.variable.type.decrement="Decrement"
@@ -1212,8 +1199,8 @@ AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
@@ -1256,14 +1243,12 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
AdvSceneSwitcher.action.twitch.type.channel.getInfo="Get channel information"
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.action.twitch.layout.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.layout.channel.getInfo="Using account{{account}}{{actions}}of{{channel}}"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="Enter title"
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
@@ -1388,8 +1373,6 @@ AdvSceneSwitcher.hotkey.macro.segment.remove="Remove selected macro segment"
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
AdvSceneSwitcher.askForMacro="Select macro{{macroSelection}}"
AdvSceneSwitcher.crashDetected="OBS did not shut down cleanly (for example, due to a crash or freeze).\n\nThe Advanced Scene Switcher plugin would normally start automatically.\nWould you like to keep it stopped for now?"
AdvSceneSwitcher.close="Close"
AdvSceneSwitcher.browse="Browse"
@@ -1619,11 +1602,7 @@ AdvSceneSwitcher.twitchToken.moderator.chat.settings.manage="Manage channel's ch
AdvSceneSwitcher.twitchToken.user.whispers.manage="Manage user's whispers."
AdvSceneSwitcher.twitchToken.chat.read="View live stream chat messages."
AdvSceneSwitcher.twitchToken.chat.edit="Send live stream chat messages."
AdvSceneSwitcher.twitchToken.validateTimestamps="Validate timestamps of received Twitch event messages."
AdvSceneSwitcher.twitchToken.validateTimestamps.tooltip="Verifies that incoming Twitch messages have valid timestamps to prevent replayed or spoofed messages.\nDisabling this wont affect normal functionality, and you can try turning it off if you experience issues with the Twitch connection."
AdvSceneSwitcher.twitchToken.warnIfInvalid="Display warning if the token expired or is invalid."
AdvSceneSwitcher.twitchToken.warnIfInvalid.doNotShowAgain="Don't show again for this connection"
AdvSceneSwitcher.twitchToken.warnIfInvalid.message="The Twitch connection \"%1\" is invalid.\nOpen connection settings or ignore?"
AdvSceneSwitcher.twitchToken.validateTimestamps="Validate timestamps of received Twitch event messages. (Recommended)"
AdvSceneSwitcher.twitch.selection.channel.open="Open channel"
AdvSceneSwitcher.twitch.selection.channel.open.tooltip.details="Open channel in external application handling the HTTPS protocol."
@@ -2149,8 +2128,6 @@ AdvSceneSwitcher.tempVar.window.window="Window title"
AdvSceneSwitcher.tempVar.window.window.description="The window title of the matching window."
AdvSceneSwitcher.tempVar.window.windowClass="Window class"
AdvSceneSwitcher.tempVar.window.windowClass.description="The window class of the matching window."
AdvSceneSwitcher.tempVar.window.windowText="Window text"
AdvSceneSwitcher.tempVar.window.windowText.description="Text contained within the window (won't work on all window types)."
AdvSceneSwitcher.tempVar.timer.seconds="Seconds"
AdvSceneSwitcher.tempVar.timer.minutes="Minutes"
@@ -2192,8 +2169,6 @@ AdvSceneSwitcher.tempVar.run.process.none.description="When not waiting for the
AdvSceneSwitcher.tempVar.recording.durationSeconds="Recording duration"
AdvSceneSwitcher.tempVar.recording.durationSeconds.description="Recording duration in seconds.\nThis value does not change while the recording is paused and will be reset to zero if the recording is stopped."
AdvSceneSwitcher.tempVar.recording.lastSavePath="Last save path"
AdvSceneSwitcher.tempVar.recording.lastSavePath.description="The file path of the last saved recording."
AdvSceneSwitcher.tempVar.video.patternCount="Pattern count"
AdvSceneSwitcher.tempVar.video.patternCount.description="The number of times the given pattern has been found in a given video input frame."
@@ -2343,9 +2318,6 @@ AdvSceneSwitcher.tempVar.mqtt.message="Message"
AdvSceneSwitcher.tempVar.cursor.x="Cursor position (X)"
AdvSceneSwitcher.tempVar.cursor.y="Cursor position (Y)"
AdvSceneSwitcher.tempVar.replay.lastSavePath="Last save path"
AdvSceneSwitcher.tempVar.replay.lastSavePath.description="The file path of the last replay buffer saved."
AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectCanvas="--select canvas--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene"

View File

@@ -413,7 +413,7 @@ AdvSceneSwitcher.action.file.type.write="Escribir"
AdvSceneSwitcher.action.file.type.append="Agregar"
AdvSceneSwitcher.action.file.entry="{{actions}} a {{filePath}}:"
AdvSceneSwitcher.action.studioMode="Modo estudio"
AdvSceneSwitcher.action.studioMode.type.swap="Intercambiar vista previa y escena del programa"
AdvSceneSwitcher.action.studioMode.type.transitionWithSwap="Intercambiar vista previa y escena del programa"
AdvSceneSwitcher.action.studioMode.type.setScene="Establecer escena de vista previa en"
AdvSceneSwitcher.action.studioMode.type.enable="Activar modo estudio"
AdvSceneSwitcher.action.studioMode.type.disable="Deshabilitar el modo de estudio"

View File

@@ -613,7 +613,7 @@ AdvSceneSwitcher.action.file.type.write="Écrire"
AdvSceneSwitcher.action.file.type.append="Ajouter"
AdvSceneSwitcher.action.file.entry="{{actions}}vers{{filePath}}:"
AdvSceneSwitcher.action.studioMode="Mode studio"
AdvSceneSwitcher.action.studioMode.type.swap="Permuter la scène de prévisualisation et la scène du programme"
AdvSceneSwitcher.action.studioMode.type.transitionWithSwap="Permuter la scène de prévisualisation et la scène du programme"
AdvSceneSwitcher.action.studioMode.type.setScene="Définir la scène de prévisualisation sur"
AdvSceneSwitcher.action.studioMode.type.enable="Activer le mode studio"
AdvSceneSwitcher.action.studioMode.type.disable="Désactiver le mode studio"
@@ -701,8 +701,8 @@ AdvSceneSwitcher.action.variable.invalidSelection="Sélection invalide !"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !"
AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :"
AdvSceneSwitcher.action.variable.layout.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière:{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière :"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
AdvSceneSwitcher.action.projector="Projecteur"

View File

@@ -42,6 +42,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="プラグイ
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>次のプラグイン ライブラリの読み込みに失敗したため、一部の高機能シーンスイッチャーが利用できなくなる可能性があります:%1詳細については、OBS ログを確認してください。<br>このメッセージは、[全般] タブで無効にすることができます。 </body></html>"
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="プラグインが正しくインストールできておらずクラッシュする可能性があります!\nプラグインが正しくインストールされているか確認してください"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="旧来型マクロ設定のタブを非表示にする(廃止予定タブ)"
AdvSceneSwitcher.generalTab.generalBehavior.disableMacroWidgetCache="マクロウィジェットのキャッシングを無効にする"
AdvSceneSwitcher.generalTab.matchBehavior="マッチした場合の動作(廃止予定の設定項目)"
AdvSceneSwitcher.generalTab.priority="優先順位"
AdvSceneSwitcher.generalTab.priority.description="切り替え方法の優先順位(優先順位が高いものが上)"
@@ -1015,7 +1016,7 @@ AdvSceneSwitcher.action.file="ファイル"
; AdvSceneSwitcher.action.file.type.append="Append"
AdvSceneSwitcher.action.file.entry="{{actions}}から{{filePath}}へ:"
AdvSceneSwitcher.action.studioMode="スタジオモード"
AdvSceneSwitcher.action.studioMode.type.swap="プレビューと番組シーンの入れ替え"
AdvSceneSwitcher.action.studioMode.type.transitionWithSwap="プレビューと番組シーンの入れ替え"
AdvSceneSwitcher.action.studioMode.type.setScene="プレビューシーンを設定する"
AdvSceneSwitcher.action.studioMode.type.enable="スタジオモードを有効にする"
AdvSceneSwitcher.action.studioMode.type.disable="スタジオモードを無効にする"
@@ -1149,8 +1150,8 @@ AdvSceneSwitcher.action.variable.currentSegmentValue="現在値:"
; AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。"
AdvSceneSwitcher.action.variable.layout.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:"
; AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}"
@@ -1193,10 +1194,10 @@ AdvSceneSwitcher.action.twitch.type.user.getInfo="ユーザー情報を取得"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="チャンネルポイントリワード情報を取得"
AdvSceneSwitcher.action.twitch.reward.toggleControl="リワード名/変数選択コントロールの切り替え"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません"
; AdvSceneSwitcher.action.twitch.layout.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.layout.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="{{userInfoQueryType}}{{userLogin}}{{userId}}でアカウント{{account}}{{actions}}を使用"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="チャンネル{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}でアカウント{{account}}{{actions}}を使用"
; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="{{userInfoQueryType}}{{userLogin}}{{userId}}でアカウント{{account}}{{actions}}を使用"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="チャンネル{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}でアカウント{{account}}{{actions}}を使用"
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力"
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"

View File

@@ -861,9 +861,9 @@ AdvSceneSwitcher.action.macro.type.stop="Parar ações"
AdvSceneSwitcher.action.macro.type.disableAction="Desabilitar ação"
AdvSceneSwitcher.action.macro.type.enableAction="Habilitar ação"
AdvSceneSwitcher.action.macro.type.toggleAction="Alternar ação"
AdvSceneSwitcher.action.macro.layout.run="{{actions}}{{actionSections}}de{{macros}}"
AdvSceneSwitcher.action.macro.layout.run.condition="{{conditionBehaviors}}de{{conditionMacros}}"
AdvSceneSwitcher.action.macro.layout.other="{{actions}}{{macros}}"
AdvSceneSwitcher.action.macro.entry.run="{{actions}}{{actionTypes}}de{{macros}}"
AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}de{{conditionMacros}}"
AdvSceneSwitcher.action.macro.entry.other="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Estado do plugin"
AdvSceneSwitcher.action.pluginState.type.stop="Parar o plugin Advanced Scene Switcher"
AdvSceneSwitcher.action.pluginState.type.noMatch="Alterar o comportamento em caso de não correspondência:"
@@ -923,7 +923,7 @@ AdvSceneSwitcher.action.file.type.write="Escrever"
AdvSceneSwitcher.action.file.type.append="Anexar"
AdvSceneSwitcher.action.file.entry="{{actions}}para{{filePath}}:"
AdvSceneSwitcher.action.studioMode="Modo estúdio"
AdvSceneSwitcher.action.studioMode.type.swap="Trocar cena de visualização e programa"
AdvSceneSwitcher.action.studioMode.type.transitionWithSwap="Trocar cena de visualização e programa"
AdvSceneSwitcher.action.studioMode.type.setScene="Definir cena de visualização para"
AdvSceneSwitcher.action.studioMode.type.enable="Habilitar modo estúdio"
AdvSceneSwitcher.action.studioMode.type.disable="Desabilitar modo estúdio"
@@ -1030,8 +1030,8 @@ AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Obter valores de va
AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
@@ -1065,8 +1065,8 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.layout.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"

View File

@@ -920,9 +920,9 @@ AdvSceneSwitcher.action.macro.type.stop="停止操作"
AdvSceneSwitcher.action.macro.type.disableAction="停用操作"
AdvSceneSwitcher.action.macro.type.enableAction="启用操作"
AdvSceneSwitcher.action.macro.type.toggleAction="切换操作开关"
AdvSceneSwitcher.action.macro.layout.run="{{actions}}{{actionSections}}{{macros}}"
AdvSceneSwitcher.action.macro.layout.run.condition="{{conditionBehaviors}}{{conditionMacros}}"
AdvSceneSwitcher.action.macro.layout.other="{{actions}}{{macros}}"
AdvSceneSwitcher.action.macro.entry.run="{{actions}}{{actionTypes}}{{macros}}"
AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}{{conditionMacros}}"
AdvSceneSwitcher.action.macro.entry.other="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="插件状态"
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项时:"
@@ -984,7 +984,7 @@ AdvSceneSwitcher.action.file.type.write="覆盖写入"
AdvSceneSwitcher.action.file.type.append="追加写入"
AdvSceneSwitcher.action.file.entry="{{actions}} 到 {{filePath}}:"
AdvSceneSwitcher.action.studioMode="工作室模式"
AdvSceneSwitcher.action.studioMode.type.swap="切换预览和程序场景"
AdvSceneSwitcher.action.studioMode.type.transitionWithSwap="切换预览和程序场景"
AdvSceneSwitcher.action.studioMode.type.setScene="将预览场景设置为"
AdvSceneSwitcher.action.studioMode.type.enable="启用工作室模式"
AdvSceneSwitcher.action.studioMode.type.disable="停用工作室模式"
@@ -1100,8 +1100,8 @@ AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条
AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}于{{variables}}的{{direction}}起,内容为{{paddingCharSelection}}将其变量值长度增加至{{stringLength}}."
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}于{{variables}}的{{direction}}起,将变量长度缩减至{{stringLength}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="截取字符串开始位置:{{subStringStart}} 截取字符串长度:{{subStringSize}}{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:{{subStringRegex}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="截取字符串开始位置:{{subStringStart}} 截取字符串长度:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}使用自定义提示{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}用占位符填充{{inputPlaceholder}}"
@@ -1139,10 +1139,10 @@ AdvSceneSwitcher.action.twitch.type.user.getInfo="获取用户信息"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="获取频道积分奖励信息"
AdvSceneSwitcher.action.twitch.reward.toggleControl="切换奖励名称/变量选择控制"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="在未选择 Twitch 帐户的情况下无法选择类别!"
AdvSceneSwitcher.action.twitch.layout.default="{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.layout.chat="使用账户{{account}}{{actions}}{{channel}}"
AdvSceneSwitcher.action.twitch.layout.user.getInfo="使用账户{{account}}{{actions}}{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="使用账户{{account}}{{actions}} 频道{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.entry.default="{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="使用账户{{account}}{{actions}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="使用账户{{account}}{{actions}}{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="使用账户{{account}}{{actions}} 频道{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="输入标题"
AdvSceneSwitcher.action.twitch.marker.description="标记描述"
AdvSceneSwitcher.action.twitch.clip.hasDelay="在捕捉视频片段前稍加延迟"

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
#include "advanced-scene-switcher.hpp"
#include "backup.hpp"
#include "crash-handler.hpp"
#include "log-helper.hpp"
#include "macro-helpers.hpp"
#include "obs-module-helper.hpp"
@@ -208,15 +207,9 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->LoadSettings(data);
switcher->m.unlock();
if (switcher->stop) {
return;
if (!switcher->stop) {
switcher->Start();
}
if (ShouldSkipPluginStartOnUncleanShutdown()) {
return;
}
switcher->Start();
}
}
@@ -428,7 +421,7 @@ bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
static void ResetMacros()
{
for (auto &m : GetTopLevelMacros()) {
for (auto &m : GetMacros()) {
ResetMacroRunCount(m.get());
ResetMacroConditionTimers(m.get());
}

View File

@@ -65,6 +65,7 @@ public slots:
void on_showTrayNotifications_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state);
void on_disableComboBoxFilter_stateChanged(int state);
void on_disableMacroWidgetCache_stateChanged(int state);
void on_warnPluginLoadFailure_stateChanged(int state);
void on_hideLegacyTabs_stateChanged(int state);
void on_priorityUp_clicked();
@@ -101,10 +102,10 @@ public:
void HighlightMacroSettingsButton(bool enable = true);
public slots:
void on_macroAdd_triggered();
void on_macroRemove_triggered();
void on_macroUp_triggered() const;
void on_macroDown_triggered() const;
void on_macroAdd_clicked();
void on_macroRemove_clicked();
void on_macroUp_clicked() const;
void on_macroDown_clicked() const;
void on_macroName_editingFinished();
void on_runMacroInParallel_stateChanged(int value) const;
void on_runMacroOnChange_stateChanged(int value) const;

View File

@@ -3,7 +3,6 @@
#include "filter-combo-box.hpp"
#include "layout-helpers.hpp"
#include "macro.hpp"
#include "macro-search.hpp"
#include "macro-settings.hpp"
#include "path-helpers.hpp"
#include "selection-helpers.hpp"
@@ -186,6 +185,16 @@ void AdvSceneSwitcher::on_disableComboBoxFilter_stateChanged(int state)
FilterComboBox::SetFilterBehaviourEnabled(!state);
}
void AdvSceneSwitcher::on_disableMacroWidgetCache_stateChanged(int state)
{
if (loading) {
return;
}
switcher->disableMacroWidgetCache = state;
MacroSegmentList::SetCachingEnabled(!state);
}
void AdvSceneSwitcher::on_warnPluginLoadFailure_stateChanged(int state)
{
if (loading) {
@@ -450,7 +459,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
LoadHotkeys(obj);
LoadUISettings(obj);
RunAndClearPostLoadSteps();
RunPostLoadSteps();
// Reset on startup and scene collection change
ResetLastOpenedTab();
@@ -522,6 +531,8 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
obs_data_set_bool(obj, "disableHints", disableHints);
obs_data_set_bool(obj, "disableFilterComboboxFilter",
disableFilterComboboxFilter);
obs_data_set_bool(obj, "disableMacroWidgetCache",
disableMacroWidgetCache);
obs_data_set_bool(obj, "warnPluginLoadFailure", warnPluginLoadFailure);
obs_data_set_bool(obj, "hideLegacyTabs", hideLegacyTabs);
@@ -591,6 +602,8 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
disableHints = obs_data_get_bool(obj, "disableHints");
disableFilterComboboxFilter =
obs_data_get_bool(obj, "disableFilterComboboxFilter");
disableMacroWidgetCache =
obs_data_get_bool(obj, "disableMacroWidgetCache");
obs_data_set_default_bool(obj, "warnPluginLoadFailure", true);
warnPluginLoadFailure = obs_data_get_bool(obj, "warnPluginLoadFailure");
obs_data_set_default_bool(obj, "hideLegacyTabs", true);
@@ -914,6 +927,9 @@ void AdvSceneSwitcher::SetupGeneralTab()
switcher->disableFilterComboboxFilter);
FilterComboBox::SetFilterBehaviourEnabled(
!switcher->disableFilterComboboxFilter);
ui->disableMacroWidgetCache->setChecked(
switcher->disableMacroWidgetCache);
MacroSegmentList::SetCachingEnabled(!switcher->disableMacroWidgetCache);
ui->warnPluginLoadFailure->setChecked(switcher->warnPluginLoadFailure);
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);
@@ -997,19 +1013,6 @@ void AdvSceneSwitcher::SetupGeneralTab()
switcher->CheckAutoStart();
});
ui->alwaysShowMacroSearch->setChecked(
GetMacroSearchSettings().showAlways);
connect(ui->alwaysShowMacroSearch, &QCheckBox::stateChanged, this,
[this](int enabled) {
GetMacroSearchSettings().showAlways = enabled;
if (loading) {
return;
}
CheckMacroSearchVisibility();
});
// Set up status control
auto statusControl = new StatusControl(this, true);
ui->statusLayout->addWidget(statusControl->StatusPrefixLabel(), 1, 0);
@@ -1029,8 +1032,6 @@ void AdvSceneSwitcher::SetupGeneralTab()
setTabOrder(ui->importSettings, ui->cooldownTime);
setTabOrder(ui->cooldownTime, ui->noMatchDontSwitch);
SetupShowAllTabsCheckBox(ui->alwaysShowFeatureTabs, ui->tabWidget);
MinimizeSizeOfColumn(ui->statusLayout, 0);
setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
}

View File

@@ -28,7 +28,8 @@ static inline void populateActionSelection(QComboBox *list)
}
MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData)
std::shared_ptr<MacroAction> *entryData,
const std::string &id)
: MacroSegmentEdit(parent),
_actionSelection(new FilterComboBox()),
_enable(new SwitchButton()),
@@ -116,7 +117,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
*_entryData = MacroActionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx);
(*_entryData)->PostLoad();
RunAndClearPostLoadSteps();
RunPostLoadSteps();
}
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),

View File

@@ -13,8 +13,10 @@ class MacroActionEdit : public MacroSegmentEdit {
Q_OBJECT
public:
MacroActionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroAction> * = nullptr);
MacroActionEdit(
QWidget *parent = nullptr,
std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = MacroAction::GetDefaultID().data());
void SetupWidgets(bool basicSetup = false);
void SetEntryData(std::shared_ptr<MacroAction> *);

View File

@@ -2,7 +2,6 @@
#include "help-icon.hpp"
#include "layout-helpers.hpp"
#include "macro.hpp"
#include "macro-action-factory.hpp"
namespace advss {
@@ -13,74 +12,6 @@ bool MacroActionMacro::_registered = MacroActionFactory::Register(
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
"AdvSceneSwitcher.action.macro"});
void MacroActionMacro::AdjustActionState(Macro *macro) const
{
const auto &macroActions = _useElseSection ? macro->ElseActions()
: macro->Actions();
std::vector<std::shared_ptr<MacroAction>> actionsToModify;
switch (_actionSelectionType) {
case SelectionType::INDEX: {
const bool isValidAction =
(_useElseSection &&
IsValidElseActionIndex(macro, _actionIndex - 1)) ||
(!_useElseSection &&
IsValidActionIndex(macro, _actionIndex - 1));
if (isValidAction) {
actionsToModify.emplace_back(
macroActions.at(_actionIndex - 1));
}
break;
}
case SelectionType::LABEL:
for (const auto &action : macroActions) {
if (!action->GetUseCustomLabel()) {
continue;
}
const auto label = action->GetCustomLabel();
if (_regex.Enabled()) {
if (_regex.Matches(label, _label)) {
actionsToModify.emplace_back(action);
}
continue;
}
if (label == std::string(_label)) {
actionsToModify.emplace_back(action);
}
}
break;
case SelectionType::ID:
for (const auto &action : macroActions) {
if (action->GetId() == _actionId) {
actionsToModify.emplace_back(action);
}
}
break;
default:
break;
}
for (const auto &action : actionsToModify) {
switch (_action) {
case Action::DISABLE_ACTION:
action->SetEnabled(false);
break;
case Action::ENABLE_ACTION:
action->SetEnabled(true);
break;
case Action::TOGGLE_ACTION:
action->SetEnabled(!action->Enabled());
break;
default:
break;
}
}
}
bool MacroActionMacro::PerformAction()
{
if (_action == Action::NESTED_MACRO) {
@@ -93,46 +24,47 @@ bool MacroActionMacro::PerformAction()
return true;
}
const auto performActionForMacro = [this](Macro *macro) {
switch (_action) {
case Action::PAUSE:
macro->SetPaused();
break;
case Action::UNPAUSE:
macro->SetPaused(false);
break;
case Action::TOGGLE_PAUSE:
macro->SetPaused(!macro->Paused());
break;
case Action::RESET_COUNTER:
macro->ResetRunCount();
break;
case Action::RUN_ACTIONS:
RunActions(macro);
break;
case Action::STOP:
macro->Stop();
break;
case Action::DISABLE_ACTION:
case Action::ENABLE_ACTION:
case Action::TOGGLE_ACTION:
AdjustActionState(macro);
break;
default:
break;
switch (_action) {
case Action::PAUSE:
macro->SetPaused();
break;
case Action::UNPAUSE:
macro->SetPaused(false);
break;
case Action::TOGGLE_PAUSE:
macro->SetPaused(!macro->Paused());
break;
case Action::RESET_COUNTER:
macro->ResetRunCount();
break;
case Action::RUN_ACTIONS:
RunActions(macro.get());
break;
case Action::STOP:
macro->Stop();
break;
case Action::DISABLE_ACTION:
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
false)) {
macro->Actions().at(_actionIndex - 1)->SetEnabled(false);
}
};
if (!IsGroupMacro(macro.get())) {
performActionForMacro(macro.get());
return true;
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;
}
auto macros = GetGroupMacroEntries(macro.get());
for (const auto &macro : macros) {
performActionForMacro(macro.get());
}
return true;
}
@@ -183,14 +115,9 @@ void MacroActionMacro::LogAction() const
bool MacroActionMacro::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_macro.Save(obj);
obs_data_set_int(obj, "actionSelectionType",
static_cast<int>(_actionSelectionType));
_actionIndex.Save(obj, "actionIndex");
_label.Save(obj, "label");
obs_data_set_string(obj, "actionId", _actionId.c_str());
_regex.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_runOptions.Save(obj);
OBSDataAutoRelease nestedMacroData = obs_data_create();
_nestedMacro->Save(nestedMacroData);
@@ -202,15 +129,10 @@ bool MacroActionMacro::Save(obs_data_t *obj) const
bool MacroActionMacro::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_macro.Load(obj);
_actionIndex.Load(obj, "actionIndex");
_action = static_cast<MacroActionMacro::Action>(
obs_data_get_int(obj, "action"));
_macro.Load(obj);
_actionSelectionType = static_cast<SelectionType>(
obs_data_get_int(obj, "actionSelectionType"));
_actionIndex.Load(obj, "actionIndex");
_label.Load(obj, "label");
_actionId = obs_data_get_string(obj, "actionId");
_regex.Load(obj);
_runOptions.Load(obj);
if (obs_data_has_user_value(obj, "nestedMacro")) {
@@ -261,7 +183,6 @@ std::shared_ptr<MacroAction> MacroActionMacro::Copy() const
void MacroActionMacro::ResolveVariablesToFixedValues()
{
_actionIndex.ResolveVariables();
_label.ResolveVariables();
}
static void runActionsHelper(Macro *macro, bool runElseActions, bool setInputs,
@@ -345,7 +266,7 @@ static void populateConditionBehaviorSelection(QComboBox *list)
"AdvSceneSwitcher.action.macro.type.run.conditions.false"));
}
static void populateActionSectionSelection(QComboBox *list)
static void populateActionTypeSelection(QComboBox *list)
{
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.actionType.regular"));
@@ -353,47 +274,19 @@ static void populateActionSectionSelection(QComboBox *list)
"AdvSceneSwitcher.action.macro.type.run.actionType.else"));
}
static void populateActionTypes(QComboBox *list)
{
for (const auto &[id, info] : MacroActionFactory::GetActionTypes()) {
list->addItem(obs_module_text(info._name.c_str()),
QString::fromStdString(id));
}
}
static void populateActionSelectionTypes(QComboBox *list)
{
list->addItem(
obs_module_text(
"AdvSceneSwitcher.action.macro.actionSelectionType.index"),
static_cast<int>(MacroActionMacro::SelectionType::INDEX));
list->addItem(
obs_module_text(
"AdvSceneSwitcher.action.macro.actionSelectionType.label"),
static_cast<int>(MacroActionMacro::SelectionType::LABEL));
list->addItem(
obs_module_text(
"AdvSceneSwitcher.action.macro.actionSelectionType.id"),
static_cast<int>(MacroActionMacro::SelectionType::ID));
}
MacroActionMacroEdit::MacroActionMacroEdit(
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
: ResizableWidget(parent),
_actions(new QComboBox()),
_macros(new MacroSelection(parent)),
_actionSelectionType(new QComboBox(this)),
_actionIndex(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::ACTION)),
_label(new VariableLineEdit(this)),
_actionTypes(new FilterComboBox(this)),
_regex(new RegexConfigWidget(this)),
_actions(new QComboBox()),
_conditionMacros(new MacroSelection(parent)),
_conditionBehaviors(new QComboBox()),
_reevaluateConditionState(new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.macro.type.run."
"updateConditionMatchState"))),
_actionSections(new QComboBox(this)),
_actionTypes(new QComboBox()),
_skipWhenPaused(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.macro.type.run.skipWhenPaused"))),
_setInputs(new QCheckBox(obs_module_text(
@@ -412,36 +305,24 @@ MacroActionMacroEdit::MacroActionMacroEdit(
{
populateActionSelection(_actions);
populateConditionBehaviorSelection(_conditionBehaviors);
populateActionSectionSelection(_actionSections);
populateActionSelectionTypes(_actionSelectionType);
populateActionTypes(_actionTypes);
populateActionTypeSelection(_actionTypes);
_conditionMacros->HideSelectedMacro();
_conditionMacros->HideGroups();
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_actionSelectionType, SIGNAL(currentIndexChanged(int)),
this, SLOT(ActionSelectionTypeChanged(int)));
QWidget::connect(_actionIndex,
SIGNAL(SelectionChanged(const IntVariable &)), this,
SLOT(ActionIndexChanged(const IntVariable &)));
QWidget::connect(_label, SIGNAL(editingFinished()), this,
SLOT(LabelChanged()));
QWidget::connect(_actionTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionTypeChanged(int)));
QWidget::connect(_regex,
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
SLOT(RegexChanged(const RegexConfig &)));
QWidget::connect(_conditionMacros,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ConditionMacroChanged(const QString &)));
QWidget::connect(_conditionBehaviors, SIGNAL(currentIndexChanged(int)),
this, SLOT(ConditionBehaviorChanged(int)));
QWidget::connect(_actionSections, SIGNAL(currentIndexChanged(int)),
this, SLOT(ActionSectionChanged(int)));
QWidget::connect(_actionTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionTypeChanged(int)));
QWidget::connect(_skipWhenPaused, SIGNAL(stateChanged(int)), this,
SLOT(SkipWhenPausedChanged(int)));
QWidget::connect(_setInputs, SIGNAL(stateChanged(int)), this,
@@ -494,24 +375,17 @@ void MacroActionMacroEdit::UpdateEntryData()
if (!_entryData) {
return;
}
_actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->_action)));
_actionSelectionType->setCurrentIndex(_actionSelectionType->findData(
static_cast<int>(_entryData->_actionSelectionType)));
_actionIndex->SetValue(_entryData->_actionIndex);
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
_label->setText(_entryData->_label);
_actionTypes->setCurrentIndex(_actionTypes->findData(
QString::fromStdString(_entryData->_actionId)));
_regex->SetRegexConfig(_entryData->_regex);
_macros->SetCurrentMacro(_entryData->_macro);
_conditionMacros->SetCurrentMacro(_entryData->_runOptions.macro);
_conditionBehaviors->setCurrentIndex(
static_cast<int>(_entryData->_runOptions.logic));
_reevaluateConditionState->setChecked(
_entryData->_runOptions.reevaluateConditionState);
_actionSections->setCurrentIndex(
_actionTypes->setCurrentIndex(
_entryData->_runOptions.runElseActions ? 1 : 0);
_skipWhenPaused->setChecked(_entryData->_runOptions.skipWhenPaused);
_setInputs->setChecked(_entryData->_runOptions.setInputs);
@@ -550,40 +424,12 @@ void MacroActionMacroEdit::ActionChanged(int idx)
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionSelectionTypeChanged(int idx)
{
GUARD_LOADING_AND_LOCK();
_entryData->_actionSelectionType =
static_cast<MacroActionMacro::SelectionType>(
_actionSelectionType->itemData(idx).toInt());
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_actionIndex = value;
}
void MacroActionMacroEdit::LabelChanged()
{
GUARD_LOADING_AND_LOCK();
_entryData->_label = _label->text().toStdString();
}
void MacroActionMacroEdit::ActionTypeChanged(int idx)
{
GUARD_LOADING_AND_LOCK();
_entryData->_actionId =
_actionTypes->itemData(idx).toString().toStdString();
}
void MacroActionMacroEdit::RegexChanged(const RegexConfig &regex)
{
GUARD_LOADING_AND_LOCK();
_entryData->_regex = regex;
}
void MacroActionMacroEdit::ConditionMacroChanged(const QString &text)
{
GUARD_LOADING_AND_LOCK();
@@ -605,13 +451,10 @@ void MacroActionMacroEdit::ReevaluateConditionStateChanged(int value)
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionSectionChanged(int useElse)
void MacroActionMacroEdit::ActionTypeChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_runOptions.runElseActions = useElse;
_entryData->_useElseSection = useElse;
_actionIndex->SetType(useElse ? MacroSegmentSelection::Type::ELSE_ACTION
: MacroSegmentSelection::Type::ACTION);
_entryData->_runOptions.runElseActions = value;
}
void MacroActionMacroEdit::SkipWhenPausedChanged(int value)
@@ -640,11 +483,7 @@ void MacroActionMacroEdit::SetWidgetVisibility()
_entryLayout->removeWidget(_actions);
_entryLayout->removeWidget(_actionIndex);
_entryLayout->removeWidget(_macros);
_entryLayout->removeWidget(_actionSections);
_entryLayout->removeWidget(_label);
_entryLayout->removeWidget(_regex);
_entryLayout->removeWidget(_actionTypes);
_entryLayout->removeWidget(_actionSelectionType);
_conditionLayout->removeWidget(_conditionBehaviors);
_conditionLayout->removeWidget(_conditionMacros);
@@ -655,40 +494,19 @@ void MacroActionMacroEdit::SetWidgetVisibility()
{"{{actions}}", _actions},
{"{{actionIndex}}", _actionIndex},
{"{{macros}}", _macros},
{"{{actionSections}}", _actionSections},
{"{{actionTypes}}", _actionTypes},
{"{{conditionBehaviors}}", _conditionBehaviors},
{"{{conditionMacros}}", _conditionMacros},
{"{{actionSelectionType}}", _actionSelectionType},
{"{{label}}", _label},
{"{{regex}}", _regex},
{"{{actionTypes}}", _actionTypes},
};
const auto action = _entryData->_action;
const char *layoutText = "";
switch (action) {
case MacroActionMacro::Action::PAUSE:
case MacroActionMacro::Action::UNPAUSE:
case MacroActionMacro::Action::TOGGLE_PAUSE:
case MacroActionMacro::Action::RESET_COUNTER:
case MacroActionMacro::Action::STOP:
case MacroActionMacro::Action::NESTED_MACRO:
layoutText = "AdvSceneSwitcher.action.macro.layout.other";
break;
case MacroActionMacro::Action::RUN_ACTIONS:
layoutText = "AdvSceneSwitcher.action.macro.layout.run";
break;
case MacroActionMacro::Action::DISABLE_ACTION:
case MacroActionMacro::Action::ENABLE_ACTION:
case MacroActionMacro::Action::TOGGLE_ACTION:
layoutText = "AdvSceneSwitcher.action.macro.layout.actionState";
break;
default:
break;
}
PlaceWidgets(obs_module_text(layoutText), _entryLayout, placeholders);
PlaceWidgets(
obs_module_text(
_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS
? "AdvSceneSwitcher.action.macro.entry.run"
: "AdvSceneSwitcher.action.macro.entry.other"),
_entryLayout, placeholders);
if (_entryData->_runOptions.logic ==
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS) {
@@ -697,63 +515,56 @@ void MacroActionMacroEdit::SetWidgetVisibility()
} else {
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.macro.layout.run.condition"),
"AdvSceneSwitcher.action.macro.entry.run.condition"),
_conditionLayout, placeholders);
}
if (action == MacroActionMacro::Action::RUN_ACTIONS ||
action == MacroActionMacro::Action::STOP) {
if (_entryData->_action == MacroActionMacro::Action::RUN_ACTIONS ||
_entryData->_action == MacroActionMacro::Action::STOP) {
_macros->HideSelectedMacro();
} else {
_macros->ShowAllMacros();
}
const auto actionSelectionType = _entryData->_actionSelectionType;
const bool isModifyingActionState =
action == MacroActionMacro::Action::DISABLE_ACTION ||
action == MacroActionMacro::Action::ENABLE_ACTION ||
action == MacroActionMacro::Action::TOGGLE_ACTION;
_actionSelectionType->setVisible(isModifyingActionState);
_actionIndex->setVisible(
isModifyingActionState &&
actionSelectionType == MacroActionMacro::SelectionType::INDEX);
_label->setVisible(isModifyingActionState &&
actionSelectionType ==
MacroActionMacro::SelectionType::LABEL);
_regex->setVisible(isModifyingActionState &&
actionSelectionType ==
MacroActionMacro::SelectionType::LABEL);
_actionTypes->setVisible(isModifyingActionState &&
actionSelectionType ==
MacroActionMacro::SelectionType::ID);
_entryData->_action ==
MacroActionMacro::Action::DISABLE_ACTION ||
_entryData->_action ==
MacroActionMacro::Action::ENABLE_ACTION ||
_entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION;
_actionIndex->setVisible(isModifyingActionState);
SetLayoutVisible(_conditionLayout,
action == MacroActionMacro::Action::RUN_ACTIONS);
_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS);
const bool needsAdditionalConditionWidgets =
action == MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_action == MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_runOptions.logic !=
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS;
_conditionMacros->setVisible(needsAdditionalConditionWidgets);
SetLayoutVisible(_reevaluateConditionStateLayout,
needsAdditionalConditionWidgets);
SetLayoutVisible(_setInputsLayout,
action == MacroActionMacro::Action::RUN_ACTIONS);
_inputs->setVisible(action == MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS);
_inputs->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_runOptions.setInputs);
HighlightMacroSettingsButton(
action == MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_action == MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_runOptions.setInputs &&
!_inputs->HasInputsToSet());
_actionSections->setVisible(
action == MacroActionMacro::Action::RUN_ACTIONS ||
isModifyingActionState);
_skipWhenPaused->setVisible(action ==
_actionTypes->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS);
_skipWhenPaused->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS);
_nestedMacro->setVisible(action ==
_nestedMacro->setVisible(_entryData->_action ==
MacroActionMacro::Action::NESTED_MACRO);
_macros->setVisible(action != MacroActionMacro::Action::NESTED_MACRO);
SetResizingEnabled(action == MacroActionMacro::Action::NESTED_MACRO);
_macros->setVisible(_entryData->_action !=
MacroActionMacro::Action::NESTED_MACRO);
SetResizingEnabled(_entryData->_action ==
MacroActionMacro::Action::NESTED_MACRO);
if (_nestedMacro->IsEmpty()) {
_nestedMacro->ShowAllMacroSections();

View File

@@ -5,9 +5,7 @@
#include "macro-input.hpp"
#include "macro-selection.hpp"
#include "macro-segment-selection.hpp"
#include "regex-config.hpp"
#include "resizable-widget.hpp"
#include "variable-line-edit.hpp"
#include <QCheckBox>
#include <QHBoxLayout>
@@ -57,23 +55,14 @@ public:
TOGGLE_PAUSE,
NESTED_MACRO,
};
enum class SelectionType { INDEX, LABEL, ID };
Action _action = Action::NESTED_MACRO;
SelectionType _actionSelectionType = SelectionType::INDEX;
bool _useElseSection = false;
IntVariable _actionIndex = 1;
StringVariable _label = "Custom label";
std::string _actionId;
RegexConfig _regex;
RunOptions _runOptions = {};
std::shared_ptr<Macro> _nestedMacro = std::make_shared<Macro>();
int _customWidgetHeight = 0;
private:
void RunActions(Macro *actionMacro) const;
void AdjustActionState(Macro *) const;
static bool _registered;
static const std::string id;
@@ -93,15 +82,11 @@ public:
private slots:
void MacroChanged(const QString &text);
void ActionChanged(int value);
void ActionSelectionTypeChanged(int value);
void ActionIndexChanged(const IntVariable &value);
void LabelChanged();
void ActionTypeChanged(int value);
void RegexChanged(const RegexConfig &);
void ConditionMacroChanged(const QString &text);
void ConditionBehaviorChanged(int value);
void ReevaluateConditionStateChanged(int value);
void ActionSectionChanged(int value);
void ActionTypeChanged(int value);
void SkipWhenPausedChanged(int value);
void SetInputsChanged(int value);
void InputsChanged(const StringList &);
@@ -113,17 +98,13 @@ private:
void SetWidgetVisibility();
void SetupMacroInput(Macro *) const;
QComboBox *_actions;
MacroSelection *_macros;
QComboBox *_actionSelectionType;
MacroSegmentSelection *_actionIndex;
VariableLineEdit *_label;
FilterComboBox *_actionTypes;
RegexConfigWidget *_regex;
QComboBox *_actions;
MacroSelection *_conditionMacros;
QComboBox *_conditionBehaviors;
QCheckBox *_reevaluateConditionState;
QComboBox *_actionSections;
QComboBox *_actionTypes;
QCheckBox *_skipWhenPaused;
QCheckBox *_setInputs;
MacroInputEdit *_inputs;

View File

@@ -286,14 +286,6 @@ bool MacroActionVariable::PerformAction()
case Action::APPEND:
apppend(*var, _strValue);
break;
case Action::COPY_VAR: {
auto var2 = _variable2.lock();
if (!var2) {
return true;
}
var->SetValue(var2->Value());
break;
}
case Action::APPEND_VAR: {
auto var2 = _variable2.lock();
if (!var2) {
@@ -678,8 +670,6 @@ static inline void populateActionSelection(QComboBox *list)
actions = {
{MacroActionVariable::Action::SET_VALUE,
"AdvSceneSwitcher.action.variable.type.set"},
{MacroActionVariable::Action::COPY_VAR,
"AdvSceneSwitcher.action.variable.type.copy"},
{MacroActionVariable::Action::APPEND,
"AdvSceneSwitcher.action.variable.type.append"},
{MacroActionVariable::Action::PAD,
@@ -801,10 +791,11 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_segmentValueStatus(new QLabel()),
_segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)),
_substringLayout(new QVBoxLayout()),
_subStringControlsLayout(new QHBoxLayout()),
_subStringIndexEntryLayout(new QHBoxLayout()),
_subStringRegexEntryLayout(new QHBoxLayout()),
_subStringStart(new VariableSpinBox(this)),
_subStringSize(new VariableSpinBox(this)),
_subStringRegex(new RegexConfigWidget(parent)),
_substringRegex(new RegexConfigWidget(parent)),
_regexPattern(new ResizingPlainTextEdit(this, 10, 1, 1)),
_regexMatchIdx(new VariableSpinBox(this)),
_findReplaceLayout(new QHBoxLayout()),
@@ -900,7 +891,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_subStringSize,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(SubStringSizeChanged(const NumberVariable<int> &)));
QWidget::connect(_subStringRegex,
QWidget::connect(_substringRegex,
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
SLOT(SubStringRegexChanged(const RegexConfig &)));
QWidget::connect(_regexPattern, SIGNAL(textChanged()), this,
@@ -974,6 +965,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
{"{{strValue}}", _strValue},
{"{{numValue}}", _numValue},
{"{{segmentIndex}}", _segmentIdx},
{"{{subStringStart}}", _subStringStart},
{"{{subStringSize}}", _subStringSize},
{"{{regexMatchIdx}}", _regexMatchIdx},
{"{{findRegex}}", _findRegex},
{"{{findStr}}", _findStr},
{"{{replaceStr}}", _replaceStr},
@@ -1005,7 +999,12 @@ MacroActionVariableEdit::MacroActionVariableEdit(
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.variable.layout.substringIndex"),
_subStringControlsLayout, widgetPlaceholders);
_subStringIndexEntryLayout, widgetPlaceholders);
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.action.variable.layout.substringRegex"),
_subStringRegexEntryLayout, widgetPlaceholders);
PlaceWidgets(
obs_module_text(
@@ -1030,10 +1029,11 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_randomLayout->addWidget(_generateInteger);
auto regexConfigLayout = new QHBoxLayout;
regexConfigLayout->addWidget(_subStringRegex);
regexConfigLayout->addWidget(_substringRegex);
regexConfigLayout->addStretch();
_substringLayout->addLayout(_subStringControlsLayout);
_substringLayout->addLayout(_subStringIndexEntryLayout);
_substringLayout->addLayout(_subStringRegexEntryLayout);
_substringLayout->addWidget(_regexPattern);
_substringLayout->addLayout(regexConfigLayout);
@@ -1080,7 +1080,7 @@ void MacroActionVariableEdit::UpdateEntryData()
: MacroSegmentSelection::Type::ACTION);
_subStringStart->SetValue(_entryData->_subStringStart);
_subStringSize->SetValue(_entryData->_subStringSize);
_subStringRegex->SetRegexConfig(_entryData->_subStringRegex);
_substringRegex->SetRegexConfig(_entryData->_subStringRegex);
_findRegex->SetRegexConfig(_entryData->_findRegex);
_regexPattern->setPlainText(
QString::fromStdString(_entryData->_regexPattern));
@@ -1485,20 +1485,8 @@ void MacroActionVariableEdit::SetWidgetVisibility()
{"{{jsonQuery}}", _jsonQuery},
{"{{jsonQueryHelp}}", _jsonQueryHelp},
{"{{jsonIndex}}", _jsonIndex},
{"{{subStringRegex}}", _subStringRegex},
{"{{subStringStart}}", _subStringStart},
{"{{subStringSize}}", _subStringSize},
{"{{regexMatchIdx}}", _regexMatchIdx},
};
for (const auto &[_, widget] : widgetPlaceholders) {
_entryLayout->removeWidget(widget);
_subStringControlsLayout->removeWidget(widget);
}
ClearLayout(_entryLayout);
ClearLayout(_subStringControlsLayout);
const char *layoutString = "";
if (_entryData->_action == MacroActionVariable::Action::PAD) {
layoutString = obs_module_text(
@@ -1512,6 +1500,11 @@ void MacroActionVariableEdit::SetWidgetVisibility()
"AdvSceneSwitcher.action.variable.layout.other");
}
for (const auto &[_, widget] : widgetPlaceholders) {
_entryLayout->removeWidget(widget);
}
ClearLayout(_entryLayout);
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
if (_entryData->_action == MacroActionVariable::Action::SET_VALUE ||
@@ -1528,7 +1521,6 @@ void MacroActionVariableEdit::SetWidgetVisibility()
}
_variables2->setVisible(
_entryData->_action == MacroActionVariable::Action::COPY_VAR ||
_entryData->_action ==
MacroActionVariable::Action::APPEND_VAR ||
_entryData->_action ==
@@ -1558,25 +1550,15 @@ void MacroActionVariableEdit::SetWidgetVisibility()
MacroActionVariable::Action::SET_ACTION_VALUE ||
_entryData->_action ==
MacroActionVariable::Action::SET_CONDITION_VALUE);
bool showRegex = _entryData->_subStringRegex.Enabled();
layoutString =
showRegex
? "AdvSceneSwitcher.action.variable.layout.substringRegex"
: "AdvSceneSwitcher.action.variable.layout.substringIndex";
PlaceWidgets(obs_module_text(layoutString), _subStringControlsLayout,
widgetPlaceholders);
_subStringStart->setVisible(!showRegex);
_subStringSize->setVisible(!showRegex);
_regexMatchIdx->setVisible(showRegex);
SetLayoutVisible(_substringLayout,
_entryData->_action ==
MacroActionVariable::Action::SUBSTRING);
_regexPattern->setVisible(
showRegex &&
_entryData->_action == MacroActionVariable::Action::SUBSTRING);
if (_entryData->_action == MacroActionVariable::Action::SUBSTRING) {
bool showRegex = _entryData->_subStringRegex.Enabled();
SetLayoutVisible(_subStringIndexEntryLayout, !showRegex);
SetLayoutVisible(_subStringRegexEntryLayout, showRegex);
_regexPattern->setVisible(showRegex);
}
SetLayoutVisible(_findReplaceLayout,
_entryData->_action ==
MacroActionVariable::Action::FIND_AND_REPLACE);

View File

@@ -55,7 +55,6 @@ public:
RANDOM_NUMBER,
QUERY_JSON,
ARRAY_JSON,
COPY_VAR,
};
Action _action = Action::SET_VALUE;
@@ -194,10 +193,11 @@ private:
QLabel *_segmentValueStatus;
ResizingPlainTextEdit *_segmentValue;
QVBoxLayout *_substringLayout;
QHBoxLayout *_subStringControlsLayout;
QHBoxLayout *_subStringIndexEntryLayout;
QHBoxLayout *_subStringRegexEntryLayout;
VariableSpinBox *_subStringStart;
VariableSpinBox *_subStringSize;
RegexConfigWidget *_subStringRegex;
RegexConfigWidget *_substringRegex;
ResizingPlainTextEdit *_regexPattern;
VariableSpinBox *_regexMatchIdx;
QHBoxLayout *_findReplaceLayout;

View File

@@ -100,7 +100,7 @@ void DurationModifierEdit::Collapse(bool collapse)
MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
bool isRootCondition)
const std::string &id, bool isRootCondition)
: MacroSegmentEdit(parent),
_logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()),
@@ -239,7 +239,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
(*_entryData)->SetIndex(idx);
(*_entryData)->SetLogicType(logic);
(*_entryData)->PostLoad();
RunAndClearPostLoadSteps();
RunPostLoadSteps();
}
auto widget =
MacroConditionFactory::CreateWidget(id, this, *_entryData);

View File

@@ -34,9 +34,11 @@ class MacroConditionEdit : public MacroSegmentEdit {
Q_OBJECT
public:
MacroConditionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroCondition> * = nullptr,
bool root = true);
MacroConditionEdit(
QWidget *parent = nullptr,
std::shared_ptr<MacroCondition> * = nullptr,
const std::string &id = MacroCondition::GetDefaultID().data(),
bool root = true);
bool IsRootNode() const;
void SetRootNode(bool);
void SetupWidgets(bool basicSetup = false);

View File

@@ -102,7 +102,7 @@ bool MacroConditionMacro::CheckActionStateCondition()
if (!macro) {
return false;
}
if (!IsValidActionIndex(macro.get(), _actionIndex - 1)) {
if (!IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1, false)) {
return false;
}
if (_type == Type::ACTION_DISABLED) {
@@ -315,7 +315,6 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
_actionIndex(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::ACTION))
{
_macros->HideGroups();
_count->setMaximum(10000000);
populateTypeSelection(_types);
populateCounterConditionSelection(_counterConditions);

View File

@@ -110,7 +110,7 @@ void MacroDock::RunClicked()
return;
}
auto ret = macro->PerformActions(true, true, true);
auto ret = macro->PerformActions(true);
if (!ret) {
QString err =
obs_module_text("AdvSceneSwitcher.macroTab.runFail");

View File

@@ -418,7 +418,8 @@ void MacroEdit::PopulateMacroActions(Macro &m, uint32_t afterIdx)
ui->actionsList->SetVisibilityCheckEnable(false);
for (; afterIdx < actions.size(); afterIdx++) {
auto newEntry = new MacroActionEdit(this, &actions[afterIdx]);
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
actions[afterIdx]->GetId());
ui->actionsList->Add(newEntry);
}
@@ -443,7 +444,8 @@ void MacroEdit::PopulateMacroElseActions(Macro &m, uint32_t afterIdx)
ui->elseActionsList->SetVisibilityCheckEnable(false);
for (; afterIdx < actions.size(); afterIdx++) {
auto newEntry = new MacroActionEdit(this, &actions[afterIdx]);
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
actions[afterIdx]->GetId());
ui->elseActionsList->Add(newEntry);
}
@@ -470,7 +472,8 @@ void MacroEdit::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
for (; afterIdx < conditions.size(); afterIdx++) {
auto newEntry = new MacroConditionEdit(
this, &conditions[afterIdx], root);
this, &conditions[afterIdx],
conditions[afterIdx]->GetId(), root);
ui->conditionsList->Add(newEntry);
root = false;
}
@@ -1077,10 +1080,11 @@ void MacroEdit::AddMacroAction(Macro *macro, int idx, const std::string &id,
macro->Actions().at(idx)->Load(data);
}
macro->Actions().at(idx)->PostLoad();
RunAndClearPostLoadSteps();
RunPostLoadSteps();
macro->UpdateActionIndices();
ui->actionsList->Insert(
idx, new MacroActionEdit(this, &macro->Actions()[idx]));
idx,
new MacroActionEdit(this, &macro->Actions()[idx], id));
SetActionData(*macro);
}
HighlightAction(idx);
@@ -1379,11 +1383,11 @@ void MacroEdit::AddMacroElseAction(Macro *macro, int idx, const std::string &id,
macro->ElseActions().at(idx)->Load(data);
}
macro->ElseActions().at(idx)->PostLoad();
RunAndClearPostLoadSteps();
RunPostLoadSteps();
macro->UpdateElseActionIndices();
ui->elseActionsList->Insert(
idx,
new MacroActionEdit(this, &macro->ElseActions()[idx]));
idx, new MacroActionEdit(
this, &macro->ElseActions()[idx], id));
SetElseActionData(*macro);
}
HighlightElseAction(idx);
@@ -1579,13 +1583,13 @@ void MacroEdit::AddMacroCondition(Macro *macro, int idx, const std::string &id,
macro->Conditions().at(idx)->Load(data);
}
macro->Conditions().at(idx)->PostLoad();
RunAndClearPostLoadSteps();
RunPostLoadSteps();
(*cond)->SetLogicType(logic);
macro->UpdateConditionIndices();
ui->conditionsList->Insert(
idx,
new MacroConditionEdit(this, &macro->Conditions()[idx],
idx == 0));
id, idx == 0));
SetConditionData(*macro);
}
HighlightCondition(idx);

View File

@@ -1,6 +1,5 @@
#include "macro-helpers.hpp"
#include "macro.hpp"
#include "macro-action-macro.hpp"
#include "plugin-state-helpers.hpp"
namespace advss {
@@ -9,87 +8,6 @@ static std::atomic_bool abortMacroWait = {false};
static std::atomic_bool macroSceneSwitched = {false};
static std::atomic_int shutdownConditionCount = {0};
static void appendNestedMacros(std::deque<std::shared_ptr<Macro>> &macros,
Macro *macro)
{
if (!macro) {
return;
}
const auto iterate = [&macros](const std::deque<
std::shared_ptr<MacroAction>> &actions) {
for (const auto &action : actions) {
const auto nestedMacroAction =
dynamic_cast<MacroActionMacro *>(action.get());
if (!nestedMacroAction) {
continue;
}
macros.push_back(nestedMacroAction->_nestedMacro);
appendNestedMacros(
macros, nestedMacroAction->_nestedMacro.get());
}
};
iterate(macro->Actions());
iterate(macro->ElseActions());
}
std::deque<std::shared_ptr<Macro>> &GetTopLevelMacros()
{
static std::deque<std::shared_ptr<Macro>> macros;
return macros;
}
std::deque<std::shared_ptr<Macro>> &GetTemporaryMacros()
{
static std::deque<std::shared_ptr<Macro>> tempMacros;
return tempMacros;
}
std::deque<std::shared_ptr<Macro>> GetAllMacros()
{
auto macros = GetTopLevelMacros();
for (const auto &topLevelMacro : macros) {
appendNestedMacros(macros, topLevelMacro.get());
}
const auto &tempMacros = GetTemporaryMacros();
macros.insert(macros.end(), tempMacros.begin(), tempMacros.end());
for (const auto &tempMacro : tempMacros) {
appendNestedMacros(macros, tempMacro.get());
}
return macros;
}
Macro *GetMacroByName(const char *name)
{
for (const auto &m : GetTopLevelMacros()) {
if (m->Name() == name) {
return m.get();
}
}
return nullptr;
}
Macro *GetMacroByQString(const QString &name)
{
return GetMacroByName(name.toUtf8().constData());
}
std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
{
for (const auto &m : GetTopLevelMacros()) {
if (m->Name() == name) {
return m;
}
}
return {};
}
std::optional<std::deque<std::shared_ptr<MacroAction>>>
GetMacroActions(Macro *macro)
{
@@ -99,15 +17,6 @@ GetMacroActions(Macro *macro)
return macro->Actions();
}
std::optional<std::deque<std::shared_ptr<MacroAction>>>
GetMacroElseActions(Macro *macro)
{
if (!macro) {
return {};
}
return macro->ElseActions();
}
std::optional<std::deque<std::shared_ptr<MacroCondition>>>
GetMacroConditions(Macro *macro)
{
@@ -117,32 +26,9 @@ GetMacroConditions(Macro *macro)
return macro->Conditions();
}
bool IsGroupMacro(Macro *macro)
std::string_view GetSceneSwitchActionId()
{
return macro && macro->IsGroup();
}
std::vector<std::shared_ptr<Macro>> GetGroupMacroEntries(Macro *macro)
{
if (!macro || !macro->IsGroup()) {
return {};
}
std::vector<std::shared_ptr<Macro>> entries;
entries.reserve(macro->GroupSize());
const auto &macros = GetTopLevelMacros();
for (auto it = macros.begin(); it < macros.end(); it++) {
if ((*it)->Name() != macro->Name()) {
continue;
}
for (uint32_t i = 1; i <= macro->GroupSize(); i++) {
entries.emplace_back(*std::next(it, i));
}
break;
}
return entries;
return MacroAction::GetDefaultID();
}
std::condition_variable &GetMacroWaitCV()
@@ -258,42 +144,20 @@ void ResetMacroRunCount(Macro *macro)
macro->ResetRunCount();
}
bool IsValidActionIndex(const Macro *m, const int idx)
bool IsValidMacroSegmentIndex(const Macro *m, const int idx, bool isCondition)
{
if (!m || idx < 0) {
return false;
}
if (idx >= (int)m->Actions().size()) {
return false;
if (isCondition) {
if (idx >= (int)m->Conditions().size()) {
return false;
}
} else {
if (idx >= (int)m->Actions().size()) {
return false;
}
}
return true;
}
bool IsValidElseActionIndex(const Macro *m, const int idx)
{
if (!m || idx < 0) {
return false;
}
if (idx >= (int)m->ElseActions().size()) {
return false;
}
return true;
}
bool IsValidConditionIndex(const Macro *m, const int idx)
{
if (!m || idx < 0) {
return false;
}
if (idx >= (int)m->Conditions().size()) {
return false;
}
return true;
}

View File

@@ -1,8 +1,6 @@
#pragma once
#include "export-symbol-helper.hpp"
#include <QString>
#include <atomic>
#include <chrono>
#include <condition_variable>
@@ -20,25 +18,16 @@ class Macro;
class MacroAction;
class MacroCondition;
static const int macro_func = 10;
EXPORT std::deque<std::shared_ptr<Macro>> &GetTopLevelMacros();
std::deque<std::shared_ptr<Macro>> &GetTemporaryMacros();
EXPORT std::deque<std::shared_ptr<Macro>> GetAllMacros();
Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name);
std::weak_ptr<Macro> GetWeakMacroByName(const char *name);
EXPORT std::deque<std::shared_ptr<Macro>> &GetMacros();
EXPORT std::optional<std::deque<std::shared_ptr<MacroAction>>>
GetMacroActions(Macro *);
EXPORT std::optional<std::deque<std::shared_ptr<MacroAction>>>
GetMacroElseActions(Macro *);
EXPORT std::optional<std::deque<std::shared_ptr<MacroCondition>>>
GetMacroConditions(Macro *);
EXPORT bool IsGroupMacro(Macro *);
EXPORT std::vector<std::shared_ptr<Macro>> GetGroupMacroEntries(Macro *);
std::string_view GetSceneSwitchActionId();
constexpr auto macro_func = 10;
EXPORT std::condition_variable &GetMacroWaitCV();
EXPORT std::condition_variable &GetMacroTransitionCV();
@@ -79,8 +68,6 @@ EXPORT void InvalidateMacroTempVarValues();
EXPORT void ResetMacroConditionTimers(Macro *);
EXPORT void ResetMacroRunCount(Macro *);
bool IsValidActionIndex(const Macro *m, const int idx);
bool IsValidElseActionIndex(const Macro *m, const int idx);
bool IsValidConditionIndex(const Macro *m, const int idx);
bool IsValidMacroSegmentIndex(const Macro *m, const int idx, bool isCondition);
} // namespace advss

View File

@@ -1,323 +0,0 @@
#include "macro-search.hpp"
#include "layout-helpers.hpp"
#include "macro.hpp"
#include "macro-action-factory.hpp"
#include "macro-condition-factory.hpp"
#include "macro-helpers.hpp"
#include "macro-signals.hpp"
#include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp"
#include <obs.hpp>
#include <QComboBox>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
namespace advss {
static bool searchEnabled = false;
static bool setup();
static bool setupDone = setup();
static QLayout *searchLayout = nullptr;
static QComboBox *searchType = nullptr;
static RegexConfigWidget *searchRegex = nullptr;
static QPushButton *showSettings = nullptr;
static std::function<void()> refreshFilter;
static void save(obs_data_t *data)
{
GetMacroSearchSettings().Save(data, "macroSearchSettings");
}
static void load(obs_data_t *data)
{
GetMacroSearchSettings().Load(data, "macroSearchSettings");
}
static bool setup()
{
AddSaveStep(save);
AddLoadStep(load);
return true;
}
void MacroSearchSettings::Save(obs_data_t *data, const char *name)
{
OBSDataAutoRelease settings = obs_data_create();
obs_data_set_bool(settings, "showAlways", showAlways);
obs_data_set_int(settings, "searchType", static_cast<int>(searchType));
obs_data_set_string(settings, "searchString", searchString.c_str());
regex.Save(settings);
obs_data_set_obj(data, name, settings);
}
void MacroSearchSettings::Load(obs_data_t *data, const char *name)
{
OBSDataAutoRelease settings = obs_data_get_obj(data, name);
showAlways = obs_data_get_bool(settings, "showAlways");
searchType = static_cast<SearchType>(
obs_data_get_int(settings, "searchType"));
searchString = obs_data_get_string(settings, "searchString");
regex.Load(settings);
}
static void showAdvancedSearchSettings(bool show)
{
assert(searchType);
assert(searchRegex);
searchType->setVisible(show);
searchRegex->setVisible(show);
}
static bool shouldShowSearch()
{
static const int showSearchThreshold = 10;
return GetMacroSearchSettings().showAlways ||
GetTopLevelMacros().size() >= showSearchThreshold;
}
void CheckMacroSearchVisibility()
{
assert(searchLayout);
assert(searchType);
assert(searchRegex);
assert(showSettings);
searchEnabled = shouldShowSearch();
SetLayoutVisible(searchLayout, searchEnabled);
showAdvancedSearchSettings(false);
showSettings->setChecked(false);
refreshFilter();
}
void SetupMacroSearchWidgets(QLayout *searchLayoutArg, QLineEdit *searchText,
QPushButton *searchClear, QComboBox *searchTypeArg,
RegexConfigWidget *searchRegexArg,
QPushButton *showSettingsArg,
const std::function<void()> &refreshArg)
{
searchLayout = searchLayoutArg;
searchType = searchTypeArg;
searchRegex = searchRegexArg;
refreshFilter = refreshArg;
showSettings = showSettingsArg;
searchClear->setMaximumWidth(22);
SetButtonIcon(searchClear, GetThemeTypeName() == "Light"
? "theme:Light/close.svg"
: "theme:Dark/close.svg");
searchClear->setDisabled(GetMacroSearchSettings().searchString.empty());
QWidget::connect(searchClear, &QPushButton::clicked, searchClear,
[=]() {
searchText->setText("");
searchClear->setDisabled(true);
});
const auto ph = searchText->placeholderText();
const QFontMetrics fm(searchText->font());
const int width = fm.horizontalAdvance(ph);
// Add a little padding so the text isn't cramped
searchText->setMinimumWidth(width + 10);
searchText->setText(
QString::fromStdString(GetMacroSearchSettings().searchString));
QWidget::connect(searchText, &QLineEdit::textChanged, searchText,
[searchClear](const QString &text) {
GetMacroSearchSettings().searchString =
text.toStdString();
searchClear->setDisabled(text.isEmpty());
refreshFilter();
});
searchType->addItem(
obs_module_text("AdvSceneSwitcher.macroTab.search.name"),
static_cast<int>(MacroSearchSettings::SearchType::NAME));
searchType->addItem(
obs_module_text("AdvSceneSwitcher.macroTab.search.allSegments"),
static_cast<int>(
MacroSearchSettings::SearchType::ALL_SEGMENTS));
searchType->addItem(
obs_module_text("AdvSceneSwitcher.macroTab.search.conditions"),
static_cast<int>(MacroSearchSettings::SearchType::CONDITIONS));
searchType->addItem(
obs_module_text("AdvSceneSwitcher.macroTab.search.actions"),
static_cast<int>(MacroSearchSettings::SearchType::ACTIONS));
searchType->addItem(
obs_module_text("AdvSceneSwitcher.macroTab.search.label"),
static_cast<int>(MacroSearchSettings::SearchType::LABEL));
searchType->setCurrentIndex(searchType->findData(
static_cast<int>(GetMacroSearchSettings().searchType)));
QWidget::connect(
searchType, &QComboBox::currentIndexChanged, searchType, []() {
GetMacroSearchSettings().searchType =
static_cast<MacroSearchSettings::SearchType>(
searchType->currentData().toInt());
refreshFilter();
});
searchRegex->SetRegexConfig(GetMacroSearchSettings().regex);
QWidget::connect(searchRegex, &RegexConfigWidget::RegexConfigChanged,
searchRegex, [](const RegexConfig &regex) {
GetMacroSearchSettings().regex = regex;
refreshFilter();
});
showSettings->setCheckable(true);
showSettings->setMaximumWidth(11);
SetButtonIcon(showSettings, GetThemeTypeName() == "Light"
? ":/res/images/dots-vert.svg"
: "theme:Dark/dots-vert.svg");
QWidget::connect(showSettings, &QPushButton::toggled, showSettings,
showAdvancedSearchSettings);
QWidget::connect(MacroSignalManager::Instance(),
&MacroSignalManager::Rename, searchText,
[]() { refreshFilter(); });
CheckMacroSearchVisibility();
}
MacroSearchSettings &GetMacroSearchSettings()
{
static MacroSearchSettings settings;
return settings;
}
static bool stringMatches(const std::string &text)
{
const auto &settings = GetMacroSearchSettings();
const auto &regex = settings.regex;
const auto &searchString = settings.searchString;
if (regex.Enabled()) {
return regex.Matches(text, searchString);
}
return QString::fromStdString(text).contains(
QString::fromStdString(searchString), Qt::CaseInsensitive);
}
static bool segmentTypeMatches(MacroSegment *segment, bool isCondition)
{
if (!segment) {
return false;
}
const auto getNameFromId =
isCondition ? MacroConditionFactory::GetConditionName
: MacroActionFactory::GetActionName;
const auto name = getNameFromId(segment->GetId());
return stringMatches(obs_module_text(name.c_str()));
}
static bool segmentLabelMatches(MacroSegment *segment)
{
if (!segment) {
return false;
}
if (!segment->GetUseCustomLabel()) {
return false;
}
const auto label = segment->GetCustomLabel();
return stringMatches(label);
}
bool MacroMatchesSearchFilter(Macro *macro)
{
assert(macro);
if (!searchEnabled) {
return true;
}
const auto &settings = GetMacroSearchSettings();
if (settings.searchString.empty()) {
return true;
}
if (macro->IsGroup()) {
const auto groupEntries = GetGroupMacroEntries(macro);
for (const auto &entry : groupEntries) {
if (MacroMatchesSearchFilter(entry.get())) {
return true;
}
}
return false;
}
switch (settings.searchType) {
case MacroSearchSettings::SearchType::NAME: {
const auto name = macro->Name();
return stringMatches(name);
}
case MacroSearchSettings::SearchType::ALL_SEGMENTS:
for (const auto &condition : macro->Conditions()) {
if (segmentTypeMatches(condition.get(), true)) {
return true;
}
}
for (const auto &action : macro->Actions()) {
if (segmentTypeMatches(action.get(), false)) {
return true;
}
}
for (const auto &action : macro->ElseActions()) {
if (segmentTypeMatches(action.get(), false)) {
return true;
}
}
return false;
case MacroSearchSettings::SearchType::CONDITIONS:
for (const auto &condition : macro->Conditions()) {
if (segmentTypeMatches(condition.get(), true)) {
return true;
}
}
return false;
case MacroSearchSettings::SearchType::ACTIONS:
for (const auto &action : macro->Actions()) {
if (segmentTypeMatches(action.get(), false)) {
return true;
}
}
for (const auto &action : macro->ElseActions()) {
if (segmentTypeMatches(action.get(), false)) {
return true;
}
}
return false;
case MacroSearchSettings::SearchType::LABEL:
for (const auto &condition : macro->Conditions()) {
if (segmentLabelMatches(condition.get())) {
return true;
}
}
for (const auto &action : macro->Actions()) {
if (segmentLabelMatches(action.get())) {
return true;
}
}
for (const auto &action : macro->ElseActions()) {
if (segmentLabelMatches(action.get())) {
return true;
}
}
return false;
default:
break;
}
// Unhandled search type
assert(false);
return true;
}
} // namespace advss

View File

@@ -1,43 +0,0 @@
#pragma once
#include "regex-config.hpp"
#include <functional>
#include <string>
class QComboBox;
class QLayout;
class QLineEdit;
class QPushButton;
namespace advss {
class Macro;
struct MacroSearchSettings {
void Save(obs_data_t *data, const char *name);
void Load(obs_data_t *data, const char *name);
enum class SearchType {
NAME = 0,
ALL_SEGMENTS = 10,
CONDITIONS = 20,
ACTIONS = 30,
LABEL = 40,
};
SearchType searchType = SearchType::NAME;
std::string searchString;
RegexConfig regex;
bool showAlways = false;
};
void CheckMacroSearchVisibility();
void SetupMacroSearchWidgets(QLayout *searchLayout, QLineEdit *searchText,
QPushButton *searchClear, QComboBox *searchType,
RegexConfigWidget *searchRegex,
QPushButton *showSettings,
const std::function<void()> &refresh);
MacroSearchSettings &GetMacroSearchSettings();
bool MacroMatchesSearchFilter(Macro *macro);
} // namespace advss

View File

@@ -73,46 +73,35 @@ void MacroSegmentSelection::MacroSegmentOrderChanged()
}
static QString GetMacroSegmentDescription(Macro *macro, int idx,
MacroSegmentSelection::Type type)
bool isCondition)
{
if (!macro) {
return "";
}
MacroSegment *segment;
switch (type) {
case MacroSegmentSelection::Type::CONDITION:
if (!IsValidConditionIndex(macro, idx)) {
return "";
}
segment = GetMacroConditions(macro)->at(idx).get();
break;
case MacroSegmentSelection::Type::ACTION:
if (!IsValidActionIndex(macro, idx)) {
return "";
}
segment = GetMacroActions(macro)->at(idx).get();
break;
case MacroSegmentSelection::Type::ELSE_ACTION:
if (!IsValidElseActionIndex(macro, idx)) {
return "";
}
segment = GetMacroElseActions(macro)->at(idx).get();
break;
default:
if (!IsValidMacroSegmentIndex(macro, idx, isCondition)) {
return "";
}
const auto idToName = type == MacroSegmentSelection::Type::CONDITION
? MacroConditionFactory::GetConditionName
: MacroActionFactory::GetActionName;
const QString typeStr =
obs_module_text(idToName(segment->GetId()).c_str());
const QString description =
QString::fromStdString(segment->GetShortDesc());
MacroSegment *segment;
if (isCondition) {
segment = GetMacroConditions(macro)->at(idx).get();
} else {
segment = GetMacroActions(macro)->at(idx).get();
}
QString result = typeStr;
QString description = QString::fromStdString(segment->GetShortDesc());
QString type;
if (isCondition) {
type = obs_module_text(MacroConditionFactory::GetConditionName(
segment->GetId())
.c_str());
} else {
type = obs_module_text(
MacroActionFactory::GetActionName(segment->GetId())
.c_str());
}
QString result = type;
if (!description.isEmpty()) {
result += ": " + description;
}
@@ -134,25 +123,11 @@ void MacroSegmentSelection::SetupDescription() const
return;
}
bool validIndex = false;
switch (_type) {
case Type::CONDITION:
validIndex = IsValidConditionIndex(_macro, index - 1);
break;
case Type::ACTION:
validIndex = IsValidActionIndex(_macro, index - 1);
break;
case Type::ELSE_ACTION:
validIndex = IsValidElseActionIndex(_macro, index - 1);
break;
default:
break;
}
QString description;
if (validIndex) {
description =
GetMacroSegmentDescription(_macro, index - 1, _type);
if (IsValidMacroSegmentIndex(_macro, index - 1,
_type == Type::CONDITION)) {
description = GetMacroSegmentDescription(
_macro, index - 1, _type == Type::CONDITION);
} else {
description = obs_module_text(
"AdvSceneSwitcher.macroSegmentSelection.invalid");
@@ -190,23 +165,8 @@ void MacroSegmentSelection::MarkSelectedSegment()
if (index.GetType() == IntVariable::Type::VARIABLE) {
return;
}
bool validIndex = false;
switch (_type) {
case Type::CONDITION:
validIndex = IsValidConditionIndex(_macro, index - 1);
break;
case Type::ACTION:
validIndex = IsValidActionIndex(_macro, index - 1);
break;
case Type::ELSE_ACTION:
validIndex = IsValidElseActionIndex(_macro, index - 1);
break;
default:
break;
}
if (!validIndex) {
if (!IsValidMacroSegmentIndex(_macro, index - 1,
_type == Type::CONDITION)) {
return;
}

View File

@@ -8,7 +8,7 @@ class Macro;
class MacroSegmentSelection : public QWidget {
Q_OBJECT
public:
enum class Type { CONDITION, ACTION, ELSE_ACTION };
enum class Type { CONDITION, ACTION };
MacroSegmentSelection(QWidget *parent, Type type,
bool allowVariables = true);

View File

@@ -14,7 +14,10 @@ MacroSelection::MacroSelection(QWidget *parent)
: FilterComboBox(parent,
obs_module_text("AdvSceneSwitcher.selectMacro"))
{
for (const auto &m : GetTopLevelMacros()) {
for (const auto &m : GetMacros()) {
if (m->IsGroup()) {
continue;
}
addItem(QString::fromStdString(m->Name()));
}
@@ -63,22 +66,6 @@ void MacroSelection::HideSelectedMacro()
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
}
void MacroSelection::HideGroups()
{
for (const auto &macro : GetTopLevelMacros()) {
if (!macro->IsGroup()) {
continue;
}
int idx = findText(QString::fromStdString(macro->Name()));
if (idx == -1) {
continue;
}
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
}
}
void MacroSelection::ShowAllMacros()
{
auto v = qobject_cast<QListView *>(view());

View File

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

View File

@@ -4,7 +4,6 @@
#include "macro-condition-edit.hpp"
#include "macro-export-import-dialog.hpp"
#include "macro-settings.hpp"
#include "macro-search.hpp"
#include "macro-signals.hpp"
#include "macro-tree.hpp"
#include "macro.hpp"
@@ -97,7 +96,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
return true;
}
void AdvSceneSwitcher::on_macroAdd_triggered()
void AdvSceneSwitcher::on_macroAdd_clicked()
{
// Hotkey to add new macro will also use this function.
// Since we don't want the hotkey to have an effect if the macro tab is
@@ -147,7 +146,20 @@ void AdvSceneSwitcher::on_macroAdd_triggered()
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros,
const std::shared_ptr<Macro> &group)
{
auto subitems = GetGroupMacroEntries(group.get());
std::vector<std::shared_ptr<Macro>> subitems;
subitems.reserve(group->GroupSize());
// Find all subitems
auto allMacros = GetMacros();
for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
if ((*it)->Name() != group->Name()) {
continue;
}
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
subitems.emplace_back(*std::next(it, i));
}
break;
}
// Remove subitems which were already selected to avoid duplicates
for (const auto &subitem : subitems) {
@@ -195,7 +207,7 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> &macro)
// Don't cache widgets for about to be deleted macros
MacroSegmentList::SetCachingEnabled(false);
ui->macros->Remove(macro);
MacroSegmentList::SetCachingEnabled(true);
MacroSegmentList::SetCachingEnabled(!switcher->disableMacroWidgetCache);
MacroSignalManager::Instance()->Remove(name);
}
@@ -247,12 +259,12 @@ void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> &macro,
MacroSignalManager::Instance()->Rename(oldName, name);
}
void AdvSceneSwitcher::on_macroRemove_triggered()
void AdvSceneSwitcher::on_macroRemove_clicked()
{
RemoveSelectedMacros();
}
void AdvSceneSwitcher::on_macroUp_triggered() const
void AdvSceneSwitcher::on_macroUp_clicked() const
{
auto macro = GetSelectedMacro();
if (!macro) {
@@ -261,7 +273,7 @@ void AdvSceneSwitcher::on_macroUp_triggered() const
ui->macros->Up(macro);
}
void AdvSceneSwitcher::on_macroDown_triggered() const
void AdvSceneSwitcher::on_macroDown_clicked() const
{
auto macro = GetSelectedMacro();
if (!macro) {
@@ -398,17 +410,13 @@ void AdvSceneSwitcher::ImportMacros()
int groupSize = 0;
std::shared_ptr<Macro> group;
std::vector<std::shared_ptr<Macro>> importedMacros;
auto &tempMacros = GetTemporaryMacros();
auto lock = LockContext();
for (size_t i = 0; i < count; i++) {
tempMacros.clear();
OBSDataAutoRelease array_obj = obs_data_array_item(array, i);
auto macro = std::make_shared<Macro>();
tempMacros.emplace_back(macro);
macro->Load(array_obj);
RunAndClearPostLoadSteps();
RunPostLoadSteps();
if (macroNameExists(macro->Name()) &&
!ResolveMacroImportNameConflict(macro)) {
@@ -417,7 +425,7 @@ void AdvSceneSwitcher::ImportMacros()
}
importedMacros.emplace_back(macro);
GetTopLevelMacros().emplace_back(macro);
GetMacros().emplace_back(macro);
if (groupSize > 0 && !macro->IsGroup()) {
Macro::PrepareMoveToGroup(group, macro);
groupSize--;
@@ -436,10 +444,9 @@ void AdvSceneSwitcher::ImportMacros()
for (const auto &macro : importedMacros) {
macro->PostLoad();
}
RunAndClearPostLoadSteps();
tempMacros.clear();
RunPostLoadSteps();
ui->macros->Reset(GetTopLevelMacros(),
ui->macros->Reset(GetMacros(),
GetGlobalMacroSettings()._highlightExecuted);
disableAddButtonHighlight();
}
@@ -603,20 +610,17 @@ setupToolBar(const std::initializer_list<std::initializer_list<QWidget *>>
auto toolbar = new QToolBar();
toolbar->setIconSize({16, 16});
QAction *lastSeparator = nullptr;
QAction *lastSeperator = nullptr;
for (const auto &widgetGroup : widgetGroups) {
for (const auto &widget : widgetGroup) {
if (!widget) {
continue;
}
toolbar->addWidget(widget);
}
lastSeparator = toolbar->addSeparator();
lastSeperator = toolbar->addSeparator();
}
if (lastSeparator) {
toolbar->removeAction(lastSeparator);
if (lastSeperator) {
toolbar->removeAction(lastSeperator);
}
// Prevent "extension" button from showing up
@@ -629,15 +633,16 @@ void AdvSceneSwitcher::SetupMacroTab()
{
ui->macros->installEventFilter(this);
auto &macros = GetTopLevelMacros();
if (macros.size() == 0 && !switcher->disableHints) {
addPulse = HighlightWidget(
ui->macrosToolbar->widgetForAction(ui->macroAdd),
QColor(Qt::green));
if (GetMacros().size() == 0 && !switcher->disableHints) {
addPulse = HighlightWidget(ui->macroAdd, QColor(Qt::green));
}
ui->macros->Reset(macros, GetGlobalMacroSettings()._highlightExecuted);
auto macroControls = setupToolBar({{ui->macroAdd, ui->macroRemove},
{ui->macroUp, ui->macroDown}});
ui->macroControlLayout->addWidget(macroControls);
ui->macros->Reset(GetMacros(),
GetGlobalMacroSettings()._highlightExecuted);
connect(ui->macros, SIGNAL(MacroSelectionChanged()), this,
SLOT(MacroSelectionChanged()));
ui->runMacro->SetMacroTree(ui->macros);
@@ -666,12 +671,6 @@ void AdvSceneSwitcher::SetupMacroTab()
switcher->macroListMacroEditSplitterPosition);
}
}
SetupMacroSearchWidgets(ui->macroSearchLayout, ui->macroSearchText,
ui->macroSearchClear, ui->macroSearchType,
ui->macroSearchRegex,
ui->macroSearchShowSettings,
[this]() { ui->macros->RefreshFilter(); });
}
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
@@ -681,7 +680,7 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.contextMenuAdd"),
this, &AdvSceneSwitcher::on_macroAdd_triggered);
this, &AdvSceneSwitcher::on_macroAdd_clicked);
auto copy = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.copy"), this,
@@ -697,7 +696,7 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
auto remove = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.remove"), this,
&AdvSceneSwitcher::on_macroRemove_triggered);
&AdvSceneSwitcher::on_macroRemove_clicked);
remove->setDisabled(ui->macros->SelectionEmpty());
menu.addSeparator();
@@ -753,7 +752,7 @@ void AdvSceneSwitcher::CopyMacro()
newMacro->Load(data);
newMacro->PostLoad();
newMacro->SetName(name);
RunAndClearPostLoadSteps();
RunPostLoadSteps();
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
ui->macros->Add(newMacro, macro);

View File

@@ -1,6 +1,5 @@
#include "macro-tree.hpp"
#include "macro.hpp"
#include "macro-search.hpp"
#include "macro-signals.hpp"
#include "path-helpers.hpp"
#include "sync-helpers.hpp"
@@ -62,11 +61,12 @@ MacroTreeItem::MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macroItem,
_boxLayout = new QHBoxLayout();
_boxLayout->setContentsMargins(0, 0, 0, 0);
_boxLayout->addWidget(_running);
if (isGroup) {
_boxLayout->addWidget(_iconLabel);
_boxLayout->addSpacing(2);
_running->hide();
}
_boxLayout->addWidget(_running);
_boxLayout->addWidget(_label);
#ifdef __APPLE__
/* Hack: Fixes a bug where scrollbars would be above the lock icon */
@@ -76,17 +76,17 @@ MacroTreeItem::MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macroItem,
Update(true);
setLayout(_boxLayout);
connect(_running, SIGNAL(clicked(bool)), this,
SLOT(RunningClicked(bool)));
auto setRunning = [this](bool val) {
_macro->SetPaused(!val);
};
connect(_running, &QAbstractButton::clicked, setRunning);
connect(MacroSignalManager::Instance(), SIGNAL(HighlightChanged(bool)),
this, SLOT(EnableHighlight(bool)));
connect(MacroSignalManager::Instance(),
SIGNAL(Rename(const QString &, const QString &)), this,
SLOT(MacroRenamed(const QString &, const QString &)));
connect(&_timer, SIGNAL(timeout()), this, SLOT(HighlightIfExecuted()));
connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateRunning()));
UpdateRunning();
connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdatePaused()));
_timer.start(1500);
}
@@ -95,90 +95,10 @@ void MacroTreeItem::EnableHighlight(bool enable)
_highlight = enable;
}
void MacroTreeItem::RunningClicked(bool running)
{
const auto updateWidget = [this](Macro *macro) {
if (!macro) {
return;
}
const auto &macros = GetTopLevelMacros();
bool found = false;
int idx = 0;
for (const auto &m : macros) {
if (m.get() == macro) {
found = true;
break;
}
idx++;
}
if (!found) {
return;
}
const auto widget = _tree->GetItemWidget(idx);
if (widget) {
widget->UpdateRunning();
}
};
if (!_macro->IsGroup()) {
_macro->SetPaused(!running);
if (!_macro->IsSubitem()) {
return;
}
const auto group = _macro->Parent();
updateWidget(group.get());
return;
}
const auto macros = GetGroupMacroEntries(_macro.get());
for (const auto &macro : macros) {
macro->SetPaused(!running);
}
// Update backend values before updating UI to prevent flickering in
// running state of the group
for (const auto &macro : macros) {
updateWidget(macro.get());
}
UpdateRunning();
}
void MacroTreeItem::UpdateRunning()
void MacroTreeItem::UpdatePaused()
{
const QSignalBlocker blocker(_running);
if (!_macro->IsGroup()) {
_running->setChecked(!_macro->Paused());
return;
}
const auto macros = GetGroupMacroEntries(_macro.get());
bool allRunning = true;
bool allPaused = true;
for (const auto &macro : macros) {
if (macro->Paused()) {
allRunning = false;
} else {
allPaused = false;
}
}
if (allRunning) {
_running->setCheckState(Qt::Checked);
return;
}
if (allPaused) {
_running->setCheckState(Qt::Unchecked);
return;
}
_running->setCheckState(Qt::PartiallyChecked);
_running->setChecked(!_macro->Paused());
}
void MacroTreeItem::HighlightIfExecuted()
@@ -293,6 +213,7 @@ void MacroTreeModel::Reset(std::deque<std::shared_ptr<Macro>> &newItems)
_macros = newItems;
endResetModel();
UpdateGroupState(false);
_mt->ResetWidgets();
}
@@ -537,6 +458,9 @@ void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
_mt->selectionModel()->clear();
if (isGroup) {
UpdateGroupState(true);
}
assert(IsInValidState());
}
@@ -625,6 +549,7 @@ MacroTreeModel::MacroTreeModel(MacroTree *st_,
_mt(st_),
_macros(macros)
{
UpdateGroupState(false);
}
int MacroTreeModel::rowCount(const QModelIndex &parent) const
@@ -778,6 +703,7 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
offset++;
}
_hasGroups = true;
_mt->selectionModel()->clear();
Reset(_macros);
@@ -835,12 +761,29 @@ void MacroTreeModel::CollapseGroup(std::shared_ptr<Macro> item)
assert(IsInValidState());
}
void MacroTreeModel::UpdateGroupState(bool update)
{
bool nowHasGroups = false;
for (auto &item : _macros) {
if (item->IsGroup()) {
nowHasGroups = true;
break;
}
}
if (nowHasGroups != _hasGroups) {
_hasGroups = nowHasGroups;
if (update) {
_mt->UpdateWidgets(true);
}
}
}
void MacroTree::Reset(std::deque<std::shared_ptr<Macro>> &macros,
bool highlight)
{
_highlight = highlight;
auto mtm = new MacroTreeModel(this, macros);
MacroTreeModel *mtm = new MacroTreeModel(this, macros);
setModel(mtm);
GetModel()->Reset(macros);
connect(selectionModel(),
@@ -897,6 +840,7 @@ MacroTree::MacroTree(QWidget *parent_) : QListView(parent_)
void MacroTree::ResetWidgets()
{
MacroTreeModel *mtm = GetModel();
mtm->UpdateGroupState(false);
int modelIdx = 0;
for (int i = 0; i < (int)mtm->_macros.size(); i++) {
QModelIndex index = mtm->createIndex(modelIdx, 0, nullptr);
@@ -925,7 +869,7 @@ void MacroTree::UpdateWidgets(bool force)
for (int i = 0; i < (int)mtm->_macros.size(); i++) {
std::shared_ptr<Macro> item = mtm->_macros[i];
auto widget = GetItemWidget(i);
MacroTreeItem *widget = GetItemWidget(i);
if (!widget) {
UpdateWidget(mtm->createIndex(i, 0, nullptr), item);
@@ -1240,7 +1184,7 @@ bool MacroTree::SelectionEmpty() const
bool MacroTree::GroupsExist() const
{
for (const auto &macro : GetTopLevelMacros()) {
for (const auto &macro : GetMacros()) {
if (macro->IsGroup()) {
return true;
}
@@ -1260,12 +1204,6 @@ void MacroTree::CollapseGroup(std::shared_ptr<Macro> item) const
mtm->CollapseGroup(item);
}
void MacroTree::RefreshFilter()
{
UpdateWidgets();
doItemsLayout();
}
void MacroTree::MoveItemBefore(const std::shared_ptr<Macro> &item,
const std::shared_ptr<Macro> &after) const
{
@@ -1280,7 +1218,7 @@ void MacroTree::MoveItemAfter(const std::shared_ptr<Macro> &item,
MacroTreeModel *MacroTree::GetModel() const
{
return qobject_cast<MacroTreeModel *>(model());
return reinterpret_cast<MacroTreeModel *>(model());
}
void MacroTree::Remove(std::shared_ptr<Macro> item) const
@@ -1368,7 +1306,7 @@ void MacroTree::UngroupSelectedGroups()
void MacroTree::ExpandAll()
{
for (const auto &macro : GetTopLevelMacros()) {
for (const auto &macro : GetMacros()) {
if (!macro->IsGroup()) {
continue;
}
@@ -1378,7 +1316,7 @@ void MacroTree::ExpandAll()
void MacroTree::CollapseAll()
{
for (const auto &macro : GetTopLevelMacros()) {
for (const auto &macro : GetMacros()) {
if (!macro->IsGroup()) {
continue;
}
@@ -1395,8 +1333,8 @@ void MacroTree::SelectionChangedHelper(const QItemSelection &,
inline MacroTreeItem *MacroTree::GetItemWidget(int idx) const
{
auto widget = indexWidget(GetModel()->createIndex(idx, 0, nullptr));
return qobject_cast<MacroTreeItem *>(widget);
QWidget *widget = indexWidget(GetModel()->createIndex(idx, 0, nullptr));
return reinterpret_cast<MacroTreeItem *>(widget);
}
void MacroTree::paintEvent(QPaintEvent *event)
@@ -1423,25 +1361,14 @@ MacroTreeDelegate::MacroTreeDelegate(QObject *parent)
QSize MacroTreeDelegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
auto tree = qobject_cast<MacroTree *>(parent());
auto widget = tree->indexWidget(index);
MacroTree *tree = qobject_cast<MacroTree *>(parent());
QWidget *item = tree->indexWidget(index);
if (!widget) {
if (!item) {
return QStyledItemDelegate::sizeHint(option, index);
}
auto name = index.data(Qt::AccessibleTextRole).toString();
auto macro = GetMacroByQString(name);
if (!macro) {
return QStyledItemDelegate::sizeHint(option, index);
}
if (MacroMatchesSearchFilter(macro)) {
return QSize(widget->sizeHint());
}
return QSize(0, 0);
return QSize(item->sizeHint());
}
} // namespace advss

View File

@@ -37,8 +37,7 @@ public:
private slots:
void ExpandClicked(bool checked);
void EnableHighlight(bool enable);
void RunningClicked(bool);
void UpdateRunning();
void UpdatePaused();
void HighlightIfExecuted();
void MacroRenamed(const QString &, const QString &);
@@ -107,6 +106,7 @@ private:
void UngroupSelectedGroups(QModelIndexList &indices);
void ExpandGroup(std::shared_ptr<Macro> item);
void CollapseGroup(std::shared_ptr<Macro> item);
void UpdateGroupState(bool update);
int GetItemMacroIndex(const std::shared_ptr<Macro> &item) const;
int GetItemModelIndex(const std::shared_ptr<Macro> &item) const;
bool IsLastItem(std::shared_ptr<Macro> item) const;
@@ -114,6 +114,7 @@ private:
MacroTree *_mt;
std::deque<std::shared_ptr<Macro>> &_macros;
bool _hasGroups = false;
friend class MacroTree;
friend class MacroTreeItem;
@@ -141,7 +142,6 @@ public:
bool GroupsExist() const;
void ExpandGroup(std::shared_ptr<Macro> item) const;
void CollapseGroup(std::shared_ptr<Macro> item) const;
void RefreshFilter();
public slots:
void GroupSelectedItems();

View File

@@ -17,6 +17,8 @@
namespace advss {
static std::deque<std::shared_ptr<Macro>> macros;
Macro::Macro(const std::string &name) : _dockSettings(this)
{
SetName(name);
@@ -57,7 +59,6 @@ Macro::CreateGroup(const std::string &name,
void Macro::RemoveGroup(std::shared_ptr<Macro> group)
{
auto &macros = GetTopLevelMacros();
auto it = std::find(macros.begin(), macros.end(), group);
if (it == macros.end()) {
return;
@@ -74,7 +75,6 @@ void Macro::RemoveGroup(std::shared_ptr<Macro> group)
void Macro::PrepareMoveToGroup(Macro *group, std::shared_ptr<Macro> item)
{
auto &macros = GetTopLevelMacros();
for (const auto &m : macros) {
if (m.get() == group) {
PrepareMoveToGroup(m, item);
@@ -948,12 +948,12 @@ bool Macro::PostLoad()
bool Macro::SwitchesScene() const
{
for (const auto &a : _actions) {
if (a->GetId() == MacroAction::GetDefaultID()) {
if (a->GetId() == GetSceneSwitchActionId()) {
return true;
}
}
for (const auto &a : _elseActions) {
if (a->GetId() == MacroAction::GetDefaultID()) {
if (a->GetId() == GetSceneSwitchActionId()) {
return true;
}
}
@@ -1114,7 +1114,7 @@ void Macro::SetHotkeysDesc() const
void SaveMacros(obs_data_t *obj)
{
obs_data_array_t *macroArray = obs_data_array_create();
for (const auto &m : GetTopLevelMacros()) {
for (const auto &m : macros) {
obs_data_t *array_obj = obs_data_create();
m->Save(array_obj);
@@ -1128,9 +1128,7 @@ void SaveMacros(obs_data_t *obj)
void LoadMacros(obs_data_t *obj)
{
auto &macros = GetTopLevelMacros();
macros.clear();
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
size_t count = obs_data_array_count(macroArray);
@@ -1180,10 +1178,15 @@ void LoadMacros(obs_data_t *obj)
}
}
std::deque<std::shared_ptr<Macro>> &GetMacros()
{
return macros;
}
bool CheckMacros()
{
bool matchFound = false;
for (const auto &m : GetTopLevelMacros()) {
for (const auto &m : macros) {
if (!m->ConditionsShouldBeChecked()) {
vblog(LOG_INFO,
"skipping condition check for macro \"%s\" "
@@ -1210,7 +1213,7 @@ bool RunMacros()
// 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.
auto runPhaseMacros = GetTopLevelMacros();
auto runPhaseMacros = macros;
// Avoid deadlocks when opening settings window and calling frontend
// API functions at the same time.
@@ -1250,14 +1253,41 @@ bool RunMacros()
void StopAllMacros()
{
for (const auto &m : GetAllMacros()) {
for (const auto &m : macros) {
m->Stop();
}
}
Macro *GetMacroByName(const char *name)
{
for (const auto &m : macros) {
if (m->Name() == name) {
return m.get();
}
}
return nullptr;
}
Macro *GetMacroByQString(const QString &name)
{
return GetMacroByName(name.toUtf8().constData());
}
std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
{
for (const auto &m : macros) {
if (m->Name() == name) {
return m;
}
}
return {};
}
void InvalidateMacroTempVarValues()
{
for (const auto &m : GetTopLevelMacros()) {
for (const auto &m : macros) {
// Do not invalidate the temp vars set during condition checks
// or action executions running in parallel to the "main" macro
// loop, as otherwise access to the information stored in those
@@ -1272,8 +1302,6 @@ void InvalidateMacroTempVarValues()
std::shared_ptr<Macro> GetMacroWithInvalidConditionInterval()
{
auto &macros = GetTopLevelMacros();
if (macros.empty()) {
return {};
}

View File

@@ -9,6 +9,8 @@
#include "temp-variable.hpp"
#include <future>
#include <QString>
#include <QByteArray>
#include <string>
#include <deque>
#include <memory>
@@ -215,9 +217,13 @@ private:
void LoadMacros(obs_data_t *obj);
void SaveMacros(obs_data_t *obj);
std::deque<std::shared_ptr<Macro>> &GetMacros();
bool CheckMacros();
bool RunMacros();
void StopAllMacros();
Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name);
std::weak_ptr<Macro> GetWeakMacroByName(const char *name);
void InvalidateMacroTempVarValues();
std::shared_ptr<Macro> GetMacroWithInvalidConditionInterval();

View File

@@ -258,7 +258,7 @@ bool IsFullscreen(const std::string &title)
return false;
}
std::optional<std::string> GetTextInWindow(const std::string &)
std::optional<std::string> GetTextInWindow(const std::string &window)
{
// Not implemented
return {};

View File

@@ -113,7 +113,7 @@ void ActionQueue::Add(const std::shared_ptr<MacroAction> &action)
action->Save(data);
copy->Load(data);
copy->PostLoad();
RunAndClearPostLoadSteps();
RunPostLoadSteps();
copy->ResolveVariablesToFixedValues();
_actions.emplace_back(copy);
} else {
@@ -423,7 +423,7 @@ void ImportQueues(obs_data_t *data)
importedQueues->emplace_back(queue);
}
QueueUITask(signalImportedQueues, importedQueues);
QeueUITask(signalImportedQueues, importedQueues);
}
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name)

View File

@@ -163,6 +163,7 @@ public:
bool warnPluginLoadFailure = true;
bool disableHints = false;
bool disableFilterComboboxFilter = false;
bool disableMacroWidgetCache = false;
bool hideLegacyTabs = true;
bool saveWindowGeo = false;
QPoint windowPos = {};

View File

@@ -67,7 +67,7 @@ int GetCanvasCount()
return 1;
#else
static const auto enumCanvases = [](void *countPtr,
obs_canvas_t *) -> bool {
obs_canvas_t *canvas) -> bool {
auto count = static_cast<int *>(countPtr);
(*count)++;
return true;
@@ -145,15 +145,6 @@ OBSWeakSource GetActiveCanvasScene(const OBSWeakCanvas &weakCanvas)
}
#endif
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
// The frontend scene order does not necessarily match the order of the
// scenes in the canvas.
if (canvas == mainCanvas) {
OBSSourceAutoRelease scene = obs_frontend_get_current_scene();
return OBSGetWeakRef(scene);
}
static const auto enumCanvasScenes = [](void *scenePtr,
obs_source_t *source) -> bool {
auto scene = static_cast<OBSWeakSource *>(scenePtr);
@@ -190,25 +181,6 @@ OBSWeakSource GetSceneAtIndex(const OBSWeakCanvas &weakCanvas, int idx)
}
#endif
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
// The frontend scene order does not necessarily match the order of the
// scenes in the canvas.
// So, rely on obs_frontend_get_scenes() in case of the main canvas
// instead.
if (canvas == mainCanvas) {
struct obs_frontend_source_list scenes = {0};
obs_frontend_get_scenes(&scenes);
if (idx >= (int)scenes.sources.num) {
obs_frontend_source_list_free(&scenes);
return nullptr;
}
auto scene = OBSGetWeakRef(scenes.sources.array[idx]);
obs_frontend_source_list_free(&scenes);
return scene;
}
struct Data {
const int idx;
int currentIdx;
@@ -251,29 +223,6 @@ int GetIndexOfScene(const OBSWeakCanvas &weakCanvas, const OBSWeakSource &scene)
return 0;
}
#endif
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
// The frontend scene order does not necessarily match the order of the
// scenes in the canvas.
// So, rely on obs_frontend_get_scenes() in case of the main canvas
// instead.
if (canvas == mainCanvas) {
struct obs_frontend_source_list scenes = {0};
obs_frontend_get_scenes(&scenes);
OBSSourceAutoRelease sceneSource = OBSGetStrongRef(scene);
int idx = 0;
for (size_t i = 0; i < scenes.sources.num; i++) {
if (scenes.sources.array[i] == sceneSource) {
idx = i;
break;
}
}
obs_frontend_source_list_free(&scenes);
return idx;
}
struct Data {
int idx;
@@ -297,12 +246,6 @@ int GetIndexOfScene(const OBSWeakCanvas &weakCanvas, const OBSWeakSource &scene)
return data.idx;
}
OBSWeakCanvas GetMainCanvas()
{
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
return OBSGetWeakRef(mainCanvas);
}
#if LIBOBS_API_VER < MAKE_SEMANTIC_VERSION(31, 1, 0)
obs_canvas_t *obs_canvas_get_ref(obs_canvas_t *)

View File

@@ -42,7 +42,6 @@ EXPORT OBSWeakSource GetActiveCanvasScene(const OBSWeakCanvas &canvas);
EXPORT OBSWeakSource GetSceneAtIndex(const OBSWeakCanvas &weakCanvas, int idx);
EXPORT int GetIndexOfScene(const OBSWeakCanvas &weakCanvas,
const OBSWeakSource &scene);
EXPORT OBSWeakCanvas GetMainCanvas();
class CanvasSelection : public FilterComboBox {
Q_OBJECT

View File

@@ -1,135 +0,0 @@
#include "crash-handler.hpp"
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp"
#include <obs-frontend-api.h>
#include <obs-module.h>
#include <QDir>
#include <QFile>
#include <thread>
namespace advss {
static constexpr std::string_view sentinel = ".running";
#ifndef NDEBUG
static constexpr bool handleUncleanShutdown = false;
#else
static constexpr bool handleUncleanShutdown = true;
#endif
static bool wasCleanShutdown = false;
static void setup();
static bool setupDone = []() {
AddPluginInitStep(setup);
return true;
}();
static void handleShutdown(enum obs_frontend_event event, void *)
{
if (event != OBS_FRONTEND_EVENT_EXIT) {
return;
}
char *sentinelFile = obs_module_config_path(sentinel.data());
if (!sentinelFile) {
return;
}
QFile file(sentinelFile);
if (!file.exists()) {
bfree(sentinelFile);
return;
}
if (!file.remove()) {
blog(LOG_WARNING, "failed to remove sentinel file");
}
bfree(sentinelFile);
}
static void setup()
{
char *sentinelFile = obs_module_config_path(sentinel.data());
if (!sentinelFile) {
return;
}
QString dirPath = QFileInfo(sentinelFile).absolutePath();
QDir dir(dirPath);
if (!dir.exists()) {
if (!dir.mkpath(dirPath)) {
blog(LOG_WARNING,
"failed to create directory for sentinel file");
bfree(sentinelFile);
return;
}
}
QFile file(sentinelFile);
wasCleanShutdown = file.exists();
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
blog(LOG_WARNING, "failed to create sentinel file");
bfree(sentinelFile);
return;
}
file.write("running");
file.close();
bfree(sentinelFile);
obs_frontend_add_event_callback(handleShutdown, nullptr);
return;
}
static bool wasUncleanShutdown()
{
static bool alreadyHandled = false;
if (!handleUncleanShutdown || !wasCleanShutdown || alreadyHandled) {
alreadyHandled = true;
return false;
}
alreadyHandled = true;
blog(LOG_WARNING, "unclean shutdown detected");
return true;
}
static bool askForStartupSkip()
{
return DisplayMessage(obs_module_text("AdvSceneSwitcher.crashDetected"),
true, false);
}
bool ShouldSkipPluginStartOnUncleanShutdown()
{
if (!wasUncleanShutdown()) {
return false;
}
std::thread t([]() {
obs_queue_task(
OBS_TASK_UI,
[](void *) {
const bool skipStart = askForStartupSkip();
if (!skipStart) {
StartPlugin();
}
},
nullptr, false);
});
t.detach();
return true;
}
} // namespace advss

View File

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

View File

@@ -56,7 +56,7 @@ std::optional<double> GetDouble(const std::string &str)
std::optional<int> GetInt(const std::string &str)
{
char *end = nullptr;
int value = (int)std::strtol(str.c_str(), &end, 10);
int value = std::strtol(str.c_str(), &end, 10);
if (end != str.c_str() && *end == '\0' && value != INT_MAX &&
value != INT_MIN) {

View File

@@ -113,13 +113,12 @@ void RunLoadSteps(obs_data_t *obj)
}
}
void RunAndClearPostLoadSteps()
void RunPostLoadSteps()
{
std::lock_guard<std::mutex> lock(postLoadMutex);
for (const auto &func : getPostLoadSteps()) {
func();
}
getPostLoadSteps().clear();
}
void ClearPostLoadSteps()

View File

@@ -14,7 +14,7 @@ EXPORT void AddPostLoadStep(std::function<void()>);
EXPORT void AddIntervalResetStep(std::function<void()>);
void RunSaveSteps(obs_data_t *);
void RunLoadSteps(obs_data_t *);
EXPORT void RunAndClearPostLoadSteps();
EXPORT void RunPostLoadSteps();
void ClearPostLoadSteps();
EXPORT void AddPluginInitStep(std::function<void()>);

View File

@@ -8,7 +8,7 @@
namespace advss {
ResourceTable::ResourceTable(QWidget *parent, const QString &help,
ResourceTable::ResourceTable(QTabWidget *parent, const QString &help,
const QString &addToolTip,
const QString &removeToolTip,
const QStringList &headers,
@@ -57,7 +57,6 @@ ResourceTable::ResourceTable(QWidget *parent, const QString &help,
auto layout = new QVBoxLayout();
layout->addLayout(helpAndTableLayout);
layout->addLayout(controlLayout);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(Add()));
@@ -66,16 +65,13 @@ ResourceTable::ResourceTable(QWidget *parent, const QString &help,
[openSettings]() { openSettings(); });
auto settingsShortcut = new QShortcut(QKeySequence("F2"), this);
settingsShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(settingsShortcut, &QShortcut::activated, this,
openSettings);
auto removeShortcut = new QShortcut(QKeySequence("Del"), this);
removeShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(removeShortcut, &QShortcut::activated, this,
[this]() { Remove(); });
auto newShortcut = new QShortcut(QKeySequence("Ctrl+N"), this);
newShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QWidget::connect(newShortcut, &QShortcut::activated, this,
[this]() { Add(); });
}

View File

@@ -14,7 +14,7 @@ class ADVSS_EXPORT ResourceTable : public QWidget {
Q_OBJECT
public:
ResourceTable(QWidget *parent, const QString &help,
ResourceTable(QTabWidget *parent, const QString &help,
const QString &addToolTip, const QString &removeToolTip,
const QStringList &headers,
const std::function<void()> &openSettings);

View File

@@ -103,11 +103,10 @@ void SceneSelection::Load(obs_data_t *obj, const char *name,
OBSDataAutoRelease data =
obs_data_get_obj(obj, selectionSaveName.data());
const char *canvasName =
obs_data_get_string(data, canvasSaveName.data());
_canvas = GetWeakCanvasByName(
obs_data_get_string(data, canvasSaveName.data()));
if (strcmp(canvasName, "") == 0) {
if (obs_data_has_user_value(data, canvasSaveName.data())) {
_canvas = GetWeakCanvasByName(
obs_data_get_string(data, canvasSaveName.data()));
} else {
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
_canvas = obs_canvas_get_weak_canvas(mainCanvas);
obs_weak_canvas_release(_canvas);
@@ -240,26 +239,22 @@ void SceneSelection::ResolveVariables()
_type = Type::SCENE;
}
static obs_weak_canvas_t *getWeakRefToMainCanvas()
{
static auto canvas = obs_get_main_canvas();
static auto weakCanvas = obs_canvas_get_weak_canvas(canvas);
[[maybe_unused]] static const bool _ = []() {
// Let's just hope we don't have to deal with selecting scenes
// when the OBS main canvas gets deleted and release the
// references here already to avoid reporting leaks on shutdown
obs_canvas_release(canvas);
obs_weak_canvas_release(weakCanvas);
return true;
}();
return weakCanvas;
}
SceneSelection SceneSelectionWidget::CurrentSelection()
{
SceneSelection s;
s._canvas = _forceMainCanvas ? OBSWeakCanvas(getWeakRefToMainCanvas())
static auto mainCanvas = obs_get_main_canvas();
static auto mainCanvasWeak = obs_canvas_get_weak_canvas(mainCanvas);
[[maybe_unused]] static const bool _ = []() {
// Let's just hope we don't have to deal with selecting scenes
// when the OBS main canvas gets deleted and release the
// references here already to avoid reporting leaks on shutdown
obs_canvas_release(mainCanvas);
obs_weak_canvas_release(mainCanvasWeak);
return true;
}();
s._canvas = _forceMainCanvas ? OBSWeakCanvas(mainCanvasWeak)
: _canvas->GetCanvas();
const int idx = _scenes->currentIndex();
@@ -335,7 +330,6 @@ static QStringList getCanvasScenesList(obs_weak_canvas_t *weakCanvas)
QStringList list;
obs_canvas_enum_scenes(canvas, enumCanvasScenes, &list);
list.sort();
return list;
}
@@ -348,10 +342,6 @@ void SceneSelectionWidget::Reset()
void SceneSelectionWidget::PopulateSceneSelection(obs_weak_canvas_t *canvas)
{
if (_forceMainCanvas) {
canvas = getWeakRefToMainCanvas();
}
_scenes->clear();
if ((_current || _previous)) {
const bool isMain = IsMainCanvas(canvas);
@@ -428,7 +418,7 @@ SceneSelectionWidget::SceneSelectionWidget(QWidget *parent, bool variables,
layout->setContentsMargins(0, 0, 0, 0);
if (GetCanvasCount() <= 1) {
LockToMainCanvas();
_canvas->hide();
}
Resize();

View File

@@ -30,7 +30,7 @@ public:
private:
OBSWeakSource _scene;
OBSWeakCanvas _canvas = GetMainCanvas();
OBSWeakCanvas _canvas;
SceneGroup *_group = nullptr;
std::weak_ptr<Variable> _variable;
Type _type = Type::SCENE;

View File

@@ -199,17 +199,7 @@ std::chrono::high_resolution_clock::time_point GetLastSceneChangeTime()
OBSWeakSource GetCurrentScene()
{
if (switcher->currentScene) {
return switcher->currentScene;
}
// If there wasn't any scene switch yet switcher->currentScene will be
// null and we must use obs_frontend_get_current_scene() instead
OBSSourceAutoRelease currentSceneSource =
obs_frontend_get_current_scene();
OBSWeakSourceAutoRelease currentSceneWeakSource =
obs_source_get_weak_source(currentSceneSource);
return currentSceneWeakSource.Get();
return switcher->currentScene;
}
OBSWeakSource GetPreviousScene()

View File

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

View File

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

View File

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

View File

@@ -190,6 +190,29 @@ static bool isMatchingNestedMacroAction(MacroAction *action, const Macro *macro)
return nestedMacroAction->_nestedMacro.get() == macro;
}
static void appendNestedMacros(std::deque<std::shared_ptr<Macro>> &macros,
Macro *macro)
{
if (!macro) {
return;
}
for (const auto &action : macro->Actions()) {
const auto nestedMacroAction =
dynamic_cast<MacroActionMacro *>(action.get());
if (nestedMacroAction) {
macros.push_back(nestedMacroAction->_nestedMacro);
}
}
for (const auto &action : macro->ElseActions()) {
const auto nestedMacroAction =
dynamic_cast<MacroActionMacro *>(action.get());
if (nestedMacroAction) {
macros.push_back(nestedMacroAction->_nestedMacro);
}
}
}
static Macro *getParentMacro(const Macro *targetMacro,
const std::vector<Macro *> &macros)
{
@@ -240,10 +263,8 @@ static Macro *getParentMacro(const Macro *targetMacro,
static Macro *getParentMacro(const Macro *macro)
{
const auto &topLevelMacros = GetTopLevelMacros();
std::vector<Macro *> macros;
macros.reserve(topLevelMacros.size());
for (const auto &macro : topLevelMacros) {
for (const auto &macro : GetMacros()) {
macros.emplace_back(macro.get());
}
return getParentMacro(macro, macros);
@@ -280,9 +301,8 @@ void TempVariableRef::Save(obs_data_t *obj, Macro *macro,
void TempVariableRef::Load(obs_data_t *obj, Macro *macroPtr, const char *name)
{
const auto macros = GetAllMacros();
std::weak_ptr<Macro> macro;
for (const auto &macroShared : macros) {
for (const auto &macroShared : GetMacros()) {
if (macroShared.get() == macroPtr) {
macro = macroShared;
break;
@@ -320,9 +340,9 @@ void TempVariableRef::PostLoad(int idx, SegmentType type,
return;
}
auto macro = childMacro.get();
Macro *macro = nullptr;
for (int i = 0; i < _depth; i++) {
macro = getParentMacro(macro);
macro = getParentMacro(childMacro.get());
}
if (!macro) {
@@ -466,9 +486,15 @@ void TempVariableSelection::MacroSegmentsChanged()
SetVariable(currentSelection);
}
void TempVariableSelection::SegmentTempVarsChanged(MacroSegment *)
void TempVariableSelection::SegmentTempVarsChanged(MacroSegment *segment)
{
MacroSegmentsChanged();
const auto currentSegment = GetSegment();
const auto currentMacro = currentSegment ? currentSegment->GetMacro()
: nullptr;
const auto changeMacro = segment ? segment->GetMacro() : nullptr;
if (currentMacro == changeMacro) {
MacroSegmentsChanged();
}
}
void TempVariableSelection::HighlightChanged(int idx)

View File

@@ -75,7 +75,7 @@ private:
void PostLoad(int idx, SegmentType, const std::weak_ptr<Macro> &);
std::string _id = "";
std::weak_ptr<MacroSegment> _segment = {};
std::weak_ptr<MacroSegment> _segment;
int _depth = 0;
friend TempVariable;

View File

@@ -178,7 +178,7 @@ std::string GetThemeTypeName()
#endif
}
void QueueUITask(void (*task)(void *param), void *param)
void QeueUITask(void (*task)(void *param), void *param)
{
obs_queue_task(OBS_TASK_UI, task, param, false);
}

View File

@@ -35,7 +35,7 @@ EXPORT void DisplayTrayMessage(const QString &title, const QString &msg,
EXPORT std::string GetThemeTypeName();
EXPORT QWidget *GetSettingsWindow();
EXPORT void QueueUITask(void (*task)(void *param), void *param);
void QeueUITask(void (*task)(void *param), void *param);
bool IsCursorInWidgetArea(QWidget *widget);

View File

@@ -8,9 +8,6 @@
#include "ui-helpers.hpp"
#include "variable.hpp"
#include <obs-frontend-api.h>
#include <QLayout>
#include <QTimer>
namespace advss {
@@ -19,62 +16,14 @@ static bool registerTab();
static void setupTab(QTabWidget *);
static bool registerTabDone = registerTab();
static VariableTable *dockWidget = nullptr;
static bool addDock = false;
static VariableTable::Settings tabSettings;
static VariableTable::Settings dockSettings;
static void save(obs_data_t *data)
{
tabSettings.Save(data, "tabSettings");
dockSettings.Save(data, "dockSettings");
obs_data_set_bool(data, "addVariablesDock", addDock);
}
static void enableDock(bool enable)
{
if (OBSIsShuttingDown()) {
return;
}
obs_frontend_remove_dock("advss-variable-dock");
addDock = enable;
if (!addDock) {
return;
}
dockWidget = new VariableTable(dockSettings);
dockWidget->HideDockOptions();
if (obs_frontend_add_dock_by_id(
"advss-variable-dock",
obs_module_text("AdvSceneSwitcher.variableTab.title"),
dockWidget)) {
return;
}
blog(LOG_INFO, "failed to register variable dock!");
dockWidget->deleteLater();
dockWidget = nullptr;
}
static void load(obs_data_t *data)
{
tabSettings.Load(data, "tabSettings");
dockSettings.Load(data, "dockSettings");
enableDock(obs_data_get_bool(data, "addVariablesDock"));
}
static VariableTable *tabWidget = nullptr;
static bool registerTab()
{
AddPluginInitStep([]() {
AddSetupTabCallback("variableTab",
VariableTable::CreateTabTable, setupTab);
AddSetupTabCallback("variableTab", VariableTable::Create,
setupTab);
});
AddSaveStep(save);
AddLoadStep(load);
return true;
}
@@ -85,14 +34,10 @@ static void setTabVisible(QTabWidget *tabWidget, bool visible)
obs_module_text("AdvSceneSwitcher.variableTab.title"));
}
VariableTable *VariableTable::CreateTabTable()
VariableTable *VariableTable::Create()
{
return new VariableTable(tabSettings);
}
void VariableTable::HideDockOptions() const
{
_addDock->hide();
tabWidget = new VariableTable();
return tabWidget;
}
void VariableTable::Add()
@@ -218,14 +163,15 @@ static void updateVariableStatus(QTableWidget *table)
}
}
static void openSettingsDialog(VariableTable *table)
static void openSettingsDialog()
{
auto selectedRows = table->Table()->selectionModel()->selectedRows();
auto selectedRows =
tabWidget->Table()->selectionModel()->selectedRows();
if (selectedRows.empty()) {
return;
}
auto cell = table->Table()->item(selectedRows.last().row(), 0);
auto cell = tabWidget->Table()->item(selectedRows.last().row(), 0);
if (!cell) {
return;
}
@@ -248,14 +194,15 @@ static void openSettingsDialog(VariableTable *table)
void VariableTable::Remove()
{
auto selectedRows = Table()->selectionModel()->selectedRows();
auto selectedRows =
tabWidget->Table()->selectionModel()->selectedRows();
if (selectedRows.empty()) {
return;
}
QStringList varNames;
for (const auto &row : selectedRows) {
auto cell = Table()->item(row.row(), 0);
auto cell = tabWidget->Table()->item(row.row(), 0);
if (!cell) {
continue;
}
@@ -288,52 +235,7 @@ void VariableTable::Remove()
}
}
void VariableTable::Filter()
{
const auto itemMatches = [&](const QTableWidgetItem *item) {
if (!item) {
return false;
}
if (_settings.searchString.empty()) {
return true;
}
const auto text = item->text();
if (_settings.regex.Enabled()) {
return _settings.regex.Matches(text.toStdString(),
_settings.searchString);
}
return text.contains(
QString::fromStdString(_settings.searchString),
Qt::CaseInsensitive);
};
for (int row = 0; row < Table()->rowCount(); ++row) {
bool match = false;
if (_settings.searchType == Settings::SearchType::ALL) {
for (int col = 0; col < Table()->columnCount(); ++col) {
if (itemMatches(Table()->item(row, col))) {
match = true;
break;
}
}
} else {
if (itemMatches(Table()->item(
row,
static_cast<int>(_settings.searchType)))) {
match = true;
}
}
Table()->setRowHidden(row, !match);
}
}
VariableTable::VariableTable(Settings &settings, QWidget *parent)
VariableTable::VariableTable(QTabWidget *parent)
: ResourceTable(
parent, obs_module_text("AdvSceneSwitcher.variableTab.help"),
obs_module_text(
@@ -351,113 +253,13 @@ VariableTable::VariableTable(Settings &settings, QWidget *parent)
"AdvSceneSwitcher.variableTab.lastUsed.header")
<< obs_module_text(
"AdvSceneSwitcher.variableTab.lastChanged.header"),
[this]() { openSettingsDialog(this); }),
_searchField(new QLineEdit(this)),
_clear(new QPushButton(this)),
_searchType(new QComboBox(this)),
_regexWidget(new RegexConfigWidget(this)),
_addDock(new QCheckBox(
obs_module_text("AdvSceneSwitcher.variableTab.addDock"))),
_settings(settings)
openSettingsDialog)
{
_clear->setMaximumWidth(22);
SetButtonIcon(_clear, GetThemeTypeName() == "Light"
? "theme:Light/close.svg"
: "theme:Dark/close.svg");
_clear->setToolTip(
obs_module_text("AdvSceneSwitcher.variableTab.clear"));
_clear->setDisabled(_settings.searchString.empty());
connect(_clear, &QPushButton::clicked, this, [this]() {
_searchField->setText("");
_clear->setDisabled(true);
});
for (const auto &variable : GetVariables()) {
auto v = std::static_pointer_cast<Variable>(variable);
AddItemTableRow(Table(), getCellLabels(v.get()));
}
connect(Table(), &QTableWidget::itemChanged, this,
[this]() { Filter(); });
_searchField->setPlaceholderText(obs_module_text(
("AdvSceneSwitcher.variableTab.search.placeholder")));
_searchField->setText(QString::fromStdString(_settings.searchString));
connect(_searchField, &QLineEdit::textChanged, this,
[this](const QString &text) {
_settings.searchString = text.toStdString();
_clear->setDisabled(text.isEmpty());
Filter();
});
_searchType->addItem(
obs_module_text("AdvSceneSwitcher.variableTab.search.all"),
Settings::SearchType::ALL);
_searchType->addItem(
obs_module_text("AdvSceneSwitcher.variableTab.search.name"),
Settings::SearchType::NAME);
_searchType->addItem(
obs_module_text("AdvSceneSwitcher.variableTab.search.value"),
Settings::SearchType::VALUE);
_searchType->setCurrentIndex(
_searchType->findData(_settings.searchType));
connect(_searchType, &QComboBox::currentIndexChanged, this, [this]() {
_settings.searchType = static_cast<Settings::SearchType>(
_searchType->currentData().toInt());
});
_regexWidget->SetRegexConfig(_settings.regex);
connect(_regexWidget, &RegexConfigWidget::RegexConfigChanged, this,
[this](const RegexConfig &regex) {
_settings.regex = regex;
Filter();
});
_addDock->setChecked(addDock);
connect(_addDock, &QCheckBox::stateChanged, this,
[this](int checked) { enableDock(checked); });
QWidget::connect(VariableSignalManager::Instance(),
&VariableSignalManager::Rename, this,
[this](const QString &oldName,
const QString &newName) {
RenameItemTableRow(Table(), oldName, newName);
});
QWidget::connect(
VariableSignalManager::Instance(), &VariableSignalManager::Add,
this, [this](const QString &name) {
AddItemTableRow(
Table(),
getCellLabels(GetVariableByQString(name)));
SetHelpVisible(false);
HighlightAddButton(false);
});
QWidget::connect(VariableSignalManager::Instance(),
&VariableSignalManager::Remove, this,
[this](const QString &name) {
RemoveItemTableRow(Table(), name);
if (Table()->rowCount() == 0) {
SetHelpVisible(true);
HighlightAddButton(true);
}
});
auto timer = new QTimer(this);
timer->setInterval(1000);
QWidget::connect(timer, &QTimer::timeout,
[this]() { updateVariableStatus(Table()); });
timer->start();
auto searchLayout = new QHBoxLayout();
searchLayout->addWidget(_searchField);
searchLayout->addWidget(_clear);
searchLayout->addWidget(_searchType);
searchLayout->addWidget(_regexWidget);
searchLayout->addWidget(_addDock);
qobject_cast<QVBoxLayout *>(layout())->insertLayout(0, searchLayout);
SetHelpVisible(GetVariables().empty());
}
@@ -468,26 +270,36 @@ static void setupTab(QTabWidget *tab)
}
QWidget::connect(VariableSignalManager::Instance(),
&VariableSignalManager::Add, tab,
[tab](const QString &) { setTabVisible(tab, true); });
}
&VariableSignalManager::Rename, tab,
[](const QString &oldName, const QString &newName) {
RenameItemTableRow(tabWidget->Table(), oldName,
newName);
});
QWidget::connect(
VariableSignalManager::Instance(), &VariableSignalManager::Add,
tab, [tab](const QString &name) {
AddItemTableRow(
tabWidget->Table(),
getCellLabels(GetVariableByQString(name)));
tabWidget->SetHelpVisible(false);
tabWidget->HighlightAddButton(false);
setTabVisible(tab, true);
});
QWidget::connect(VariableSignalManager::Instance(),
&VariableSignalManager::Remove, tab,
[](const QString &name) {
RemoveItemTableRow(tabWidget->Table(), name);
if (tabWidget->Table()->rowCount() == 0) {
tabWidget->SetHelpVisible(true);
tabWidget->HighlightAddButton(true);
}
});
void VariableTable::Settings::Save(obs_data_t *data, const char *name)
{
OBSDataAutoRelease settings = obs_data_create();
obs_data_set_int(settings, "searchType", static_cast<int>(searchType));
obs_data_set_string(settings, "searchString", searchString.c_str());
regex.Save(settings);
obs_data_set_obj(data, name, settings);
}
void VariableTable::Settings::Load(obs_data_t *data, const char *name)
{
OBSDataAutoRelease settings = obs_data_get_obj(data, name);
searchType = static_cast<SearchType>(
obs_data_get_int(settings, "searchType"));
searchString = obs_data_get_string(settings, "searchString");
regex.Load(settings);
auto timer = new QTimer(tabWidget);
timer->setInterval(1000);
QWidget::connect(timer, &QTimer::timeout,
[]() { updateVariableStatus(tabWidget->Table()); });
timer->start();
}
} // namespace advss

View File

@@ -1,8 +1,5 @@
#pragma once
#include "resource-table.hpp"
#include "regex-config.hpp"
#include <QComboBox>
namespace advss {
@@ -10,34 +7,14 @@ class VariableTable final : public ResourceTable {
Q_OBJECT
public:
struct Settings {
void Save(obs_data_t *data, const char *name);
void Load(obs_data_t *data, const char *name);
enum SearchType { ALL = -1, NAME = 0, VALUE = 1 };
SearchType searchType = SearchType::ALL;
std::string searchString;
RegexConfig regex;
};
VariableTable(Settings &settings, QWidget *parent = nullptr);
static VariableTable *CreateTabTable();
void HideDockOptions() const;
static VariableTable *Create();
private slots:
void Add();
void Remove();
void Filter();
private:
QLineEdit *_searchField;
QPushButton *_clear;
QComboBox *_searchType;
RegexConfigWidget *_regexWidget;
QCheckBox *_addDock;
Settings &_settings;
VariableTable(QTabWidget *parent = nullptr);
};
} // namespace advss

View File

@@ -456,7 +456,7 @@ void ImportVariables(obs_data_t *data)
obs_data_array_release(array);
QueueUITask(signalImportedVariables, importedVars);
QeueUITask(signalImportedVariables, importedVars);
}
std::chrono::high_resolution_clock::time_point GetLastVariableChangeTime()

View File

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

View File

@@ -601,10 +601,10 @@ static void populateHotkeyNames(QComboBox *list,
MacroActionHotkeyEdit::MacroActionHotkeyEdit(
QWidget *parent, std::shared_ptr<MacroActionHotkey> entryData)
: QWidget(parent),
_actionType(new FilterComboBox()),
_hotkeyType(new FilterComboBox()),
_obsHotkeys(new FilterComboBox()),
_keys(new FilterComboBox()),
_actionType(new QComboBox()),
_hotkeyType(new QComboBox()),
_obsHotkeys(new QComboBox()),
_keys(new QComboBox()),
_leftShift(new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftShift"))),
_rightShift(new QCheckBox(obs_module_text(

View File

@@ -85,14 +85,11 @@ private slots:
void DurationChanged(const Duration &);
void OnlySendToOBSChanged(int state);
private:
void RepopulateOBSHotkeySelection();
void SetWidgetVisibility();
FilterComboBox *_actionType;
FilterComboBox *_hotkeyType;
FilterComboBox *_obsHotkeys;
FilterComboBox *_keys;
protected:
QComboBox *_actionType;
QComboBox *_hotkeyType;
QComboBox *_obsHotkeys;
QComboBox *_keys;
QCheckBox *_leftShift;
QCheckBox *_rightShift;
QCheckBox *_leftCtrl;
@@ -104,10 +101,15 @@ private:
DurationSelection *_duration;
QCheckBox *_onlySendToOBS;
QLabel *_noKeyPressSimulationWarning;
QHBoxLayout *_entryLayout;
QHBoxLayout *_keyConfigLayout;
std::shared_ptr<MacroActionHotkey> _entryData;
private:
void RepopulateOBSHotkeySelection();
void SetWidgetVisibility();
QHBoxLayout *_entryLayout;
QHBoxLayout *_keyConfigLayout;
bool _loading = true;
};

View File

@@ -137,51 +137,31 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene,
bool MacroActionSwitchScene::PerformAction()
{
OBSWeakSource sceneToSwitchTo;
OBSWeakCanvas canvasToSwitchFor;
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
OBSWeakCanvas mainCanvasWeak = OBSGetWeakRef(mainCanvas);
const auto getActiveSceneHelper = [this](const OBSWeakCanvas &canvas) {
OBSWeakSource activeScene;
if (IsMainCanvas(canvas) && _sceneType == SceneType::PREVIEW) {
OBSSourceAutoRelease previewSource =
obs_frontend_get_current_preview_scene();
return OBSGetWeakRef(previewSource);
} else {
return GetActiveCanvasScene(canvas);
}
};
OBSWeakCanvas canvas;
switch (_action) {
case Action::SCENE_NAME:
sceneToSwitchTo = _scene.GetScene();
canvasToSwitchFor = _scene.GetCanvas();
canvas = _scene.GetCanvas();
break;
case Action::SCENE_LIST_NEXT: {
canvasToSwitchFor = _canvas ? _canvas : mainCanvasWeak;
const auto activeScene =
getActiveSceneHelper(canvasToSwitchFor);
const int currentIndex =
GetIndexOfScene(canvasToSwitchFor, activeScene);
sceneToSwitchTo =
GetSceneAtIndex(canvasToSwitchFor, currentIndex + 1);
const auto activeScene = GetActiveCanvasScene(_canvas);
const int currentIndex = GetIndexOfScene(_canvas, activeScene);
sceneToSwitchTo = GetSceneAtIndex(_canvas, currentIndex + 1);
canvas = _canvas;
break;
}
case Action::SCENE_LIST_PREVIOUS: {
canvasToSwitchFor = _canvas ? _canvas : mainCanvasWeak;
const auto activeScene =
getActiveSceneHelper(canvasToSwitchFor);
const int currentIndex =
GetIndexOfScene(canvasToSwitchFor, activeScene);
sceneToSwitchTo =
GetSceneAtIndex(canvasToSwitchFor, currentIndex - 1);
const auto activeScene = GetActiveCanvasScene(_canvas);
const int currentIndex = GetIndexOfScene(_canvas, activeScene);
sceneToSwitchTo = GetSceneAtIndex(_canvas, currentIndex - 1);
canvas = _canvas;
break;
}
case Action::SCENE_LIST_INDEX: {
canvasToSwitchFor = _canvas ? _canvas : mainCanvasWeak;
sceneToSwitchTo = GetSceneAtIndex(canvasToSwitchFor,
_index.GetValue() - 1);
sceneToSwitchTo =
GetSceneAtIndex(_canvas, _index.GetValue() - 1);
canvas = _canvas;
break;
}
default:
@@ -196,12 +176,11 @@ bool MacroActionSwitchScene::PerformAction()
}
auto transition = _transition.GetTransition();
SwitchScene(
{sceneToSwitchTo, transition, (int)(_duration.Milliseconds())},
obs_frontend_preview_program_mode_active(), canvasToSwitchFor);
SwitchScene({sceneToSwitchTo, transition,
(int)(_duration.Milliseconds())},
obs_frontend_preview_program_mode_active(), canvas);
if (_blockUntilTransitionDone && sceneToSwitchTo &&
sceneToSwitchTo != GetCurrentScene() &&
IsMainCanvas(canvasToSwitchFor)) {
sceneToSwitchTo != GetCurrentScene() && IsMainCanvas(canvas)) {
return WaitForTransition(sceneToSwitchTo, transition);
}
return true;
@@ -413,7 +392,6 @@ void MacroActionSwitchSceneEdit::SetWidgetData() const
_sceneTypes->setCurrentIndex(_sceneTypes->findData(
static_cast<int>(_entryData->_sceneType)));
_scenes->SetScene(_entryData->_scene);
_canvas->SetCanvas(_entryData->_canvas);
_transitions->SetTransition(_entryData->_transition);
_duration->SetDuration(_entryData->_duration);
_blockUntilTransitionDone->setChecked(
@@ -453,7 +431,7 @@ void MacroActionSwitchSceneEdit::SceneSelectionCanvasChanged(
SetWidgetVisibility();
}
void MacroActionSwitchSceneEdit::ActionChanged(int)
void MacroActionSwitchSceneEdit::ActionChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionSwitchScene::Action>(
@@ -583,17 +561,9 @@ void MacroActionSwitchSceneEdit::SetWidgetVisibility()
{{"{{transitions}}", _transitions},
{"{{duration}}", _duration}});
OBSWeakCanvas canvas;
if (action == MacroActionSwitchScene::Action::SCENE_NAME) {
canvas = _entryData->_scene.GetCanvas();
} else {
if (_entryData->_canvas) {
canvas = _entryData->_canvas;
} else {
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
canvas = OBSGetWeakRef(mainCanvas);
}
}
const auto canvas = action == MacroActionSwitchScene::Action::SCENE_NAME
? _entryData->_scene.GetCanvas()
: _entryData->_canvas;
const bool isMainCanvas = IsMainCanvas(canvas);
SetLayoutVisible(_transitionLayout, isMainCanvas);
_blockUntilTransitionDone->setVisible(isMainCanvas);
@@ -610,7 +580,7 @@ void MacroActionSwitchSceneEdit::SetWidgetVisibility()
_notSupportedWarning->setText(
fmt.arg(QString::fromStdString(GetWeakCanvasName(canvas))));
_notSupportedWarning->setVisible(
!isMainCanvas &&
!IsMainCanvas(canvas) &&
_entryData->_sceneType ==
MacroActionSwitchScene::SceneType::PREVIEW);

View File

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

View File

@@ -7,8 +7,6 @@
#include <obs-frontend-api.h>
#include <QMainWindow>
namespace advss {
const std::string MacroActionSource::id = "source";
@@ -39,12 +37,6 @@ const static std::map<MacroActionSource::Action, std::string> actionTypes = {
"AdvSceneSwitcher.action.source.type.openFilterDialog"},
{MacroActionSource::Action::OPEN_PROPERTIES_DIALOG,
"AdvSceneSwitcher.action.source.type.openPropertiesDialog"},
{MacroActionSource::Action::CLOSE_INTERACTION_DIALOG,
"AdvSceneSwitcher.action.source.type.closeInteractionDialog"},
{MacroActionSource::Action::CLOSE_FILTER_DIALOG,
"AdvSceneSwitcher.action.source.type.closeFilterDialog"},
{MacroActionSource::Action::CLOSE_PROPERTIES_DIALOG,
"AdvSceneSwitcher.action.source.type.closePropertiesDialog"},
};
const static std::map<obs_deinterlace_mode, std::string> deinterlaceModes = {
@@ -116,81 +108,9 @@ static bool isInteractable(obs_source_t *source)
return (flags & OBS_SOURCE_INTERACTION) != 0;
}
static void closeSourceDialog(obs_source_t *source, bool accept,
const char *className)
{
if (!source) {
return;
}
auto mainWindow =
reinterpret_cast<QMainWindow *>(obs_frontend_get_main_window());
if (!mainWindow) {
return;
}
for (const auto widget : mainWindow->children()) {
auto dialog = qobject_cast<QDialog *>(widget);
if (!dialog) {
continue;
}
if (dialog->metaObject()->className() != QString(className)) {
continue;
}
const char *name = obs_source_get_name(source);
if (!name) {
continue;
}
if (!dialog->windowTitle().contains(QString(name))) {
continue;
}
if (accept) {
dialog->accept();
return;
}
// dialog->reject() will ask for confirmation
// Try to avoid this by finding and pressing the cancel button
for (const auto widget : dialog->children()) {
auto buttons = qobject_cast<QDialogButtonBox *>(widget);
if (!buttons) {
continue;
}
for (auto *button : buttons->buttons()) {
if (buttons->buttonRole(button) ==
QDialogButtonBox::RejectRole) {
button->click();
break;
}
}
}
}
}
template<typename F> void QueueUITaskLambda(F &&func)
{
using FnType = std::decay_t<F>;
auto *heapFunc = new FnType(std::forward<F>(func));
QueueUITask(
[](void *param) {
std::unique_ptr<FnType> fn(
static_cast<FnType *>(param));
(*fn)();
},
heapFunc);
}
bool MacroActionSource::PerformAction()
{
OBSSource s = obs_weak_source_get_source(_source.GetSource());
obs_source_release(s);
auto s = obs_weak_source_get_source(_source.GetSource());
switch (_action) {
case Action::ENABLE:
obs_source_set_enabled(s, true);
@@ -252,31 +172,10 @@ bool MacroActionSource::PerformAction()
case Action::OPEN_PROPERTIES_DIALOG:
obs_frontend_open_source_properties(s);
break;
case Action::CLOSE_INTERACTION_DIALOG:
if (!isInteractable(s)) {
// Nothing to do
break;
}
QueueUITaskLambda([&]() {
closeSourceDialog(s, true, "OBSBasicInteraction");
});
break;
case Action::CLOSE_FILTER_DIALOG:
QueueUITaskLambda([&]() {
closeSourceDialog(s, _acceptDialog, "OBSBasicFilters");
});
break;
case Action::CLOSE_PROPERTIES_DIALOG:
QueueUITaskLambda([&]() {
closeSourceDialog(s, _acceptDialog,
"OBSBasicProperties");
});
break;
default:
break;
}
obs_source_release(s);
return true;
}
@@ -308,7 +207,6 @@ bool MacroActionSource::Save(obs_data_t *obj) const
static_cast<int>(_deinterlaceMode));
obs_data_set_int(obj, "deinterlaceOrder",
static_cast<int>(_deinterlaceOrder));
obs_data_set_bool(obj, "acceptDialog", _acceptDialog);
return true;
}
@@ -333,7 +231,6 @@ bool MacroActionSource::Load(obs_data_t *obj)
obs_data_get_int(obj, "deinterlaceMode"));
_deinterlaceOrder = static_cast<obs_deinterlace_field_order>(
obs_data_get_int(obj, "deinterlaceOrder"));
_acceptDialog = obs_data_get_bool(obj, "acceptDialog");
return true;
}
@@ -410,9 +307,7 @@ MacroActionSourceEdit::MacroActionSourceEdit(
_warning(new QLabel(
obs_module_text("AdvSceneSwitcher.action.source.warning"))),
_refreshSettingSelection(new QPushButton(
obs_module_text("AdvSceneSwitcher.action.source.refresh"))),
_acceptDialog(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.source.dialog.accept")))
obs_module_text("AdvSceneSwitcher.action.source.refresh")))
{
populateActionSelection(_actions);
@@ -452,8 +347,6 @@ MacroActionSourceEdit::MacroActionSourceEdit(
SLOT(SelectionChanged(const SourceSetting &)));
QWidget::connect(_refreshSettingSelection, SIGNAL(clicked()), this,
SLOT(RefreshVariableSourceSelectionValue()));
QWidget::connect(_acceptDialog, SIGNAL(stateChanged(int)), this,
SLOT(AcceptDialogChanged(int)));
auto entryLayout = new QHBoxLayout;
entryLayout->setContentsMargins(0, 0, 0, 0);
@@ -481,7 +374,6 @@ MacroActionSourceEdit::MacroActionSourceEdit(
mainLayout->addLayout(_settingsLayout);
mainLayout->addWidget(_warning);
mainLayout->addWidget(_settingsString);
mainLayout->addWidget(_acceptDialog);
auto buttonLayout = new QHBoxLayout;
buttonLayout->setContentsMargins(0, 0, 0, 0);
buttonLayout->addWidget(_getSettings);
@@ -514,7 +406,6 @@ void MacroActionSourceEdit::UpdateEntryData()
static_cast<int>(_entryData->_settingsInputMethod)));
_tempVars->SetVariable(_entryData->_tempVar);
_manualSettingValue->setPlainText(_entryData->_manualSettingValue);
_acceptDialog->setChecked(_entryData->_acceptDialog);
SetWidgetVisibility();
}
@@ -640,12 +531,6 @@ void MacroActionSourceEdit::RefreshVariableSourceSelectionValue()
_sourceSettings->SetSource(_entryData->_source.GetSource());
}
void MacroActionSourceEdit::AcceptDialogChanged(int state)
{
GUARD_LOADING_AND_LOCK();
_entryData->_acceptDialog = state;
}
static QString GetIndividualListEntryName()
{
static const auto matchesInput =
@@ -728,12 +613,6 @@ void MacroActionSourceEdit::SetWidgetVisibility()
_entryData->_source.GetType() ==
SourceSelection::Type::VARIABLE);
_acceptDialog->setVisible(
_entryData->_action ==
MacroActionSource::Action::CLOSE_FILTER_DIALOG ||
_entryData->_action ==
MacroActionSource::Action::CLOSE_PROPERTIES_DIALOG);
adjustSize();
updateGeometry();
}

View File

@@ -5,10 +5,9 @@
#include "source-selection.hpp"
#include "source-setting.hpp"
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
namespace advss {
@@ -34,7 +33,6 @@ public:
OBS_DEINTERLACE_FIELD_ORDER_TOP;
TempVariableRef _tempVar;
SourceSetting _setting;
bool _acceptDialog = false;
enum class Action {
ENABLE,
@@ -47,9 +45,6 @@ public:
OPEN_INTERACTION_DIALOG,
OPEN_FILTER_DIALOG,
OPEN_PROPERTIES_DIALOG,
CLOSE_INTERACTION_DIALOG,
CLOSE_FILTER_DIALOG,
CLOSE_PROPERTIES_DIALOG,
};
Action _action = Action::SETTINGS;
@@ -96,7 +91,6 @@ private slots:
void SelectionChanged(const SourceSetting &);
void ManualSettingsValueChanged();
void RefreshVariableSourceSelectionValue();
void AcceptDialogChanged(int);
signals:
void HeaderInfoChanged(const QString &);
@@ -118,7 +112,6 @@ private:
QComboBox *_deinterlaceOrder;
QLabel *_warning;
QPushButton *_refreshSettingSelection;
QCheckBox *_acceptDialog;
std::shared_ptr<MacroActionSource> _entryData;
bool _loading = true;

View File

@@ -2,6 +2,7 @@
#include "layout-helpers.hpp"
#include <obs-frontend-api.h>
#include <util/config-file.h>
namespace advss {
@@ -13,16 +14,64 @@ bool MacroActionSudioMode::_registered = MacroActionFactory::Register(
"AdvSceneSwitcher.action.studioMode"});
const static std::map<MacroActionSudioMode::Action, std::string> actionTypes = {
{MacroActionSudioMode::Action::SWAP_SCENE,
"AdvSceneSwitcher.action.studioMode.type.swap"},
{MacroActionSudioMode::Action::SET_SCENE,
"AdvSceneSwitcher.action.studioMode.type.setScene"},
{MacroActionSudioMode::Action::TRANSITION,
"AdvSceneSwitcher.action.studioMode.type.transition"},
{MacroActionSudioMode::Action::TRANSITION_WITH_SWAP,
"AdvSceneSwitcher.action.studioMode.type.transitionWithSwap"},
{MacroActionSudioMode::Action::ENALBE_TRANSITION_SWAP,
"AdvSceneSwitcher.action.studioMode.type.transitionSwap.enable"},
{MacroActionSudioMode::Action::DISABLE_TRANSITION_SWAP,
"AdvSceneSwitcher.action.studioMode.type.transitionSwap.disable"},
{MacroActionSudioMode::Action::TOGGLE_TRANSITION_SWAP,
"AdvSceneSwitcher.action.studioMode.type.transitionSwap.toggle"},
{MacroActionSudioMode::Action::ENALBE_DUPLICATE_SCENE,
"AdvSceneSwitcher.action.studioMode.type.duplicateScene.enable"},
{MacroActionSudioMode::Action::DISABLE_DUPLICATE_SCENE,
"AdvSceneSwitcher.action.studioMode.type.duplicateScene.disable"},
{MacroActionSudioMode::Action::TOGGLE_DUPLICATE_SCENE,
"AdvSceneSwitcher.action.studioMode.type.duplicateScene.toggle"},
{MacroActionSudioMode::Action::ENALBE_DUPLICATE_SOURCE,
"AdvSceneSwitcher.action.studioMode.type.duplicateSource.enable"},
{MacroActionSudioMode::Action::DISABLE_DUPLICATE_SOURCE,
"AdvSceneSwitcher.action.studioMode.type.duplicateSource.disable"},
{MacroActionSudioMode::Action::TOGGLE_DUPLICATE_SOURCE,
"AdvSceneSwitcher.action.studioMode.type.duplicateSource.toggle"},
{MacroActionSudioMode::Action::ENABLE_STUDIO_MODE,
"AdvSceneSwitcher.action.studioMode.type.enable"},
{MacroActionSudioMode::Action::DISABLE_STUDIO_MODE,
"AdvSceneSwitcher.action.studioMode.type.disable"},
{MacroActionSudioMode::Action::TOGGLE_STUDIO_MODE,
"AdvSceneSwitcher.action.studioMode.type.toggle"},
{MacroActionSudioMode::Action::SET_SCENE,
"AdvSceneSwitcher.action.studioMode.type.setScene"},
};
template<typename Func, typename... Args>
static void setConfigValueHelper(Func func, Args... args)
{
auto config = obs_frontend_get_user_config();
func(config, args...);
if (config_save(config) != CONFIG_SUCCESS) {
blog(LOG_WARNING, "failed to save user config!");
}
}
template<typename Func, typename... Args>
static auto getConfigValueHelper(Func func, Func defaultFunc, Args... args)
-> std::optional<std::invoke_result_t<Func, config_t *, Args...>>
{
using Ret = std::invoke_result_t<Func, config_t *, Args...>;
auto config = obs_frontend_get_user_config();
if (config_has_user_value(config, args...)) {
return func(config, args...);
}
if (config_has_default_value(config, args...)) {
return defaultFunc(config, args...);
}
return std::optional<Ret>{};
}
// Calling obs_frontend_set_preview_program_mode() directly from a thread that
// is not the main OBS UI thread will lead to undefined behaviour, so we have
// to use this helper function instead - copied from obs-websocket plugin
@@ -42,14 +91,78 @@ static void enableStudioMode(bool enable)
bool MacroActionSudioMode::PerformAction()
{
obs_frontend_get_user_config();
switch (_action) {
case Action::SWAP_SCENE:
case Action::TRANSITION:
obs_frontend_preview_program_trigger_transition();
break;
case Action::SET_SCENE: {
auto s = obs_weak_source_get_source(_scene.GetScene());
obs_frontend_set_current_preview_scene(s);
obs_source_release(s);
case Action::TRANSITION_WITH_SWAP: {
auto swapEnabled = getConfigValueHelper(config_get_bool,
config_get_default_bool,
"BasicWindow",
"SwapScenesMode");
if (swapEnabled.has_value() && *swapEnabled == true) {
obs_frontend_preview_program_trigger_transition();
break;
}
setConfigValueHelper(config_set_bool, "BasicWindow",
"SwapScenesMode", true);
obs_frontend_preview_program_trigger_transition();
setConfigValueHelper(config_set_bool, "BasicWindow",
"SwapScenesMode", false);
break;
}
case Action::ENALBE_TRANSITION_SWAP:
setConfigValueHelper(config_set_bool, "BasicWindow",
"SwapScenesMode", true);
break;
case Action::DISABLE_TRANSITION_SWAP:
setConfigValueHelper(config_set_bool, "BasicWindow",
"SwapScenesMode", false);
break;
case Action::TOGGLE_TRANSITION_SWAP: {
const auto enabled = getConfigValueHelper(
config_get_bool, config_get_default_bool, "BasicWindow",
"SwapScenesMode");
setConfigValueHelper(config_set_bool, "BasicWindow",
"SwapScenesMode",
enabled.has_value() && !*enabled);
break;
}
case Action::ENALBE_DUPLICATE_SCENE:
setConfigValueHelper(config_set_bool, "BasicWindow",
"SceneDuplicationMode", true);
break;
case Action::DISABLE_DUPLICATE_SCENE:
setConfigValueHelper(config_set_bool, "BasicWindow",
"SceneDuplicationMode", false);
break;
case Action::TOGGLE_DUPLICATE_SCENE: {
const auto enabled = getConfigValueHelper(
config_get_bool, config_get_default_bool, "BasicWindow",
"SceneDuplicationMode");
setConfigValueHelper(config_set_bool, "BasicWindow",
"SceneDuplicationMode",
enabled.has_value() && !*enabled);
break;
}
case Action::ENALBE_DUPLICATE_SOURCE:
setConfigValueHelper(config_set_bool, "BasicWindow",
"EditPropertiesMode", true);
break;
case Action::DISABLE_DUPLICATE_SOURCE:
setConfigValueHelper(config_set_bool, "BasicWindow",
"EditPropertiesMode", false);
break;
case Action::TOGGLE_DUPLICATE_SOURCE: {
const auto enabled = getConfigValueHelper(
config_get_bool, config_get_default_bool, "BasicWindow",
"EditPropertiesMode");
setConfigValueHelper(config_set_bool, "BasicWindow",
"EditPropertiesMode",
enabled.has_value() && !*enabled);
break;
}
case Action::ENABLE_STUDIO_MODE:
@@ -58,6 +171,15 @@ bool MacroActionSudioMode::PerformAction()
case Action::DISABLE_STUDIO_MODE:
enableStudioMode(false);
break;
case Action::TOGGLE_STUDIO_MODE:
enableStudioMode(!obs_frontend_preview_program_mode_active());
break;
case Action::SET_SCENE: {
auto s = obs_weak_source_get_source(_scene.GetScene());
obs_frontend_set_current_preview_scene(s);
obs_source_release(s);
break;
}
default:
break;
}
@@ -82,6 +204,7 @@ bool MacroActionSudioMode::Save(obs_data_t *obj) const
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_scene.Save(obj);
obs_data_set_int(obj, "version", 1);
return true;
}
@@ -90,6 +213,34 @@ bool MacroActionSudioMode::Load(obs_data_t *obj)
MacroAction::Load(obj);
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_scene.Load(obj);
if (!obs_data_has_user_value(obj, "version")) {
enum class OldAction {
SWAP_SCENE,
SET_SCENE,
ENABLE_STUDIO_MODE,
DISABLE_STUDIO_MODE,
};
switch (static_cast<OldAction>(
obs_data_get_int(obj, "action"))) {
case OldAction::SWAP_SCENE:
_action = Action::TRANSITION_WITH_SWAP;
break;
case OldAction::SET_SCENE:
_action = Action::SET_SCENE;
break;
case OldAction::ENABLE_STUDIO_MODE:
_action = Action::ENABLE_STUDIO_MODE;
break;
case OldAction::DISABLE_STUDIO_MODE:
_action = Action::DISABLE_STUDIO_MODE;
break;
default:
break;
}
}
return true;
}
@@ -136,15 +287,11 @@ MacroActionStudioModeEdit::MacroActionStudioModeEdit(
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{scenes}}", _scenes},
};
QHBoxLayout *mainLayout = new QHBoxLayout;
auto layout = new QHBoxLayout;
PlaceWidgets(
obs_module_text("AdvSceneSwitcher.action.studioMode.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
layout, {{"{{actions}}", _actions}, {"{{scenes}}", _scenes}});
setLayout(layout);
_entryData = entryData;
UpdateEntryData();

View File

@@ -18,13 +18,28 @@ public:
void ResolveVariablesToFixedValues();
enum class Action {
SWAP_SCENE,
SET_SCENE, // TODO: Remove in future version as the
// functionality moved to the scene switch action
ENABLE_STUDIO_MODE,
DISABLE_STUDIO_MODE,
TRANSITION = 10,
TRANSITION_WITH_SWAP = 20,
ENALBE_TRANSITION_SWAP = 30,
DISABLE_TRANSITION_SWAP = 40,
TOGGLE_TRANSITION_SWAP = 50,
ENALBE_DUPLICATE_SCENE = 60,
DISABLE_DUPLICATE_SCENE = 70,
TOGGLE_DUPLICATE_SCENE = 80,
ENALBE_DUPLICATE_SOURCE = 90,
DISABLE_DUPLICATE_SOURCE = 100,
TOGGLE_DUPLICATE_SOURCE = 110,
ENABLE_STUDIO_MODE = 120,
DISABLE_STUDIO_MODE = 130,
TOGGLE_STUDIO_MODE = 140,
SET_SCENE = 1000, // Deprecated
};
Action _action = Action::SWAP_SCENE;
Action _action = Action::TRANSITION;
SceneSelection _scene;
private:

View File

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

View File

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

View File

@@ -154,7 +154,7 @@ void MacroConditionProcessEdit::ProcessChanged(const QString &text)
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionProcessEdit::showEvent(QShowEvent *)
void MacroConditionProcessEdit::showEvent(QShowEvent *event)
{
const QSignalBlocker b(_processSelection);
const auto text = _processSelection->currentText();

View File

@@ -31,10 +31,6 @@ static int currentRecordingDurationInSeconds = 0;
bool MacroConditionRecord::CheckCondition()
{
char *lastSavePath = obs_frontend_get_last_recording();
SetTempVarValue("lastSavePath", lastSavePath ? lastSavePath : "");
bfree(lastSavePath);
switch (_condition) {
case Condition::STOP:
return !obs_frontend_recording_active();
@@ -78,14 +74,6 @@ void MacroConditionRecord::SetCondition(Condition condition)
void MacroConditionRecord::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar(
"lastSavePath",
obs_module_text(
"AdvSceneSwitcher.tempVar.recording.lastSavePath"),
obs_module_text(
"AdvSceneSwitcher.tempVar.recording.lastSavePath.description"));
switch (_condition) {
case Condition::DURATION:
AddTempvar(

View File

@@ -58,10 +58,6 @@ bool MacroConditionReplayBuffer::ReplayBufferWasSaved()
bool MacroConditionReplayBuffer::CheckCondition()
{
char *lastSavePath = obs_frontend_get_last_replay();
SetTempVarValue("lastSavePath", lastSavePath ? lastSavePath : "");
bfree(lastSavePath);
switch (_state) {
case Condition::STOP:
return !obs_frontend_replay_buffer_active();
@@ -89,16 +85,6 @@ bool MacroConditionReplayBuffer::Load(obs_data_t *obj)
return true;
}
void MacroConditionReplayBuffer::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar(
"lastSavePath",
obs_module_text("AdvSceneSwitcher.tempVar.replay.lastSavePath"),
obs_module_text(
"AdvSceneSwitcher.tempVar.replay.lastSavePath.description"));
}
static inline void populateStateSelection(QComboBox *list)
{
for (const auto &entry : conditions) {

View File

@@ -6,7 +6,7 @@
namespace advss {
class MacroConditionReplayBuffer final : public MacroCondition {
class MacroConditionReplayBuffer : public MacroCondition {
public:
MacroConditionReplayBuffer(Macro *m) : MacroCondition(m) {}
bool CheckCondition();
@@ -27,7 +27,6 @@ public:
Condition _state = Condition::STOP;
private:
void SetupTempVars();
bool ReplayBufferWasSaved();
bool _saveTimeInitialized = false;
@@ -36,7 +35,7 @@ private:
static const std::string id;
};
class MacroConditionReplayBufferEdit final : public QWidget {
class MacroConditionReplayBufferEdit : public QWidget {
Q_OBJECT
public:
@@ -56,10 +55,11 @@ public:
private slots:
void StateChanged(int value);
private:
protected:
QComboBox *_state;
std::shared_ptr<MacroConditionReplayBuffer> _entryData;
private:
bool _loading = true;
};

View File

@@ -252,7 +252,6 @@ MacroConditionSlideshowEdit::MacroConditionSlideshowEdit(
setToolTip(obs_module_text(
"AdvSceneSwitcher.condition.slideshow.updateInterval.tooltip"));
_index->setMinimum(1);
_index->setMaximum(9999);
populateConditionSelection(_conditions);
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -210,7 +210,7 @@ findListPropertyById(const obs_source_t *source, const std::string &id)
}
auto property = obs_properties_first(properties);
return {findListPropertyByIdHelper(property, id),
properties_t(properties, obs_properties_destroy)};
properties_t(nullptr, obs_properties_destroy)};
}
static std::pair<obs_property_t *, properties_t>

View File

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

View File

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

View File

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

View File

@@ -72,6 +72,7 @@ void EventSub::UnregisterInstance()
void EventSub::ConnectThread()
{
_client->reset();
_connected = true;
websocketpp::lib::error_code ec;
EventSubWSClient::connection_ptr con =
_client->get_connection(_url, ec);
@@ -112,7 +113,6 @@ void EventSub::Connect()
_thread.join();
}
_disconnect = false;
_connected = true;
_thread = std::thread(&EventSub::ConnectThread, this);
}
@@ -448,7 +448,7 @@ void EventSub::StartServerMigrationClient(const std::string &url)
auto client = std::make_unique<EventSubWSClient>();
SetupClient(*client);
client->set_open_handler([this](connection_hdl) {
client->set_open_handler([this](connection_hdl hdl) {
vblog(LOG_INFO, "Twitch EventSub migration client opened");
});

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
#include "token.hpp"
#include "twitch-helpers.hpp"
#include <help-icon.hpp>
#include <layout-helpers.hpp>
#include <log-helper.hpp>
#include <obs-module-helper.hpp>
@@ -164,8 +163,7 @@ TwitchToken::TwitchToken(const TwitchToken &other)
_userID(other._userID),
_tokenOptions(other._tokenOptions),
_eventSub(),
_validateEventSubTimestamps(other._validateEventSubTimestamps),
_warnIfInvalid(other._warnIfInvalid)
_validateEventSubTimestamps(other._validateEventSubTimestamps)
{
}
@@ -176,7 +174,6 @@ TwitchToken &TwitchToken::operator=(const TwitchToken &other)
_userID = other._userID;
_tokenOptions = other._tokenOptions;
_validateEventSubTimestamps = other._validateEventSubTimestamps;
_warnIfInvalid = other._warnIfInvalid;
return *this;
}
@@ -188,9 +185,6 @@ void TwitchToken::Load(obs_data_t *obj)
obs_data_set_default_bool(obj, "validateEventSubTimestamps", true);
_validateEventSubTimestamps =
obs_data_get_bool(obj, "validateEventSubTimestamps");
_warnIfInvalid = obs_data_has_user_value(obj, "warnIfInvalid")
? obs_data_get_bool(obj, "warnIfInvalid")
: true;
_tokenOptions.clear();
OBSDataArrayAutoRelease options = obs_data_get_array(obj, "options");
size_t count = obs_data_array_count(options);
@@ -209,7 +203,6 @@ void TwitchToken::Save(obs_data_t *obj) const
obs_data_set_string(obj, "userID", _userID.c_str());
obs_data_set_bool(obj, "validateEventSubTimestamps",
_validateEventSubTimestamps);
obs_data_set_bool(obj, "warnIfInvalid", _warnIfInvalid);
OBSDataArrayAutoRelease options = obs_data_array_create();
for (auto &option : _tokenOptions) {
OBSDataAutoRelease arrayObj = obs_data_create();
@@ -484,9 +477,7 @@ TwitchTokenSettingsDialog::TwitchTokenSettingsDialog(
_tokenStatus(new QLabel()),
_generalSettingsGrid(new QGridLayout()),
_validateTimestamps(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.twitchToken.validateTimestamps"))),
_warnIfInvalid(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.twitchToken.warnIfInvalid")))
"AdvSceneSwitcher.twitchToken.validateTimestamps")))
{
_showToken->setMaximumWidth(22);
_showToken->setFlat(true);
@@ -548,18 +539,11 @@ TwitchTokenSettingsDialog::TwitchTokenSettingsDialog(
scrollArea->setWidgetResizable(true);
scrollArea->setFrameShape(QFrame::NoFrame);
auto timestampsLayout = new QHBoxLayout();
timestampsLayout->addWidget(_validateTimestamps);
auto timestampHelp = new HelpIcon(obs_module_text(
"AdvSceneSwitcher.twitchToken.validateTimestamps.tooltip"));
timestampsLayout->addWidget(timestampHelp);
auto contentWidget = new QWidget(scrollArea);
auto layout = new QVBoxLayout(contentWidget);
layout->addLayout(_generalSettingsGrid);
layout->addWidget(optionsBox);
layout->addLayout(timestampsLayout);
layout->addWidget(_warnIfInvalid);
layout->addWidget(_validateTimestamps);
layout->setContentsMargins(0, 0, 0, 0);
scrollArea->setWidget(contentWidget);
@@ -585,7 +569,6 @@ TwitchTokenSettingsDialog::TwitchTokenSettingsDialog(
#ifndef VERIFY_TIMESTAMPS
_validateTimestamps->hide();
#endif
_warnIfInvalid->setChecked(settings._warnIfInvalid);
_currentToken = settings;
@@ -794,8 +777,6 @@ bool TwitchTokenSettingsDialog::AskForSettings(QWidget *parent,
settings._tokenOptions = dialog.GetEnabledOptions();
settings._validateEventSubTimestamps =
dialog._validateTimestamps->isChecked();
settings._warnIfInvalid = dialog._warnIfInvalid->isChecked();
if (settings._eventSub) {
settings._eventSub->EnableTimestampValidation(
settings._validateEventSubTimestamps);
@@ -829,7 +810,7 @@ void TokenGrabberThread::run()
if (_serverThread.joinable()) {
_serverThread.join();
}
_stopWaiting = false;
_stopWaiting = {false};
// Generate URI to request token
auto state = generateStateString();

View File

@@ -55,8 +55,6 @@ public:
std::shared_ptr<EventSub> GetEventSub();
bool ValidateTimestamps() const { return _validateEventSubTimestamps; }
bool IsValid(bool forceUpdate = false) const;
bool WarnIfInvalid() const { return _warnIfInvalid; }
void SetWarnIfInvalid(bool value) { _warnIfInvalid = value; }
size_t PermissionCount() const { return _tokenOptions.size(); }
private:
@@ -69,7 +67,6 @@ private:
std::set<TokenOption> _tokenOptions = TokenOption::GetAllTokenOptions();
std::shared_ptr<EventSub> _eventSub;
bool _validateEventSubTimestamps = false;
bool _warnIfInvalid = true;
static bool _setup;
@@ -134,7 +131,6 @@ private:
TwitchToken _currentToken;
std::unordered_map<std::string, QCheckBox *> _optionWidgets;
QCheckBox *_validateTimestamps;
QCheckBox *_warnIfInvalid;
QTimer _validationTimer;
};

View File

@@ -144,14 +144,9 @@ static void handleThrottling(const httplib::Result &response)
return;
}
if (sleepDuration < std::chrono::seconds(1)) {
sleepDuration = std::chrono::seconds(1);
}
blog(LOG_WARNING,
"Twitch API access is throttled for %lld milliseconds!",
blog(LOG_WARNING, "Twitch API access is throttled for %lld seconds!",
static_cast<long long int>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::duration_cast<std::chrono::seconds>(
sleepDuration)
.count()));
apiIsThrottling = true;

View File

@@ -1,47 +1,24 @@
#include "twitch-tab.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "sync-helpers.hpp"
#include "tab-helpers.hpp"
#include "token.hpp"
#include "ui-helpers.hpp"
#include <QDialogButtonBox>
#include <QTabWidget>
namespace advss {
static bool setup();
static bool registerTab();
static void setupTab(QTabWidget *);
static bool setupDone = setup();
static bool registerTabDone = registerTab();
static TwitchConnectionsTable *tabWidget = nullptr;
static QStringList getInvalidTokens();
static bool setup()
static bool registerTab()
{
AddSetupTabCallback("twitchConnectionTab",
TwitchConnectionsTable::Create, setupTab);
static const auto showInvalidWarnings = []() {
const auto invalidTokens = getInvalidTokens();
for (const auto &token : invalidTokens) {
QueueUITask(
[](void *tokenPtr) {
auto tokenName = static_cast<QString *>(
tokenPtr);
InvalidTokenDialog::ShowWarning(
*tokenName);
},
(void *)&token);
}
};
AddLoadStep([](obs_data_t *) {
AddPostLoadStep([]() { showInvalidWarnings(); });
});
return true;
}
@@ -211,25 +188,6 @@ static void updateConnectionStatus(QTableWidget *table)
}
}
static QStringList getInvalidTokens()
{
QStringList tokens;
for (const auto &t : GetTwitchTokens()) {
if (!t) {
continue;
}
auto token = std::static_pointer_cast<TwitchToken>(t);
if (!token->WarnIfInvalid() || token->IsValid(true)) {
continue;
}
tokens << QString::fromStdString(token->GetName());
}
return tokens;
}
static void setupTab(QTabWidget *tab)
{
if (GetTwitchTokens().empty()) {
@@ -264,63 +222,6 @@ static void setupTab(QTabWidget *tab)
updateConnectionStatus(tabWidget->Table());
}
});
const auto invalidTokens = getInvalidTokens();
for (const auto &token : invalidTokens) {
// Constructing the warning dialog in the constructor of the settings
// window might lead to a crash, so wait for the settings window
// constructor to complete
QTimer::singleShot(0, tab, [token]() {
InvalidTokenDialog::ShowWarning(token);
});
}
}
void InvalidTokenDialog::ShowWarning(const QString &tokenName)
{
auto weakToken = GetWeakTwitchTokenByQString(tokenName);
auto token = weakToken.lock();
if (!token) {
return;
}
auto dialog = new InvalidTokenDialog(tokenName);
dialog->setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
const bool ignore = dialog->exec() != DialogCode::Accepted;
token->SetWarnIfInvalid(!dialog->_doNotShowAgain->isChecked());
dialog->deleteLater();
if (ignore) {
return;
}
TwitchTokenSettingsDialog::AskForSettings(GetSettingsWindow(),
*token.get());
}
InvalidTokenDialog::InvalidTokenDialog(const QString &name)
: QDialog(GetSettingsWindow()),
_doNotShowAgain(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.twitchToken.warnIfInvalid.doNotShowAgain")))
{
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ignore |
QDialogButtonBox::Open);
connect(buttons->button(QDialogButtonBox::Ignore),
&QPushButton::clicked, this, &QDialog::reject);
connect(buttons->button(QDialogButtonBox::Open), &QPushButton::clicked,
this, &QDialog::accept);
buttons->setCenterButtons(true);
const QString format(obs_module_text(
"AdvSceneSwitcher.twitchToken.warnIfInvalid.message"));
const QString message = format.arg(name);
auto layout = new QVBoxLayout;
layout->addWidget(new QLabel(message));
layout->addWidget(_doNotShowAgain);
layout->addWidget(buttons);
setLayout(layout);
}
} // namespace advss

View File

@@ -1,9 +1,6 @@
#pragma once
#include "resource-table.hpp"
#include <QCheckBox>
#include <QDialog>
namespace advss {
class TwitchConnectionsTable final : public ResourceTable {
@@ -20,14 +17,4 @@ private:
TwitchConnectionsTable(QTabWidget *parent = nullptr);
};
class InvalidTokenDialog : public QDialog {
Q_OBJECT
public:
static void ShowWarning(const QString &name);
private:
InvalidTokenDialog(const QString &name);
QCheckBox *_doNotShowAgain;
};
} // namespace advss

View File

@@ -106,11 +106,8 @@ target_sources(
# --- variable --- #
if(OS_WINDOWS)
target_compile_options(${PROJECT_NAME} PRIVATE /wd4100)
else()
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-error=shadow
-Wno-unused-parameter)
if(NOT OS_WINDOWS)
target_compile_options(${PROJECT_NAME} PUBLIC -Wno-error=shadow)
endif()
target_sources(

View File

@@ -40,7 +40,7 @@ std::string GetThemeTypeName()
return "Dark";
}
void QueueUITask(void (*task)(void *param), void *) {}
void QeueUITask(void (*task)(void *param), void *) {}
QWidget *GetSettingsWindow()
{