Merge branch 'master' into virtual-desktop

This commit is contained in:
WarmUpTill 2021-06-06 19:14:14 +02:00
commit d66a2dc408
105 changed files with 4948 additions and 1446 deletions

View File

@ -200,7 +200,7 @@ jobs:
QT_VERSION: 5.10.1
CMAKE_GENERATOR: "Visual Studio 16 2019"
CMAKE_SYSTEM_VERSION: "10.0.18363.657"
WINDOWS_DEPS_VERSION: '2017'
WINDOWS_DEPS_VERSION: '2019'
steps:
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.2

View File

@ -62,7 +62,6 @@ set(advanced-scene-switcher_HEADERS
${advanced-scene-switcher_HEADERS}
src/headers/advanced-scene-switcher.hpp
src/headers/switcher-data-structs.hpp
src/headers/utility.hpp
src/headers/scene-group.hpp
src/headers/scene-trigger.hpp
src/headers/switch-audio.hpp
@ -82,32 +81,44 @@ set(advanced-scene-switcher_HEADERS
src/headers/switch-generic.hpp
src/headers/macro-action-edit.hpp
src/headers/macro-action-audio.hpp
src/headers/macro-action-filter.hpp
src/headers/macro-action-macro.hpp
src/headers/macro-action-media.hpp
src/headers/macro-action-plugin-state.hpp
src/headers/macro-action-recording.hpp
src/headers/macro-action-replay-buffer.hpp
src/headers/macro-action-run.hpp
src/headers/macro-action-scene-switch.hpp
src/headers/macro-action-scene-visibility.hpp
src/headers/macro-action-source.hpp
src/headers/macro-action-streaming.hpp
src/headers/macro-action-switch-scene.hpp
src/headers/macro-action-wait.hpp
src/headers/macro-condition-edit.hpp
src/headers/macro-condition-audio.hpp
src/headers/macro-condition-counter.hpp
src/headers/macro-condition-file.hpp
src/headers/macro-condition-idle.hpp
src/headers/macro-condition-interval.hpp
src/headers/macro-condition-media.hpp
src/headers/macro-condition-plugin-state.hpp
src/headers/macro-condition-process.hpp
src/headers/macro-condition-recording.hpp
src/headers/macro-condition-region.hpp
src/headers/macro-condition-scene.hpp
src/headers/macro-condition-source.hpp
src/headers/macro-condition-streaming.hpp
src/headers/macro-condition-video.hpp
src/headers/macro-condition-virt-desktop.hpp
src/headers/macro-condition-window.hpp
src/headers/macro.hpp
src/headers/macro-selection.hpp
src/headers/curl-helper.hpp
src/headers/screenshot-helper.hpp
src/headers/name-dialog.hpp
src/headers/duration-control.hpp
src/headers/section.hpp
src/headers/platform-funcs.hpp
src/headers/utility.hpp
src/headers/volume-control.hpp
src/headers/version.h
)
@ -139,33 +150,44 @@ set(advanced-scene-switcher_SOURCES
src/switch-generic.cpp
src/macro-action-edit.cpp
src/macro-action-audio.cpp
src/macro-action-filter.cpp
src/macro-action-macro.cpp
src/macro-action-media.cpp
src/macro-action-plugin-state.cpp
src/macro-action-recording.cpp
src/macro-action-replay-buffer.cpp
src/macro-action-run.cpp
src/macro-action-scene-switch.cpp
src/macro-action-scene-visibility.cpp
src/macro-action-source.cpp
src/macro-action-streaming.cpp
src/macro-action-switch-scene.cpp
src/macro-action-wait.cpp
src/macro-condition-edit.cpp
src/macro-condition-audio.cpp
src/macro-condition-counter.cpp
src/macro-condition-file.cpp
src/macro-condition-idle.cpp
src/macro-condition-interval.cpp
src/macro-condition-media.cpp
src/macro-condition-plugin-state.cpp
src/macro-condition-process.cpp
src/macro-condition-recording.cpp
src/macro-condition-region.cpp
src/macro-condition-scene.cpp
src/macro-condition-source.cpp
src/macro-condition-streaming.cpp
src/macro-condition-video.cpp
src/macro-condition-virt-desktop.cpp
src/macro-condition-window.cpp
src/macro.cpp
src/macro-selection.cpp
src/macro-tab.cpp
src/curl-helper.cpp
src/screenshot-helper.cpp
src/name-dialog.cpp
src/duration-control.cpp
src/section.cpp
src/utility.cpp
src/volume-control.cpp
src/version.cpp
)

View File

@ -227,7 +227,9 @@ AdvSceneSwitcher.networkTab.Disabledwarning="Diese Funktionalität musste unter
AdvSceneSwitcher.networkTab.server="Server starten (Sendet Szenenwechselnachrichten zu allen verbundenen Clients)"
AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Sende nur Szenenwechselnachrichten für automatisierte Szenenwechsel"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Sende Nachrichten für Szenenwechsel"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Sende nur Szenenwechselnachrichten für automatisierte Szenenwechsel"
AdvSceneSwitcher.networkTab.server.sendPreview="Sende Nachrichten für Vorschau Szenenwechsel im Studio-Modus"
AdvSceneSwitcher.networkTab.startFailed.message="Der WebSocket-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Fehler: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Aktueller Status"
AdvSceneSwitcher.networkTab.server.status.notRunning="Nicht gestartet"

View File

@ -57,8 +57,8 @@ AdvSceneSwitcher.generalTab.priority.macro="Macro"
AdvSceneSwitcher.macroTab.title="Macro"
AdvSceneSwitcher.macroTab.macros="Macros"
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define Macro conditions.\n\nSelect an existing or add a new Macro on the left."
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define Macro actions.\n\nSelect an existing or add a new Macro on the left."
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.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.edit="Edit macro"
AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
@ -68,6 +68,7 @@ AdvSceneSwitcher.macroTab.name="Name:"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
AdvSceneSwitcher.macroTab.copy="Create copy"
; Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry"
AdvSceneSwitcher.logic.and="And"
@ -76,17 +77,18 @@ AdvSceneSwitcher.logic.andNot="And not"
AdvSceneSwitcher.logic.orNot="Or not"
AdvSceneSwitcher.logic.rootNone="If"
AdvSceneSwitcher.logic.not="If not"
; Macro Conditions
AdvSceneSwitcher.condition.audio="Audio"
AdvSceneSwitcher.ondition.audio.state.below="Below"
AdvSceneSwitcher.ondition.audio.state.above="Above"
AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}} for {{duration}} seconds"
AdvSceneSwitcher.condition.audio.state.below="Below"
AdvSceneSwitcher.condition.audio.state.above="Above"
AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}}"
AdvSceneSwitcher.condition.region="Screen region"
AdvSceneSwitcher.condition.region.entry="Cursor is in {{minX}} {{minY}} x {{maxX}} {{maxY}}"
AdvSceneSwitcher.condition.scene="Scene"
AdvSceneSwitcher.condition.scene.type.current="Current"
AdvSceneSwitcher.condition.scene.type.previous="Previous"
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} scene is {{scenes}} for {{duration}}"
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} scene is {{scenes}}"
AdvSceneSwitcher.condition.window="Window"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused"
@ -105,16 +107,16 @@ AdvSceneSwitcher.condition.video.condition.noImage="has no output"
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}} for {{duration}}"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}}"
AdvSceneSwitcher.condition.stream="Streaming"
AdvSceneSwitcher.condition.stream.state.start="Stream running"
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
AdvSceneSwitcher.condition.stream.entry="{{streamState}} for {{duration}}"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
AdvSceneSwitcher.condition.record="Recording"
AdvSceneSwitcher.condition.record.state.start="Recording running"
AdvSceneSwitcher.condition.record.state.pause="Recording paused"
AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
AdvSceneSwitcher.condition.record.entry="{{recordState}} for {{duration}}"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Process"
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
AdvSceneSwitcher.condition.idle="Idle"
@ -126,6 +128,24 @@ AdvSceneSwitcher.condition.virtDesktop="Virtual desktop"
AdvSceneSwitcher.condition.virtDesktop.notAvailable="Not availbale"
AdvSceneSwitcher.condition.virtDesktop.entry.line1="Active virtual desktop is {{virtDesktops}}"
AdvSceneSwitcher.condition.virtDesktop.entry.line2="Current: {{currentDesktop}}"
AdvSceneSwitcher.condition.interval="Interval"
AdvSceneSwitcher.condition.interval.entry="{{duration}} have passed"
AdvSceneSwitcher.condition.counter="Count"
AdvSceneSwitcher.condition.counter.type.below="Less than"
AdvSceneSwitcher.condition.counter.type.above="More than"
AdvSceneSwitcher.condition.counter.type.equal="Exactly"
AdvSceneSwitcher.condition.counter.reset="Reset"
AdvSceneSwitcher.condition.counter.entry.line1="{{macros}} was executed {{conditions}} {{count}} times"
AdvSceneSwitcher.condition.counter.entry.line2="Current count: {{currentCount}} {{resetCount}}"
AdvSceneSwitcher.condition.source="Source"
AdvSceneSwitcher.condition.source.type.active="Is active"
AdvSceneSwitcher.condition.source.type.showing="Is showing"
AdvSceneSwitcher.condition.source.type.settings="Settings match"
AdvSceneSwitcher.condition.source.regex="Use regular expressions"
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Switch scene"
@ -159,7 +179,37 @@ AdvSceneSwitcher.action.streaming.type.start="Start streaming"
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Run"
AdvSceneSwitcher.action.run.entry="Run {{filePath}} {{browseButton}}"
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
AdvSceneSwitcher.action.sceneVisibility.entry="On {{scenes}} {{actions}} {{sources}}"
AdvSceneSwitcher.action.filter="Filter"
AdvSceneSwitcher.action.filter.type.enable="Enable"
AdvSceneSwitcher.action.filter.type.disable="Disable"
AdvSceneSwitcher.action.filter.entry="On {{sources}} {{actions}} {{filters}}"
AdvSceneSwitcher.action.source="Source"
AdvSceneSwitcher.action.source.type.enable="Enable"
AdvSceneSwitcher.action.source.type.disable="Disable"
AdvSceneSwitcher.action.source.type.settings="Set settings"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI"
AdvSceneSwitcher.action.source.getSettings="Get current settings"
AdvSceneSwitcher.action.media="Media"
AdvSceneSwitcher.action.media.type.play="Play"
AdvSceneSwitcher.action.media.type.pause="Pause"
AdvSceneSwitcher.action.media.type.stop="Stop"
AdvSceneSwitcher.action.media.type.restart="Restart"
AdvSceneSwitcher.action.media.type.next="Next"
AdvSceneSwitcher.action.media.type.previous="Previous"
AdvSceneSwitcher.action.media.entry="{{actions}} {{mediaSources}}"
AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pause"
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.pluginState="Plugin state"
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
AdvSceneSwitcher.action.pluginState.entry="{{actions}}"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition"
@ -336,7 +386,9 @@ AdvSceneSwitcher.networkTab.DisabledWarning="This functionality unfortunately ha
AdvSceneSwitcher.networkTab.server="Start server (Sends scene switch messages to all connected clients)"
AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Lock server to only using IPv4"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Send messages for scene changes"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Only send messages for automated scene switches"
AdvSceneSwitcher.networkTab.server.sendPreview="Send messages for preview scene change when running in Studio mode"
AdvSceneSwitcher.networkTab.startFailed.message="The WebSockets server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - Error message: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Current status"
AdvSceneSwitcher.networkTab.server.status.notRunning="Not running"
@ -398,6 +450,8 @@ AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.macro.pause="Pause macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Unpause macro %1"
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
@ -409,10 +463,14 @@ AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.currentTransition="Current Transition"
AdvSceneSwitcher.selectTransition="--select transition--"
AdvSceneSwitcher.selectWindow="--select window--"
AdvSceneSwitcher.selectSource="--select source--"
AdvSceneSwitcher.selectAudioSource="--select audio source--"
AdvSceneSwitcher.selectVideoSource="--select video source--"
AdvSceneSwitcher.selectMediaSource="--select media source--"
AdvSceneSwitcher.selectProcess="--select process--"
AdvSceneSwitcher.selectFilter="--select filter--"
AdvSceneSwitcher.selectMacro="--select macro--"
AdvSceneSwitcher.selectItem="--select item--"
AdvSceneSwitcher.enterPath="--enter path--"
AdvSceneSwitcher.enterText="--enter text--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
@ -425,3 +483,7 @@ AdvSceneSwitcher.unit.milliseconds="milliseconds"
AdvSceneSwitcher.unit.secends="seconds"
AdvSceneSwitcher.unit.minutes="minutes"
AdvSceneSwitcher.unit.hours="hours"
AdvSceneSwitcher.duration.condition.none="No time constraint"
AdvSceneSwitcher.duration.condition.more="For at least"
AdvSceneSwitcher.duration.condition.equal="For exactly"
AdvSceneSwitcher.duration.condition.less="For at most"

423
data/locale/ru-RU.ini Normal file
View File

@ -0,0 +1,423 @@
AdvSceneSwitcher.pluginName="Advanced Scene Switcher"
AdvSceneSwitcher.windowTitle="Advanced Scene Switcher"
; General Tab
AdvSceneSwitcher.generalTab.title="Общие"
AdvSceneSwitcher.generalTab.status="Статус"
AdvSceneSwitcher.generalTab.status.hotkeytips="Горячие клавиши можно определить в настройках OBS"
AdvSceneSwitcher.generalTab.status.currentStatus="Advanced Scene Switcher сейчас:"
AdvSceneSwitcher.generalTab.status.onStartup="При запуске OBS:"
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Запустить переключатель сцен, если он был запущен"
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Всегда запускать переключатель сцен"
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Не запускать переключатель сцен"
AdvSceneSwitcher.generalTab.status.start="Старт"
AdvSceneSwitcher.generalTab.status.stop="Стоп"
AdvSceneSwitcher.generalTab.status.autoStart="Автоматически запускать переключатель сцен, когда:"
AdvSceneSwitcher.generalTab.status.autoStart.never="Никогда"
AdvSceneSwitcher.generalTab.status.autoStart.recording="Запись"
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Вещание"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Запись или потоковое вещание"
AdvSceneSwitcher.generalTab.status.checkInterval="Проверять условия переключения каждый раз"
AdvSceneSwitcher.generalTab.generalBehavior="Общее поведение"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Если не выполняется условие переключения для"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Будет только настолько точным, насколько настроен интервал проверки."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Не переключаться"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Переключиться на любую сцену на вкладке Random"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Переключиться на:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="После совпадения не переключаться между сценами в течение"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Включить ведение подробного журнала"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Отключить подсказки пользовательского интерфейса"
AdvSceneSwitcher.generalTab.priority="Приоритет"
AdvSceneSwitcher.generalTab.priority.description="Приоритет методов переключения (самый высокий приоритет - вверху)"
AdvSceneSwitcher.generalTab.priority.threadPriority="Использовать приоритет потока"
AdvSceneSwitcher.generalTab.priority.threadPriorityNotice="(Поднимать приоритет выше \"Нормальный\" не рекомендуется)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings="Сохранить / загрузить настройки"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Экспорт"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Импорт"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Экспортировать настройки расширенного переключателя сцен в файл ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Импортировать настройки Advanced Scene Switcher из файла ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Текстовые файлы (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Advanced Scene Switcher не удалось импортировать настройки"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Настройки Advanced Scene Switcher импортированы успешно"
AdvSceneSwitcher.generalTab.priority.fileContent="Содержание файла"
AdvSceneSwitcher.generalTab.priority.sceneSequence="Последовательность сцены"
AdvSceneSwitcher.generalTab.priority.idleDetection="Обнаружение простоя"
AdvSceneSwitcher.generalTab.priority.executable="Исполняемый файл"
AdvSceneSwitcher.generalTab.priority.screenRegion="Область экрана"
AdvSceneSwitcher.generalTab.priority.windowTitle="Заголовок окна"
AdvSceneSwitcher.generalTab.priority.media="Медиа"
AdvSceneSwitcher.generalTab.priority.time="Время"
AdvSceneSwitcher.generalTab.priority.audio="Аудио"
AdvSceneSwitcher.generalTab.priority.video="Видео"
AdvSceneSwitcher.generalTab.priority.macro="Макрос"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Макрос"
AdvSceneSwitcher.macroTab.macros="Макросы"
AdvSceneSwitcher.macroTab.help="Макросы позволяют выполнять ряд действий в зависимости от нескольких условий.\n\nНажмите на выделенный символ плюса, чтобы добавить новый макрос."
AdvSceneSwitcher.macroTab.editConditionHelp="Этот раздел позволяет определить условия макроса.\n\nВыберите существующий или добавьте новый макрос слева."
AdvSceneSwitcher.macroTab.editActionHelp="Этот раздел позволяет определить действия макроса.\n\n\nВыберите существующий или добавьте новый макрос слева."
AdvSceneSwitcher.macroTab.edit="Редактировать макрос"
AdvSceneSwitcher.macroTab.edit.logic="Тип логики:"
AdvSceneSwitcher.macroTab.edit.condition="Тип условия:"
AdvSceneSwitcher.macroTab.edit.action="Тип действия:"
AdvSceneSwitcher.macroTab.add="Добавить новый макрос"
AdvSceneSwitcher.macroTab.name="Имя:"
AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
AdvSceneSwitcher.macroTab.exists="Имя макроса уже существует"
AdvSceneSwitcher.macroTab.copy="Создать копию"
; Macro Logic
AdvSceneSwitcher.logic.none="Игнорировать вход"
AdvSceneSwitcher.logic.and="И"
AdvSceneSwitcher.logic.or="Или"
AdvSceneSwitcher.logic.andNot="И не"
AdvSceneSwitcher.logic.orNot="Или нет"
AdvSceneSwitcher.logic.rootNone="Если"
AdvSceneSwitcher.logic.not="Если нет"
; Macro Conditions
AdvSceneSwitcher.condition.audio="Аудио"
AdvSceneSwitcher.ondition.audio.state.below="Ниже"
AdvSceneSwitcher.ondition.audio.state.above="Выше"
AdvSceneSwitcher.condition.audio.entry="Громкость {{audioSources}} равна {{condition}} {{volume}} в течении {{duration}} секунд"
AdvSceneSwitcher.condition.region="Область экрана"
AdvSceneSwitcher.condition.region.entry="Курсор находится в {{minX}} {{minY}} x {{maxX}} {{maxY}}"
AdvSceneSwitcher.condition.scene="Сцена"
AdvSceneSwitcher.condition.scene.type.current="Текущий"
AdvSceneSwitcher.condition.scene.type.previous="Предыдущий"
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} сцена является {{scenes}} на {{duration}}"
AdvSceneSwitcher.condition.window="Окно"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} существует и ..."
AdvSceneSwitcher.condition.window.entry.line2="... это {{fullscreen}} полноэкранный {{maximized}} максимизированный {{focused}} сфокусированный"
AdvSceneSwitcher.condition.file="Файл"
AdvSceneSwitcher.condition.file.entry.line1="Содержимое {{fileType}} {{filePath}} {{browseButton}} соответствует:"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="Медиа"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}} состояние {{states}} и {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Видео"
AdvSceneSwitcher.condition.video.condition.match="точно соответствует"
AdvSceneSwitcher.condition.video.condition.differ="не совпадает"
AdvSceneSwitcher.condition.video.condition.hasChanged="изменилось"
AdvSceneSwitcher.condition.video.condition.hasNotChanged="не изменилось"
AdvSceneSwitcher.condition.video.condition.noImage="не имеет вывода"
AdvSceneSwitcher.condition.video.askFileAction="Вы хотите использовать существующий файл или создать скриншот текущего выбранного источника?"
AdvSceneSwitcher.condition.video.askFileAction.file="Использовать существующий файл"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Создать скриншот"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}} для {{duration}}"
AdvSceneSwitcher.condition.stream="Потоковое вещание"
AdvSceneSwitcher.condition.stream.state.start="Поток запущен"
AdvSceneSwitcher.condition.stream.state.stop="Поток остановлен"
AdvSceneSwitcher.condition.stream.entry="{{streamState}} для {{duration}}"
AdvSceneSwitcher.condition.record="Запись"
AdvSceneSwitcher.condition.record.state.start="Запись запущена"
AdvSceneSwitcher.condition.record.state.pause="Запись приостановлена"
AdvSceneSwitcher.condition.record.state.stop="Запись остановлена"
AdvSceneSwitcher.condition.record.entry="{{recordState}} для {{duration}}"
AdvSceneSwitcher.condition.process="Процесс"
AdvSceneSwitcher.condition.process.entry="{{processes}} запущен {{focused}} и сфокусирован"
AdvSceneSwitcher.condition.idle="Простой"
AdvSceneSwitcher.condition.idle.entry="Не было ни клавиатуры, ни мыши в течении{{duration}}"
AdvSceneSwitcher.condition.pluginState="Состояние плагина"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Автоматическая смена сцены была запущена в этом интервале"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Переключить сцену"
AdvSceneSwitcher.action.scene.entry="Перейти к сцене {{scenes}} используя {{transitions}} с продолжительностью {{duration}} секунд"
AdvSceneSwitcher.action.wait="Подождать"
AdvSceneSwitcher.action.wait.type.fixed="фиксированный"
AdvSceneSwitcher.action.wait.type.random="случайный"
AdvSceneSwitcher.action.wait.entry.fixed="Ожидать {{waitType}} в течении {{duration}}"
AdvSceneSwitcher.action.wait.entry.random="Ожидание {{waitType}} длительностью от {{duration}} до {{duration2}}"
AdvSceneSwitcher.action.audio="Аудио"
AdvSceneSwitcher.action.audio.type.mute="Замьютить"
AdvSceneSwitcher.action.audio.type.unmute="Размьютить"
AdvSceneSwitcher.action.audio.type.sourceVolume="Установить громкость источника"
AdvSceneSwitcher.action.audio.type.masterVolume="Установить главную громкость"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.recording="Запись"
AdvSceneSwitcher.action.recording.type.stop="Остановить запись"
AdvSceneSwitcher.action.recording.type.start="Начать запись"
AdvSceneSwitcher.action.recording.type.pause="Пауза записи"
AdvSceneSwitcher.action.recording.type.unpause="Снять запись с паузы"
AdvSceneSwitcher.action.recording.pause.hint="Обратите внимание, что в зависимости от настроек записи вы можете не иметь возможности приостановить запись"
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
AdvSceneSwitcher.action.replay="Буфер воспроизведения"
AdvSceneSwitcher.action.replay.type.stop="Остановить буфер воспроизведения"
AdvSceneSwitcher.action.replay.type.start="Начать воспроизведение буфера"
AdvSceneSwitcher.action.replay.type.save="Сохранить буфер воспроизведения"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Потоковое вещание"
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Запустить"
AdvSceneSwitcher.action.run.entry="Запустить {{filePath}} {{browseButton}}"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Переход"
AdvSceneSwitcher.transitionTab.setTransitionBy="При изменении переходов:"
AdvSceneSwitcher.transitionTab.transitionOverride="Установить переопределение переходов"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Изменить тип активного перехода"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
AdvSceneSwitcher.transitionTab.transitionForAToB="Использовать переход для автоматического переключения сцены со сцены A на сцену B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Эти настройки <span style=\"font-style:italic;\">только</span> влияют на переходы, вызванные переключателем сцен - Проверьте <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> если вы хотите настроить его для ручного изменения сцены.<br/>Настройки, определенные здесь, имеют приоритет над настройками перехода, сконфигурированными в других местах переключателя сцен.<br/><br/>Нажмите на символ плюса ниже, чтобы добавить новую запись.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Изменить переход, если сцена активна"
AdvSceneSwitcher.transitionTab.entry="Переход от {{scenes}} к {{scenes2}} с помощью {{transitions}} с длительностью {{duration}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Когда сцена {{scenes}} активна, изменить переход сцены по умолчанию на {{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Нажмите на символ плюса, чтобы добавить запись."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Переключить переход {{defTransitionDelay}} после смены сцены."
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="Задержка используется для того, чтобы избежать отмены переключения сцены, что может произойти, если тип перехода будет изменен, когда переход еще продолжается."
; Pause Scenes Tab
AdvSceneSwitcher.pauseTab.title="Пауза"
AdvSceneSwitcher.pauseTab.pauseOnScene="Приостановить Scene Switcher на сцене"
AdvSceneSwitcher.pauseTab.pauseInFocus1="Приостановить Scene Switcher когда "
AdvSceneSwitcher.pauseTab.pauseInFocus2="находится в фокусе"
AdvSceneSwitcher.pauseTab.pauseTypeScene="сцена активна"
AdvSceneSwitcher.pauseTab.pauseTypeWindow="окно в фокусе"
AdvSceneSwitcher.pauseTab.pauseTargetAll="все"
AdvSceneSwitcher.pauseTab.pauseEntry="Пауза {{pauseTargets}} проверяет, когда {{pauseTypes}} {{scenes}} {{windows}}"
AdvSceneSwitcher.pauseTab.help="На этой вкладке вы можете настроить приостановку отдельных методов переключения, если сцена активна или окно находится в фокусе.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Window Title Tab
AdvSceneSwitcher.windowTitleTab.title="Название"
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Введите либо прямые заголовки окон, либо правильное регулярное выражение. Вы можете проверить синтаксис и соответствие регулярных выражений, используя <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a></p></body></html>"
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Игнорировать имя этого окна"
AdvSceneSwitcher.windowTitleTab.stayInFocus2=" "
AdvSceneSwitcher.windowTitleTab.fullscreen="if fullscreen"
AdvSceneSwitcher.windowTitleTab.maximized="if maximized"
AdvSceneSwitcher.windowTitleTab.focused="if focused"
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
AdvSceneSwitcher.windowTitleTab.windowsHelp="Переключение сцен на основе заголовков окон запущенных приложений.\nВы можете выбрать следующие дополнительные условия:\nОкно полноэкранное\nОкно максимизированное\nОкно сфокусированное\n\nНажмите на выделенный символ плюса, чтобы продолжить."
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="Если заголовок окна игнорируется, переключатель сцен будет действовать так, как будто ранее выбранное окно все еще в фокусе.\nЭто позволит вам избежать переключения сцен, если вы часто переключаетесь на другое окно, которое не должно вызывать смену сцены.\n\nВыберите окно или введите заголовок окна выше и нажмите на символ плюса ниже, чтобы добавить его в список."
; Executable Tab
AdvSceneSwitcher.executableTab.title="Исполняемый"
AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven"
AdvSceneSwitcher.executableTab.requiresFocus="только если сфокусирован"
AdvSceneSwitcher.executableTab.entry="Когда {{processes}} запущены, переключить на {{scenes}} используя {{transitions}} {{requiresFocus}}"
AdvSceneSwitcher.executableTab.help="Эта вкладка позволит вам автоматически переключаться между сценами, если запущен процесс.\nЭто может быть полезно в ситуациях, когда имя окна может измениться или неизвестно.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Screen Region Tab
AdvSceneSwitcher.screenRegionTab.title="Регион"
AdvSceneSwitcher.screenRegionTab.currentPosition="Курсор в данный момент находится в:"
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Показать направляющие кадры"
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Скрыть направляющие рамки"
AdvSceneSwitcher.screenRegionTab.excludeScenes.None="Нет выбора"
AdvSceneSwitcher.screenRegionTab.entry="Если курсор находится в {{minX}} {{minY}} x {{maxX}} {{maxY}} переключить на {{scenes}} используя {{transitions}} если не указано {{excludeScenes}}"
AdvSceneSwitcher.screenRegionTab.help="Эта вкладка позволит вам автоматически переключать сцены, основываясь на текущем положении курсора мыши.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Media Tab
AdvSceneSwitcher.mediaTab.title="Медиа"
AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro"
AdvSceneSwitcher.mediaTab.states.none="Нет"
AdvSceneSwitcher.mediaTab.states.playing="Играет"
AdvSceneSwitcher.mediaTab.states.opening="Открытие"
AdvSceneSwitcher.mediaTab.states.buffering="Буферизация"
AdvSceneSwitcher.mediaTab.states.Paused="Приостановлено"
AdvSceneSwitcher.mediaTab.states.stopped="Остановлено"
AdvSceneSwitcher.mediaTab.states.ended="Закончилось"
AdvSceneSwitcher.mediaTab.states.error="Ошибка"
AdvSceneSwitcher.mediaTab.states.playedToEnd="Воспроизведено до конца"
AdvSceneSwitcher.mediaTab.states.any="Любой"
AdvSceneSwitcher.mediaTab.timeRestriction.none="Нет"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Время короче"
AdvSceneSwitcher.mediaTab.timeRestriction.longer="Время больше"
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="Оставшееся время короче"
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Время, оставшееся дольше"
AdvSceneSwitcher.mediaTab.entry="Когда состояние {{mediaSources}} равно {{states}} и {{timeRestrictions}} {{time}} переключиться на {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.mediaTab.help="Эта вкладка позволит вам переключать сцены на основе состояний медиаисточников.\nНапример, вы можете автоматически переключиться на предыдущую сцену, когда выбранный медиаисточник закончил свое воспроизведение.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; File Tab
AdvSceneSwitcher.fileTab.title="Файл"
AdvSceneSwitcher.fileTab.readWriteSceneFile="Считать / записать сцену из / в файл"
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Записать имя текущей сцены в этот файл:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFile="Включить переключение сцен на основе ввода файла"
AdvSceneSwitcher.fileTab.switchSceneNameInputFile="Считать имя сцены, на которую нужно переключиться, из этого файла:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFileContent="Переключать сцену на основе содержимого файла"
AdvSceneSwitcher.fileTab.remoteFileWarning="Обратите внимание, что при выборе опции remote переключатель сцен будет пытаться получить доступ к удаленному местоположению каждые x мс, как указано на вкладке Общие"
AdvSceneSwitcher.fileTab.remoteFileWarning1="Обратите внимание, что переключатель сцены будет пытаться получить доступ к удаленному местоположению каждые "
AdvSceneSwitcher.fileTab.remoteFileWarning2="ms"
AdvSceneSwitcher.fileTab.libcurlWarning="Не удалось загрузить libcurl! Доступ к удаленным файлам невозможен!"
AdvSceneSwitcher.fileTab.selectWrite="Выберите файл для записи в ..."
AdvSceneSwitcher.fileTab.selectRead="Выберите файл для чтения из ..."
AdvSceneSwitcher.fileTab.textFileType="Текстовые файлы (*.txt)"
AdvSceneSwitcher.fileTab.anyFileType="Любые файлы (*.*)"
AdvSceneSwitcher.fileTab.remote="удаленный файл"
AdvSceneSwitcher.fileTab.local="локальный файл"
AdvSceneSwitcher.fileTab.useRegExp="использовать регулярные выражения (сопоставление шаблонов)"
AdvSceneSwitcher.fileTab.checkfileContentTime="если дата модификации изменена"
AdvSceneSwitcher.fileTab.checkfileContent="если содержимое изменено"
AdvSceneSwitcher.fileTab.entry="Переключиться на {{scenes}} используя {{transitions}} если содержимое {{fileType}} {{filePath}} {{browseButton}} совпадает:"
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.fileTab.help="Эта вкладка позволит вам автоматически переключать сцены на основе содержимого удаленных или локальных файлов.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Random Tab
AdvSceneSwitcher.randomTab.title="Рандом"
AdvSceneSwitcher.randomTab.randomDisabledWarning="Функциональность отключена - для активации выберите \"Если условие переключения не выполнено, переключиться на любую сцену на вкладке Random\" на вкладке Общие"
AdvSceneSwitcher.randomTab.entry="Если условие переключения не выполнено, переключиться на {{scenes}} используя {{transitions}} для {{delay}}"
AdvSceneSwitcher.randomTab.help="Переключатель сцен будет случайным образом выбирать запись на этой вкладке для переключения на заданное время.\nЗаметьте, что одна и та же запись не будет выбрана дважды подряд.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Time Tab
AdvSceneSwitcher.timeTab.title="Время"
AdvSceneSwitcher.timeTab.anyDay="В любой день"
AdvSceneSwitcher.timeTab.mondays="По понедельникам"
AdvSceneSwitcher.timeTab.tuesdays="По вторникам"
AdvSceneSwitcher.timeTab.wednesdays="По средам"
AdvSceneSwitcher.timeTab.thursdays="По четвергам"
AdvSceneSwitcher.timeTab.fridays="По пятницам"
AdvSceneSwitcher.timeTab.saturdays="По субботам"
AdvSceneSwitcher.timeTab.sundays="По воскресеньям"
AdvSceneSwitcher.timeTab.afterstart="После начала потокового вещания/записи"
AdvSceneSwitcher.timeTab.afterstart.tip="Будет использоваться время относительно начала потокового вещания/записи"
AdvSceneSwitcher.timeTab.entry="{{triggers}} в {{time}} переключаются на {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.timeTab.help="Эта вкладка позволит вам автоматически переключаться на другую сцену на основе текущего местного времени.\n\nЗаметьте, что переключатель сцен будет переключаться только в точное время, которое вы указали.\nУбедитесь, что вы настроили параметры приоритета на вкладке Общие по своему вкусу, чтобы выбранная временная точка не была пропущена из-за других методов переключения, имеющих более высокий приоритет.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Idle Tab
AdvSceneSwitcher.idleTab.title="Бездействие"
AdvSceneSwitcher.idleTab.enable="Включить обнаружение простоя"
AdvSceneSwitcher.idleTab.idleswitch="После {{duration}} отсутствия ввода с клавиатуры или мыши переключиться на сцену {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Не переключаться, если"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="находится в фокусе"
; Scene Sequence Tab
AdvSceneSwitcher.sceneSequenceTab.title="Последовательность"
AdvSceneSwitcher.sceneSequenceTab.description="Последовательность автоматического переключения сцен может быть отменена либо паузой/остановкой переключателя сцен, либо ручным переключением на другую сцену"
AdvSceneSwitcher.sceneSequenceTab.save="Сохранить последовательность сцен в файл"
AdvSceneSwitcher.sceneSequenceTab.load="Загрузить последовательности сцен из файла"
AdvSceneSwitcher.sceneSequenceTab.saveTitle="Сохранить последовательность сцен в файл ..."
AdvSceneSwitcher.sceneSequenceTab.loadTitle="Выберите файл для чтения последовательности сцен из файла ..."
AdvSceneSwitcher.sceneSequenceTab.loadFail="Advanced Scene Switcher не удалось импортировать настройки!"
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Настройки Advanced Scene Switcher импортированы успешно!"
AdvSceneSwitcher.sceneSequenceTab.fileType="Текстовые файлы (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="прервать"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Другие методы переключения разрешены для прерывания этой последовательности сцен"
AdvSceneSwitcher.sceneSequenceTab.entry="Когда {{startScenes}} активен, переключить на {{scenes}} после {{delay}} используя {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Расширить последовательность"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="После {{delay}} переключиться на {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.help="Эта вкладка позволит вам автоматически переключаться на другую сцену, если сцена была активна в течение заданного периода времени.\nНапример, вы можете автоматически переключаться между двумя сценами.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Audio Tab
AdvSceneSwitcher.audioTab.title="Аудио"
AdvSceneSwitcher.audioTab.condition.above="выше"
AdvSceneSwitcher.audioTab.condition.below="ниже"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="если источник неактивен"
AdvSceneSwitcher.audioTab.entry="Когда громкость {{audioSources}} равна {{condition}} {{volumeWidget}} в течение {{duration}} секунд переключить на {{scenes}} используя {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Если несколько записей совпадают ..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... в течение {{duration}} секунд переключить на {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.audioTab.help="Эта вкладка позволит вам переключать сцены в зависимости от громкости источников.\nНапример, вы можете автоматически переключиться на другую сцену, если громкость вашего микрофона достигнет определенного порога.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Video Tab
AdvSceneSwitcher.videoTab.title="Видео"
AdvSceneSwitcher.videoTab.getScreenshot="Получить снимок экрана для выбранной записи"
AdvSceneSwitcher.videoTab.getScreenshotHelp="Получить скриншот источника видео текущей выбранной записи и автоматически установить его в качестве целевого изображени"
AdvSceneSwitcher.videoTab.condition.match="точно соответствует"
AdvSceneSwitcher.videoTab.condition.match.tooltip="Точное совпадение требует, чтобы целевое и исходное изображение имели одинаковое разрешение.\nДаже каждый пиксель должен совпадать, поэтому не рекомендуется использовать форматы изображений, которые используют сжатие (например, .JPG)!"
AdvSceneSwitcher.videoTab.condition.differ="не совпадает"
AdvSceneSwitcher.videoTab.condition.hasNotChanged="не изменилось"
AdvSceneSwitcher.videoTab.condition.hasChanged="изменилось"
AdvSceneSwitcher.videoTab.ignoreInactiveSource="если источник неактивен"
AdvSceneSwitcher.videoTab.entry="Когда {{videoSources}} {{condition}} {{filePath}} {{browseButton}} для {{duration}} переключиться на {{scenes}} используя {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Эта вкладка позволит вам переключать сцены на основе текущего видеовыхода выбранных источников.<br/>Обязательно проверьте <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> для еще лучшей реализации этой функциональности.<br/><br/> Нажмите на выделенный символ плюса, чтобы продолжить.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Сеть"
AdvSceneSwitcher.networkTab.warning="Запуск сервера вне локальной сети позволит третьим лицам читать активную сцену."
AdvSceneSwitcher.networkTab.DisabledWarning="ту функциональность, к сожалению, пришлось отключить на macOS из-за несовместимости библиотек при параллельном запуске плагина obs-websocket."
AdvSceneSwitcher.networkTab.server="Запустить сервер (отправляет сообщения о переключении сцены всем подключенным клиентам)"
AdvSceneSwitcher.networkTab.server.port="Порт"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Заблокировать сервер на использование только IPv4"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Отправлять сообщения только для автоматических переключателей сцен"
AdvSceneSwitcher.networkTab.startFailed.message="Сервер WebSockets не удалось запустить, возможно потому, что:\n - TCP порт %1 может в настоящее время использоваться в другом месте в этой системе, возможно, другим приложением. Попробуйте установить другой TCP порт в настройках сервера WebSocket, или остановите любое приложение, которое может использовать этот порт.\n - Сообщение об ошибке: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Текущий статус"
AdvSceneSwitcher.networkTab.server.status.notRunning="Не запущен"
AdvSceneSwitcher.networkTab.server.status.starting="Запуск"
AdvSceneSwitcher.networkTab.server.status.running="Работает"
AdvSceneSwitcher.networkTab.server.restart="Перезапустить сервер"
AdvSceneSwitcher.networkTab.client="Запустить клиент (Получает сообщения о переключении сцен)"
AdvSceneSwitcher.networkTab.client.address="Имя хоста или IP-адрес"
AdvSceneSwitcher.networkTab.client.port="Порт"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Текущий статус"
AdvSceneSwitcher.networkTab.client.status.disconnected="Отключено"
AdvSceneSwitcher.networkTab.client.status.connecting="Подключение"
AdvSceneSwitcher.networkTab.client.status.connected="Подключено"
AdvSceneSwitcher.networkTab.client.reconnect="Принудительное переподключение"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Группа сцен"
AdvSceneSwitcher.sceneGroupTab.list="Группы сцен"
AdvSceneSwitcher.sceneGroupTab.edit="Редактировать группы сцен"
AdvSceneSwitcher.sceneGroupTab.edit.name="Имя:"
AdvSceneSwitcher.sceneGroupTab.edit.type="Тип: {{type}}"
AdvSceneSwitcher.sceneGroupTab.type.count="Количество"
AdvSceneSwitcher.sceneGroupTab.type.time="Время"
AdvSceneSwitcher.sceneGroupTab.type.random="Случайно"
AdvSceneSwitcher.sceneGroupTab.edit.count="Переход к следующей сцене в списке после {{count}} совпадений"
AdvSceneSwitcher.sceneGroupTab.edit.time="Переход к следующей сцене в списке по истечении {{time}}"
AdvSceneSwitcher.sceneGroupTab.edit.random="Выбирать следующую сцену в списке случайным образом"
AdvSceneSwitcher.sceneGroupTab.edit.repeat="Начинать с начала, если достигнут конец списка сцен"
AdvSceneSwitcher.sceneGroupTab.edit.addScene="Добавить сцену"
AdvSceneSwitcher.sceneGroupTab.add="Добавить группу сцен"
AdvSceneSwitcher.sceneGroupTab.defaultname="Группа сцен %1"
AdvSceneSwitcher.sceneGroupTab.exists="Группа сцен или название сцены уже существует"
AdvSceneSwitcher.sceneGroupTab.help="Группы сцен могут быть выбраны в качестве цели так же, как и обычные сцены.\n\nКак следует из названия, группа сцен представляет собой набор из нескольких сцен.\nГруппа сцен будет продвигаться по списку назначенных ей сцен в зависимости от настроенных параметров, которые можно найти справа.\n\nВы можете настроить группу сцен на переход к следующей сцене в списке:\nПосле определенного количества раз, когда группа сцен выбрана в качестве цели.\nПо истечении определенного времени.\nИли случайным образом.\n\nНапример, группа сцен, содержащая сцены ...\nScene 1\nScene 2\nScene 3 \n... активирует \"Scene 1\" в первый раз, когда он выбран в качестве цели.\nВо второй раз он активируется \"Scene 2\".\nОставшееся время \"Scene 3\" будет активирован.\n\nНажмите на выделенный символ плюса ниже, чтобы добавить новую группу сцен."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Выберите группу сцен, которую вы хотите изменить слева.\n\nВыберите сцену для добавления в эту группу сцен, выбрав сцену выше и нажав на символ плюса ниже.\n\nСцена может быть добавлена несколько раз в одну и ту же группу сцен."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Триггеры сцены"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--выбрать триггер--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="активен"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="не активен"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="переключился с"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--выбрать действие--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="начать запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="приостановить запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="отменить паузу записи"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="остановить запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="остановить потоковое вещание"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="начать потоковое вещание"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="запустить буфер воспроизведения"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="остановить буфер воспроизведения"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="отключить источник"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="включить источник"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="запустить переключатель сцены"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="остановить переключатель сцены"
AdvSceneSwitcher.sceneTriggerTab.entry="Когда {{scenes}} {{triggers}} {{actions}} {{audioSources}} после {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Эта вкладка позволяет запускать действия при изменении сцены, например, остановку записи или потоковой передачи."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Запустить Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="становить Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Переключить старт/стоп для Advanced Scene Switcher"
AdvSceneSwitcher.askBackup="Обнаружена новая версия Advanced Scene Switcher.\nНужно ли создать резервную копию старых настроек?"
AdvSceneSwitcher.close="Закрыть"
AdvSceneSwitcher.browse="Обзор"
AdvSceneSwitcher.selectScene="--выбрать сцену--"
AdvSceneSwitcher.selectPreviousScene="Предыдущая сцена"
AdvSceneSwitcher.currentTransition="Текущий переход"
AdvSceneSwitcher.selectTransition="--выбрать переход--"
AdvSceneSwitcher.selectWindow="--выбрать окно--"
AdvSceneSwitcher.selectAudioSource="--выбрать источник звука--"
AdvSceneSwitcher.selectVideoSource="--sвыбрать источник видео--"
AdvSceneSwitcher.selectMediaSource="--выбрать источник мультимедиа--"
AdvSceneSwitcher.selectProcess="--выбрать процесс--"
AdvSceneSwitcher.enterPath="--ввести путь--"
AdvSceneSwitcher.enterText="--ввести текст--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="недействительные записи не будут сохранены"
AdvSceneSwitcher.selectWindowTip="Используйте \"OBS\" для указания окна OBS\nИспользуйте \"Переключение задач\"для указания ALT + TAB"
AdvSceneSwitcher.status.active="Активный"
AdvSceneSwitcher.status.inactive="Неактивен"
AdvSceneSwitcher.unit.milliseconds="миллисекунды"
AdvSceneSwitcher.unit.secends="секунды"
AdvSceneSwitcher.unit.minutes="минуты"
AdvSceneSwitcher.unit.hours="часы"

3
data/res/time.svg Normal file
View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M599.62 325.49C599.62 477.26 476.41 600.47 324.64 600.47C172.88 600.47 49.67 477.26 49.67 325.49C49.67 173.73 172.88 50.52 324.64 50.52C476.41 50.52 599.62 173.73 599.62 325.49Z" id="c3Wyq0kfYS"></path><mask id="maskanTyyngos" x="-20.33" y="-19.48" width="689.95" height="689.95" maskUnits="userSpaceOnUse"><rect x="-20.33" y="-19.48" width="689.95" height="689.95" fill="white"></rect><use xlink:href="#c3Wyq0kfYS" opacity="1" fill="black"></use></mask><path d="M334.97 96.07C339.51 96.07 343.2 99.75 343.2 104.3C343.2 151.24 343.2 283.84 343.2 330.78C343.2 335.32 339.51 339 334.97 339C329.2 339 320.09 339 314.31 339C309.77 339 306.09 335.32 306.09 330.78C306.09 283.84 306.09 151.24 306.09 104.3C306.09 99.75 309.77 96.07 314.31 96.07C320.09 96.07 329.2 96.07 334.97 96.07Z" id="fxMeu932c"></path><path d="M471.96 338.21C471.97 341.31 469.46 343.83 466.37 343.84C434.35 343.93 343.92 344.21 311.91 344.31C308.81 344.32 306.3 341.82 306.29 338.72C306.27 332.42 306.23 319.13 306.21 312.83C306.2 309.73 308.7 307.21 311.8 307.2C343.81 307.11 434.24 306.83 466.25 306.73C469.35 306.72 471.87 309.23 471.88 312.32C471.9 318.62 471.94 331.91 471.96 338.21Z" id="h65nGmP2BL"></path></defs><g><g><g><g mask="url(#maskanTyyngos)"><use xlink:href="#c3Wyq0kfYS" opacity="1" fill-opacity="0" stroke="#d2d2d2" stroke-width="70" stroke-opacity="1"></use></g></g><g><use xlink:href="#fxMeu932c" opacity="1" fill="#d2d2d2" fill-opacity="1"></use></g><g><use xlink:href="#h65nGmP2BL" opacity="1" fill="#d2d2d2" fill-opacity="1"></use></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -723,7 +723,7 @@
<property name="title">
<string>AdvSceneSwitcher.macroTab.edit</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_34" stretch="1,20,10">
<layout class="QVBoxLayout" name="verticalLayout_34">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0">
<item>
@ -752,184 +752,204 @@
</layout>
</item>
<item>
<widget class="QScrollArea" name="macroConditions">
<property name="widgetResizable">
<bool>true</bool>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>293</height>
</rect>
<widget class="QScrollArea" name="macroConditions">
<property name="widgetResizable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_35" stretch="10,1">
<item>
<layout class="QGridLayout" name="gridLayout_27">
<item row="0" column="0">
<widget class="QLabel" name="macroEditConditionHelp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.editConditionHelp</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="macroEditConditionLayout"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QPushButton" name="conditionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="conditionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>220</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_35" stretch="10,1">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_27">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="macroEditConditionHelp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.editConditionHelp</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="macroEditConditionLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QPushButton" name="conditionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="conditionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
<item>
<widget class="QScrollArea" name="macroActions">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>145</height>
</rect>
<widget class="QScrollArea" name="macroActions">
<property name="widgetResizable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_33" stretch="10,1">
<item>
<layout class="QGridLayout" name="gridLayout_25">
<item row="0" column="0">
<widget class="QLabel" name="macroEditActionHelp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.editActionHelp</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="macroEditActionLayout"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<widget class="QPushButton" name="actionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
<widget class="QWidget" name="scrollAreaWidgetContents_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>219</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_33" stretch="10,1">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_25">
<item row="0" column="0">
<widget class="QLabel" name="macroEditActionHelp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.editActionHelp</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="macroEditActionLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<widget class="QPushButton" name="actionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
@ -3724,6 +3744,27 @@
<layout class="QVBoxLayout" name="verticalLayout_31">
<item>
<layout class="QGridLayout" name="gridLayout_24">
<item row="3" column="1">
<widget class="QCheckBox" name="restrictSend">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.status.currentStatus</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="serverStatus">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.status.notRunning</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="serverPort">
<property name="minimum">
@ -3751,24 +3792,17 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_19">
<item row="4" column="1">
<widget class="QCheckBox" name="sendPreview">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.status.currentStatus</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="serverStatus">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.status.notRunning</string>
<string>AdvSceneSwitcher.networkTab.server.sendPreview</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="restrictSend">
<widget class="QCheckBox" name="sendSceneChange">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches</string>
<string>AdvSceneSwitcher.networkTab.server.sendSceneChange</string>
</property>
</widget>
</item>

View File

@ -1,13 +1,9 @@
#include <QMainWindow>
#include <QAction>
#include <QtGui/qstandarditemmodel.h>
#include <QPropertyAnimation>
#include <QGraphicsColorizeEffect>
#include <QMessageBox>
#include <QFileDialog>
#include <obs-module.h>
#include <obs-frontend-api.h>
#include <util/util.hpp>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/curl-helper.hpp"
@ -73,331 +69,36 @@ void AdvSceneSwitcher::loadUI()
}
/******************************************************************************
* UI helpers
* Saving and loading
******************************************************************************/
void AdvSceneSwitcher::addSelectionEntry(QComboBox *sel,
const char *description,
bool selectable, const char *tooltip)
void AskBackup(obs_data_t *obj)
{
sel->addItem(description);
bool backupSettings = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true);
if (strcmp(tooltip, "") != 0) {
sel->setItemData(0, tooltip, Qt::ToolTipRole);
}
QStandardItemModel *model =
qobject_cast<QStandardItemModel *>(sel->model());
QModelIndex firstIndex =
model->index(0, sel->modelColumn(), sel->rootModelIndex());
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
if (!selectable) {
firstItem->setSelectable(false);
firstItem->setEnabled(false);
}
}
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
bool addSceneGroup,
bool addSelect,
std::string selectText,
bool selectable)
{
if (addSelect) {
if (selectText.empty()) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectScene"),
selectable,
obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
} else {
addSelectionEntry(sel, selectText.c_str(), selectable);
}
}
BPtr<char *> scenes = obs_frontend_get_scene_names();
char **temp = scenes;
while (*temp) {
const char *name = *temp;
sel->addItem(name);
temp++;
}
if (addPrevious) {
sel->addItem(obs_module_text(
"AdvSceneSwitcher.selectPreviousScene"));
}
if (addSceneGroup) {
for (auto &sg : switcher->sceneGroups) {
sel->addItem(QString::fromStdString(sg.name));
}
}
}
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
bool addCurrent,
bool addSelect,
bool selectable)
{
if (addSelect) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectTransition"),
selectable);
}
if (addCurrent) {
sel->addItem(
obs_module_text("AdvSceneSwitcher.currentTransition"));
}
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
for (size_t i = 0; i < transitions->sources.num; i++) {
const char *name =
obs_source_get_name(transitions->sources.array[i]);
sel->addItem(name);
}
obs_frontend_source_list_free(transitions);
}
void AdvSceneSwitcher::populateWindowSelection(QComboBox *sel, bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel, obs_module_text("AdvSceneSwitcher.selectWindow"));
}
std::vector<std::string> windows;
GetWindowList(windows);
sort(windows.begin(), windows.end());
for (std::string &window : windows) {
sel->addItem(window.c_str());
}
#ifdef WIN32
sel->setItemData(0, obs_module_text("AdvSceneSwitcher.selectWindowTip"),
Qt::ToolTipRole);
#endif
}
void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectAudioSource"),
false,
obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
}
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
std::vector<std::string> *list =
reinterpret_cast<std::vector<std::string> *>(data);
uint32_t flags = obs_source_get_output_flags(source);
if ((flags & OBS_SOURCE_AUDIO) != 0) {
list->push_back(obs_source_get_name(source));
}
return true;
};
std::vector<std::string> audioSources;
obs_enum_sources(sourceEnum, &audioSources);
sort(audioSources.begin(), audioSources.end());
for (std::string &source : audioSources) {
sel->addItem(source.c_str());
}
}
void AdvSceneSwitcher::populateVideoSelection(QComboBox *sel, bool addScenes,
bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectVideoSource"),
false,
obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
}
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
std::vector<std::string> *list =
reinterpret_cast<std::vector<std::string> *>(data);
uint32_t flags = obs_source_get_output_flags(source);
std::string test = obs_source_get_name(source);
if ((flags & (OBS_SOURCE_VIDEO | OBS_SOURCE_ASYNC)) != 0) {
list->push_back(obs_source_get_name(source));
}
return true;
};
std::vector<std::string> videoSources;
obs_enum_sources(sourceEnum, &videoSources);
sort(videoSources.begin(), videoSources.end());
for (std::string &source : videoSources) {
sel->addItem(source.c_str());
}
if (addScenes) {
populateSceneSelection(sel, false, false, false);
}
}
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectMediaSource"),
false,
obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
}
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
std::vector<std::string> *list =
reinterpret_cast<std::vector<std::string> *>(data);
std::string sourceId = obs_source_get_id(source);
if (sourceId.compare("ffmpeg_source") == 0 ||
sourceId.compare("vlc_source") == 0) {
list->push_back(obs_source_get_name(source));
}
return true;
};
std::vector<std::string> mediaSources;
obs_enum_sources(sourceEnum, &mediaSources);
sort(mediaSources.begin(), mediaSources.end());
for (std::string &source : mediaSources) {
sel->addItem(source.c_str());
}
}
void AdvSceneSwitcher::populateProcessSelection(QComboBox *sel, bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel, obs_module_text("AdvSceneSwitcher.selectProcess"));
}
QStringList processes;
GetProcessList(processes);
processes.sort();
for (QString &process : processes)
sel->addItem(process);
}
void AdvSceneSwitcher::listAddClicked(QListWidget *list,
SwitchWidget *newWidget,
QPushButton *addButton,
QMetaObject::Connection *addHighlight)
{
if (!list || !newWidget) {
blog(LOG_WARNING,
"listAddClicked called without valid list or widget");
if (!backupSettings) {
return;
}
if (addButton && addHighlight) {
addButton->disconnect(*addHighlight);
QString directory = QFileDialog::getSaveFileName(
nullptr,
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle"),
QDir::currentPath(),
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
if (directory.isEmpty()) {
return;
}
QListWidgetItem *item;
item = new QListWidgetItem(list);
list->addItem(item);
item->setSizeHint(newWidget->minimumSizeHint());
list->setItemWidget(item, newWidget);
list->scrollToItem(item);
}
bool AdvSceneSwitcher::listMoveUp(QListWidget *list)
{
int index = list->currentRow();
if (index == -1 || index == 0) {
return false;
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
QWidget *row = list->itemWidget(list->currentItem());
QListWidgetItem *itemN = list->currentItem()->clone();
list->insertItem(index - 1, itemN);
list->setItemWidget(itemN, row);
list->takeItem(index + 1);
list->setCurrentRow(index - 1);
return true;
obs_data_save_json(obj, file.fileName().toUtf8().constData());
}
bool AdvSceneSwitcher::listMoveDown(QListWidget *list)
{
int index = list->currentRow();
if (index == -1 || index == list->count() - 1) {
return false;
}
QWidget *row = list->itemWidget(list->currentItem());
QListWidgetItem *itemN = list->currentItem()->clone();
list->insertItem(index + 2, itemN);
list->setItemWidget(itemN, row);
list->takeItem(index);
list->setCurrentRow(index + 1);
return true;
}
QMetaObject::Connection AdvSceneSwitcher::PulseWidget(QWidget *widget,
QColor endColor,
QColor startColor,
QString specifier)
{
if (switcher->disableHints) {
return QMetaObject::Connection();
}
widget->setStyleSheet(specifier + "{ \
border-style: outset; \
border-width: 2px; \
border-radius: 10px; \
border-color: rgb(0,0,0,0) \
}");
QGraphicsColorizeEffect *eEffect = new QGraphicsColorizeEffect(widget);
widget->setGraphicsEffect(eEffect);
QPropertyAnimation *paAnimation =
new QPropertyAnimation(eEffect, "color");
paAnimation->setStartValue(startColor);
paAnimation->setEndValue(endColor);
paAnimation->setDuration(1000);
// Play backwards to return to original state on timer end
paAnimation->setDirection(QAbstractAnimation::Backward);
auto con = QWidget::connect(
paAnimation, &QPropertyAnimation::finished, [paAnimation]() {
QTimer::singleShot(1000, [paAnimation] {
paAnimation->start();
});
});
paAnimation->start();
return con;
}
/******************************************************************************
* Saving and loading
******************************************************************************/
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
{
if (saving) {
@ -422,7 +123,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obj = obs_data_create();
}
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
AdvSceneSwitcher::AskBackup(obj);
AskBackup(obj);
}
switcher->loadSettings(obj);
@ -484,6 +185,7 @@ void SwitcherData::Thread()
}
vblog(LOG_INFO, "try to sleep for %ld", duration.count());
setWaitScene();
cv.wait_for(lock, duration);
startTime = std::chrono::high_resolution_clock::now();
@ -510,6 +212,7 @@ void SwitcherData::Thread()
vblog(LOG_INFO, "sleep for %ld before switching scene",
duration.count());
setWaitScene();
cv.wait_for(lock, duration);
if (stop) {
@ -616,7 +319,7 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
return match;
}
void switchScene(sceneSwitchInfo sceneSwitch)
void switchScene(const sceneSwitchInfo &sceneSwitch)
{
if (!sceneSwitch.scene && switcher->verbose) {
blog(LOG_INFO, "nothing to switch to");
@ -640,7 +343,7 @@ void switchScene(sceneSwitchInfo sceneSwitch)
blog(LOG_INFO, "switched scene");
}
if (switcher->networkConfig.ServerEnabled) {
if (switcher->networkConfig.ShouldSendSceneChange()) {
switcher->server.sendMessage(sceneSwitch);
}
}
@ -648,6 +351,13 @@ void switchScene(sceneSwitchInfo sceneSwitch)
obs_source_release(source);
}
void switchPreviewScene(const OBSWeakSource &ws)
{
auto source = obs_weak_source_get_source(ws);
obs_frontend_set_current_preview_scene(source);
obs_source_release(source);
}
void SwitcherData::Start()
{
if (!(th && th->isRunning())) {
@ -669,6 +379,13 @@ void SwitcherData::Start()
}
}
void ResetMacroCounters()
{
for (auto &m : switcher->macros) {
m.ResetCount();
}
}
void SwitcherData::Stop()
{
if (th && th->isRunning()) {
@ -680,12 +397,19 @@ void SwitcherData::Stop()
th = nullptr;
writeToStatusFile("Advanced Scene Switcher stopped");
ResetMacroCounters();
}
server.stop();
client.disconnect();
}
void SwitcherData::setWaitScene()
{
waitScene = obs_frontend_get_current_scene();
obs_source_release(waitScene);
}
bool SwitcherData::sceneChangedDuringWait()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
@ -713,11 +437,11 @@ extern "C" void FreeSceneSwitcher()
switcher = nullptr;
}
void handleSceneChange(SwitcherData *s)
void handleSceneChange()
{
// Stop waiting if scene was changed
if (s->sceneChangedDuringWait()) {
s->cv.notify_one();
if (switcher->sceneChangedDuringWait()) {
switcher->cv.notify_one();
}
// Set previous scene
@ -725,42 +449,52 @@ void handleSceneChange(SwitcherData *s)
obs_weak_source_t *ws = obs_source_get_weak_source(source);
obs_source_release(source);
obs_weak_source_release(ws);
if (source && s->previousSceneHelper != ws) {
s->previousScene = s->previousSceneHelper;
s->previousSceneHelper = ws;
if (source && switcher->previousSceneHelper != ws) {
switcher->previousScene = switcher->previousSceneHelper;
switcher->previousSceneHelper = ws;
}
s->checkTriggers();
s->checkDefaultSceneTransitions();
switcher->checkTriggers();
switcher->checkDefaultSceneTransitions();
if (switcher->networkConfig.ServerEnabled &&
switcher->networkConfig.SendAll) {
if (switcher->networkConfig.ShouldSendFrontendSceneChange()) {
switcher->server.sendMessage({ws, nullptr, 0});
}
}
void setLiveTime(SwitcherData *s)
void setLiveTime()
{
s->liveTime = QDateTime::currentDateTime();
switcher->liveTime = QDateTime::currentDateTime();
}
void resetLiveTime(SwitcherData *s)
void resetLiveTime()
{
s->liveTime = QDateTime();
switcher->liveTime = QDateTime();
}
void checkAutoStartRecording(SwitcherData *s)
void checkAutoStartRecording()
{
if (s->autoStartEvent == AutoStartEvent::RECORDING ||
s->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
s->Start();
if (switcher->autoStartEvent == AutoStartEvent::RECORDING ||
switcher->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
switcher->Start();
}
void checkAutoStartStreaming(SwitcherData *s)
void checkAutoStartStreaming()
{
if (s->autoStartEvent == AutoStartEvent::STREAMING ||
s->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
s->Start();
if (switcher->autoStartEvent == AutoStartEvent::STREAMING ||
switcher->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
switcher->Start();
}
void handlePeviewSceneChange()
{
if (switcher->networkConfig.ShouldSendPrviewSceneChange()) {
auto source = obs_frontend_get_current_preview_scene();
auto weak = obs_source_get_weak_source(source);
switcher->server.sendMessage({weak, nullptr, 0}, true);
obs_weak_source_release(weak);
obs_source_release(source);
}
}
// Note to future self:
@ -768,24 +502,31 @@ void checkAutoStartStreaming(SwitcherData *s)
// frontend functions such as obs_frontend_set_current_scene()
static void OBSEvent(enum obs_frontend_event event, void *switcher)
{
if (!switcher) {
return;
}
switch (event) {
case OBS_FRONTEND_EVENT_EXIT:
FreeSceneSwitcher();
break;
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange((SwitcherData *)switcher);
handleSceneChange();
break;
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
handlePeviewSceneChange();
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
setLiveTime((SwitcherData *)switcher);
checkAutoStartRecording((SwitcherData *)switcher);
setLiveTime();
checkAutoStartRecording();
break;
case OBS_FRONTEND_EVENT_STREAMING_STARTED:
setLiveTime((SwitcherData *)switcher);
checkAutoStartStreaming((SwitcherData *)switcher);
setLiveTime();
checkAutoStartStreaming();
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
resetLiveTime((SwitcherData *)switcher);
resetLiveTime();
break;
default:
break;

View File

@ -1,4 +1,5 @@
#include "headers/duration-control.hpp"
#include "headers/utility.hpp"
#include "obs-module.h"
#include <sstream>
@ -99,6 +100,8 @@ DurationSelection::DurationSelection(QWidget *parent, bool showUnitSelection)
SLOT(_UnitChanged(int)));
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(11);
layout->addWidget(_duration);
if (showUnitSelection) {
layout->addWidget(_unitSelection);
@ -137,3 +140,131 @@ void DurationSelection::_UnitChanged(int idx)
emit UnitChanged(unit);
}
void DurationConstraint::Save(obs_data_t *obj, const char *condName,
const char *secondsName, const char *unitName)
{
obs_data_set_int(obj, condName, static_cast<int>(_type));
_dur.Save(obj, secondsName, unitName);
}
void DurationConstraint::Load(obs_data_t *obj, const char *condName,
const char *secondsName, const char *unitName)
{
// For backwards compatability check if duration value exist without
// time constraint condition - if so assume DurationCondition::MORE
if (!obs_data_has_user_value(obj, condName) &&
obs_data_has_user_value(obj, secondsName)) {
obs_data_set_int(obj, condName,
static_cast<int>(DurationCondition::MORE));
}
_type = static_cast<DurationCondition>(obs_data_get_int(obj, condName));
_dur.Load(obj, secondsName, unitName);
}
bool DurationConstraint::DurationReached()
{
switch (_type) {
case DurationCondition::NONE:
return true;
break;
case DurationCondition::MORE:
return _dur.DurationReached();
break;
case DurationCondition::EQUAL:
if (_dur.DurationReached() && !_timeReached) {
_timeReached = true;
return true;
}
break;
case DurationCondition::LESS:
return !_dur.DurationReached();
break;
default:
break;
}
return false;
}
void DurationConstraint::Reset()
{
_timeReached = false;
_dur.Reset();
}
static void populateConditions(QComboBox *list)
{
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.none"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.more"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.equal"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.less"));
}
DurationConstraintEdit::DurationConstraintEdit(QWidget *parent)
{
_condition = new QComboBox(parent);
_duration = new DurationSelection(parent);
_toggle = new QPushButton(parent);
_toggle->setMaximumSize(22, 22);
_toggle->setIcon(
QIcon(QString::fromStdString(getDataFilePath("res/time.svg"))));
populateConditions(_condition);
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(_ConditionChanged(int)));
QObject::connect(_duration, &DurationSelection::DurationChanged, this,
&DurationConstraintEdit::DurationChanged);
QObject::connect(_duration, &DurationSelection::UnitChanged, this,
&DurationConstraintEdit::UnitChanged);
QWidget::connect(_toggle, SIGNAL(clicked()), this,
SLOT(ToggleClicked()));
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(11);
layout->addWidget(_toggle);
layout->addWidget(_condition);
layout->addWidget(_duration);
setLayout(layout);
Collapse(true);
}
void DurationConstraintEdit::SetValue(DurationConstraint &value)
{
_duration->SetDuration(value.GetDuration());
_condition->setCurrentIndex(static_cast<int>(value.GetCondition()));
_duration->setVisible(value.GetCondition() != DurationCondition::NONE);
}
void DurationConstraintEdit::SetUnit(DurationUnit u)
{
_duration->SetUnit(u);
}
void DurationConstraintEdit::SetDuration(const Duration &d)
{
_duration->SetDuration(d);
}
void DurationConstraintEdit::_ConditionChanged(int value)
{
auto cond = static_cast<DurationCondition>(value);
Collapse(cond == DurationCondition::NONE);
emit ConditionChanged(cond);
}
void DurationConstraintEdit::ToggleClicked()
{
Collapse(false);
}
void DurationConstraintEdit::Collapse(bool collapse)
{
_toggle->setVisible(collapse);
_duration->setVisible(!collapse);
_condition->setVisible(!collapse);
}

View File

@ -1,9 +1,9 @@
#include <QFileDialog>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include "headers/version.h"
#include <QFileDialog>
QMetaObject::Connection inactivePluse;
void AdvSceneSwitcher::on_close_clicked()
@ -156,8 +156,11 @@ void AdvSceneSwitcher::SetStopped()
obs_module_text("AdvSceneSwitcher.generalTab.status.start"));
ui->pluginRunningText->setText(
obs_module_text("AdvSceneSwitcher.status.inactive"));
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
if (!switcher->disableHints) {
inactivePluse = PulseWidget(ui->pluginRunningText,
QColor(Qt::red), QColor(0, 0, 0, 0),
"QLabel ");
}
currentStatusActive = false;
}
@ -207,34 +210,6 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
switcher->disableHints = state;
}
void AdvSceneSwitcher::AskBackup(obs_data_t *obj)
{
bool backupSettings = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true);
if (!backupSettings) {
return;
}
QString directory = QFileDialog::getSaveFileName(
nullptr,
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle"),
QDir::currentPath(),
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
if (directory.isEmpty()) {
return;
}
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
obs_data_save_json(obj, file.fileName().toUtf8().constData());
}
void AdvSceneSwitcher::on_exportSettings_clicked()
{
QString directory = QFileDialog::getSaveFileName(

View File

@ -5,6 +5,7 @@
#include "ui_advanced-scene-switcher.h"
#endif
#include "switcher-data-structs.hpp"
#include "platform-funcs.hpp"
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
#define vblog(level, msg, ...) \
@ -69,44 +70,10 @@ public:
void setTabOrder();
void restoreWindowGeo();
static void AskBackup(obs_data_t *obj);
static void addSelectionEntry(QComboBox *sel, const char *description,
bool selectable = false,
const char *tooltip = "");
static void populateSceneSelection(QComboBox *sel,
bool addPrevious = false,
bool addSceneGroup = false,
bool addSelect = true,
std::string selectText = "",
bool selectable = false);
static void populateTransitionSelection(QComboBox *sel,
bool addCurrent = true,
bool addSelect = true,
bool selectable = false);
static void populateWindowSelection(QComboBox *sel,
bool addSelect = true);
static void populateAudioSelection(QComboBox *sel,
bool addSelect = true);
static void populateVideoSelection(QComboBox *sel,
bool addScenes = true,
bool addSelect = true);
static void populateMediaSelection(QComboBox *sel,
bool addSelect = true);
static void populateProcessSelection(QComboBox *sel,
bool addSelect = true);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
QColor = QColor(0, 0, 0, 0),
QString specifier = "QLabel ");
void listAddClicked(QListWidget *list, SwitchWidget *newWidget,
QPushButton *addButton = nullptr,
QMetaObject::Connection *addHighlight = nullptr);
bool listMoveUp(QListWidget *list);
bool listMoveDown(QListWidget *list);
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString newName);
void SceneGroupAdded(const QString &name);
void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName);
@ -137,6 +104,7 @@ public slots:
void on_macroDown_clicked();
void on_macroName_editingFinished();
void on_macros_currentRowChanged(int idx);
void on_macros_itemChanged(QListWidgetItem *);
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_actionAdd_clicked();
@ -243,7 +211,9 @@ public slots:
void on_clientSettings_toggled(bool on);
void on_clientHostname_textChanged(const QString &text);
void on_clientPort_valueChanged(int value);
void on_sendSceneChange_stateChanged(int state);
void on_restrictSend_stateChanged(int state);
void on_sendPreview_stateChanged(int state);
void on_clientReconnect_clicked();
void updateClientStatus();
@ -276,42 +246,17 @@ public slots:
private:
};
/******************************************************************************
* Windowtitle helper
******************************************************************************/
void GetWindowList(std::vector<std::string> &windows);
void GetWindowList(QStringList &windows);
void GetCurrentWindowTitle(std::string &title);
bool isFullscreen(std::string &title);
bool isMaximized(std::string &title);
bool GetCurrentVirtualDesktop(long &desktop);
bool GetVirtualDesktopCount(long &ndesktops);
/******************************************************************************
* Screenregion helper
******************************************************************************/
std::pair<int, int> getCursorPos();
/******************************************************************************
* Idle detection helper
******************************************************************************/
int secondsSinceLastInput();
/******************************************************************************
* Executable helper
******************************************************************************/
void GetProcessList(QStringList &processes);
bool isInFocus(const QString &executable);
/******************************************************************************
* Sceneswitch helper
******************************************************************************/
void setNextTransition(sceneSwitchInfo &ssi, obs_source_t *currentSource,
void setNextTransition(const sceneSwitchInfo &ssi, obs_source_t *currentSource,
transitionData &td);
void overwriteTransitionOverride(sceneSwitchInfo ssi, transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, transitionData td);
void switchScene(sceneSwitchInfo ssi);
void overwriteTransitionOverride(const sceneSwitchInfo &ssi,
transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, const transitionData &td);
void switchScene(const sceneSwitchInfo &ssi);
void switchPreviewScene(const OBSWeakSource &ws);
/******************************************************************************
* Main SwitcherData

View File

@ -2,6 +2,7 @@
#include <QWidget>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QPushButton>
#include <chrono>
#include "obs-data.h"
@ -53,3 +54,57 @@ private:
double _unitMultiplier;
};
enum class DurationCondition {
NONE,
MORE,
EQUAL,
LESS,
};
class DurationConstraint {
public:
void Save(obs_data_t *obj, const char *condName = "time_constraint",
const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void Load(obs_data_t *obj, const char *condName = "time_constraint",
const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void SetCondition(DurationCondition cond) { _type = cond; }
void SetDuration(const Duration &dur) { _dur = dur; }
void SetValue(double value) { _dur.seconds = value; }
void SetUnit(DurationUnit u) { _dur.displayUnit = u; }
DurationCondition GetCondition() { return _type; }
Duration GetDuration() { return _dur; }
bool DurationReached();
void Reset();
private:
DurationCondition _type = DurationCondition::NONE;
Duration _dur;
bool _timeReached = false;
};
class DurationConstraintEdit : public QWidget {
Q_OBJECT
public:
DurationConstraintEdit(QWidget *parent = nullptr);
void SetValue(DurationConstraint &value);
void SetUnit(DurationUnit u);
void SetDuration(const Duration &d);
private slots:
void _ConditionChanged(int value);
void ToggleClicked();
signals:
void DurationChanged(double value);
void UnitChanged(DurationUnit u);
void ConditionChanged(DurationCondition value);
private:
void Collapse(bool collapse);
DurationSelection *_duration;
QComboBox *_condition;
QPushButton *_toggle;
};

View File

@ -15,7 +15,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionAudio>();
@ -27,7 +27,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionAudioEdit : public QWidget {
@ -37,6 +37,7 @@ public:
MacroActionAudioEdit(
QWidget *parent,
std::shared_ptr<MacroActionAudio> entryData = nullptr);
void SetWidgetVisibility();
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)

View File

@ -20,14 +20,16 @@ struct MacroActionInfo {
class MacroActionFactory {
public:
MacroActionFactory() = delete;
static bool Register(int id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const int id);
static QWidget *CreateWidget(const int id, QWidget *parent,
static bool Register(const std::string &id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const std::string &id);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroAction> action);
static auto GetActionTypes() { return _methods; }
static std::string GetActionName(const std::string &id);
static std::string GetIdByName(const QString &name);
private:
static std::map<int, MacroActionInfo> _methods;
static std::map<std::string, MacroActionInfo> _methods;
};
class MacroActionEdit : public QWidget {
@ -35,13 +37,13 @@ class MacroActionEdit : public QWidget {
public:
MacroActionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroAction> * = nullptr, int type = 0,
std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = "scene_switch",
bool startCollapsed = false);
void UpdateEntryData(int type);
void Collapse(bool collapsed);
void UpdateEntryData(const std::string &id, bool collapse);
private slots:
void ActionSelectionChanged(int idx);
void ActionSelectionChanged(const QString &text);
protected:
QComboBox *_actionSelection;

View File

@ -0,0 +1,61 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
enum class FilterAction {
ENABLE,
DISABLE,
};
class MacroActionFilter : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionFilter>();
}
OBSWeakSource _source;
OBSWeakSource _filter;
FilterAction _action = FilterAction::ENABLE;
private:
static bool _registered;
static const std::string id;
};
class MacroActionFilterEdit : public QWidget {
Q_OBJECT
public:
MacroActionFilterEdit(
QWidget *parent,
std::shared_ptr<MacroActionFilter> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionFilterEdit(
parent,
std::dynamic_pointer_cast<MacroActionFilter>(action));
}
private slots:
void SourceChanged(const QString &text);
void FilterChanged(const QString &text);
void ActionChanged(int value);
protected:
QComboBox *_sources;
QComboBox *_filters;
QComboBox *_actions;
std::shared_ptr<MacroActionFilter> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@ -0,0 +1,60 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "macro-selection.hpp"
enum class PerformMacroAction {
PAUSE,
UNPAUSE,
RESET_COUNTER,
};
class MacroActionMacro : public MacroRefAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionMacro>();
}
PerformMacroAction _action = PerformMacroAction::PAUSE;
private:
static bool _registered;
static const std::string id;
};
class MacroActionMacroEdit : public QWidget {
Q_OBJECT
public:
MacroActionMacroEdit(
QWidget *parent,
std::shared_ptr<MacroActionMacro> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionMacroEdit(
parent,
std::dynamic_pointer_cast<MacroActionMacro>(action));
}
private slots:
void MacroChanged(const QString &text);
void MacroRemove(const QString &name);
void ActionChanged(int value);
protected:
MacroSelection *_macros;
QComboBox *_actions;
std::shared_ptr<MacroActionMacro> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@ -0,0 +1,62 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
enum class MediaAction {
PLAY,
PAUSE,
STOP,
RESTART,
NEXT,
PREVIOUS,
};
class MacroActionMedia : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionMedia>();
}
OBSWeakSource _mediaSource;
MediaAction _action = MediaAction::PLAY;
private:
static bool _registered;
static const std::string id;
};
class MacroActionMediaEdit : public QWidget {
Q_OBJECT
public:
MacroActionMediaEdit(
QWidget *parent,
std::shared_ptr<MacroActionMedia> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionMediaEdit(
parent,
std::dynamic_pointer_cast<MacroActionMedia>(action));
}
private slots:
void SourceChanged(const QString &text);
void ActionChanged(int value);
protected:
QComboBox *_mediaSources;
QComboBox *_actions;
std::shared_ptr<MacroActionMedia> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@ -0,0 +1,55 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
enum class PluginStateAction {
STOP,
};
class MacroActionPluginState : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionPluginState>();
}
PluginStateAction _action = PluginStateAction::STOP;
private:
static bool _registered;
static const std::string id;
};
class MacroActionPluginStateEdit : public QWidget {
Q_OBJECT
public:
MacroActionPluginStateEdit(
QWidget *parent,
std::shared_ptr<MacroActionPluginState> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionPluginStateEdit(
parent,
std::dynamic_pointer_cast<MacroActionPluginState>(
action));
}
private slots:
void ActionChanged(int value);
protected:
QComboBox *_actions;
std::shared_ptr<MacroActionPluginState> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@ -16,7 +16,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionRecord>();
@ -26,7 +26,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionRecordEdit : public QWidget {

View File

@ -15,7 +15,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionReplayBuffer>();
@ -33,7 +33,7 @@ private:
Duration _duration;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionReplayBufferEdit : public QWidget {

View File

@ -10,7 +10,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionRun>();
@ -20,7 +20,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionRunEdit : public QWidget {

View File

@ -9,7 +9,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
@ -21,7 +21,7 @@ private:
const char *getType() { return "MacroActionSwitchScene"; }
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionSwitchSceneEdit : public SwitchWidget {

View File

@ -0,0 +1,62 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
enum class SceneVisibilityAction {
SHOW,
HIDE,
};
class MacroActionSceneVisibility : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionSceneVisibility>();
}
OBSWeakSource _scene;
OBSWeakSource _source;
SceneVisibilityAction _action = SceneVisibilityAction::SHOW;
private:
static bool _registered;
static const std::string id;
};
class MacroActionSceneVisibilityEdit : public QWidget {
Q_OBJECT
public:
MacroActionSceneVisibilityEdit(
QWidget *parent,
std::shared_ptr<MacroActionSceneVisibility> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSceneVisibilityEdit(
parent,
std::dynamic_pointer_cast<MacroActionSceneVisibility>(
action));
}
private slots:
void SceneChanged(const QString &text);
void SourceChanged(const QString &text);
void ActionChanged(int value);
protected:
QComboBox *_scenes;
QComboBox *_sources;
QComboBox *_actions;
std::shared_ptr<MacroActionSceneVisibility> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@ -0,0 +1,70 @@
#pragma once
#include "macro-action-edit.hpp"
#include <QSpinBox>
#include <QLabel>
#include <QPushButton>
#include <QPlainTextEdit>
enum class SourceAction {
ENABLE,
DISABLE,
SETTINGS,
};
class MacroActionSource : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionSource>();
}
OBSWeakSource _source;
std::string _settings = "";
SourceAction _action = SourceAction::ENABLE;
private:
static bool _registered;
static const std::string id;
};
class MacroActionSourceEdit : public QWidget {
Q_OBJECT
public:
MacroActionSourceEdit(
QWidget *parent,
std::shared_ptr<MacroActionSource> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSourceEdit(
parent,
std::dynamic_pointer_cast<MacroActionSource>(action));
}
private slots:
void SourceChanged(const QString &text);
void ActionChanged(int value);
void GetSettingsClicked();
void SettingsChanged();
protected:
QComboBox *_sources;
QComboBox *_actions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
QLabel *_warning;
std::shared_ptr<MacroActionSource> _entryData;
private:
void SetWidgetVisibility(bool);
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@ -14,7 +14,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionStream>();
@ -28,7 +28,7 @@ private:
Duration _retryCooldown;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionStreamEdit : public QWidget {

View File

@ -13,7 +13,7 @@ public:
bool PerformAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionWait>();
@ -24,7 +24,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionWaitEdit : public QWidget {

View File

@ -6,8 +6,6 @@
#include <QComboBox>
#include <chrono>
#include "duration-control.hpp"
enum class AudioCondition {
ABOVE,
BELOW,
@ -19,7 +17,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionAudio>();
@ -33,13 +31,12 @@ public:
OBSWeakSource _audioSource;
int _volume = 0;
AudioCondition _condition = AudioCondition::ABOVE;
Duration _duration;
obs_volmeter_t *_volmeter = nullptr;
private:
float _peak = -std::numeric_limits<float>::infinity();
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionAudioEdit : public QWidget {
@ -63,13 +60,11 @@ private slots:
void SourceChanged(const QString &text);
void VolumeThresholdChanged(int vol);
void ConditionChanged(int cond);
void DurationChanged(double seconds);
protected:
QComboBox *_audioSources;
QComboBox *_condition;
QSpinBox *_volume;
DurationSelection *_duration;
VolControl *_volMeter = nullptr;
std::shared_ptr<MacroConditionAudio> _entryData;

View File

@ -0,0 +1,70 @@
#pragma once
#include "macro.hpp"
#include "macro-selection.hpp"
#include <QWidget>
#include <QSpinBox>
#include <QLabel>
#include <QTimer>
enum class CounterCondition {
BELOW,
ABOVE,
EQUAL,
};
class MacroConditionCounter : public MacroRefCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionCounter>();
}
CounterCondition _condition = CounterCondition::BELOW;
int _count = 0;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionCounterEdit : public QWidget {
Q_OBJECT
public:
MacroConditionCounterEdit(
QWidget *parent,
std::shared_ptr<MacroConditionCounter> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionCounterEdit(
parent,
std::dynamic_pointer_cast<MacroConditionCounter>(cond));
}
private slots:
void MacroChanged(const QString &text);
void MacroRemove(const QString &name);
void CountChanged(int value);
void ConditionChanged(int cond);
void ResetClicked();
void UpdateCount();
protected:
MacroSelection *_macros;
QComboBox *_conditions;
QSpinBox *_count;
QLabel *_currentCount;
QPushButton *_resetCount;
std::unique_ptr<QTimer> _timer;
std::shared_ptr<MacroConditionCounter> _entryData;
private:
void ResetTimer();
bool _loading = true;
};

View File

@ -15,19 +15,23 @@ struct MacroConditionInfo {
TCreateMethod _createFunc;
TCreateWidgetMethod _createWidgetFunc;
std::string _name;
bool _useDurationConstraint = true;
};
class MacroConditionFactory {
public:
MacroConditionFactory() = delete;
static bool Register(int id, MacroConditionInfo);
static std::shared_ptr<MacroCondition> Create(const int id);
static QWidget *CreateWidget(const int id, QWidget *parent,
static bool Register(const std::string &, MacroConditionInfo);
static std::shared_ptr<MacroCondition> Create(const std::string &);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition>);
static auto GetConditionTypes() { return _methods; }
static std::string GetConditionName(const std::string &);
static std::string GetIdByName(const QString &name);
static bool UsesDurationConstraint(const std::string &id);
private:
static std::map<int, MacroConditionInfo> _methods;
static std::map<std::string, MacroConditionInfo> _methods;
};
class MacroConditionEdit : public QWidget {
@ -36,20 +40,23 @@ class MacroConditionEdit : public QWidget {
public:
MacroConditionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroCondition> * = nullptr,
int type = 0, bool root = true,
const std::string &id = "scene", bool root = true,
bool startCollapsed = false);
bool IsRootNode();
void UpdateEntryData(int type);
void Collapse(bool collapsed);
void UpdateEntryData(const std::string &id, bool collapse);
private slots:
void LogicSelectionChanged(int idx);
void ConditionSelectionChanged(int idx);
void ConditionSelectionChanged(const QString &text);
void DurationChanged(double seconds);
void DurationConditionChanged(DurationCondition cond);
void DurationUnitChanged(DurationUnit unit);
protected:
QComboBox *_logicSelection;
QComboBox *_conditionSelection;
Section *_section;
DurationConstraintEdit *_dur;
std::shared_ptr<MacroCondition> *_entryData;

View File

@ -18,7 +18,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionFile>();
@ -39,7 +39,7 @@ private:
QDateTime _lastMod;
size_t _lastHash = 0;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionFileEdit : public QWidget {

View File

@ -1,15 +1,16 @@
#pragma once
#include "macro.hpp"
#include "duration-control.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
class MacroConditionIdle : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionIdle>();
@ -19,7 +20,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionIdleEdit : public QWidget {

View File

@ -0,0 +1,52 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
class MacroConditionInterval : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionInterval>();
}
Duration _duration;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionIntervalEdit : public QWidget {
Q_OBJECT
public:
MacroConditionIntervalEdit(
QWidget *parent,
std::shared_ptr<MacroConditionInterval> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionIntervalEdit(
parent,
std::dynamic_pointer_cast<MacroConditionInterval>(
cond));
}
private slots:
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
protected:
DurationSelection *_duration;
std::shared_ptr<MacroConditionInterval> _entryData;
private:
bool _loading = true;
};

View File

@ -39,7 +39,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionMedia>();
@ -65,7 +65,7 @@ private:
bool _playedToEnd = false;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionMediaEdit : public QWidget {

View File

@ -12,7 +12,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionPluginState>();
@ -22,7 +22,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionPluginStateEdit : public QWidget {

View File

@ -8,7 +8,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionProcess>();
@ -19,7 +19,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionProcessEdit : public QWidget {

View File

@ -2,7 +2,6 @@
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
enum class RecordState {
STOP,
@ -15,18 +14,17 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionRecord>();
}
RecordState _recordState;
Duration _duration;
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionRecordEdit : public QWidget {
@ -47,12 +45,9 @@ public:
private slots:
void StateChanged(int value);
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
protected:
QComboBox *_recordState;
DurationSelection *_duration;
std::shared_ptr<MacroConditionRecord> _entryData;
private:

View File

@ -8,7 +8,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionRegion>();
@ -18,7 +18,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionRegionEdit : public QWidget {

View File

@ -2,7 +2,6 @@
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
enum class SceneType {
CURRENT,
@ -14,7 +13,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionScene>();
@ -22,11 +21,10 @@ public:
OBSWeakSource _scene;
SceneType _type;
Duration _duration;
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionSceneEdit : public QWidget {
@ -48,13 +46,10 @@ public:
private slots:
void SceneChanged(const QString &text);
void TypeChanged(int value);
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
protected:
QComboBox *_sceneSelection;
QComboBox *_sceneType;
DurationSelection *_duration;
std::shared_ptr<MacroConditionScene> _entryData;
private:

View File

@ -0,0 +1,68 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
enum class SourceCondition {
ACTIVE,
SHOWING,
SETTINGS,
};
class MacroConditionSource : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionSource>();
}
OBSWeakSource _source = nullptr;
SourceCondition _condition = SourceCondition::ACTIVE;
std::string _settings = "";
bool _regex = false;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionSourceEdit : public QWidget {
Q_OBJECT
public:
MacroConditionSourceEdit(
QWidget *parent,
std::shared_ptr<MacroConditionSource> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionSourceEdit(
parent,
std::dynamic_pointer_cast<MacroConditionSource>(cond));
}
private slots:
void SourceChanged(const QString &text);
void ConditionChanged(int cond);
void GetSettingsClicked();
void SettingsChanged();
void RegexChanged(int);
protected:
QComboBox *_sources;
QComboBox *_conditions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
QCheckBox *_regex;
std::shared_ptr<MacroConditionSource> _entryData;
private:
void SetSettingsSelectionVisible(bool visible);
bool _loading = true;
};

View File

@ -2,7 +2,6 @@
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
enum class StreamState {
STOP,
@ -14,18 +13,17 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionStream>();
}
StreamState _streamState;
Duration _duration;
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionStreamEdit : public QWidget {
@ -46,12 +44,9 @@ public:
private slots:
void StateChanged(int value);
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
protected:
QComboBox *_streamState;
DurationSelection *_duration;
std::shared_ptr<MacroConditionStream> _entryData;
private:

View File

@ -1,10 +1,10 @@
#pragma once
#include "macro.hpp"
#include "screenshot-helper.hpp"
#include <QWidget>
#include <QComboBox>
#include <chrono>
#include "duration-control.hpp"
#include "screenshot-helper.hpp"
enum class VideoCondition {
MATCH,
@ -19,7 +19,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
QImage GetMatchImage() { return _matchImage; };
static std::shared_ptr<MacroCondition> Create()
{
@ -30,7 +30,6 @@ public:
OBSWeakSource _videoSource;
VideoCondition _condition = VideoCondition::MATCH;
Duration _duration;
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
private:
@ -39,7 +38,7 @@ private:
std::unique_ptr<AdvSSScreenshotObj> _screenshotData = nullptr;
QImage _matchImage;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionVideoEdit : public QWidget {
@ -66,12 +65,9 @@ private slots:
void ConditionChanged(int cond);
void FilePathChanged();
void BrowseButtonClicked();
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
protected:
QComboBox *_videoSelection;
DurationSelection *_duration;
QComboBox *_condition;
QLineEdit *_filePath;
QPushButton *_browseButton;

View File

@ -8,7 +8,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionWindow>();
@ -27,7 +27,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionWindowEdit : public QWidget {

View File

@ -0,0 +1,17 @@
#pragma once
#include <QComboBox>
class Macro;
class MacroSelection : public QComboBox {
Q_OBJECT
public:
MacroSelection(QWidget *parent);
void SetCurrentMacro(Macro *);
private slots:
void MacroAdd(const QString &name);
void MacroRemove(const QString &name);
void MacroRename(const QString &oldName, const QString &newName);
};

View File

@ -1,10 +1,13 @@
#pragma once
#include "duration-control.hpp"
#include <string>
#include <deque>
#include <memory>
#include <map>
#include <obs.hpp>
#include <obs-module.h>
#include <QString>
constexpr auto macro_func = 10;
constexpr auto default_priority_10 = macro_func;
@ -38,14 +41,23 @@ public:
virtual bool CheckCondition() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual int GetId() = 0;
virtual std::string GetId() = 0;
LogicType GetLogicType() { return _logic; }
void SetLogicType(LogicType logic) { _logic = logic; }
static const std::map<LogicType, LogicTypeInfo> logicTypes;
bool DurationReached() { return _duration.DurationReached(); }
void ResetDuration() { _duration.Reset(); }
DurationConstraint GetDurationConstraint() { return _duration; }
void SetDurationConstraint(const DurationConstraint &dur);
void SetDurationCondition(DurationCondition cond);
void SetDurationUnit(DurationUnit u);
void SetDuration(double seconds);
private:
LogicType _logic;
DurationConstraint _duration;
};
class MacroAction {
@ -53,21 +65,24 @@ public:
virtual bool PerformAction() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual int GetId() = 0;
virtual std::string GetId() = 0;
virtual void LogAction();
};
class Macro {
public:
Macro(std::string name = "");
Macro(const std::string &name = "");
virtual ~Macro();
bool CeckMatch();
bool PerformAction();
bool Matched() { return _matched; }
std::string Name() { return _name; }
void SetName(std::string name) { _name = name; }
void SetName(const std::string &name);
void SetPaused(bool pause = true) { _paused = pause; }
bool Paused() { return _paused; }
int GetCount() { return _count; };
void ResetCount() { _count = 0; };
std::deque<std::shared_ptr<MacroCondition>> &Conditions()
{
return _conditions;
@ -76,13 +91,56 @@ public:
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
// Some macros can refer to other macros, which are not yet loaded.
// Use this function to set these references after loading is complete.
void ResolveMacroRef();
// Helper function for plugin state condition regarding scene change
bool SwitchesScene();
private:
void SetupHotkeys();
void ClearHotkeys();
void SetHotkeysDesc();
std::string _name = "";
std::deque<std::shared_ptr<MacroCondition>> _conditions;
std::deque<std::shared_ptr<MacroAction>> _actions;
bool _matched = false;
bool _paused = false;
int _count = 0;
obs_hotkey_id _pauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
};
Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name);
class MacroRef {
public:
MacroRef(){};
MacroRef(std::string name);
void UpdateRef();
void UpdateRef(std::string name);
void UpdateRef(QString name);
void Save(obs_data_t *obj);
void Load(obs_data_t *obj);
Macro *get();
Macro *operator->();
private:
std::string _name = "";
Macro *_ref = nullptr;
};
class MacroRefCondition : public MacroCondition {
public:
void ResolveMacroRef();
MacroRef _macro;
};
class MacroRefAction : public MacroAction {
public:
void ResolveMacroRef();
MacroRef _macro;
};

View File

@ -0,0 +1,11 @@
#pragma once
void GetWindowList(std::vector<std::string> &windows);
void GetWindowList(QStringList &windows);
void GetCurrentWindowTitle(std::string &title);
bool isFullscreen(const std::string &title);
bool isMaximized(const std::string &title);
std::pair<int, int> getCursorPos();
int secondsSinceLastInput();
void GetProcessList(QStringList &processes);
bool isInFocus(const QString &executable);

View File

@ -40,9 +40,9 @@ struct SceneGroup {
int lastRandomScene = -1;
inline SceneGroup(){};
inline SceneGroup(std::string name_) : name(name_){};
inline SceneGroup(std::string name_, AdvanceCondition type_,
std::vector<OBSWeakSource> scenes_, int count_,
inline SceneGroup(const std::string &name_) : name(name_){};
inline SceneGroup(const std::string &name_, AdvanceCondition type_,
const std::vector<OBSWeakSource> &scenes_, int count_,
double time_, bool repeat_)
: name(name_),
type(type_),

View File

@ -14,18 +14,31 @@ public:
explicit Section(const int animationDuration = 300,
QWidget *parent = 0);
void SetContent(QWidget *w);
void SetContent(QWidget *w, bool collapsed = true);
void AddHeaderWidget(QWidget *);
public slots:
void Collapse(bool collapsed);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots:
void AnimationFinished();
void Collapse(bool collapse);
private:
void SetupAnimations();
void CleanUpPreviousContent();
QGridLayout *_mainLayout;
QHBoxLayout *_headerWidgetLayout;
QToolButton *_toggleButton;
QFrame *_headerLine;
QParallelAnimationGroup *_toggleAnimation = nullptr;
QParallelAnimationGroup *_contentAnimation = nullptr;
QScrollArea *_contentArea = nullptr;
QWidget *_content = nullptr;
int _animationDuration;
std::atomic_bool _transitioning = {false};
std::atomic_bool _collapsed = {false};
int _headerHeight = 0;
int _contentHeight = 0;
};

View File

@ -33,6 +33,10 @@ public:
std::string GetClientUri();
bool ShouldSendSceneChange();
bool ShouldSendFrontendSceneChange();
bool ShouldSendPrviewSceneChange();
// Server
bool ServerEnabled;
uint64_t ServerPort;
@ -42,7 +46,9 @@ public:
bool ClientEnabled;
std::string Address;
uint64_t ClientPort;
bool SendAll;
bool SendSceneChange;
bool SendSceneChangeAll;
bool SendPreview;
};
class WSServer : public QObject {
@ -53,7 +59,7 @@ public:
virtual ~WSServer();
void start(quint16 port, bool lockToIPv4);
void stop();
void sendMessage(sceneSwitchInfo sceneSwitch);
void sendMessage(sceneSwitchInfo sceneSwitch, bool preview = false);
QThreadPool *threadPool() { return &_threadPool; }
private:

View File

@ -192,6 +192,7 @@ struct SwitcherData {
void Start();
void Stop();
void setWaitScene();
bool sceneChangedDuringWait();
bool prioFuncsValid();
@ -200,7 +201,7 @@ struct SwitcherData {
void resetTabOrder();
void writeSceneInfoToFile();
void writeToStatusFile(QString status);
void writeToStatusFile(const QString &msg);
bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPreviousSceneAsMatch,
@ -253,7 +254,7 @@ struct SwitcherData {
void saveNetworkSwitches(obs_data_t *obj);
void saveGeneralSettings(obs_data_t *obj);
void saveHotkeys(obs_data_t *obj);
void saveVersion(obs_data_t *obj, std::string currentVersion);
void saveVersion(obs_data_t *obj, const std::string &currentVersion);
void loadSettings(obs_data_t *obj);
void loadMacros(obs_data_t *obj);

View File

@ -1,118 +1,28 @@
#pragma once
#include <QString>
#include <QTextStream>
#include <QLayout>
#include <QLabel>
#include <QMessageBox>
#include <unordered_map>
#include <QComboBox>
#include <QMetaObject>
#include <QListWidget>
#include <QPushButton>
#include <QColor>
#include <obs.hpp>
#include <obs-module.h>
#include <obs-frontend-api.h>
#include <deque>
#include <unordered_map>
#include "scene-group.hpp"
static inline bool WeakSourceValid(obs_weak_source_t *ws)
{
obs_source_t *source = obs_weak_source_get_source(ws);
if (source) {
obs_source_release(source);
}
return !!source;
}
static inline std::string GetWeakSourceName(obs_weak_source_t *weak_source)
{
std::string name;
obs_source_t *source = obs_weak_source_get_source(weak_source);
if (source) {
name = obs_source_get_name(source);
obs_source_release(source);
}
return name;
}
static inline OBSWeakSource GetWeakSourceByName(const char *name)
{
OBSWeakSource weak;
obs_source_t *source = obs_get_source_by_name(name);
if (source) {
weak = obs_source_get_weak_source(source);
obs_weak_source_release(weak);
obs_source_release(source);
}
return weak;
}
static inline OBSWeakSource GetWeakSourceByQString(const QString &name)
{
return GetWeakSourceByName(name.toUtf8().constData());
}
static inline OBSWeakSource GetWeakTransitionByName(const char *transitionName)
{
OBSWeakSource weak;
obs_source_t *source = nullptr;
if (strcmp(transitionName, "Default") == 0) {
source = obs_frontend_get_current_transition();
weak = obs_source_get_weak_source(source);
obs_source_release(source);
obs_weak_source_release(weak);
return weak;
}
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
bool match = false;
for (size_t i = 0; i < transitions->sources.num; i++) {
const char *name =
obs_source_get_name(transitions->sources.array[i]);
if (strcmp(transitionName, name) == 0) {
match = true;
source = transitions->sources.array[i];
break;
}
}
if (match) {
weak = obs_source_get_weak_source(source);
obs_weak_source_release(weak);
}
obs_frontend_source_list_free(transitions);
return weak;
}
static inline OBSWeakSource GetWeakTransitionByQString(const QString &name)
{
return GetWeakTransitionByName(name.toUtf8().constData());
}
static inline std::string
getNextDelim(std::string text,
std::unordered_map<std::string, QWidget *> placeholders)
{
size_t pos = std::string::npos;
std::string res = "";
for (const auto &ph : placeholders) {
size_t newPos = text.find(ph.first);
if (newPos <= pos) {
pos = newPos;
res = ph.first;
}
}
if (pos == std::string::npos) {
return "";
}
return res;
}
bool WeakSourceValid(obs_weak_source_t *ws);
std::string GetWeakSourceName(obs_weak_source_t *weak_source);
OBSWeakSource GetWeakSourceByName(const char *name);
OBSWeakSource GetWeakSourceByQString(const QString &name);
OBSWeakSource GetWeakTransitionByName(const char *transitionName);
OBSWeakSource GetWeakTransitionByQString(const QString &name);
OBSWeakSource GetWeakFilterByName(OBSWeakSource source, const char *name);
OBSWeakSource GetWeakFilterByQString(OBSWeakSource source, const QString &name);
bool compareIgnoringLineEnding(QString &s1, QString &s2);
std::string getSourceSettings(OBSWeakSource ws);
std::string getDataFilePath(const std::string &file);
/**
* Populate layout with labels and widgets based on provided text
@ -122,95 +32,32 @@ getNextDelim(std::string text,
* @param placeholders Map containing a mapping of placeholder strings to widgets.
* @param addStretch Add addStretch() to layout.
*/
static inline void
placeWidgets(std::string text, QBoxLayout *layout,
std::unordered_map<std::string, QWidget *> placeholders,
bool addStretch = true)
{
std::vector<std::pair<std::string, QWidget *>> labelsWidgetsPairs;
std::string delim = getNextDelim(text, placeholders);
while (delim != "") {
size_t pos = text.find(delim);
if (pos != std::string::npos) {
labelsWidgetsPairs.emplace_back(text.substr(0, pos),
placeholders[delim]);
text.erase(0, pos + delim.length());
}
delim = getNextDelim(text, placeholders);
}
if (text != "") {
labelsWidgetsPairs.emplace_back(text, nullptr);
}
for (auto &lw : labelsWidgetsPairs) {
if (lw.first != "") {
layout->addWidget(new QLabel(lw.first.c_str()));
}
if (lw.second) {
layout->addWidget(lw.second);
}
}
if (addStretch) {
layout->addStretch();
}
}
static inline void clearLayout(QLayout *layout)
{
QLayoutItem *item;
while ((item = layout->takeAt(0))) {
if (item->layout()) {
clearLayout(item->layout());
delete item->layout();
}
if (item->widget()) {
delete item->widget();
}
delete item;
}
}
static inline bool compareIgnoringLineEnding(QString &s1, QString &s2)
{
// Let QT deal with different types of lineendings
QTextStream s1stream(&s1);
QTextStream s2stream(&s2);
while (!s1stream.atEnd() || !s2stream.atEnd()) {
QString s1s = s1stream.readLine();
QString s2s = s2stream.readLine();
if (s1s != s2s) {
return false;
}
}
if (!s1stream.atEnd() && !s2stream.atEnd()) {
return false;
}
return true;
}
static inline bool DisplayMessage(QString msg, bool question = false)
{
if (question) {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(
nullptr, "Advanced Scene Switcher", msg,
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
return true;
} else {
return false;
}
} else {
QMessageBox Msgbox;
Msgbox.setWindowTitle("Advanced Scene Switcher");
Msgbox.setText(msg);
Msgbox.exec();
}
return false;
}
void placeWidgets(std::string text, QBoxLayout *layout,
std::unordered_map<std::string, QWidget *> placeholders,
bool addStretch = true);
void clearLayout(QLayout *layout);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
QColor = QColor(0, 0, 0, 0),
QString specifier = "QLabel ");
void listAddClicked(QListWidget *list, QWidget *newWidget,
QPushButton *addButton = nullptr,
QMetaObject::Connection *addHighlight = nullptr);
bool listMoveUp(QListWidget *list);
bool listMoveDown(QListWidget *list);
bool DisplayMessage(const QString &msg, bool question = false);
void addSelectionEntry(QComboBox *sel, const char *description,
bool selectable = false, const char *tooltip = "");
void populateTransitionSelection(QComboBox *sel, bool addCurrent = true,
bool addSelect = true,
bool selectable = false);
void populateWindowSelection(QComboBox *sel, bool addSelect = true);
void populateAudioSelection(QComboBox *sel, bool addSelect = true);
void populateVideoSelection(QComboBox *sel, bool addSelect = true);
void populateMediaSelection(QComboBox *sel, bool addSelect = true);
void populateProcessSelection(QComboBox *sel, bool addSelect = true);
void populateSourceSelection(QComboBox *list, bool addSelect = true);
void populateSceneSelection(QComboBox *sel, bool addPrevious = false,
bool addSceneGroup = false,
std::deque<SceneGroup> *sceneGroups = nullptr,
bool addSelect = true, std::string selectText = "",
bool selectable = false);

View File

@ -253,7 +253,7 @@ std::pair<int, int> getCursorPos()
return pos;
}
bool isMaximized(std::string &title)
bool isMaximized(const std::string &title)
{
if (!ewmhIsSupported())
return false;
@ -302,7 +302,7 @@ bool isMaximized(std::string &title)
return false;
}
bool isFullscreen(std::string &title)
bool isFullscreen(const std::string &title)
{
if (!ewmhIsSupported())
return false;

View File

@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionAudio::id = 2;
const std::string MacroActionAudio::id = "audio";
bool MacroActionAudio::_registered = MacroActionFactory::Register(
MacroActionAudio::id,
@ -94,7 +94,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
_volumePercent->setSuffix("%");
populateActionSelection(_actions);
AdvSceneSwitcher::populateAudioSelection(_audioSources);
populateAudioSelection(_audioSources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
@ -130,17 +130,8 @@ bool hasSourceControl(AudioAction action)
return action != AudioAction::MASTER_VOLUME;
}
void MacroActionAudioEdit::UpdateEntryData()
void MacroActionAudioEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
_audioSources->setCurrentText(
GetWeakSourceName(_entryData->_audioSource).c_str());
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_volumePercent->setValue(_entryData->_volume);
if (hasVolumeControl(_entryData->_action)) {
_volumePercent->show();
} else {
@ -154,6 +145,20 @@ void MacroActionAudioEdit::UpdateEntryData()
}
}
void MacroActionAudioEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_audioSources->setCurrentText(
GetWeakSourceName(_entryData->_audioSource).c_str());
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_volumePercent->setValue(_entryData->_volume);
SetWidgetVisibility();
}
void MacroActionAudioEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
@ -172,7 +177,7 @@ void MacroActionAudioEdit::ActionChanged(int value)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<AudioAction>(value);
UpdateEntryData();
SetWidgetVisibility();
}
void MacroActionAudioEdit::VolumeChanged(int value)

View File

@ -1,11 +1,11 @@
#include "headers/macro-action-edit.hpp"
#include "headers/macro-action-switch-scene.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
std::map<int, MacroActionInfo> MacroActionFactory::_methods;
std::map<std::string, MacroActionInfo> MacroActionFactory::_methods;
bool MacroActionFactory::Register(int id, MacroActionInfo info)
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
if (auto it = _methods.find(id); it == _methods.end()) {
_methods[id] = info;
@ -14,7 +14,7 @@ bool MacroActionFactory::Register(int id, MacroActionInfo info)
return false;
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const int id)
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end())
return it->second._createFunc();
@ -22,7 +22,8 @@ std::shared_ptr<MacroAction> MacroActionFactory::Create(const int id)
return nullptr;
}
QWidget *MacroActionFactory::CreateWidget(const int id, QWidget *parent,
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
if (auto it = _methods.find(id); it != _methods.end())
@ -31,6 +32,24 @@ QWidget *MacroActionFactory::CreateWidget(const int id, QWidget *parent,
return nullptr;
}
std::string MacroActionFactory::GetActionName(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._name;
}
return "unknown action";
}
std::string MacroActionFactory::GetIdByName(const QString &name)
{
for (auto it : _methods) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : MacroActionFactory::GetActionTypes()) {
@ -40,14 +59,15 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData,
int type, bool startCollapsed)
const std::string &id, bool startCollapsed)
: QWidget(parent)
{
_actionSelection = new QComboBox();
_section = new Section(300);
QWidget::connect(_actionSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(ActionSelectionChanged(int)));
QWidget::connect(_actionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ActionSelectionChanged(const QString &)));
populateActionSelection(_actionSelection);
@ -58,38 +78,34 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData(type);
UpdateEntryData(id, startCollapsed);
_loading = false;
_section->Collapse(startCollapsed);
}
void MacroActionEdit::ActionSelectionChanged(int idx)
void MacroActionEdit::ActionSelectionChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::string id = MacroActionFactory::GetIdByName(text);
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->reset();
*_entryData = MacroActionFactory::Create(idx);
*_entryData = MacroActionFactory::Create(id);
auto widget =
MacroActionFactory::CreateWidget(idx, window(), *_entryData);
_section->SetContent(widget);
_section->Collapse(false);
MacroActionFactory::CreateWidget(id, window(), *_entryData);
_section->SetContent(widget, false);
}
void MacroActionEdit::UpdateEntryData(int type)
void MacroActionEdit::UpdateEntryData(const std::string &id, bool collapse)
{
_actionSelection->setCurrentIndex(type);
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
auto widget =
MacroActionFactory::CreateWidget(type, window(), *_entryData);
_section->SetContent(widget);
}
void MacroActionEdit::Collapse(bool collapsed)
{
_section->Collapse(collapsed);
MacroActionFactory::CreateWidget(id, window(), *_entryData);
_section->SetContent(widget, collapse);
}
void AdvSceneSwitcher::on_actionAdd_clicked()
@ -98,12 +114,16 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
if (!macro) {
return;
}
MacroActionSwitchScene temp;
std::string id = temp.GetId();
std::lock_guard<std::mutex> lock(switcher->m);
macro->Actions().emplace_back(MacroActionFactory::Create(0));
auto newEntry = new MacroActionEdit(this, &macro->Actions().back(), 0);
macro->Actions().emplace_back(MacroActionFactory::Create(id));
auto newEntry =
new MacroActionEdit(this, &macro->Actions().back(), id, false);
ui->macroEditActionLayout->addWidget(newEntry);
ui->macroEditActionHelp->setVisible(false);
newEntry->Collapse(false);
}
void AdvSceneSwitcher::on_actionRemove_clicked()

203
src/macro-action-filter.cpp Normal file
View File

@ -0,0 +1,203 @@
#include "headers/macro-action-filter.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionFilter::id = "filter";
bool MacroActionFilter::_registered = MacroActionFactory::Register(
MacroActionFilter::id,
{MacroActionFilter::Create, MacroActionFilterEdit::Create,
"AdvSceneSwitcher.action.filter"});
const static std::map<FilterAction, std::string> actionTypes = {
{FilterAction::ENABLE, "AdvSceneSwitcher.action.filter.type.enable"},
{FilterAction::DISABLE, "AdvSceneSwitcher.action.filter.type.disable"},
};
bool MacroActionFilter::PerformAction()
{
auto s = obs_weak_source_get_source(_filter);
switch (_action) {
case FilterAction::ENABLE:
obs_source_set_enabled(s, true);
break;
case FilterAction::DISABLE:
obs_source_set_enabled(s, false);
break;
default:
break;
}
obs_source_release(s);
return true;
}
void MacroActionFilter::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed action \"%s\" for filter \"%s\" on source \"%s\"",
it->second.c_str(), GetWeakSourceName(_filter).c_str(),
GetWeakSourceName(_source).c_str());
} else {
blog(LOG_WARNING, "ignored unknown filter action %d",
static_cast<int>(_action));
}
}
bool MacroActionFilter::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionFilter::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
const char *filterName = obs_data_get_string(obj, "filter");
_filter = GetWeakFilterByQString(_source, filterName);
_action = static_cast<FilterAction>(obs_data_get_int(obj, "action"));
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
static inline void populateFilters(QComboBox *list,
OBSWeakSource weakSource = nullptr)
{
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *ptr) {
QComboBox *list = reinterpret_cast<QComboBox *>(ptr);
auto name = obs_source_get_name(filter);
list->addItem(name);
};
auto s = obs_weak_source_get_source(weakSource);
obs_source_enum_filters(s, enumFilters, list);
list->model()->sort(0);
addSelectionEntry(list,
obs_module_text("AdvSceneSwitcher.selectFilter"));
obs_source_release(s);
list->setCurrentIndex(0);
}
static inline void hasFilterEnum(obs_source_t *, obs_source_t *filter,
void *ptr)
{
if (!filter) {
return;
}
bool *hasFilter = reinterpret_cast<bool *>(ptr);
*hasFilter = true;
}
static inline void populateSourcesWithFilter(QComboBox *list)
{
auto enumSourcesWithFilters = [](void *param, obs_source_t *source) {
if (!source) {
return true;
}
QComboBox *list = reinterpret_cast<QComboBox *>(param);
bool hasFilter = false;
obs_source_enum_filters(source, hasFilterEnum, &hasFilter);
if (hasFilter) {
list->addItem(obs_source_get_name(source));
}
return true;
};
obs_enum_sources(enumSourcesWithFilters, list);
obs_enum_scenes(enumSourcesWithFilters, list);
list->model()->sort(0);
addSelectionEntry(list,
obs_module_text("AdvSceneSwitcher.selectSource"));
list->setCurrentIndex(0);
}
MacroActionFilterEdit::MacroActionFilterEdit(
QWidget *parent, std::shared_ptr<MacroActionFilter> entryData)
: QWidget(parent)
{
_sources = new QComboBox();
_filters = new QComboBox();
_actions = new QComboBox();
populateActionSelection(_actions);
populateSourcesWithFilter(_sources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_filters, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(FilterChanged(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources},
{"{{filters}}", _filters},
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionFilterEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
populateFilters(_filters, _entryData->_source);
_filters->setCurrentText(
GetWeakSourceName(_entryData->_filter).c_str());
}
void MacroActionFilterEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
_filters->clear();
populateFilters(_filters, _entryData->_source);
}
void MacroActionFilterEdit::FilterChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_filter = GetWeakFilterByQString(_entryData->_source, text);
}
void MacroActionFilterEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<FilterAction>(value);
}

152
src/macro-action-macro.cpp Normal file
View File

@ -0,0 +1,152 @@
#include "headers/macro-action-macro.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionMacro::id = "macro";
bool MacroActionMacro::_registered = MacroActionFactory::Register(
MacroActionMacro::id,
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
"AdvSceneSwitcher.action.macro"});
const static std::map<PerformMacroAction, std::string> actionTypes = {
{PerformMacroAction::PAUSE, "AdvSceneSwitcher.action.macro.type.pause"},
{PerformMacroAction::UNPAUSE,
"AdvSceneSwitcher.action.macro.type.unpause"},
{PerformMacroAction::RESET_COUNTER,
"AdvSceneSwitcher.action.macro.type.resetCounter"},
};
bool MacroActionMacro::PerformAction()
{
if (!_macro.get()) {
return true;
}
switch (_action) {
case PerformMacroAction::PAUSE:
_macro->SetPaused();
break;
case PerformMacroAction::UNPAUSE:
_macro->SetPaused(false);
break;
case PerformMacroAction::RESET_COUNTER:
_macro->ResetCount();
break;
default:
break;
}
return true;
}
void MacroActionMacro::LogAction()
{
if (!_macro.get()) {
return;
}
switch (_action) {
case PerformMacroAction::PAUSE:
vblog(LOG_INFO, "paused \"%s\"", _macro->Name().c_str());
break;
case PerformMacroAction::UNPAUSE:
vblog(LOG_INFO, "unpaused \"%s\"", _macro->Name().c_str());
break;
case PerformMacroAction::RESET_COUNTER:
vblog(LOG_INFO, "reset counter for \"%s\"",
_macro->Name().c_str());
break;
default:
break;
}
}
bool MacroActionMacro::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
_macro.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionMacro::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_macro.Load(obj);
_action = static_cast<PerformMacroAction>(
obs_data_get_int(obj, "action"));
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionMacroEdit::MacroActionMacroEdit(
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
: QWidget(parent)
{
_macros = new MacroSelection(parent);
_actions = new QComboBox();
populateActionSelection(_actions);
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{macros}}", _macros},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionMacroEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_macros->SetCurrentMacro(_entryData->_macro.get());
}
void MacroActionMacroEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_macro.UpdateRef(text);
}
void MacroActionMacroEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<PerformMacroAction>(value);
}
void MacroActionMacroEdit::MacroRemove(const QString &name)
{
UNUSED_PARAMETER(name);
if (_entryData) {
_entryData->_macro.UpdateRef();
}
}

153
src/macro-action-media.cpp Normal file
View File

@ -0,0 +1,153 @@
#include "headers/macro-action-media.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionMedia::id = "media";
bool MacroActionMedia::_registered = MacroActionFactory::Register(
MacroActionMedia::id,
{MacroActionMedia::Create, MacroActionMediaEdit::Create,
"AdvSceneSwitcher.action.media"});
const static std::map<MediaAction, std::string> actionTypes = {
{MediaAction::PLAY, "AdvSceneSwitcher.action.media.type.play"},
{MediaAction::PAUSE, "AdvSceneSwitcher.action.media.type.pause"},
{MediaAction::STOP, "AdvSceneSwitcher.action.media.type.stop"},
{MediaAction::RESTART, "AdvSceneSwitcher.action.media.type.restart"},
{MediaAction::NEXT, "AdvSceneSwitcher.action.media.type.next"},
{MediaAction::PREVIOUS, "AdvSceneSwitcher.action.media.type.previous"},
};
bool MacroActionMedia::PerformAction()
{
auto source = obs_weak_source_get_source(_mediaSource);
obs_media_state state = obs_source_media_get_state(source);
switch (_action) {
case MediaAction::PLAY:
if (state == OBS_MEDIA_STATE_STOPPED ||
state == OBS_MEDIA_STATE_ENDED) {
obs_source_media_restart(source);
} else {
obs_source_media_play_pause(source, false);
}
break;
case MediaAction::PAUSE:
obs_source_media_play_pause(source, true);
break;
case MediaAction::STOP:
obs_source_media_stop(source);
break;
case MediaAction::RESTART:
obs_source_media_restart(source);
break;
case MediaAction::NEXT:
obs_source_media_next(source);
break;
case MediaAction::PREVIOUS:
obs_source_media_previous(source);
break;
default:
break;
}
obs_source_release(source);
return true;
}
void MacroActionMedia::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" for source \"%s\"",
it->second.c_str(),
GetWeakSourceName(_mediaSource).c_str());
} else {
blog(LOG_WARNING, "ignored unknown media action %d",
static_cast<int>(_action));
}
}
bool MacroActionMedia::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "mediaSource",
GetWeakSourceName(_mediaSource).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionMedia::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *MediaSourceName = obs_data_get_string(obj, "mediaSource");
_mediaSource = GetWeakSourceByName(MediaSourceName);
_action = static_cast<MediaAction>(obs_data_get_int(obj, "action"));
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionMediaEdit::MacroActionMediaEdit(
QWidget *parent, std::shared_ptr<MacroActionMedia> entryData)
: QWidget(parent)
{
_mediaSources = new QComboBox();
_actions = new QComboBox();
populateActionSelection(_actions);
populateMediaSelection(_mediaSources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_mediaSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{mediaSources}}", _mediaSources},
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.media.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionMediaEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_mediaSources->setCurrentText(
GetWeakSourceName(_entryData->_mediaSource).c_str());
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
}
void MacroActionMediaEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_mediaSource = GetWeakSourceByQString(text);
}
void MacroActionMediaEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<MediaAction>(value);
}

View File

@ -0,0 +1,114 @@
#include "headers/macro-action-plugin-state.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include <thread>
const std::string MacroActionPluginState::id = "plugin_state";
bool MacroActionPluginState::_registered = MacroActionFactory::Register(
MacroActionPluginState::id,
{MacroActionPluginState::Create, MacroActionPluginStateEdit::Create,
"AdvSceneSwitcher.action.PluginState"});
const static std::map<PluginStateAction, std::string> actionTypes = {
{PluginStateAction::STOP,
"AdvSceneSwitcher.action.pluginState.type.stop"},
};
void stopPlugin()
{
std::thread t([]() { switcher->Stop(); });
t.detach();
}
bool MacroActionPluginState::PerformAction()
{
switch (_action) {
case PluginStateAction::STOP:
stopPlugin();
break;
default:
break;
}
return true;
}
void MacroActionPluginState::LogAction()
{
auto it = actionTypes.find(_action);
switch (_action) {
case PluginStateAction::STOP:
blog(LOG_INFO, "stop() called by macro", it->second.c_str());
break;
default:
blog(LOG_WARNING, "ignored unknown pluginState action %d",
static_cast<int>(_action));
break;
}
}
bool MacroActionPluginState::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionPluginState::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action =
static_cast<PluginStateAction>(obs_data_get_int(obj, "action"));
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionPluginStateEdit::MacroActionPluginStateEdit(
QWidget *parent, std::shared_ptr<MacroActionPluginState> entryData)
: QWidget(parent)
{
_actions = new QComboBox();
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
};
placeWidgets(
obs_module_text("AdvSceneSwitcher.action.pluginState.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionPluginStateEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
}
void MacroActionPluginStateEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<PluginStateAction>(value);
}

View File

@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionRecord::id = 4;
const std::string MacroActionRecord::id = "recording";
bool MacroActionRecord::_registered = MacroActionFactory::Register(
MacroActionRecord::id,

View File

@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionReplayBuffer::id = 5;
const std::string MacroActionReplayBuffer::id = "replay_buffer";
bool MacroActionReplayBuffer::_registered = MacroActionFactory::Register(
MacroActionReplayBuffer::id,

View File

@ -5,7 +5,7 @@
#include <QProcess>
#include <QFileDialog>
const int MacroActionRun::id = 6;
const std::string MacroActionRun::id = "run";
bool MacroActionRun::_registered = MacroActionFactory::Register(
MacroActionRun::id, {MacroActionRun::Create, MacroActionRunEdit::Create,

View File

@ -1,8 +1,8 @@
#include "headers/macro-action-switch-scene.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionSwitchScene::id = 0;
const std::string MacroActionSwitchScene::id = "scene_switch";
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
MacroActionSwitchScene::id,

View File

@ -0,0 +1,212 @@
#include "headers/macro-action-scene-visibility.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionSceneVisibility::id = "scene_visibility";
bool MacroActionSceneVisibility::_registered = MacroActionFactory::Register(
MacroActionSceneVisibility::id,
{MacroActionSceneVisibility::Create,
MacroActionSceneVisibilityEdit::Create,
"AdvSceneSwitcher.action.sceneVisibility"});
const static std::map<SceneVisibilityAction, std::string> actionTypes = {
{SceneVisibilityAction::SHOW,
"AdvSceneSwitcher.action.sceneVisibility.type.show"},
{SceneVisibilityAction::HIDE,
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
};
struct VisInfo {
std::string name;
bool visible;
};
static bool visibilityEnum(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
VisInfo *vInfo = reinterpret_cast<VisInfo *>(ptr);
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
if (vInfo->name == sourceName) {
obs_sceneitem_set_visible(item, vInfo->visible);
}
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, visibilityEnum, ptr);
}
return true;
}
bool MacroActionSceneVisibility::PerformAction()
{
auto s = obs_weak_source_get_source(_scene);
auto scene = obs_scene_from_source(s);
auto sourceName = GetWeakSourceName(_source);
VisInfo vInfo = {sourceName, _action == SceneVisibilityAction::SHOW};
switch (_action) {
case SceneVisibilityAction::SHOW:
obs_scene_enum_items(scene, visibilityEnum, &vInfo);
break;
case SceneVisibilityAction::HIDE:
obs_scene_enum_items(scene, visibilityEnum, &vInfo);
break;
default:
break;
}
obs_source_release(s);
return true;
}
void MacroActionSceneVisibility::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed action \"%s\" for source \"%s\" on scene \"%s\"",
it->second.c_str(), GetWeakSourceName(_scene).c_str(),
GetWeakSourceName(_scene).c_str());
} else {
blog(LOG_WARNING, "ignored unknown SceneVisibility action %d",
static_cast<int>(_action));
}
}
bool MacroActionSceneVisibility::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "scene", GetWeakSourceName(_scene).c_str());
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionSceneVisibility::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *sceneName = obs_data_get_string(obj, "scene");
_scene = GetWeakSourceByName(sceneName);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_action = static_cast<SceneVisibilityAction>(
obs_data_get_int(obj, "action"));
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
std::set<QString> *names = reinterpret_cast<std::set<QString> *>(ptr);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, enumItem, ptr);
}
auto name = obs_source_get_name(obs_sceneitem_get_source(item));
names->emplace(name);
return true;
}
static inline void populateSceneItems(QComboBox *list,
OBSWeakSource sceneWeakSource = nullptr)
{
std::set<QString> names;
auto s = obs_weak_source_get_source(sceneWeakSource);
auto scene = obs_scene_from_source(s);
obs_scene_enum_items(scene, enumItem, &names);
obs_source_release(s);
for (auto &name : names) {
list->addItem(name);
}
list->model()->sort(0);
addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem"));
list->setCurrentIndex(0);
}
MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
: QWidget(parent)
{
_scenes = new QComboBox();
_sources = new QComboBox();
_actions = new QComboBox();
populateActionSelection(_actions);
populateSceneSelection(_scenes);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_scenes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SceneChanged(const QString &)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text(
"AdvSceneSwitcher.action.SceneVisibility.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionSceneVisibilityEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_scenes->setCurrentText(GetWeakSourceName(_entryData->_scene).c_str());
populateSceneItems(_sources, _entryData->_scene);
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
}
void MacroActionSceneVisibilityEdit::SceneChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_scene = GetWeakSourceByQString(text);
}
_sources->clear();
populateSceneItems(_sources, _entryData->_scene);
}
void MacroActionSceneVisibilityEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
void MacroActionSceneVisibilityEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<SceneVisibilityAction>(value);
}

201
src/macro-action-source.cpp Normal file
View File

@ -0,0 +1,201 @@
#include "headers/macro-action-source.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionSource::id = "source";
bool MacroActionSource::_registered = MacroActionFactory::Register(
MacroActionSource::id,
{MacroActionSource::Create, MacroActionSourceEdit::Create,
"AdvSceneSwitcher.action.source"});
const static std::map<SourceAction, std::string> actionTypes = {
{SourceAction::ENABLE, "AdvSceneSwitcher.action.source.type.enable"},
{SourceAction::DISABLE, "AdvSceneSwitcher.action.source.type.disable"},
{SourceAction::SETTINGS,
"AdvSceneSwitcher.action.source.type.settings"},
};
void setSourceSettings(obs_source_t *s, const std::string &settings)
{
if (settings.empty()) {
return;
}
obs_data_t *data = obs_data_create_from_json(settings.c_str());
if (!data) {
blog(LOG_WARNING, "invalid source settings provided: \n%s",
settings.c_str());
return;
}
obs_source_update(s, data);
obs_data_release(data);
}
bool MacroActionSource::PerformAction()
{
auto s = obs_weak_source_get_source(_source);
switch (_action) {
case SourceAction::ENABLE:
obs_source_set_enabled(s, true);
break;
case SourceAction::DISABLE:
obs_source_set_enabled(s, false);
break;
case SourceAction::SETTINGS:
setSourceSettings(s, _settings);
break;
default:
break;
}
obs_source_release(s);
return true;
}
void MacroActionSource::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" for Source \"%s\"",
it->second.c_str(), GetWeakSourceName(_source).c_str());
} else {
blog(LOG_WARNING, "ignored unknown source action %d",
static_cast<int>(_action));
}
}
bool MacroActionSource::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_string(obj, "settings", _settings.c_str());
return true;
}
bool MacroActionSource::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_action = static_cast<SourceAction>(obs_data_get_int(obj, "action"));
_settings = obs_data_get_string(obj, "settings");
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionSourceEdit::MacroActionSourceEdit(
QWidget *parent, std::shared_ptr<MacroActionSource> entryData)
: QWidget(parent)
{
_sources = new QComboBox();
_actions = new QComboBox();
_getSettings = new QPushButton(
obs_module_text("AdvSceneSwitcher.action.source.getSettings"));
_settings = new QPlainTextEdit();
_warning = new QLabel(
obs_module_text("AdvSceneSwitcher.action.source.warning"));
populateActionSelection(_actions);
populateSourceSelection(_sources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
SLOT(SettingsChanged()));
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *entryLayout = new QHBoxLayout;
QHBoxLayout *buttonLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources},
{"{{actions}}", _actions},
{"{{settings}}", _settings},
{"{{getSettings}}", _getSettings},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.source.entry"),
entryLayout, widgetPlaceholders);
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_warning);
mainLayout->addWidget(_settings);
buttonLayout->addWidget(_getSettings);
buttonLayout->addStretch();
mainLayout->addLayout(buttonLayout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionSourceEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
SetWidgetVisibility(_entryData->_action == SourceAction::SETTINGS);
}
void MacroActionSourceEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
void MacroActionSourceEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<SourceAction>(value);
SetWidgetVisibility(_entryData->_action == SourceAction::SETTINGS);
}
void MacroActionSourceEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_source) {
return;
}
_settings->setPlainText(
QString::fromStdString(getSourceSettings(_entryData->_source)));
}
void MacroActionSourceEdit::SettingsChanged()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
}
void MacroActionSourceEdit::SetWidgetVisibility(bool showSettings)
{
_settings->setVisible(showSettings);
_getSettings->setVisible(showSettings);
_warning->setVisible(!showSettings);
adjustSize();
}

View File

@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionStream::id = 3;
const std::string MacroActionStream::id = "streaming";
bool MacroActionStream::_registered = MacroActionFactory::Register(
MacroActionStream::id,

View File

@ -4,7 +4,7 @@
#include <random>
const int MacroActionWait::id = 1;
const std::string MacroActionWait::id = "wait";
bool MacroActionWait::_registered = MacroActionFactory::Register(
MacroActionWait::id,

View File

@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionAudio::id = 3;
const std::string MacroConditionAudio::id = "audio";
bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
MacroConditionAudio::id,
@ -11,8 +11,8 @@ bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
"AdvSceneSwitcher.condition.audio"});
static std::map<AudioCondition, std::string> audioConditionTypes = {
{AudioCondition::ABOVE, "AdvSceneSwitcher.ondition.audio.state.above"},
{AudioCondition::BELOW, "AdvSceneSwitcher.ondition.audio.state.below"},
{AudioCondition::ABOVE, "AdvSceneSwitcher.condition.audio.state.above"},
{AudioCondition::BELOW, "AdvSceneSwitcher.condition.audio.state.below"},
};
MacroConditionAudio::~MacroConditionAudio()
@ -35,11 +35,7 @@ bool MacroConditionAudio::CheckCondition()
// Reset for next check
_peak = -std::numeric_limits<float>::infinity();
if (!volumeThresholdreached) {
_duration.Reset();
return false;
}
return _duration.DurationReached();
return volumeThresholdreached;
}
bool MacroConditionAudio::Save(obs_data_t *obj)
@ -49,7 +45,6 @@ bool MacroConditionAudio::Save(obs_data_t *obj)
GetWeakSourceName(_audioSource).c_str());
obs_data_set_int(obj, "volume", _volume);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
_duration.Save(obj);
return true;
}
@ -75,12 +70,9 @@ bool MacroConditionAudio::Load(obs_data_t *obj)
MacroCondition::Load(obj);
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
_audioSource = GetWeakSourceByName(audioSourceName);
_volume = obs_data_get_int(obj, "volume");
_condition =
static_cast<AudioCondition>(obs_data_get_int(obj, "condition"));
_duration.Load(obj);
_volmeter = AddVolmeterToSource(this, _audioSource);
return true;
}
@ -123,7 +115,6 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
_audioSources = new QComboBox();
_condition = new QComboBox();
_volume = new QSpinBox();
_duration = new DurationSelection(parent, false);
_volume->setSuffix("%");
_volume->setMaximum(100);
@ -133,13 +124,11 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
SLOT(VolumeThresholdChanged(int)));
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_audioSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
AdvSceneSwitcher::populateAudioSelection(_audioSources);
populateAudioSelection(_audioSources);
populateConditionSelection(_condition);
QHBoxLayout *switchLayout = new QHBoxLayout;
@ -147,7 +136,7 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
{"{{audioSources}}", _audioSources},
{"{{volume}}", _volume},
{"{{condition}}", _condition},
{"{{duration}}", _duration}};
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.audio.entry"),
switchLayout, widgetPlaceholders);
@ -214,16 +203,6 @@ void MacroConditionAudioEdit::ConditionChanged(int cond)
_entryData->_condition = static_cast<AudioCondition>(cond);
}
void MacroConditionAudioEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionAudioEdit::UpdateEntryData()
{
if (!_entryData) {
@ -234,6 +213,5 @@ void MacroConditionAudioEdit::UpdateEntryData()
GetWeakSourceName(_entryData->_audioSource).c_str());
_volume->setValue(_entryData->_volume);
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_duration->SetDuration(_entryData->_duration);
UpdateVolmeterSource();
}

View File

@ -0,0 +1,194 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-counter.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionCounter::id = "counter";
bool MacroConditionCounter::_registered = MacroConditionFactory::Register(
MacroConditionCounter::id,
{MacroConditionCounter::Create, MacroConditionCounterEdit::Create,
"AdvSceneSwitcher.condition.counter"});
static std::map<CounterCondition, std::string> counterConditionTypes = {
{CounterCondition::BELOW,
"AdvSceneSwitcher.condition.counter.type.below"},
{CounterCondition::ABOVE,
"AdvSceneSwitcher.condition.counter.type.above"},
{CounterCondition::EQUAL,
"AdvSceneSwitcher.condition.counter.type.equal"},
};
bool MacroConditionCounter::CheckCondition()
{
if (!_macro.get()) {
return false;
}
switch (_condition) {
case CounterCondition::BELOW:
return _macro->GetCount() < _count;
case CounterCondition::ABOVE:
return _macro->GetCount() > _count;
case CounterCondition::EQUAL:
return _macro->GetCount() == _count;
default:
break;
}
return false;
}
bool MacroConditionCounter::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
_macro.Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_int(obj, "count", _count);
return true;
}
bool MacroConditionCounter::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_macro.Load(obj);
_condition = static_cast<CounterCondition>(
obs_data_get_int(obj, "condition"));
_count = obs_data_get_int(obj, "count");
return true;
}
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : counterConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionCounterEdit::MacroConditionCounterEdit(
QWidget *parent, std::shared_ptr<MacroConditionCounter> entryData)
: QWidget(parent)
{
_macros = new MacroSelection(parent);
_conditions = new QComboBox();
_count = new QSpinBox();
_currentCount = new QLabel();
_resetCount = new QPushButton(
obs_module_text("AdvSceneSwitcher.condition.counter.reset"));
_count->setMaximum(10000000);
populateConditionSelection(_conditions);
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_count, SIGNAL(valueChanged(int)), this,
SLOT(CountChanged(int)));
QWidget::connect(_resetCount, SIGNAL(clicked()), this,
SLOT(ResetClicked()));
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{macros}}", _macros},
{"{{conditions}}", _conditions},
{"{{count}}", _count},
{"{{currentCount}}", _currentCount},
{"{{resetCount}}", _resetCount},
};
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.counter.entry.line1"),
line1Layout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.counter.entry.line2"),
line2Layout, widgetPlaceholders);
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionCounterEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_macros->SetCurrentMacro(_entryData->_macro.get());
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_count->setValue(_entryData->_count);
ResetTimer();
}
void MacroConditionCounterEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_macro.UpdateRef(text);
ResetTimer();
}
void MacroConditionCounterEdit::CountChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_count = value;
}
void MacroConditionCounterEdit::ConditionChanged(int cond)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<CounterCondition>(cond);
}
void MacroConditionCounterEdit::MacroRemove(const QString &name)
{
UNUSED_PARAMETER(name);
if (_entryData) {
_entryData->_macro.UpdateRef();
}
}
void MacroConditionCounterEdit::ResetClicked()
{
if (_loading || !_entryData || !_entryData->_macro.get()) {
return;
}
_entryData->_macro->ResetCount();
ResetTimer();
}
void MacroConditionCounterEdit::UpdateCount()
{
if (_entryData && _entryData->_macro.get()) {
_currentCount->setText(
QString::number(_entryData->_macro->GetCount()));
} else {
_currentCount->setText("-");
}
}
void MacroConditionCounterEdit::ResetTimer()
{
_timer.reset(new QTimer(this));
connect(_timer.get(), SIGNAL(timeout()), this, SLOT(UpdateCount()));
_timer->start(1000);
}

View File

@ -1,8 +1,10 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-scene.hpp"
std::map<int, MacroConditionInfo> MacroConditionFactory::_methods;
std::map<std::string, MacroConditionInfo> MacroConditionFactory::_methods;
bool MacroConditionFactory::Register(int id, MacroConditionInfo info)
bool MacroConditionFactory::Register(const std::string &id,
MacroConditionInfo info)
{
if (auto it = _methods.find(id); it == _methods.end()) {
_methods[id] = info;
@ -11,24 +13,51 @@ bool MacroConditionFactory::Register(int id, MacroConditionInfo info)
return false;
}
std::shared_ptr<MacroCondition> MacroConditionFactory::Create(const int id)
std::shared_ptr<MacroCondition>
MacroConditionFactory::Create(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end())
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._createFunc();
}
return nullptr;
}
QWidget *
MacroConditionFactory::CreateWidget(const int id, QWidget *parent,
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
if (auto it = _methods.find(id); it != _methods.end())
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._createWidgetFunc(parent, cond);
}
return nullptr;
}
std::string MacroConditionFactory::GetConditionName(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._name;
}
return "unknown condition";
}
std::string MacroConditionFactory::GetIdByName(const QString &name)
{
for (auto it : _methods) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
bool MacroConditionFactory::UsesDurationConstraint(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._useDurationConstraint;
}
return false;
}
static inline void populateLogicSelection(QComboBox *list, bool root = false)
{
if (root) {
@ -57,24 +86,34 @@ static inline void populateConditionSelection(QComboBox *list)
}
MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData, int type,
bool root, bool startCollapsed)
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root, bool startCollapsed)
: QWidget(parent)
{
_logicSelection = new QComboBox();
_conditionSelection = new QComboBox();
_section = new Section(300);
_dur = new DurationConstraintEdit();
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(LogicSelectionChanged(int)));
QWidget::connect(_conditionSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(ConditionSelectionChanged(int)));
QWidget::connect(_conditionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ConditionSelectionChanged(const QString &)));
QWidget::connect(_dur, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_dur, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
QWidget::connect(_dur, SIGNAL(ConditionChanged(DurationCondition)),
this,
SLOT(DurationConditionChanged(DurationCondition)));
populateLogicSelection(_logicSelection, root);
populateConditionSelection(_conditionSelection);
_section->AddHeaderWidget(_logicSelection);
_section->AddHeaderWidget(_conditionSelection);
_section->AddHeaderWidget(_dur);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(_section);
@ -82,9 +121,8 @@ MacroConditionEdit::MacroConditionEdit(
_entryData = entryData;
_isRoot = root;
UpdateEntryData(type);
UpdateEntryData(id, startCollapsed);
_loading = false;
_section->Collapse(startCollapsed);
}
void MacroConditionEdit::LogicSelectionChanged(int idx)
@ -109,11 +147,12 @@ bool MacroConditionEdit::IsRootNode()
return _isRoot;
}
void MacroConditionEdit::UpdateEntryData(int type)
void MacroConditionEdit::UpdateEntryData(const std::string &id, bool collapse)
{
_conditionSelection->setCurrentIndex(type);
auto widget = MacroConditionFactory::CreateWidget(type, window(),
*_entryData);
_conditionSelection->setCurrentText(obs_module_text(
MacroConditionFactory::GetConditionName(id).c_str()));
auto widget =
MacroConditionFactory::CreateWidget(id, window(), *_entryData);
auto logic = (*_entryData)->GetLogicType();
if (IsRootNode()) {
_logicSelection->setCurrentIndex(static_cast<int>(logic));
@ -121,29 +160,63 @@ void MacroConditionEdit::UpdateEntryData(int type)
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
logic_root_offset);
}
_section->SetContent(widget);
_section->SetContent(widget, collapse);
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
auto constraint = (*_entryData)->GetDurationConstraint();
_dur->SetValue(constraint);
}
void MacroConditionEdit::Collapse(bool collapsed)
void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
{
_section->Collapse(collapsed);
if (_loading || !_entryData) {
return;
}
std::string id = MacroConditionFactory::GetIdByName(text);
auto temp = DurationConstraint();
_dur->SetValue(temp);
std::lock_guard<std::mutex> lock(switcher->m);
auto logic = (*_entryData)->GetLogicType();
_entryData->reset();
*_entryData = MacroConditionFactory::Create(id);
(*_entryData)->SetLogicType(logic);
auto widget =
MacroConditionFactory::CreateWidget(id, window(), *_entryData);
_section->SetContent(widget, false);
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
}
void MacroConditionEdit::ConditionSelectionChanged(int idx)
void MacroConditionEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
auto logic = (*_entryData)->GetLogicType();
_entryData->reset();
*_entryData = MacroConditionFactory::Create(idx);
(*_entryData)->SetLogicType(logic);
auto widget =
MacroConditionFactory::CreateWidget(idx, window(), *_entryData);
_section->SetContent(widget);
_section->Collapse(false);
(*_entryData)->SetDuration(seconds);
}
void MacroConditionEdit::DurationConditionChanged(DurationCondition cond)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
(*_entryData)->SetDurationCondition(cond);
}
void MacroConditionEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
(*_entryData)->SetDurationUnit(unit);
}
void AdvSceneSwitcher::on_conditionAdd_clicked()
@ -152,17 +225,18 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
if (!macro) {
return;
}
MacroConditionScene temp;
std::string id = temp.GetId();
std::lock_guard<std::mutex> lock(switcher->m);
bool root = macro->Conditions().size() == 0;
macro->Conditions().emplace_back(MacroConditionFactory::Create(0));
macro->Conditions().emplace_back(MacroConditionFactory::Create(id));
auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE;
macro->Conditions().back()->SetLogicType(logic);
auto newEntry = new MacroConditionEdit(
this, &macro->Conditions().back(), 0, root);
this, &macro->Conditions().back(), id, root, false);
ui->macroEditConditionLayout->addWidget(newEntry);
ui->macroEditConditionHelp->setVisible(false);
newEntry->Collapse(false);
}
void AdvSceneSwitcher::on_conditionRemove_clicked()

View File

@ -7,7 +7,7 @@
#include <QTextStream>
#include <QFileDialog>
const int MacroConditionFile::id = 4;
const std::string MacroConditionFile::id = "file";
bool MacroConditionFile::_registered = MacroConditionFactory::Register(
MacroConditionFile::id,

View File

@ -3,12 +3,12 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionIdle::id = 10;
const std::string MacroConditionIdle::id = "idle";
bool MacroConditionIdle::_registered = MacroConditionFactory::Register(
MacroConditionIdle::id,
{MacroConditionIdle::Create, MacroConditionIdleEdit::Create,
"AdvSceneSwitcher.condition.idle"});
"AdvSceneSwitcher.condition.idle", false});
bool MacroConditionIdle::CheckCondition()
{

View File

@ -0,0 +1,88 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-interval.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionInterval::id = "interval";
bool MacroConditionInterval::_registered = MacroConditionFactory::Register(
MacroConditionInterval::id,
{MacroConditionInterval::Create, MacroConditionIntervalEdit::Create,
"AdvSceneSwitcher.condition.interval", false});
bool MacroConditionInterval::CheckCondition()
{
if (_duration.DurationReached()) {
_duration.Reset();
return true;
}
return false;
}
bool MacroConditionInterval::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
_duration.Save(obj);
return true;
}
bool MacroConditionInterval::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_duration.Load(obj);
return true;
}
MacroConditionIntervalEdit::MacroConditionIntervalEdit(
QWidget *parent, std::shared_ptr<MacroConditionInterval> entryData)
: QWidget(parent)
{
_duration = new DurationSelection();
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{duration}}", _duration},
};
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.interval.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionIntervalEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionIntervalEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.displayUnit = unit;
}
void MacroConditionIntervalEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_duration->SetDuration(_entryData->_duration);
}

View File

@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionMedia::id = 5;
const std::string MacroConditionMedia::id = "media";
bool MacroConditionMedia::_registered = MacroConditionFactory::Register(
MacroConditionMedia::id,
@ -256,7 +256,7 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
QWidget::connect(_time, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(TimeUnitChanged(DurationUnit)));
AdvSceneSwitcher::populateMediaSelection(_mediaSources);
populateMediaSelection(_mediaSources);
populateMediaStates(_states);
populateMediaTimeRestrictions(_timeRestrictions);

View File

@ -3,13 +3,13 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionPluginState::id = 11;
const std::string MacroConditionPluginState::id = "plugin_state";
bool MacroConditionPluginState::_registered = MacroConditionFactory::Register(
MacroConditionPluginState::id,
{MacroConditionPluginState::Create,
MacroConditionPluginStateEdit::Create,
"AdvSceneSwitcher.condition.pluginState"});
"AdvSceneSwitcher.condition.pluginState", false});
static std::map<PluginStateCondition, std::string> pluginStateConditionTypes = {
{PluginStateCondition::SCENESWITCHED,

View File

@ -5,7 +5,7 @@
#include <regex>
const int MacroConditionProcess::id = 9;
const std::string MacroConditionProcess::id = "process";
bool MacroConditionProcess::_registered = MacroConditionFactory::Register(
MacroConditionProcess::id,
@ -61,7 +61,7 @@ MacroConditionProcessEdit::MacroConditionProcessEdit(
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateProcessSelection(_processSelection);
populateProcessSelection(_processSelection);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{processes}}", _processSelection},

View File

@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionRecord::id = 8;
const std::string MacroConditionRecord::id = "recording";
bool MacroConditionRecord::_registered = MacroConditionFactory::Register(
MacroConditionRecord::id,
@ -32,20 +32,13 @@ bool MacroConditionRecord::CheckCondition()
default:
break;
}
if (!stateMatch) {
_duration.Reset();
return false;
}
return _duration.DurationReached();
return stateMatch;
}
bool MacroConditionRecord::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "state", static_cast<int>(_recordState));
_duration.Save(obj);
return true;
}
@ -53,7 +46,6 @@ bool MacroConditionRecord::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_recordState = static_cast<RecordState>(obs_data_get_int(obj, "state"));
_duration.Load(obj);
return true;
}
@ -69,14 +61,9 @@ MacroConditionRecordEdit::MacroConditionRecordEdit(
: QWidget(parent)
{
_recordState = new QComboBox();
_duration = new DurationSelection();
QWidget::connect(_recordState, SIGNAL(currentIndexChanged(int)), this,
SLOT(StateChanged(int)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
populateStateSelection(_recordState);
@ -84,7 +71,6 @@ MacroConditionRecordEdit::MacroConditionRecordEdit(
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{recordState}}", _recordState},
{"{{duration}}", _duration},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.record.entry"),
@ -106,26 +92,6 @@ void MacroConditionRecordEdit::StateChanged(int value)
_entryData->_recordState = static_cast<RecordState>(value);
}
void MacroConditionRecordEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionRecordEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.displayUnit = unit;
}
void MacroConditionRecordEdit::UpdateEntryData()
{
if (!_entryData) {
@ -134,5 +100,4 @@ void MacroConditionRecordEdit::UpdateEntryData()
_recordState->setCurrentIndex(
static_cast<int>(_entryData->_recordState));
_duration->SetDuration(_entryData->_duration);
}

View File

@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionRegion::id = 2;
const std::string MacroConditionRegion::id = "region";
bool MacroConditionRegion::_registered = MacroConditionFactory::Register(
MacroConditionRegion::id,

View File

@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionScene::id = 0;
const std::string MacroConditionScene::id = "scene";
bool MacroConditionScene::_registered = MacroConditionFactory::Register(
MacroConditionScene::id,
@ -29,11 +29,7 @@ bool MacroConditionScene::CheckCondition()
sceneMatch = switcher->previousScene == _scene;
}
if (!sceneMatch) {
_duration.Reset();
return false;
}
return _duration.DurationReached();
return sceneMatch;
}
bool MacroConditionScene::Save(obs_data_t *obj)
@ -41,7 +37,6 @@ bool MacroConditionScene::Save(obs_data_t *obj)
MacroCondition::Save(obj);
obs_data_set_string(obj, "scene", GetWeakSourceName(_scene).c_str());
obs_data_set_int(obj, "type", static_cast<int>(_type));
_duration.Save(obj);
return true;
}
@ -50,7 +45,6 @@ bool MacroConditionScene::Load(obs_data_t *obj)
MacroCondition::Load(obj);
_scene = GetWeakSourceByName(obs_data_get_string(obj, "scene"));
_type = static_cast<SceneType>(obs_data_get_int(obj, "type"));
_duration.Load(obj);
return true;
}
@ -67,26 +61,20 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
{
_sceneSelection = new QComboBox();
_sceneType = new QComboBox();
_duration = new DurationSelection();
QWidget::connect(_sceneSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SceneChanged(const QString &)));
QWidget::connect(_sceneType, SIGNAL(currentIndexChanged(int)), this,
SLOT(TypeChanged(int)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
AdvSceneSwitcher::populateSceneSelection(_sceneSelection);
populateSceneSelection(_sceneSelection);
populateTypeSelection(_sceneType);
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _sceneSelection},
{"{{sceneType}}", _sceneType},
{"{{duration}}", _duration},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.scene.entry"),
mainLayout, widgetPlaceholders);
@ -117,26 +105,6 @@ void MacroConditionSceneEdit::TypeChanged(int value)
_entryData->_type = static_cast<SceneType>(value);
}
void MacroConditionSceneEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionSceneEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.displayUnit = unit;
}
void MacroConditionSceneEdit::UpdateEntryData()
{
if (!_entryData) {
@ -146,5 +114,4 @@ void MacroConditionSceneEdit::UpdateEntryData()
_sceneSelection->setCurrentText(
GetWeakSourceName(_entryData->_scene).c_str());
_sceneType->setCurrentIndex(static_cast<int>(_entryData->_type));
_duration->SetDuration(_entryData->_duration);
}

View File

@ -0,0 +1,223 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-source.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include <regex>
const std::string MacroConditionSource::id = "source";
bool MacroConditionSource::_registered = MacroConditionFactory::Register(
MacroConditionSource::id,
{MacroConditionSource::Create, MacroConditionSourceEdit::Create,
"AdvSceneSwitcher.condition.source"});
static std::map<SourceCondition, std::string> sourceConditionTypes = {
{SourceCondition::ACTIVE,
"AdvSceneSwitcher.condition.source.type.active"},
{SourceCondition::SHOWING,
"AdvSceneSwitcher.condition.source.type.showing"},
{SourceCondition::SETTINGS,
"AdvSceneSwitcher.condition.source.type.settings"},
};
bool checkSettings(const OBSWeakSource &source, const std::string &settings,
bool useRegex)
{
bool ret = false;
std::string currentSettings = getSourceSettings(source);
if (useRegex) {
try {
std::regex expr(settings);
ret = std::regex_match(currentSettings, expr);
} catch (const std::regex_error &) {
}
} else {
ret = currentSettings == settings;
}
return ret;
}
bool MacroConditionSource::CheckCondition()
{
if (!_source) {
return false;
}
bool ret = false;
auto s = obs_weak_source_get_source(_source);
switch (_condition) {
case SourceCondition::ACTIVE:
ret = obs_source_active(s);
break;
case SourceCondition::SHOWING:
ret = obs_source_showing(s);
break;
case SourceCondition::SETTINGS:
ret = checkSettings(_source, _settings, _regex);
break;
default:
break;
}
obs_source_release(s);
return ret;
}
bool MacroConditionSource::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_string(obj, "settings", _settings.c_str());
return true;
}
bool MacroConditionSource::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_condition = static_cast<SourceCondition>(
obs_data_get_int(obj, "condition"));
_settings = obs_data_get_string(obj, "settings");
return true;
}
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : sourceConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionSourceEdit::MacroConditionSourceEdit(
QWidget *parent, std::shared_ptr<MacroConditionSource> entryData)
: QWidget(parent)
{
_sources = new QComboBox();
_conditions = new QComboBox();
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.source.getSettings"));
_settings = new QPlainTextEdit();
_regex = new QCheckBox(
obs_module_text("AdvSceneSwitcher.condition.source.regex"));
populateConditionSelection(_conditions);
populateSourceSelection(_sources);
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
SLOT(SettingsChanged()));
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
SLOT(RegexChanged(int)));
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
QHBoxLayout *line3Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources}, {"{{conditions}}", _conditions},
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
{"{{regex}}", _regex},
};
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.source.entry.line1"),
line1Layout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.source.entry.line2"),
line2Layout, widgetPlaceholders, false);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.source.entry.line3"),
line3Layout, widgetPlaceholders);
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
mainLayout->addLayout(line3Layout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionSourceEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
void MacroConditionSourceEdit::ConditionChanged(int index)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<SourceCondition>(index);
SetSettingsSelectionVisible(_entryData->_condition ==
SourceCondition::SETTINGS);
}
void MacroConditionSourceEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_source) {
return;
}
_settings->setPlainText(
QString::fromStdString(getSourceSettings(_entryData->_source)));
}
void MacroConditionSourceEdit::SettingsChanged()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
}
void MacroConditionSourceEdit::RegexChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_regex = state;
}
void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible)
{
_settings->setVisible(visible);
_getSettings->setVisible(visible);
_regex->setVisible(visible);
adjustSize();
}
void MacroConditionSourceEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
_regex->setChecked(_entryData->_regex);
SetSettingsSelectionVisible(_entryData->_condition ==
SourceCondition::SETTINGS);
}

View File

@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionStream::id = 7;
const std::string MacroConditionStream::id = "streaming";
bool MacroConditionStream::_registered = MacroConditionFactory::Register(
MacroConditionStream::id,
@ -28,20 +28,13 @@ bool MacroConditionStream::CheckCondition()
default:
break;
}
if (!stateMatch) {
_duration.Reset();
return false;
}
return _duration.DurationReached();
return stateMatch;
}
bool MacroConditionStream::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "state", static_cast<int>(_streamState));
_duration.Save(obj);
return true;
}
@ -49,7 +42,6 @@ bool MacroConditionStream::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_streamState = static_cast<StreamState>(obs_data_get_int(obj, "state"));
_duration.Load(obj);
return true;
}
@ -65,14 +57,9 @@ MacroConditionStreamEdit::MacroConditionStreamEdit(
: QWidget(parent)
{
_streamState = new QComboBox();
_duration = new DurationSelection();
QWidget::connect(_streamState, SIGNAL(currentIndexChanged(int)), this,
SLOT(StateChanged(int)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
populateStateSelection(_streamState);
@ -80,7 +67,6 @@ MacroConditionStreamEdit::MacroConditionStreamEdit(
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{streamState}}", _streamState},
{"{{duration}}", _duration},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.stream.entry"),
@ -102,26 +88,6 @@ void MacroConditionStreamEdit::StateChanged(int value)
_entryData->_streamState = static_cast<StreamState>(value);
}
void MacroConditionStreamEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionStreamEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.displayUnit = unit;
}
void MacroConditionStreamEdit::UpdateEntryData()
{
if (!_entryData) {
@ -130,5 +96,4 @@ void MacroConditionStreamEdit::UpdateEntryData()
_streamState->setCurrentIndex(
static_cast<int>(_entryData->_streamState));
_duration->SetDuration(_entryData->_duration);
}

View File

@ -8,7 +8,7 @@
#include <QToolTip>
#include <QMessageBox>
const int MacroConditionVideo::id = 6;
const std::string MacroConditionVideo::id = "video";
bool MacroConditionVideo::_registered = MacroConditionFactory::Register(
MacroConditionVideo::id,
@ -39,15 +39,7 @@ bool MacroConditionVideo::CheckCondition()
if (_screenshotData) {
if (_screenshotData->done) {
bool imageMatch = Compare();
if (!imageMatch) {
_duration.Reset();
}
if (imageMatch && _duration.DurationReached()) {
match = true;
}
match = Compare();
if (!requiresFileInput(_condition)) {
_matchImage = std::move(_screenshotData->image);
@ -66,7 +58,6 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
obs_data_set_string(obj, "videoSource",
GetWeakSourceName(_videoSource).c_str());
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
_duration.Save(obj);
obs_data_set_string(obj, "filePath", _file.c_str());
return true;
}
@ -77,7 +68,6 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
_videoSource = GetWeakSourceByName(videoSourceName);
_condition =
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
_duration.Load(obj);
_file = obs_data_get_string(obj, "filePath");
if (requiresFileInput(_condition)) {
@ -137,7 +127,6 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
{
_videoSelection = new QComboBox();
_condition = new QComboBox();
_duration = new DurationSelection();
_filePath = new QLineEdit();
_browseButton =
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
@ -151,23 +140,18 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
SLOT(SourceChanged(const QString &)));
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
SLOT(FilePathChanged()));
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
SLOT(BrowseButtonClicked()));
AdvSceneSwitcher::populateVideoSelection(_videoSelection, false);
populateVideoSelection(_videoSelection);
populateConditionSelection(_condition);
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{videoSources}}", _videoSelection},
{"{{condition}}", _condition},
{"{{duration}}", _duration},
{"{{filePath}}", _filePath},
{"{{browseButton}}", _browseButton},
};
@ -324,26 +308,6 @@ void MacroConditionVideoEdit::BrowseButtonClicked()
SetFilePath(path);
}
void MacroConditionVideoEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionVideoEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.displayUnit = unit;
}
void MacroConditionVideoEdit::UpdateEntryData()
{
if (!_entryData) {
@ -353,7 +317,6 @@ void MacroConditionVideoEdit::UpdateEntryData()
_videoSelection->setCurrentText(
GetWeakSourceName(_entryData->_videoSource).c_str());
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_duration->SetDuration(_entryData->_duration);
_filePath->setText(QString::fromStdString(_entryData->_file));
if (!requiresFileInput(_entryData->_condition)) {

View File

@ -5,7 +5,7 @@
#include <regex>
const int MacroConditionWindow::id = 1;
const std::string MacroConditionWindow::id = "window";
bool MacroConditionWindow::_registered = MacroConditionFactory::Register(
MacroConditionWindow::id,
@ -83,12 +83,7 @@ bool MacroConditionWindow::Load(obs_data_t *obj)
MacroCondition::Load(obj);
_window = obs_data_get_string(obj, "window");
_fullscreen = obs_data_get_bool(obj, "fullscreen");
#if __APPLE__
// TODO: Implement maximized check on MacOS
_maximized = false;
#else
_maximized = obs_data_get_bool(obj, "maximized");
#endif
_focus = obs_data_get_bool(obj, "focus");
return true;
}
@ -115,7 +110,7 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateWindowSelection(_windowSelection);
populateWindowSelection(_windowSelection);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{windows}}", _windowSelection},

68
src/macro-selection.cpp Normal file
View File

@ -0,0 +1,68 @@
#include "headers/macro-selection.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include <QStandardItemModel>
MacroSelection::MacroSelection(QWidget *parent) : QComboBox(parent)
{
addItem(obs_module_text("AdvSceneSwitcher.selectMacro"));
QStandardItemModel *model =
qobject_cast<QStandardItemModel *>(this->model());
QModelIndex firstIndex =
model->index(0, modelColumn(), rootModelIndex());
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
firstItem->setSelectable(false);
firstItem->setEnabled(false);
for (auto &m : switcher->macros) {
addItem(QString::fromStdString(m.Name()));
}
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
SLOT(MacroAdd(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
QWidget::connect(parent,
SIGNAL(MacroRenamed(const QString &, const QString &)),
this,
SLOT(MacroRename(const QString &, const QString &)));
}
void MacroSelection::MacroAdd(const QString &name)
{
addItem(name);
}
void MacroSelection::SetCurrentMacro(Macro *m)
{
if (!m) {
this->setCurrentIndex(0);
} else {
this->setCurrentText(QString::fromStdString(m->Name()));
}
}
void MacroSelection::MacroRemove(const QString &name)
{
int idx = findText(name);
if (idx == -1) {
return;
}
removeItem(idx);
setCurrentIndex(0);
}
void MacroSelection::MacroRename(const QString &oldName, const QString &newName)
{
bool renameSelected = currentText() == oldName;
int idx = findText(oldName);
if (idx == -1) {
return;
}
removeItem(idx);
insertItem(idx, newName);
if (renameSelected) {
setCurrentIndex(findText(newName));
}
}

View File

@ -9,17 +9,11 @@
static QMetaObject::Connection addPulse;
const auto conditionsCollapseThreshold = 4;
const auto actionsCollapseThreshold = 2;
const auto actionsCollapseThreshold = 4;
bool macroNameExists(std::string name)
{
for (auto &m : switcher->macros) {
if (m.Name() == name) {
return true;
}
}
return false;
return !!GetMacroByName(name.c_str());
}
bool AdvSceneSwitcher::addNewMacro(std::string &name)
@ -69,10 +63,14 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Checked);
ui->macros->setCurrentItem(item);
ui->macroAdd->disconnect(addPulse);
ui->macroHelp->setVisible(false);
emit MacroAdded(QString::fromStdString(name));
}
void AdvSceneSwitcher::on_macroRemove_clicked()
@ -81,9 +79,11 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
if (!item) {
return;
}
QString name;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->macros->currentRow();
QString::fromStdString(switcher->macros[idx].Name());
switcher->macros.erase(switcher->macros.begin() + idx);
}
@ -92,6 +92,7 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
if (ui->macros->count() == 0) {
ui->macroHelp->setVisible(true);
}
emit MacroRemoved(name);
}
void AdvSceneSwitcher::on_macroUp_clicked()
@ -147,12 +148,17 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
if (nameValid) {
macro->SetName(newName.toUtf8().constData());
QListWidgetItem *item = ui->macros->currentItem();
item->setData(Qt::UserRole, newName);
// Don't trigger itemChanged()
// pause state remains as is
ui->macros->blockSignals(true);
item->setText(newName);
ui->macros->blockSignals(false);
} else {
ui->macroName->setText(oldName);
}
}
emit MacroRenamed(oldName, newName);
}
void AdvSceneSwitcher::SetEditMacro(Macro &m)
@ -196,22 +202,14 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m)
Macro *AdvSceneSwitcher::getSelectedMacro()
{
Macro *macro = nullptr;
QListWidgetItem *item = ui->macros->currentItem();
if (!item) {
return macro;
return nullptr;
}
QString name = item->data(Qt::UserRole).toString();
for (auto &m : switcher->macros) {
if (name.compare(m.Name().c_str()) == 0) {
macro = &m;
break;
}
}
return macro;
QString name = item->text();
return GetMacroByQString(name);
}
void AdvSceneSwitcher::on_macros_currentRowChanged(int idx)
@ -226,13 +224,26 @@ void AdvSceneSwitcher::on_macros_currentRowChanged(int idx)
}
QListWidgetItem *item = ui->macros->item(idx);
QString macroName = item->data(Qt::UserRole).toString();
QString macroName = item->text();
for (auto &m : switcher->macros) {
if (macroName.compare(m.Name().c_str()) == 0) {
SetEditMacro(m);
break;
}
auto macro = GetMacroByQString(macroName);
if (macro) {
SetEditMacro(*macro);
}
}
void AdvSceneSwitcher::on_macros_itemChanged(QListWidgetItem *item)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
QString name = item->text();
auto m = GetMacroByQString(name);
if (m) {
m->SetPaused(item->checkState() != Qt::Checked);
}
}
@ -243,10 +254,18 @@ void AdvSceneSwitcher::setupMacroTab()
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
if (m.Paused()) {
item->setCheckState(Qt::Unchecked);
} else {
item->setCheckState(Qt::Checked);
}
}
if (switcher->macros.size() == 0) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
}
ui->macroHelp->setVisible(true);
} else {
ui->macroHelp->setVisible(false);
@ -286,5 +305,7 @@ void AdvSceneSwitcher::copyMacro()
QString text = QString::fromStdString(name);
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Checked);
ui->macros->setCurrentItem(item);
}

View File

@ -2,7 +2,10 @@
#include "headers/macro-action-edit.hpp"
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-action-switch-scene.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include <limits>
#undef max
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
@ -14,15 +17,26 @@ const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
};
Macro::Macro(std::string name) : _name(name) {}
Macro::Macro(const std::string &name)
{
SetupHotkeys();
SetName(name);
}
Macro::~Macro() {}
Macro::~Macro()
{
ClearHotkeys();
}
bool Macro::CeckMatch()
{
_matched = false;
for (auto &c : _conditions) {
bool cond = c->CheckCondition();
if (!cond) {
c->ResetDuration();
}
cond = cond && c->DurationReached();
switch (c->GetLogicType()) {
case LogicType::NONE:
@ -55,6 +69,21 @@ bool Macro::CeckMatch()
_name.c_str());
break;
}
vblog(LOG_INFO, "condition %s returned %d", c->GetId().c_str(),
cond);
}
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
// Condition checks shall still be run even if macro is paused.
// Otherwise conditions might behave in unexpected ways when resuming.
//
// For example, audio could immediately match after unpause, when it
// matched before it was paused due to timers not being updated.
if (_paused) {
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
_matched = false;
return false;
}
return _matched;
@ -70,13 +99,29 @@ bool Macro::PerformAction()
return false;
}
}
if (ret && _count != std::numeric_limits<int>::max()) {
_count++;
}
return ret;
}
void Macro::SetName(const std::string &name)
{
_name = name;
SetHotkeysDesc();
}
bool Macro::Save(obs_data_t *obj)
{
obs_data_set_string(obj, "name", _name.c_str());
obs_data_set_bool(obj, "pause", _paused);
obs_data_array_t *pauseHotkey = obs_hotkey_save(_pauseHotkey);
obs_data_set_array(obj, "pauseHotkey", pauseHotkey);
obs_data_array_release(pauseHotkey);
obs_data_array_t *unpauseHotkey = obs_hotkey_save(_unpauseHotkey);
obs_data_set_array(obj, "unpauseHotkey", unpauseHotkey);
obs_data_array_release(unpauseHotkey);
obs_data_array_t *conditions = obs_data_array_create();
for (auto &c : _conditions) {
@ -142,15 +187,27 @@ void setValidLogic(MacroCondition *c, bool root, std::string name)
bool Macro::Load(obs_data_t *obj)
{
_name = obs_data_get_string(obj, "name");
bool root = true;
_paused = obs_data_get_bool(obj, "pause");
obs_data_array_t *pauseHotkey = obs_data_get_array(obj, "pauseHotkey");
obs_hotkey_load(_pauseHotkey, pauseHotkey);
obs_data_array_release(pauseHotkey);
obs_data_array_t *unpauseHotkey =
obs_data_get_array(obj, "unpauseHotkey");
obs_hotkey_load(_unpauseHotkey, unpauseHotkey);
obs_data_array_release(unpauseHotkey);
SetHotkeysDesc();
bool root = true;
obs_data_array_t *conditions = obs_data_get_array(obj, "conditions");
size_t count = obs_data_array_count(conditions);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(conditions, i);
int id = obs_data_get_int(array_obj, "id");
std::string id = obs_data_get_string(array_obj, "id");
auto newEntry = MacroConditionFactory::Create(id);
if (newEntry) {
@ -160,8 +217,8 @@ bool Macro::Load(obs_data_t *obj)
setValidLogic(c, root, _name);
} else {
blog(LOG_WARNING,
"discarding condition entry with unkown id (%d) for macro %s",
id, _name.c_str());
"discarding condition entry with unkown id (%s) for macro %s",
id.c_str(), _name.c_str());
}
obs_data_release(array_obj);
@ -175,7 +232,7 @@ bool Macro::Load(obs_data_t *obj)
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(actions, i);
int id = obs_data_get_int(array_obj, "id");
std::string id = obs_data_get_string(array_obj, "id");
auto newEntry = MacroActionFactory::Create(id);
if (newEntry) {
@ -183,44 +240,154 @@ bool Macro::Load(obs_data_t *obj)
_actions.back()->Load(array_obj);
} else {
blog(LOG_WARNING,
"discarding action entry with unkown id (%d) for macro %s",
id, _name.c_str());
"discarding action entry with unkown id (%s) for macro %s",
id.c_str(), _name.c_str());
}
obs_data_release(array_obj);
}
obs_data_array_release(actions);
return true;
}
void Macro::ResolveMacroRef()
{
for (auto &c : _conditions) {
MacroRefCondition *ref =
dynamic_cast<MacroRefCondition *>(c.get());
if (ref) {
ref->_macro.UpdateRef();
}
}
for (auto &a : _actions) {
MacroRefAction *ref = dynamic_cast<MacroRefAction *>(a.get());
if (ref) {
ref->_macro.UpdateRef();
}
}
}
bool Macro::SwitchesScene()
{
MacroActionSwitchScene temp;
auto sceneSwitchId = temp.GetId();
for (auto &a : _actions) {
if (a->GetId() == temp.GetId()) {
if (a->GetId() == sceneSwitchId) {
return true;
}
}
return false;
}
static void pauseCB(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(hotkey);
if (pressed) {
auto m = static_cast<Macro *>(data);
m->SetPaused(true);
}
}
static void unpauseCB(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(hotkey);
if (pressed) {
auto m = static_cast<Macro *>(data);
m->SetPaused(false);
}
}
static int macroHotkeyID = 0;
void Macro::SetupHotkeys()
{
if (_pauseHotkey != OBS_INVALID_HOTKEY_ID ||
_unpauseHotkey != OBS_INVALID_HOTKEY_ID) {
ClearHotkeys();
}
macroHotkeyID++;
std::string hotkeyName =
"macro_pause_hotkey_" + std::to_string(macroHotkeyID);
QString format{obs_module_text("AdvSceneSwitcher.hotkey.macro.pause")};
QString hotkeyDesc = format.arg(QString::fromStdString(_name));
_pauseHotkey = obs_hotkey_register_frontend(
hotkeyName.c_str(), hotkeyDesc.toStdString().c_str(), pauseCB,
this);
macroHotkeyID++;
hotkeyName = "macro_pause_hotkey_" + _name;
format = {obs_module_text("AdvSceneSwitcher.hotkey.macro.unpause")};
hotkeyDesc = format.arg(QString::fromStdString(_name));
_unpauseHotkey = obs_hotkey_register_frontend(
hotkeyName.c_str(), hotkeyDesc.toStdString().c_str(), unpauseCB,
this);
}
void Macro::ClearHotkeys()
{
obs_hotkey_unregister(_pauseHotkey);
obs_hotkey_unregister(_unpauseHotkey);
}
void Macro::SetHotkeysDesc()
{
QString format{obs_module_text("AdvSceneSwitcher.hotkey.macro.pause")};
QString hotkeyDesc = format.arg(QString::fromStdString(_name));
obs_hotkey_set_description(_pauseHotkey,
hotkeyDesc.toStdString().c_str());
format = {obs_module_text("AdvSceneSwitcher.hotkey.macro.unpause")};
hotkeyDesc = format.arg(QString::fromStdString(_name));
obs_hotkey_set_description(_unpauseHotkey,
hotkeyDesc.toStdString().c_str());
}
bool MacroCondition::Save(obs_data_t *obj)
{
obs_data_set_int(obj, "id", GetId());
obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
_duration.Save(obj);
return true;
}
bool MacroCondition::Load(obs_data_t *obj)
{
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
_duration.Load(obj);
return true;
}
void MacroCondition::SetDurationConstraint(const DurationConstraint &dur)
{
_duration = dur;
}
void MacroCondition::SetDurationCondition(DurationCondition cond)
{
_duration.SetCondition(cond);
}
void MacroCondition::SetDurationUnit(DurationUnit u)
{
_duration.SetUnit(u);
}
void MacroCondition::SetDuration(double seconds)
{
_duration.SetValue(seconds);
}
bool MacroAction::Save(obs_data_t *obj)
{
obs_data_set_int(obj, "id", GetId());
obs_data_set_string(obj, "id", GetId().c_str());
return true;
}
@ -232,7 +399,7 @@ bool MacroAction::Load(obs_data_t *obj)
void MacroAction::LogAction()
{
blog(LOG_INFO, "performed action %d", GetId());
vblog(LOG_INFO, "performed action %s", GetId().c_str());
}
void SwitcherData::saveMacros(obs_data_t *obj)
@ -250,8 +417,71 @@ void SwitcherData::saveMacros(obs_data_t *obj)
obs_data_array_release(macroArray);
}
// Temporary helper functions to convert old settings format to new one
static std::unordered_map<int, std::string> actionIntToActionString = {
{2, "audio"}, {4, "recording"}, {5, "replay_buffer"}, {6, "run"},
{3, "streaming"}, {0, "scene_switch"}, {1, "wait"},
};
static void replaceActionIds(obs_data_t *obj)
{
obs_data_array_t *actions = obs_data_get_array(obj, "actions");
size_t count = obs_data_array_count(actions);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(actions, i);
auto oldId = obs_data_get_int(array_obj, "id");
obs_data_set_string(array_obj, "id",
actionIntToActionString[oldId].c_str());
obs_data_release(array_obj);
}
obs_data_array_release(actions);
}
static std::unordered_map<int, std::string> conditionIntToConditionString = {
{3, "audio"}, {4, "file"}, {10, "idle"}, {5, "media"},
{11, "plugin_state"}, {9, "process"}, {8, "recording"}, {2, "region"},
{0, "scene"}, {7, "streaming"}, {6, "video"}, {1, "window"},
};
static void replaceConditionIds(obs_data_t *obj)
{
obs_data_array_t *conditions = obs_data_get_array(obj, "conditions");
size_t count = obs_data_array_count(conditions);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(conditions, i);
auto oldId = obs_data_get_int(array_obj, "id");
obs_data_set_string(
array_obj, "id",
conditionIntToConditionString[oldId].c_str());
obs_data_release(array_obj);
}
obs_data_array_release(conditions);
}
static void convertOldMacroIdsToString(obs_data_t *obj)
{
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
size_t count = obs_data_array_count(macroArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(macroArray, i);
replaceActionIds(array_obj);
replaceConditionIds(array_obj);
obs_data_release(array_obj);
}
obs_data_array_release(macroArray);
}
void SwitcherData::loadMacros(obs_data_t *obj)
{
// TODO: Remove conversion helper in future version
std::string previousVersion = obs_data_get_string(obj, "version");
if (previousVersion == "2ce0b35921be892c987c7dbb5fc90db38f15f0a6") {
convertOldMacroIdsToString(obj);
}
macros.clear();
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
@ -264,6 +494,10 @@ void SwitcherData::loadMacros(obs_data_t *obj)
obs_data_release(array_obj);
}
obs_data_array_release(macroArray);
for (auto &m : macros) {
m.ResolveMacroRef();
}
}
bool SwitcherData::checkMacros()
@ -286,7 +520,7 @@ bool SwitcherData::runMacros()
{
for (auto &m : macros) {
if (m.Matched()) {
blog(LOG_INFO, "running macro: %s", m.Name().c_str());
vblog(LOG_INFO, "running macro: %s", m.Name().c_str());
if (!m.PerformAction()) {
blog(LOG_WARNING, "abort macro: %s",
m.Name().c_str());
@ -296,3 +530,69 @@ bool SwitcherData::runMacros()
}
return true;
}
Macro *GetMacroByName(const char *name)
{
for (auto &m : switcher->macros) {
if (m.Name() == name) {
return &m;
}
}
return nullptr;
}
Macro *GetMacroByQString(const QString &name)
{
return GetMacroByName(name.toUtf8().constData());
}
MacroRef::MacroRef(std::string name) : _name(name)
{
UpdateRef();
}
void MacroRef::UpdateRef()
{
_ref = GetMacroByName(_name.c_str());
}
void MacroRef::UpdateRef(std::string newName)
{
_name = newName;
UpdateRef();
}
void MacroRef::UpdateRef(QString newName)
{
_name = newName.toStdString();
UpdateRef();
}
void MacroRef::Save(obs_data_t *obj)
{
if (_ref) {
obs_data_set_string(obj, "macro", _ref->Name().c_str());
}
}
void MacroRef::Load(obs_data_t *obj)
{
_name = obs_data_get_string(obj, "macro");
UpdateRef();
}
Macro *MacroRef::get()
{
return _ref;
}
Macro *MacroRef::operator->()
{
return _ref;
}
void MacroRefCondition::ResolveMacroRef()
{
_macro.UpdateRef();
}
void MacroRefAction::ResolveMacroRef()
{
_macro.UpdateRef();
}

View File

@ -17,7 +17,6 @@ void GetWindowList(std::vector<std::string> &windows)
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
[[app objectForKey:@"kCGWindowName"]
@ -30,53 +29,26 @@ void GetWindowList(std::vector<std::string> &windows)
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// Check if name exists
if (!name.empty() &&
find(windows.begin(), windows.end(), name) ==
windows.end())
windows.emplace_back(name);
// Check if owner exists
else if (!owner.empty() &&
find(windows.begin(), windows.end(), owner) ==
windows.end())
if (!owner.empty() &&
find(windows.begin(), windows.end(), owner) ==
windows.end())
windows.emplace_back(owner);
}
}
}
// Overloaded
void GetWindowList(QStringList &windows)
{
windows.clear();
@autoreleasepool {
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
[[app objectForKey:@"kCGWindowName"]
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
std::string owner(
[[app objectForKey:@"kCGWindowOwnerName"]
UTF8String],
[[app objectForKey:@"kCGWindowOwnerName"]
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// Check if name exists
if (!name.empty() &&
!windows.contains(QString::fromStdString(name)))
windows << QString::fromStdString(name);
// Check if owner exists
else if (!owner.empty() &&
!windows.contains(
QString::fromStdString(name)))
windows << QString::fromStdString(owner);
}
std::vector<std::string> temp;
GetWindowList(temp);
for (auto &w : temp) {
windows << QString::fromStdString(w);
}
}
@ -94,7 +66,6 @@ void GetCurrentWindowTitle(std::string &title)
[[app objectForKey:@"kCGWindowLayer"] intValue];
// True if window is frontmost
if (layer == 0) {
// Construct string from NSString accounting for nil
std::string name(
[[app objectForKey:@"kCGWindowName"]
UTF8String],
@ -130,23 +101,68 @@ std::pair<int, int> getCursorPos()
return pos;
}
// TODO:
// not implemented on MacOS as I cannot test it
bool isMaximized(std::string &title)
bool isWindowOriginOnScreen(NSDictionary *app, NSScreen *screen,
bool fullscreen = false)
{
return false;
NSArray *screens = [NSScreen screens];
NSRect mainScreenFrame = [screens[0] frame];
NSRect screenFrame;
if (fullscreen) {
screenFrame = [screen frame];
} else {
screenFrame = [screen visibleFrame];
}
NSRect windowBounds;
CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)[app objectForKey:@"kCGWindowBounds"],
&windowBounds);
return (windowBounds.origin.x == screenFrame.origin.x &&
(mainScreenFrame.size.height - screenFrame.size.height -
windowBounds.origin.y ==
screenFrame.origin.y));
}
bool isFullscreen(std::string &title)
bool isWindowMaximizedOnScreen(NSDictionary *app, NSScreen *screen)
{
double maximizedTolerance = 0.99;
NSRect screenFrame = [screen frame];
NSRect windowBounds;
CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)[app objectForKey:@"kCGWindowBounds"],
&windowBounds);
int sumX = windowBounds.origin.x + windowBounds.size.width;
int sumY = windowBounds.origin.y + windowBounds.size.height;
// Return false if window spans over multiple screens
if (sumX > screenFrame.size.width) {
return false;
}
if (sumY > screenFrame.size.height) {
return false;
}
return ((double)sumX / (double)screenFrame.size.width) >
maximizedTolerance &&
((double)sumY / (double)screenFrame.size.height) >
maximizedTolerance;
}
bool nameMachesPattern(std::string windowName, std::string pattern)
{
return QString::fromStdString(windowName)
.contains(QRegularExpression(QString::fromStdString(pattern)));
}
bool isMaximized(const std::string &title)
{
// Check for match
@autoreleasepool {
NSArray *screens = [NSScreen screens];
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
[[app objectForKey:@"kCGWindowName"]
@ -159,51 +175,69 @@ bool isFullscreen(std::string &title)
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// True if switch equals app
bool equals = (title == name || title == owner);
// True if switch matches app
bool matches = (QString::fromStdString(name).contains(
QRegularExpression(
QString::fromStdString(
title))) ||
QString::fromStdString(owner).contains(
QRegularExpression(
QString::fromStdString(
title))));
// If found, check if fullscreen
bool matches = nameMachesPattern(name, title) ||
nameMachesPattern(owner, title);
if (equals || matches) {
// Get window bounds
NSRect bounds;
CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)[app
objectForKey:@"kCGWindowBounds"],
&bounds);
// Compare to screen bounds
for (NSScreen *screen in screens) {
NSRect frame = [screen visibleFrame];
if (isWindowOriginOnScreen(app,
screen) &&
isWindowMaximizedOnScreen(app,
screen)) {
return true;
}
}
}
}
}
return false;
}
// True if flipped window origin equals screen origin
bool origin =
(bounds.origin.x ==
frame.origin.x &&
([screens[0] visibleFrame]
.size.height -
frame.size.height -
bounds.origin.y ==
frame.origin.y));
// True if window size equals screen size
bool size = NSEqualSizes(bounds.size,
frame.size);
bool isWindowFullscreenOnScreen(NSDictionary *app, NSScreen *screen)
{
NSRect screenFrame = [screen frame];
NSRect windowBounds;
CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)[app objectForKey:@"kCGWindowBounds"],
&windowBounds);
if (origin && size)
return NSEqualSizes(windowBounds.size, screenFrame.size);
}
bool isFullscreen(const std::string &title)
{
@autoreleasepool {
NSArray *screens = [NSScreen screens];
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
for (NSDictionary *app in apps) {
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
[[app objectForKey:@"kCGWindowName"]
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
std::string owner(
[[app objectForKey:@"kCGWindowOwnerName"]
UTF8String],
[[app objectForKey:@"kCGWindowOwnerName"]
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
bool equals = (title == name || title == owner);
bool matches = nameMachesPattern(name, title) ||
nameMachesPattern(owner, title);
if (equals || matches) {
for (NSScreen *screen in screens) {
if (isWindowOriginOnScreen(app, screen,
true) &&
isWindowFullscreenOnScreen(app,
screen))
return true;
}
}
}
}
return false;
}

View File

@ -499,7 +499,10 @@ void AdvSceneSwitcher::setupSceneGroupTab()
}
if (switcher->sceneGroups.size() == 0) {
addPulse = PulseWidget(ui->sceneGroupAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->sceneGroupAdd,
QColor(Qt::green));
}
ui->sceneGroupHelp->setVisible(true);
} else {
ui->sceneGroupHelp->setVisible(false);

View File

@ -347,7 +347,10 @@ void AdvSceneSwitcher::setupTriggerTab()
}
if (switcher->sceneTriggers.size() == 0) {
addPulse = PulseWidget(ui->triggerAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse =
PulseWidget(ui->triggerAdd, QColor(Qt::green));
}
ui->triggerHelp->setVisible(true);
} else {
ui->triggerHelp->setVisible(false);
@ -381,7 +384,7 @@ void SceneTrigger::load(obs_data_t *obj)
static inline void populateTriggers(QComboBox *list)
{
AdvSceneSwitcher::addSelectionEntry(
addSelectionEntry(
list,
obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
@ -396,7 +399,7 @@ static inline void populateTriggers(QComboBox *list)
inline void populateActions(QComboBox *list)
{
AdvSceneSwitcher::addSelectionEntry(
addSelectionEntry(
list,
obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
@ -451,7 +454,7 @@ SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
populateTriggers(triggers);
populateActions(actions);
AdvSceneSwitcher::populateAudioSelection(audioSources);
populateAudioSelection(audioSources);
if (s) {
triggers->setCurrentIndex(static_cast<int>(s->triggerType));

View File

@ -1,7 +1,9 @@
#include <QPropertyAnimation>
#include "headers/section.hpp"
#include "headers/utility.hpp"
#include <QPropertyAnimation>
#include <QEvent>
Section::Section(const int animationDuration, QWidget *parent)
: QWidget(parent), _animationDuration(animationDuration)
{
@ -38,45 +40,74 @@ Section::Section(const int animationDuration, QWidget *parent)
connect(_toggleButton, &QToolButton::toggled, this, &Section::Collapse);
}
void Section::Collapse(bool collapsed)
void Section::Collapse(bool collapse)
{
_toggleButton->setChecked(collapsed);
_toggleButton->setArrowType(!collapsed ? Qt::ArrowType::DownArrow
: Qt::ArrowType::RightArrow);
_toggleAnimation->setDirection(!collapsed
_toggleButton->setChecked(collapse);
_toggleButton->setArrowType(!collapse ? Qt::ArrowType::DownArrow
: Qt::ArrowType::RightArrow);
_toggleAnimation->setDirection(!collapse
? QAbstractAnimation::Forward
: QAbstractAnimation::Backward);
_transitioning = true;
_collapsed = collapse;
_toggleAnimation->start();
}
void Section::SetContent(QWidget *w)
void Section::SetContent(QWidget *w, bool collapsed)
{
// Clean up previous content
if (_contentArea) {
auto oldLayout = _contentArea->layout();
if (oldLayout) {
clearLayout(oldLayout);
delete oldLayout;
}
}
CleanUpPreviousContent();
delete _contentArea;
delete _toggleAnimation;
// Setup contentArea
_contentArea = new QScrollArea(this);
_contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// Start out collapsed
_contentArea->setStyleSheet("QScrollArea { border: none; }");
_contentArea->setMaximumHeight(0);
_contentArea->setMinimumHeight(0);
w->installEventFilter(this);
_content = w;
auto newLayout = new QVBoxLayout();
newLayout->setContentsMargins(0, 0, 0, 0);
newLayout->addWidget(w);
_contentArea->setLayout(newLayout);
_mainLayout->addWidget(_contentArea, 1, 0, 1, 3);
// Animation Setup
_headerHeight = sizeHint().height() - _contentArea->maximumHeight();
_contentHeight = newLayout->sizeHint().height();
if (collapsed) {
this->setMinimumHeight(_headerHeight);
_contentArea->setMaximumHeight(0);
} else {
this->setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
}
SetupAnimations();
Collapse(collapsed);
}
void Section::AddHeaderWidget(QWidget *w)
{
_headerWidgetLayout->addWidget(w);
}
bool Section::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Resize && !_transitioning && !_collapsed) {
_contentHeight = _content->sizeHint().height();
setMaximumHeight(_headerHeight + _contentHeight);
setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
SetupAnimations();
}
return QObject::eventFilter(obj, event);
}
void Section::SetupAnimations()
{
delete _toggleAnimation;
_toggleAnimation = new QParallelAnimationGroup(this);
_toggleAnimation->addAnimation(
new QPropertyAnimation(this, "minimumHeight"));
@ -85,17 +116,13 @@ void Section::SetContent(QWidget *w)
_toggleAnimation->addAnimation(
new QPropertyAnimation(_contentArea, "maximumHeight"));
const auto collapsedHeight =
sizeHint().height() - _contentArea->maximumHeight();
auto contentHeight = newLayout->sizeHint().height();
for (int i = 0; i < _toggleAnimation->animationCount() - 1; ++i) {
QPropertyAnimation *SectionAnimation =
static_cast<QPropertyAnimation *>(
_toggleAnimation->animationAt(i));
SectionAnimation->setDuration(_animationDuration);
SectionAnimation->setStartValue(collapsedHeight);
SectionAnimation->setEndValue(collapsedHeight + contentHeight);
SectionAnimation->setStartValue(_headerHeight);
SectionAnimation->setEndValue(_headerHeight + _contentHeight);
}
QPropertyAnimation *contentAnimation =
@ -103,10 +130,24 @@ void Section::SetContent(QWidget *w)
_toggleAnimation->animationCount() - 1));
contentAnimation->setDuration(_animationDuration);
contentAnimation->setStartValue(0);
contentAnimation->setEndValue(contentHeight);
contentAnimation->setEndValue(_contentHeight);
QWidget::connect(_toggleAnimation, SIGNAL(finished()), this,
SLOT(AnimationFinished()));
}
void Section::AddHeaderWidget(QWidget *w)
void Section::CleanUpPreviousContent()
{
_headerWidgetLayout->addWidget(w);
if (_contentArea) {
auto oldLayout = _contentArea->layout();
if (oldLayout) {
clearLayout(oldLayout);
delete oldLayout;
}
}
}
void Section::AnimationFinished()
{
_transitioning = false;
}

View File

@ -224,7 +224,9 @@ void AdvSceneSwitcher::setupAudioTab()
}
if (switcher->audioSwitches.size() == 0) {
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
}
ui->audioHelp->setVisible(true);
} else {
ui->audioHelp->setVisible(false);
@ -447,7 +449,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
QWidget::connect(ignoreInactiveSource, SIGNAL(stateChanged(int)), this,
SLOT(IgnoreInactiveChanged(int)));
AdvSceneSwitcher::populateAudioSelection(audioSources);
populateAudioSelection(audioSources);
populateConditionSelection(condition);
if (s) {

View File

@ -187,7 +187,10 @@ void AdvSceneSwitcher::setupExecutableTab()
}
if (switcher->executableSwitches.size() == 0) {
addPulse = PulseWidget(ui->executableAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->executableAdd,
QColor(Qt::green));
}
ui->exeHelp->setVisible(true);
} else {
ui->exeHelp->setVisible(false);
@ -223,7 +226,7 @@ ExecutableSwitchWidget::ExecutableSwitchWidget(QWidget *parent,
QWidget::connect(requiresFocus, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateProcessSelection(processes);
populateProcessSelection(processes);
processes->setEditable(true);
processes->setMaxVisibleItems(20);

View File

@ -101,7 +101,7 @@ void SwitcherData::writeSceneInfoToFile()
obs_source_release(currentSource);
}
void SwitcherData::writeToStatusFile(QString msg)
void SwitcherData::writeToStatusFile(const QString &msg)
{
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
return;
@ -415,7 +415,9 @@ void AdvSceneSwitcher::setupFileTab()
}
if (switcher->fileSwitches.size() == 0) {
addPulse = PulseWidget(ui->fileAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->fileAdd, QColor(Qt::green));
}
ui->fileHelp->setVisible(true);
} else {
ui->fileHelp->setVisible(false);

View File

@ -228,10 +228,9 @@ SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
SIGNAL(SceneGroupRenamed(const QString &, const QString &)),
this, SLOT(SceneGroupRename(const QString &, const QString &)));
AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene,
addSceneGroup);
AdvSceneSwitcher::populateTransitionSelection(transitions,
addCurrentTransition);
populateSceneSelection(scenes, usePreviousScene, addSceneGroup,
&switcher->sceneGroups);
populateTransitionSelection(transitions, addCurrentTransition);
switchData = s;
showSwitchData();

View File

@ -244,7 +244,9 @@ void AdvSceneSwitcher::setupMediaTab()
}
if (switcher->mediaSwitches.size() == 0) {
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
}
ui->mediaHelp->setVisible(true);
} else {
ui->mediaHelp->setVisible(false);
@ -458,7 +460,7 @@ MediaSwitchWidget::MediaSwitchWidget(QWidget *parent, MediaSwitch *s)
QWidget::connect(time, SIGNAL(valueChanged(int)), this,
SLOT(TimeChanged(int)));
AdvSceneSwitcher::populateMediaSelection(mediaSources);
populateMediaSelection(mediaSources);
populateMediaStates(states);
populateTimeRestrictions(timeRestrictions);

View File

@ -17,13 +17,16 @@ Most of this code is based on https://github.com/Palakis/obs-websocket
#define PARAM_CLIENT_ENABLE "ClientEnabled"
#define PARAM_CLIENT_PORT "ClientPort"
#define PARAM_ADDRESS "Address"
#define PARAM_CLIENT_SENDALL "SendAll"
#define PARAM_CLIENT_SEND_SCENE_CHANGE "SendSceneChange"
#define PARAM_CLIENT_SEND_SCENE_CHANGE_ALL "SendSceneChangeAll"
#define PARAM_CLIENT_SENDPREVIEW "SendPreview"
#define RECONNECT_DELAY 10
#define SCENE_ENTRY "scene"
#define TRANSITION_ENTRY "transition"
#define TRANSITION_DURATION "duration"
#define SET_PREVIEW "preview"
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
@ -36,7 +39,9 @@ NetworkConfig::NetworkConfig()
ClientEnabled(false),
Address(""),
ClientPort(55555),
SendAll(true)
SendSceneChange(true),
SendSceneChangeAll(true),
SendPreview(true)
{
}
@ -51,7 +56,11 @@ void NetworkConfig::Load(obs_data_t *obj)
ClientEnabled = obs_data_get_bool(obj, PARAM_CLIENT_ENABLE);
Address = obs_data_get_string(obj, PARAM_ADDRESS);
ClientPort = obs_data_get_int(obj, PARAM_CLIENT_PORT);
SendAll = obs_data_get_bool(obj, PARAM_CLIENT_SENDALL);
SendSceneChange =
obs_data_get_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE);
SendSceneChangeAll =
obs_data_get_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL);
SendPreview = obs_data_get_bool(obj, PARAM_CLIENT_SENDPREVIEW);
}
void NetworkConfig::Save(obs_data_t *obj)
@ -63,7 +72,10 @@ void NetworkConfig::Save(obs_data_t *obj)
obs_data_set_bool(obj, PARAM_CLIENT_ENABLE, ClientEnabled);
obs_data_set_string(obj, PARAM_ADDRESS, Address.c_str());
obs_data_set_int(obj, PARAM_CLIENT_PORT, ClientPort);
obs_data_set_bool(obj, PARAM_CLIENT_SENDALL, SendAll);
obs_data_set_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE, SendSceneChange);
obs_data_set_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL,
SendSceneChangeAll);
obs_data_set_bool(obj, PARAM_CLIENT_SENDPREVIEW, SendPreview);
}
void NetworkConfig::SetDefaults(obs_data_t *obj)
@ -75,7 +87,11 @@ void NetworkConfig::SetDefaults(obs_data_t *obj)
obs_data_set_default_bool(obj, PARAM_CLIENT_ENABLE, ClientEnabled);
obs_data_set_default_string(obj, PARAM_ADDRESS, Address.c_str());
obs_data_set_default_int(obj, PARAM_CLIENT_PORT, ClientPort);
obs_data_set_default_bool(obj, PARAM_CLIENT_SENDALL, SendAll);
obs_data_set_default_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE,
SendSceneChange);
obs_data_set_default_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL,
SendSceneChangeAll);
obs_data_set_default_bool(obj, PARAM_CLIENT_SENDPREVIEW, SendPreview);
}
std::string NetworkConfig::GetClientUri()
@ -83,6 +99,21 @@ std::string NetworkConfig::GetClientUri()
return "ws://" + Address + ":" + std::to_string(ClientPort);
}
bool NetworkConfig::ShouldSendSceneChange()
{
return ServerEnabled && SendSceneChange;
}
bool NetworkConfig::ShouldSendFrontendSceneChange()
{
return ShouldSendSceneChange() && SendSceneChangeAll;
}
bool NetworkConfig::ShouldSendPrviewSceneChange()
{
return ServerEnabled && SendPreview;
}
WSServer::WSServer()
: QObject(nullptr), _connections(), _clMutex(QMutex::Recursive)
{
@ -192,7 +223,7 @@ void WSServer::stop()
blog(LOG_INFO, "server stopped successfully");
}
void WSServer::sendMessage(sceneSwitchInfo sceneSwitch)
void WSServer::sendMessage(sceneSwitchInfo sceneSwitch, bool preview)
{
if (!sceneSwitch.scene) {
return;
@ -204,6 +235,7 @@ void WSServer::sendMessage(sceneSwitchInfo sceneSwitch)
obs_data_set_string(data, TRANSITION_ENTRY,
GetWeakSourceName(sceneSwitch.transition).c_str());
obs_data_set_int(data, TRANSITION_DURATION, sceneSwitch.duration);
obs_data_set_bool(data, SET_PREVIEW, preview);
std::string message = obs_data_get_json(data);
obs_data_release(data);
@ -248,7 +280,8 @@ std::string processMessage(std::string payload)
if (!obs_data_has_user_value(data, SCENE_ENTRY) ||
!obs_data_has_user_value(data, TRANSITION_ENTRY) ||
!obs_data_has_user_value(data, TRANSITION_DURATION)) {
!obs_data_has_user_value(data, TRANSITION_DURATION) ||
!obs_data_has_user_value(data, SET_PREVIEW)) {
return "missing request parameters";
}
@ -256,6 +289,7 @@ std::string processMessage(std::string payload)
std::string transitionName =
obs_data_get_string(data, TRANSITION_ENTRY);
int duration = obs_data_get_int(data, TRANSITION_DURATION);
bool preview = obs_data_get_bool(data, SET_PREVIEW);
obs_data_release(data);
@ -271,8 +305,11 @@ std::string processMessage(std::string payload)
ret += " - ignoring invalid transition: '" + transitionName +
"'";
}
switchScene({scene, transition, duration});
if (preview) {
switchPreviewScene(scene);
} else {
switchScene({scene, transition, duration});
}
return ret;
}
@ -478,7 +515,12 @@ void AdvSceneSwitcher::setupNetworkTab()
ui->clientSettings->setChecked(switcher->networkConfig.ClientEnabled);
ui->clientHostname->setText(switcher->networkConfig.Address.c_str());
ui->clientPort->setValue(switcher->networkConfig.ClientPort);
ui->restrictSend->setChecked(!switcher->networkConfig.SendAll);
ui->sendSceneChange->setChecked(
switcher->networkConfig.SendSceneChange);
ui->restrictSend->setChecked(
!switcher->networkConfig.SendSceneChangeAll);
ui->sendPreview->setChecked(switcher->networkConfig.SendPreview);
ui->restrictSend->setDisabled(!switcher->networkConfig.SendSceneChange);
QTimer *statusTimer = new QTimer(this);
connect(statusTimer, SIGNAL(timeout()), this,
@ -592,6 +634,17 @@ void AdvSceneSwitcher::on_clientPort_valueChanged(int value)
switcher->networkConfig.ClientPort = value;
}
void AdvSceneSwitcher::on_sendSceneChange_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendSceneChange = state;
ui->restrictSend->setDisabled(!state);
}
void AdvSceneSwitcher::on_restrictSend_stateChanged(int state)
{
if (loading) {
@ -599,7 +652,17 @@ void AdvSceneSwitcher::on_restrictSend_stateChanged(int state)
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendAll = !state;
switcher->networkConfig.SendSceneChangeAll = !state;
}
void AdvSceneSwitcher::on_sendPreview_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendPreview = state;
}
void AdvSceneSwitcher::on_clientReconnect_clicked()

View File

@ -222,7 +222,6 @@ void SwitcherData::savePauseSwitches(obs_data_t *obj)
obs_data_release(array_obj);
}
obs_data_set_array(obj, "pauseEntries", pauseScenesArray);
obs_data_set_int(obj, "oldPauseValuesImported", 1);
obs_data_array_release(pauseScenesArray);
}
@ -265,7 +264,9 @@ void AdvSceneSwitcher::setupPauseTab()
}
if (switcher->pauseEntries.size() == 0) {
addPulse = PulseWidget(ui->pauseAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->pauseAdd, QColor(Qt::green));
}
ui->pauseHelp->setVisible(true);
} else {
ui->pauseHelp->setVisible(false);
@ -316,7 +317,7 @@ PauseEntryWidget::PauseEntryWidget(QWidget *parent, PauseEntry *s)
populatePauseTypes(pauseTypes);
populatePauseTargets(pauseTargets);
AdvSceneSwitcher::populateWindowSelection(windows);
populateWindowSelection(windows);
windows->setEditable(true);
windows->setMaxVisibleItems(20);

View File

@ -125,15 +125,20 @@ void AdvSceneSwitcher::setupRandomTab()
}
if (switcher->randomSwitches.size() == 0) {
addPulse = PulseWidget(ui->randomAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse =
PulseWidget(ui->randomAdd, QColor(Qt::green));
}
ui->randomHelp->setVisible(true);
} else {
ui->randomHelp->setVisible(false);
}
if (switcher->switchIfNotMatching != RANDOM_SWITCH) {
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
if (!switcher->disableHints) {
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
}
} else {
ui->randomDisabledWarning->setVisible(false);
}

View File

@ -245,7 +245,10 @@ void AdvSceneSwitcher::setupRegionTab()
}
if (switcher->screenRegionSwitches.size() == 0) {
addPulse = PulseWidget(ui->screenRegionAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->screenRegionAdd,
QColor(Qt::green));
}
ui->regionHelp->setVisible(true);
} else {
ui->regionHelp->setVisible(false);
@ -320,8 +323,8 @@ ScreenRegionWidget::ScreenRegionWidget(QWidget *parent, ScreenRegionSwitch *s)
QWidget::connect(maxY, SIGNAL(valueChanged(int)), this,
SLOT(MaxYChanged(int)));
AdvSceneSwitcher::populateSceneSelection(
excludeScenes, false, false, true,
populateSceneSelection(
excludeScenes, false, false, nullptr, true,
obs_module_text(
"AdvSceneSwitcher.screenRegionTab.excludeScenes.None"),
true);

View File

@ -289,7 +289,10 @@ void AdvSceneSwitcher::setupSequenceTab()
}
if (switcher->sceneSequenceSwitches.size() == 0) {
addPulse = PulseWidget(ui->sceneSequenceAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->sceneSequenceAdd,
QColor(Qt::green));
}
ui->sequenceHelp->setVisible(true);
} else {
ui->sequenceHelp->setVisible(false);
@ -440,8 +443,6 @@ void SceneSequenceSwitch::prepareUninterruptibleMatch(
{
int dur = delay.seconds * 1000;
if (dur > 0) {
switcher->waitScene = obs_weak_source_get_source(currentScene);
obs_source_release(switcher->waitScene);
linger = dur;
}
}
@ -588,7 +589,7 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
QWidget::connect(reduce, SIGNAL(clicked()), this,
SLOT(ReduceClicked()));
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
populateSceneSelection(startScenes);
interruptible->setToolTip(obs_module_text(
"AdvSceneSwitcher.sceneSequenceTab.interruptibleHint"));

View File

@ -191,7 +191,9 @@ void AdvSceneSwitcher::setupTimeTab()
}
if (switcher->timeSwitches.size() == 0) {
addPulse = PulseWidget(ui->timeAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->timeAdd, QColor(Qt::green));
}
ui->timeHelp->setVisible(true);
} else {
ui->timeHelp->setVisible(false);

View File

@ -240,7 +240,7 @@ std::pair<obs_weak_source_t *, int> getNextTransition(obs_weak_source_t *scene1,
return std::make_pair(ws, duration);
}
void overwriteTransitionOverride(sceneSwitchInfo ssi, transitionData &td)
void overwriteTransitionOverride(const sceneSwitchInfo &ssi, transitionData &td)
{
obs_source_t *scene = obs_weak_source_get_source(ssi.scene);
obs_data_t *data = obs_source_get_private_settings(scene);
@ -257,7 +257,7 @@ void overwriteTransitionOverride(sceneSwitchInfo ssi, transitionData &td)
obs_source_release(scene);
}
void restoreTransitionOverride(obs_source_t *scene, transitionData td)
void restoreTransitionOverride(obs_source_t *scene, const transitionData &td)
{
obs_data_t *data = obs_source_get_private_settings(scene);
@ -267,7 +267,7 @@ void restoreTransitionOverride(obs_source_t *scene, transitionData td)
obs_data_release(data);
}
void setNextTransition(sceneSwitchInfo &sceneSwitch,
void setNextTransition(const sceneSwitchInfo &sceneSwitch,
obs_source_t *currentSource, transitionData &td)
{
// Priority:
@ -495,7 +495,7 @@ TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent,
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
SLOT(DurationChanged(double)));
AdvSceneSwitcher::populateSceneSelection(scenes2, false);
populateSceneSelection(scenes2);
if (s) {
scenes2->setCurrentText(GetWeakSourceName(s->scene2).c_str());

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