mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-30 05:06:14 -05:00
Compare commits
30 Commits
record-pat
...
1.32.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03fe7016e4 | ||
|
|
2ea89912a3 | ||
|
|
78d2efa083 | ||
|
|
149fce1c2b | ||
|
|
2dd9120265 | ||
|
|
3a9f315b67 | ||
|
|
6ba01dca39 | ||
|
|
ad1f1effeb | ||
|
|
0a53a8649f | ||
|
|
7ea721f9f1 | ||
|
|
718a899a98 | ||
|
|
4c493451f4 | ||
|
|
a8d483f5a7 | ||
|
|
dc786a5313 | ||
|
|
628a4d896c | ||
|
|
74a9681c8e | ||
|
|
faf52c38bb | ||
|
|
bad1a548fb | ||
|
|
f706416df5 | ||
|
|
c344c88acd | ||
|
|
2cd9e61717 | ||
|
|
6314de8f37 | ||
|
|
6bfcabc4af | ||
|
|
69711d973a | ||
|
|
225913b44d | ||
|
|
7c6d657fdd | ||
|
|
12e38274f5 | ||
|
|
5d49e8825b | ||
|
|
21c34356ed | ||
|
|
266e470509 |
@@ -6,18 +6,18 @@ You have the option to ...
|
|||||||
|
|
||||||
Both methods require [CMake](https://cmake.org/download/).
|
Both methods require [CMake](https://cmake.org/download/).
|
||||||
|
|
||||||
The plugin can be compiled for OBS 27 and above, although using the latest version of OBS is recommended to support all features.
|
The plugin can be compiled for OBS 31 and above, although using the latest version of OBS is recommended to support all features.
|
||||||
|
|
||||||
## Compiling in tree (recommended for development)
|
## Compiling in tree (recommended for development)
|
||||||
This section assumes that you have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio).
|
This section assumes that you have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio).
|
||||||
|
|
||||||
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
|
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/plugins:
|
||||||
```
|
```
|
||||||
cd obs-studio/UI/frontend-plugins/
|
cd obs-studio/plugins
|
||||||
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
|
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt Example and add an entry for the scene switcher:
|
Then modify the obs-studio/plugins/CMakeLists.txt and add an entry for the scene switcher:
|
||||||
```
|
```
|
||||||
add_subdirectory(SceneSwitcher)
|
add_subdirectory(SceneSwitcher)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -184,6 +184,8 @@ target_sources(
|
|||||||
lib/utils/canvas-helpers.hpp
|
lib/utils/canvas-helpers.hpp
|
||||||
lib/utils/condition-logic.cpp
|
lib/utils/condition-logic.cpp
|
||||||
lib/utils/condition-logic.hpp
|
lib/utils/condition-logic.hpp
|
||||||
|
lib/utils/crash-handler.cpp
|
||||||
|
lib/utils/crash-handler.hpp
|
||||||
lib/utils/curl-helper.cpp
|
lib/utils/curl-helper.cpp
|
||||||
lib/utils/curl-helper.hpp
|
lib/utils/curl-helper.hpp
|
||||||
lib/utils/cursor-shape-changer.cpp
|
lib/utils/cursor-shape-changer.cpp
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Verstecken"
|
|||||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Umschalten"
|
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Umschalten"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Quelle"
|
AdvSceneSwitcher.action.sceneVisibility.type.source="Quelle"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Beliebig"
|
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Beliebig"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.entry="Auf{{scenes}}{{actions}}{{sources}}"
|
AdvSceneSwitcher.action.sceneVisibility.layout="Auf{{scenes}}{{actions}}{{sources}}"
|
||||||
AdvSceneSwitcher.action.filter="Filter"
|
AdvSceneSwitcher.action.filter="Filter"
|
||||||
AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
|
AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
|
||||||
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
|
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html>
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!"
|
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
|
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch="Always show macro search"
|
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch="Always show macro search"
|
||||||
|
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs="Always show all feature tabs"
|
||||||
|
AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs.tooltip="Show tabs that are normally hidden until first used in a macro (e.g. Action Queues, Variables, Twitch, Websockets)."
|
||||||
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
|
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
|
||||||
AdvSceneSwitcher.generalTab.priority="Priority"
|
AdvSceneSwitcher.generalTab.priority="Priority"
|
||||||
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
|
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
|
||||||
@@ -158,6 +160,8 @@ AdvSceneSwitcher.macroTab.title="Macro"
|
|||||||
AdvSceneSwitcher.macroTab.macros="Macros"
|
AdvSceneSwitcher.macroTab.macros="Macros"
|
||||||
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
|
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
|
||||||
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
|
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
|
||||||
|
AdvSceneSwitcher.macroTab.macroLastExecutedTooltip="Was last executed %1 seconds ago."
|
||||||
|
AdvSceneSwitcher.macroTab.macroNotYetExecutedTooltip="Was not yet executed."
|
||||||
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define macro conditions.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new condition."
|
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define macro conditions.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new condition."
|
||||||
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
|
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
|
||||||
AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
|
AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
|
||||||
@@ -914,7 +918,9 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
|||||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Toggle"
|
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Toggle"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
|
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sources}}"
|
AdvSceneSwitcher.action.sceneVisibility.layout="On{{scenes}}{{actions}}{{sources}}"
|
||||||
|
AdvSceneSwitcher.action.sceneVisibility.layout.transition="{{updateTransition}}Set transition to{{transitions}}"
|
||||||
|
AdvSceneSwitcher.action.sceneVisibility.layout.duration="{{updateDuration}}Set transition duration to{{duration}}seconds"
|
||||||
AdvSceneSwitcher.action.filter="Filter"
|
AdvSceneSwitcher.action.filter="Filter"
|
||||||
AdvSceneSwitcher.action.filter.type.enable="Enable"
|
AdvSceneSwitcher.action.filter.type.enable="Enable"
|
||||||
AdvSceneSwitcher.action.filter.type.disable="Disable"
|
AdvSceneSwitcher.action.filter.type.disable="Disable"
|
||||||
@@ -1190,6 +1196,8 @@ AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values"
|
|||||||
AdvSceneSwitcher.action.variable.type.trim="Trim whitespace"
|
AdvSceneSwitcher.action.variable.type.trim="Trim whitespace"
|
||||||
AdvSceneSwitcher.action.variable.type.changeCase="Change case"
|
AdvSceneSwitcher.action.variable.type.changeCase="Change case"
|
||||||
AdvSceneSwitcher.action.variable.type.randomNumber="Generate random number"
|
AdvSceneSwitcher.action.variable.type.randomNumber="Generate random number"
|
||||||
|
AdvSceneSwitcher.action.variable.type.randomListValue="Select random value"
|
||||||
|
AdvSceneSwitcher.action.variable.type.allowRepeat="Allow repeat values"
|
||||||
AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase"
|
AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase"
|
||||||
AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE"
|
AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE"
|
||||||
AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized"
|
AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized"
|
||||||
@@ -1212,8 +1220,8 @@ AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
|
|||||||
AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}"
|
AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
|
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
|
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
|
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:"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:{{subStringRegex}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
|
||||||
@@ -1256,12 +1264,14 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote
|
|||||||
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
|
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
|
||||||
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
|
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
|
||||||
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
|
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
|
||||||
|
AdvSceneSwitcher.action.twitch.type.channel.getInfo="Get channel information"
|
||||||
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
|
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
|
||||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
|
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
|
||||||
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
|
AdvSceneSwitcher.action.twitch.layout.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.layout.chat="Using account{{account}}{{actions}}on{{channel}}"
|
||||||
AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
|
AdvSceneSwitcher.action.twitch.layout.channel.getInfo="Using account{{account}}{{actions}}of{{channel}}"
|
||||||
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
|
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.title.title="Enter title"
|
AdvSceneSwitcher.action.twitch.title.title="Enter title"
|
||||||
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
|
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
|
||||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
|
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
|
||||||
@@ -1386,6 +1396,8 @@ AdvSceneSwitcher.hotkey.macro.segment.remove="Remove selected macro segment"
|
|||||||
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
|
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
|
||||||
AdvSceneSwitcher.askForMacro="Select macro{{macroSelection}}"
|
AdvSceneSwitcher.askForMacro="Select macro{{macroSelection}}"
|
||||||
|
|
||||||
|
AdvSceneSwitcher.crashDetected="OBS did not shut down cleanly (for example, due to a crash or freeze).\n\nThe Advanced Scene Switcher plugin would normally start automatically.\nWould you like to keep it stopped for now?"
|
||||||
|
|
||||||
AdvSceneSwitcher.close="Close"
|
AdvSceneSwitcher.close="Close"
|
||||||
AdvSceneSwitcher.browse="Browse"
|
AdvSceneSwitcher.browse="Browse"
|
||||||
|
|
||||||
@@ -2145,6 +2157,8 @@ AdvSceneSwitcher.tempVar.window.window="Window title"
|
|||||||
AdvSceneSwitcher.tempVar.window.window.description="The window title of the matching window."
|
AdvSceneSwitcher.tempVar.window.window.description="The window title of the matching window."
|
||||||
AdvSceneSwitcher.tempVar.window.windowClass="Window class"
|
AdvSceneSwitcher.tempVar.window.windowClass="Window class"
|
||||||
AdvSceneSwitcher.tempVar.window.windowClass.description="The window class of the matching window."
|
AdvSceneSwitcher.tempVar.window.windowClass.description="The window class of the matching window."
|
||||||
|
AdvSceneSwitcher.tempVar.window.windowText="Window text"
|
||||||
|
AdvSceneSwitcher.tempVar.window.windowText.description="Text contained within the window (won't work on all window types)."
|
||||||
|
|
||||||
AdvSceneSwitcher.tempVar.timer.seconds="Seconds"
|
AdvSceneSwitcher.tempVar.timer.seconds="Seconds"
|
||||||
AdvSceneSwitcher.tempVar.timer.minutes="Minutes"
|
AdvSceneSwitcher.tempVar.timer.minutes="Minutes"
|
||||||
@@ -2186,6 +2200,8 @@ AdvSceneSwitcher.tempVar.run.process.none.description="When not waiting for the
|
|||||||
|
|
||||||
AdvSceneSwitcher.tempVar.recording.durationSeconds="Recording duration"
|
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.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="Pattern count"
|
||||||
AdvSceneSwitcher.tempVar.video.patternCount.description="The number of times the given pattern has been found in a given video input frame."
|
AdvSceneSwitcher.tempVar.video.patternCount.description="The number of times the given pattern has been found in a given video input frame."
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Mostrar"
|
|||||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
|
AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Fuente"
|
AdvSceneSwitcher.action.sceneVisibility.type.source="Fuente"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Cualquiera"
|
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Cualquiera"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sources}}"
|
AdvSceneSwitcher.action.sceneVisibility.layout="En{{scenes}}{{actions}}{{sources}}"
|
||||||
AdvSceneSwitcher.action.filter="Filtro"
|
AdvSceneSwitcher.action.filter="Filtro"
|
||||||
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
|
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
|
||||||
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
|
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
|
||||||
|
|||||||
@@ -510,7 +510,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Masquer"
|
|||||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Basculer"
|
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Basculer"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="N'importe"
|
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="N'importe"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.entry="Sur{{scenes}}{{actions}}{{sources}}"
|
AdvSceneSwitcher.action.sceneVisibility.layout="Sur{{scenes}}{{actions}}{{sources}}"
|
||||||
AdvSceneSwitcher.action.filter="Filtre"
|
AdvSceneSwitcher.action.filter="Filtre"
|
||||||
AdvSceneSwitcher.action.filter.type.enable="Activer"
|
AdvSceneSwitcher.action.filter.type.enable="Activer"
|
||||||
AdvSceneSwitcher.action.filter.type.disable="Désactiver"
|
AdvSceneSwitcher.action.filter.type.disable="Désactiver"
|
||||||
@@ -701,8 +701,8 @@ AdvSceneSwitcher.action.variable.invalidSelection="Sélection invalide !"
|
|||||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !"
|
AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !"
|
||||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !"
|
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !"
|
||||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :"
|
AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :"
|
||||||
AdvSceneSwitcher.action.variable.layout.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
|
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 :"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière:{{subStringRegex}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
|
||||||
AdvSceneSwitcher.action.projector="Projecteur"
|
AdvSceneSwitcher.action.projector="Projecteur"
|
||||||
|
|||||||
@@ -870,7 +870,6 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="非表示"
|
|||||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切り替え"
|
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切り替え"
|
||||||
; AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
; AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
||||||
; AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
|
; AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
|
||||||
; AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sources}}"
|
|
||||||
AdvSceneSwitcher.action.filter="フィルタ"
|
AdvSceneSwitcher.action.filter="フィルタ"
|
||||||
AdvSceneSwitcher.action.filter.type.enable="有効にする"
|
AdvSceneSwitcher.action.filter.type.enable="有効にする"
|
||||||
AdvSceneSwitcher.action.filter.type.disable="無効化"
|
AdvSceneSwitcher.action.filter.type.disable="無効化"
|
||||||
@@ -1149,8 +1148,8 @@ AdvSceneSwitcher.action.variable.currentSegmentValue="現在値:"
|
|||||||
; AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
; AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。"
|
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。"
|
||||||
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。"
|
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。"
|
||||||
AdvSceneSwitcher.action.variable.layout.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}"
|
AdvSceneSwitcher.action.variable.layout.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}{{subStringRegex}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:{{subStringRegex}}"
|
||||||
; AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
; AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}"
|
||||||
@@ -1193,10 +1192,10 @@ AdvSceneSwitcher.action.twitch.type.user.getInfo="ユーザー情報を取得"
|
|||||||
AdvSceneSwitcher.action.twitch.type.reward.getInfo="チャンネルポイントリワード情報を取得"
|
AdvSceneSwitcher.action.twitch.type.reward.getInfo="チャンネルポイントリワード情報を取得"
|
||||||
AdvSceneSwitcher.action.twitch.reward.toggleControl="リワード名/変数選択コントロールの切り替え"
|
AdvSceneSwitcher.action.twitch.reward.toggleControl="リワード名/変数選択コントロールの切り替え"
|
||||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません!"
|
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません!"
|
||||||
; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
|
; AdvSceneSwitcher.action.twitch.layout.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
|
||||||
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
|
AdvSceneSwitcher.action.twitch.layout.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
|
||||||
AdvSceneSwitcher.action.twitch.entry.user.getInfo="{{userInfoQueryType}}{{userLogin}}{{userId}}でアカウント{{account}}{{actions}}を使用"
|
AdvSceneSwitcher.action.twitch.layout.user.getInfo="{{userInfoQueryType}}{{userLogin}}{{userId}}でアカウント{{account}}{{actions}}を使用"
|
||||||
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="チャンネル{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}でアカウント{{account}}{{actions}}を使用"
|
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="チャンネル{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}でアカウント{{account}}{{actions}}を使用"
|
||||||
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力"
|
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力"
|
||||||
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
|
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
|
||||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"
|
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"
|
||||||
|
|||||||
@@ -781,7 +781,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
|
|||||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Alternar"
|
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Alternar"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Fonte"
|
AdvSceneSwitcher.action.sceneVisibility.type.source="Fonte"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Qualquer"
|
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Qualquer"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.entry="Em{{scenes}}{{actions}}{{sources}}"
|
AdvSceneSwitcher.action.sceneVisibility.layout="Em{{scenes}}{{actions}}{{sources}}"
|
||||||
AdvSceneSwitcher.action.filter="Filtro"
|
AdvSceneSwitcher.action.filter="Filtro"
|
||||||
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
|
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
|
||||||
AdvSceneSwitcher.action.filter.type.disable="Desabilitar"
|
AdvSceneSwitcher.action.filter.type.disable="Desabilitar"
|
||||||
@@ -1030,8 +1030,8 @@ AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Obter valores de va
|
|||||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:"
|
AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:"
|
||||||
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
|
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
|
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
|
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:"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:{{subStringRegex}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
|
||||||
@@ -1065,8 +1065,8 @@ AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas
|
|||||||
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
|
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
|
||||||
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
|
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
|
||||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
|
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
|
||||||
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
|
AdvSceneSwitcher.action.twitch.layout.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.layout.chat="Usando conta{{account}}{{actions}}em{{channel}}"
|
||||||
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
|
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
|
||||||
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
|
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
|
||||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"
|
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Göster"
|
|||||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle"
|
AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt"
|
AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi"
|
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sources}}"
|
AdvSceneSwitcher.action.sceneVisibility.layout="Açık{{scenes}}{{actions}}{{sources}}"
|
||||||
AdvSceneSwitcher.action.filter="Filtrele"
|
AdvSceneSwitcher.action.filter="Filtrele"
|
||||||
AdvSceneSwitcher.action.filter.type.enable="Etkin"
|
AdvSceneSwitcher.action.filter.type.enable="Etkin"
|
||||||
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
|
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
|
||||||
|
|||||||
@@ -840,7 +840,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏"
|
|||||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切换可见性"
|
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切换可见性"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.source="源"
|
AdvSceneSwitcher.action.sceneVisibility.type.source="源"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="任何"
|
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="任何"
|
||||||
AdvSceneSwitcher.action.sceneVisibility.entry="{{scenes}}{{actions}}{{sources}}"
|
AdvSceneSwitcher.action.sceneVisibility.layout="{{scenes}}{{actions}}{{sources}}"
|
||||||
AdvSceneSwitcher.action.filter="滤镜"
|
AdvSceneSwitcher.action.filter="滤镜"
|
||||||
AdvSceneSwitcher.action.filter.type.enable="开启"
|
AdvSceneSwitcher.action.filter.type.enable="开启"
|
||||||
AdvSceneSwitcher.action.filter.type.disable="关闭"
|
AdvSceneSwitcher.action.filter.type.disable="关闭"
|
||||||
@@ -1100,8 +1100,8 @@ AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条
|
|||||||
AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:"
|
AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:"
|
||||||
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}于{{variables}}的{{direction}}起,内容为{{paddingCharSelection}}将其变量值长度增加至{{stringLength}}."
|
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}于{{variables}}的{{direction}}起,内容为{{paddingCharSelection}}将其变量值长度增加至{{stringLength}}."
|
||||||
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}于{{variables}}的{{direction}}起,将变量长度缩减至{{stringLength}}"
|
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}于{{variables}}的{{direction}}起,将变量长度缩减至{{stringLength}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.substringIndex="截取字符串开始位置:{{subStringStart}} 截取字符串长度:{{subStringSize}}"
|
AdvSceneSwitcher.action.variable.layout.substringIndex="截取字符串开始位置:{{subStringStart}} 截取字符串长度:{{subStringSize}}{{subStringRegex}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:"
|
AdvSceneSwitcher.action.variable.layout.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:{{subStringRegex}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}使用自定义提示{{inputPrompt}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}使用自定义提示{{inputPrompt}}"
|
||||||
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}用占位符填充{{inputPlaceholder}}"
|
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}用占位符填充{{inputPlaceholder}}"
|
||||||
@@ -1139,10 +1139,10 @@ AdvSceneSwitcher.action.twitch.type.user.getInfo="获取用户信息"
|
|||||||
AdvSceneSwitcher.action.twitch.type.reward.getInfo="获取频道积分奖励信息"
|
AdvSceneSwitcher.action.twitch.type.reward.getInfo="获取频道积分奖励信息"
|
||||||
AdvSceneSwitcher.action.twitch.reward.toggleControl="切换奖励名称/变量选择控制"
|
AdvSceneSwitcher.action.twitch.reward.toggleControl="切换奖励名称/变量选择控制"
|
||||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="在未选择 Twitch 帐户的情况下无法选择类别!"
|
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="在未选择 Twitch 帐户的情况下无法选择类别!"
|
||||||
AdvSceneSwitcher.action.twitch.entry.default="{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
|
AdvSceneSwitcher.action.twitch.layout.default="{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
|
||||||
AdvSceneSwitcher.action.twitch.entry.chat="使用账户{{account}}{{actions}}{{channel}}"
|
AdvSceneSwitcher.action.twitch.layout.chat="使用账户{{account}}{{actions}}{{channel}}"
|
||||||
AdvSceneSwitcher.action.twitch.entry.user.getInfo="使用账户{{account}}{{actions}}{{userInfoQueryType}}{{userLogin}}{{userId}}"
|
AdvSceneSwitcher.action.twitch.layout.user.getInfo="使用账户{{account}}{{actions}}{{userInfoQueryType}}{{userLogin}}{{userId}}"
|
||||||
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="使用账户{{account}}{{actions}} 频道{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
|
AdvSceneSwitcher.action.twitch.layout.reward.getInfo="使用账户{{account}}{{actions}} 频道{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
|
||||||
AdvSceneSwitcher.action.twitch.title.title="输入标题"
|
AdvSceneSwitcher.action.twitch.title.title="输入标题"
|
||||||
AdvSceneSwitcher.action.twitch.marker.description="标记描述"
|
AdvSceneSwitcher.action.twitch.marker.description="标记描述"
|
||||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="在捕捉视频片段前稍加延迟"
|
AdvSceneSwitcher.action.twitch.clip.hasDelay="在捕捉视频片段前稍加延迟"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>962</width>
|
<width>962</width>
|
||||||
<height>1132</height>
|
<height>1160</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||||
@@ -268,9 +268,19 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="uiHintsDisable">
|
<widget class="QCheckBox" name="alwaysShowMacroSearch">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints</string>
|
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="alwaysShowFeatureTabs">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs.tooltip</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowFeatureTabs</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -282,9 +292,9 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="alwaysShowMacroSearch">
|
<widget class="QCheckBox" name="uiHintsDisable">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.alwaysShowMacroSearch</string>
|
<string>AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
#include "advanced-scene-switcher.hpp"
|
||||||
#include "backup.hpp"
|
#include "backup.hpp"
|
||||||
|
#include "crash-handler.hpp"
|
||||||
#include "log-helper.hpp"
|
#include "log-helper.hpp"
|
||||||
#include "macro-helpers.hpp"
|
#include "macro-helpers.hpp"
|
||||||
#include "obs-module-helper.hpp"
|
#include "obs-module-helper.hpp"
|
||||||
@@ -207,9 +208,15 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
|||||||
switcher->LoadSettings(data);
|
switcher->LoadSettings(data);
|
||||||
switcher->m.unlock();
|
switcher->m.unlock();
|
||||||
|
|
||||||
if (!switcher->stop) {
|
if (switcher->stop) {
|
||||||
switcher->Start();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ShouldSkipPluginStartOnUncleanShutdown()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switcher->Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1029,6 +1029,8 @@ void AdvSceneSwitcher::SetupGeneralTab()
|
|||||||
setTabOrder(ui->importSettings, ui->cooldownTime);
|
setTabOrder(ui->importSettings, ui->cooldownTime);
|
||||||
setTabOrder(ui->cooldownTime, ui->noMatchDontSwitch);
|
setTabOrder(ui->cooldownTime, ui->noMatchDontSwitch);
|
||||||
|
|
||||||
|
SetupShowAllTabsCheckBox(ui->alwaysShowFeatureTabs, ui->tabWidget);
|
||||||
|
|
||||||
MinimizeSizeOfColumn(ui->statusLayout, 0);
|
MinimizeSizeOfColumn(ui->statusLayout, 0);
|
||||||
setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
|
setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ static inline void populateActionSelection(QComboBox *list)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MacroActionEdit::MacroActionEdit(QWidget *parent,
|
MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||||
std::shared_ptr<MacroAction> *entryData,
|
std::shared_ptr<MacroAction> *entryData)
|
||||||
const std::string &id)
|
|
||||||
: MacroSegmentEdit(parent),
|
: MacroSegmentEdit(parent),
|
||||||
_actionSelection(new FilterComboBox()),
|
_actionSelection(new FilterComboBox()),
|
||||||
_enable(new SwitchButton()),
|
_enable(new SwitchButton()),
|
||||||
|
|||||||
@@ -13,10 +13,8 @@ class MacroActionEdit : public MacroSegmentEdit {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MacroActionEdit(
|
MacroActionEdit(QWidget *parent = nullptr,
|
||||||
QWidget *parent = nullptr,
|
std::shared_ptr<MacroAction> * = nullptr);
|
||||||
std::shared_ptr<MacroAction> * = nullptr,
|
|
||||||
const std::string &id = MacroAction::GetDefaultID().data());
|
|
||||||
void SetupWidgets(bool basicSetup = false);
|
void SetupWidgets(bool basicSetup = false);
|
||||||
void SetEntryData(std::shared_ptr<MacroAction> *);
|
void SetEntryData(std::shared_ptr<MacroAction> *);
|
||||||
|
|
||||||
|
|||||||
@@ -93,33 +93,46 @@ bool MacroActionMacro::PerformAction()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_action) {
|
const auto performActionForMacro = [this](Macro *macro) {
|
||||||
case Action::PAUSE:
|
switch (_action) {
|
||||||
macro->SetPaused();
|
case Action::PAUSE:
|
||||||
break;
|
macro->SetPaused();
|
||||||
case Action::UNPAUSE:
|
break;
|
||||||
macro->SetPaused(false);
|
case Action::UNPAUSE:
|
||||||
break;
|
macro->SetPaused(false);
|
||||||
case Action::TOGGLE_PAUSE:
|
break;
|
||||||
macro->SetPaused(!macro->Paused());
|
case Action::TOGGLE_PAUSE:
|
||||||
break;
|
macro->SetPaused(!macro->Paused());
|
||||||
case Action::RESET_COUNTER:
|
break;
|
||||||
macro->ResetRunCount();
|
case Action::RESET_COUNTER:
|
||||||
break;
|
macro->ResetRunCount();
|
||||||
case Action::RUN_ACTIONS:
|
break;
|
||||||
RunActions(macro.get());
|
case Action::RUN_ACTIONS:
|
||||||
break;
|
RunActions(macro);
|
||||||
case Action::STOP:
|
break;
|
||||||
macro->Stop();
|
case Action::STOP:
|
||||||
break;
|
macro->Stop();
|
||||||
case Action::DISABLE_ACTION:
|
break;
|
||||||
case Action::ENABLE_ACTION:
|
case Action::DISABLE_ACTION:
|
||||||
case Action::TOGGLE_ACTION:
|
case Action::ENABLE_ACTION:
|
||||||
AdjustActionState(macro.get());
|
case Action::TOGGLE_ACTION:
|
||||||
break;
|
AdjustActionState(macro);
|
||||||
default:
|
break;
|
||||||
break;
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!IsGroupMacro(macro.get())) {
|
||||||
|
performActionForMacro(macro.get());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto macros = GetGroupMacroEntries(macro.get());
|
||||||
|
for (const auto ¯o : macros) {
|
||||||
|
performActionForMacro(macro.get());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,6 +417,7 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
|||||||
populateActionTypes(_actionTypes);
|
populateActionTypes(_actionTypes);
|
||||||
|
|
||||||
_conditionMacros->HideSelectedMacro();
|
_conditionMacros->HideSelectedMacro();
|
||||||
|
_conditionMacros->HideGroups();
|
||||||
|
|
||||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||||
this, SLOT(MacroChanged(const QString &)));
|
this, SLOT(MacroChanged(const QString &)));
|
||||||
|
|||||||
@@ -219,6 +219,43 @@ void MacroActionVariable::GenerateRandomNumber(Variable *var)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionVariable::PickRandomValue(Variable *var)
|
||||||
|
{
|
||||||
|
static std::random_device rd;
|
||||||
|
static std::mt19937 gen(rd());
|
||||||
|
static const uint8_t maxIter = 255;
|
||||||
|
|
||||||
|
if (_randomValues.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_randomValues.size() == 1) {
|
||||||
|
const auto &value = _randomValues.at(0);
|
||||||
|
var->SetValue(value);
|
||||||
|
_lastRandomValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uniform_int_distribution<int> dis(0, _randomValues.size() - 1);
|
||||||
|
StringVariable value = _randomValues.at(dis(gen));
|
||||||
|
|
||||||
|
uint8_t iter = 0;
|
||||||
|
for (; !_allowRepeatValues && iter < maxIter &&
|
||||||
|
_lastRandomValue.has_value() &&
|
||||||
|
std::string(value) == _lastRandomValue.value();
|
||||||
|
iter++) {
|
||||||
|
value = _randomValues.at(dis(gen));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iter == maxIter) {
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"giving up picking non-repeat random value after %d tries",
|
||||||
|
maxIter);
|
||||||
|
}
|
||||||
|
|
||||||
|
var->SetValue(value);
|
||||||
|
_lastRandomValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
struct AskForInputParams {
|
struct AskForInputParams {
|
||||||
AskForInputParams(const QString &prompt_, const QString &placeholder_)
|
AskForInputParams(const QString &prompt_, const QString &placeholder_)
|
||||||
: prompt(prompt_),
|
: prompt(prompt_),
|
||||||
@@ -458,6 +495,9 @@ bool MacroActionVariable::PerformAction()
|
|||||||
case Action::RANDOM_NUMBER:
|
case Action::RANDOM_NUMBER:
|
||||||
GenerateRandomNumber(var.get());
|
GenerateRandomNumber(var.get());
|
||||||
return true;
|
return true;
|
||||||
|
case Action::RANDOM_LIST_VALUE:
|
||||||
|
PickRandomValue(var.get());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -498,6 +538,8 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
|
|||||||
_randomNumberStart.Save(obj, "randomNumberStart");
|
_randomNumberStart.Save(obj, "randomNumberStart");
|
||||||
_randomNumberEnd.Save(obj, "randomNumberEnd");
|
_randomNumberEnd.Save(obj, "randomNumberEnd");
|
||||||
obs_data_set_bool(obj, "generateInteger", _generateInteger);
|
obs_data_set_bool(obj, "generateInteger", _generateInteger);
|
||||||
|
_randomValues.Save(obj, "randomValues", "value");
|
||||||
|
obs_data_set_bool(obj, "allowRepeatValues", _allowRepeatValues);
|
||||||
_jsonQuery.Save(obj, "jsonQuery");
|
_jsonQuery.Save(obj, "jsonQuery");
|
||||||
_jsonIndex.Save(obj, "jsonIndex");
|
_jsonIndex.Save(obj, "jsonIndex");
|
||||||
|
|
||||||
@@ -556,6 +598,8 @@ bool MacroActionVariable::Load(obs_data_t *obj)
|
|||||||
_randomNumberStart.Load(obj, "randomNumberStart");
|
_randomNumberStart.Load(obj, "randomNumberStart");
|
||||||
_randomNumberEnd.Load(obj, "randomNumberEnd");
|
_randomNumberEnd.Load(obj, "randomNumberEnd");
|
||||||
_generateInteger = obs_data_get_bool(obj, "generateInteger");
|
_generateInteger = obs_data_get_bool(obj, "generateInteger");
|
||||||
|
_randomValues.Load(obj, "randomValues", "value");
|
||||||
|
_allowRepeatValues = obs_data_get_bool(obj, "allowRepeatValues");
|
||||||
_jsonQuery.Load(obj, "jsonQuery");
|
_jsonQuery.Load(obj, "jsonQuery");
|
||||||
_jsonIndex.Load(obj, "jsonIndex");
|
_jsonIndex.Load(obj, "jsonIndex");
|
||||||
|
|
||||||
@@ -724,6 +768,8 @@ static inline void populateActionSelection(QComboBox *list)
|
|||||||
"AdvSceneSwitcher.action.variable.type.roundToInt"},
|
"AdvSceneSwitcher.action.variable.type.roundToInt"},
|
||||||
{MacroActionVariable::Action::RANDOM_NUMBER,
|
{MacroActionVariable::Action::RANDOM_NUMBER,
|
||||||
"AdvSceneSwitcher.action.variable.type.randomNumber"},
|
"AdvSceneSwitcher.action.variable.type.randomNumber"},
|
||||||
|
{MacroActionVariable::Action::RANDOM_LIST_VALUE,
|
||||||
|
"AdvSceneSwitcher.action.variable.type.randomListValue"},
|
||||||
{true, ""}, // Separator
|
{true, ""}, // Separator
|
||||||
|
|
||||||
{MacroActionVariable::Action::USER_INPUT,
|
{MacroActionVariable::Action::USER_INPUT,
|
||||||
@@ -801,11 +847,10 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
_segmentValueStatus(new QLabel()),
|
_segmentValueStatus(new QLabel()),
|
||||||
_segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)),
|
_segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)),
|
||||||
_substringLayout(new QVBoxLayout()),
|
_substringLayout(new QVBoxLayout()),
|
||||||
_subStringIndexEntryLayout(new QHBoxLayout()),
|
_subStringControlsLayout(new QHBoxLayout()),
|
||||||
_subStringRegexEntryLayout(new QHBoxLayout()),
|
|
||||||
_subStringStart(new VariableSpinBox(this)),
|
_subStringStart(new VariableSpinBox(this)),
|
||||||
_subStringSize(new VariableSpinBox(this)),
|
_subStringSize(new VariableSpinBox(this)),
|
||||||
_substringRegex(new RegexConfigWidget(parent)),
|
_subStringRegex(new RegexConfigWidget(parent)),
|
||||||
_regexPattern(new ResizingPlainTextEdit(this, 10, 1, 1)),
|
_regexPattern(new ResizingPlainTextEdit(this, 10, 1, 1)),
|
||||||
_regexMatchIdx(new VariableSpinBox(this)),
|
_regexMatchIdx(new VariableSpinBox(this)),
|
||||||
_findReplaceLayout(new QHBoxLayout()),
|
_findReplaceLayout(new QHBoxLayout()),
|
||||||
@@ -837,7 +882,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.generateInteger"),
|
"AdvSceneSwitcher.action.variable.generateInteger"),
|
||||||
this)),
|
this)),
|
||||||
_randomLayout(new QVBoxLayout()),
|
_randomNumberLayout(new QVBoxLayout()),
|
||||||
|
_randomValues(new StringListEdit(this)),
|
||||||
|
_allowRepeatValues(new QCheckBox(
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.variable.type.allowRepeat"),
|
||||||
|
this)),
|
||||||
|
_randomValueLayout(new QVBoxLayout()),
|
||||||
_jsonQuery(new VariableLineEdit(this)),
|
_jsonQuery(new VariableLineEdit(this)),
|
||||||
_jsonQueryHelp(new HelpIcon(
|
_jsonQueryHelp(new HelpIcon(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
@@ -873,6 +924,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
_randomNumberStart->setMaximum(9999999999);
|
_randomNumberStart->setMaximum(9999999999);
|
||||||
_randomNumberEnd->setMinimum(-9999999999);
|
_randomNumberEnd->setMinimum(-9999999999);
|
||||||
_randomNumberEnd->setMaximum(9999999999);
|
_randomNumberEnd->setMaximum(9999999999);
|
||||||
|
_randomValues->SetMaxStringSize(99999999);
|
||||||
_jsonIndex->setMaximum(999);
|
_jsonIndex->setMaximum(999);
|
||||||
|
|
||||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||||
@@ -901,7 +953,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
_subStringSize,
|
_subStringSize,
|
||||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||||
this, SLOT(SubStringSizeChanged(const NumberVariable<int> &)));
|
this, SLOT(SubStringSizeChanged(const NumberVariable<int> &)));
|
||||||
QWidget::connect(_substringRegex,
|
QWidget::connect(_subStringRegex,
|
||||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||||
SLOT(SubStringRegexChanged(const RegexConfig &)));
|
SLOT(SubStringRegexChanged(const RegexConfig &)));
|
||||||
QWidget::connect(_regexPattern, SIGNAL(textChanged()), this,
|
QWidget::connect(_regexPattern, SIGNAL(textChanged()), this,
|
||||||
@@ -961,6 +1013,11 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
SLOT(RandomNumberEndChanged(const NumberVariable<double> &)));
|
SLOT(RandomNumberEndChanged(const NumberVariable<double> &)));
|
||||||
QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this,
|
||||||
SLOT(GenerateIntegerChanged(int)));
|
SLOT(GenerateIntegerChanged(int)));
|
||||||
|
QWidget::connect(_randomValues,
|
||||||
|
SIGNAL(StringListChanged(const StringList &)), this,
|
||||||
|
SLOT(RandomValueListChanged(const StringList &)));
|
||||||
|
QWidget::connect(_allowRepeatValues, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(AllowRepeatValuesChanged(int)));
|
||||||
QWidget::connect(_jsonQuery, SIGNAL(editingFinished()), this,
|
QWidget::connect(_jsonQuery, SIGNAL(editingFinished()), this,
|
||||||
SLOT(JsonQueryChanged()));
|
SLOT(JsonQueryChanged()));
|
||||||
QWidget::connect(
|
QWidget::connect(
|
||||||
@@ -975,9 +1032,6 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
{"{{strValue}}", _strValue},
|
{"{{strValue}}", _strValue},
|
||||||
{"{{numValue}}", _numValue},
|
{"{{numValue}}", _numValue},
|
||||||
{"{{segmentIndex}}", _segmentIdx},
|
{"{{segmentIndex}}", _segmentIdx},
|
||||||
{"{{subStringStart}}", _subStringStart},
|
|
||||||
{"{{subStringSize}}", _subStringSize},
|
|
||||||
{"{{regexMatchIdx}}", _regexMatchIdx},
|
|
||||||
{"{{findRegex}}", _findRegex},
|
{"{{findRegex}}", _findRegex},
|
||||||
{"{{findStr}}", _findStr},
|
{"{{findStr}}", _findStr},
|
||||||
{"{{replaceStr}}", _replaceStr},
|
{"{{replaceStr}}", _replaceStr},
|
||||||
@@ -1009,12 +1063,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.layout.substringIndex"),
|
"AdvSceneSwitcher.action.variable.layout.substringIndex"),
|
||||||
_subStringIndexEntryLayout, widgetPlaceholders);
|
_subStringControlsLayout, widgetPlaceholders);
|
||||||
|
|
||||||
PlaceWidgets(
|
|
||||||
obs_module_text(
|
|
||||||
"AdvSceneSwitcher.action.variable.layout.substringRegex"),
|
|
||||||
_subStringRegexEntryLayout, widgetPlaceholders);
|
|
||||||
|
|
||||||
PlaceWidgets(
|
PlaceWidgets(
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
@@ -1035,15 +1084,17 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.action.variable.layout.randomNumber"),
|
"AdvSceneSwitcher.action.variable.layout.randomNumber"),
|
||||||
randomLayout, widgetPlaceholders);
|
randomLayout, widgetPlaceholders);
|
||||||
_randomLayout->addLayout(randomLayout);
|
_randomNumberLayout->addLayout(randomLayout);
|
||||||
_randomLayout->addWidget(_generateInteger);
|
_randomNumberLayout->addWidget(_generateInteger);
|
||||||
|
|
||||||
|
_randomValueLayout->addWidget(_randomValues);
|
||||||
|
_randomValueLayout->addWidget(_allowRepeatValues);
|
||||||
|
|
||||||
auto regexConfigLayout = new QHBoxLayout;
|
auto regexConfigLayout = new QHBoxLayout;
|
||||||
regexConfigLayout->addWidget(_substringRegex);
|
regexConfigLayout->addWidget(_subStringRegex);
|
||||||
regexConfigLayout->addStretch();
|
regexConfigLayout->addStretch();
|
||||||
|
|
||||||
_substringLayout->addLayout(_subStringIndexEntryLayout);
|
_substringLayout->addLayout(_subStringControlsLayout);
|
||||||
_substringLayout->addLayout(_subStringRegexEntryLayout);
|
|
||||||
_substringLayout->addWidget(_regexPattern);
|
_substringLayout->addWidget(_regexPattern);
|
||||||
_substringLayout->addLayout(regexConfigLayout);
|
_substringLayout->addLayout(regexConfigLayout);
|
||||||
|
|
||||||
@@ -1056,7 +1107,8 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
|||||||
layout->addWidget(_mathExpressionResult);
|
layout->addWidget(_mathExpressionResult);
|
||||||
layout->addLayout(_promptLayout);
|
layout->addLayout(_promptLayout);
|
||||||
layout->addLayout(_placeholderLayout);
|
layout->addLayout(_placeholderLayout);
|
||||||
layout->addLayout(_randomLayout);
|
layout->addLayout(_randomNumberLayout);
|
||||||
|
layout->addLayout(_randomValueLayout);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
@@ -1090,7 +1142,7 @@ void MacroActionVariableEdit::UpdateEntryData()
|
|||||||
: MacroSegmentSelection::Type::ACTION);
|
: MacroSegmentSelection::Type::ACTION);
|
||||||
_subStringStart->SetValue(_entryData->_subStringStart);
|
_subStringStart->SetValue(_entryData->_subStringStart);
|
||||||
_subStringSize->SetValue(_entryData->_subStringSize);
|
_subStringSize->SetValue(_entryData->_subStringSize);
|
||||||
_substringRegex->SetRegexConfig(_entryData->_subStringRegex);
|
_subStringRegex->SetRegexConfig(_entryData->_subStringRegex);
|
||||||
_findRegex->SetRegexConfig(_entryData->_findRegex);
|
_findRegex->SetRegexConfig(_entryData->_findRegex);
|
||||||
_regexPattern->setPlainText(
|
_regexPattern->setPlainText(
|
||||||
QString::fromStdString(_entryData->_regexPattern));
|
QString::fromStdString(_entryData->_regexPattern));
|
||||||
@@ -1115,6 +1167,8 @@ void MacroActionVariableEdit::UpdateEntryData()
|
|||||||
_randomNumberStart->SetValue(_entryData->_randomNumberStart);
|
_randomNumberStart->SetValue(_entryData->_randomNumberStart);
|
||||||
_randomNumberEnd->SetValue(_entryData->_randomNumberEnd);
|
_randomNumberEnd->SetValue(_entryData->_randomNumberEnd);
|
||||||
_generateInteger->setChecked(_entryData->_generateInteger);
|
_generateInteger->setChecked(_entryData->_generateInteger);
|
||||||
|
_allowRepeatValues->setChecked(_entryData->_allowRepeatValues);
|
||||||
|
_randomValues->SetStringList(_entryData->_randomValues);
|
||||||
_jsonQuery->setText(_entryData->_jsonQuery);
|
_jsonQuery->setText(_entryData->_jsonQuery);
|
||||||
_jsonIndex->SetValue(_entryData->_jsonIndex);
|
_jsonIndex->SetValue(_entryData->_jsonIndex);
|
||||||
|
|
||||||
@@ -1457,6 +1511,18 @@ void MacroActionVariableEdit::GenerateIntegerChanged(int value)
|
|||||||
_entryData->_generateInteger = value;
|
_entryData->_generateInteger = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionVariableEdit::RandomValueListChanged(const StringList &values)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_randomValues = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionVariableEdit::AllowRepeatValuesChanged(int value)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_allowRepeatValues = value;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroActionVariableEdit::JsonQueryChanged()
|
void MacroActionVariableEdit::JsonQueryChanged()
|
||||||
{
|
{
|
||||||
GUARD_LOADING_AND_LOCK();
|
GUARD_LOADING_AND_LOCK();
|
||||||
@@ -1495,8 +1561,20 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
{"{{jsonQuery}}", _jsonQuery},
|
{"{{jsonQuery}}", _jsonQuery},
|
||||||
{"{{jsonQueryHelp}}", _jsonQueryHelp},
|
{"{{jsonQueryHelp}}", _jsonQueryHelp},
|
||||||
{"{{jsonIndex}}", _jsonIndex},
|
{"{{jsonIndex}}", _jsonIndex},
|
||||||
|
{"{{subStringRegex}}", _subStringRegex},
|
||||||
|
{"{{subStringStart}}", _subStringStart},
|
||||||
|
{"{{subStringSize}}", _subStringSize},
|
||||||
|
{"{{regexMatchIdx}}", _regexMatchIdx},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (const auto &[_, widget] : widgetPlaceholders) {
|
||||||
|
_entryLayout->removeWidget(widget);
|
||||||
|
_subStringControlsLayout->removeWidget(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearLayout(_entryLayout);
|
||||||
|
ClearLayout(_subStringControlsLayout);
|
||||||
|
|
||||||
const char *layoutString = "";
|
const char *layoutString = "";
|
||||||
if (_entryData->_action == MacroActionVariable::Action::PAD) {
|
if (_entryData->_action == MacroActionVariable::Action::PAD) {
|
||||||
layoutString = obs_module_text(
|
layoutString = obs_module_text(
|
||||||
@@ -1510,11 +1588,6 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
"AdvSceneSwitcher.action.variable.layout.other");
|
"AdvSceneSwitcher.action.variable.layout.other");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &[_, widget] : widgetPlaceholders) {
|
|
||||||
_entryLayout->removeWidget(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearLayout(_entryLayout);
|
|
||||||
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
|
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
|
||||||
|
|
||||||
if (_entryData->_action == MacroActionVariable::Action::SET_VALUE ||
|
if (_entryData->_action == MacroActionVariable::Action::SET_VALUE ||
|
||||||
@@ -1561,15 +1634,25 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
MacroActionVariable::Action::SET_ACTION_VALUE ||
|
MacroActionVariable::Action::SET_ACTION_VALUE ||
|
||||||
_entryData->_action ==
|
_entryData->_action ==
|
||||||
MacroActionVariable::Action::SET_CONDITION_VALUE);
|
MacroActionVariable::Action::SET_CONDITION_VALUE);
|
||||||
|
|
||||||
|
bool showRegex = _entryData->_subStringRegex.Enabled();
|
||||||
|
layoutString =
|
||||||
|
showRegex
|
||||||
|
? "AdvSceneSwitcher.action.variable.layout.substringRegex"
|
||||||
|
: "AdvSceneSwitcher.action.variable.layout.substringIndex";
|
||||||
|
PlaceWidgets(obs_module_text(layoutString), _subStringControlsLayout,
|
||||||
|
widgetPlaceholders);
|
||||||
|
_subStringStart->setVisible(!showRegex);
|
||||||
|
_subStringSize->setVisible(!showRegex);
|
||||||
|
_regexMatchIdx->setVisible(showRegex);
|
||||||
|
|
||||||
SetLayoutVisible(_substringLayout,
|
SetLayoutVisible(_substringLayout,
|
||||||
_entryData->_action ==
|
_entryData->_action ==
|
||||||
MacroActionVariable::Action::SUBSTRING);
|
MacroActionVariable::Action::SUBSTRING);
|
||||||
if (_entryData->_action == MacroActionVariable::Action::SUBSTRING) {
|
_regexPattern->setVisible(
|
||||||
bool showRegex = _entryData->_subStringRegex.Enabled();
|
showRegex &&
|
||||||
SetLayoutVisible(_subStringIndexEntryLayout, !showRegex);
|
_entryData->_action == MacroActionVariable::Action::SUBSTRING);
|
||||||
SetLayoutVisible(_subStringRegexEntryLayout, showRegex);
|
|
||||||
_regexPattern->setVisible(showRegex);
|
|
||||||
}
|
|
||||||
SetLayoutVisible(_findReplaceLayout,
|
SetLayoutVisible(_findReplaceLayout,
|
||||||
_entryData->_action ==
|
_entryData->_action ==
|
||||||
MacroActionVariable::Action::FIND_AND_REPLACE);
|
MacroActionVariable::Action::FIND_AND_REPLACE);
|
||||||
@@ -1634,9 +1717,13 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
|||||||
MacroActionVariable::Action::PAD);
|
MacroActionVariable::Action::PAD);
|
||||||
_caseType->setVisible(_entryData->_action ==
|
_caseType->setVisible(_entryData->_action ==
|
||||||
MacroActionVariable::Action::CHANGE_CASE);
|
MacroActionVariable::Action::CHANGE_CASE);
|
||||||
SetLayoutVisible(_randomLayout,
|
SetLayoutVisible(_randomNumberLayout,
|
||||||
_entryData->_action ==
|
_entryData->_action ==
|
||||||
MacroActionVariable::Action::RANDOM_NUMBER);
|
MacroActionVariable::Action::RANDOM_NUMBER);
|
||||||
|
SetLayoutVisible(
|
||||||
|
_randomValueLayout,
|
||||||
|
_entryData->_action ==
|
||||||
|
MacroActionVariable::Action::RANDOM_LIST_VALUE);
|
||||||
_jsonQuery->setVisible(_entryData->_action ==
|
_jsonQuery->setVisible(_entryData->_action ==
|
||||||
MacroActionVariable::Action::QUERY_JSON);
|
MacroActionVariable::Action::QUERY_JSON);
|
||||||
_jsonQueryHelp->setVisible(_entryData->_action ==
|
_jsonQueryHelp->setVisible(_entryData->_action ==
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "resizing-text-edit.hpp"
|
#include "resizing-text-edit.hpp"
|
||||||
#include "scene-selection.hpp"
|
#include "scene-selection.hpp"
|
||||||
#include "single-char-selection.hpp"
|
#include "single-char-selection.hpp"
|
||||||
|
#include "string-list.hpp"
|
||||||
#include "variable-line-edit.hpp"
|
#include "variable-line-edit.hpp"
|
||||||
#include "variable-text-edit.hpp"
|
#include "variable-text-edit.hpp"
|
||||||
#include "variable-spinbox.hpp"
|
#include "variable-spinbox.hpp"
|
||||||
@@ -56,6 +57,7 @@ public:
|
|||||||
QUERY_JSON,
|
QUERY_JSON,
|
||||||
ARRAY_JSON,
|
ARRAY_JSON,
|
||||||
COPY_VAR,
|
COPY_VAR,
|
||||||
|
RANDOM_LIST_VALUE,
|
||||||
};
|
};
|
||||||
|
|
||||||
Action _action = Action::SET_VALUE;
|
Action _action = Action::SET_VALUE;
|
||||||
@@ -107,6 +109,10 @@ public:
|
|||||||
DoubleVariable _randomNumberEnd = 100;
|
DoubleVariable _randomNumberEnd = 100;
|
||||||
bool _generateInteger = true;
|
bool _generateInteger = true;
|
||||||
|
|
||||||
|
StringList _randomValues = {"value1", "value2", "value3"};
|
||||||
|
bool _allowRepeatValues = true;
|
||||||
|
std::optional<std::string> _lastRandomValue;
|
||||||
|
|
||||||
StringVariable _jsonQuery = "$.some.nested.value";
|
StringVariable _jsonQuery = "$.some.nested.value";
|
||||||
IntVariable _jsonIndex = 0;
|
IntVariable _jsonIndex = 0;
|
||||||
|
|
||||||
@@ -119,6 +125,7 @@ private:
|
|||||||
void HandleCaseChange(Variable *);
|
void HandleCaseChange(Variable *);
|
||||||
void SetToSceneItemName(Variable *);
|
void SetToSceneItemName(Variable *);
|
||||||
void GenerateRandomNumber(Variable *);
|
void GenerateRandomNumber(Variable *);
|
||||||
|
void PickRandomValue(Variable *);
|
||||||
|
|
||||||
std::weak_ptr<MacroSegment> _macroSegment;
|
std::weak_ptr<MacroSegment> _macroSegment;
|
||||||
int _segmentIdxLoadValue = -1;
|
int _segmentIdxLoadValue = -1;
|
||||||
@@ -175,6 +182,8 @@ private slots:
|
|||||||
void RandomNumberStartChanged(const NumberVariable<double> &);
|
void RandomNumberStartChanged(const NumberVariable<double> &);
|
||||||
void RandomNumberEndChanged(const NumberVariable<double> &);
|
void RandomNumberEndChanged(const NumberVariable<double> &);
|
||||||
void GenerateIntegerChanged(int);
|
void GenerateIntegerChanged(int);
|
||||||
|
void RandomValueListChanged(const StringList &);
|
||||||
|
void AllowRepeatValuesChanged(int);
|
||||||
void JsonQueryChanged();
|
void JsonQueryChanged();
|
||||||
void JsonIndexChanged(const NumberVariable<int> &);
|
void JsonIndexChanged(const NumberVariable<int> &);
|
||||||
|
|
||||||
@@ -194,11 +203,10 @@ private:
|
|||||||
QLabel *_segmentValueStatus;
|
QLabel *_segmentValueStatus;
|
||||||
ResizingPlainTextEdit *_segmentValue;
|
ResizingPlainTextEdit *_segmentValue;
|
||||||
QVBoxLayout *_substringLayout;
|
QVBoxLayout *_substringLayout;
|
||||||
QHBoxLayout *_subStringIndexEntryLayout;
|
QHBoxLayout *_subStringControlsLayout;
|
||||||
QHBoxLayout *_subStringRegexEntryLayout;
|
|
||||||
VariableSpinBox *_subStringStart;
|
VariableSpinBox *_subStringStart;
|
||||||
VariableSpinBox *_subStringSize;
|
VariableSpinBox *_subStringSize;
|
||||||
RegexConfigWidget *_substringRegex;
|
RegexConfigWidget *_subStringRegex;
|
||||||
ResizingPlainTextEdit *_regexPattern;
|
ResizingPlainTextEdit *_regexPattern;
|
||||||
VariableSpinBox *_regexMatchIdx;
|
VariableSpinBox *_regexMatchIdx;
|
||||||
QHBoxLayout *_findReplaceLayout;
|
QHBoxLayout *_findReplaceLayout;
|
||||||
@@ -225,7 +233,10 @@ private:
|
|||||||
VariableDoubleSpinBox *_randomNumberStart;
|
VariableDoubleSpinBox *_randomNumberStart;
|
||||||
VariableDoubleSpinBox *_randomNumberEnd;
|
VariableDoubleSpinBox *_randomNumberEnd;
|
||||||
QCheckBox *_generateInteger;
|
QCheckBox *_generateInteger;
|
||||||
QVBoxLayout *_randomLayout;
|
QVBoxLayout *_randomNumberLayout;
|
||||||
|
StringListEdit *_randomValues;
|
||||||
|
QCheckBox *_allowRepeatValues;
|
||||||
|
QVBoxLayout *_randomValueLayout;
|
||||||
VariableLineEdit *_jsonQuery;
|
VariableLineEdit *_jsonQuery;
|
||||||
QLabel *_jsonQueryHelp;
|
QLabel *_jsonQueryHelp;
|
||||||
VariableSpinBox *_jsonIndex;
|
VariableSpinBox *_jsonIndex;
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ void DurationModifierEdit::Collapse(bool collapse)
|
|||||||
|
|
||||||
MacroConditionEdit::MacroConditionEdit(
|
MacroConditionEdit::MacroConditionEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
||||||
const std::string &id, bool isRootCondition)
|
bool isRootCondition)
|
||||||
: MacroSegmentEdit(parent),
|
: MacroSegmentEdit(parent),
|
||||||
_logicSelection(new QComboBox()),
|
_logicSelection(new QComboBox()),
|
||||||
_conditionSelection(new FilterComboBox()),
|
_conditionSelection(new FilterComboBox()),
|
||||||
|
|||||||
@@ -34,11 +34,9 @@ class MacroConditionEdit : public MacroSegmentEdit {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MacroConditionEdit(
|
MacroConditionEdit(QWidget *parent = nullptr,
|
||||||
QWidget *parent = nullptr,
|
std::shared_ptr<MacroCondition> * = nullptr,
|
||||||
std::shared_ptr<MacroCondition> * = nullptr,
|
bool root = true);
|
||||||
const std::string &id = MacroCondition::GetDefaultID().data(),
|
|
||||||
bool root = true);
|
|
||||||
bool IsRootNode() const;
|
bool IsRootNode() const;
|
||||||
void SetRootNode(bool);
|
void SetRootNode(bool);
|
||||||
void SetupWidgets(bool basicSetup = false);
|
void SetupWidgets(bool basicSetup = false);
|
||||||
|
|||||||
@@ -315,6 +315,7 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
|
|||||||
_actionIndex(new MacroSegmentSelection(
|
_actionIndex(new MacroSegmentSelection(
|
||||||
this, MacroSegmentSelection::Type::ACTION))
|
this, MacroSegmentSelection::Type::ACTION))
|
||||||
{
|
{
|
||||||
|
_macros->HideGroups();
|
||||||
_count->setMaximum(10000000);
|
_count->setMaximum(10000000);
|
||||||
populateTypeSelection(_types);
|
populateTypeSelection(_types);
|
||||||
populateCounterConditionSelection(_counterConditions);
|
populateCounterConditionSelection(_counterConditions);
|
||||||
|
|||||||
@@ -418,8 +418,7 @@ void MacroEdit::PopulateMacroActions(Macro &m, uint32_t afterIdx)
|
|||||||
ui->actionsList->SetVisibilityCheckEnable(false);
|
ui->actionsList->SetVisibilityCheckEnable(false);
|
||||||
|
|
||||||
for (; afterIdx < actions.size(); afterIdx++) {
|
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);
|
ui->actionsList->Add(newEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,8 +443,7 @@ void MacroEdit::PopulateMacroElseActions(Macro &m, uint32_t afterIdx)
|
|||||||
ui->elseActionsList->SetVisibilityCheckEnable(false);
|
ui->elseActionsList->SetVisibilityCheckEnable(false);
|
||||||
|
|
||||||
for (; afterIdx < actions.size(); afterIdx++) {
|
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);
|
ui->elseActionsList->Add(newEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,8 +470,7 @@ void MacroEdit::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
|
|||||||
|
|
||||||
for (; afterIdx < conditions.size(); afterIdx++) {
|
for (; afterIdx < conditions.size(); afterIdx++) {
|
||||||
auto newEntry = new MacroConditionEdit(
|
auto newEntry = new MacroConditionEdit(
|
||||||
this, &conditions[afterIdx],
|
this, &conditions[afterIdx], root);
|
||||||
conditions[afterIdx]->GetId(), root);
|
|
||||||
ui->conditionsList->Add(newEntry);
|
ui->conditionsList->Add(newEntry);
|
||||||
root = false;
|
root = false;
|
||||||
}
|
}
|
||||||
@@ -1083,8 +1080,7 @@ void MacroEdit::AddMacroAction(Macro *macro, int idx, const std::string &id,
|
|||||||
RunAndClearPostLoadSteps();
|
RunAndClearPostLoadSteps();
|
||||||
macro->UpdateActionIndices();
|
macro->UpdateActionIndices();
|
||||||
ui->actionsList->Insert(
|
ui->actionsList->Insert(
|
||||||
idx,
|
idx, new MacroActionEdit(this, ¯o->Actions()[idx]));
|
||||||
new MacroActionEdit(this, ¯o->Actions()[idx], id));
|
|
||||||
SetActionData(*macro);
|
SetActionData(*macro);
|
||||||
}
|
}
|
||||||
HighlightAction(idx);
|
HighlightAction(idx);
|
||||||
@@ -1386,8 +1382,8 @@ void MacroEdit::AddMacroElseAction(Macro *macro, int idx, const std::string &id,
|
|||||||
RunAndClearPostLoadSteps();
|
RunAndClearPostLoadSteps();
|
||||||
macro->UpdateElseActionIndices();
|
macro->UpdateElseActionIndices();
|
||||||
ui->elseActionsList->Insert(
|
ui->elseActionsList->Insert(
|
||||||
idx, new MacroActionEdit(
|
idx,
|
||||||
this, ¯o->ElseActions()[idx], id));
|
new MacroActionEdit(this, ¯o->ElseActions()[idx]));
|
||||||
SetElseActionData(*macro);
|
SetElseActionData(*macro);
|
||||||
}
|
}
|
||||||
HighlightElseAction(idx);
|
HighlightElseAction(idx);
|
||||||
@@ -1589,7 +1585,7 @@ void MacroEdit::AddMacroCondition(Macro *macro, int idx, const std::string &id,
|
|||||||
ui->conditionsList->Insert(
|
ui->conditionsList->Insert(
|
||||||
idx,
|
idx,
|
||||||
new MacroConditionEdit(this, ¯o->Conditions()[idx],
|
new MacroConditionEdit(this, ¯o->Conditions()[idx],
|
||||||
id, idx == 0));
|
idx == 0));
|
||||||
SetConditionData(*macro);
|
SetConditionData(*macro);
|
||||||
}
|
}
|
||||||
HighlightCondition(idx);
|
HighlightCondition(idx);
|
||||||
|
|||||||
@@ -117,9 +117,32 @@ GetMacroConditions(Macro *macro)
|
|||||||
return macro->Conditions();
|
return macro->Conditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view GetSceneSwitchActionId()
|
bool IsGroupMacro(Macro *macro)
|
||||||
{
|
{
|
||||||
return MacroAction::GetDefaultID();
|
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 ¯os = GetTopLevelMacros();
|
||||||
|
for (auto it = macros.begin(); it < macros.end(); it++) {
|
||||||
|
if ((*it)->Name() != macro->Name()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (uint32_t i = 1; i <= macro->GroupSize(); i++) {
|
||||||
|
entries.emplace_back(*std::next(it, i));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::condition_variable &GetMacroWaitCV()
|
std::condition_variable &GetMacroWaitCV()
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ class Macro;
|
|||||||
class MacroAction;
|
class MacroAction;
|
||||||
class MacroCondition;
|
class MacroCondition;
|
||||||
|
|
||||||
|
static const int macro_func = 10;
|
||||||
|
|
||||||
EXPORT std::deque<std::shared_ptr<Macro>> &GetTopLevelMacros();
|
EXPORT std::deque<std::shared_ptr<Macro>> &GetTopLevelMacros();
|
||||||
std::deque<std::shared_ptr<Macro>> &GetTemporaryMacros();
|
std::deque<std::shared_ptr<Macro>> &GetTemporaryMacros();
|
||||||
EXPORT std::deque<std::shared_ptr<Macro>> GetAllMacros();
|
EXPORT std::deque<std::shared_ptr<Macro>> GetAllMacros();
|
||||||
@@ -35,9 +37,8 @@ GetMacroElseActions(Macro *);
|
|||||||
EXPORT std::optional<std::deque<std::shared_ptr<MacroCondition>>>
|
EXPORT std::optional<std::deque<std::shared_ptr<MacroCondition>>>
|
||||||
GetMacroConditions(Macro *);
|
GetMacroConditions(Macro *);
|
||||||
|
|
||||||
std::string_view GetSceneSwitchActionId();
|
EXPORT bool IsGroupMacro(Macro *);
|
||||||
|
EXPORT std::vector<std::shared_ptr<Macro>> GetGroupMacroEntries(Macro *);
|
||||||
constexpr auto macro_func = 10;
|
|
||||||
|
|
||||||
EXPORT std::condition_variable &GetMacroWaitCV();
|
EXPORT std::condition_variable &GetMacroWaitCV();
|
||||||
EXPORT std::condition_variable &GetMacroTransitionCV();
|
EXPORT std::condition_variable &GetMacroTransitionCV();
|
||||||
|
|||||||
@@ -244,22 +244,9 @@ bool MacroMatchesSearchFilter(Macro *macro)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (macro->IsGroup()) {
|
if (macro->IsGroup()) {
|
||||||
std::vector<std::shared_ptr<Macro>> subItems;
|
const auto groupEntries = GetGroupMacroEntries(macro);
|
||||||
subItems.reserve(macro->GroupSize());
|
for (const auto &entry : groupEntries) {
|
||||||
|
if (MacroMatchesSearchFilter(entry.get())) {
|
||||||
const auto ¯os = GetTopLevelMacros();
|
|
||||||
for (auto it = macros.begin(); it < macros.end(); it++) {
|
|
||||||
if ((*it)->Name() != macro->Name()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (uint32_t i = 1; i <= macro->GroupSize(); i++) {
|
|
||||||
subItems.emplace_back(*std::next(it, i));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &item : subItems) {
|
|
||||||
if (MacroMatchesSearchFilter(item.get())) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ MacroSelection::MacroSelection(QWidget *parent)
|
|||||||
obs_module_text("AdvSceneSwitcher.selectMacro"))
|
obs_module_text("AdvSceneSwitcher.selectMacro"))
|
||||||
{
|
{
|
||||||
for (const auto &m : GetTopLevelMacros()) {
|
for (const auto &m : GetTopLevelMacros()) {
|
||||||
if (m->IsGroup()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
addItem(QString::fromStdString(m->Name()));
|
addItem(QString::fromStdString(m->Name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +63,22 @@ void MacroSelection::HideSelectedMacro()
|
|||||||
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
|
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroSelection::HideGroups()
|
||||||
|
{
|
||||||
|
for (const auto ¯o : GetTopLevelMacros()) {
|
||||||
|
if (!macro->IsGroup()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = findText(QString::fromStdString(macro->Name()));
|
||||||
|
if (idx == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qobject_cast<QListView *>(view())->setRowHidden(idx, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MacroSelection::ShowAllMacros()
|
void MacroSelection::ShowAllMacros()
|
||||||
{
|
{
|
||||||
auto v = qobject_cast<QListView *>(view());
|
auto v = qobject_cast<QListView *>(view());
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public:
|
|||||||
MacroSelection(QWidget *parent);
|
MacroSelection(QWidget *parent);
|
||||||
void SetCurrentMacro(const MacroRef &);
|
void SetCurrentMacro(const MacroRef &);
|
||||||
void HideSelectedMacro(); // Macro currently being edited
|
void HideSelectedMacro(); // Macro currently being edited
|
||||||
|
void HideGroups();
|
||||||
void ShowAllMacros();
|
void ShowAllMacros();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ namespace advss {
|
|||||||
|
|
||||||
static QObject *addPulse = nullptr;
|
static QObject *addPulse = nullptr;
|
||||||
static QTimer onChangeHighlightTimer;
|
static QTimer onChangeHighlightTimer;
|
||||||
|
static std::chrono::high_resolution_clock::time_point
|
||||||
|
lastOnChangeHighlightCheckTime{};
|
||||||
|
|
||||||
static void disableAddButtonHighlight()
|
static void disableAddButtonHighlight()
|
||||||
{
|
{
|
||||||
@@ -147,20 +149,7 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
|||||||
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> ¯os,
|
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> ¯os,
|
||||||
const std::shared_ptr<Macro> &group)
|
const std::shared_ptr<Macro> &group)
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<Macro>> subitems;
|
auto subitems = GetGroupMacroEntries(group.get());
|
||||||
subitems.reserve(group->GroupSize());
|
|
||||||
|
|
||||||
// Find all subitems
|
|
||||||
auto allMacros = GetTopLevelMacros();
|
|
||||||
for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
|
|
||||||
if ((*it)->Name() != group->Name()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
|
|
||||||
subitems.emplace_back(*std::next(it, i));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove subitems which were already selected to avoid duplicates
|
// Remove subitems which were already selected to avoid duplicates
|
||||||
for (const auto &subitem : subitems) {
|
for (const auto &subitem : subitems) {
|
||||||
@@ -563,10 +552,14 @@ void AdvSceneSwitcher::HighlightOnChange() const
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (macro->OnChangePreventedActionsRecently()) {
|
if (macro->OnChangePreventedActionsSince(
|
||||||
|
lastOnChangeHighlightCheckTime)) {
|
||||||
HighlightWidget(ui->runMacroOnChange, Qt::yellow,
|
HighlightWidget(ui->runMacroOnChange, Qt::yellow,
|
||||||
Qt::transparent, true);
|
Qt::transparent, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastOnChangeHighlightCheckTime =
|
||||||
|
std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_macroSettings_clicked()
|
void AdvSceneSwitcher::on_macroSettings_clicked()
|
||||||
@@ -665,6 +658,8 @@ void AdvSceneSwitcher::SetupMacroTab()
|
|||||||
ui->macroPriorityWarning->setVisible(
|
ui->macroPriorityWarning->setVisible(
|
||||||
switcher->functionNamesByPriority[0] != macro_func);
|
switcher->functionNamesByPriority[0] != macro_func);
|
||||||
|
|
||||||
|
lastOnChangeHighlightCheckTime =
|
||||||
|
std::chrono::high_resolution_clock::now();
|
||||||
onChangeHighlightTimer.setInterval(1500);
|
onChangeHighlightTimer.setInterval(1500);
|
||||||
connect(&onChangeHighlightTimer, SIGNAL(timeout()), this,
|
connect(&onChangeHighlightTimer, SIGNAL(timeout()), this,
|
||||||
SLOT(HighlightOnChange()));
|
SLOT(HighlightOnChange()));
|
||||||
|
|||||||
@@ -5,18 +5,17 @@
|
|||||||
#include "path-helpers.hpp"
|
#include "path-helpers.hpp"
|
||||||
#include "sync-helpers.hpp"
|
#include "sync-helpers.hpp"
|
||||||
#include "ui-helpers.hpp"
|
#include "ui-helpers.hpp"
|
||||||
#include "utility.hpp"
|
|
||||||
|
|
||||||
#include <obs.h>
|
#include <obs.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QSpacerItem>
|
#include <QSpacerItem>
|
||||||
#include <QPushButton>
|
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QStylePainter>
|
#include <QStylePainter>
|
||||||
|
#include <QToolTip>
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::shared_ptr<advss::Macro>);
|
Q_DECLARE_METATYPE(std::shared_ptr<advss::Macro>);
|
||||||
|
|
||||||
@@ -62,12 +61,11 @@ MacroTreeItem::MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macroItem,
|
|||||||
|
|
||||||
_boxLayout = new QHBoxLayout();
|
_boxLayout = new QHBoxLayout();
|
||||||
_boxLayout->setContentsMargins(0, 0, 0, 0);
|
_boxLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
_boxLayout->addWidget(_running);
|
|
||||||
if (isGroup) {
|
if (isGroup) {
|
||||||
_boxLayout->addWidget(_iconLabel);
|
_boxLayout->addWidget(_iconLabel);
|
||||||
_boxLayout->addSpacing(2);
|
_boxLayout->addSpacing(2);
|
||||||
_running->hide();
|
|
||||||
}
|
}
|
||||||
|
_boxLayout->addWidget(_running);
|
||||||
_boxLayout->addWidget(_label);
|
_boxLayout->addWidget(_label);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
/* Hack: Fixes a bug where scrollbars would be above the lock icon */
|
/* Hack: Fixes a bug where scrollbars would be above the lock icon */
|
||||||
@@ -77,29 +75,138 @@ MacroTreeItem::MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macroItem,
|
|||||||
Update(true);
|
Update(true);
|
||||||
setLayout(_boxLayout);
|
setLayout(_boxLayout);
|
||||||
|
|
||||||
auto setRunning = [this](bool val) {
|
connect(_running, SIGNAL(clicked(bool)), this,
|
||||||
_macro->SetPaused(!val);
|
SLOT(RunningClicked(bool)));
|
||||||
};
|
|
||||||
connect(_running, &QAbstractButton::clicked, setRunning);
|
|
||||||
connect(MacroSignalManager::Instance(), SIGNAL(HighlightChanged(bool)),
|
connect(MacroSignalManager::Instance(), SIGNAL(HighlightChanged(bool)),
|
||||||
this, SLOT(EnableHighlight(bool)));
|
this, SLOT(EnableHighlight(bool)));
|
||||||
connect(MacroSignalManager::Instance(),
|
connect(MacroSignalManager::Instance(),
|
||||||
SIGNAL(Rename(const QString &, const QString &)), this,
|
SIGNAL(Rename(const QString &, const QString &)), this,
|
||||||
SLOT(MacroRenamed(const QString &, const QString &)));
|
SLOT(MacroRenamed(const QString &, const QString &)));
|
||||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(HighlightIfExecuted()));
|
connect(&_timer, SIGNAL(timeout()), this, SLOT(HighlightIfExecuted()));
|
||||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdatePaused()));
|
connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateRunning()));
|
||||||
|
|
||||||
|
UpdateRunning();
|
||||||
_timer.start(1500);
|
_timer.start(1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MacroTreeItem::event(QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() != QEvent::ToolTip) {
|
||||||
|
return QFrame::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_macro->IsGroup()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
if (!_macro->WasExecutedSince({})) {
|
||||||
|
text = obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.macroNotYetExecutedTooltip");
|
||||||
|
} else {
|
||||||
|
const QString formatStr = obs_module_text(
|
||||||
|
"AdvSceneSwitcher.macroTab.macroLastExecutedTooltip");
|
||||||
|
const auto secondsSinceLastRun =
|
||||||
|
std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
std::chrono::high_resolution_clock::now() -
|
||||||
|
_macro->GetLastExecutionTime());
|
||||||
|
text = formatStr.arg(secondsSinceLastRun.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto helpEvent = static_cast<QHelpEvent *>(event);
|
||||||
|
QToolTip::showText(helpEvent->globalPos(), text, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroTreeItem::EnableHighlight(bool enable)
|
void MacroTreeItem::EnableHighlight(bool enable)
|
||||||
{
|
{
|
||||||
_highlight = enable;
|
_highlight = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroTreeItem::UpdatePaused()
|
void MacroTreeItem::RunningClicked(bool running)
|
||||||
|
{
|
||||||
|
const auto updateWidget = [this](Macro *macro) {
|
||||||
|
if (!macro) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ¯os = 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 ¯o : macros) {
|
||||||
|
macro->SetPaused(!running);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update backend values before updating UI to prevent flickering in
|
||||||
|
// running state of the group
|
||||||
|
for (const auto ¯o : macros) {
|
||||||
|
updateWidget(macro.get());
|
||||||
|
}
|
||||||
|
UpdateRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroTreeItem::UpdateRunning()
|
||||||
{
|
{
|
||||||
const QSignalBlocker blocker(_running);
|
const QSignalBlocker blocker(_running);
|
||||||
_running->setChecked(!_macro->Paused());
|
|
||||||
|
if (!_macro->IsGroup()) {
|
||||||
|
_running->setChecked(!_macro->Paused());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto macros = GetGroupMacroEntries(_macro.get());
|
||||||
|
bool allRunning = true;
|
||||||
|
bool allPaused = true;
|
||||||
|
for (const auto ¯o : macros) {
|
||||||
|
if (macro->Paused()) {
|
||||||
|
allRunning = false;
|
||||||
|
} else {
|
||||||
|
allPaused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allRunning) {
|
||||||
|
_running->setCheckState(Qt::Checked);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allPaused) {
|
||||||
|
_running->setCheckState(Qt::Unchecked);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_running->setCheckState(Qt::PartiallyChecked);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroTreeItem::HighlightIfExecuted()
|
void MacroTreeItem::HighlightIfExecuted()
|
||||||
@@ -108,10 +215,19 @@ void MacroTreeItem::HighlightIfExecuted()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wasHighlighted = false;
|
||||||
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
|
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
|
||||||
_macro->WasExecutedSince(_lastHighlightCheckTime)) {
|
_macro->WasExecutedSince(_lastHighlightCheckTime)) {
|
||||||
HighlightWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
|
HighlightWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
|
||||||
|
wasHighlighted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!wasHighlighted &&
|
||||||
|
_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
|
||||||
|
_macro->OnChangePreventedActionsSince(_lastHighlightCheckTime)) {
|
||||||
|
HighlightWidget(this, Qt::yellow, QColor(0, 0, 0, 0), true);
|
||||||
|
}
|
||||||
|
|
||||||
_lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
|
_lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +304,7 @@ void MacroTreeItem::Update(bool force)
|
|||||||
_expand->blockSignals(true);
|
_expand->blockSignals(true);
|
||||||
_expand->setChecked(_macro->IsCollapsed());
|
_expand->setChecked(_macro->IsCollapsed());
|
||||||
_expand->blockSignals(false);
|
_expand->blockSignals(false);
|
||||||
connect(_expand, &QPushButton::toggled, this,
|
connect(_expand, &QCheckBox::toggled, this,
|
||||||
&MacroTreeItem::ExpandClicked);
|
&MacroTreeItem::ExpandClicked);
|
||||||
} else {
|
} else {
|
||||||
_spacer = new QSpacerItem(3, 1);
|
_spacer = new QSpacerItem(3, 1);
|
||||||
@@ -214,7 +330,6 @@ void MacroTreeModel::Reset(std::deque<std::shared_ptr<Macro>> &newItems)
|
|||||||
_macros = newItems;
|
_macros = newItems;
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
||||||
UpdateGroupState(false);
|
|
||||||
_mt->ResetWidgets();
|
_mt->ResetWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,9 +574,6 @@ void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
|
|||||||
|
|
||||||
_mt->selectionModel()->clear();
|
_mt->selectionModel()->clear();
|
||||||
|
|
||||||
if (isGroup) {
|
|
||||||
UpdateGroupState(true);
|
|
||||||
}
|
|
||||||
assert(IsInValidState());
|
assert(IsInValidState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,7 +662,6 @@ MacroTreeModel::MacroTreeModel(MacroTree *st_,
|
|||||||
_mt(st_),
|
_mt(st_),
|
||||||
_macros(macros)
|
_macros(macros)
|
||||||
{
|
{
|
||||||
UpdateGroupState(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int MacroTreeModel::rowCount(const QModelIndex &parent) const
|
int MacroTreeModel::rowCount(const QModelIndex &parent) const
|
||||||
@@ -704,7 +815,6 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
|
|||||||
offset++;
|
offset++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasGroups = true;
|
|
||||||
_mt->selectionModel()->clear();
|
_mt->selectionModel()->clear();
|
||||||
|
|
||||||
Reset(_macros);
|
Reset(_macros);
|
||||||
@@ -762,24 +872,6 @@ void MacroTreeModel::CollapseGroup(std::shared_ptr<Macro> item)
|
|||||||
assert(IsInValidState());
|
assert(IsInValidState());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroTreeModel::UpdateGroupState(bool update)
|
|
||||||
{
|
|
||||||
bool nowHasGroups = false;
|
|
||||||
for (auto &item : _macros) {
|
|
||||||
if (item->IsGroup()) {
|
|
||||||
nowHasGroups = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nowHasGroups != _hasGroups) {
|
|
||||||
_hasGroups = nowHasGroups;
|
|
||||||
if (update) {
|
|
||||||
_mt->UpdateWidgets(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroTree::Reset(std::deque<std::shared_ptr<Macro>> ¯os,
|
void MacroTree::Reset(std::deque<std::shared_ptr<Macro>> ¯os,
|
||||||
bool highlight)
|
bool highlight)
|
||||||
{
|
{
|
||||||
@@ -842,7 +934,6 @@ MacroTree::MacroTree(QWidget *parent_) : QListView(parent_)
|
|||||||
void MacroTree::ResetWidgets()
|
void MacroTree::ResetWidgets()
|
||||||
{
|
{
|
||||||
MacroTreeModel *mtm = GetModel();
|
MacroTreeModel *mtm = GetModel();
|
||||||
mtm->UpdateGroupState(false);
|
|
||||||
int modelIdx = 0;
|
int modelIdx = 0;
|
||||||
for (int i = 0; i < (int)mtm->_macros.size(); i++) {
|
for (int i = 0; i < (int)mtm->_macros.size(); i++) {
|
||||||
QModelIndex index = mtm->createIndex(modelIdx, 0, nullptr);
|
QModelIndex index = mtm->createIndex(modelIdx, 0, nullptr);
|
||||||
|
|||||||
@@ -34,10 +34,14 @@ public:
|
|||||||
explicit MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macro,
|
explicit MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macro,
|
||||||
bool highlight);
|
bool highlight);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool event(QEvent *) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ExpandClicked(bool checked);
|
void ExpandClicked(bool checked);
|
||||||
void EnableHighlight(bool enable);
|
void EnableHighlight(bool enable);
|
||||||
void UpdatePaused();
|
void RunningClicked(bool);
|
||||||
|
void UpdateRunning();
|
||||||
void HighlightIfExecuted();
|
void HighlightIfExecuted();
|
||||||
void MacroRenamed(const QString &, const QString &);
|
void MacroRenamed(const QString &, const QString &);
|
||||||
|
|
||||||
@@ -106,7 +110,6 @@ private:
|
|||||||
void UngroupSelectedGroups(QModelIndexList &indices);
|
void UngroupSelectedGroups(QModelIndexList &indices);
|
||||||
void ExpandGroup(std::shared_ptr<Macro> item);
|
void ExpandGroup(std::shared_ptr<Macro> item);
|
||||||
void CollapseGroup(std::shared_ptr<Macro> item);
|
void CollapseGroup(std::shared_ptr<Macro> item);
|
||||||
void UpdateGroupState(bool update);
|
|
||||||
int GetItemMacroIndex(const std::shared_ptr<Macro> &item) const;
|
int GetItemMacroIndex(const std::shared_ptr<Macro> &item) const;
|
||||||
int GetItemModelIndex(const std::shared_ptr<Macro> &item) const;
|
int GetItemModelIndex(const std::shared_ptr<Macro> &item) const;
|
||||||
bool IsLastItem(std::shared_ptr<Macro> item) const;
|
bool IsLastItem(std::shared_ptr<Macro> item) const;
|
||||||
@@ -114,7 +117,6 @@ private:
|
|||||||
|
|
||||||
MacroTree *_mt;
|
MacroTree *_mt;
|
||||||
std::deque<std::shared_ptr<Macro>> &_macros;
|
std::deque<std::shared_ptr<Macro>> &_macros;
|
||||||
bool _hasGroups = false;
|
|
||||||
|
|
||||||
friend class MacroTree;
|
friend class MacroTree;
|
||||||
friend class MacroTreeItem;
|
friend class MacroTreeItem;
|
||||||
|
|||||||
@@ -242,8 +242,12 @@ bool Macro::CheckConditions(bool ignorePause)
|
|||||||
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
||||||
|
|
||||||
_conditionSateChanged = _lastMatched != _matched;
|
_conditionSateChanged = _lastMatched != _matched;
|
||||||
if (!_conditionSateChanged && _performActionsOnChange) {
|
const bool hasActionsToExecute = _matched ? (_actions.size() > 0)
|
||||||
_onPreventedActionExecution = true;
|
: (_elseActions.size() > 0);
|
||||||
|
if (!_conditionSateChanged && _performActionsOnChange &&
|
||||||
|
hasActionsToExecute) {
|
||||||
|
_lastOnChangeActionsPreventedTime =
|
||||||
|
std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastMatched = _matched;
|
_lastMatched = _matched;
|
||||||
@@ -302,6 +306,16 @@ bool Macro::WasExecutedSince(const TimePoint &time) const
|
|||||||
return _lastExecutionTime > time;
|
return _lastExecutionTime > time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Macro::OnChangePreventedActionsSince(const TimePoint &time) const
|
||||||
|
{
|
||||||
|
return _lastOnChangeActionsPreventedTime > time;
|
||||||
|
}
|
||||||
|
|
||||||
|
Macro::TimePoint Macro::GetLastExecutionTime() const
|
||||||
|
{
|
||||||
|
return _lastExecutionTime;
|
||||||
|
}
|
||||||
|
|
||||||
bool Macro::ConditionsShouldBeChecked() const
|
bool Macro::ConditionsShouldBeChecked() const
|
||||||
{
|
{
|
||||||
if (!_useCustomConditionCheckInterval) {
|
if (!_useCustomConditionCheckInterval) {
|
||||||
@@ -948,12 +962,12 @@ bool Macro::PostLoad()
|
|||||||
bool Macro::SwitchesScene() const
|
bool Macro::SwitchesScene() const
|
||||||
{
|
{
|
||||||
for (const auto &a : _actions) {
|
for (const auto &a : _actions) {
|
||||||
if (a->GetId() == GetSceneSwitchActionId()) {
|
if (a->GetId() == MacroAction::GetDefaultID()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto &a : _elseActions) {
|
for (const auto &a : _elseActions) {
|
||||||
if (a->GetId() == GetSceneSwitchActionId()) {
|
if (a->GetId() == MacroAction::GetDefaultID()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -986,18 +1000,8 @@ bool Macro::HasValidSplitterPositions() const
|
|||||||
!_elseActionSplitterPosition.empty();
|
!_elseActionSplitterPosition.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Macro::OnChangePreventedActionsRecently()
|
|
||||||
{
|
|
||||||
if (_onPreventedActionExecution) {
|
|
||||||
_onPreventedActionExecution = false;
|
|
||||||
return _matched ? _actions.size() > 0 : _elseActions.size() > 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Macro::ResetUIHelpers()
|
void Macro::ResetUIHelpers()
|
||||||
{
|
{
|
||||||
_onPreventedActionExecution = false;
|
|
||||||
for (auto c : _conditions) {
|
for (auto c : _conditions) {
|
||||||
c->GetHighlightAndReset();
|
c->GetHighlightAndReset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ public:
|
|||||||
const QList<int> &GetElseActionSplitterPosition() const;
|
const QList<int> &GetElseActionSplitterPosition() const;
|
||||||
bool HasValidSplitterPositions() const;
|
bool HasValidSplitterPositions() const;
|
||||||
bool WasExecutedSince(const TimePoint &) const;
|
bool WasExecutedSince(const TimePoint &) const;
|
||||||
bool OnChangePreventedActionsRecently();
|
bool OnChangePreventedActionsSince(const TimePoint &) const;
|
||||||
|
TimePoint GetLastExecutionTime() const;
|
||||||
void ResetUIHelpers();
|
void ResetUIHelpers();
|
||||||
|
|
||||||
// Hotkeys
|
// Hotkeys
|
||||||
@@ -168,6 +169,7 @@ private:
|
|||||||
TimePoint _lastCheckTime{};
|
TimePoint _lastCheckTime{};
|
||||||
TimePoint _lastUnpauseTime{};
|
TimePoint _lastUnpauseTime{};
|
||||||
TimePoint _lastExecutionTime{};
|
TimePoint _lastExecutionTime{};
|
||||||
|
TimePoint _lastOnChangeActionsPreventedTime{};
|
||||||
std::vector<std::thread> _helperThreads;
|
std::vector<std::thread> _helperThreads;
|
||||||
|
|
||||||
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
||||||
@@ -204,9 +206,6 @@ private:
|
|||||||
|
|
||||||
MacroInputVariables _inputVariables;
|
MacroInputVariables _inputVariables;
|
||||||
|
|
||||||
// UI helpers
|
|
||||||
bool _onPreventedActionExecution = false;
|
|
||||||
|
|
||||||
QList<int> _actionConditionSplitterPosition;
|
QList<int> _actionConditionSplitterPosition;
|
||||||
QList<int> _elseActionSplitterPosition;
|
QList<int> _elseActionSplitterPosition;
|
||||||
|
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ bool IsFullscreen(const std::string &title)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> GetTextInWindow(const std::string &window)
|
std::optional<std::string> GetTextInWindow(const std::string &)
|
||||||
{
|
{
|
||||||
// Not implemented
|
// Not implemented
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "auto-update-tooltip-label.hpp"
|
#include "auto-update-tooltip-label.hpp"
|
||||||
|
|
||||||
|
#include <QToolTip>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
AutoUpdateHelpIcon::AutoUpdateHelpIcon(
|
AutoUpdateHelpIcon::AutoUpdateHelpIcon(
|
||||||
@@ -16,6 +18,7 @@ AutoUpdateHelpIcon::AutoUpdateHelpIcon(
|
|||||||
|
|
||||||
void AutoUpdateHelpIcon::enterEvent(QEnterEvent *event)
|
void AutoUpdateHelpIcon::enterEvent(QEnterEvent *event)
|
||||||
{
|
{
|
||||||
|
UpdateTooltip();
|
||||||
_timer->start(_updateIntervalMs);
|
_timer->start(_updateIntervalMs);
|
||||||
QLabel::enterEvent(event);
|
QLabel::enterEvent(event);
|
||||||
}
|
}
|
||||||
@@ -28,7 +31,12 @@ void AutoUpdateHelpIcon::leaveEvent(QEvent *event)
|
|||||||
|
|
||||||
void AutoUpdateHelpIcon::UpdateTooltip()
|
void AutoUpdateHelpIcon::UpdateTooltip()
|
||||||
{
|
{
|
||||||
setToolTip(_callback());
|
if (!underMouse()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString text = _callback();
|
||||||
|
QToolTip::showText(QCursor::pos(), text, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
135
lib/utils/crash-handler.cpp
Normal file
135
lib/utils/crash-handler.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#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
|
||||||
7
lib/utils/crash-handler.hpp
Normal file
7
lib/utils/crash-handler.hpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
bool ShouldSkipPluginStartOnUncleanShutdown();
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -56,7 +56,7 @@ std::optional<double> GetDouble(const std::string &str)
|
|||||||
std::optional<int> GetInt(const std::string &str)
|
std::optional<int> GetInt(const std::string &str)
|
||||||
{
|
{
|
||||||
char *end = nullptr;
|
char *end = nullptr;
|
||||||
int value = std::strtol(str.c_str(), &end, 10);
|
int value = (int)std::strtol(str.c_str(), &end, 10);
|
||||||
|
|
||||||
if (end != str.c_str() && *end == '\0' && value != INT_MAX &&
|
if (end != str.c_str() && *end == '\0' && value != INT_MAX &&
|
||||||
value != INT_MIN) {
|
value != INT_MIN) {
|
||||||
|
|||||||
@@ -65,13 +65,16 @@ ResourceTable::ResourceTable(QWidget *parent, const QString &help,
|
|||||||
[openSettings]() { openSettings(); });
|
[openSettings]() { openSettings(); });
|
||||||
|
|
||||||
auto settingsShortcut = new QShortcut(QKeySequence("F2"), this);
|
auto settingsShortcut = new QShortcut(QKeySequence("F2"), this);
|
||||||
|
settingsShortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||||
QWidget::connect(settingsShortcut, &QShortcut::activated, this,
|
QWidget::connect(settingsShortcut, &QShortcut::activated, this,
|
||||||
openSettings);
|
openSettings);
|
||||||
auto removeShortcut = new QShortcut(QKeySequence("Del"), this);
|
auto removeShortcut = new QShortcut(QKeySequence("Del"), this);
|
||||||
|
removeShortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||||
QWidget::connect(removeShortcut, &QShortcut::activated, this,
|
QWidget::connect(removeShortcut, &QShortcut::activated, this,
|
||||||
[this]() { Remove(); });
|
[this]() { Remove(); });
|
||||||
|
|
||||||
auto newShortcut = new QShortcut(QKeySequence("Ctrl+N"), this);
|
auto newShortcut = new QShortcut(QKeySequence("Ctrl+N"), this);
|
||||||
|
newShortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||||
QWidget::connect(newShortcut, &QShortcut::activated, this,
|
QWidget::connect(newShortcut, &QShortcut::activated, this,
|
||||||
[this]() { Add(); });
|
[this]() { Add(); });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "source-helpers.hpp"
|
#include "source-helpers.hpp"
|
||||||
|
#include "canvas-helpers.hpp"
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
@@ -28,15 +29,43 @@ std::string GetWeakSourceName(obs_weak_source_t *weak_source)
|
|||||||
|
|
||||||
OBSWeakSource GetWeakSourceByName(const char *name)
|
OBSWeakSource GetWeakSourceByName(const char *name)
|
||||||
{
|
{
|
||||||
OBSWeakSource weak;
|
OBSSourceAutoRelease source = obs_get_source_by_name(name);
|
||||||
obs_source_t *source = obs_get_source_by_name(name);
|
|
||||||
if (source) {
|
if (source) {
|
||||||
weak = obs_source_get_weak_source(source);
|
return OBSGetWeakRef(source);
|
||||||
obs_weak_source_release(weak);
|
|
||||||
obs_source_release(source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return weak;
|
#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
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSWeakSource GetWeakSourceByQString(const QString &name)
|
OBSWeakSource GetWeakSourceByQString(const QString &name)
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ StringListEdit::StringListEdit(
|
|||||||
_filterCallback(filter),
|
_filterCallback(filter),
|
||||||
_preprocessCallback(preprocess)
|
_preprocessCallback(preprocess)
|
||||||
{
|
{
|
||||||
|
if (_addString.isEmpty()) {
|
||||||
|
_addString = obs_module_text("AdvSceneSwitcher.windowTitle");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringListEdit::SetStringList(const StringList &list)
|
void StringListEdit::SetStringList(const StringList &list)
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace advss {
|
|||||||
|
|
||||||
class StringList : public QList<StringVariable> {
|
class StringList : public QList<StringVariable> {
|
||||||
public:
|
public:
|
||||||
|
using QList<StringVariable>::QList;
|
||||||
|
|
||||||
EXPORT bool Save(obs_data_t *obj, const char *name,
|
EXPORT bool Save(obs_data_t *obj, const char *name,
|
||||||
const char *elementName = "string") const;
|
const char *elementName = "string") const;
|
||||||
EXPORT bool Load(obs_data_t *obj, const char *name,
|
EXPORT bool Load(obs_data_t *obj, const char *name,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "tab-helpers.hpp"
|
#include "tab-helpers.hpp"
|
||||||
#include "log-helper.hpp"
|
#include "log-helper.hpp"
|
||||||
#include "obs-module-helper.hpp"
|
#include "obs-module-helper.hpp"
|
||||||
|
#include "plugin-state-helpers.hpp"
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <QTabBar>
|
#include <QTabBar>
|
||||||
@@ -33,6 +34,21 @@ struct TabCallbacks {
|
|||||||
static std::unordered_map<const char *, TabCallbacks> createTabCallbacks;
|
static std::unordered_map<const char *, TabCallbacks> createTabCallbacks;
|
||||||
|
|
||||||
static int lastOpenedTab = -1;
|
static int lastOpenedTab = -1;
|
||||||
|
static bool alwaysShowTabs = false;
|
||||||
|
|
||||||
|
[[maybe_unused]] static bool _ = []() {
|
||||||
|
AddPluginInitStep([]() {
|
||||||
|
AddSaveStep([](obs_data_t *data) {
|
||||||
|
obs_data_set_bool(data, "alwaysShowTabs",
|
||||||
|
alwaysShowTabs);
|
||||||
|
});
|
||||||
|
AddLoadStep([](obs_data_t *data) {
|
||||||
|
alwaysShowTabs =
|
||||||
|
obs_data_get_bool(data, "alwaysShowTabs");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}();
|
||||||
|
|
||||||
void SetTabVisibleByName(QTabWidget *tabWidget, bool visible,
|
void SetTabVisibleByName(QTabWidget *tabWidget, bool visible,
|
||||||
const QString &name)
|
const QString &name)
|
||||||
@@ -197,7 +213,41 @@ void SetupOtherTabs(QTabWidget *tabWidget)
|
|||||||
.c_str());
|
.c_str());
|
||||||
tabWidget->insertTab(0, widget, tabText);
|
tabWidget->insertTab(0, widget, tabText);
|
||||||
callbacks.setupTab(tabWidget);
|
callbacks.setupTab(tabWidget);
|
||||||
|
|
||||||
|
if (alwaysShowTabs) {
|
||||||
|
tabWidget->setTabVisible(0, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupShowAllTabsCheckBox(QCheckBox *checkBox, QTabWidget *tabWidget)
|
||||||
|
{
|
||||||
|
const auto stateChanged = [tabWidget](int state) {
|
||||||
|
const bool newState = (state != 0);
|
||||||
|
if (newState == alwaysShowTabs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
alwaysShowTabs = newState;
|
||||||
|
|
||||||
|
// Only show currently hidden tabs, but don't hide tabs which
|
||||||
|
// are already visible
|
||||||
|
if (!alwaysShowTabs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &[name, _] : createTabCallbacks) {
|
||||||
|
const auto localeKey =
|
||||||
|
std::string("AdvSceneSwitcher.") + name +
|
||||||
|
".title";
|
||||||
|
auto tabText = obs_module_text(localeKey.c_str());
|
||||||
|
SetTabVisibleByName(tabWidget, true, tabText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkBox->setChecked(alwaysShowTabs);
|
||||||
|
QWidget::connect(checkBox, &QCheckBox::stateChanged, checkBox,
|
||||||
|
stateChanged);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#include "export-symbol-helper.hpp"
|
#include "export-symbol-helper.hpp"
|
||||||
|
|
||||||
#include <obs-data.h>
|
#include <obs-data.h>
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
@@ -19,5 +21,6 @@ void SetupOtherTabs(QTabWidget *tabWidget);
|
|||||||
void SaveTabOrder(obs_data_t *obj);
|
void SaveTabOrder(obs_data_t *obj);
|
||||||
void LoadTabOrder(obs_data_t *obj);
|
void LoadTabOrder(obs_data_t *obj);
|
||||||
bool MacroTabIsInFocus();
|
bool MacroTabIsInFocus();
|
||||||
|
void SetupShowAllTabsCheckBox(QCheckBox *checkBox, QTabWidget *tabWidget);
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -467,9 +467,9 @@ static void setupTab(QTabWidget *tab)
|
|||||||
setTabVisible(tab, false);
|
setTabVisible(tab, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget::connect(
|
QWidget::connect(VariableSignalManager::Instance(),
|
||||||
VariableSignalManager::Instance(), &VariableSignalManager::Add,
|
&VariableSignalManager::Add, tab,
|
||||||
tab, [tab](const QString &name) { setTabVisible(tab, true); });
|
[tab](const QString &) { setTabVisible(tab, true); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void VariableTable::Settings::Save(obs_data_t *data, const char *name)
|
void VariableTable::Settings::Save(obs_data_t *data, const char *name)
|
||||||
|
|||||||
@@ -261,6 +261,14 @@ std::optional<std::string> GetTextInWindow(const std::string &window)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD pid = 0;
|
||||||
|
DWORD thid = 0;
|
||||||
|
thid = GetWindowThreadProcessId(hwnd, &pid);
|
||||||
|
// Calling CoCreateInstance() on the OBS windows might cause a deadlock
|
||||||
|
if (GetCurrentProcessId() == pid) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
IUIAutomation *automation = nullptr;
|
IUIAutomation *automation = nullptr;
|
||||||
auto hr = CoCreateInstance(__uuidof(CUIAutomation), nullptr,
|
auto hr = CoCreateInstance(__uuidof(CUIAutomation), nullptr,
|
||||||
CLSCTX_INPROC_SERVER,
|
CLSCTX_INPROC_SERVER,
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ target_sources(
|
|||||||
utils/scene-item-selection.hpp
|
utils/scene-item-selection.hpp
|
||||||
utils/scene-item-transform-helpers.cpp
|
utils/scene-item-transform-helpers.cpp
|
||||||
utils/scene-item-transform-helpers.hpp
|
utils/scene-item-transform-helpers.hpp
|
||||||
|
utils/transition-helpers.cpp
|
||||||
|
utils/transition-helpers.hpp
|
||||||
utils/source-properties-button.cpp
|
utils/source-properties-button.cpp
|
||||||
utils/source-properties-button.hpp
|
utils/source-properties-button.hpp
|
||||||
utils/source-settings-helpers.cpp
|
utils/source-settings-helpers.cpp
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "plugin-state-helpers.hpp"
|
#include "plugin-state-helpers.hpp"
|
||||||
#include "scene-switch-helpers.hpp"
|
#include "scene-switch-helpers.hpp"
|
||||||
#include "source-helpers.hpp"
|
#include "source-helpers.hpp"
|
||||||
|
#include "transition-helpers.hpp"
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
@@ -68,14 +69,6 @@ static int getTransitionOverrideDuration(OBSWeakSource &scene)
|
|||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isUsingFixedLengthTransition(const OBSWeakSource &transition)
|
|
||||||
{
|
|
||||||
obs_source_t *source = obs_weak_source_get_source(transition);
|
|
||||||
bool ret = obs_transition_fixed(source);
|
|
||||||
obs_source_release(source);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static OBSWeakSource getOverrideTransition(OBSWeakSource &scene)
|
static OBSWeakSource getOverrideTransition(OBSWeakSource &scene)
|
||||||
{
|
{
|
||||||
OBSWeakSource transition;
|
OBSWeakSource transition;
|
||||||
@@ -101,13 +94,13 @@ static int getExpectedTransitionDuration(OBSWeakSource &scene,
|
|||||||
auto overrideTransition = getOverrideTransition(scene);
|
auto overrideTransition = getOverrideTransition(scene);
|
||||||
if (overrideTransition) {
|
if (overrideTransition) {
|
||||||
transition = overrideTransition;
|
transition = overrideTransition;
|
||||||
if (!isUsingFixedLengthTransition(transition)) {
|
if (!IsFixedLengthTransition(transition)) {
|
||||||
return getTransitionOverrideDuration(scene);
|
return getTransitionOverrideDuration(scene);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUsingFixedLengthTransition(transition)) {
|
if (IsFixedLengthTransition(transition)) {
|
||||||
return -1; // no API is available to access the fixed duration
|
return -1; // no API is available to access the fixed duration
|
||||||
}
|
}
|
||||||
if (duration != 0) {
|
if (duration != 0) {
|
||||||
@@ -453,7 +446,7 @@ void MacroActionSwitchSceneEdit::SceneSelectionCanvasChanged(
|
|||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionSwitchSceneEdit::ActionChanged(int value)
|
void MacroActionSwitchSceneEdit::ActionChanged(int)
|
||||||
{
|
{
|
||||||
GUARD_LOADING_AND_LOCK();
|
GUARD_LOADING_AND_LOCK();
|
||||||
_entryData->_action = static_cast<MacroActionSwitchScene::Action>(
|
_entryData->_action = static_cast<MacroActionSwitchScene::Action>(
|
||||||
@@ -503,7 +496,7 @@ shouldShowDurationInTransitionLayout(MacroActionSwitchScene::SceneType type,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUsingFixedLengthTransition(transition.GetTransition())) {
|
if (IsFixedLengthTransition(transition.GetTransition())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "macro-action-scene-visibility.hpp"
|
#include "macro-action-scene-visibility.hpp"
|
||||||
#include "layout-helpers.hpp"
|
#include "layout-helpers.hpp"
|
||||||
|
#include "transition-helpers.hpp"
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
@@ -21,9 +22,74 @@ const static std::map<MacroActionSceneVisibility::Action, std::string>
|
|||||||
"AdvSceneSwitcher.action.sceneVisibility.type.toggle"},
|
"AdvSceneSwitcher.action.sceneVisibility.type.toggle"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct TransitionRestoreContext {
|
||||||
|
obs_sceneitem_t *item;
|
||||||
|
bool wasVisible;
|
||||||
|
|
||||||
|
bool restoreTransition;
|
||||||
|
OBSSource originalTransition;
|
||||||
|
bool restoreDuration;
|
||||||
|
uint32_t originalDuration;
|
||||||
|
|
||||||
|
signal_handler_t *sh = nullptr;
|
||||||
|
|
||||||
|
~TransitionRestoreContext()
|
||||||
|
{
|
||||||
|
if (!sh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_handler_disconnect(
|
||||||
|
sh, "transition_stop",
|
||||||
|
&TransitionRestoreContext::ResetSceneItemTransition,
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ResetSceneItemTransition(void *param, calldata_t *)
|
||||||
|
{
|
||||||
|
auto *ctx = static_cast<TransitionRestoreContext *>(param);
|
||||||
|
SetSceneItemTransition(ctx->item, ctx->originalTransition,
|
||||||
|
!ctx->wasVisible);
|
||||||
|
|
||||||
|
obs_sceneitem_set_transition_duration(
|
||||||
|
ctx->item, !ctx->wasVisible, ctx->originalDuration);
|
||||||
|
delete ctx;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
static void setSceneItemVisibility(obs_sceneitem_t *item,
|
static void setSceneItemVisibility(obs_sceneitem_t *item,
|
||||||
|
const bool setTransition,
|
||||||
|
const OBSWeakSource &transitionWeak,
|
||||||
|
const bool setDuration,
|
||||||
|
const Duration &duration,
|
||||||
MacroActionSceneVisibility::Action action)
|
MacroActionSceneVisibility::Action action)
|
||||||
{
|
{
|
||||||
|
const OBSSourceAutoRelease transition = OBSGetStrongRef(transitionWeak);
|
||||||
|
const bool itemIsVisible = obs_sceneitem_visible(item);
|
||||||
|
|
||||||
|
const OBSSource currentTransition =
|
||||||
|
obs_sceneitem_get_transition(item, !itemIsVisible);
|
||||||
|
const uint32_t currentTransitionDuration =
|
||||||
|
obs_sceneitem_get_transition_duration(item, !itemIsVisible);
|
||||||
|
|
||||||
|
obs_source_t *privateTransitionSource = nullptr;
|
||||||
|
if (setTransition) {
|
||||||
|
privateTransitionSource = SetSceneItemTransition(
|
||||||
|
item, transition, !itemIsVisible);
|
||||||
|
} else {
|
||||||
|
privateTransitionSource =
|
||||||
|
obs_sceneitem_get_transition(item, !itemIsVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setDuration) {
|
||||||
|
obs_sceneitem_set_transition_duration(item, !itemIsVisible,
|
||||||
|
duration.Milliseconds());
|
||||||
|
}
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case MacroActionSceneVisibility::Action::SHOW:
|
case MacroActionSceneVisibility::Action::SHOW:
|
||||||
obs_sceneitem_set_visible(item, true);
|
obs_sceneitem_set_visible(item, true);
|
||||||
@@ -32,16 +98,48 @@ static void setSceneItemVisibility(obs_sceneitem_t *item,
|
|||||||
obs_sceneitem_set_visible(item, false);
|
obs_sceneitem_set_visible(item, false);
|
||||||
break;
|
break;
|
||||||
case MacroActionSceneVisibility::Action::TOGGLE:
|
case MacroActionSceneVisibility::Action::TOGGLE:
|
||||||
obs_sceneitem_set_visible(item, !obs_sceneitem_visible(item));
|
obs_sceneitem_set_visible(item, !itemIsVisible);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!privateTransitionSource) {
|
||||||
|
if (setTransition) {
|
||||||
|
SetSceneItemTransition(item, currentTransition,
|
||||||
|
!itemIsVisible);
|
||||||
|
}
|
||||||
|
if (setDuration) {
|
||||||
|
obs_sceneitem_set_transition_duration(
|
||||||
|
item, !itemIsVisible,
|
||||||
|
currentTransitionDuration);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sh = obs_source_get_signal_handler(privateTransitionSource);
|
||||||
|
if (!sh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ctx = new TransitionRestoreContext{item,
|
||||||
|
itemIsVisible,
|
||||||
|
setTransition,
|
||||||
|
currentTransition,
|
||||||
|
setDuration,
|
||||||
|
currentTransitionDuration,
|
||||||
|
sh};
|
||||||
|
|
||||||
|
signal_handler_connect(
|
||||||
|
sh, "transition_stop",
|
||||||
|
&TransitionRestoreContext::ResetSceneItemTransition, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroActionSceneVisibility::PerformAction()
|
bool MacroActionSceneVisibility::PerformAction()
|
||||||
{
|
{
|
||||||
auto items = _source.GetSceneItems(_scene);
|
auto items = _source.GetSceneItems(_scene);
|
||||||
for (const auto &item : items) {
|
for (const auto &item : items) {
|
||||||
setSceneItemVisibility(item, _action);
|
setSceneItemVisibility(item, _updateTransition,
|
||||||
|
_transition.GetTransition(),
|
||||||
|
_updateDuration, _duration, _action);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -66,6 +164,10 @@ bool MacroActionSceneVisibility::Save(obs_data_t *obj) const
|
|||||||
MacroAction::Save(obj);
|
MacroAction::Save(obj);
|
||||||
_scene.Save(obj);
|
_scene.Save(obj);
|
||||||
_source.Save(obj);
|
_source.Save(obj);
|
||||||
|
obs_data_set_bool(obj, "updateTransition", _updateTransition);
|
||||||
|
_transition.Save(obj);
|
||||||
|
obs_data_set_bool(obj, "updateDuration", _updateDuration);
|
||||||
|
_duration.Save(obj);
|
||||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -82,6 +184,10 @@ bool MacroActionSceneVisibility::Load(obs_data_t *obj)
|
|||||||
MacroAction::Load(obj);
|
MacroAction::Load(obj);
|
||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_source.Load(obj);
|
_source.Load(obj);
|
||||||
|
_updateTransition = obs_data_get_bool(obj, "updateTransition");
|
||||||
|
_transition.Load(obj);
|
||||||
|
_updateDuration = obs_data_get_bool(obj, "updateDuration");
|
||||||
|
_duration.Load(obj);
|
||||||
_action = static_cast<MacroActionSceneVisibility::Action>(
|
_action = static_cast<MacroActionSceneVisibility::Action>(
|
||||||
obs_data_get_int(obj, "action"));
|
obs_data_get_int(obj, "action"));
|
||||||
|
|
||||||
@@ -141,10 +247,17 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
|||||||
SceneItemSelection::Type::ALL,
|
SceneItemSelection::Type::ALL,
|
||||||
},
|
},
|
||||||
SceneItemSelectionWidget::NameClashMode::ALL)),
|
SceneItemSelectionWidget::NameClashMode::ALL)),
|
||||||
|
_updateTransition(new QCheckBox(this)),
|
||||||
|
_transitions(new TransitionSelectionWidget(this, false, false)),
|
||||||
|
_updateDuration(new QCheckBox(this)),
|
||||||
|
_duration(new DurationSelection(this, false)),
|
||||||
|
_durationLayout(new QHBoxLayout),
|
||||||
_actions(new QComboBox())
|
_actions(new QComboBox())
|
||||||
{
|
{
|
||||||
populateActionSelection(_actions);
|
populateActionSelection(_actions);
|
||||||
|
|
||||||
|
_duration->SpinBox()->setSpecialValueText("-");
|
||||||
|
|
||||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(ActionChanged(int)));
|
SLOT(ActionChanged(int)));
|
||||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||||
@@ -154,18 +267,49 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
|||||||
QWidget::connect(_sources,
|
QWidget::connect(_sources,
|
||||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||||
|
QWidget::connect(_updateTransition, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(UpdateTransitionChanged(int)));
|
||||||
|
QWidget::connect(_transitions,
|
||||||
|
SIGNAL(TransitionChanged(const TransitionSelection &)),
|
||||||
|
this,
|
||||||
|
SLOT(TransitionChanged(const TransitionSelection &)));
|
||||||
|
QWidget::connect(_updateDuration, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(UpdateDurationChanged(int)));
|
||||||
|
QWidget::connect(_duration, SIGNAL(DurationChanged(const Duration &)),
|
||||||
|
this, SLOT(DurationChanged(const Duration &)));
|
||||||
|
|
||||||
auto layout = new QHBoxLayout;
|
auto sceneItemLayout = new QHBoxLayout;
|
||||||
PlaceWidgets(obs_module_text(
|
PlaceWidgets(obs_module_text(
|
||||||
"AdvSceneSwitcher.action.sceneVisibility.entry"),
|
"AdvSceneSwitcher.action.sceneVisibility.layout"),
|
||||||
layout,
|
sceneItemLayout,
|
||||||
{{"{{scenes}}", _scenes},
|
{{"{{scenes}}", _scenes},
|
||||||
{"{{sources}}", _sources},
|
{"{{sources}}", _sources},
|
||||||
{"{{actions}}", _actions}});
|
{"{{actions}}", _actions}});
|
||||||
|
|
||||||
|
auto transitionLayout = new QHBoxLayout;
|
||||||
|
PlaceWidgets(
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.sceneVisibility.layout.transition"),
|
||||||
|
transitionLayout,
|
||||||
|
{{"{{updateTransition}}", _updateTransition},
|
||||||
|
{"{{transitions}}", _transitions}});
|
||||||
|
|
||||||
|
PlaceWidgets(
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.sceneVisibility.layout.duration"),
|
||||||
|
_durationLayout,
|
||||||
|
{{"{{updateDuration}}", _updateDuration},
|
||||||
|
{"{{duration}}", _duration}});
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
layout->addLayout(sceneItemLayout);
|
||||||
|
layout->addLayout(transitionLayout);
|
||||||
|
layout->addLayout(_durationLayout);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
UpdateEntryData();
|
UpdateEntryData();
|
||||||
|
SetWidgetVisibility();
|
||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +321,11 @@ void MacroActionSceneVisibilityEdit::UpdateEntryData()
|
|||||||
|
|
||||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||||
_scenes->SetScene(_entryData->_scene);
|
_scenes->SetScene(_entryData->_scene);
|
||||||
_sources->SetSceneItem((_entryData->_source));
|
_sources->SetSceneItem(_entryData->_source);
|
||||||
|
_updateTransition->setChecked(_entryData->_updateTransition);
|
||||||
|
_transitions->SetTransition(_entryData->_transition);
|
||||||
|
_updateDuration->setChecked(_entryData->_updateDuration);
|
||||||
|
_duration->SetDuration(_entryData->_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||||
@@ -197,6 +345,34 @@ void MacroActionSceneVisibilityEdit::SourceChanged(
|
|||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneVisibilityEdit::UpdateTransitionChanged(int state)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_updateTransition = state;
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneVisibilityEdit::TransitionChanged(
|
||||||
|
const TransitionSelection &t)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_transition = t;
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneVisibilityEdit::UpdateDurationChanged(int state)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_updateDuration = state;
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneVisibilityEdit::DurationChanged(const Duration &dur)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->_duration = dur;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroActionSceneVisibilityEdit::ActionChanged(int value)
|
void MacroActionSceneVisibilityEdit::ActionChanged(int value)
|
||||||
{
|
{
|
||||||
GUARD_LOADING_AND_LOCK();
|
GUARD_LOADING_AND_LOCK();
|
||||||
@@ -204,4 +380,20 @@ void MacroActionSceneVisibilityEdit::ActionChanged(int value)
|
|||||||
static_cast<MacroActionSceneVisibility::Action>(value);
|
static_cast<MacroActionSceneVisibility::Action>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionSceneVisibilityEdit::SetWidgetVisibility()
|
||||||
|
{
|
||||||
|
const bool hideDurationSelection =
|
||||||
|
_entryData->_updateTransition &&
|
||||||
|
IsFixedLengthTransition(
|
||||||
|
_entryData->_transition.GetTransition());
|
||||||
|
|
||||||
|
SetLayoutVisible(_durationLayout, !hideDurationSelection);
|
||||||
|
|
||||||
|
_transitions->setEnabled(_entryData->_updateTransition);
|
||||||
|
_duration->setEnabled(_entryData->_updateDuration);
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "duration-control.hpp"
|
||||||
#include "macro-action-edit.hpp"
|
#include "macro-action-edit.hpp"
|
||||||
#include "scene-selection.hpp"
|
#include "scene-selection.hpp"
|
||||||
#include "scene-item-selection.hpp"
|
#include "scene-item-selection.hpp"
|
||||||
|
#include "transition-selection.hpp"
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
@@ -20,6 +22,10 @@ public:
|
|||||||
|
|
||||||
SceneSelection _scene;
|
SceneSelection _scene;
|
||||||
SceneItemSelection _source;
|
SceneItemSelection _source;
|
||||||
|
bool _updateTransition = false;
|
||||||
|
TransitionSelection _transition;
|
||||||
|
bool _updateDuration = false;
|
||||||
|
Duration _duration;
|
||||||
|
|
||||||
enum class Action {
|
enum class Action {
|
||||||
SHOW,
|
SHOW,
|
||||||
@@ -53,17 +59,27 @@ public:
|
|||||||
private slots:
|
private slots:
|
||||||
void SceneChanged(const SceneSelection &);
|
void SceneChanged(const SceneSelection &);
|
||||||
void SourceChanged(const SceneItemSelection &);
|
void SourceChanged(const SceneItemSelection &);
|
||||||
|
void UpdateTransitionChanged(int);
|
||||||
|
void TransitionChanged(const TransitionSelection &);
|
||||||
|
void UpdateDurationChanged(int);
|
||||||
|
void DurationChanged(const Duration &seconds);
|
||||||
void ActionChanged(int value);
|
void ActionChanged(int value);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
SceneSelectionWidget *_scenes;
|
SceneSelectionWidget *_scenes;
|
||||||
SceneItemSelectionWidget *_sources;
|
SceneItemSelectionWidget *_sources;
|
||||||
|
QCheckBox *_updateTransition;
|
||||||
|
TransitionSelectionWidget *_transitions;
|
||||||
|
QCheckBox *_updateDuration;
|
||||||
|
DurationSelection *_duration;
|
||||||
|
QHBoxLayout *_durationLayout;
|
||||||
QComboBox *_actions;
|
QComboBox *_actions;
|
||||||
std::shared_ptr<MacroActionSceneVisibility> _entryData;
|
|
||||||
|
|
||||||
private:
|
std::shared_ptr<MacroActionSceneVisibility> _entryData;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ MacroActionSequenceEdit::MacroActionSequenceEdit(
|
|||||||
_resetIndex(new VariableSpinBox()),
|
_resetIndex(new VariableSpinBox()),
|
||||||
_layout(new QHBoxLayout())
|
_layout(new QHBoxLayout())
|
||||||
{
|
{
|
||||||
|
_macros->HideGroups();
|
||||||
populateActionSelection(_actions);
|
populateActionSelection(_actions);
|
||||||
|
|
||||||
_resetIndex->setMinimum(1);
|
_resetIndex->setMinimum(1);
|
||||||
|
|||||||
@@ -31,15 +31,16 @@ bool MacroActionTimer::PerformAction()
|
|||||||
if (!macro) {
|
if (!macro) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto conditions = *GetMacroConditions(macro.get());
|
|
||||||
for (auto condition : conditions) {
|
const auto updateCondition = [this](MacroCondition *condition) {
|
||||||
if (condition->GetId() != id) {
|
if (condition->GetId() != id) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto timerCondition =
|
auto timerCondition =
|
||||||
dynamic_cast<MacroConditionTimer *>(condition.get());
|
dynamic_cast<MacroConditionTimer *>(condition);
|
||||||
if (!timerCondition) {
|
if (!timerCondition) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_actionType) {
|
switch (_actionType) {
|
||||||
@@ -59,6 +60,23 @@ bool MacroActionTimer::PerformAction()
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto updateMacro = [&](Macro *macro) {
|
||||||
|
auto conditions = *GetMacroConditions(macro);
|
||||||
|
for (auto condition : conditions) {
|
||||||
|
updateCondition(condition.get());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!IsGroupMacro(macro.get())) {
|
||||||
|
updateMacro(macro.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto macros = GetGroupMacroEntries(macro.get());
|
||||||
|
for (const auto ¯o : macros) {
|
||||||
|
updateMacro(macro.get());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -131,7 +149,7 @@ void MacroActionTimer::ResolveVariablesToFixedValues()
|
|||||||
_duration.ResolveVariables();
|
_duration.ResolveVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void populateTypeSelection(QComboBox *list)
|
static void populateTypeSelection(QComboBox *list)
|
||||||
{
|
{
|
||||||
for (const auto &[_, name] : timerActions) {
|
for (const auto &[_, name] : timerActions) {
|
||||||
list->addItem(obs_module_text(name.c_str()));
|
list->addItem(obs_module_text(name.c_str()));
|
||||||
@@ -140,12 +158,11 @@ static inline void populateTypeSelection(QComboBox *list)
|
|||||||
|
|
||||||
MacroActionTimerEdit::MacroActionTimerEdit(
|
MacroActionTimerEdit::MacroActionTimerEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroActionTimer> entryData)
|
QWidget *parent, std::shared_ptr<MacroActionTimer> entryData)
|
||||||
: QWidget(parent)
|
: QWidget(parent),
|
||||||
|
_macros(new MacroSelection(parent)),
|
||||||
|
_duration(new DurationSelection(this)),
|
||||||
|
_timerAction(new QComboBox(this))
|
||||||
{
|
{
|
||||||
_macros = new MacroSelection(parent);
|
|
||||||
_duration = new DurationSelection();
|
|
||||||
_timerAction = new QComboBox();
|
|
||||||
|
|
||||||
populateTypeSelection(_timerAction);
|
populateTypeSelection(_timerAction);
|
||||||
|
|
||||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||||
@@ -155,15 +172,13 @@ MacroActionTimerEdit::MacroActionTimerEdit(
|
|||||||
QWidget::connect(_timerAction, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_timerAction, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(ActionTypeChanged(int)));
|
SLOT(ActionTypeChanged(int)));
|
||||||
|
|
||||||
_mainLayout = new QHBoxLayout;
|
auto layout = new QHBoxLayout;
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
|
||||||
{"{{macros}}", _macros},
|
|
||||||
{"{{duration}}", _duration},
|
|
||||||
{"{{timerAction}}", _timerAction},
|
|
||||||
};
|
|
||||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.timer.entry"),
|
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.timer.entry"),
|
||||||
_mainLayout, widgetPlaceholders);
|
layout,
|
||||||
setLayout(_mainLayout);
|
{{"{{macros}}", _macros},
|
||||||
|
{"{{duration}}", _duration},
|
||||||
|
{"{{timerAction}}", _timerAction}});
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
_entryData = entryData;
|
_entryData = entryData;
|
||||||
UpdateEntryData();
|
UpdateEntryData();
|
||||||
|
|||||||
@@ -58,16 +58,14 @@ private slots:
|
|||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
void SetWidgetVisibility();
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
MacroSelection *_macros;
|
MacroSelection *_macros;
|
||||||
DurationSelection *_duration;
|
DurationSelection *_duration;
|
||||||
QComboBox *_timerAction;
|
QComboBox *_timerAction;
|
||||||
std::shared_ptr<MacroActionTimer> _entryData;
|
|
||||||
|
|
||||||
private:
|
std::shared_ptr<MacroActionTimer> _entryData;
|
||||||
QHBoxLayout *_mainLayout;
|
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "macro-action-transition.hpp"
|
#include "macro-action-transition.hpp"
|
||||||
#include "layout-helpers.hpp"
|
#include "layout-helpers.hpp"
|
||||||
|
#include "transition-helpers.hpp"
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
@@ -75,26 +76,6 @@ static void obs_sceneitem_set_transition_duration(obs_sceneitem_t *item,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void setSceneItemTransition(const OBSSceneItem &item,
|
|
||||||
const OBSSourceAutoRelease &transition,
|
|
||||||
bool show)
|
|
||||||
{
|
|
||||||
OBSDataAutoRelease settings = obs_source_get_settings(transition);
|
|
||||||
if (!transition || !settings) {
|
|
||||||
// Set transition to "None"
|
|
||||||
obs_sceneitem_set_transition(item, show, nullptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We cannot share the transition source between
|
|
||||||
// scene items without introducing strange graphical
|
|
||||||
// artifacts so we have to create new ones here
|
|
||||||
OBSSourceAutoRelease transitionSource = obs_source_create_private(
|
|
||||||
obs_source_get_id(transition), obs_source_get_name(transition),
|
|
||||||
settings);
|
|
||||||
obs_sceneitem_set_transition(item, show, transitionSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroActionTransition::SetSourceTransition(bool show)
|
void MacroActionTransition::SetSourceTransition(bool show)
|
||||||
{
|
{
|
||||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
|
||||||
@@ -103,7 +84,7 @@ void MacroActionTransition::SetSourceTransition(bool show)
|
|||||||
const auto items = _source.GetSceneItems(_scene);
|
const auto items = _source.GetSceneItems(_scene);
|
||||||
for (const auto &item : items) {
|
for (const auto &item : items) {
|
||||||
if (_setTransitionType) {
|
if (_setTransitionType) {
|
||||||
setSceneItemTransition(item, transition, show);
|
SetSceneItemTransition(item, transition, show);
|
||||||
}
|
}
|
||||||
if (_setDuration) {
|
if (_setDuration) {
|
||||||
obs_sceneitem_set_transition_duration(
|
obs_sceneitem_set_transition_duration(
|
||||||
|
|||||||
@@ -55,17 +55,17 @@ void MacroConditionGameCapture::GetCalldataInfo(calldata_t *cd)
|
|||||||
if (!calldata_get_string(cd, "title", &title)) {
|
if (!calldata_get_string(cd, "title", &title)) {
|
||||||
blog(LOG_WARNING, "%s failed to get title", __func__);
|
blog(LOG_WARNING, "%s failed to get title", __func__);
|
||||||
}
|
}
|
||||||
_title = title;
|
_title = title ? title : "";
|
||||||
const char *className = "";
|
const char *className = "";
|
||||||
if (!calldata_get_string(cd, "class", &className)) {
|
if (!calldata_get_string(cd, "class", &className)) {
|
||||||
blog(LOG_WARNING, "%s failed to get class", __func__);
|
blog(LOG_WARNING, "%s failed to get class", __func__);
|
||||||
}
|
}
|
||||||
_class = className;
|
_class = className ? className : "";
|
||||||
const char *executable = "";
|
const char *executable = "";
|
||||||
if (!calldata_get_string(cd, "executable", &executable)) {
|
if (!calldata_get_string(cd, "executable", &executable)) {
|
||||||
blog(LOG_WARNING, "%s failed to get executable", __func__);
|
blog(LOG_WARNING, "%s failed to get executable", __func__);
|
||||||
}
|
}
|
||||||
_executable = executable;
|
_executable = executable ? executable : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionGameCapture::HookedSignalReceived(void *data, calldata_t *cd)
|
void MacroConditionGameCapture::HookedSignalReceived(void *data, calldata_t *cd)
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ void MacroConditionProcessEdit::ProcessChanged(const QString &text)
|
|||||||
QString::fromStdString(_entryData->GetShortDesc()));
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionProcessEdit::showEvent(QShowEvent *event)
|
void MacroConditionProcessEdit::showEvent(QShowEvent *)
|
||||||
{
|
{
|
||||||
const QSignalBlocker b(_processSelection);
|
const QSignalBlocker b(_processSelection);
|
||||||
const auto text = _processSelection->currentText();
|
const auto text = _processSelection->currentText();
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ static int currentRecordingDurationInSeconds = 0;
|
|||||||
|
|
||||||
bool MacroConditionRecord::CheckCondition()
|
bool MacroConditionRecord::CheckCondition()
|
||||||
{
|
{
|
||||||
|
char *lastSavePath = obs_frontend_get_last_recording();
|
||||||
|
SetTempVarValue("lastSavePath", lastSavePath ? lastSavePath : "");
|
||||||
|
bfree(lastSavePath);
|
||||||
|
|
||||||
switch (_condition) {
|
switch (_condition) {
|
||||||
case Condition::STOP:
|
case Condition::STOP:
|
||||||
return !obs_frontend_recording_active();
|
return !obs_frontend_recording_active();
|
||||||
@@ -74,6 +78,14 @@ void MacroConditionRecord::SetCondition(Condition condition)
|
|||||||
void MacroConditionRecord::SetupTempVars()
|
void MacroConditionRecord::SetupTempVars()
|
||||||
{
|
{
|
||||||
MacroCondition::SetupTempVars();
|
MacroCondition::SetupTempVars();
|
||||||
|
|
||||||
|
AddTempvar(
|
||||||
|
"lastSavePath",
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.recording.lastSavePath"),
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.recording.lastSavePath.description"));
|
||||||
|
|
||||||
switch (_condition) {
|
switch (_condition) {
|
||||||
case Condition::DURATION:
|
case Condition::DURATION:
|
||||||
AddTempvar(
|
AddTempvar(
|
||||||
|
|||||||
@@ -29,6 +29,22 @@ static bool windowContainsText(const std::string &window,
|
|||||||
return text == matchText;
|
return text == matchText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionWindow::SetCheckText(bool value)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
_checkText = value;
|
||||||
|
#else
|
||||||
|
(void)value;
|
||||||
|
_checkText = false;
|
||||||
|
#endif
|
||||||
|
SetupTempVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionWindow::GetCheckText()
|
||||||
|
{
|
||||||
|
return _checkText;
|
||||||
|
}
|
||||||
|
|
||||||
bool MacroConditionWindow::WindowMatchesRequirements(
|
bool MacroConditionWindow::WindowMatchesRequirements(
|
||||||
const std::string &window) const
|
const std::string &window) const
|
||||||
{
|
{
|
||||||
@@ -94,12 +110,18 @@ void MacroConditionWindow::SetVariableValueBasedOnMatch(
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetTempVarValue("windowClass",
|
SetTempVarValue("windowClass",
|
||||||
GetWindowClassByWindowTitle(matchWindow));
|
GetWindowClassByWindowTitle(matchWindow));
|
||||||
|
if (_checkText) {
|
||||||
|
const auto text = GetTextInWindow(matchWindow);
|
||||||
|
if (text) {
|
||||||
|
SetTempVarValue("windowText", *text);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!IsReferencedInVars()) {
|
if (!IsReferencedInVars()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_checkText) {
|
if (_checkText) {
|
||||||
auto text = GetTextInWindow(matchWindow);
|
const auto text = GetTextInWindow(matchWindow);
|
||||||
SetVariableValue(text.value_or(""));
|
SetVariableValue(text.value_or(""));
|
||||||
} else {
|
} else {
|
||||||
SetVariableValue(ForegroundWindowTitle());
|
SetVariableValue(ForegroundWindowTitle());
|
||||||
@@ -186,6 +208,16 @@ void MacroConditionWindow::SetupTempVars()
|
|||||||
obs_module_text("AdvSceneSwitcher.tempVar.window.windowClass"),
|
obs_module_text("AdvSceneSwitcher.tempVar.window.windowClass"),
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.tempVar.window.windowClass.description"));
|
"AdvSceneSwitcher.tempVar.window.windowClass.description"));
|
||||||
|
|
||||||
|
if (!_checkText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTempvar(
|
||||||
|
"windowText",
|
||||||
|
obs_module_text("AdvSceneSwitcher.tempVar.window.windowText"),
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.tempVar.window.windowText.description"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +381,7 @@ void MacroConditionWindowEdit::CheckTitleChanged(int state)
|
|||||||
void MacroConditionWindowEdit::CheckTextChanged(int state)
|
void MacroConditionWindowEdit::CheckTextChanged(int state)
|
||||||
{
|
{
|
||||||
GUARD_LOADING_AND_LOCK();
|
GUARD_LOADING_AND_LOCK();
|
||||||
_entryData->_checkText = state;
|
_entryData->SetCheckText(state);
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,8 +441,8 @@ void MacroConditionWindowEdit::SetWidgetVisibility()
|
|||||||
_entryData->_focus || _entryData->_windowFocusChanged);
|
_entryData->_focus || _entryData->_windowFocusChanged);
|
||||||
_windowSelection->setVisible(_entryData->_checkTitle);
|
_windowSelection->setVisible(_entryData->_checkTitle);
|
||||||
_windowRegex->setVisible(_entryData->_checkTitle);
|
_windowRegex->setVisible(_entryData->_checkTitle);
|
||||||
_textRegex->setVisible(_entryData->_checkText);
|
_textRegex->setVisible(_entryData->GetCheckText());
|
||||||
_text->setVisible(_entryData->_checkText);
|
_text->setVisible(_entryData->GetCheckText());
|
||||||
adjustSize();
|
adjustSize();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
@@ -429,7 +461,7 @@ void MacroConditionWindowEdit::UpdateEntryData()
|
|||||||
_maximized->setChecked(_entryData->_maximized);
|
_maximized->setChecked(_entryData->_maximized);
|
||||||
_focused->setChecked(_entryData->_focus);
|
_focused->setChecked(_entryData->_focus);
|
||||||
_windowFocusChanged->setChecked(_entryData->_windowFocusChanged);
|
_windowFocusChanged->setChecked(_entryData->_windowFocusChanged);
|
||||||
_checkText->setChecked(_entryData->_checkText);
|
_checkText->setChecked(_entryData->GetCheckText());
|
||||||
_text->setPlainText(_entryData->_text);
|
_text->setPlainText(_entryData->_text);
|
||||||
_textRegex->SetRegexConfig(_entryData->_textRegex);
|
_textRegex->SetRegexConfig(_entryData->_textRegex);
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ public:
|
|||||||
return std::make_shared<MacroConditionWindow>(m);
|
return std::make_shared<MacroConditionWindow>(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetCheckText(bool value);
|
||||||
|
bool GetCheckText();
|
||||||
|
|
||||||
StringVariable _window;
|
StringVariable _window;
|
||||||
RegexConfig _windowRegex;
|
RegexConfig _windowRegex;
|
||||||
bool _checkTitle = true;
|
bool _checkTitle = true;
|
||||||
@@ -30,8 +33,6 @@ public:
|
|||||||
bool _focus = true;
|
bool _focus = true;
|
||||||
bool _windowFocusChanged = false;
|
bool _windowFocusChanged = false;
|
||||||
|
|
||||||
// For now only supported on Windows
|
|
||||||
bool _checkText = false;
|
|
||||||
StringVariable _text;
|
StringVariable _text;
|
||||||
RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig();
|
RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig();
|
||||||
|
|
||||||
@@ -42,6 +43,9 @@ private:
|
|||||||
void SetVariableValueBasedOnMatch(const std::string &matchWindow);
|
void SetVariableValueBasedOnMatch(const std::string &matchWindow);
|
||||||
void SetupTempVars();
|
void SetupTempVars();
|
||||||
|
|
||||||
|
// For now only supported on Windows
|
||||||
|
bool _checkText = false;
|
||||||
|
|
||||||
static bool _registered;
|
static bool _registered;
|
||||||
static const std::string id;
|
static const std::string id;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace advss {
|
|||||||
enum class HotkeyType;
|
enum class HotkeyType;
|
||||||
|
|
||||||
bool CanSimulateKeyPresses();
|
bool CanSimulateKeyPresses();
|
||||||
void PressKeys(const std::vector<HotkeyType> keys, int duration);
|
void PressKeys(const std::vector<HotkeyType> &keys, int duration);
|
||||||
|
|
||||||
class Hotkey {
|
class Hotkey {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ static const std::unordered_map<HotkeyType, long> keyTable = {
|
|||||||
{HotkeyType::Key_NumpadEnter, XK_KP_Enter},
|
{HotkeyType::Key_NumpadEnter, XK_KP_Enter},
|
||||||
};
|
};
|
||||||
|
|
||||||
void PressKeys(const std::vector<HotkeyType> keys, int duration)
|
void PressKeys(const std::vector<HotkeyType> &keys, int duration)
|
||||||
{
|
{
|
||||||
if (!canSimulateKeyPresses) {
|
if (!canSimulateKeyPresses) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ bool CanSimulateKeyPresses()
|
|||||||
return canSimulateKeyPresses;
|
return canSimulateKeyPresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PressKeys(const std::vector<HotkeyType> keys, int duration)
|
void PressKeys(const std::vector<HotkeyType> &, int)
|
||||||
{
|
{
|
||||||
// Not supported on MacOS
|
// Not supported on MacOS
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ findListPropertyById(const obs_source_t *source, const std::string &id)
|
|||||||
}
|
}
|
||||||
auto property = obs_properties_first(properties);
|
auto property = obs_properties_first(properties);
|
||||||
return {findListPropertyByIdHelper(property, id),
|
return {findListPropertyByIdHelper(property, id),
|
||||||
properties_t(nullptr, obs_properties_destroy)};
|
properties_t(properties, obs_properties_destroy)};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<obs_property_t *, properties_t>
|
static std::pair<obs_property_t *, properties_t>
|
||||||
@@ -335,6 +335,12 @@ void SetSourceSetting(obs_source_t *source, const SourceSetting &setting,
|
|||||||
case OBS_DATA_OBJECT: {
|
case OBS_DATA_OBJECT: {
|
||||||
OBSDataAutoRelease json =
|
OBSDataAutoRelease json =
|
||||||
obs_data_create_from_json(value.c_str());
|
obs_data_create_from_json(value.c_str());
|
||||||
|
if (!json) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"not setting invalid data object settings value! (%s)",
|
||||||
|
value.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
obs_data_set_obj(data, id.c_str(), json);
|
obs_data_set_obj(data, id.c_str(), json);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
32
plugins/base/utils/transition-helpers.cpp
Normal file
32
plugins/base/utils/transition-helpers.cpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include "transition-helpers.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
bool IsFixedLengthTransition(const OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
OBSSourceAutoRelease source = obs_weak_source_get_source(transition);
|
||||||
|
return obs_transition_fixed(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_t *SetSceneItemTransition(const OBSSceneItem &item,
|
||||||
|
const OBSSourceAutoRelease &transition,
|
||||||
|
bool show)
|
||||||
|
{
|
||||||
|
OBSDataAutoRelease settings = obs_source_get_settings(transition);
|
||||||
|
if (!transition || !settings) {
|
||||||
|
// Set transition to "None"
|
||||||
|
obs_sceneitem_set_transition(item, show, nullptr);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We cannot share the transition source between
|
||||||
|
// scene items without introducing strange graphical
|
||||||
|
// artifacts so we have to create new ones here
|
||||||
|
OBSSourceAutoRelease transitionSource = obs_source_create_private(
|
||||||
|
obs_source_get_id(transition), obs_source_get_name(transition),
|
||||||
|
settings);
|
||||||
|
obs_sceneitem_set_transition(item, show, transitionSource);
|
||||||
|
return transitionSource.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
11
plugins/base/utils/transition-helpers.hpp
Normal file
11
plugins/base/utils/transition-helpers.hpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <obs.hpp>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
bool IsFixedLengthTransition(const OBSWeakSource &transition);
|
||||||
|
obs_source_t *SetSceneItemTransition(const OBSSceneItem &item,
|
||||||
|
const OBSSourceAutoRelease &transition,
|
||||||
|
bool show);
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -138,7 +138,7 @@ static const std::unordered_map<HotkeyType, long> keyTable = {
|
|||||||
{HotkeyType::Key_NumpadEnter, VK_RETURN},
|
{HotkeyType::Key_NumpadEnter, VK_RETURN},
|
||||||
};
|
};
|
||||||
|
|
||||||
void PressKeys(const std::vector<HotkeyType> keys, int duration)
|
void PressKeys(const std::vector<HotkeyType> &keys, int duration)
|
||||||
{
|
{
|
||||||
const int repeatInterval = 100;
|
const int repeatInterval = 100;
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ struct URLInfo {
|
|||||||
std::string path;
|
std::string path;
|
||||||
};
|
};
|
||||||
|
|
||||||
static URLInfo getURLInfo(const std::string &input)
|
static URLInfo getURLInfo(const std::string &input, bool keepParams)
|
||||||
{
|
{
|
||||||
if (input.empty()) {
|
if (input.empty()) {
|
||||||
return {};
|
return {};
|
||||||
@@ -84,21 +84,35 @@ static URLInfo getURLInfo(const std::string &input)
|
|||||||
const QUrl url(urlInput);
|
const QUrl url(urlInput);
|
||||||
auto host =
|
auto host =
|
||||||
url.scheme().toStdString() + "://" + url.host().toStdString();
|
url.scheme().toStdString() + "://" + url.host().toStdString();
|
||||||
int port = url.port();
|
|
||||||
|
const int port = url.port();
|
||||||
if (port != -1) {
|
if (port != -1) {
|
||||||
host += ":" + std::to_string(port);
|
host += ":" + std::to_string(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto path = url.path().toStdString();
|
auto path = url.path().toStdString();
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
path = "/";
|
path = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto query = url.query().toStdString();
|
||||||
|
if (!query.empty()) {
|
||||||
|
if (keepParams) {
|
||||||
|
path += "?" + query;
|
||||||
|
} else {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"ignoring query parameters \"%s\" in URL field. "
|
||||||
|
"Using parameter field values instead.",
|
||||||
|
query.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {host, path};
|
return {host, path};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroActionHttp::PerformAction()
|
bool MacroActionHttp::PerformAction()
|
||||||
{
|
{
|
||||||
const auto [host, path] = getURLInfo(_url);
|
const auto [host, path] = getURLInfo(_url, !_setParams);
|
||||||
|
|
||||||
httplib::Client cli(host);
|
httplib::Client cli(host);
|
||||||
setTimeout(cli, _timeout);
|
setTimeout(cli, _timeout);
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
|
|||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
|
void ScriptHandler::GetRunningStatus(void *, calldata_t *data)
|
||||||
{
|
{
|
||||||
calldata_set_bool(data, "is_running", PluginIsRunning());
|
calldata_set_bool(data, "is_running", PluginIsRunning());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,14 +58,16 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const
|
|||||||
}
|
}
|
||||||
OBSDataArrayAutoRelease array = obs_data_get_array(res.data, "data");
|
OBSDataArrayAutoRelease array = obs_data_get_array(res.data, "data");
|
||||||
size_t count = obs_data_array_count(array);
|
size_t count = obs_data_array_count(array);
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
OBSDataAutoRelease arrayObj = obs_data_array_item(array, i);
|
if (count == 0) {
|
||||||
std::string id = obs_data_get_string(arrayObj, "id");
|
userIDCache[_name] = "invalid";
|
||||||
userIDCache[_name] = id;
|
return "invalid";
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
userIDCache[_name] = "invalid";
|
|
||||||
return "invalid";
|
OBSDataAutoRelease arrayObj = obs_data_array_item(array, 0);
|
||||||
|
const std::string id = obs_data_get_string(arrayObj, "id");
|
||||||
|
userIDCache[_name] = id;
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QDate parseRFC3339Date(const std::string &rfc3339Date)
|
static QDate parseRFC3339Date(const std::string &rfc3339Date)
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ void EventSub::UnregisterInstance()
|
|||||||
void EventSub::ConnectThread()
|
void EventSub::ConnectThread()
|
||||||
{
|
{
|
||||||
_client->reset();
|
_client->reset();
|
||||||
_connected = true;
|
|
||||||
websocketpp::lib::error_code ec;
|
websocketpp::lib::error_code ec;
|
||||||
EventSubWSClient::connection_ptr con =
|
EventSubWSClient::connection_ptr con =
|
||||||
_client->get_connection(_url, ec);
|
_client->get_connection(_url, ec);
|
||||||
@@ -90,12 +89,24 @@ void EventSub::ConnectThread()
|
|||||||
|
|
||||||
void EventSub::WaitAndReconnect()
|
void EventSub::WaitAndReconnect()
|
||||||
{
|
{
|
||||||
|
if (_reconnecting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_reconnecting = true;
|
||||||
|
|
||||||
auto thread = std::thread([this]() {
|
auto thread = std::thread([this]() {
|
||||||
std::unique_lock<std::mutex> lock(_waitMtx);
|
std::unique_lock<std::mutex> lock(_waitMtx);
|
||||||
blog(LOG_INFO,
|
blog(LOG_INFO,
|
||||||
"Twitch EventSub trying to reconnect to in %d seconds.",
|
"Twitch EventSub trying to reconnect to in %d seconds.",
|
||||||
reconnectDelay);
|
reconnectDelay);
|
||||||
_cv.wait_for(lock, std::chrono::seconds(reconnectDelay));
|
_cv.wait_for(lock, std::chrono::seconds(reconnectDelay));
|
||||||
|
_reconnecting = false;
|
||||||
|
|
||||||
|
if (_disconnect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Connect();
|
Connect();
|
||||||
});
|
});
|
||||||
thread.detach();
|
thread.detach();
|
||||||
@@ -103,6 +114,10 @@ void EventSub::WaitAndReconnect()
|
|||||||
|
|
||||||
void EventSub::Connect()
|
void EventSub::Connect()
|
||||||
{
|
{
|
||||||
|
if (_reconnecting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(_connectMtx);
|
std::lock_guard<std::mutex> lock(_connectMtx);
|
||||||
if (_connected) {
|
if (_connected) {
|
||||||
vblog(LOG_INFO, "Twitch EventSub connect already in progress");
|
vblog(LOG_INFO, "Twitch EventSub connect already in progress");
|
||||||
@@ -113,6 +128,7 @@ void EventSub::Connect()
|
|||||||
_thread.join();
|
_thread.join();
|
||||||
}
|
}
|
||||||
_disconnect = false;
|
_disconnect = false;
|
||||||
|
_connected = true;
|
||||||
_thread = std::thread(&EventSub::ConnectThread, this);
|
_thread = std::thread(&EventSub::ConnectThread, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,7 +464,7 @@ void EventSub::StartServerMigrationClient(const std::string &url)
|
|||||||
auto client = std::make_unique<EventSubWSClient>();
|
auto client = std::make_unique<EventSubWSClient>();
|
||||||
SetupClient(*client);
|
SetupClient(*client);
|
||||||
|
|
||||||
client->set_open_handler([this](connection_hdl hdl) {
|
client->set_open_handler([this](connection_hdl) {
|
||||||
vblog(LOG_INFO, "Twitch EventSub migration client opened");
|
vblog(LOG_INFO, "Twitch EventSub migration client opened");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -465,8 +481,8 @@ void EventSub::StartServerMigrationClient(const std::string &url)
|
|||||||
const auto &data = msg->payload;
|
const auto &data = msg->payload;
|
||||||
|
|
||||||
if (type == "session_welcome") {
|
if (type == "session_welcome") {
|
||||||
vblog(LOG_INFO,
|
blog(LOG_INFO,
|
||||||
"Twitch EventSub migration successful - switching to new connection");
|
"Twitch EventSub migration successful - switching to new connection");
|
||||||
OBSDataAutoRelease session =
|
OBSDataAutoRelease session =
|
||||||
obs_data_get_obj(data, "session");
|
obs_data_get_obj(data, "session");
|
||||||
_migrationSessionID =
|
_migrationSessionID =
|
||||||
@@ -565,7 +581,7 @@ void EventSub::OnClose(connection_hdl hdl)
|
|||||||
blog(LOG_INFO, "Twitch EventSub connection closed: %s / %s (%d)",
|
blog(LOG_INFO, "Twitch EventSub connection closed: %s / %s (%d)",
|
||||||
msg.c_str(), reason.c_str(), code);
|
msg.c_str(), reason.c_str(), code);
|
||||||
|
|
||||||
if (_migrating) {
|
if (!_migrating) {
|
||||||
ClearActiveSubscriptions();
|
ClearActiveSubscriptions();
|
||||||
}
|
}
|
||||||
_connected = false;
|
_connected = false;
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ private:
|
|||||||
std::condition_variable _cv;
|
std::condition_variable _cv;
|
||||||
std::atomic_bool _connected{false};
|
std::atomic_bool _connected{false};
|
||||||
std::atomic_bool _disconnect{false};
|
std::atomic_bool _disconnect{false};
|
||||||
|
std::atomic_bool _reconnecting{false};
|
||||||
|
|
||||||
std::string _url;
|
std::string _url;
|
||||||
std::string _sessionID;
|
std::string _sessionID;
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ const static std::map<MacroActionTwitch::Action, std::string> actionTypes = {
|
|||||||
"AdvSceneSwitcher.action.twitch.type.user.getInfo"},
|
"AdvSceneSwitcher.action.twitch.type.user.getInfo"},
|
||||||
{MacroActionTwitch::Action::POINTS_REWARD_GET_INFO,
|
{MacroActionTwitch::Action::POINTS_REWARD_GET_INFO,
|
||||||
"AdvSceneSwitcher.action.twitch.type.reward.getInfo"},
|
"AdvSceneSwitcher.action.twitch.type.reward.getInfo"},
|
||||||
|
{MacroActionTwitch::Action::CHANNEL_GET_INFO,
|
||||||
|
"AdvSceneSwitcher.action.twitch.type.channel.getInfo"},
|
||||||
};
|
};
|
||||||
|
|
||||||
const static std::map<MacroActionTwitch::AnnouncementColor, std::string>
|
const static std::map<MacroActionTwitch::AnnouncementColor, std::string>
|
||||||
@@ -322,6 +324,41 @@ void MacroActionTwitch::GetUserInfo(const std::shared_ptr<TwitchToken> &token)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroActionTwitch::GetChannelInfo(const std::shared_ptr<TwitchToken> &token)
|
||||||
|
{
|
||||||
|
auto info = _channel.GetInfo(*token);
|
||||||
|
if (!info) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTempVarValue("broadcaster_user_id", info->broadcaster_id);
|
||||||
|
SetTempVarValue("broadcaster_user_login", info->broadcaster_login);
|
||||||
|
SetTempVarValue("broadcaster_user_name", info->broadcaster_name);
|
||||||
|
SetTempVarValue("language", info->broadcaster_language);
|
||||||
|
SetTempVarValue("game_id", info->game_id);
|
||||||
|
SetTempVarValue("game_name", info->game_name);
|
||||||
|
SetTempVarValue("title", info->title);
|
||||||
|
SetTempVarValue("delay", std::to_string(info->delay));
|
||||||
|
std::string tags;
|
||||||
|
for (const auto &tag : info->tags) {
|
||||||
|
tags += tag + " ";
|
||||||
|
}
|
||||||
|
if (!tags.empty()) {
|
||||||
|
tags.pop_back();
|
||||||
|
}
|
||||||
|
SetTempVarValue("tags", tags);
|
||||||
|
std::string classificationLabels;
|
||||||
|
for (const auto &label : info->content_classification_labels) {
|
||||||
|
classificationLabels += label + " ";
|
||||||
|
}
|
||||||
|
if (!classificationLabels.empty()) {
|
||||||
|
classificationLabels.pop_back();
|
||||||
|
}
|
||||||
|
SetTempVarValue("content_classification_labels", classificationLabels);
|
||||||
|
SetTempVarValue("is_branded_content",
|
||||||
|
info->is_branded_content ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
bool MacroActionTwitch::ResolveVariableSelectionToRewardId(
|
bool MacroActionTwitch::ResolveVariableSelectionToRewardId(
|
||||||
const std::shared_ptr<TwitchToken> &token)
|
const std::shared_ptr<TwitchToken> &token)
|
||||||
{
|
{
|
||||||
@@ -505,6 +542,16 @@ void MacroActionTwitch::SetupTempVars()
|
|||||||
setupTempVarHelper("offline_image_url", ".user.getInfo");
|
setupTempVarHelper("offline_image_url", ".user.getInfo");
|
||||||
setupTempVarHelper("created_at", ".user.getInfo");
|
setupTempVarHelper("created_at", ".user.getInfo");
|
||||||
break;
|
break;
|
||||||
|
case Action::CHANNEL_GET_INFO:
|
||||||
|
setupTempVarHelper("language");
|
||||||
|
setupTempVarHelper("game_id");
|
||||||
|
setupTempVarHelper("game_name");
|
||||||
|
setupTempVarHelper("title");
|
||||||
|
setupTempVarHelper("delay");
|
||||||
|
setupTempVarHelper("tags");
|
||||||
|
setupTempVarHelper("content_classification_labels");
|
||||||
|
setupTempVarHelper("is_branded_content");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -563,6 +610,9 @@ bool MacroActionTwitch::PerformAction()
|
|||||||
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
|
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
|
||||||
GetRewardInfo(token);
|
GetRewardInfo(token);
|
||||||
break;
|
break;
|
||||||
|
case MacroActionTwitch::Action::CHANNEL_GET_INFO:
|
||||||
|
GetChannelInfo(token);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -772,6 +822,7 @@ bool MacroActionTwitch::ActionIsSupportedByToken()
|
|||||||
{Action::WHISPER_SEND, {{"user:manage:whispers"}}},
|
{Action::WHISPER_SEND, {{"user:manage:whispers"}}},
|
||||||
{Action::SEND_CHAT_MESSAGE, {{"chat:edit"}}},
|
{Action::SEND_CHAT_MESSAGE, {{"chat:edit"}}},
|
||||||
{Action::USER_GET_INFO, {}},
|
{Action::USER_GET_INFO, {}},
|
||||||
|
{Action::CHANNEL_GET_INFO, {}},
|
||||||
{Action::POINTS_REWARD_GET_INFO,
|
{Action::POINTS_REWARD_GET_INFO,
|
||||||
{{"channel:read:redemptions"},
|
{{"channel:read:redemptions"},
|
||||||
{"channel:manage:redemptions"}}}};
|
{"channel:manage:redemptions"}}}};
|
||||||
@@ -1127,6 +1178,8 @@ void MacroActionTwitchEdit::SetWidgetVisibility()
|
|||||||
_entryData->GetAction() ==
|
_entryData->GetAction() ==
|
||||||
MacroActionTwitch::Action::CHANNEL_INFO_CONTENT_LABELS_SET);
|
MacroActionTwitch::Action::CHANNEL_INFO_CONTENT_LABELS_SET);
|
||||||
_channel->setVisible(
|
_channel->setVisible(
|
||||||
|
_entryData->GetAction() ==
|
||||||
|
MacroActionTwitch::Action::CHANNEL_GET_INFO ||
|
||||||
_entryData->GetAction() ==
|
_entryData->GetAction() ==
|
||||||
MacroActionTwitch::Action::RAID_START ||
|
MacroActionTwitch::Action::RAID_START ||
|
||||||
_entryData->GetAction() ==
|
_entryData->GetAction() ==
|
||||||
@@ -1273,19 +1326,23 @@ void MacroActionTwitchEdit::SetWidgetLayout()
|
|||||||
switch (_entryData->GetAction()) {
|
switch (_entryData->GetAction()) {
|
||||||
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
|
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
|
||||||
layoutText = obs_module_text(
|
layoutText = obs_module_text(
|
||||||
"AdvSceneSwitcher.action.twitch.entry.chat");
|
"AdvSceneSwitcher.action.twitch.layout.chat");
|
||||||
break;
|
break;
|
||||||
case MacroActionTwitch::Action::USER_GET_INFO:
|
case MacroActionTwitch::Action::USER_GET_INFO:
|
||||||
layoutText = obs_module_text(
|
layoutText = obs_module_text(
|
||||||
"AdvSceneSwitcher.action.twitch.entry.user.getInfo");
|
"AdvSceneSwitcher.action.twitch.layout.user.getInfo");
|
||||||
break;
|
break;
|
||||||
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
|
case MacroActionTwitch::Action::POINTS_REWARD_GET_INFO:
|
||||||
layoutText = obs_module_text(
|
layoutText = obs_module_text(
|
||||||
"AdvSceneSwitcher.action.twitch.entry.reward.getInfo");
|
"AdvSceneSwitcher.action.twitch.layout.reward.getInfo");
|
||||||
|
break;
|
||||||
|
case MacroActionTwitch::Action::CHANNEL_GET_INFO:
|
||||||
|
layoutText = obs_module_text(
|
||||||
|
"AdvSceneSwitcher.action.twitch.layout.channel.getInfo");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
layoutText = obs_module_text(
|
layoutText = obs_module_text(
|
||||||
"AdvSceneSwitcher.action.twitch.entry.default");
|
"AdvSceneSwitcher.action.twitch.layout.default");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,8 @@ public:
|
|||||||
SEND_CHAT_MESSAGE = 5000,
|
SEND_CHAT_MESSAGE = 5000,
|
||||||
|
|
||||||
// Get user info
|
// Get user info
|
||||||
USER_GET_INFO = 6000
|
USER_GET_INFO = 6000,
|
||||||
|
CHANNEL_GET_INFO = 6100,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class AnnouncementColor {
|
enum class AnnouncementColor {
|
||||||
@@ -203,6 +204,7 @@ private:
|
|||||||
void SendChatMessage(const std::shared_ptr<TwitchToken> &);
|
void SendChatMessage(const std::shared_ptr<TwitchToken> &);
|
||||||
void GetUserInfo(const std::shared_ptr<TwitchToken> &);
|
void GetUserInfo(const std::shared_ptr<TwitchToken> &);
|
||||||
void GetRewardInfo(const std::shared_ptr<TwitchToken> &);
|
void GetRewardInfo(const std::shared_ptr<TwitchToken> &);
|
||||||
|
void GetChannelInfo(const std::shared_ptr<TwitchToken> &token);
|
||||||
|
|
||||||
bool ResolveVariableSelectionToRewardId(
|
bool ResolveVariableSelectionToRewardId(
|
||||||
const std::shared_ptr<TwitchToken> &);
|
const std::shared_ptr<TwitchToken> &);
|
||||||
|
|||||||
@@ -629,7 +629,7 @@ void MacroConditionTwitch::SetTempVarValues(const ChannelInfo &info)
|
|||||||
if (!classificationLabels.empty()) {
|
if (!classificationLabels.empty()) {
|
||||||
classificationLabels.pop_back();
|
classificationLabels.pop_back();
|
||||||
}
|
}
|
||||||
SetTempVarValue("content_classification_labels", tags);
|
SetTempVarValue("content_classification_labels", classificationLabels);
|
||||||
SetTempVarValue("is_branded_content",
|
SetTempVarValue("is_branded_content",
|
||||||
info.is_branded_content ? "true" : "false");
|
info.is_branded_content ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -829,7 +829,7 @@ void TokenGrabberThread::run()
|
|||||||
if (_serverThread.joinable()) {
|
if (_serverThread.joinable()) {
|
||||||
_serverThread.join();
|
_serverThread.join();
|
||||||
}
|
}
|
||||||
_stopWaiting = {false};
|
_stopWaiting = false;
|
||||||
|
|
||||||
// Generate URI to request token
|
// Generate URI to request token
|
||||||
auto state = generateStateString();
|
auto state = generateStateString();
|
||||||
|
|||||||
@@ -106,8 +106,11 @@ target_sources(
|
|||||||
|
|
||||||
# --- variable --- #
|
# --- variable --- #
|
||||||
|
|
||||||
if(NOT OS_WINDOWS)
|
if(OS_WINDOWS)
|
||||||
target_compile_options(${PROJECT_NAME} PUBLIC -Wno-error=shadow)
|
target_compile_options(${PROJECT_NAME} PRIVATE /wd4100)
|
||||||
|
else()
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-error=shadow
|
||||||
|
-Wno-unused-parameter)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_sources(
|
target_sources(
|
||||||
|
|||||||
Reference in New Issue
Block a user