From 8f16d05a28d0bc011a6c8c766b66f329647fb43d Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 16 May 2021 17:09:41 +0200 Subject: [PATCH 01/66] Fix potential crash in audio macro action UpdateEntryData() could be called from ActionChanged() leading to a potential double lock of switcher->m when triggerin other slots of this widget --- src/headers/macro-action-audio.hpp | 1 + src/macro-action-audio.cpp | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/headers/macro-action-audio.hpp b/src/headers/macro-action-audio.hpp index 7f4d60b1..a00d4a0a 100644 --- a/src/headers/macro-action-audio.hpp +++ b/src/headers/macro-action-audio.hpp @@ -37,6 +37,7 @@ public: MacroActionAudioEdit( QWidget *parent, std::shared_ptr entryData = nullptr); + void SetWidgetVisibility(); void UpdateEntryData(); static QWidget *Create(QWidget *parent, std::shared_ptr action) diff --git a/src/macro-action-audio.cpp b/src/macro-action-audio.cpp index a5473845..314c410f 100644 --- a/src/macro-action-audio.cpp +++ b/src/macro-action-audio.cpp @@ -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(_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(_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 lock(switcher->m); _entryData->_action = static_cast(value); - UpdateEntryData(); + SetWidgetVisibility(); } void MacroActionAudioEdit::VolumeChanged(int value) From 2a73583793cbdc839ace0d6d09aa639f4166f101 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 16 May 2021 13:30:27 +0200 Subject: [PATCH 02/66] Fix fullscreen and implement maximized checks --- src/osx/advanced-scene-switcher-osx.mm | 192 +++++++++++++++---------- 1 file changed, 113 insertions(+), 79 deletions(-) diff --git a/src/osx/advanced-scene-switcher-osx.mm b/src/osx/advanced-scene-switcher-osx.mm index 1bb0c578..d5cdebdd 100644 --- a/src/osx/advanced-scene-switcher-osx.mm +++ b/src/osx/advanced-scene-switcher-osx.mm @@ -17,7 +17,6 @@ void GetWindowList(std::vector &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 &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 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 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(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(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; } From c911862d8c365fed1176a4acd1b7353d8a4d7fa5 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 17 May 2021 19:21:35 +0200 Subject: [PATCH 03/66] Add Russian locale --- data/locale/ru-RU.ini | 423 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 423 insertions(+) create mode 100644 data/locale/ru-RU.ini diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini new file mode 100644 index 00000000..f4104ce8 --- /dev/null +++ b/data/locale/ru-RU.ini @@ -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="

Эти настройки только влияют на переходы, вызванные переключателем сцен - Проверьте Transition Table если вы хотите настроить его для ручного изменения сцены.
Настройки, определенные здесь, имеют приоритет над настройками перехода, сконфигурированными в других местах переключателя сцен.

Нажмите на символ плюса ниже, чтобы добавить новую запись.

" +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="

Введите либо прямые заголовки окон, либо правильное регулярное выражение. Вы можете проверить синтаксис и соответствие регулярных выражений, используя RegExr

" +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="

Эта вкладка позволит вам переключать сцены на основе текущего видеовыхода выбранных источников.
Обязательно проверьте Pixel Match Switcher для еще лучшей реализации этой функциональности.

Нажмите на выделенный символ плюса, чтобы продолжить.

" + +; 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="часы" From 74dfd9af3011d5f8becc7347214c776be1405826 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 16 May 2021 15:31:14 +0200 Subject: [PATCH 04/66] Rename --- CMakeLists.txt | 4 ++-- ...-action-switch-scene.hpp => macro-action-scene-switch.hpp} | 0 ...-action-switch-scene.cpp => macro-action-scene-switch.cpp} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/headers/{macro-action-switch-scene.hpp => macro-action-scene-switch.hpp} (100%) rename src/{macro-action-switch-scene.cpp => macro-action-scene-switch.cpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f80e9438..160a3e35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,8 +85,8 @@ set(advanced-scene-switcher_HEADERS 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-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 @@ -141,8 +141,8 @@ set(advanced-scene-switcher_SOURCES 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-streaming.cpp - src/macro-action-switch-scene.cpp src/macro-action-wait.cpp src/macro-condition-edit.cpp src/macro-condition-audio.cpp diff --git a/src/headers/macro-action-switch-scene.hpp b/src/headers/macro-action-scene-switch.hpp similarity index 100% rename from src/headers/macro-action-switch-scene.hpp rename to src/headers/macro-action-scene-switch.hpp diff --git a/src/macro-action-switch-scene.cpp b/src/macro-action-scene-switch.cpp similarity index 100% rename from src/macro-action-switch-scene.cpp rename to src/macro-action-scene-switch.cpp From 305ce96ad0da5891f7225bb1ee127fc104a32fd1 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 16 May 2021 15:33:24 +0200 Subject: [PATCH 05/66] Adjust includes to new file name --- src/macro-action-edit.cpp | 2 +- src/macro-action-scene-switch.cpp | 2 +- src/macro.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/macro-action-edit.cpp b/src/macro-action-edit.cpp index 8cf6eb99..5314e315 100644 --- a/src/macro-action-edit.cpp +++ b/src/macro-action-edit.cpp @@ -1,5 +1,5 @@ #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" diff --git a/src/macro-action-scene-switch.cpp b/src/macro-action-scene-switch.cpp index 2c34bb79..11b7bab0 100644 --- a/src/macro-action-scene-switch.cpp +++ b/src/macro-action-scene-switch.cpp @@ -1,4 +1,4 @@ -#include "headers/macro-action-switch-scene.hpp" +#include "headers/macro-action-scene-switch.hpp" #include "headers/advanced-scene-switcher.hpp" #include "headers/utility.hpp" diff --git a/src/macro.cpp b/src/macro.cpp index e0dda440..b4b7b7d3 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -2,7 +2,7 @@ #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" const std::map MacroCondition::logicTypes = { {LogicType::NONE, {"AdvSceneSwitcher.logic.none"}}, From 4248b86705f2d5317692a3bc9fc8b8d4480d332b Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 16 May 2021 17:04:55 +0200 Subject: [PATCH 06/66] Add macro action to control scene item visibility --- CMakeLists.txt | 2 + data/locale/en-US.ini | 5 + src/headers/macro-action-scene-visibility.hpp | 62 ++++++ src/macro-action-scene-visibility.cpp | 195 ++++++++++++++++++ 4 files changed, 264 insertions(+) create mode 100644 src/headers/macro-action-scene-visibility.hpp create mode 100644 src/macro-action-scene-visibility.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 160a3e35..54524cfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ set(advanced-scene-switcher_HEADERS 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-streaming.hpp src/headers/macro-action-wait.hpp src/headers/macro-condition-edit.hpp @@ -142,6 +143,7 @@ set(advanced-scene-switcher_SOURCES 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-streaming.cpp src/macro-action-wait.cpp src/macro-condition-edit.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 6bf74f3a..bec3c457 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -155,6 +155,10 @@ 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 visibility" +AdvSceneSwitcher.action.sceneVisibility.type.show="Show" +AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide" +AdvSceneSwitcher.action.sceneVisibility.entry="On {{scenes}} {{actions}} {{sources}}" ; Transition Tab @@ -409,6 +413,7 @@ AdvSceneSwitcher.selectAudioSource="--select audio source--" AdvSceneSwitcher.selectVideoSource="--select video source--" AdvSceneSwitcher.selectMediaSource="--select media source--" AdvSceneSwitcher.selectProcess="--select process--" +AdvSceneSwitcher.selectItem="--select item--" AdvSceneSwitcher.enterPath="--enter path--" AdvSceneSwitcher.enterText="--enter text--" AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved" diff --git a/src/headers/macro-action-scene-visibility.hpp b/src/headers/macro-action-scene-visibility.hpp new file mode 100644 index 00000000..773dbb59 --- /dev/null +++ b/src/headers/macro-action-scene-visibility.hpp @@ -0,0 +1,62 @@ +#pragma once +#include +#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); + int GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + OBSWeakSource _scene; + OBSWeakSource _source; + SceneVisibilityAction _action = SceneVisibilityAction::SHOW; + +private: + static bool _registered; + static const int id; +}; + +class MacroActionSceneVisibilityEdit : public QWidget { + Q_OBJECT + +public: + MacroActionSceneVisibilityEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionSceneVisibilityEdit( + parent, + std::dynamic_pointer_cast( + 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 _entryData; + +private: + QHBoxLayout *_mainLayout; + bool _loading = true; +}; diff --git a/src/macro-action-scene-visibility.cpp b/src/macro-action-scene-visibility.cpp new file mode 100644 index 00000000..bd6a7a97 --- /dev/null +++ b/src/macro-action-scene-visibility.cpp @@ -0,0 +1,195 @@ +#include "headers/macro-action-scene-visibility.hpp" +#include "headers/advanced-scene-switcher.hpp" +#include "headers/utility.hpp" + +const int MacroActionSceneVisibility::id = 7; + +bool MacroActionSceneVisibility::_registered = MacroActionFactory::Register( + MacroActionSceneVisibility::id, + {MacroActionSceneVisibility::Create, + MacroActionSceneVisibilityEdit::Create, + "AdvSceneSwitcher.action.sceneVisibility"}); + +const static std::map actionTypes = { + {SceneVisibilityAction::SHOW, + "AdvSceneSwitcher.action.sceneVisibility.type.show"}, + {SceneVisibilityAction::HIDE, + "AdvSceneSwitcher.action.sceneVisibility.type.hide"}, +}; + +bool MacroActionSceneVisibility::PerformAction() +{ + auto s = obs_weak_source_get_source(_scene); + auto scene = obs_scene_from_source(s); + auto sourceName = GetWeakSourceName(_source); + switch (_action) { + case SceneVisibilityAction::SHOW: + obs_sceneitem_set_visible( + obs_scene_find_source(scene, sourceName.c_str()), true); + break; + case SceneVisibilityAction::HIDE: + obs_sceneitem_set_visible( + obs_scene_find_source(scene, sourceName.c_str()), + false); + 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(_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(_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( + 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) +{ + QComboBox *list = reinterpret_cast(ptr); + + if (obs_sceneitem_is_group(item)) { + obs_data_t *data = obs_sceneitem_get_private_settings(item); + + bool collapse = obs_data_get_bool(data, "collapsed"); + if (!collapse) { + obs_scene_t *scene = + obs_sceneitem_group_get_scene(item); + + obs_scene_enum_items(scene, enumItem, ptr); + } + + obs_data_release(data); + } else { + auto name = obs_source_get_name(obs_sceneitem_get_source(item)); + list->addItem(name); + } + return true; +} + +static inline void populateSceneItems(QComboBox *list, + OBSWeakSource sceneWeakSource = nullptr) +{ + list->clear(); + list->addItem(obs_module_text("AdvSceneSwitcher.selectItem")); + auto s = obs_weak_source_get_source(sceneWeakSource); + auto scene = obs_scene_from_source(s); + obs_scene_enum_items(scene, enumItem, list); + obs_source_release(s); +} + +MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _scenes = new QComboBox(); + _sources = new QComboBox(); + _actions = new QComboBox(); + + populateActionSelection(_actions); + AdvSceneSwitcher::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 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(_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 lock(switcher->m); + _entryData->_scene = GetWeakSourceByQString(text); + } + populateSceneItems(_sources, _entryData->_scene); +} + +void MacroActionSceneVisibilityEdit::SourceChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_source = GetWeakSourceByQString(text); +} + +void MacroActionSceneVisibilityEdit::ActionChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_action = static_cast(value); +} From f4d11dd6e6bc61e04207c0613ab15b713e91f389 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 17 May 2021 20:48:46 +0200 Subject: [PATCH 07/66] Add filter helper functions --- src/headers/utility.hpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp index 726f5c9c..b427141b 100644 --- a/src/headers/utility.hpp +++ b/src/headers/utility.hpp @@ -92,6 +92,27 @@ static inline OBSWeakSource GetWeakTransitionByQString(const QString &name) return GetWeakTransitionByName(name.toUtf8().constData()); } +static inline OBSWeakSource GetWeakFilterByName(OBSWeakSource source, + const char *name) +{ + OBSWeakSource weak; + auto s = obs_weak_source_get_source(source); + if (s) { + auto filterSource = obs_source_get_filter_by_name(s, name); + weak = obs_source_get_weak_source(filterSource); + obs_weak_source_release(weak); + obs_source_release(filterSource); + obs_source_release(s); + } + return weak; +} + +static inline OBSWeakSource GetWeakFilterByQString(OBSWeakSource source, + const QString &name) +{ + return GetWeakFilterByName(source, name.toUtf8().constData()); +} + static inline std::string getNextDelim(std::string text, std::unordered_map placeholders) From b80c16a446c68902666892d55cae11535f49cf15 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 17 May 2021 20:49:28 +0200 Subject: [PATCH 08/66] Add macro action filter --- CMakeLists.txt | 2 + data/locale/en-US.ini | 6 + src/headers/macro-action-filter.hpp | 61 +++++++++ src/macro-action-filter.cpp | 198 ++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 src/headers/macro-action-filter.hpp create mode 100644 src/macro-action-filter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 54524cfe..39e4f3ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,7 @@ set(advanced-scene-switcher_HEADERS src/headers/switch-video.hpp src/headers/switch-generic.hpp src/headers/macro-action-edit.hpp + src/headers/macro-action-filter.hpp src/headers/macro-action-audio.hpp src/headers/macro-action-recording.hpp src/headers/macro-action-replay-buffer.hpp @@ -138,6 +139,7 @@ set(advanced-scene-switcher_SOURCES src/switch-video.cpp src/switch-generic.cpp src/macro-action-edit.cpp + src/macro-action-filter.cpp src/macro-action-audio.cpp src/macro-action-recording.cpp src/macro-action-replay-buffer.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index bec3c457..4ba0c843 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -159,6 +159,10 @@ AdvSceneSwitcher.action.sceneVisibility="Scene 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}}" ; Transition Tab @@ -409,10 +413,12 @@ 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.selectItem="--select item--" AdvSceneSwitcher.enterPath="--enter path--" AdvSceneSwitcher.enterText="--enter text--" diff --git a/src/headers/macro-action-filter.hpp b/src/headers/macro-action-filter.hpp new file mode 100644 index 00000000..64c91e60 --- /dev/null +++ b/src/headers/macro-action-filter.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#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); + int GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + OBSWeakSource _source; + OBSWeakSource _filter; + FilterAction _action = FilterAction::ENABLE; + +private: + static bool _registered; + static const int id; +}; + +class MacroActionFilterEdit : public QWidget { + Q_OBJECT + +public: + MacroActionFilterEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionFilterEdit( + parent, + std::dynamic_pointer_cast(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 _entryData; + +private: + QHBoxLayout *_mainLayout; + bool _loading = true; +}; diff --git a/src/macro-action-filter.cpp b/src/macro-action-filter.cpp new file mode 100644 index 00000000..39447aff --- /dev/null +++ b/src/macro-action-filter.cpp @@ -0,0 +1,198 @@ +#include "headers/macro-action-filter.hpp" +#include "headers/advanced-scene-switcher.hpp" +#include "headers/utility.hpp" + +const int MacroActionFilter::id = 8; + +bool MacroActionFilter::_registered = MacroActionFactory::Register( + MacroActionFilter::id, + {MacroActionFilter::Create, MacroActionFilterEdit::Create, + "AdvSceneSwitcher.action.filter"}); + +const static std::map 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(_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(_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(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(ptr); + auto name = obs_source_get_name(filter); + list->addItem(name); + }; + + list->clear(); + list->addItem(obs_module_text("AdvSceneSwitcher.selectFilter")); + auto s = obs_weak_source_get_source(weakSource); + obs_source_enum_filters(s, enumFilters, list); + obs_source_release(s); +} + +static inline void hasFilterEnum(obs_source_t *, obs_source_t *filter, + void *ptr) +{ + if (!filter) { + return; + } + bool *hasFilter = reinterpret_cast(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(param); + bool hasFilter = false; + obs_source_enum_filters(source, hasFilterEnum, &hasFilter); + if (hasFilter) { + list->addItem(obs_source_get_name(source)); + } + return true; + }; + + list->clear(); + list->addItem(obs_module_text("AdvSceneSwitcher.selectSource")); + obs_enum_sources(enumSourcesWithFilters, list); +} + +MacroActionFilterEdit::MacroActionFilterEdit( + QWidget *parent, std::shared_ptr 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 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(_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 lock(switcher->m); + _entryData->_source = GetWeakSourceByQString(text); + } + populateFilters(_filters, _entryData->_source); +} + +void MacroActionFilterEdit::FilterChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_filter = GetWeakFilterByQString(_entryData->_source, text); +} + +void MacroActionFilterEdit::ActionChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_action = static_cast(value); +} From 6a998934d0dfdcca24a782dec5117188c8947a27 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Tue, 18 May 2021 19:11:23 +0200 Subject: [PATCH 09/66] Add macro action "source" --- CMakeLists.txt | 2 + data/locale/en-US.ini | 4 + src/headers/macro-action-source.hpp | 58 +++++++++++ src/macro-action-source.cpp | 144 ++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 src/headers/macro-action-source.hpp create mode 100644 src/macro-action-source.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 39e4f3ab..25eff11f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,7 @@ set(advanced-scene-switcher_HEADERS 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-wait.hpp src/headers/macro-condition-edit.hpp @@ -146,6 +147,7 @@ set(advanced-scene-switcher_SOURCES 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-wait.cpp src/macro-condition-edit.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 4ba0c843..fc22765b 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -163,6 +163,10 @@ 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.entry="{{actions}} {{sources}}" ; Transition Tab diff --git a/src/headers/macro-action-source.hpp b/src/headers/macro-action-source.hpp new file mode 100644 index 00000000..47e375d1 --- /dev/null +++ b/src/headers/macro-action-source.hpp @@ -0,0 +1,58 @@ +#pragma once +#include +#include "macro-action-edit.hpp" + +enum class SourceAction { + ENABLE, + DISABLE, +}; + +class MacroActionSource : public MacroAction { +public: + bool PerformAction(); + void LogAction(); + bool Save(obs_data_t *obj); + bool Load(obs_data_t *obj); + int GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + OBSWeakSource _source; + SourceAction _action = SourceAction::ENABLE; + +private: + static bool _registered; + static const int id; +}; + +class MacroActionSourceEdit : public QWidget { + Q_OBJECT + +public: + MacroActionSourceEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionSourceEdit( + parent, + std::dynamic_pointer_cast(action)); + } + +private slots: + void SourceChanged(const QString &text); + void ActionChanged(int value); + +protected: + QComboBox *_sources; + QComboBox *_actions; + std::shared_ptr _entryData; + +private: + QHBoxLayout *_mainLayout; + bool _loading = true; +}; diff --git a/src/macro-action-source.cpp b/src/macro-action-source.cpp new file mode 100644 index 00000000..74681138 --- /dev/null +++ b/src/macro-action-source.cpp @@ -0,0 +1,144 @@ +#include "headers/macro-action-source.hpp" +#include "headers/advanced-scene-switcher.hpp" +#include "headers/utility.hpp" + +const int MacroActionSource::id = 9; + +bool MacroActionSource::_registered = MacroActionFactory::Register( + MacroActionSource::id, + {MacroActionSource::Create, MacroActionSourceEdit::Create, + "AdvSceneSwitcher.action.source"}); + +const static std::map actionTypes = { + {SourceAction::ENABLE, "AdvSceneSwitcher.action.source.type.enable"}, + {SourceAction::DISABLE, "AdvSceneSwitcher.action.source.type.disable"}, +}; + +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; + 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(_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(_action)); + 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(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 populateSources(QComboBox *list) +{ + auto enumSourcesWithSources = [](void *param, obs_source_t *source) { + if (!source) { + return true; + } + QComboBox *list = reinterpret_cast(param); + list->addItem(obs_source_get_name(source)); + return true; + }; + + list->clear(); + list->addItem(obs_module_text("AdvSceneSwitcher.selectSource")); + obs_enum_sources(enumSourcesWithSources, list); +} + +MacroActionSourceEdit::MacroActionSourceEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _sources = new QComboBox(); + _actions = new QComboBox(); + + populateActionSelection(_actions); + populateSources(_sources); + + QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ActionChanged(int))); + QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(SourceChanged(const QString &))); + + QHBoxLayout *mainLayout = new QHBoxLayout; + std::unordered_map widgetPlaceholders = { + {"{{sources}}", _sources}, + {"{{actions}}", _actions}, + }; + placeWidgets(obs_module_text("AdvSceneSwitcher.action.source.entry"), + mainLayout, widgetPlaceholders); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroActionSourceEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _actions->setCurrentIndex(static_cast(_entryData->_action)); + _sources->setCurrentText( + GetWeakSourceName(_entryData->_source).c_str()); +} + +void MacroActionSourceEdit::SourceChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_source = GetWeakSourceByQString(text); +} + +void MacroActionSourceEdit::ActionChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_action = static_cast(value); +} From 4b84850f00b1d519e9969ae73784e4c0bd6373f7 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Wed, 19 May 2021 20:00:42 +0200 Subject: [PATCH 10/66] Use splitter to separate actions and conditions --- forms/advanced-scene-switcher.ui | 353 ++++++++++++++++--------------- 1 file changed, 178 insertions(+), 175 deletions(-) diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index f21ba378..4726e039 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -723,7 +723,7 @@ AdvSceneSwitcher.macroTab.edit - + @@ -752,184 +752,187 @@ - - - true + + + Qt::Vertical - - - - 0 - 0 - 767 - 293 - + + + true - - - - - - - false - - - AdvSceneSwitcher.macroTab.editConditionHelp - - - Qt::AlignCenter - - - true - - - - - - - - - - - - - - - 22 - 22 - - - - true - - - addIconSmall - - - - - - - - 22 - 22 - - - - true - - - removeIconSmall - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + + 0 + 0 + 767 + 220 + + + + + + + + + false + + + AdvSceneSwitcher.macroTab.editConditionHelp + + + Qt::AlignCenter + + + true + + + + + + + + + + + + + + + 22 + 22 + + + + true + + + addIconSmall + + + + + + + + 22 + 22 + + + + true + + + removeIconSmall + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + - - - - - - true - - - - - 0 - 0 - 767 - 145 - + + + true - - - - - - - false - - - AdvSceneSwitcher.macroTab.editActionHelp - - - Qt::AlignCenter - - - true - - - - - - - - - - - - - - - 22 - 22 - - - - true - - - addIconSmall - - - - - - - - 22 - 22 - - - - true - - - removeIconSmall - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + + 0 + 0 + 767 + 219 + + + + + + + + + false + + + AdvSceneSwitcher.macroTab.editActionHelp + + + Qt::AlignCenter + + + true + + + + + + + + + + + + + + + 22 + 22 + + + + true + + + addIconSmall + + + + + + + + 22 + 22 + + + + true + + + removeIconSmall + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + From 51680adc20927937fafd8b149ef4867dfeb962ab Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Wed, 19 May 2021 20:16:50 +0200 Subject: [PATCH 11/66] Add macro condition "interval" --- CMakeLists.txt | 2 + data/locale/en-US.ini | 2 + src/headers/macro-condition-interval.hpp | 52 ++++++++++++++ src/macro-condition-interval.cpp | 88 ++++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 src/headers/macro-condition-interval.hpp create mode 100644 src/macro-condition-interval.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 25eff11f..01435fd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-condition-audio.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 @@ -154,6 +155,7 @@ set(advanced-scene-switcher_SOURCES src/macro-condition-audio.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 diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index fc22765b..1503939e 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -122,6 +122,8 @@ AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duratio AdvSceneSwitcher.condition.pluginState="Plugin state" AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval" AdvSceneSwitcher.condition.pluginState.entry="{{condition}}" +AdvSceneSwitcher.condition.interval="Interval" +AdvSceneSwitcher.condition.interval.entry="{{duration}} have passed" ; Macro Actions AdvSceneSwitcher.action.switchScene="Switch scene" diff --git a/src/headers/macro-condition-interval.hpp b/src/headers/macro-condition-interval.hpp new file mode 100644 index 00000000..59445a12 --- /dev/null +++ b/src/headers/macro-condition-interval.hpp @@ -0,0 +1,52 @@ +#pragma once +#include "macro.hpp" +#include +#include +#include "duration-control.hpp" + +class MacroConditionInterval : public MacroCondition { +public: + bool CheckCondition(); + bool Save(obs_data_t *obj); + bool Load(obs_data_t *obj); + int GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + Duration _duration; + +private: + static bool _registered; + static const int id; +}; + +class MacroConditionIntervalEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionIntervalEdit( + QWidget *parent, + std::shared_ptr cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionIntervalEdit( + parent, + std::dynamic_pointer_cast( + cond)); + } + +private slots: + void DurationChanged(double seconds); + void DurationUnitChanged(DurationUnit unit); + +protected: + DurationSelection *_duration; + std::shared_ptr _entryData; + +private: + bool _loading = true; +}; diff --git a/src/macro-condition-interval.cpp b/src/macro-condition-interval.cpp new file mode 100644 index 00000000..c188326c --- /dev/null +++ b/src/macro-condition-interval.cpp @@ -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 int MacroConditionInterval::id = 12; + +bool MacroConditionInterval::_registered = MacroConditionFactory::Register( + MacroConditionInterval::id, + {MacroConditionInterval::Create, MacroConditionIntervalEdit::Create, + "AdvSceneSwitcher.condition.interval"}); + +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 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 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 lock(switcher->m); + _entryData->_duration.seconds = seconds; +} + +void MacroConditionIntervalEdit::DurationUnitChanged(DurationUnit unit) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_duration.displayUnit = unit; +} + +void MacroConditionIntervalEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _duration->SetDuration(_entryData->_duration); +} From 72bd905e7ca536030ed93c8f4414c348889a5e9c Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Wed, 19 May 2021 20:46:13 +0200 Subject: [PATCH 12/66] Improve logging for macros --- src/headers/macro-action-edit.hpp | 1 + src/headers/macro-condition-edit.hpp | 1 + src/macro-action-edit.cpp | 8 ++++++++ src/macro-condition-edit.cpp | 8 ++++++++ src/macro.cpp | 8 +++++++- 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/headers/macro-action-edit.hpp b/src/headers/macro-action-edit.hpp index 0556cafb..01c59a83 100644 --- a/src/headers/macro-action-edit.hpp +++ b/src/headers/macro-action-edit.hpp @@ -25,6 +25,7 @@ public: static QWidget *CreateWidget(const int id, QWidget *parent, std::shared_ptr action); static auto GetActionTypes() { return _methods; } + static std::string GetActionName(int id); private: static std::map _methods; diff --git a/src/headers/macro-condition-edit.hpp b/src/headers/macro-condition-edit.hpp index 3e935ee4..fb2ba00d 100644 --- a/src/headers/macro-condition-edit.hpp +++ b/src/headers/macro-condition-edit.hpp @@ -25,6 +25,7 @@ public: static QWidget *CreateWidget(const int id, QWidget *parent, std::shared_ptr); static auto GetConditionTypes() { return _methods; } + static std::string GetConditionName(int id); private: static std::map _methods; diff --git a/src/macro-action-edit.cpp b/src/macro-action-edit.cpp index 5314e315..fc3a8878 100644 --- a/src/macro-action-edit.cpp +++ b/src/macro-action-edit.cpp @@ -31,6 +31,14 @@ QWidget *MacroActionFactory::CreateWidget(const int id, QWidget *parent, return nullptr; } +std::string MacroActionFactory::GetActionName(int id) +{ + if (auto it = _methods.find(id); it != _methods.end()) { + return it->second._name; + } + return "unknown action"; +} + static inline void populateActionSelection(QComboBox *list) { for (auto entry : MacroActionFactory::GetActionTypes()) { diff --git a/src/macro-condition-edit.cpp b/src/macro-condition-edit.cpp index 31b839b5..46acfe42 100644 --- a/src/macro-condition-edit.cpp +++ b/src/macro-condition-edit.cpp @@ -29,6 +29,14 @@ MacroConditionFactory::CreateWidget(const int id, QWidget *parent, return nullptr; } +std::string MacroConditionFactory::GetConditionName(int id) +{ + if (auto it = _methods.find(id); it != _methods.end()) { + return it->second._name; + } + return "unknown condition"; +} + static inline void populateLogicSelection(QComboBox *list, bool root = false) { if (root) { diff --git a/src/macro.cpp b/src/macro.cpp index b4b7b7d3..ea77dc89 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -55,8 +55,13 @@ bool Macro::CeckMatch() _name.c_str()); break; } + vblog(LOG_INFO, "condition %s returned %d", + MacroConditionFactory::GetConditionName(c->GetId()) + .c_str(), + cond); } + vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched); return _matched; } @@ -232,7 +237,8 @@ bool MacroAction::Load(obs_data_t *obj) void MacroAction::LogAction() { - blog(LOG_INFO, "performed action %d", GetId()); + vblog(LOG_INFO, "performed action %s", + MacroActionFactory::GetActionName(GetId()).c_str()); } void SwitcherData::saveMacros(obs_data_t *obj) From e803ba75f99559df5c2c42ea3f007681a5f687e1 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 00:57:59 +0200 Subject: [PATCH 13/66] CI: Use new windows deps version --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 82c9b5b9..5a902d71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 From ae71b24c60cd25598d284ac7a50785ddcf66c9a2 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Thu, 20 May 2021 20:44:10 +0200 Subject: [PATCH 14/66] Adjust locale --- data/locale/en-US.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 1503939e..8324233a 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -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:" From 572b974b821e8b91ecdcb82357ff2c646dc4922f Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 00:48:38 +0200 Subject: [PATCH 15/66] Switch to using string as ids for actions and conditions Using int is an id was getting messy with more and more conditions and actions beeing added. It also made the order of conditions and actions in the respective comboboxes unchangable as there was a 1 to 1 relation of index and id. --- src/headers/macro-action-audio.hpp | 4 +- src/headers/macro-action-edit.hpp | 18 +++--- src/headers/macro-action-filter.hpp | 4 +- src/headers/macro-action-recording.hpp | 4 +- src/headers/macro-action-replay-buffer.hpp | 4 +- src/headers/macro-action-run.hpp | 4 +- src/headers/macro-action-scene-switch.hpp | 4 +- src/headers/macro-action-scene-visibility.hpp | 4 +- src/headers/macro-action-source.hpp | 4 +- src/headers/macro-action-streaming.hpp | 4 +- src/headers/macro-action-wait.hpp | 4 +- src/headers/macro-condition-audio.hpp | 4 +- src/headers/macro-condition-edit.hpp | 17 +++--- src/headers/macro-condition-file.hpp | 4 +- src/headers/macro-condition-idle.hpp | 4 +- src/headers/macro-condition-interval.hpp | 4 +- src/headers/macro-condition-media.hpp | 4 +- src/headers/macro-condition-plugin-state.hpp | 4 +- src/headers/macro-condition-process.hpp | 4 +- src/headers/macro-condition-recording.hpp | 4 +- src/headers/macro-condition-region.hpp | 4 +- src/headers/macro-condition-scene.hpp | 4 +- src/headers/macro-condition-streaming.hpp | 4 +- src/headers/macro-condition-video.hpp | 4 +- src/headers/macro-condition-window.hpp | 4 +- src/headers/macro.hpp | 4 +- src/macro-action-audio.cpp | 2 +- src/macro-action-edit.cpp | 52 +++++++++++------ src/macro-action-filter.cpp | 2 +- src/macro-action-recording.cpp | 2 +- src/macro-action-replay-buffer.cpp | 2 +- src/macro-action-run.cpp | 2 +- src/macro-action-scene-switch.cpp | 2 +- src/macro-action-scene-visibility.cpp | 2 +- src/macro-action-source.cpp | 2 +- src/macro-action-streaming.cpp | 2 +- src/macro-action-wait.cpp | 2 +- src/macro-condition-audio.cpp | 2 +- src/macro-condition-edit.cpp | 57 ++++++++++++------- src/macro-condition-file.cpp | 2 +- src/macro-condition-idle.cpp | 2 +- src/macro-condition-interval.cpp | 2 +- src/macro-condition-media.cpp | 2 +- src/macro-condition-plugin-state.cpp | 2 +- src/macro-condition-process.cpp | 2 +- src/macro-condition-recording.cpp | 2 +- src/macro-condition-region.cpp | 2 +- src/macro-condition-scene.cpp | 2 +- src/macro-condition-streaming.cpp | 2 +- src/macro-condition-video.cpp | 2 +- src/macro-condition-window.cpp | 2 +- src/macro.cpp | 19 ++++--- 52 files changed, 173 insertions(+), 132 deletions(-) diff --git a/src/headers/macro-action-audio.hpp b/src/headers/macro-action-audio.hpp index a00d4a0a..315be060 100644 --- a/src/headers/macro-action-audio.hpp +++ b/src/headers/macro-action-audio.hpp @@ -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 Create() { return std::make_shared(); @@ -27,7 +27,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroActionAudioEdit : public QWidget { diff --git a/src/headers/macro-action-edit.hpp b/src/headers/macro-action-edit.hpp index 01c59a83..cedf39a8 100644 --- a/src/headers/macro-action-edit.hpp +++ b/src/headers/macro-action-edit.hpp @@ -20,15 +20,16 @@ struct MacroActionInfo { class MacroActionFactory { public: MacroActionFactory() = delete; - static bool Register(int id, MacroActionInfo); - static std::shared_ptr Create(const int id); - static QWidget *CreateWidget(const int id, QWidget *parent, + static bool Register(std::string id, MacroActionInfo); + static std::shared_ptr Create(const std::string id); + static QWidget *CreateWidget(const std::string id, QWidget *parent, std::shared_ptr action); static auto GetActionTypes() { return _methods; } - static std::string GetActionName(int id); + static std::string GetActionName(const std::string id); + static std::string GetIdByName(const QString &name); private: - static std::map _methods; + static std::map _methods; }; class MacroActionEdit : public QWidget { @@ -36,13 +37,14 @@ class MacroActionEdit : public QWidget { public: MacroActionEdit(QWidget *parent = nullptr, - std::shared_ptr * = nullptr, int type = 0, + std::shared_ptr * = nullptr, + std::string id = "scene_switch", bool startCollapsed = false); - void UpdateEntryData(int type); + void UpdateEntryData(std::string id); void Collapse(bool collapsed); private slots: - void ActionSelectionChanged(int idx); + void ActionSelectionChanged(const QString &text); protected: QComboBox *_actionSelection; diff --git a/src/headers/macro-action-filter.hpp b/src/headers/macro-action-filter.hpp index 64c91e60..63d07f3e 100644 --- a/src/headers/macro-action-filter.hpp +++ b/src/headers/macro-action-filter.hpp @@ -13,7 +13,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 Create() { return std::make_shared(); @@ -25,7 +25,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroActionFilterEdit : public QWidget { diff --git a/src/headers/macro-action-recording.hpp b/src/headers/macro-action-recording.hpp index 05c26669..34b29e6f 100644 --- a/src/headers/macro-action-recording.hpp +++ b/src/headers/macro-action-recording.hpp @@ -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 Create() { return std::make_shared(); @@ -26,7 +26,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroActionRecordEdit : public QWidget { diff --git a/src/headers/macro-action-replay-buffer.hpp b/src/headers/macro-action-replay-buffer.hpp index 26dc7fc7..5d047270 100644 --- a/src/headers/macro-action-replay-buffer.hpp +++ b/src/headers/macro-action-replay-buffer.hpp @@ -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 Create() { return std::make_shared(); @@ -33,7 +33,7 @@ private: Duration _duration; static bool _registered; - static const int id; + static const std::string id; }; class MacroActionReplayBufferEdit : public QWidget { diff --git a/src/headers/macro-action-run.hpp b/src/headers/macro-action-run.hpp index cf276057..414fa532 100644 --- a/src/headers/macro-action-run.hpp +++ b/src/headers/macro-action-run.hpp @@ -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 Create() { return std::make_shared(); @@ -20,7 +20,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroActionRunEdit : public QWidget { diff --git a/src/headers/macro-action-scene-switch.hpp b/src/headers/macro-action-scene-switch.hpp index 36b8c5ff..967e2bfc 100644 --- a/src/headers/macro-action-scene-switch.hpp +++ b/src/headers/macro-action-scene-switch.hpp @@ -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 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 { diff --git a/src/headers/macro-action-scene-visibility.hpp b/src/headers/macro-action-scene-visibility.hpp index 773dbb59..223d5c1f 100644 --- a/src/headers/macro-action-scene-visibility.hpp +++ b/src/headers/macro-action-scene-visibility.hpp @@ -13,7 +13,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 Create() { return std::make_shared(); @@ -25,7 +25,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroActionSceneVisibilityEdit : public QWidget { diff --git a/src/headers/macro-action-source.hpp b/src/headers/macro-action-source.hpp index 47e375d1..db52358d 100644 --- a/src/headers/macro-action-source.hpp +++ b/src/headers/macro-action-source.hpp @@ -13,7 +13,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 Create() { return std::make_shared(); @@ -24,7 +24,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroActionSourceEdit : public QWidget { diff --git a/src/headers/macro-action-streaming.hpp b/src/headers/macro-action-streaming.hpp index e0d71003..8d3fc038 100644 --- a/src/headers/macro-action-streaming.hpp +++ b/src/headers/macro-action-streaming.hpp @@ -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 Create() { return std::make_shared(); @@ -28,7 +28,7 @@ private: Duration _retryCooldown; static bool _registered; - static const int id; + static const std::string id; }; class MacroActionStreamEdit : public QWidget { diff --git a/src/headers/macro-action-wait.hpp b/src/headers/macro-action-wait.hpp index 50b555e4..e3b9756d 100644 --- a/src/headers/macro-action-wait.hpp +++ b/src/headers/macro-action-wait.hpp @@ -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 Create() { return std::make_shared(); @@ -24,7 +24,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroActionWaitEdit : public QWidget { diff --git a/src/headers/macro-condition-audio.hpp b/src/headers/macro-condition-audio.hpp index f6843ef2..849da3eb 100644 --- a/src/headers/macro-condition-audio.hpp +++ b/src/headers/macro-condition-audio.hpp @@ -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; }; static std::shared_ptr Create() { return std::make_shared(); @@ -39,7 +39,7 @@ public: private: float _peak = -std::numeric_limits::infinity(); static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionAudioEdit : public QWidget { diff --git a/src/headers/macro-condition-edit.hpp b/src/headers/macro-condition-edit.hpp index fb2ba00d..f03f902d 100644 --- a/src/headers/macro-condition-edit.hpp +++ b/src/headers/macro-condition-edit.hpp @@ -20,15 +20,16 @@ struct MacroConditionInfo { class MacroConditionFactory { public: MacroConditionFactory() = delete; - static bool Register(int id, MacroConditionInfo); - static std::shared_ptr Create(const int id); - static QWidget *CreateWidget(const int id, QWidget *parent, + static bool Register(const std::string &, MacroConditionInfo); + static std::shared_ptr Create(const std::string); + static QWidget *CreateWidget(const std::string &id, QWidget *parent, std::shared_ptr); static auto GetConditionTypes() { return _methods; } - static std::string GetConditionName(int id); + static std::string GetConditionName(const std::string &); + static std::string GetIdByName(const QString& name); private: - static std::map _methods; + static std::map _methods; }; class MacroConditionEdit : public QWidget { @@ -37,15 +38,15 @@ class MacroConditionEdit : public QWidget { public: MacroConditionEdit(QWidget *parent = nullptr, std::shared_ptr * = 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 UpdateEntryData(const std::string &id); void Collapse(bool collapsed); private slots: void LogicSelectionChanged(int idx); - void ConditionSelectionChanged(int idx); + void ConditionSelectionChanged(const QString &text); protected: QComboBox *_logicSelection; diff --git a/src/headers/macro-condition-file.hpp b/src/headers/macro-condition-file.hpp index f5fd34c4..5cbd8a39 100644 --- a/src/headers/macro-condition-file.hpp +++ b/src/headers/macro-condition-file.hpp @@ -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 Create() { return std::make_shared(); @@ -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 { diff --git a/src/headers/macro-condition-idle.hpp b/src/headers/macro-condition-idle.hpp index 0f1a0e49..f1fa0889 100644 --- a/src/headers/macro-condition-idle.hpp +++ b/src/headers/macro-condition-idle.hpp @@ -9,7 +9,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 Create() { return std::make_shared(); @@ -19,7 +19,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionIdleEdit : public QWidget { diff --git a/src/headers/macro-condition-interval.hpp b/src/headers/macro-condition-interval.hpp index 59445a12..7d8edfbb 100644 --- a/src/headers/macro-condition-interval.hpp +++ b/src/headers/macro-condition-interval.hpp @@ -9,7 +9,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 Create() { return std::make_shared(); @@ -19,7 +19,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionIntervalEdit : public QWidget { diff --git a/src/headers/macro-condition-media.hpp b/src/headers/macro-condition-media.hpp index fcf516b4..5c1735f8 100644 --- a/src/headers/macro-condition-media.hpp +++ b/src/headers/macro-condition-media.hpp @@ -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 Create() { return std::make_shared(); @@ -65,7 +65,7 @@ private: bool _playedToEnd = false; static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionMediaEdit : public QWidget { diff --git a/src/headers/macro-condition-plugin-state.hpp b/src/headers/macro-condition-plugin-state.hpp index 1a71df82..bf131e85 100644 --- a/src/headers/macro-condition-plugin-state.hpp +++ b/src/headers/macro-condition-plugin-state.hpp @@ -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 Create() { return std::make_shared(); @@ -22,7 +22,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionPluginStateEdit : public QWidget { diff --git a/src/headers/macro-condition-process.hpp b/src/headers/macro-condition-process.hpp index a934cc68..3f4e3d58 100644 --- a/src/headers/macro-condition-process.hpp +++ b/src/headers/macro-condition-process.hpp @@ -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 Create() { return std::make_shared(); @@ -19,7 +19,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionProcessEdit : public QWidget { diff --git a/src/headers/macro-condition-recording.hpp b/src/headers/macro-condition-recording.hpp index c66acdb1..77af9719 100644 --- a/src/headers/macro-condition-recording.hpp +++ b/src/headers/macro-condition-recording.hpp @@ -15,7 +15,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 Create() { return std::make_shared(); @@ -26,7 +26,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionRecordEdit : public QWidget { diff --git a/src/headers/macro-condition-region.hpp b/src/headers/macro-condition-region.hpp index c998512d..5a5474ad 100644 --- a/src/headers/macro-condition-region.hpp +++ b/src/headers/macro-condition-region.hpp @@ -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 Create() { return std::make_shared(); @@ -18,7 +18,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionRegionEdit : public QWidget { diff --git a/src/headers/macro-condition-scene.hpp b/src/headers/macro-condition-scene.hpp index 8d4b56d4..433b2fd5 100644 --- a/src/headers/macro-condition-scene.hpp +++ b/src/headers/macro-condition-scene.hpp @@ -14,7 +14,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 Create() { return std::make_shared(); @@ -26,7 +26,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionSceneEdit : public QWidget { diff --git a/src/headers/macro-condition-streaming.hpp b/src/headers/macro-condition-streaming.hpp index 4d959a03..62f76c1e 100644 --- a/src/headers/macro-condition-streaming.hpp +++ b/src/headers/macro-condition-streaming.hpp @@ -14,7 +14,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 Create() { return std::make_shared(); @@ -25,7 +25,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionStreamEdit : public QWidget { diff --git a/src/headers/macro-condition-video.hpp b/src/headers/macro-condition-video.hpp index 122e1b18..8693d463 100644 --- a/src/headers/macro-condition-video.hpp +++ b/src/headers/macro-condition-video.hpp @@ -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 Create() { @@ -39,7 +39,7 @@ private: std::unique_ptr _screenshotData = nullptr; QImage _matchImage; static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionVideoEdit : public QWidget { diff --git a/src/headers/macro-condition-window.hpp b/src/headers/macro-condition-window.hpp index e90921f9..022430e3 100644 --- a/src/headers/macro-condition-window.hpp +++ b/src/headers/macro-condition-window.hpp @@ -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 Create() { return std::make_shared(); @@ -27,7 +27,7 @@ public: private: static bool _registered; - static const int id; + static const std::string id; }; class MacroConditionWindowEdit : public QWidget { diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index bdc586e2..7c0271a2 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -38,7 +38,7 @@ 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; } @@ -53,7 +53,7 @@ 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(); }; diff --git a/src/macro-action-audio.cpp b/src/macro-action-audio.cpp index 314c410f..354c8b90 100644 --- a/src/macro-action-audio.cpp +++ b/src/macro-action-audio.cpp @@ -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, diff --git a/src/macro-action-edit.cpp b/src/macro-action-edit.cpp index fc3a8878..08475b49 100644 --- a/src/macro-action-edit.cpp +++ b/src/macro-action-edit.cpp @@ -3,9 +3,9 @@ #include "headers/utility.hpp" #include "headers/advanced-scene-switcher.hpp" -std::map MacroActionFactory::_methods; +std::map MacroActionFactory::_methods; -bool MacroActionFactory::Register(int id, MacroActionInfo info) +bool MacroActionFactory::Register(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 MacroActionFactory::Create(const int id) +std::shared_ptr MacroActionFactory::Create(const std::string id) { if (auto it = _methods.find(id); it != _methods.end()) return it->second._createFunc(); @@ -22,7 +22,7 @@ std::shared_ptr 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 action) { if (auto it = _methods.find(id); it != _methods.end()) @@ -31,7 +31,7 @@ QWidget *MacroActionFactory::CreateWidget(const int id, QWidget *parent, return nullptr; } -std::string MacroActionFactory::GetActionName(int id) +std::string MacroActionFactory::GetActionName(const std::string id) { if (auto it = _methods.find(id); it != _methods.end()) { return it->second._name; @@ -39,6 +39,16 @@ std::string MacroActionFactory::GetActionName(int id) 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()) { @@ -48,14 +58,15 @@ static inline void populateActionSelection(QComboBox *list) MacroActionEdit::MacroActionEdit(QWidget *parent, std::shared_ptr *entryData, - int type, bool startCollapsed) + 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); @@ -66,32 +77,35 @@ MacroActionEdit::MacroActionEdit(QWidget *parent, setLayout(mainLayout); _entryData = entryData; - UpdateEntryData(type); + UpdateEntryData(id); _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 lock(switcher->m); _entryData->reset(); - *_entryData = MacroActionFactory::Create(idx); + *_entryData = MacroActionFactory::Create(id); auto widget = - MacroActionFactory::CreateWidget(idx, window(), *_entryData); + MacroActionFactory::CreateWidget(id, window(), *_entryData); _section->SetContent(widget); _section->Collapse(false); } -void MacroActionEdit::UpdateEntryData(int type) +void MacroActionEdit::UpdateEntryData(std::string id) { - _actionSelection->setCurrentIndex(type); + _actionSelection->setCurrentText( + obs_module_text(MacroActionFactory::GetActionName(id).c_str())); auto widget = - MacroActionFactory::CreateWidget(type, window(), *_entryData); + MacroActionFactory::CreateWidget(id, window(), *_entryData); _section->SetContent(widget); } @@ -106,9 +120,13 @@ void AdvSceneSwitcher::on_actionAdd_clicked() if (!macro) { return; } + + MacroActionSwitchScene temp; + std::string id = temp.GetId(); + std::lock_guard lock(switcher->m); - macro->Actions().emplace_back(MacroActionFactory::Create(0)); - auto newEntry = new MacroActionEdit(this, ¯o->Actions().back(), 0); + macro->Actions().emplace_back(MacroActionFactory::Create(id)); + auto newEntry = new MacroActionEdit(this, ¯o->Actions().back(), id); ui->macroEditActionLayout->addWidget(newEntry); ui->macroEditActionHelp->setVisible(false); newEntry->Collapse(false); diff --git a/src/macro-action-filter.cpp b/src/macro-action-filter.cpp index 39447aff..c984e9db 100644 --- a/src/macro-action-filter.cpp +++ b/src/macro-action-filter.cpp @@ -2,7 +2,7 @@ #include "headers/advanced-scene-switcher.hpp" #include "headers/utility.hpp" -const int MacroActionFilter::id = 8; +const std::string MacroActionFilter::id = "filter"; bool MacroActionFilter::_registered = MacroActionFactory::Register( MacroActionFilter::id, diff --git a/src/macro-action-recording.cpp b/src/macro-action-recording.cpp index e479b899..527b98b7 100644 --- a/src/macro-action-recording.cpp +++ b/src/macro-action-recording.cpp @@ -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, diff --git a/src/macro-action-replay-buffer.cpp b/src/macro-action-replay-buffer.cpp index 0785bbed..d0351766 100644 --- a/src/macro-action-replay-buffer.cpp +++ b/src/macro-action-replay-buffer.cpp @@ -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, diff --git a/src/macro-action-run.cpp b/src/macro-action-run.cpp index 9923e34e..ef01f0ac 100644 --- a/src/macro-action-run.cpp +++ b/src/macro-action-run.cpp @@ -5,7 +5,7 @@ #include #include -const int MacroActionRun::id = 6; +const std::string MacroActionRun::id = "run"; bool MacroActionRun::_registered = MacroActionFactory::Register( MacroActionRun::id, {MacroActionRun::Create, MacroActionRunEdit::Create, diff --git a/src/macro-action-scene-switch.cpp b/src/macro-action-scene-switch.cpp index 11b7bab0..8ab79ff7 100644 --- a/src/macro-action-scene-switch.cpp +++ b/src/macro-action-scene-switch.cpp @@ -2,7 +2,7 @@ #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, diff --git a/src/macro-action-scene-visibility.cpp b/src/macro-action-scene-visibility.cpp index bd6a7a97..c1b480ae 100644 --- a/src/macro-action-scene-visibility.cpp +++ b/src/macro-action-scene-visibility.cpp @@ -2,7 +2,7 @@ #include "headers/advanced-scene-switcher.hpp" #include "headers/utility.hpp" -const int MacroActionSceneVisibility::id = 7; +const std::string MacroActionSceneVisibility::id = "scene_visibility"; bool MacroActionSceneVisibility::_registered = MacroActionFactory::Register( MacroActionSceneVisibility::id, diff --git a/src/macro-action-source.cpp b/src/macro-action-source.cpp index 74681138..b58f2cfb 100644 --- a/src/macro-action-source.cpp +++ b/src/macro-action-source.cpp @@ -2,7 +2,7 @@ #include "headers/advanced-scene-switcher.hpp" #include "headers/utility.hpp" -const int MacroActionSource::id = 9; +const std::string MacroActionSource::id = "source"; bool MacroActionSource::_registered = MacroActionFactory::Register( MacroActionSource::id, diff --git a/src/macro-action-streaming.cpp b/src/macro-action-streaming.cpp index 56dc0c97..b9abda30 100644 --- a/src/macro-action-streaming.cpp +++ b/src/macro-action-streaming.cpp @@ -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, diff --git a/src/macro-action-wait.cpp b/src/macro-action-wait.cpp index f30914d2..024960c4 100644 --- a/src/macro-action-wait.cpp +++ b/src/macro-action-wait.cpp @@ -4,7 +4,7 @@ #include -const int MacroActionWait::id = 1; +const std::string MacroActionWait::id = "wait"; bool MacroActionWait::_registered = MacroActionFactory::Register( MacroActionWait::id, diff --git a/src/macro-condition-audio.cpp b/src/macro-condition-audio.cpp index c25f7b96..dce2fa8f 100644 --- a/src/macro-condition-audio.cpp +++ b/src/macro-condition-audio.cpp @@ -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, diff --git a/src/macro-condition-edit.cpp b/src/macro-condition-edit.cpp index 46acfe42..488797f8 100644 --- a/src/macro-condition-edit.cpp +++ b/src/macro-condition-edit.cpp @@ -1,8 +1,10 @@ #include "headers/macro-condition-edit.hpp" +#include "headers/macro-condition-scene.hpp" -std::map MacroConditionFactory::_methods; +std::map 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,7 +13,8 @@ bool MacroConditionFactory::Register(int id, MacroConditionInfo info) return false; } -std::shared_ptr MacroConditionFactory::Create(const int id) +std::shared_ptr +MacroConditionFactory::Create(const std::string id) { if (auto it = _methods.find(id); it != _methods.end()) return it->second._createFunc(); @@ -20,7 +23,7 @@ std::shared_ptr MacroConditionFactory::Create(const int id) } QWidget * -MacroConditionFactory::CreateWidget(const int id, QWidget *parent, +MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent, std::shared_ptr cond) { if (auto it = _methods.find(id); it != _methods.end()) @@ -29,7 +32,7 @@ MacroConditionFactory::CreateWidget(const int id, QWidget *parent, return nullptr; } -std::string MacroConditionFactory::GetConditionName(int id) +std::string MacroConditionFactory::GetConditionName(const std::string &id) { if (auto it = _methods.find(id); it != _methods.end()) { return it->second._name; @@ -37,6 +40,16 @@ std::string MacroConditionFactory::GetConditionName(int id) 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 ""; +} + static inline void populateLogicSelection(QComboBox *list, bool root = false) { if (root) { @@ -65,8 +78,8 @@ static inline void populateConditionSelection(QComboBox *list) } MacroConditionEdit::MacroConditionEdit( - QWidget *parent, std::shared_ptr *entryData, int type, - bool root, bool startCollapsed) + QWidget *parent, std::shared_ptr *entryData, + const std::string &id, bool root, bool startCollapsed) : QWidget(parent) { _logicSelection = new QComboBox(); @@ -75,8 +88,9 @@ MacroConditionEdit::MacroConditionEdit( 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 &))); populateLogicSelection(_logicSelection, root); populateConditionSelection(_conditionSelection); @@ -90,7 +104,7 @@ MacroConditionEdit::MacroConditionEdit( _entryData = entryData; _isRoot = root; - UpdateEntryData(type); + UpdateEntryData(id); _loading = false; _section->Collapse(startCollapsed); } @@ -117,11 +131,12 @@ bool MacroConditionEdit::IsRootNode() return _isRoot; } -void MacroConditionEdit::UpdateEntryData(int type) +void MacroConditionEdit::UpdateEntryData(const std::string &id) { - _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(logic)); @@ -137,19 +152,21 @@ void MacroConditionEdit::Collapse(bool collapsed) _section->Collapse(collapsed); } -void MacroConditionEdit::ConditionSelectionChanged(int idx) +void MacroConditionEdit::ConditionSelectionChanged(const QString &text) { if (_loading || !_entryData) { return; } + std::string id = MacroConditionFactory::GetIdByName(text); + std::lock_guard lock(switcher->m); auto logic = (*_entryData)->GetLogicType(); _entryData->reset(); - *_entryData = MacroConditionFactory::Create(idx); + *_entryData = MacroConditionFactory::Create(id); (*_entryData)->SetLogicType(logic); auto widget = - MacroConditionFactory::CreateWidget(idx, window(), *_entryData); + MacroConditionFactory::CreateWidget(id, window(), *_entryData); _section->SetContent(widget); _section->Collapse(false); } @@ -160,14 +177,16 @@ void AdvSceneSwitcher::on_conditionAdd_clicked() if (!macro) { return; } + MacroConditionScene temp; + std::string id = temp.GetId(); std::lock_guard 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, ¯o->Conditions().back(), 0, root); + this, ¯o->Conditions().back(), id, root); ui->macroEditConditionLayout->addWidget(newEntry); ui->macroEditConditionHelp->setVisible(false); newEntry->Collapse(false); diff --git a/src/macro-condition-file.cpp b/src/macro-condition-file.cpp index 234c4a5d..16a5af4f 100644 --- a/src/macro-condition-file.cpp +++ b/src/macro-condition-file.cpp @@ -7,7 +7,7 @@ #include #include -const int MacroConditionFile::id = 4; +const std::string MacroConditionFile::id = "file"; bool MacroConditionFile::_registered = MacroConditionFactory::Register( MacroConditionFile::id, diff --git a/src/macro-condition-idle.cpp b/src/macro-condition-idle.cpp index 533d62ea..bc3d86c8 100644 --- a/src/macro-condition-idle.cpp +++ b/src/macro-condition-idle.cpp @@ -3,7 +3,7 @@ #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, diff --git a/src/macro-condition-interval.cpp b/src/macro-condition-interval.cpp index c188326c..fa37d4dc 100644 --- a/src/macro-condition-interval.cpp +++ b/src/macro-condition-interval.cpp @@ -3,7 +3,7 @@ #include "headers/utility.hpp" #include "headers/advanced-scene-switcher.hpp" -const int MacroConditionInterval::id = 12; +const std::string MacroConditionInterval::id = "interval"; bool MacroConditionInterval::_registered = MacroConditionFactory::Register( MacroConditionInterval::id, diff --git a/src/macro-condition-media.cpp b/src/macro-condition-media.cpp index 828718d5..d0e46bc2 100644 --- a/src/macro-condition-media.cpp +++ b/src/macro-condition-media.cpp @@ -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, diff --git a/src/macro-condition-plugin-state.cpp b/src/macro-condition-plugin-state.cpp index ac69b8da..ac049bfe 100644 --- a/src/macro-condition-plugin-state.cpp +++ b/src/macro-condition-plugin-state.cpp @@ -3,7 +3,7 @@ #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, diff --git a/src/macro-condition-process.cpp b/src/macro-condition-process.cpp index 992ef67a..eb1ab373 100644 --- a/src/macro-condition-process.cpp +++ b/src/macro-condition-process.cpp @@ -5,7 +5,7 @@ #include -const int MacroConditionProcess::id = 9; +const std::string MacroConditionProcess::id = "process"; bool MacroConditionProcess::_registered = MacroConditionFactory::Register( MacroConditionProcess::id, diff --git a/src/macro-condition-recording.cpp b/src/macro-condition-recording.cpp index cd23f5ce..20d70260 100644 --- a/src/macro-condition-recording.cpp +++ b/src/macro-condition-recording.cpp @@ -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, diff --git a/src/macro-condition-region.cpp b/src/macro-condition-region.cpp index fdb331c8..00016ce9 100644 --- a/src/macro-condition-region.cpp +++ b/src/macro-condition-region.cpp @@ -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, diff --git a/src/macro-condition-scene.cpp b/src/macro-condition-scene.cpp index 095bb26d..8508b9cd 100644 --- a/src/macro-condition-scene.cpp +++ b/src/macro-condition-scene.cpp @@ -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, diff --git a/src/macro-condition-streaming.cpp b/src/macro-condition-streaming.cpp index 3ccfed81..a1c6a40e 100644 --- a/src/macro-condition-streaming.cpp +++ b/src/macro-condition-streaming.cpp @@ -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, diff --git a/src/macro-condition-video.cpp b/src/macro-condition-video.cpp index fa4b40a6..85241501 100644 --- a/src/macro-condition-video.cpp +++ b/src/macro-condition-video.cpp @@ -8,7 +8,7 @@ #include #include -const int MacroConditionVideo::id = 6; +const std::string MacroConditionVideo::id = "video"; bool MacroConditionVideo::_registered = MacroConditionFactory::Register( MacroConditionVideo::id, diff --git a/src/macro-condition-window.cpp b/src/macro-condition-window.cpp index ed0923fc..40751b95 100644 --- a/src/macro-condition-window.cpp +++ b/src/macro-condition-window.cpp @@ -5,7 +5,7 @@ #include -const int MacroConditionWindow::id = 1; +const std::string MacroConditionWindow::id = "window"; bool MacroConditionWindow::_registered = MacroConditionFactory::Register( MacroConditionWindow::id, diff --git a/src/macro.cpp b/src/macro.cpp index ea77dc89..474c04eb 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -155,7 +155,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(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) { @@ -165,8 +165,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); @@ -180,7 +180,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) { @@ -188,8 +188,8 @@ 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); @@ -202,8 +202,9 @@ bool Macro::Load(obs_data_t *obj) bool Macro::SwitchesScene() { MacroActionSwitchScene temp; + auto sceneSwitchId = temp.GetId(); for (auto &a : _actions) { - if (a->GetId() == temp.GetId()) { + if (a->GetId() == sceneSwitchId) { return true; } } @@ -212,7 +213,7 @@ bool Macro::SwitchesScene() 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(_logic)); return true; } @@ -225,7 +226,7 @@ bool MacroCondition::Load(obs_data_t *obj) 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; } From 5b4ddfa669981d564defabf0cc190daaf6f2c02e Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 01:05:01 +0200 Subject: [PATCH 16/66] Adjust logging to use new ids --- src/macro.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/macro.cpp b/src/macro.cpp index 474c04eb..44430113 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -55,9 +55,7 @@ bool Macro::CeckMatch() _name.c_str()); break; } - vblog(LOG_INFO, "condition %s returned %d", - MacroConditionFactory::GetConditionName(c->GetId()) - .c_str(), + vblog(LOG_INFO, "condition %s returned %d", c->GetId().c_str(), cond); } @@ -238,8 +236,7 @@ bool MacroAction::Load(obs_data_t *obj) void MacroAction::LogAction() { - vblog(LOG_INFO, "performed action %s", - MacroActionFactory::GetActionName(GetId()).c_str()); + vblog(LOG_INFO, "performed action %s", GetId().c_str()); } void SwitcherData::saveMacros(obs_data_t *obj) From 283025f1a8ecbfeae3fb968cc47b0105a5fb3b68 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 02:07:07 +0200 Subject: [PATCH 17/66] Add helper function to convert to new id format --- src/headers/macro-condition-edit.hpp | 2 +- src/macro.cpp | 63 ++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/headers/macro-condition-edit.hpp b/src/headers/macro-condition-edit.hpp index f03f902d..775ecf6c 100644 --- a/src/headers/macro-condition-edit.hpp +++ b/src/headers/macro-condition-edit.hpp @@ -26,7 +26,7 @@ public: std::shared_ptr); static auto GetConditionTypes() { return _methods; } static std::string GetConditionName(const std::string &); - static std::string GetIdByName(const QString& name); + static std::string GetIdByName(const QString &name); private: static std::map _methods; diff --git a/src/macro.cpp b/src/macro.cpp index 44430113..6072da1d 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -254,8 +254,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 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 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"); From 186198bfd48e1533ca5105ef978f20e433bca481 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 12:14:56 +0200 Subject: [PATCH 18/66] Reduce macro logging in non-verbose mode --- src/macro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macro.cpp b/src/macro.cpp index 6072da1d..a98adbb8 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -353,7 +353,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()); From e4b060976bb610714860160b5162a3ed2e7743a4 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 12:44:40 +0200 Subject: [PATCH 19/66] Add macro action "media" --- CMakeLists.txt | 6 +- data/locale/en-US.ini | 10 ++ src/headers/macro-action-media.hpp | 62 ++++++++++++ src/macro-action-media.cpp | 153 +++++++++++++++++++++++++++++ 4 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 src/headers/macro-action-media.hpp create mode 100644 src/macro-action-media.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 01435fd8..def9a5e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,8 +81,9 @@ set(advanced-scene-switcher_HEADERS src/headers/switch-video.hpp src/headers/switch-generic.hpp src/headers/macro-action-edit.hpp - src/headers/macro-action-filter.hpp src/headers/macro-action-audio.hpp + src/headers/macro-action-filter.hpp + src/headers/macro-action-media.hpp src/headers/macro-action-recording.hpp src/headers/macro-action-replay-buffer.hpp src/headers/macro-action-run.hpp @@ -141,8 +142,9 @@ set(advanced-scene-switcher_SOURCES src/switch-video.cpp src/switch-generic.cpp src/macro-action-edit.cpp - src/macro-action-filter.cpp src/macro-action-audio.cpp + src/macro-action-filter.cpp + src/macro-action-media.cpp src/macro-action-recording.cpp src/macro-action-replay-buffer.cpp src/macro-action-run.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 8324233a..4c6a443b 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -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,6 +77,7 @@ 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" @@ -169,6 +171,14 @@ AdvSceneSwitcher.action.source="Source" AdvSceneSwitcher.action.source.type.enable="Enable" AdvSceneSwitcher.action.source.type.disable="Disable" AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}" +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}}" ; Transition Tab diff --git a/src/headers/macro-action-media.hpp b/src/headers/macro-action-media.hpp new file mode 100644 index 00000000..1ba797c1 --- /dev/null +++ b/src/headers/macro-action-media.hpp @@ -0,0 +1,62 @@ +#pragma once +#include +#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 Create() + { + return std::make_shared(); + } + + 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 entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionMediaEdit( + parent, + std::dynamic_pointer_cast(action)); + } + +private slots: + void SourceChanged(const QString &text); + void ActionChanged(int value); + +protected: + QComboBox *_mediaSources; + QComboBox *_actions; + std::shared_ptr _entryData; + +private: + QHBoxLayout *_mainLayout; + bool _loading = true; +}; diff --git a/src/macro-action-media.cpp b/src/macro-action-media.cpp new file mode 100644 index 00000000..827bc583 --- /dev/null +++ b/src/macro-action-media.cpp @@ -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 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(_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(_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(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 entryData) + : QWidget(parent) +{ + _mediaSources = new QComboBox(); + _actions = new QComboBox(); + + populateActionSelection(_actions); + AdvSceneSwitcher::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 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(_entryData->_action)); +} + +void MacroActionMediaEdit::SourceChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_mediaSource = GetWeakSourceByQString(text); +} + +void MacroActionMediaEdit::ActionChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_action = static_cast(value); +} From c1704f21647114372a615e662d90e316df24a4f3 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 19:23:55 +0200 Subject: [PATCH 20/66] Fix rapid scene switches by random tab Wait scene was not set while waiting for scene changes on the random tab. If wait scene was ever set by other means (sequence) the scene switcher would immediately wake up as the wait scene is not the expected value. --- src/advanced-scene-switcher.cpp | 8 ++++++++ src/headers/switcher-data-structs.hpp | 1 + src/switch-sequence.cpp | 2 -- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index 96f7b640..bb0e3b32 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -484,6 +484,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 +511,7 @@ void SwitcherData::Thread() vblog(LOG_INFO, "sleep for %ld before switching scene", duration.count()); + setWaitScene(); cv.wait_for(lock, duration); if (stop) { @@ -686,6 +688,12 @@ void SwitcherData::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(); diff --git a/src/headers/switcher-data-structs.hpp b/src/headers/switcher-data-structs.hpp index 7fa4864a..64f13c0e 100644 --- a/src/headers/switcher-data-structs.hpp +++ b/src/headers/switcher-data-structs.hpp @@ -192,6 +192,7 @@ struct SwitcherData { void Start(); void Stop(); + void setWaitScene(); bool sceneChangedDuringWait(); bool prioFuncsValid(); diff --git a/src/switch-sequence.cpp b/src/switch-sequence.cpp index abdb675a..fb5cfda4 100644 --- a/src/switch-sequence.cpp +++ b/src/switch-sequence.cpp @@ -440,8 +440,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; } } From 961ea655fd5baad981a3018bce40a862d1a8de96 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 23:54:59 -0700 Subject: [PATCH 21/66] Macro pause (#201) * Add option to pause individual macros * Add macro action to pause and unpause macros --- CMakeLists.txt | 2 + data/locale/en-US.ini | 5 + src/headers/advanced-scene-switcher.hpp | 4 + src/headers/macro-action-pause.hpp | 61 ++++++++ src/headers/macro.hpp | 7 + src/macro-action-pause.cpp | 189 ++++++++++++++++++++++++ src/macro-tab.cpp | 69 +++++---- src/macro.cpp | 22 +++ 8 files changed, 334 insertions(+), 25 deletions(-) create mode 100644 src/headers/macro-action-pause.hpp create mode 100644 src/macro-action-pause.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index def9a5e6..75b31bfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-action-audio.hpp src/headers/macro-action-filter.hpp src/headers/macro-action-media.hpp + src/headers/macro-action-pause.hpp src/headers/macro-action-recording.hpp src/headers/macro-action-replay-buffer.hpp src/headers/macro-action-run.hpp @@ -145,6 +146,7 @@ set(advanced-scene-switcher_SOURCES src/macro-action-audio.cpp src/macro-action-filter.cpp src/macro-action-media.cpp + src/macro-action-pause.cpp src/macro-action-recording.cpp src/macro-action-replay-buffer.cpp src/macro-action-run.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 4c6a443b..c128270a 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -179,6 +179,10 @@ 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.pause="Pause" +AdvSceneSwitcher.action.pause.type.pause="Pause" +AdvSceneSwitcher.action.pause.type.unpause="Unpause" +AdvSceneSwitcher.action.pause.entry="{{actions}} {{macros}}" ; Transition Tab @@ -435,6 +439,7 @@ 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--" diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index 737cd3b5..7bf44ce6 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -107,6 +107,9 @@ public: 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 +140,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(); diff --git a/src/headers/macro-action-pause.hpp b/src/headers/macro-action-pause.hpp new file mode 100644 index 00000000..f2f60e77 --- /dev/null +++ b/src/headers/macro-action-pause.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#include "macro-action-edit.hpp" + +enum class PauseAction { + PAUSE, + UNPAUSE, +}; + +class MacroActionPause : 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 Create() + { + return std::make_shared(); + } + + PauseAction _action = PauseAction::PAUSE; + Macro *_macro = nullptr; + +private: + static bool _registered; + static const std::string id; +}; + +class MacroActionPauseEdit : public QWidget { + Q_OBJECT + +public: + MacroActionPauseEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionPauseEdit( + parent, + std::dynamic_pointer_cast(action)); + } + +private slots: + void MacroChanged(const QString &text); + void ActionChanged(int value); + void MacroAdd(const QString &name); + void MacroRemove(const QString &name); + void MacroRename(const QString &oldName, const QString &newName); + +protected: + QComboBox *_macros; + QComboBox *_actions; + std::shared_ptr _entryData; + +private: + QHBoxLayout *_mainLayout; + bool _loading = true; +}; diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index 7c0271a2..8c2719f8 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -5,6 +5,7 @@ #include #include #include +#include constexpr auto macro_func = 10; constexpr auto default_priority_10 = macro_func; @@ -68,6 +69,8 @@ public: bool Matched() { return _matched; } std::string Name() { return _name; } void SetName(std::string name) { _name = name; } + void SetPaused(bool pause = true) { _paused = pause; } + bool Paused() { return _paused; } std::deque> &Conditions() { return _conditions; @@ -85,4 +88,8 @@ private: std::deque> _conditions; std::deque> _actions; bool _matched = false; + bool _paused = false; }; + +Macro *GetMacroByName(const char *name); +Macro *GetMacroByQString(const QString &name); diff --git a/src/macro-action-pause.cpp b/src/macro-action-pause.cpp new file mode 100644 index 00000000..d69d71ef --- /dev/null +++ b/src/macro-action-pause.cpp @@ -0,0 +1,189 @@ +#include "headers/macro-action-pause.hpp" +#include "headers/advanced-scene-switcher.hpp" +#include "headers/utility.hpp" + +const std::string MacroActionPause::id = "pause"; + +bool MacroActionPause::_registered = MacroActionFactory::Register( + MacroActionPause::id, + {MacroActionPause::Create, MacroActionPauseEdit::Create, + "AdvSceneSwitcher.action.pause"}); + +const static std::map actionTypes = { + {PauseAction::PAUSE, "AdvSceneSwitcher.action.pause.type.pause"}, + {PauseAction::UNPAUSE, "AdvSceneSwitcher.action.pause.type.unpause"}, +}; + +bool MacroActionPause::PerformAction() +{ + if (!_macro) { + return true; + } + + switch (_action) { + case PauseAction::PAUSE: + _macro->SetPaused(); + break; + case PauseAction::UNPAUSE: + _macro->SetPaused(false); + break; + default: + break; + } + return true; +} + +void MacroActionPause::LogAction() +{ + if (!_macro) { + return; + } + switch (_action) { + case PauseAction::PAUSE: + vblog(LOG_INFO, "paused \"%s\"", _macro->Name().c_str()); + break; + case PauseAction::UNPAUSE: + vblog(LOG_INFO, "unpaused \"%s\"", _macro->Name().c_str()); + break; + default: + break; + } +} + +bool MacroActionPause::Save(obs_data_t *obj) +{ + MacroAction::Save(obj); + if (_macro) { + obs_data_set_string(obj, "macro", _macro->Name().c_str()); + } + obs_data_set_int(obj, "action", static_cast(_action)); + return true; +} + +bool MacroActionPause::Load(obs_data_t *obj) +{ + MacroAction::Load(obj); + _macro = GetMacroByName(obs_data_get_string(obj, "macro")); + _action = static_cast(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 populateMacroSelection(QComboBox *list) +{ + list->addItem(obs_module_text("AdvSceneSwitcher.selectMacro")); + for (auto &m : switcher->macros) { + list->addItem(QString::fromStdString(m.Name())); + } +} + +MacroActionPauseEdit::MacroActionPauseEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _macros = new QComboBox(); + _actions = new QComboBox(); + + populateActionSelection(_actions); + populateMacroSelection(_macros); + + QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(MacroChanged(const QString &))); + QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ActionChanged(int))); + 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 &))); + + QHBoxLayout *mainLayout = new QHBoxLayout; + std::unordered_map widgetPlaceholders = { + {"{{actions}}", _actions}, + {"{{macros}}", _macros}, + }; + placeWidgets(obs_module_text("AdvSceneSwitcher.action.pause.entry"), + mainLayout, widgetPlaceholders); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroActionPauseEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + _actions->setCurrentIndex(static_cast(_entryData->_action)); + if (_entryData->_macro) { + _macros->setCurrentText( + QString::fromStdString(_entryData->_macro->Name())); + } else { + _macros->setCurrentIndex(0); + } +} + +void MacroActionPauseEdit::MacroChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_macro = GetMacroByQString(text); +} + +void MacroActionPauseEdit::ActionChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_action = static_cast(value); +} + +void MacroActionPauseEdit::MacroAdd(const QString &name) +{ + _macros->addItem(name); +} + +void MacroActionPauseEdit::MacroRemove(const QString &name) +{ + int idx = _macros->findText(name); + if (idx == -1) { + return; + } + _macros->removeItem(idx); + if (_entryData && _entryData->_macro == GetMacroByQString(name)) { + std::lock_guard lock(switcher->m); + _entryData->_macro = nullptr; + } + _macros->setCurrentIndex(0); +} + +void MacroActionPauseEdit::MacroRename(const QString &oldName, + const QString &newName) +{ + bool renameSelected = _macros->currentText() == oldName; + int idx = _macros->findText(oldName); + if (idx == -1) { + return; + } + _macros->removeItem(idx); + _macros->insertItem(idx, newName); + if (renameSelected) { + _macros->setCurrentIndex(_macros->findText(newName)); + } +} diff --git a/src/macro-tab.cpp b/src/macro-tab.cpp index 67feda0f..4dc2ce58 100644 --- a/src/macro-tab.cpp +++ b/src/macro-tab.cpp @@ -13,13 +13,7 @@ const auto actionsCollapseThreshold = 2; 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 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 lock(switcher->m); + QString name = item->text(); + + auto m = GetMacroByQString(name); + if (m) { + m->SetPaused(item->checkState() != Qt::Checked); } } @@ -243,6 +254,12 @@ 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) { @@ -286,5 +303,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); } diff --git a/src/macro.cpp b/src/macro.cpp index a98adbb8..f8a7d106 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -20,6 +20,10 @@ Macro::~Macro() {} bool Macro::CeckMatch() { + if (_paused) { + vblog(LOG_INFO, "Macro %s is paused", _name.c_str()); + return false; + } _matched = false; for (auto &c : _conditions) { bool cond = c->CheckCondition(); @@ -80,6 +84,7 @@ bool Macro::PerformAction() 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 *conditions = obs_data_array_create(); for (auto &c : _conditions) { @@ -145,6 +150,7 @@ void setValidLogic(MacroCondition *c, bool root, std::string name) bool Macro::Load(obs_data_t *obj) { _name = obs_data_get_string(obj, "name"); + _paused = obs_data_get_bool(obj, "pause"); bool root = true; obs_data_array_t *conditions = obs_data_get_array(obj, "conditions"); @@ -363,3 +369,19 @@ 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()); +} From 5eb8378612e8160d1658af132c6394ec880e902b Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 23 May 2021 15:51:16 +0200 Subject: [PATCH 22/66] Run condition check for paused macros 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. --- src/macro.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/macro.cpp b/src/macro.cpp index f8a7d106..f008ad9c 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -20,10 +20,6 @@ Macro::~Macro() {} bool Macro::CeckMatch() { - if (_paused) { - vblog(LOG_INFO, "Macro %s is paused", _name.c_str()); - return false; - } _matched = false; for (auto &c : _conditions) { bool cond = c->CheckCondition(); @@ -64,6 +60,18 @@ bool Macro::CeckMatch() } 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; } From c2d2a27d44cfaf402a785a54be2e2e4ff80a795a Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 23 May 2021 10:09:31 +0200 Subject: [PATCH 23/66] Use more const refs where applicable --- src/advanced-scene-switcher.cpp | 2 +- src/headers/advanced-scene-switcher.hpp | 13 +++++++------ src/headers/macro-action-edit.hpp | 12 ++++++------ src/headers/macro-condition-edit.hpp | 2 +- src/headers/macro.hpp | 2 +- src/headers/scene-group.hpp | 6 +++--- src/headers/switcher-data-structs.hpp | 4 ++-- src/headers/utility.hpp | 4 ++-- src/linux/advanced-scene-switcher-nix.cpp | 4 ++-- src/macro-action-edit.cpp | 13 +++++++------ src/macro-condition-edit.cpp | 2 +- src/osx/advanced-scene-switcher-osx.mm | 4 ++-- src/switch-file.cpp | 2 +- src/switch-transitions.cpp | 6 +++--- src/switcher-data-structs.cpp | 3 ++- src/win/advanced-scene-switcher-win.cpp | 4 ++-- 16 files changed, 43 insertions(+), 40 deletions(-) diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index bb0e3b32..f416c638 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -618,7 +618,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"); diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index 7bf44ce6..c5c7fde4 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -286,8 +286,8 @@ private: void GetWindowList(std::vector &windows); void GetWindowList(QStringList &windows); void GetCurrentWindowTitle(std::string &title); -bool isFullscreen(std::string &title); -bool isMaximized(std::string &title); +bool isFullscreen(const std::string &title); +bool isMaximized(const std::string &title); /****************************************************************************** * Screenregion helper @@ -309,11 +309,12 @@ 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); /****************************************************************************** * Main SwitcherData diff --git a/src/headers/macro-action-edit.hpp b/src/headers/macro-action-edit.hpp index cedf39a8..e6db7f35 100644 --- a/src/headers/macro-action-edit.hpp +++ b/src/headers/macro-action-edit.hpp @@ -20,12 +20,12 @@ struct MacroActionInfo { class MacroActionFactory { public: MacroActionFactory() = delete; - static bool Register(std::string id, MacroActionInfo); - static std::shared_ptr Create(const std::string id); - static QWidget *CreateWidget(const std::string id, QWidget *parent, + static bool Register(const std::string &id, MacroActionInfo); + static std::shared_ptr Create(const std::string &id); + static QWidget *CreateWidget(const std::string &id, QWidget *parent, std::shared_ptr action); static auto GetActionTypes() { return _methods; } - static std::string GetActionName(const std::string id); + static std::string GetActionName(const std::string &id); static std::string GetIdByName(const QString &name); private: @@ -38,9 +38,9 @@ class MacroActionEdit : public QWidget { public: MacroActionEdit(QWidget *parent = nullptr, std::shared_ptr * = nullptr, - std::string id = "scene_switch", + const std::string &id = "scene_switch", bool startCollapsed = false); - void UpdateEntryData(std::string id); + void UpdateEntryData(const std::string &id); void Collapse(bool collapsed); private slots: diff --git a/src/headers/macro-condition-edit.hpp b/src/headers/macro-condition-edit.hpp index 775ecf6c..2b05c238 100644 --- a/src/headers/macro-condition-edit.hpp +++ b/src/headers/macro-condition-edit.hpp @@ -21,7 +21,7 @@ class MacroConditionFactory { public: MacroConditionFactory() = delete; static bool Register(const std::string &, MacroConditionInfo); - static std::shared_ptr Create(const std::string); + static std::shared_ptr Create(const std::string &); static QWidget *CreateWidget(const std::string &id, QWidget *parent, std::shared_ptr); static auto GetConditionTypes() { return _methods; } diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index 8c2719f8..7468ee99 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -68,7 +68,7 @@ public: bool PerformAction(); bool Matched() { return _matched; } std::string Name() { return _name; } - void SetName(std::string name) { _name = name; } + void SetName(const std::string &name) { _name = name; } void SetPaused(bool pause = true) { _paused = pause; } bool Paused() { return _paused; } std::deque> &Conditions() diff --git a/src/headers/scene-group.hpp b/src/headers/scene-group.hpp index b29dc1ae..2e79f3e6 100644 --- a/src/headers/scene-group.hpp +++ b/src/headers/scene-group.hpp @@ -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 scenes_, int count_, + inline SceneGroup(const std::string &name_) : name(name_){}; + inline SceneGroup(const std::string &name_, AdvanceCondition type_, + const std::vector &scenes_, int count_, double time_, bool repeat_) : name(name_), type(type_), diff --git a/src/headers/switcher-data-structs.hpp b/src/headers/switcher-data-structs.hpp index 64f13c0e..a9ac72cb 100644 --- a/src/headers/switcher-data-structs.hpp +++ b/src/headers/switcher-data-structs.hpp @@ -201,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, @@ -254,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 ¤tVersion); void loadSettings(obs_data_t *obj); void loadMacros(obs_data_t *obj); diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp index b427141b..f39835b8 100644 --- a/src/headers/utility.hpp +++ b/src/headers/utility.hpp @@ -114,7 +114,7 @@ static inline OBSWeakSource GetWeakFilterByQString(OBSWeakSource source, } static inline std::string -getNextDelim(std::string text, +getNextDelim(const std::string &text, std::unordered_map placeholders) { size_t pos = std::string::npos; @@ -214,7 +214,7 @@ static inline bool compareIgnoringLineEnding(QString &s1, QString &s2) return true; } -static inline bool DisplayMessage(QString msg, bool question = false) +static inline bool DisplayMessage(const QString &msg, bool question = false) { if (question) { QMessageBox::StandardButton reply; diff --git a/src/linux/advanced-scene-switcher-nix.cpp b/src/linux/advanced-scene-switcher-nix.cpp index 9f1c1db3..297ea2b3 100644 --- a/src/linux/advanced-scene-switcher-nix.cpp +++ b/src/linux/advanced-scene-switcher-nix.cpp @@ -253,7 +253,7 @@ std::pair 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; diff --git a/src/macro-action-edit.cpp b/src/macro-action-edit.cpp index 08475b49..eb61f550 100644 --- a/src/macro-action-edit.cpp +++ b/src/macro-action-edit.cpp @@ -5,7 +5,7 @@ std::map MacroActionFactory::_methods; -bool MacroActionFactory::Register(std::string 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(std::string id, MacroActionInfo info) return false; } -std::shared_ptr MacroActionFactory::Create(const std::string id) +std::shared_ptr 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 MacroActionFactory::Create(const std::string id) return nullptr; } -QWidget *MacroActionFactory::CreateWidget(const std::string id, QWidget *parent, +QWidget *MacroActionFactory::CreateWidget(const std::string &id, + QWidget *parent, std::shared_ptr action) { if (auto it = _methods.find(id); it != _methods.end()) @@ -31,7 +32,7 @@ QWidget *MacroActionFactory::CreateWidget(const std::string id, QWidget *parent, return nullptr; } -std::string MacroActionFactory::GetActionName(const std::string id) +std::string MacroActionFactory::GetActionName(const std::string &id) { if (auto it = _methods.find(id); it != _methods.end()) { return it->second._name; @@ -58,7 +59,7 @@ static inline void populateActionSelection(QComboBox *list) MacroActionEdit::MacroActionEdit(QWidget *parent, std::shared_ptr *entryData, - std::string id, bool startCollapsed) + const std::string &id, bool startCollapsed) : QWidget(parent) { _actionSelection = new QComboBox(); @@ -100,7 +101,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text) _section->Collapse(false); } -void MacroActionEdit::UpdateEntryData(std::string id) +void MacroActionEdit::UpdateEntryData(const std::string &id) { _actionSelection->setCurrentText( obs_module_text(MacroActionFactory::GetActionName(id).c_str())); diff --git a/src/macro-condition-edit.cpp b/src/macro-condition-edit.cpp index 488797f8..71c77c1b 100644 --- a/src/macro-condition-edit.cpp +++ b/src/macro-condition-edit.cpp @@ -14,7 +14,7 @@ bool MacroConditionFactory::Register(const std::string &id, } std::shared_ptr -MacroConditionFactory::Create(const std::string id) +MacroConditionFactory::Create(const std::string &id) { if (auto it = _methods.find(id); it != _methods.end()) return it->second._createFunc(); diff --git a/src/osx/advanced-scene-switcher-osx.mm b/src/osx/advanced-scene-switcher-osx.mm index d5cdebdd..9cb9ddd8 100644 --- a/src/osx/advanced-scene-switcher-osx.mm +++ b/src/osx/advanced-scene-switcher-osx.mm @@ -155,7 +155,7 @@ bool nameMachesPattern(std::string windowName, std::string pattern) .contains(QRegularExpression(QString::fromStdString(pattern))); } -bool isMaximized(std::string &title) +bool isMaximized(const std::string &title) { @autoreleasepool { NSArray *screens = [NSScreen screens]; @@ -204,7 +204,7 @@ bool isWindowFullscreenOnScreen(NSDictionary *app, NSScreen *screen) return NSEqualSizes(windowBounds.size, screenFrame.size); } -bool isFullscreen(std::string &title) +bool isFullscreen(const std::string &title) { @autoreleasepool { NSArray *screens = [NSScreen screens]; diff --git a/src/switch-file.cpp b/src/switch-file.cpp index da050bb2..f144f3b7 100644 --- a/src/switch-file.cpp +++ b/src/switch-file.cpp @@ -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; diff --git a/src/switch-transitions.cpp b/src/switch-transitions.cpp index 494804de..221b8ca8 100644 --- a/src/switch-transitions.cpp +++ b/src/switch-transitions.cpp @@ -240,7 +240,7 @@ std::pair 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: diff --git a/src/switcher-data-structs.cpp b/src/switcher-data-structs.cpp index b889bd69..b892494f 100644 --- a/src/switcher-data-structs.cpp +++ b/src/switcher-data-structs.cpp @@ -124,7 +124,8 @@ bool SwitcherData::versionChanged(obs_data_t *obj, std::string currentVersion) return previousVersion != currentVersion; } -void SwitcherData::saveVersion(obs_data_t *obj, std::string currentVersion) +void SwitcherData::saveVersion(obs_data_t *obj, + const std::string ¤tVersion) { obs_data_set_string(obj, "version", currentVersion.c_str()); } diff --git a/src/win/advanced-scene-switcher-win.cpp b/src/win/advanced-scene-switcher-win.cpp index 02e64712..13f4c247 100644 --- a/src/win/advanced-scene-switcher-win.cpp +++ b/src/win/advanced-scene-switcher-win.cpp @@ -142,7 +142,7 @@ HWND getHWNDfromTitle(std::string title) return hwnd; } -bool isMaximized(std::string &title) +bool isMaximized(const std::string &title) { RECT appBounds; MONITORINFO monitorInfo = {0}; @@ -172,7 +172,7 @@ bool isMaximized(std::string &title) return false; } -bool isFullscreen(std::string &title) +bool isFullscreen(const std::string &title) { RECT appBounds; MONITORINFO monitorInfo = {0}; From 32101627a6658d18fab6caccbc3f3099b5a918c9 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 23 May 2021 18:38:57 +0200 Subject: [PATCH 24/66] Remove unused settings value --- src/switch-pause.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/switch-pause.cpp b/src/switch-pause.cpp index 723a9f03..0a7570cb 100644 --- a/src/switch-pause.cpp +++ b/src/switch-pause.cpp @@ -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); } From a93cec2bb9079abb5872c174c86c59ef13dc9e6f Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 13:44:51 +0200 Subject: [PATCH 25/66] Fix typo --- data/locale/en-US.ini | 4 ++-- src/macro-condition-audio.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index c128270a..d0184577 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -80,8 +80,8 @@ 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.state.below="Below" +AdvSceneSwitcher.condition.audio.state.above="Above" AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}} for {{duration}} seconds" AdvSceneSwitcher.condition.region="Screen region" AdvSceneSwitcher.condition.region.entry="Cursor is in {{minX}} {{minY}} x {{maxX}} {{maxY}}" diff --git a/src/macro-condition-audio.cpp b/src/macro-condition-audio.cpp index dce2fa8f..b84d6795 100644 --- a/src/macro-condition-audio.cpp +++ b/src/macro-condition-audio.cpp @@ -11,8 +11,8 @@ bool MacroConditionAudio::_registered = MacroConditionFactory::Register( "AdvSceneSwitcher.condition.audio"}); static std::map 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() From 5496c256c0eaae2af02576828a72c7058d0ca210 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 14:28:12 +0200 Subject: [PATCH 26/66] Move functions not required in advanced-scene-switcher.hpp Create header for platform specific functions. Move some UI helper functions to utility.hpp. --- CMakeLists.txt | 4 +- src/advanced-scene-switcher.cpp | 328 -------------- src/general.cpp | 7 +- src/headers/advanced-scene-switcher.hpp | 62 +-- src/headers/platform-funcs.hpp | 11 + src/headers/utility.hpp | 268 ++---------- src/macro-action-audio.cpp | 2 +- src/macro-action-media.cpp | 2 +- src/macro-action-scene-visibility.cpp | 2 +- src/macro-action-source.cpp | 18 +- src/macro-condition-audio.cpp | 2 +- src/macro-condition-media.cpp | 2 +- src/macro-condition-process.cpp | 2 +- src/macro-condition-scene.cpp | 2 +- src/macro-condition-video.cpp | 2 +- src/macro-condition-window.cpp | 2 +- src/macro-tab.cpp | 4 +- src/scene-group.cpp | 5 +- src/scene-trigger.cpp | 11 +- src/switch-audio.cpp | 6 +- src/switch-executable.cpp | 7 +- src/switch-file.cpp | 4 +- src/switch-generic.cpp | 7 +- src/switch-media.cpp | 6 +- src/switch-pause.cpp | 6 +- src/switch-random.cpp | 11 +- src/switch-screen-region.cpp | 9 +- src/switch-sequence.cpp | 7 +- src/switch-time.cpp | 4 +- src/switch-transitions.cpp | 2 +- src/switch-video.cpp | 9 +- src/switch-window.cpp | 7 +- src/utility.cpp | 555 ++++++++++++++++++++++++ 33 files changed, 700 insertions(+), 676 deletions(-) create mode 100644 src/headers/platform-funcs.hpp create mode 100644 src/utility.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 75b31bfa..c4665f57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ -113,6 +112,8 @@ set(advanced-scene-switcher_HEADERS 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 ) @@ -176,6 +177,7 @@ set(advanced-scene-switcher_SOURCES src/name-dialog.cpp src/duration-control.cpp src/section.cpp + src/utility.cpp src/volume-control.cpp src/version.cpp ) diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index f416c638..d3105c54 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -1,13 +1,8 @@ #include #include -#include -#include -#include -#include #include #include -#include #include "headers/advanced-scene-switcher.hpp" #include "headers/curl-helper.hpp" @@ -72,329 +67,6 @@ void AdvSceneSwitcher::loadUI() loading = false; } -/****************************************************************************** - * UI helpers - ******************************************************************************/ -void AdvSceneSwitcher::addSelectionEntry(QComboBox *sel, - const char *description, - bool selectable, const char *tooltip) -{ - sel->addItem(description); - - if (strcmp(tooltip, "") != 0) { - sel->setItemData(0, tooltip, Qt::ToolTipRole); - } - - QStandardItemModel *model = - qobject_cast(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 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 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 *list = - reinterpret_cast *>(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 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 *list = - reinterpret_cast *>(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 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 *list = - reinterpret_cast *>(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 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"); - return; - } - - if (addButton && addHighlight) { - addButton->disconnect(*addHighlight); - } - - 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; - } - - 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; -} - -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 ******************************************************************************/ diff --git a/src/general.cpp b/src/general.cpp index 161c8519..4e903cb7 100644 --- a/src/general.cpp +++ b/src/general.cpp @@ -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; } diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index c5c7fde4..519adc9b 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -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, ...) \ @@ -68,44 +69,8 @@ public: void setupMacroTab(); 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); @@ -280,31 +245,6 @@ public slots: private: }; -/****************************************************************************** - * Windowtitle helper - ******************************************************************************/ -void GetWindowList(std::vector &windows); -void GetWindowList(QStringList &windows); -void GetCurrentWindowTitle(std::string &title); -bool isFullscreen(const std::string &title); -bool isMaximized(const std::string &title); - -/****************************************************************************** - * Screenregion helper - ******************************************************************************/ -std::pair getCursorPos(); - -/****************************************************************************** - * Idle detection helper - ******************************************************************************/ -int secondsSinceLastInput(); - -/****************************************************************************** - * Executable helper - ******************************************************************************/ -void GetProcessList(QStringList &processes); -bool isInFocus(const QString &executable); - /****************************************************************************** * Sceneswitch helper ******************************************************************************/ diff --git a/src/headers/platform-funcs.hpp b/src/headers/platform-funcs.hpp new file mode 100644 index 00000000..d6164901 --- /dev/null +++ b/src/headers/platform-funcs.hpp @@ -0,0 +1,11 @@ +#pragma once + +void GetWindowList(std::vector &windows); +void GetWindowList(QStringList &windows); +void GetCurrentWindowTitle(std::string &title); +bool isFullscreen(const std::string &title); +bool isMaximized(const std::string &title); +std::pair getCursorPos(); +int secondsSinceLastInput(); +void GetProcessList(QStringList &processes); +bool isInFocus(const QString &executable); diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp index f39835b8..b300629e 100644 --- a/src/headers/utility.hpp +++ b/src/headers/utility.hpp @@ -1,139 +1,26 @@ #pragma once - #include -#include #include -#include -#include -#include - +#include +#include +#include +#include +#include #include -#include #include +#include +#include +#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 OBSWeakSource GetWeakFilterByName(OBSWeakSource source, - const char *name) -{ - OBSWeakSource weak; - auto s = obs_weak_source_get_source(source); - if (s) { - auto filterSource = obs_source_get_filter_by_name(s, name); - weak = obs_source_get_weak_source(filterSource); - obs_weak_source_release(weak); - obs_source_release(filterSource); - obs_source_release(s); - } - return weak; -} - -static inline OBSWeakSource GetWeakFilterByQString(OBSWeakSource source, - const QString &name) -{ - return GetWeakFilterByName(source, name.toUtf8().constData()); -} - -static inline std::string -getNextDelim(const std::string &text, - std::unordered_map 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); /** * Populate layout with labels and widgets based on provided text @@ -143,95 +30,32 @@ getNextDelim(const 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 placeholders, - bool addStretch = true) -{ - std::vector> 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(const 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 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 *sceneGroups = nullptr, + bool addSelect = true, std::string selectText = "", + bool selectable = false); diff --git a/src/macro-action-audio.cpp b/src/macro-action-audio.cpp index 354c8b90..ca4bf929 100644 --- a/src/macro-action-audio.cpp +++ b/src/macro-action-audio.cpp @@ -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))); diff --git a/src/macro-action-media.cpp b/src/macro-action-media.cpp index 827bc583..8e0e890d 100644 --- a/src/macro-action-media.cpp +++ b/src/macro-action-media.cpp @@ -99,7 +99,7 @@ MacroActionMediaEdit::MacroActionMediaEdit( _actions = new QComboBox(); populateActionSelection(_actions); - AdvSceneSwitcher::populateMediaSelection(_mediaSources); + populateMediaSelection(_mediaSources); QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, SLOT(ActionChanged(int))); diff --git a/src/macro-action-scene-visibility.cpp b/src/macro-action-scene-visibility.cpp index c1b480ae..3d2b1f03 100644 --- a/src/macro-action-scene-visibility.cpp +++ b/src/macro-action-scene-visibility.cpp @@ -124,7 +124,7 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit( _actions = new QComboBox(); populateActionSelection(_actions); - AdvSceneSwitcher::populateSceneSelection(_scenes); + populateSceneSelection(_scenes); QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, SLOT(ActionChanged(int))); diff --git a/src/macro-action-source.cpp b/src/macro-action-source.cpp index b58f2cfb..8da1d692 100644 --- a/src/macro-action-source.cpp +++ b/src/macro-action-source.cpp @@ -67,22 +67,6 @@ static inline void populateActionSelection(QComboBox *list) } } -static inline void populateSources(QComboBox *list) -{ - auto enumSourcesWithSources = [](void *param, obs_source_t *source) { - if (!source) { - return true; - } - QComboBox *list = reinterpret_cast(param); - list->addItem(obs_source_get_name(source)); - return true; - }; - - list->clear(); - list->addItem(obs_module_text("AdvSceneSwitcher.selectSource")); - obs_enum_sources(enumSourcesWithSources, list); -} - MacroActionSourceEdit::MacroActionSourceEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent) @@ -91,7 +75,7 @@ MacroActionSourceEdit::MacroActionSourceEdit( _actions = new QComboBox(); populateActionSelection(_actions); - populateSources(_sources); + populateSourceSelection(_sources); QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, SLOT(ActionChanged(int))); diff --git a/src/macro-condition-audio.cpp b/src/macro-condition-audio.cpp index b84d6795..dbb0c83b 100644 --- a/src/macro-condition-audio.cpp +++ b/src/macro-condition-audio.cpp @@ -139,7 +139,7 @@ MacroConditionAudioEdit::MacroConditionAudioEdit( SIGNAL(currentTextChanged(const QString &)), this, SLOT(SourceChanged(const QString &))); - AdvSceneSwitcher::populateAudioSelection(_audioSources); + populateAudioSelection(_audioSources); populateConditionSelection(_condition); QHBoxLayout *switchLayout = new QHBoxLayout; diff --git a/src/macro-condition-media.cpp b/src/macro-condition-media.cpp index d0e46bc2..d05ce8fd 100644 --- a/src/macro-condition-media.cpp +++ b/src/macro-condition-media.cpp @@ -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); diff --git a/src/macro-condition-process.cpp b/src/macro-condition-process.cpp index eb1ab373..a71341de 100644 --- a/src/macro-condition-process.cpp +++ b/src/macro-condition-process.cpp @@ -61,7 +61,7 @@ MacroConditionProcessEdit::MacroConditionProcessEdit( QWidget::connect(_focused, SIGNAL(stateChanged(int)), this, SLOT(FocusChanged(int))); - AdvSceneSwitcher::populateProcessSelection(_processSelection); + populateProcessSelection(_processSelection); std::unordered_map widgetPlaceholders = { {"{{processes}}", _processSelection}, diff --git a/src/macro-condition-scene.cpp b/src/macro-condition-scene.cpp index 8508b9cd..c0b99799 100644 --- a/src/macro-condition-scene.cpp +++ b/src/macro-condition-scene.cpp @@ -79,7 +79,7 @@ MacroConditionSceneEdit::MacroConditionSceneEdit( QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this, SLOT(DurationUnitChanged(DurationUnit))); - AdvSceneSwitcher::populateSceneSelection(_sceneSelection); + populateSceneSelection(_sceneSelection); populateTypeSelection(_sceneType); QHBoxLayout *mainLayout = new QHBoxLayout; diff --git a/src/macro-condition-video.cpp b/src/macro-condition-video.cpp index 85241501..66d55f74 100644 --- a/src/macro-condition-video.cpp +++ b/src/macro-condition-video.cpp @@ -160,7 +160,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( QWidget::connect(_browseButton, SIGNAL(clicked()), this, SLOT(BrowseButtonClicked())); - AdvSceneSwitcher::populateVideoSelection(_videoSelection, false); + populateVideoSelection(_videoSelection); populateConditionSelection(_condition); QHBoxLayout *mainLayout = new QHBoxLayout; diff --git a/src/macro-condition-window.cpp b/src/macro-condition-window.cpp index 40751b95..3a4d2407 100644 --- a/src/macro-condition-window.cpp +++ b/src/macro-condition-window.cpp @@ -115,7 +115,7 @@ MacroConditionWindowEdit::MacroConditionWindowEdit( QWidget::connect(_focused, SIGNAL(stateChanged(int)), this, SLOT(FocusChanged(int))); - AdvSceneSwitcher::populateWindowSelection(_windowSelection); + populateWindowSelection(_windowSelection); std::unordered_map widgetPlaceholders = { {"{{windows}}", _windowSelection}, diff --git a/src/macro-tab.cpp b/src/macro-tab.cpp index 4dc2ce58..ef2d69e4 100644 --- a/src/macro-tab.cpp +++ b/src/macro-tab.cpp @@ -263,7 +263,9 @@ void AdvSceneSwitcher::setupMacroTab() } 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); diff --git a/src/scene-group.cpp b/src/scene-group.cpp index b1c3cb9b..fe18da9d 100644 --- a/src/scene-group.cpp +++ b/src/scene-group.cpp @@ -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); diff --git a/src/scene-trigger.cpp b/src/scene-trigger.cpp index 055d373b..60a434de 100644 --- a/src/scene-trigger.cpp +++ b/src/scene-trigger.cpp @@ -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(s->triggerType)); diff --git a/src/switch-audio.cpp b/src/switch-audio.cpp index 03ff4197..0473346d 100644 --- a/src/switch-audio.cpp +++ b/src/switch-audio.cpp @@ -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) { diff --git a/src/switch-executable.cpp b/src/switch-executable.cpp index 04073ff7..42138f09 100644 --- a/src/switch-executable.cpp +++ b/src/switch-executable.cpp @@ -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); diff --git a/src/switch-file.cpp b/src/switch-file.cpp index f144f3b7..f963e79d 100644 --- a/src/switch-file.cpp +++ b/src/switch-file.cpp @@ -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); diff --git a/src/switch-generic.cpp b/src/switch-generic.cpp index f8cb8c5e..ef4863b5 100644 --- a/src/switch-generic.cpp +++ b/src/switch-generic.cpp @@ -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(); diff --git a/src/switch-media.cpp b/src/switch-media.cpp index c83c1627..4849ec1b 100644 --- a/src/switch-media.cpp +++ b/src/switch-media.cpp @@ -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); diff --git a/src/switch-pause.cpp b/src/switch-pause.cpp index 0a7570cb..3a4511eb 100644 --- a/src/switch-pause.cpp +++ b/src/switch-pause.cpp @@ -264,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); @@ -315,7 +317,7 @@ PauseEntryWidget::PauseEntryWidget(QWidget *parent, PauseEntry *s) populatePauseTypes(pauseTypes); populatePauseTargets(pauseTargets); - AdvSceneSwitcher::populateWindowSelection(windows); + populateWindowSelection(windows); windows->setEditable(true); windows->setMaxVisibleItems(20); diff --git a/src/switch-random.cpp b/src/switch-random.cpp index 920613c7..b45f1790 100644 --- a/src/switch-random.cpp +++ b/src/switch-random.cpp @@ -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); } diff --git a/src/switch-screen-region.cpp b/src/switch-screen-region.cpp index 7d140f68..99d4874c 100644 --- a/src/switch-screen-region.cpp +++ b/src/switch-screen-region.cpp @@ -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); diff --git a/src/switch-sequence.cpp b/src/switch-sequence.cpp index fb5cfda4..a9889845 100644 --- a/src/switch-sequence.cpp +++ b/src/switch-sequence.cpp @@ -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); @@ -586,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")); diff --git a/src/switch-time.cpp b/src/switch-time.cpp index 748aa951..1e643ff7 100644 --- a/src/switch-time.cpp +++ b/src/switch-time.cpp @@ -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); diff --git a/src/switch-transitions.cpp b/src/switch-transitions.cpp index 221b8ca8..05aed606 100644 --- a/src/switch-transitions.cpp +++ b/src/switch-transitions.cpp @@ -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()); diff --git a/src/switch-video.cpp b/src/switch-video.cpp index 95ef3041..8ec1cd15 100644 --- a/src/switch-video.cpp +++ b/src/switch-video.cpp @@ -194,7 +194,9 @@ void AdvSceneSwitcher::setupVideoTab() } if (switcher->videoSwitches.size() == 0) { - addPulse = PulseWidget(ui->videoAdd, QColor(Qt::green)); + if (!switcher->disableHints) { + addPulse = PulseWidget(ui->videoAdd, QColor(Qt::green)); + } ui->videoHelp->setVisible(true); } else { ui->videoHelp->setVisible(false); @@ -399,10 +401,7 @@ VideoSwitchWidget::VideoSwitchWidget(QWidget *parent, VideoSwitch *s) QWidget::connect(ignoreInactiveSource, SIGNAL(stateChanged(int)), this, SLOT(IgnoreInactiveChanged(int))); - // TODO: - // Figure out why scene do not work for "match exactly". - // Until then do not allow selecting scenes - AdvSceneSwitcher::populateVideoSelection(videoSources, false); + populateVideoSelection(videoSources); populateConditionSelection(condition); if (s) { diff --git a/src/switch-window.cpp b/src/switch-window.cpp index a9ee073c..f9455382 100644 --- a/src/switch-window.cpp +++ b/src/switch-window.cpp @@ -348,7 +348,10 @@ void AdvSceneSwitcher::setupTitleTab() } if (switcher->windowSwitches.size() == 0) { - addPulse = PulseWidget(ui->windowAdd, QColor(Qt::green)); + if (!switcher->disableHints) { + addPulse = + PulseWidget(ui->windowAdd, QColor(Qt::green)); + } ui->windowHelp->setVisible(true); } else { ui->windowHelp->setVisible(false); @@ -418,7 +421,7 @@ WindowSwitchWidget::WindowSwitchWidget(QWidget *parent, WindowSwitch *s) QWidget::connect(focused, SIGNAL(stateChanged(int)), this, SLOT(FocusChanged(int))); - AdvSceneSwitcher::populateWindowSelection(windows); + populateWindowSelection(windows); windows->setEditable(true); windows->setMaxVisibleItems(20); diff --git a/src/utility.cpp b/src/utility.cpp new file mode 100644 index 00000000..413571d0 --- /dev/null +++ b/src/utility.cpp @@ -0,0 +1,555 @@ +#pragma once +#include "headers/utility.hpp" +#include "headers/platform-funcs.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + +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; +} + +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; +} + +OBSWeakSource GetWeakSourceByQString(const QString &name) +{ + return GetWeakSourceByName(name.toUtf8().constData()); +} + +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; +} + +OBSWeakSource GetWeakTransitionByQString(const QString &name) +{ + return GetWeakTransitionByName(name.toUtf8().constData()); +} + +OBSWeakSource GetWeakFilterByName(OBSWeakSource source, const char *name) +{ + OBSWeakSource weak; + auto s = obs_weak_source_get_source(source); + if (s) { + auto filterSource = obs_source_get_filter_by_name(s, name); + weak = obs_source_get_weak_source(filterSource); + obs_weak_source_release(weak); + obs_source_release(filterSource); + obs_source_release(s); + } + return weak; +} + +OBSWeakSource GetWeakFilterByQString(OBSWeakSource source, const QString &name) +{ + return GetWeakFilterByName(source, name.toUtf8().constData()); +} + +std::string +getNextDelim(const std::string &text, + std::unordered_map 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; +} + +void placeWidgets(std::string text, QBoxLayout *layout, + std::unordered_map placeholders, + bool addStretch) +{ + std::vector> 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(); + } +} + +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; + } +} + +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; +} + +bool DisplayMessage(const QString &msg, bool question) +{ + 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 addSelectionEntry(QComboBox *sel, const char *description, bool selectable, + const char *tooltip) +{ + sel->addItem(description); + + if (strcmp(tooltip, "") != 0) { + sel->setItemData(0, tooltip, Qt::ToolTipRole); + } + + QStandardItemModel *model = + qobject_cast(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 populateSourceSelection(QComboBox *list, bool addSelect) +{ + auto enumSourcesWithSources = [](void *param, obs_source_t *source) { + if (!source) { + return true; + } + QComboBox *list = reinterpret_cast(param); + list->addItem(obs_source_get_name(source)); + return true; + }; + + list->clear(); + if (addSelect) { + addSelectionEntry( + list, obs_module_text("AdvSceneSwitcher.selectSource"), + false); + } + + obs_enum_sources(enumSourcesWithSources, list); +} + +void 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 populateWindowSelection(QComboBox *sel, bool addSelect) +{ + if (addSelect) { + addSelectionEntry( + sel, obs_module_text("AdvSceneSwitcher.selectWindow")); + } + + std::vector 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 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 *list = + reinterpret_cast *>(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 audioSources; + obs_enum_sources(sourceEnum, &audioSources); + sort(audioSources.begin(), audioSources.end()); + for (std::string &source : audioSources) { + sel->addItem(source.c_str()); + } +} + +void populateVideoSelection(QComboBox *sel, 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 *list = + reinterpret_cast *>(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 videoSources; + obs_enum_sources(sourceEnum, &videoSources); + sort(videoSources.begin(), videoSources.end()); + for (std::string &source : videoSources) { + sel->addItem(source.c_str()); + } +} + +void 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 *list = + reinterpret_cast *>(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 mediaSources; + obs_enum_sources(sourceEnum, &mediaSources); + sort(mediaSources.begin(), mediaSources.end()); + for (std::string &source : mediaSources) { + sel->addItem(source.c_str()); + } +} + +void 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 populateSceneSelection(QComboBox *sel, bool addPrevious, + bool addSceneGroup, + std::deque *sceneGroups, 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 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 && sceneGroups) { + for (auto &sg : *sceneGroups) { + sel->addItem(QString::fromStdString(sg.name)); + } + } +} + +QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor, + QColor startColor, QString specifier) +{ + 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; +} + +void listAddClicked(QListWidget *list, QWidget *newWidget, + QPushButton *addButton, + QMetaObject::Connection *addHighlight) +{ + if (!list || !newWidget) { + blog(LOG_WARNING, + "listAddClicked called without valid list or widget"); + return; + } + + if (addButton && addHighlight) { + addButton->disconnect(*addHighlight); + } + + QListWidgetItem *item; + item = new QListWidgetItem(list); + list->addItem(item); + item->setSizeHint(newWidget->minimumSizeHint()); + list->setItemWidget(item, newWidget); + + list->scrollToItem(item); +} + +bool listMoveUp(QListWidget *list) +{ + int index = list->currentRow(); + if (index == -1 || index == 0) { + return false; + } + + 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; +} + +bool 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; +} From 198a5dc1ec8d3167fc83c96ae7e661bad4a6a1ef Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 15:10:34 +0200 Subject: [PATCH 27/66] Move AskBackup() --- src/advanced-scene-switcher.cpp | 31 +++++++++++++++++++++++- src/general.cpp | 32 ++----------------------- src/headers/advanced-scene-switcher.hpp | 1 - 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index d3105c54..c9580f28 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -70,6 +71,34 @@ void AdvSceneSwitcher::loadUI() /****************************************************************************** * Saving and loading ******************************************************************************/ +void 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()); +} + static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) { if (saving) { @@ -94,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); diff --git a/src/general.cpp b/src/general.cpp index 4e903cb7..8e90871c 100644 --- a/src/general.cpp +++ b/src/general.cpp @@ -1,9 +1,9 @@ -#include - #include "headers/advanced-scene-switcher.hpp" #include "headers/utility.hpp" #include "headers/version.h" +#include + QMetaObject::Connection inactivePluse; void AdvSceneSwitcher::on_close_clicked() @@ -210,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( diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index 519adc9b..fa23cb96 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -69,7 +69,6 @@ public: void setupMacroTab(); void setTabOrder(); void restoreWindowGeo(); - static void AskBackup(obs_data_t *obj); signals: void MacroAdded(const QString &name); From e7f031641fc54690338f91bcc0c2bf902fa0b55a Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 15:16:31 +0200 Subject: [PATCH 28/66] Add warning for macro action source --- data/locale/en-US.ini | 1 + src/headers/macro-action-source.hpp | 2 ++ src/macro-action-source.cpp | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index d0184577..cd17b4ef 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -171,6 +171,7 @@ AdvSceneSwitcher.action.source="Source" AdvSceneSwitcher.action.source.type.enable="Enable" AdvSceneSwitcher.action.source.type.disable="Disable" 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.media="Media" AdvSceneSwitcher.action.media.type.play="Play" AdvSceneSwitcher.action.media.type.pause="Pause" diff --git a/src/headers/macro-action-source.hpp b/src/headers/macro-action-source.hpp index db52358d..0e9f5c02 100644 --- a/src/headers/macro-action-source.hpp +++ b/src/headers/macro-action-source.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include "macro-action-edit.hpp" enum class SourceAction { @@ -50,6 +51,7 @@ private slots: protected: QComboBox *_sources; QComboBox *_actions; + QLabel *_warning; std::shared_ptr _entryData; private: diff --git a/src/macro-action-source.cpp b/src/macro-action-source.cpp index 8da1d692..09e27f8c 100644 --- a/src/macro-action-source.cpp +++ b/src/macro-action-source.cpp @@ -73,6 +73,8 @@ MacroActionSourceEdit::MacroActionSourceEdit( { _sources = new QComboBox(); _actions = new QComboBox(); + _warning = new QLabel( + obs_module_text("AdvSceneSwitcher.action.source.warning")); populateActionSelection(_actions); populateSourceSelection(_sources); @@ -82,13 +84,16 @@ MacroActionSourceEdit::MacroActionSourceEdit( QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)), this, SLOT(SourceChanged(const QString &))); - QHBoxLayout *mainLayout = new QHBoxLayout; + QVBoxLayout *mainLayout = new QVBoxLayout; + QHBoxLayout *entryLayout = new QHBoxLayout; std::unordered_map widgetPlaceholders = { {"{{sources}}", _sources}, {"{{actions}}", _actions}, }; placeWidgets(obs_module_text("AdvSceneSwitcher.action.source.entry"), - mainLayout, widgetPlaceholders); + entryLayout, widgetPlaceholders); + mainLayout->addLayout(entryLayout); + mainLayout->addWidget(_warning); setLayout(mainLayout); _entryData = entryData; From 881e197cb21dd47db988a6932cfa662fe0225f8d Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 02:44:35 +0200 Subject: [PATCH 29/66] Fix visibility of scene items in groups not changing Also allow groups to be selected as target for visibility action --- src/macro-action-scene-visibility.cpp | 57 +++++++++++++++++---------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/macro-action-scene-visibility.cpp b/src/macro-action-scene-visibility.cpp index 3d2b1f03..706c6995 100644 --- a/src/macro-action-scene-visibility.cpp +++ b/src/macro-action-scene-visibility.cpp @@ -17,20 +17,40 @@ const static std::map actionTypes = { "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(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_sceneitem_set_visible( - obs_scene_find_source(scene, sourceName.c_str()), true); + obs_scene_enum_items(scene, visibilityEnum, &vInfo); break; case SceneVisibilityAction::HIDE: - obs_sceneitem_set_visible( - obs_scene_find_source(scene, sourceName.c_str()), - false); + obs_scene_enum_items(scene, visibilityEnum, &vInfo); break; default: break; @@ -83,24 +103,14 @@ static inline void populateActionSelection(QComboBox *list) static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr) { - QComboBox *list = reinterpret_cast(ptr); + std::set *names = reinterpret_cast *>(ptr); if (obs_sceneitem_is_group(item)) { - obs_data_t *data = obs_sceneitem_get_private_settings(item); - - bool collapse = obs_data_get_bool(data, "collapsed"); - if (!collapse) { - obs_scene_t *scene = - obs_sceneitem_group_get_scene(item); - - obs_scene_enum_items(scene, enumItem, ptr); - } - - obs_data_release(data); - } else { - auto name = obs_source_get_name(obs_sceneitem_get_source(item)); - list->addItem(name); + 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; } @@ -109,10 +119,15 @@ static inline void populateSceneItems(QComboBox *list, { list->clear(); list->addItem(obs_module_text("AdvSceneSwitcher.selectItem")); + std::set names; auto s = obs_weak_source_get_source(sceneWeakSource); auto scene = obs_scene_from_source(s); - obs_scene_enum_items(scene, enumItem, list); + obs_scene_enum_items(scene, enumItem, &names); obs_source_release(s); + + for (auto &name : names) { + list->addItem(name); + } } MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit( From e83a9e8d946dacc6870cae17a2d519901f7b3898 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 22 May 2021 20:13:50 +0200 Subject: [PATCH 30/66] Clean up No need to pass switcher as parameter --- src/advanced-scene-switcher.cpp | 56 ++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index c9580f28..95808908 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -422,11 +422,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 @@ -434,13 +434,13 @@ 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) { @@ -448,28 +448,28 @@ void handleSceneChange(SwitcherData *s) } } -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(); } // Note to future self: @@ -477,24 +477,28 @@ 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_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; From 66285a0d3d3b2cd17027370d64f64bb867bd9e63 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 23 May 2021 09:32:25 +0200 Subject: [PATCH 31/66] Add option to synchronize preview scene via network tab --- data/locale/en-US.ini | 1 + forms/advanced-scene-switcher.ui | 41 +++++++++++++++---------- src/advanced-scene-switcher.cpp | 22 +++++++++++++ src/headers/advanced-scene-switcher.hpp | 2 ++ src/headers/switch-network.hpp | 3 +- src/switch-network.cpp | 33 +++++++++++++++++--- 6 files changed, 79 insertions(+), 23 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index cd17b4ef..00385fbf 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -362,6 +362,7 @@ AdvSceneSwitcher.networkTab.server="Start server (Sends scene switch messages to AdvSceneSwitcher.networkTab.server.port="Port" AdvSceneSwitcher.networkTab.server.lockToIPv4="Lock server to only using IPv4" AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Only send messages for automated scene switches" +AdvSceneSwitcher.networkTab.server.sendPreview="Synchronize preview scene 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" diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index 4726e039..f2297f9b 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -3727,6 +3727,13 @@ + + + + AdvSceneSwitcher.networkTab.server.status.notRunning + + + @@ -3740,13 +3747,6 @@ - - - - AdvSceneSwitcher.networkTab.server.port - - - @@ -3754,17 +3754,10 @@ - - + + - AdvSceneSwitcher.networkTab.server.status.currentStatus - - - - - - - AdvSceneSwitcher.networkTab.server.status.notRunning + AdvSceneSwitcher.networkTab.server.port @@ -3775,6 +3768,20 @@ + + + + AdvSceneSwitcher.networkTab.server.status.currentStatus + + + + + + + AdvSceneSwitcher.networkTab.server.sendPreview + + + diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index 95808908..2839d88b 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -351,6 +351,13 @@ void switchScene(const 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())) { @@ -472,6 +479,18 @@ void checkAutoStartStreaming() switcher->Start(); } +void handlePeviewSceneChange() +{ + if (switcher->networkConfig.ServerEnabled && + switcher->networkConfig.SendPreview) { + 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: // be careful using switcher->m here as there is potential for deadlocks when using // frontend functions such as obs_frontend_set_current_scene() @@ -488,6 +507,9 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher) case OBS_FRONTEND_EVENT_SCENE_CHANGED: handleSceneChange(); break; + case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED: + handlePeviewSceneChange(); + break; case OBS_FRONTEND_EVENT_RECORDING_STARTED: setLiveTime(); checkAutoStartRecording(); diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index fa23cb96..e197fd1a 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -212,6 +212,7 @@ public slots: void on_clientHostname_textChanged(const QString &text); void on_clientPort_valueChanged(int value); void on_restrictSend_stateChanged(int state); + void on_sendPreview_stateChanged(int state); void on_clientReconnect_clicked(); void updateClientStatus(); @@ -254,6 +255,7 @@ 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 diff --git a/src/headers/switch-network.hpp b/src/headers/switch-network.hpp index 4e52061f..2a834df4 100644 --- a/src/headers/switch-network.hpp +++ b/src/headers/switch-network.hpp @@ -43,6 +43,7 @@ public: std::string Address; uint64_t ClientPort; bool SendAll; + bool SendPreview; }; class WSServer : public QObject { @@ -53,7 +54,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: diff --git a/src/switch-network.cpp b/src/switch-network.cpp index 865e4bf0..068d7b6b 100644 --- a/src/switch-network.cpp +++ b/src/switch-network.cpp @@ -18,12 +18,14 @@ Most of this code is based on https://github.com/Palakis/obs-websocket #define PARAM_CLIENT_PORT "ClientPort" #define PARAM_ADDRESS "Address" #define PARAM_CLIENT_SENDALL "SendAll" +#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 +38,8 @@ NetworkConfig::NetworkConfig() ClientEnabled(false), Address(""), ClientPort(55555), - SendAll(true) + SendAll(true), + SendPreview(true) { } @@ -52,6 +55,7 @@ void NetworkConfig::Load(obs_data_t *obj) 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); + SendPreview = obs_data_get_bool(obj, PARAM_CLIENT_SENDPREVIEW); } void NetworkConfig::Save(obs_data_t *obj) @@ -64,6 +68,7 @@ void NetworkConfig::Save(obs_data_t *obj) 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_SENDPREVIEW, SendPreview); } void NetworkConfig::SetDefaults(obs_data_t *obj) @@ -76,6 +81,7 @@ void NetworkConfig::SetDefaults(obs_data_t *obj) 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_SENDPREVIEW, SendPreview); } std::string NetworkConfig::GetClientUri() @@ -192,7 +198,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 +210,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 +255,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 +264,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 +280,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; } @@ -479,6 +491,7 @@ void AdvSceneSwitcher::setupNetworkTab() ui->clientHostname->setText(switcher->networkConfig.Address.c_str()); ui->clientPort->setValue(switcher->networkConfig.ClientPort); ui->restrictSend->setChecked(!switcher->networkConfig.SendAll); + ui->sendPreview->setChecked(switcher->networkConfig.SendPreview); QTimer *statusTimer = new QTimer(this); connect(statusTimer, SIGNAL(timeout()), this, @@ -602,6 +615,16 @@ void AdvSceneSwitcher::on_restrictSend_stateChanged(int state) switcher->networkConfig.SendAll = !state; } +void AdvSceneSwitcher::on_sendPreview_stateChanged(int state) +{ + if (loading) { + return; + } + + std::lock_guard lock(switcher->m); + switcher->networkConfig.SendPreview = state; +} + void AdvSceneSwitcher::on_clientReconnect_clicked() { if (loading) { From 9301713c492f26066c7206020a595d1d0a632e6a Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 12:35:51 +0200 Subject: [PATCH 32/66] Add option to not scene change msg via network tab --- data/locale/de-DE.ini | 4 +- data/locale/en-US.ini | 3 +- forms/advanced-scene-switcher.ui | 45 ++++++++++++--------- src/advanced-scene-switcher.cpp | 8 ++-- src/headers/advanced-scene-switcher.hpp | 1 + src/headers/switch-network.hpp | 7 +++- src/switch-network.cpp | 54 +++++++++++++++++++++---- 7 files changed, 88 insertions(+), 34 deletions(-) diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index a04f9f70..e300f5dd 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -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" diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 00385fbf..9e77cc01 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -361,8 +361,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="Synchronize preview scene when running in Studio mode" +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" diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index f2297f9b..56469ecd 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -3727,7 +3727,21 @@ - + + + + AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches + + + + + + + AdvSceneSwitcher.networkTab.server.status.currentStatus + + + + AdvSceneSwitcher.networkTab.server.status.notRunning @@ -3747,13 +3761,6 @@ - - - - AdvSceneSwitcher.networkTab.server.lockToIPv4 - - - @@ -3761,27 +3768,27 @@ - - + + - AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches + AdvSceneSwitcher.networkTab.server.lockToIPv4 - - - - AdvSceneSwitcher.networkTab.server.status.currentStatus - - - - + AdvSceneSwitcher.networkTab.server.sendPreview + + + + AdvSceneSwitcher.networkTab.server.sendSceneChange + + + diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index 2839d88b..46ab5c1f 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -343,7 +343,7 @@ void switchScene(const sceneSwitchInfo &sceneSwitch) blog(LOG_INFO, "switched scene"); } - if (switcher->networkConfig.ServerEnabled) { + if (switcher->networkConfig.ShouldSendSceneChange()) { switcher->server.sendMessage(sceneSwitch); } } @@ -449,8 +449,7 @@ void handleSceneChange() switcher->checkTriggers(); switcher->checkDefaultSceneTransitions(); - if (switcher->networkConfig.ServerEnabled && - switcher->networkConfig.SendAll) { + if (switcher->networkConfig.ShouldSendFrontendSceneChange()) { switcher->server.sendMessage({ws, nullptr, 0}); } } @@ -481,8 +480,7 @@ void checkAutoStartStreaming() void handlePeviewSceneChange() { - if (switcher->networkConfig.ServerEnabled && - switcher->networkConfig.SendPreview) { + 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); diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index e197fd1a..43124e8e 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -211,6 +211,7 @@ 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(); diff --git a/src/headers/switch-network.hpp b/src/headers/switch-network.hpp index 2a834df4..20d75552 100644 --- a/src/headers/switch-network.hpp +++ b/src/headers/switch-network.hpp @@ -33,6 +33,10 @@ public: std::string GetClientUri(); + bool ShouldSendSceneChange(); + bool ShouldSendFrontendSceneChange(); + bool ShouldSendPrviewSceneChange(); + // Server bool ServerEnabled; uint64_t ServerPort; @@ -42,7 +46,8 @@ public: bool ClientEnabled; std::string Address; uint64_t ClientPort; - bool SendAll; + bool SendSceneChange; + bool SendSceneChangeAll; bool SendPreview; }; diff --git a/src/switch-network.cpp b/src/switch-network.cpp index 068d7b6b..232cb7ee 100644 --- a/src/switch-network.cpp +++ b/src/switch-network.cpp @@ -17,7 +17,8 @@ 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 @@ -38,7 +39,8 @@ NetworkConfig::NetworkConfig() ClientEnabled(false), Address(""), ClientPort(55555), - SendAll(true), + SendSceneChange(true), + SendSceneChangeAll(true), SendPreview(true) { } @@ -54,7 +56,10 @@ 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); } @@ -67,7 +72,9 @@ 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); } @@ -80,7 +87,10 @@ 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); } @@ -89,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) { @@ -490,8 +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, @@ -605,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 lock(switcher->m); + switcher->networkConfig.SendSceneChange = state; + ui->restrictSend->setDisabled(!state); +} + void AdvSceneSwitcher::on_restrictSend_stateChanged(int state) { if (loading) { @@ -612,7 +652,7 @@ void AdvSceneSwitcher::on_restrictSend_stateChanged(int state) } std::lock_guard lock(switcher->m); - switcher->networkConfig.SendAll = !state; + switcher->networkConfig.SendSceneChangeAll = !state; } void AdvSceneSwitcher::on_sendPreview_stateChanged(int state) From e517fa7b39f92379c63852079b3494bb80d184a6 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Tue, 25 May 2021 19:23:17 +0200 Subject: [PATCH 33/66] Sort source selection alphabetically --- src/utility.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utility.cpp b/src/utility.cpp index 413571d0..5c507e89 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -268,6 +268,7 @@ void populateSourceSelection(QComboBox *list, bool addSelect) } obs_enum_sources(enumSourcesWithSources, list); + list->model()->sort(0); } void populateTransitionSelection(QComboBox *sel, bool addCurrent, From 069b0f89d031aa0fb80c008c7e1c24952e9b456d Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Tue, 25 May 2021 19:27:13 +0200 Subject: [PATCH 34/66] Adjust locale --- data/locale/en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 9e77cc01..4cb31c7d 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -159,7 +159,7 @@ 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 visibility" +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}}" From 77642c54d8e6042d0655af0900b5a3af815b15cf Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 23 May 2021 21:13:08 +0200 Subject: [PATCH 35/66] Add macro count member Used to indicate the number of times the actions were executed. --- src/headers/macro.hpp | 3 +++ src/macro.cpp | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index 7468ee99..90dd9a22 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -71,6 +71,8 @@ public: void SetName(const std::string &name) { _name = name; } void SetPaused(bool pause = true) { _paused = pause; } bool Paused() { return _paused; } + int GetCount() { return _count; }; + void ResetCount() { _count = 0; }; std::deque> &Conditions() { return _conditions; @@ -89,6 +91,7 @@ private: std::deque> _actions; bool _matched = false; bool _paused = false; + int _count = 0; }; Macro *GetMacroByName(const char *name); diff --git a/src/macro.cpp b/src/macro.cpp index f008ad9c..c62ee31f 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -4,6 +4,9 @@ #include "headers/macro-condition-edit.hpp" #include "headers/macro-action-scene-switch.hpp" +#include +#undef max + const std::map MacroCondition::logicTypes = { {LogicType::NONE, {"AdvSceneSwitcher.logic.none"}}, {LogicType::AND, {"AdvSceneSwitcher.logic.and"}}, @@ -85,7 +88,9 @@ bool Macro::PerformAction() return false; } } - + if (ret && _count != std::numeric_limits::max()) { + _count++; + } return ret; } From 4a84bede37ce97100bae3ae4715e01275053a961 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 23 May 2021 21:13:50 +0200 Subject: [PATCH 36/66] Add macro condition "count" --- CMakeLists.txt | 2 + data/locale/en-US.ini | 7 + src/headers/macro-condition-counter.hpp | 72 +++++++ src/macro-condition-counter.cpp | 240 ++++++++++++++++++++++++ 4 files changed, 321 insertions(+) create mode 100644 src/headers/macro-condition-counter.hpp create mode 100644 src/macro-condition-counter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c4665f57..b83f7edd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ set(advanced-scene-switcher_HEADERS 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 @@ -158,6 +159,7 @@ set(advanced-scene-switcher_SOURCES 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 diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 4cb31c7d..6256fc22 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -126,6 +126,13 @@ AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene chan AdvSceneSwitcher.condition.pluginState.entry="{{condition}}" 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}}" ; Macro Actions AdvSceneSwitcher.action.switchScene="Switch scene" diff --git a/src/headers/macro-condition-counter.hpp b/src/headers/macro-condition-counter.hpp new file mode 100644 index 00000000..874116ed --- /dev/null +++ b/src/headers/macro-condition-counter.hpp @@ -0,0 +1,72 @@ +#pragma once +#include "macro.hpp" +#include +#include +#include +#include + +enum class CounterCondition { + BELOW, + ABOVE, + EQUAL, +}; + +class MacroConditionCounter : 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 Create() + { + return std::make_shared(); + } + + Macro *_macro = nullptr; + 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 cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionCounterEdit( + parent, + std::dynamic_pointer_cast(cond)); + } + +private slots: + void MacroChanged(const QString &text); + void CountChanged(int value); + void ConditionChanged(int cond); + void MacroAdd(const QString &name); + void MacroRemove(const QString &name); + void MacroRename(const QString &oldName, const QString &newName); + void ResetClicked(); + void UpdateCount(); + +protected: + QComboBox *_macros; + QComboBox *_conditions; + QSpinBox *_count; + QLabel *_currentCount; + QPushButton *_resetCount; + std::unique_ptr _timer; + std::shared_ptr _entryData; + +private: + void ResetTimer(); + bool _loading = true; +}; diff --git a/src/macro-condition-counter.cpp b/src/macro-condition-counter.cpp new file mode 100644 index 00000000..cce707bf --- /dev/null +++ b/src/macro-condition-counter.cpp @@ -0,0 +1,240 @@ +#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 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() +{ + switch (_condition) { + case CounterCondition::BELOW: + return _macro && _macro->GetCount() < _count; + case CounterCondition::ABOVE: + return _macro && _macro->GetCount() > _count; + case CounterCondition::EQUAL: + return _macro && _macro->GetCount() == _count; + default: + break; + } + + return false; +} + +bool MacroConditionCounter::Save(obs_data_t *obj) +{ + MacroCondition::Save(obj); + if (_macro) { + obs_data_set_string(obj, "macro", _macro->Name().c_str()); + } + obs_data_set_int(obj, "condition", static_cast(_condition)); + obs_data_set_int(obj, "count", _count); + return true; +} + +bool MacroConditionCounter::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + _macro = GetMacroByName(obs_data_get_string(obj, "macro")); + _condition = static_cast( + 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())); + } +} + +static inline void populateMacroSelection(QComboBox *list) +{ + list->addItem(obs_module_text("AdvSceneSwitcher.selectMacro")); + for (auto &m : switcher->macros) { + list->addItem(QString::fromStdString(m.Name())); + } +} + +MacroConditionCounterEdit::MacroConditionCounterEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _macros = new QComboBox(); + _conditions = new QComboBox(); + _count = new QSpinBox(); + _currentCount = new QLabel(); + _resetCount = new QPushButton( + obs_module_text("AdvSceneSwitcher.condition.counter.reset")); + + _count->setMaximum(10000000); + + populateConditionSelection(_conditions); + populateMacroSelection(_macros); + + QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(MacroChanged(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())); + + 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 &))); + + QVBoxLayout *mainLayout = new QVBoxLayout; + QHBoxLayout *line1Layout = new QHBoxLayout; + QHBoxLayout *line2Layout = new QHBoxLayout; + std::unordered_map 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; + } + + if (_entryData->_macro) { + _macros->setCurrentText( + QString::fromStdString(_entryData->_macro->Name())); + } else { + _macros->setCurrentIndex(0); + } + _conditions->setCurrentIndex(static_cast(_entryData->_condition)); + _count->setValue(_entryData->_count); + ResetTimer(); +} + +void MacroConditionCounterEdit::MacroChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_macro = GetMacroByQString(text); + ResetTimer(); +} + +void MacroConditionCounterEdit::CountChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_count = value; +} + +void MacroConditionCounterEdit::ConditionChanged(int cond) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_condition = static_cast(cond); +} + +void MacroConditionCounterEdit::MacroAdd(const QString &name) +{ + _macros->addItem(name); +} + +void MacroConditionCounterEdit::MacroRemove(const QString &name) +{ + int idx = _macros->findText(name); + if (idx == -1) { + return; + } + _macros->removeItem(idx); + if (_entryData && _entryData->_macro == GetMacroByQString(name)) { + std::lock_guard lock(switcher->m); + _entryData->_macro = nullptr; + } + _macros->setCurrentIndex(0); +} + +void MacroConditionCounterEdit::MacroRename(const QString &oldName, + const QString &newName) +{ + bool renameSelected = _macros->currentText() == oldName; + int idx = _macros->findText(oldName); + if (idx == -1) { + return; + } + _macros->removeItem(idx); + _macros->insertItem(idx, newName); + if (renameSelected) { + _macros->setCurrentIndex(_macros->findText(newName)); + } +} + +void MacroConditionCounterEdit::ResetClicked() +{ + if (_loading || !_entryData || !_entryData->_macro) { + return; + } + + _entryData->_macro->ResetCount(); + ResetTimer(); +} + +void MacroConditionCounterEdit::UpdateCount() +{ + if (_entryData && _entryData->_macro) { + _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); +} From 6893d9539e74d119f2b5a511bf131747b9ffdcf5 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 00:37:37 +0200 Subject: [PATCH 37/66] Add MacroSelection widget --- CMakeLists.txt | 2 + src/headers/macro-action-pause.hpp | 7 ++- src/headers/macro-condition-counter.hpp | 7 ++- src/headers/macro-selection.hpp | 17 ++++++++ src/macro-action-pause.cpp | 48 ++------------------- src/macro-condition-counter.cpp | 50 ++-------------------- src/macro-selection.cpp | 57 +++++++++++++++++++++++++ 7 files changed, 90 insertions(+), 98 deletions(-) create mode 100644 src/headers/macro-selection.hpp create mode 100644 src/macro-selection.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b83f7edd..56126785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-condition-video.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 @@ -173,6 +174,7 @@ set(advanced-scene-switcher_SOURCES src/macro-condition-video.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 diff --git a/src/headers/macro-action-pause.hpp b/src/headers/macro-action-pause.hpp index f2f60e77..56a7f793 100644 --- a/src/headers/macro-action-pause.hpp +++ b/src/headers/macro-action-pause.hpp @@ -1,6 +1,7 @@ #pragma once #include #include "macro-action-edit.hpp" +#include "macro-selection.hpp" enum class PauseAction { PAUSE, @@ -45,13 +46,11 @@ public: private slots: void MacroChanged(const QString &text); - void ActionChanged(int value); - void MacroAdd(const QString &name); void MacroRemove(const QString &name); - void MacroRename(const QString &oldName, const QString &newName); + void ActionChanged(int value); protected: - QComboBox *_macros; + MacroSelection *_macros; QComboBox *_actions; std::shared_ptr _entryData; diff --git a/src/headers/macro-condition-counter.hpp b/src/headers/macro-condition-counter.hpp index 874116ed..d788009b 100644 --- a/src/headers/macro-condition-counter.hpp +++ b/src/headers/macro-condition-counter.hpp @@ -1,5 +1,6 @@ #pragma once #include "macro.hpp" +#include "macro-selection.hpp" #include #include #include @@ -49,16 +50,14 @@ public: private slots: void MacroChanged(const QString &text); + void MacroRemove(const QString &name); void CountChanged(int value); void ConditionChanged(int cond); - void MacroAdd(const QString &name); - void MacroRemove(const QString &name); - void MacroRename(const QString &oldName, const QString &newName); void ResetClicked(); void UpdateCount(); protected: - QComboBox *_macros; + MacroSelection *_macros; QComboBox *_conditions; QSpinBox *_count; QLabel *_currentCount; diff --git a/src/headers/macro-selection.hpp b/src/headers/macro-selection.hpp new file mode 100644 index 00000000..3fe13b93 --- /dev/null +++ b/src/headers/macro-selection.hpp @@ -0,0 +1,17 @@ +#pragma once +#include + +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); +}; diff --git a/src/macro-action-pause.cpp b/src/macro-action-pause.cpp index d69d71ef..1e79f649 100644 --- a/src/macro-action-pause.cpp +++ b/src/macro-action-pause.cpp @@ -75,36 +75,21 @@ static inline void populateActionSelection(QComboBox *list) } } -static inline void populateMacroSelection(QComboBox *list) -{ - list->addItem(obs_module_text("AdvSceneSwitcher.selectMacro")); - for (auto &m : switcher->macros) { - list->addItem(QString::fromStdString(m.Name())); - } -} - MacroActionPauseEdit::MacroActionPauseEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent) { - _macros = new QComboBox(); + _macros = new MacroSelection(parent); _actions = new QComboBox(); populateActionSelection(_actions); - populateMacroSelection(_macros); QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)), this, SLOT(MacroChanged(const QString &))); - QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, - SLOT(ActionChanged(int))); - 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 &))); + QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ActionChanged(int))); QHBoxLayout *mainLayout = new QHBoxLayout; std::unordered_map widgetPlaceholders = { @@ -126,12 +111,7 @@ void MacroActionPauseEdit::UpdateEntryData() return; } _actions->setCurrentIndex(static_cast(_entryData->_action)); - if (_entryData->_macro) { - _macros->setCurrentText( - QString::fromStdString(_entryData->_macro->Name())); - } else { - _macros->setCurrentIndex(0); - } + _macros->SetCurrentMacro(_entryData->_macro); } void MacroActionPauseEdit::MacroChanged(const QString &text) @@ -154,11 +134,6 @@ void MacroActionPauseEdit::ActionChanged(int value) _entryData->_action = static_cast(value); } -void MacroActionPauseEdit::MacroAdd(const QString &name) -{ - _macros->addItem(name); -} - void MacroActionPauseEdit::MacroRemove(const QString &name) { int idx = _macros->findText(name); @@ -172,18 +147,3 @@ void MacroActionPauseEdit::MacroRemove(const QString &name) } _macros->setCurrentIndex(0); } - -void MacroActionPauseEdit::MacroRename(const QString &oldName, - const QString &newName) -{ - bool renameSelected = _macros->currentText() == oldName; - int idx = _macros->findText(oldName); - if (idx == -1) { - return; - } - _macros->removeItem(idx); - _macros->insertItem(idx, newName); - if (renameSelected) { - _macros->setCurrentIndex(_macros->findText(newName)); - } -} diff --git a/src/macro-condition-counter.cpp b/src/macro-condition-counter.cpp index cce707bf..dbfbc443 100644 --- a/src/macro-condition-counter.cpp +++ b/src/macro-condition-counter.cpp @@ -63,19 +63,11 @@ static inline void populateConditionSelection(QComboBox *list) } } -static inline void populateMacroSelection(QComboBox *list) -{ - list->addItem(obs_module_text("AdvSceneSwitcher.selectMacro")); - for (auto &m : switcher->macros) { - list->addItem(QString::fromStdString(m.Name())); - } -} - MacroConditionCounterEdit::MacroConditionCounterEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent) { - _macros = new QComboBox(); + _macros = new MacroSelection(parent); _conditions = new QComboBox(); _count = new QSpinBox(); _currentCount = new QLabel(); @@ -83,12 +75,12 @@ MacroConditionCounterEdit::MacroConditionCounterEdit( obs_module_text("AdvSceneSwitcher.condition.counter.reset")); _count->setMaximum(10000000); - populateConditionSelection(_conditions); - populateMacroSelection(_macros); 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, @@ -96,15 +88,6 @@ MacroConditionCounterEdit::MacroConditionCounterEdit( QWidget::connect(_resetCount, SIGNAL(clicked()), this, SLOT(ResetClicked())); - 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 &))); - QVBoxLayout *mainLayout = new QVBoxLayout; QHBoxLayout *line1Layout = new QHBoxLayout; QHBoxLayout *line2Layout = new QHBoxLayout; @@ -136,12 +119,7 @@ void MacroConditionCounterEdit::UpdateEntryData() return; } - if (_entryData->_macro) { - _macros->setCurrentText( - QString::fromStdString(_entryData->_macro->Name())); - } else { - _macros->setCurrentIndex(0); - } + _macros->SetCurrentMacro(_entryData->_macro); _conditions->setCurrentIndex(static_cast(_entryData->_condition)); _count->setValue(_entryData->_count); ResetTimer(); @@ -178,11 +156,6 @@ void MacroConditionCounterEdit::ConditionChanged(int cond) _entryData->_condition = static_cast(cond); } -void MacroConditionCounterEdit::MacroAdd(const QString &name) -{ - _macros->addItem(name); -} - void MacroConditionCounterEdit::MacroRemove(const QString &name) { int idx = _macros->findText(name); @@ -197,21 +170,6 @@ void MacroConditionCounterEdit::MacroRemove(const QString &name) _macros->setCurrentIndex(0); } -void MacroConditionCounterEdit::MacroRename(const QString &oldName, - const QString &newName) -{ - bool renameSelected = _macros->currentText() == oldName; - int idx = _macros->findText(oldName); - if (idx == -1) { - return; - } - _macros->removeItem(idx); - _macros->insertItem(idx, newName); - if (renameSelected) { - _macros->setCurrentIndex(_macros->findText(newName)); - } -} - void MacroConditionCounterEdit::ResetClicked() { if (_loading || !_entryData || !_entryData->_macro) { diff --git a/src/macro-selection.cpp b/src/macro-selection.cpp new file mode 100644 index 00000000..3658a07e --- /dev/null +++ b/src/macro-selection.cpp @@ -0,0 +1,57 @@ +#include "headers/macro-selection.hpp" +#include "headers/advanced-scene-switcher.hpp" + +MacroSelection::MacroSelection(QWidget *parent) : QComboBox(parent) +{ + addItem(obs_module_text("AdvSceneSwitcher.selectMacro")); + 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)); + } +} From 48e6bcbb7a19c1fced69a07fe0ed3b1048422040 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 02:01:32 +0200 Subject: [PATCH 38/66] Fix conditions and actions referencing macros not loading correctly The reason is that while loading the required macro might not have been loaded resulting in the pointer to the macro never being set correctly in the loading phase. --- src/headers/macro-action-pause.hpp | 3 +- src/headers/macro-condition-counter.hpp | 3 +- src/headers/macro.hpp | 33 +++++++++++- src/macro-action-pause.cpp | 26 ++++----- src/macro-condition-counter.cpp | 36 ++++++------- src/macro.cpp | 71 +++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 42 deletions(-) diff --git a/src/headers/macro-action-pause.hpp b/src/headers/macro-action-pause.hpp index 56a7f793..be670f50 100644 --- a/src/headers/macro-action-pause.hpp +++ b/src/headers/macro-action-pause.hpp @@ -8,7 +8,7 @@ enum class PauseAction { UNPAUSE, }; -class MacroActionPause : public MacroAction { +class MacroActionPause : public MacroRefAction { public: bool PerformAction(); void LogAction(); @@ -21,7 +21,6 @@ public: } PauseAction _action = PauseAction::PAUSE; - Macro *_macro = nullptr; private: static bool _registered; diff --git a/src/headers/macro-condition-counter.hpp b/src/headers/macro-condition-counter.hpp index d788009b..e02bc646 100644 --- a/src/headers/macro-condition-counter.hpp +++ b/src/headers/macro-condition-counter.hpp @@ -12,7 +12,7 @@ enum class CounterCondition { EQUAL, }; -class MacroConditionCounter : public MacroCondition { +class MacroConditionCounter : public MacroRefCondition { public: bool CheckCondition(); bool Save(obs_data_t *obj); @@ -23,7 +23,6 @@ public: return std::make_shared(); } - Macro *_macro = nullptr; CounterCondition _condition = CounterCondition::BELOW; int _count = 0; diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index 90dd9a22..f5532c20 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -59,7 +59,6 @@ public: }; class Macro { - public: Macro(std::string name = ""); virtual ~Macro(); @@ -81,6 +80,9 @@ 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(); @@ -96,3 +98,32 @@ private: 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; +}; diff --git a/src/macro-action-pause.cpp b/src/macro-action-pause.cpp index 1e79f649..28c30a34 100644 --- a/src/macro-action-pause.cpp +++ b/src/macro-action-pause.cpp @@ -16,7 +16,7 @@ const static std::map actionTypes = { bool MacroActionPause::PerformAction() { - if (!_macro) { + if (!_macro.get()) { return true; } @@ -35,7 +35,7 @@ bool MacroActionPause::PerformAction() void MacroActionPause::LogAction() { - if (!_macro) { + if (!_macro.get()) { return; } switch (_action) { @@ -53,9 +53,7 @@ void MacroActionPause::LogAction() bool MacroActionPause::Save(obs_data_t *obj) { MacroAction::Save(obj); - if (_macro) { - obs_data_set_string(obj, "macro", _macro->Name().c_str()); - } + _macro.Save(obj); obs_data_set_int(obj, "action", static_cast(_action)); return true; } @@ -63,7 +61,7 @@ bool MacroActionPause::Save(obs_data_t *obj) bool MacroActionPause::Load(obs_data_t *obj) { MacroAction::Load(obj); - _macro = GetMacroByName(obs_data_get_string(obj, "macro")); + _macro.Load(obj); _action = static_cast(obs_data_get_int(obj, "action")); return true; } @@ -111,7 +109,7 @@ void MacroActionPauseEdit::UpdateEntryData() return; } _actions->setCurrentIndex(static_cast(_entryData->_action)); - _macros->SetCurrentMacro(_entryData->_macro); + _macros->SetCurrentMacro(_entryData->_macro.get()); } void MacroActionPauseEdit::MacroChanged(const QString &text) @@ -121,7 +119,7 @@ void MacroActionPauseEdit::MacroChanged(const QString &text) } std::lock_guard lock(switcher->m); - _entryData->_macro = GetMacroByQString(text); + _entryData->_macro.UpdateRef(text); } void MacroActionPauseEdit::ActionChanged(int value) @@ -136,14 +134,8 @@ void MacroActionPauseEdit::ActionChanged(int value) void MacroActionPauseEdit::MacroRemove(const QString &name) { - int idx = _macros->findText(name); - if (idx == -1) { - return; + UNUSED_PARAMETER(name); + if (_entryData) { + _entryData->_macro.UpdateRef(); } - _macros->removeItem(idx); - if (_entryData && _entryData->_macro == GetMacroByQString(name)) { - std::lock_guard lock(switcher->m); - _entryData->_macro = nullptr; - } - _macros->setCurrentIndex(0); } diff --git a/src/macro-condition-counter.cpp b/src/macro-condition-counter.cpp index dbfbc443..b8d18f13 100644 --- a/src/macro-condition-counter.cpp +++ b/src/macro-condition-counter.cpp @@ -21,13 +21,17 @@ static std::map counterConditionTypes = { bool MacroConditionCounter::CheckCondition() { + if (!_macro.get()) { + return false; + } + switch (_condition) { case CounterCondition::BELOW: - return _macro && _macro->GetCount() < _count; + return _macro->GetCount() < _count; case CounterCondition::ABOVE: - return _macro && _macro->GetCount() > _count; + return _macro->GetCount() > _count; case CounterCondition::EQUAL: - return _macro && _macro->GetCount() == _count; + return _macro->GetCount() == _count; default: break; } @@ -38,9 +42,7 @@ bool MacroConditionCounter::CheckCondition() bool MacroConditionCounter::Save(obs_data_t *obj) { MacroCondition::Save(obj); - if (_macro) { - obs_data_set_string(obj, "macro", _macro->Name().c_str()); - } + _macro.Save(obj); obs_data_set_int(obj, "condition", static_cast(_condition)); obs_data_set_int(obj, "count", _count); return true; @@ -49,7 +51,7 @@ bool MacroConditionCounter::Save(obs_data_t *obj) bool MacroConditionCounter::Load(obs_data_t *obj) { MacroCondition::Load(obj); - _macro = GetMacroByName(obs_data_get_string(obj, "macro")); + _macro.Load(obj); _condition = static_cast( obs_data_get_int(obj, "condition")); _count = obs_data_get_int(obj, "count"); @@ -119,7 +121,7 @@ void MacroConditionCounterEdit::UpdateEntryData() return; } - _macros->SetCurrentMacro(_entryData->_macro); + _macros->SetCurrentMacro(_entryData->_macro.get()); _conditions->setCurrentIndex(static_cast(_entryData->_condition)); _count->setValue(_entryData->_count); ResetTimer(); @@ -132,7 +134,7 @@ void MacroConditionCounterEdit::MacroChanged(const QString &text) } std::lock_guard lock(switcher->m); - _entryData->_macro = GetMacroByQString(text); + _entryData->_macro.UpdateRef(text); ResetTimer(); } @@ -158,21 +160,15 @@ void MacroConditionCounterEdit::ConditionChanged(int cond) void MacroConditionCounterEdit::MacroRemove(const QString &name) { - int idx = _macros->findText(name); - if (idx == -1) { - return; + UNUSED_PARAMETER(name); + if (_entryData) { + _entryData->_macro.UpdateRef(); } - _macros->removeItem(idx); - if (_entryData && _entryData->_macro == GetMacroByQString(name)) { - std::lock_guard lock(switcher->m); - _entryData->_macro = nullptr; - } - _macros->setCurrentIndex(0); } void MacroConditionCounterEdit::ResetClicked() { - if (_loading || !_entryData || !_entryData->_macro) { + if (_loading || !_entryData || !_entryData->_macro.get()) { return; } @@ -182,7 +178,7 @@ void MacroConditionCounterEdit::ResetClicked() void MacroConditionCounterEdit::UpdateCount() { - if (_entryData && _entryData->_macro) { + if (_entryData && _entryData->_macro.get()) { _currentCount->setText( QString::number(_entryData->_macro->GetCount())); } else { diff --git a/src/macro.cpp b/src/macro.cpp index c62ee31f..1e4865ab 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -216,6 +216,23 @@ bool Macro::Load(obs_data_t *obj) return true; } +void Macro::ResolveMacroRef() +{ + for (auto &c : _conditions) { + MacroRefCondition *ref = + dynamic_cast(c.get()); + if (ref) { + ref->_macro.UpdateRef(); + } + } + for (auto &a : _actions) { + MacroRefAction *ref = dynamic_cast(a.get()); + if (ref) { + ref->_macro.UpdateRef(); + } + } +} + bool Macro::SwitchesScene() { MacroActionSwitchScene temp; @@ -350,6 +367,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() @@ -398,3 +419,53 @@ 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(); +} From 89849a0792b7c22f9d2d76558de38a2ef19a2bd5 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 02:55:44 +0200 Subject: [PATCH 39/66] Rename files --- CMakeLists.txt | 4 ++-- .../{macro-action-pause.hpp => macro-action-macro.hpp} | 0 src/{macro-action-pause.cpp => macro-action-macro.cpp} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/headers/{macro-action-pause.hpp => macro-action-macro.hpp} (100%) rename src/{macro-action-pause.cpp => macro-action-macro.cpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56126785..c6650336 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,8 +82,8 @@ set(advanced-scene-switcher_HEADERS 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-pause.hpp src/headers/macro-action-recording.hpp src/headers/macro-action-replay-buffer.hpp src/headers/macro-action-run.hpp @@ -148,8 +148,8 @@ set(advanced-scene-switcher_SOURCES 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-pause.cpp src/macro-action-recording.cpp src/macro-action-replay-buffer.cpp src/macro-action-run.cpp diff --git a/src/headers/macro-action-pause.hpp b/src/headers/macro-action-macro.hpp similarity index 100% rename from src/headers/macro-action-pause.hpp rename to src/headers/macro-action-macro.hpp diff --git a/src/macro-action-pause.cpp b/src/macro-action-macro.cpp similarity index 100% rename from src/macro-action-pause.cpp rename to src/macro-action-macro.cpp From ec6cffcd6a5823d6660410330635717be42ac58b Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 13:11:06 +0200 Subject: [PATCH 40/66] Rename MacroActionPause and add action to reset macro counter --- data/locale/en-US.ini | 9 +++-- src/headers/macro-action-macro.hpp | 21 +++++----- src/macro-action-macro.cpp | 63 ++++++++++++++++++------------ 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 6256fc22..35820bf7 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -187,10 +187,11 @@ 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.pause="Pause" -AdvSceneSwitcher.action.pause.type.pause="Pause" -AdvSceneSwitcher.action.pause.type.unpause="Unpause" -AdvSceneSwitcher.action.pause.entry="{{actions}} {{macros}}" +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}}" ; Transition Tab diff --git a/src/headers/macro-action-macro.hpp b/src/headers/macro-action-macro.hpp index be670f50..75ae149b 100644 --- a/src/headers/macro-action-macro.hpp +++ b/src/headers/macro-action-macro.hpp @@ -3,12 +3,13 @@ #include "macro-action-edit.hpp" #include "macro-selection.hpp" -enum class PauseAction { +enum class PerformMacroAction { PAUSE, UNPAUSE, + RESET_COUNTER, }; -class MacroActionPause : public MacroRefAction { +class MacroActionMacro : public MacroRefAction { public: bool PerformAction(); void LogAction(); @@ -17,30 +18,30 @@ public: std::string GetId() { return id; }; static std::shared_ptr Create() { - return std::make_shared(); + return std::make_shared(); } - PauseAction _action = PauseAction::PAUSE; + PerformMacroAction _action = PerformMacroAction::PAUSE; private: static bool _registered; static const std::string id; }; -class MacroActionPauseEdit : public QWidget { +class MacroActionMacroEdit : public QWidget { Q_OBJECT public: - MacroActionPauseEdit( + MacroActionMacroEdit( QWidget *parent, - std::shared_ptr entryData = nullptr); + std::shared_ptr entryData = nullptr); void UpdateEntryData(); static QWidget *Create(QWidget *parent, std::shared_ptr action) { - return new MacroActionPauseEdit( + return new MacroActionMacroEdit( parent, - std::dynamic_pointer_cast(action)); + std::dynamic_pointer_cast(action)); } private slots: @@ -51,7 +52,7 @@ private slots: protected: MacroSelection *_macros; QComboBox *_actions; - std::shared_ptr _entryData; + std::shared_ptr _entryData; private: QHBoxLayout *_mainLayout; diff --git a/src/macro-action-macro.cpp b/src/macro-action-macro.cpp index 28c30a34..a5bea1b2 100644 --- a/src/macro-action-macro.cpp +++ b/src/macro-action-macro.cpp @@ -1,56 +1,66 @@ -#include "headers/macro-action-pause.hpp" +#include "headers/macro-action-macro.hpp" #include "headers/advanced-scene-switcher.hpp" #include "headers/utility.hpp" -const std::string MacroActionPause::id = "pause"; +const std::string MacroActionMacro::id = "macro"; -bool MacroActionPause::_registered = MacroActionFactory::Register( - MacroActionPause::id, - {MacroActionPause::Create, MacroActionPauseEdit::Create, - "AdvSceneSwitcher.action.pause"}); +bool MacroActionMacro::_registered = MacroActionFactory::Register( + MacroActionMacro::id, + {MacroActionMacro::Create, MacroActionMacroEdit::Create, + "AdvSceneSwitcher.action.macro"}); -const static std::map actionTypes = { - {PauseAction::PAUSE, "AdvSceneSwitcher.action.pause.type.pause"}, - {PauseAction::UNPAUSE, "AdvSceneSwitcher.action.pause.type.unpause"}, +const static std::map actionTypes = { + {PerformMacroAction::PAUSE, "AdvSceneSwitcher.action.macro.type.pause"}, + {PerformMacroAction::UNPAUSE, + "AdvSceneSwitcher.action.macro.type.unpause"}, + {PerformMacroAction::RESET_COUNTER, + "AdvSceneSwitcher.action.macro.type.resetCounter"}, }; -bool MacroActionPause::PerformAction() +bool MacroActionMacro::PerformAction() { if (!_macro.get()) { return true; } switch (_action) { - case PauseAction::PAUSE: + case PerformMacroAction::PAUSE: _macro->SetPaused(); break; - case PauseAction::UNPAUSE: + case PerformMacroAction::UNPAUSE: _macro->SetPaused(false); break; + case PerformMacroAction::RESET_COUNTER: + _macro->ResetCount(); + break; default: break; } return true; } -void MacroActionPause::LogAction() +void MacroActionMacro::LogAction() { if (!_macro.get()) { return; } switch (_action) { - case PauseAction::PAUSE: + case PerformMacroAction::PAUSE: vblog(LOG_INFO, "paused \"%s\"", _macro->Name().c_str()); break; - case PauseAction::UNPAUSE: + 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 MacroActionPause::Save(obs_data_t *obj) +bool MacroActionMacro::Save(obs_data_t *obj) { MacroAction::Save(obj); _macro.Save(obj); @@ -58,11 +68,12 @@ bool MacroActionPause::Save(obs_data_t *obj) return true; } -bool MacroActionPause::Load(obs_data_t *obj) +bool MacroActionMacro::Load(obs_data_t *obj) { MacroAction::Load(obj); _macro.Load(obj); - _action = static_cast(obs_data_get_int(obj, "action")); + _action = static_cast( + obs_data_get_int(obj, "action")); return true; } @@ -73,8 +84,8 @@ static inline void populateActionSelection(QComboBox *list) } } -MacroActionPauseEdit::MacroActionPauseEdit( - QWidget *parent, std::shared_ptr entryData) +MacroActionMacroEdit::MacroActionMacroEdit( + QWidget *parent, std::shared_ptr entryData) : QWidget(parent) { _macros = new MacroSelection(parent); @@ -94,7 +105,7 @@ MacroActionPauseEdit::MacroActionPauseEdit( {"{{actions}}", _actions}, {"{{macros}}", _macros}, }; - placeWidgets(obs_module_text("AdvSceneSwitcher.action.pause.entry"), + placeWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"), mainLayout, widgetPlaceholders); setLayout(mainLayout); @@ -103,7 +114,7 @@ MacroActionPauseEdit::MacroActionPauseEdit( _loading = false; } -void MacroActionPauseEdit::UpdateEntryData() +void MacroActionMacroEdit::UpdateEntryData() { if (!_entryData) { return; @@ -112,7 +123,7 @@ void MacroActionPauseEdit::UpdateEntryData() _macros->SetCurrentMacro(_entryData->_macro.get()); } -void MacroActionPauseEdit::MacroChanged(const QString &text) +void MacroActionMacroEdit::MacroChanged(const QString &text) { if (_loading || !_entryData) { return; @@ -122,17 +133,17 @@ void MacroActionPauseEdit::MacroChanged(const QString &text) _entryData->_macro.UpdateRef(text); } -void MacroActionPauseEdit::ActionChanged(int value) +void MacroActionMacroEdit::ActionChanged(int value) { if (_loading || !_entryData) { return; } std::lock_guard lock(switcher->m); - _entryData->_action = static_cast(value); + _entryData->_action = static_cast(value); } -void MacroActionPauseEdit::MacroRemove(const QString &name) +void MacroActionMacroEdit::MacroRemove(const QString &name) { UNUSED_PARAMETER(name); if (_entryData) { From a5edbc8b883ac6fbcf2d7bfded1531d7eaa28841 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Tue, 25 May 2021 19:11:11 +0200 Subject: [PATCH 41/66] Reset macro counters when stopping the plugin --- src/advanced-scene-switcher.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index 46ab5c1f..f87dea31 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -379,6 +379,13 @@ void SwitcherData::Start() } } +void ResetMacroCounters() +{ + for (auto &m : switcher->macros) { + m.ResetCount(); + } +} + void SwitcherData::Stop() { if (th && th->isRunning()) { @@ -390,6 +397,7 @@ void SwitcherData::Stop() th = nullptr; writeToStatusFile("Advanced Scene Switcher stopped"); + ResetMacroCounters(); } server.stop(); From 2454ea8fa5eeab471708c06caf21476ba128179b Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 24 May 2021 13:43:34 +0200 Subject: [PATCH 42/66] Add macro condition "source" --- CMakeLists.txt | 2 + data/locale/en-US.ini | 9 + src/headers/macro-condition-source.hpp | 69 ++++++++ src/macro-condition-source.cpp | 232 +++++++++++++++++++++++++ 4 files changed, 312 insertions(+) create mode 100644 src/headers/macro-condition-source.hpp create mode 100644 src/macro-condition-source.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c6650336..44d1871e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,7 @@ set(advanced-scene-switcher_HEADERS 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-window.hpp @@ -170,6 +171,7 @@ set(advanced-scene-switcher_SOURCES 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-window.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 35820bf7..c189551a 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -133,6 +133,15 @@ 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" diff --git a/src/headers/macro-condition-source.hpp b/src/headers/macro-condition-source.hpp new file mode 100644 index 00000000..d0f92e26 --- /dev/null +++ b/src/headers/macro-condition-source.hpp @@ -0,0 +1,69 @@ +#pragma once +#include "macro.hpp" +#include +#include +#include + +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 Create() + { + return std::make_shared(); + } + + 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 cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionSourceEdit( + parent, + std::dynamic_pointer_cast(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 _entryData; + +private: + void SetSettingsSelectionVisible(bool visible); + bool _loading = true; +}; diff --git a/src/macro-condition-source.cpp b/src/macro-condition-source.cpp new file mode 100644 index 00000000..2fff7c60 --- /dev/null +++ b/src/macro-condition-source.cpp @@ -0,0 +1,232 @@ +#include "headers/macro-condition-edit.hpp" +#include "headers/macro-condition-source.hpp" +#include "headers/utility.hpp" +#include "headers/advanced-scene-switcher.hpp" + +#include + +const std::string MacroConditionSource::id = "source"; + +bool MacroConditionSource::_registered = MacroConditionFactory::Register( + MacroConditionSource::id, + {MacroConditionSource::Create, MacroConditionSourceEdit::Create, + "AdvSceneSwitcher.condition.source"}); + +static std::map sourceConditionTypes = { + {SourceCondition::ACTIVE, + "AdvSceneSwitcher.condition.source.type.active"}, + {SourceCondition::SHOWING, + "AdvSceneSwitcher.condition.source.type.showing"}, + {SourceCondition::SETTINGS, + "AdvSceneSwitcher.condition.source.type.settings"}, +}; + +std::string getSourceSettings(OBSWeakSource ws) +{ + auto s = obs_weak_source_get_source(ws); + obs_data_t *data = obs_source_get_settings(s); + std::string settings = obs_data_get_json(data); + obs_data_release(data); + obs_source_release(s); + + return 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(_condition)); + 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( + obs_data_get_int(obj, "condition")); + 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 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 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 lock(switcher->m); + _entryData->_source = GetWeakSourceByQString(text); +} + +void MacroConditionSourceEdit::ConditionChanged(int index) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_condition = static_cast(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 lock(switcher->m); + _entryData->_settings = _settings->toPlainText().toStdString(); +} + +void MacroConditionSourceEdit::RegexChanged(int state) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_regex = state; +} + +void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible) +{ + _settings->setVisible(visible); + _getSettings->setVisible(visible); + _regex->setVisible(visible); +} + +void MacroConditionSourceEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _sources->setCurrentText( + GetWeakSourceName(_entryData->_source).c_str()); + _conditions->setCurrentIndex(static_cast(_entryData->_condition)); + _settings->setPlainText(QString::fromStdString(_entryData->_settings)); + _regex->setChecked(_entryData->_regex); + SetSettingsSelectionVisible(_entryData->_condition == + SourceCondition::SETTINGS); +} From aa7714210a2da7510292585ed4507460976da587 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Thu, 27 May 2021 20:55:31 +0200 Subject: [PATCH 43/66] Rework section widget to support dynamically sized content --- src/headers/section.hpp | 18 ++++- src/section.cpp | 149 +++++++++++++++++++++++++--------------- 2 files changed, 110 insertions(+), 57 deletions(-) diff --git a/src/headers/section.hpp b/src/headers/section.hpp index 8e3154a2..9dea41b2 100644 --- a/src/headers/section.hpp +++ b/src/headers/section.hpp @@ -14,18 +14,30 @@ 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}; + int _headerHeight = 0; + int _contentHeight = 0; }; diff --git a/src/section.cpp b/src/section.cpp index 9713950c..c76b6800 100644 --- a/src/section.cpp +++ b/src/section.cpp @@ -1,7 +1,9 @@ -#include #include "headers/section.hpp" #include "headers/utility.hpp" +#include +#include + Section::Section(const int animationDuration, QWidget *parent) : QWidget(parent), _animationDuration(animationDuration) { @@ -38,18 +40,102 @@ 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; _toggleAnimation->start(); } -void Section::SetContent(QWidget *w) +void Section::SetContent(QWidget *w, bool collapsed) +{ + CleanUpPreviousContent(); + delete _contentArea; + + // Setup contentArea + _contentArea = new QScrollArea(this); + _contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + _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); + + _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) { + _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")); + _toggleAnimation->addAnimation( + new QPropertyAnimation(this, "maximumHeight")); + _toggleAnimation->addAnimation( + new QPropertyAnimation(_contentArea, "maximumHeight")); + + for (int i = 0; i < _toggleAnimation->animationCount() - 1; ++i) { + QPropertyAnimation *SectionAnimation = + static_cast( + _toggleAnimation->animationAt(i)); + SectionAnimation->setDuration(_animationDuration); + SectionAnimation->setStartValue(_headerHeight); + SectionAnimation->setEndValue(_headerHeight + _contentHeight); + } + + QPropertyAnimation *contentAnimation = + static_cast(_toggleAnimation->animationAt( + _toggleAnimation->animationCount() - 1)); + contentAnimation->setDuration(_animationDuration); + contentAnimation->setStartValue(0); + contentAnimation->setEndValue(_contentHeight); + + QWidget::connect(_toggleAnimation, SIGNAL(finished()), this, + SLOT(AnimationFinished())); +} + +void Section::CleanUpPreviousContent() { // Clean up previous content if (_contentArea) { @@ -59,54 +145,9 @@ void Section::SetContent(QWidget *w) delete oldLayout; } } - - delete _contentArea; - delete _toggleAnimation; - - // Setup contentArea - _contentArea = new QScrollArea(this); - _contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - - // Start out collapsed - _contentArea->setMaximumHeight(0); - _contentArea->setMinimumHeight(0); - - auto newLayout = new QVBoxLayout(); - newLayout->addWidget(w); - _contentArea->setLayout(newLayout); - _mainLayout->addWidget(_contentArea, 1, 0, 1, 3); - - // Animation Setup - _toggleAnimation = new QParallelAnimationGroup(this); - _toggleAnimation->addAnimation( - new QPropertyAnimation(this, "minimumHeight")); - _toggleAnimation->addAnimation( - new QPropertyAnimation(this, "maximumHeight")); - _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( - _toggleAnimation->animationAt(i)); - SectionAnimation->setDuration(_animationDuration); - SectionAnimation->setStartValue(collapsedHeight); - SectionAnimation->setEndValue(collapsedHeight + contentHeight); - } - - QPropertyAnimation *contentAnimation = - static_cast(_toggleAnimation->animationAt( - _toggleAnimation->animationCount() - 1)); - contentAnimation->setDuration(_animationDuration); - contentAnimation->setStartValue(0); - contentAnimation->setEndValue(contentHeight); } -void Section::AddHeaderWidget(QWidget *w) +void Section::AnimationFinished() { - _headerWidgetLayout->addWidget(w); + _transitioning = false; } From bf03edd39129aaa4b0f3781c855545ef05a5dd9f Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Thu, 27 May 2021 20:57:38 +0200 Subject: [PATCH 44/66] Adjust size constraints of macro condition / action layouts The reason for this change is that the macro condition "source" was introduced which can change its required size drastically depending on which condition type is selected. --- forms/advanced-scene-switcher.ui | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index 56469ecd..890d39e4 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -770,8 +770,14 @@ + + QLayout::SetMinAndMaxSize + + + QLayout::SetMinAndMaxSize + @@ -789,7 +795,11 @@ - + + + QLayout::SetFixedSize + + @@ -859,6 +869,9 @@ + + QLayout::SetMinAndMaxSize + @@ -878,7 +891,11 @@ - + + + QLayout::SetMinAndMaxSize + + From 86d4e59dc20f338cd5482d249790452f6c783d9d Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Thu, 27 May 2021 21:00:17 +0200 Subject: [PATCH 45/66] Adjust to section widget rework --- src/headers/macro-action-edit.hpp | 3 +-- src/headers/macro-condition-edit.hpp | 3 +-- src/macro-action-edit.cpp | 19 ++++++------------- src/macro-condition-edit.cpp | 18 +++++------------- 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/headers/macro-action-edit.hpp b/src/headers/macro-action-edit.hpp index e6db7f35..ee045053 100644 --- a/src/headers/macro-action-edit.hpp +++ b/src/headers/macro-action-edit.hpp @@ -40,8 +40,7 @@ public: std::shared_ptr * = nullptr, const std::string &id = "scene_switch", bool startCollapsed = false); - void UpdateEntryData(const std::string &id); - void Collapse(bool collapsed); + void UpdateEntryData(const std::string &id, bool collapse); private slots: void ActionSelectionChanged(const QString &text); diff --git a/src/headers/macro-condition-edit.hpp b/src/headers/macro-condition-edit.hpp index 2b05c238..24f9b4fc 100644 --- a/src/headers/macro-condition-edit.hpp +++ b/src/headers/macro-condition-edit.hpp @@ -41,8 +41,7 @@ public: const std::string &id = "scene", bool root = true, bool startCollapsed = false); bool IsRootNode(); - void UpdateEntryData(const std::string &id); - void Collapse(bool collapsed); + void UpdateEntryData(const std::string &id, bool collapse); private slots: void LogicSelectionChanged(int idx); diff --git a/src/macro-action-edit.cpp b/src/macro-action-edit.cpp index eb61f550..1624a4e2 100644 --- a/src/macro-action-edit.cpp +++ b/src/macro-action-edit.cpp @@ -78,10 +78,9 @@ MacroActionEdit::MacroActionEdit(QWidget *parent, setLayout(mainLayout); _entryData = entryData; - UpdateEntryData(id); + UpdateEntryData(id, startCollapsed); _loading = false; - _section->Collapse(startCollapsed); } void MacroActionEdit::ActionSelectionChanged(const QString &text) @@ -97,22 +96,16 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text) *_entryData = MacroActionFactory::Create(id); auto widget = MacroActionFactory::CreateWidget(id, window(), *_entryData); - _section->SetContent(widget); - _section->Collapse(false); + _section->SetContent(widget, false); } -void MacroActionEdit::UpdateEntryData(const std::string &id) +void MacroActionEdit::UpdateEntryData(const std::string &id, bool collapse) { _actionSelection->setCurrentText( obs_module_text(MacroActionFactory::GetActionName(id).c_str())); auto widget = MacroActionFactory::CreateWidget(id, window(), *_entryData); - _section->SetContent(widget); -} - -void MacroActionEdit::Collapse(bool collapsed) -{ - _section->Collapse(collapsed); + _section->SetContent(widget, collapse); } void AdvSceneSwitcher::on_actionAdd_clicked() @@ -127,10 +120,10 @@ void AdvSceneSwitcher::on_actionAdd_clicked() std::lock_guard lock(switcher->m); macro->Actions().emplace_back(MacroActionFactory::Create(id)); - auto newEntry = new MacroActionEdit(this, ¯o->Actions().back(), id); + auto newEntry = + new MacroActionEdit(this, ¯o->Actions().back(), id, false); ui->macroEditActionLayout->addWidget(newEntry); ui->macroEditActionHelp->setVisible(false); - newEntry->Collapse(false); } void AdvSceneSwitcher::on_actionRemove_clicked() diff --git a/src/macro-condition-edit.cpp b/src/macro-condition-edit.cpp index 71c77c1b..9ebd22a9 100644 --- a/src/macro-condition-edit.cpp +++ b/src/macro-condition-edit.cpp @@ -104,9 +104,8 @@ MacroConditionEdit::MacroConditionEdit( _entryData = entryData; _isRoot = root; - UpdateEntryData(id); + UpdateEntryData(id, startCollapsed); _loading = false; - _section->Collapse(startCollapsed); } void MacroConditionEdit::LogicSelectionChanged(int idx) @@ -131,7 +130,7 @@ bool MacroConditionEdit::IsRootNode() return _isRoot; } -void MacroConditionEdit::UpdateEntryData(const std::string &id) +void MacroConditionEdit::UpdateEntryData(const std::string &id, bool collapse) { _conditionSelection->setCurrentText(obs_module_text( MacroConditionFactory::GetConditionName(id).c_str())); @@ -144,12 +143,7 @@ void MacroConditionEdit::UpdateEntryData(const std::string &id) _logicSelection->setCurrentIndex(static_cast(logic) - logic_root_offset); } - _section->SetContent(widget); -} - -void MacroConditionEdit::Collapse(bool collapsed) -{ - _section->Collapse(collapsed); + _section->SetContent(widget, collapse); } void MacroConditionEdit::ConditionSelectionChanged(const QString &text) @@ -167,8 +161,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text) (*_entryData)->SetLogicType(logic); auto widget = MacroConditionFactory::CreateWidget(id, window(), *_entryData); - _section->SetContent(widget); - _section->Collapse(false); + _section->SetContent(widget, false); } void AdvSceneSwitcher::on_conditionAdd_clicked() @@ -186,10 +179,9 @@ void AdvSceneSwitcher::on_conditionAdd_clicked() auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE; macro->Conditions().back()->SetLogicType(logic); auto newEntry = new MacroConditionEdit( - this, ¯o->Conditions().back(), id, root); + this, ¯o->Conditions().back(), id, root, false); ui->macroEditConditionLayout->addWidget(newEntry); ui->macroEditConditionHelp->setVisible(false); - newEntry->Collapse(false); } void AdvSceneSwitcher::on_conditionRemove_clicked() From 09ab29b4bade15899afee4e17263c8bf0e80cb82 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Thu, 27 May 2021 21:02:24 +0200 Subject: [PATCH 46/66] Trigger resize of widget --- src/macro-condition-source.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/macro-condition-source.cpp b/src/macro-condition-source.cpp index 2fff7c60..7dd4cac1 100644 --- a/src/macro-condition-source.cpp +++ b/src/macro-condition-source.cpp @@ -214,6 +214,7 @@ void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible) _settings->setVisible(visible); _getSettings->setVisible(visible); _regex->setVisible(visible); + adjustSize(); } void MacroConditionSourceEdit::UpdateEntryData() From 546a83ee0717fb5de666416b3b580ca443e41955 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Thu, 27 May 2021 21:27:59 +0200 Subject: [PATCH 47/66] Add macro action "plugin-state" --- CMakeLists.txt | 2 + data/locale/en-US.ini | 4 +- src/headers/macro-action-plugin-state.hpp | 55 +++++++++++ src/macro-action-plugin-state.cpp | 114 ++++++++++++++++++++++ 4 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 src/headers/macro-action-plugin-state.hpp create mode 100644 src/macro-action-plugin-state.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 44d1871e..48ac403d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,7 @@ set(advanced-scene-switcher_HEADERS 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 @@ -151,6 +152,7 @@ set(advanced-scene-switcher_SOURCES 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 diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index c189551a..592df4f7 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -201,7 +201,9 @@ 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" diff --git a/src/headers/macro-action-plugin-state.hpp b/src/headers/macro-action-plugin-state.hpp new file mode 100644 index 00000000..fd4dcb7c --- /dev/null +++ b/src/headers/macro-action-plugin-state.hpp @@ -0,0 +1,55 @@ +#pragma once +#include +#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 Create() + { + return std::make_shared(); + } + + 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 entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionPluginStateEdit( + parent, + std::dynamic_pointer_cast( + action)); + } + +private slots: + void ActionChanged(int value); + +protected: + QComboBox *_actions; + std::shared_ptr _entryData; + +private: + QHBoxLayout *_mainLayout; + bool _loading = true; +}; diff --git a/src/macro-action-plugin-state.cpp b/src/macro-action-plugin-state.cpp new file mode 100644 index 00000000..4f368862 --- /dev/null +++ b/src/macro-action-plugin-state.cpp @@ -0,0 +1,114 @@ +#include "headers/macro-action-plugin-state.hpp" +#include "headers/advanced-scene-switcher.hpp" +#include "headers/utility.hpp" + +#include + +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 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(_action)); + break; + } +} + +bool MacroActionPluginState::Save(obs_data_t *obj) +{ + MacroAction::Save(obj); + obs_data_set_int(obj, "action", static_cast(_action)); + return true; +} + +bool MacroActionPluginState::Load(obs_data_t *obj) +{ + MacroAction::Load(obj); + _action = + static_cast(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 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 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(_entryData->_action)); +} + +void MacroActionPluginStateEdit::ActionChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_action = static_cast(value); +} From 5c4dd6dae1d2a0f74dc9404cbb68a530b6b22ae7 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 20:42:02 +0200 Subject: [PATCH 48/66] Set "select " to disabled and sort selection alphabetically --- src/macro-action-filter.cpp | 12 ++- src/macro-action-scene-visibility.cpp | 5 +- src/macro-selection.cpp | 11 +++ src/utility.cpp | 134 ++++++++++++++------------ 4 files changed, 93 insertions(+), 69 deletions(-) diff --git a/src/macro-action-filter.cpp b/src/macro-action-filter.cpp index c984e9db..1d84fc0c 100644 --- a/src/macro-action-filter.cpp +++ b/src/macro-action-filter.cpp @@ -81,10 +81,11 @@ static inline void populateFilters(QComboBox *list, list->addItem(name); }; - list->clear(); - list->addItem(obs_module_text("AdvSceneSwitcher.selectFilter")); 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); } @@ -112,10 +113,10 @@ static inline void populateSourcesWithFilter(QComboBox *list) } return true; }; - - list->clear(); - list->addItem(obs_module_text("AdvSceneSwitcher.selectSource")); obs_enum_sources(enumSourcesWithFilters, list); + list->model()->sort(0); + addSelectionEntry(list, + obs_module_text("AdvSceneSwitcher.selectSource")); } MacroActionFilterEdit::MacroActionFilterEdit( @@ -174,6 +175,7 @@ void MacroActionFilterEdit::SourceChanged(const QString &text) std::lock_guard lock(switcher->m); _entryData->_source = GetWeakSourceByQString(text); } + _filters->clear(); populateFilters(_filters, _entryData->_source); } diff --git a/src/macro-action-scene-visibility.cpp b/src/macro-action-scene-visibility.cpp index 706c6995..c45cbf5b 100644 --- a/src/macro-action-scene-visibility.cpp +++ b/src/macro-action-scene-visibility.cpp @@ -117,8 +117,6 @@ static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr) static inline void populateSceneItems(QComboBox *list, OBSWeakSource sceneWeakSource = nullptr) { - list->clear(); - list->addItem(obs_module_text("AdvSceneSwitcher.selectItem")); std::set names; auto s = obs_weak_source_get_source(sceneWeakSource); auto scene = obs_scene_from_source(s); @@ -128,6 +126,8 @@ static inline void populateSceneItems(QComboBox *list, for (auto &name : names) { list->addItem(name); } + list->model()->sort(0); + addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem")); } MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit( @@ -186,6 +186,7 @@ void MacroActionSceneVisibilityEdit::SceneChanged(const QString &text) std::lock_guard lock(switcher->m); _entryData->_scene = GetWeakSourceByQString(text); } + _sources->clear(); populateSceneItems(_sources, _entryData->_scene); } diff --git a/src/macro-selection.cpp b/src/macro-selection.cpp index 3658a07e..9b610259 100644 --- a/src/macro-selection.cpp +++ b/src/macro-selection.cpp @@ -1,9 +1,20 @@ #include "headers/macro-selection.hpp" #include "headers/advanced-scene-switcher.hpp" +#include + MacroSelection::MacroSelection(QWidget *parent) : QComboBox(parent) { addItem(obs_module_text("AdvSceneSwitcher.selectMacro")); + + QStandardItemModel *model = + qobject_cast(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())); } diff --git a/src/utility.cpp b/src/utility.cpp index 5c507e89..e052be25 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -232,7 +232,7 @@ bool DisplayMessage(const QString &msg, bool question) void addSelectionEntry(QComboBox *sel, const char *description, bool selectable, const char *tooltip) { - sel->addItem(description); + sel->insertItem(0, description); if (strcmp(tooltip, "") != 0) { sel->setItemData(0, tooltip, Qt::ToolTipRole); @@ -260,31 +260,19 @@ void populateSourceSelection(QComboBox *list, bool addSelect) return true; }; - list->clear(); + obs_enum_sources(enumSourcesWithSources, list); + + list->model()->sort(0); if (addSelect) { addSelectionEntry( list, obs_module_text("AdvSceneSwitcher.selectSource"), false); } - - obs_enum_sources(enumSourcesWithSources, list); - list->model()->sort(0); } void 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); @@ -296,23 +284,38 @@ void populateTransitionSelection(QComboBox *sel, bool addCurrent, } obs_frontend_source_list_free(transitions); + + sel->model()->sort(0); + + if (addCurrent) { + sel->insertItem( + 0, + obs_module_text("AdvSceneSwitcher.currentTransition")); + } + + if (addSelect) { + addSelectionEntry( + sel, + obs_module_text("AdvSceneSwitcher.selectTransition"), + selectable); + } } void populateWindowSelection(QComboBox *sel, bool addSelect) { - if (addSelect) { - addSelectionEntry( - sel, obs_module_text("AdvSceneSwitcher.selectWindow")); - } std::vector windows; GetWindowList(windows); - sort(windows.begin(), windows.end()); for (std::string &window : windows) { sel->addItem(window.c_str()); } + sel->model()->sort(0); + if (addSelect) { + addSelectionEntry( + sel, obs_module_text("AdvSceneSwitcher.selectWindow")); + } #ifdef WIN32 sel->setItemData(0, obs_module_text("AdvSceneSwitcher.selectWindowTip"), Qt::ToolTipRole); @@ -321,14 +324,6 @@ void populateWindowSelection(QComboBox *sel, bool addSelect) void 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 /* -- */ { @@ -344,22 +339,24 @@ void populateAudioSelection(QComboBox *sel, bool addSelect) std::vector audioSources; obs_enum_sources(sourceEnum, &audioSources); - sort(audioSources.begin(), audioSources.end()); + for (std::string &source : audioSources) { sel->addItem(source.c_str()); } + + sel->model()->sort(0); + if (addSelect) { + addSelectionEntry( + sel, + obs_module_text("AdvSceneSwitcher.selectAudioSource"), + false, + obs_module_text( + "AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); + } } void populateVideoSelection(QComboBox *sel, 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 /* -- */ { @@ -379,19 +376,20 @@ void populateVideoSelection(QComboBox *sel, bool addSelect) for (std::string &source : videoSources) { sel->addItem(source.c_str()); } -} -void populateMediaSelection(QComboBox *sel, bool addSelect) -{ + sel->model()->sort(0); if (addSelect) { addSelectionEntry( sel, - obs_module_text("AdvSceneSwitcher.selectMediaSource"), + obs_module_text("AdvSceneSwitcher.selectVideoSource"), false, obs_module_text( "AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); } +} +void populateMediaSelection(QComboBox *sel, bool addSelect) +{ auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */ { std::vector *list = @@ -406,24 +404,35 @@ void populateMediaSelection(QComboBox *sel, bool addSelect) std::vector mediaSources; obs_enum_sources(sourceEnum, &mediaSources); - sort(mediaSources.begin(), mediaSources.end()); for (std::string &source : mediaSources) { sel->addItem(source.c_str()); } + + sel->model()->sort(0); + if (addSelect) { + addSelectionEntry( + sel, + obs_module_text("AdvSceneSwitcher.selectMediaSource"), + false, + obs_module_text( + "AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); + } } void populateProcessSelection(QComboBox *sel, bool addSelect) { + QStringList processes; + GetProcessList(processes); + processes.sort(); + for (QString &process : processes) { + sel->addItem(process); + } + + sel->model()->sort(0); if (addSelect) { addSelectionEntry( sel, obs_module_text("AdvSceneSwitcher.selectProcess")); } - - QStringList processes; - GetProcessList(processes); - processes.sort(); - for (QString &process : processes) - sel->addItem(process); } void populateSceneSelection(QComboBox *sel, bool addPrevious, @@ -431,19 +440,6 @@ void populateSceneSelection(QComboBox *sel, bool addPrevious, std::deque *sceneGroups, 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 scenes = obs_frontend_get_scene_names(); char **temp = scenes; while (*temp) { @@ -462,6 +458,20 @@ void populateSceneSelection(QComboBox *sel, bool addPrevious, sel->addItem(QString::fromStdString(sg.name)); } } + + sel->model()->sort(0); + 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); + } + } } QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor, From d112e92d33eb00c7a3cefa058eecba0259cfed28 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 20:54:27 +0200 Subject: [PATCH 49/66] Add scenes with filters to source selection for filter action --- src/macro-action-filter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/macro-action-filter.cpp b/src/macro-action-filter.cpp index 1d84fc0c..d9620e96 100644 --- a/src/macro-action-filter.cpp +++ b/src/macro-action-filter.cpp @@ -114,6 +114,7 @@ static inline void populateSourcesWithFilter(QComboBox *list) return true; }; obs_enum_sources(enumSourcesWithFilters, list); + obs_enum_scenes(enumSourcesWithFilters, list); list->model()->sort(0); addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectSource")); From b1f7a255d568cb0d39a1df83a7543c7d245f93f8 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 23:45:43 +0200 Subject: [PATCH 50/66] Use same collapse threshold for actions and sources Actions and conditions are no longer in fixed positions and instead divided by a separator. --- src/macro-tab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macro-tab.cpp b/src/macro-tab.cpp index ef2d69e4..3e81250f 100644 --- a/src/macro-tab.cpp +++ b/src/macro-tab.cpp @@ -9,7 +9,7 @@ static QMetaObject::Connection addPulse; const auto conditionsCollapseThreshold = 4; -const auto actionsCollapseThreshold = 2; +const auto actionsCollapseThreshold = 4; bool macroNameExists(std::string name) { From de6120c40119901cf380b67bbe737c34727043b2 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 20:07:29 +0200 Subject: [PATCH 51/66] Save and load source settings --- src/macro-condition-source.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/macro-condition-source.cpp b/src/macro-condition-source.cpp index 7dd4cac1..18af9997 100644 --- a/src/macro-condition-source.cpp +++ b/src/macro-condition-source.cpp @@ -82,6 +82,7 @@ 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(_condition)); + obs_data_set_string(obj, "settings", _settings.c_str()); return true; } @@ -92,6 +93,7 @@ bool MacroConditionSource::Load(obs_data_t *obj) _source = GetWeakSourceByName(sourceName); _condition = static_cast( obs_data_get_int(obj, "condition")); + _settings = obs_data_get_string(obj, "settings"); return true; } @@ -108,7 +110,6 @@ MacroConditionSourceEdit::MacroConditionSourceEdit( { _sources = new QComboBox(); _conditions = new QComboBox(); - _getSettings = new QPushButton(obs_module_text( "AdvSceneSwitcher.condition.source.getSettings")); _settings = new QPlainTextEdit(); From 703f24b8fcb6ae495e8e28025343512bc1109a7c Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 20:08:17 +0200 Subject: [PATCH 52/66] Remove unused include --- src/headers/macro-condition-source.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/headers/macro-condition-source.hpp b/src/headers/macro-condition-source.hpp index d0f92e26..60ff4107 100644 --- a/src/headers/macro-condition-source.hpp +++ b/src/headers/macro-condition-source.hpp @@ -1,6 +1,5 @@ #pragma once #include "macro.hpp" -#include #include #include From adb266927fdc77083f74ae1601da3577fa231ef1 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 20:09:05 +0200 Subject: [PATCH 53/66] Move getSourceSettings() to utility --- src/headers/utility.hpp | 1 + src/macro-condition-source.cpp | 11 ----------- src/utility.cpp | 11 +++++++++++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp index b300629e..c6f2b4c3 100644 --- a/src/headers/utility.hpp +++ b/src/headers/utility.hpp @@ -21,6 +21,7 @@ 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); /** * Populate layout with labels and widgets based on provided text diff --git a/src/macro-condition-source.cpp b/src/macro-condition-source.cpp index 18af9997..0bd58ef3 100644 --- a/src/macro-condition-source.cpp +++ b/src/macro-condition-source.cpp @@ -21,17 +21,6 @@ static std::map sourceConditionTypes = { "AdvSceneSwitcher.condition.source.type.settings"}, }; -std::string getSourceSettings(OBSWeakSource ws) -{ - auto s = obs_weak_source_get_source(ws); - obs_data_t *data = obs_source_get_settings(s); - std::string settings = obs_data_get_json(data); - obs_data_release(data); - obs_source_release(s); - - return settings; -} - bool checkSettings(const OBSWeakSource &source, const std::string &settings, bool useRegex) { diff --git a/src/utility.cpp b/src/utility.cpp index e052be25..1ad1eb82 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -207,6 +207,17 @@ bool compareIgnoringLineEnding(QString &s1, QString &s2) return true; } +std::string getSourceSettings(OBSWeakSource ws) +{ + auto s = obs_weak_source_get_source(ws); + obs_data_t *data = obs_source_get_settings(s); + std::string settings = obs_data_get_json(data); + obs_data_release(data); + obs_source_release(s); + + return settings; +} + bool DisplayMessage(const QString &msg, bool question) { if (question) { From 49a042039eb6e2a856895cbf915a8042c5e7b03f Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 20:09:29 +0200 Subject: [PATCH 54/66] Add ability to set source settings to macro action "source" --- data/locale/en-US.ini | 2 + src/headers/macro-action-source.hpp | 12 ++++- src/macro-action-source.cpp | 68 +++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 592df4f7..5c845ea1 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -186,8 +186,10 @@ 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" diff --git a/src/headers/macro-action-source.hpp b/src/headers/macro-action-source.hpp index 0e9f5c02..8b0e5178 100644 --- a/src/headers/macro-action-source.hpp +++ b/src/headers/macro-action-source.hpp @@ -1,11 +1,15 @@ #pragma once +#include "macro-action-edit.hpp" + #include #include -#include "macro-action-edit.hpp" +#include +#include enum class SourceAction { ENABLE, DISABLE, + SETTINGS, }; class MacroActionSource : public MacroAction { @@ -21,6 +25,7 @@ public: } OBSWeakSource _source; + std::string _settings = ""; SourceAction _action = SourceAction::ENABLE; private: @@ -47,14 +52,19 @@ public: 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 _entryData; private: + void SetWidgetVisibility(bool); QHBoxLayout *_mainLayout; bool _loading = true; }; diff --git a/src/macro-action-source.cpp b/src/macro-action-source.cpp index 09e27f8c..a3fd8936 100644 --- a/src/macro-action-source.cpp +++ b/src/macro-action-source.cpp @@ -12,8 +12,26 @@ bool MacroActionSource::_registered = MacroActionFactory::Register( const static std::map 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); @@ -24,6 +42,9 @@ bool MacroActionSource::PerformAction() case SourceAction::DISABLE: obs_source_set_enabled(s, false); break; + case SourceAction::SETTINGS: + setSourceSettings(s, _settings); + break; default: break; } @@ -48,6 +69,7 @@ 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(_action)); + obs_data_set_string(obj, "settings", _settings.c_str()); return true; } @@ -57,6 +79,7 @@ bool MacroActionSource::Load(obs_data_t *obj) const char *sourceName = obs_data_get_string(obj, "source"); _source = GetWeakSourceByName(sourceName); _action = static_cast(obs_data_get_int(obj, "action")); + _settings = obs_data_get_string(obj, "settings"); return true; } @@ -73,6 +96,9 @@ MacroActionSourceEdit::MacroActionSourceEdit( { _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")); @@ -83,17 +109,28 @@ MacroActionSourceEdit::MacroActionSourceEdit( 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 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; @@ -110,6 +147,8 @@ void MacroActionSourceEdit::UpdateEntryData() _actions->setCurrentIndex(static_cast(_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) @@ -130,4 +169,33 @@ void MacroActionSourceEdit::ActionChanged(int value) std::lock_guard lock(switcher->m); _entryData->_action = static_cast(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 lock(switcher->m); + _entryData->_settings = _settings->toPlainText().toStdString(); +} + +void MacroActionSourceEdit::SetWidgetVisibility(bool showSettings) +{ + _settings->setVisible(showSettings); + _getSettings->setVisible(showSettings); + _warning->setVisible(!showSettings); + adjustSize(); } From 78966e456a38d14aa1564ceb5df1e435d88ace93 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 29 May 2021 23:50:31 +0200 Subject: [PATCH 55/66] Fix empty selection not showing correctly --- src/utility.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utility.cpp b/src/utility.cpp index 1ad1eb82..7a94d3da 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -279,6 +279,7 @@ void populateSourceSelection(QComboBox *list, bool addSelect) list, obs_module_text("AdvSceneSwitcher.selectSource"), false); } + list->setCurrentIndex(0); } void populateTransitionSelection(QComboBox *sel, bool addCurrent, @@ -310,6 +311,7 @@ void populateTransitionSelection(QComboBox *sel, bool addCurrent, obs_module_text("AdvSceneSwitcher.selectTransition"), selectable); } + sel->setCurrentIndex(0); } void populateWindowSelection(QComboBox *sel, bool addSelect) @@ -327,6 +329,7 @@ void populateWindowSelection(QComboBox *sel, bool addSelect) addSelectionEntry( sel, obs_module_text("AdvSceneSwitcher.selectWindow")); } + sel->setCurrentIndex(0); #ifdef WIN32 sel->setItemData(0, obs_module_text("AdvSceneSwitcher.selectWindowTip"), Qt::ToolTipRole); @@ -364,6 +367,7 @@ void populateAudioSelection(QComboBox *sel, bool addSelect) obs_module_text( "AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); } + sel->setCurrentIndex(0); } void populateVideoSelection(QComboBox *sel, bool addSelect) @@ -397,6 +401,7 @@ void populateVideoSelection(QComboBox *sel, bool addSelect) obs_module_text( "AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); } + sel->setCurrentIndex(0); } void populateMediaSelection(QComboBox *sel, bool addSelect) @@ -428,6 +433,7 @@ void populateMediaSelection(QComboBox *sel, bool addSelect) obs_module_text( "AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); } + sel->setCurrentIndex(0); } void populateProcessSelection(QComboBox *sel, bool addSelect) @@ -444,6 +450,7 @@ void populateProcessSelection(QComboBox *sel, bool addSelect) addSelectionEntry( sel, obs_module_text("AdvSceneSwitcher.selectProcess")); } + sel->setCurrentIndex(0); } void populateSceneSelection(QComboBox *sel, bool addPrevious, @@ -483,6 +490,7 @@ void populateSceneSelection(QComboBox *sel, bool addPrevious, addSelectionEntry(sel, selectText.c_str(), selectable); } } + sel->setCurrentIndex(0); } QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor, From 7fda5e38207452a26ef977898cc26a8f01e0b7ff Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 30 May 2021 08:29:20 +0200 Subject: [PATCH 56/66] Fix empty selection not showing correctly for visibility and filter --- src/macro-action-filter.cpp | 2 ++ src/macro-action-scene-visibility.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/macro-action-filter.cpp b/src/macro-action-filter.cpp index d9620e96..81e4f95c 100644 --- a/src/macro-action-filter.cpp +++ b/src/macro-action-filter.cpp @@ -87,6 +87,7 @@ static inline void populateFilters(QComboBox *list, 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, @@ -118,6 +119,7 @@ static inline void populateSourcesWithFilter(QComboBox *list) list->model()->sort(0); addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectSource")); + list->setCurrentIndex(0); } MacroActionFilterEdit::MacroActionFilterEdit( diff --git a/src/macro-action-scene-visibility.cpp b/src/macro-action-scene-visibility.cpp index c45cbf5b..ab3d7ff1 100644 --- a/src/macro-action-scene-visibility.cpp +++ b/src/macro-action-scene-visibility.cpp @@ -128,6 +128,7 @@ static inline void populateSceneItems(QComboBox *list, } list->model()->sort(0); addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem")); + list->setCurrentIndex(0); } MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit( From fdb72629db7aca21d32ca5243b5e46faa5c937a3 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 30 May 2021 08:36:19 +0200 Subject: [PATCH 57/66] Do not trigger resize if section is collapsed --- src/headers/section.hpp | 1 + src/section.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/headers/section.hpp b/src/headers/section.hpp index 9dea41b2..b3a1ef2b 100644 --- a/src/headers/section.hpp +++ b/src/headers/section.hpp @@ -38,6 +38,7 @@ private: QWidget *_content = nullptr; int _animationDuration; std::atomic_bool _transitioning = {false}; + std::atomic_bool _collapsed = {false}; int _headerHeight = 0; int _contentHeight = 0; }; diff --git a/src/section.cpp b/src/section.cpp index c76b6800..c06a37dc 100644 --- a/src/section.cpp +++ b/src/section.cpp @@ -49,6 +49,7 @@ void Section::Collapse(bool collapse) ? QAbstractAnimation::Forward : QAbstractAnimation::Backward); _transitioning = true; + _collapsed = collapse; _toggleAnimation->start(); } @@ -93,7 +94,7 @@ void Section::AddHeaderWidget(QWidget *w) bool Section::eventFilter(QObject *obj, QEvent *event) { - if (event->type() == QEvent::Resize && !_transitioning) { + if (event->type() == QEvent::Resize && !_transitioning && !_collapsed) { _contentHeight = _content->sizeHint().height(); setMaximumHeight(_headerHeight + _contentHeight); setMinimumHeight(_headerHeight + _contentHeight); From c18d799f4af42f38fd90bebb3d118977dd5979e8 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 30 May 2021 08:36:50 +0200 Subject: [PATCH 58/66] Remove comment --- src/section.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/section.cpp b/src/section.cpp index c06a37dc..f38fa63a 100644 --- a/src/section.cpp +++ b/src/section.cpp @@ -138,7 +138,6 @@ void Section::SetupAnimations() void Section::CleanUpPreviousContent() { - // Clean up previous content if (_contentArea) { auto oldLayout = _contentArea->layout(); if (oldLayout) { From d9997d374dddd2726b69e237b0960cbf656f0c13 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 22:21:00 +0200 Subject: [PATCH 59/66] Add option to un-/pause macros using hotkeys --- data/locale/en-US.ini | 2 + src/headers/macro.hpp | 10 +++- src/macro.cpp | 109 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 115 insertions(+), 6 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 5c845ea1..3d24aa93 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -446,6 +446,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?" diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index f5532c20..27595da8 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -60,14 +60,14 @@ public: 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(const 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; }; @@ -88,12 +88,18 @@ public: bool SwitchesScene(); private: + void SetupHotkeys(); + void ClearHotkeys(); + void SetHotkeysDesc(); + std::string _name = ""; std::deque> _conditions; std::deque> _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); diff --git a/src/macro.cpp b/src/macro.cpp index 1e4865ab..04da2b70 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -17,9 +17,16 @@ const std::map 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() { @@ -94,11 +101,24 @@ bool Macro::PerformAction() 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) { obs_data_t *array_obj = obs_data_create(); @@ -164,8 +184,19 @@ bool Macro::Load(obs_data_t *obj) { _name = obs_data_get_string(obj, "name"); _paused = obs_data_get_bool(obj, "pause"); - bool root = true; + 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); @@ -212,7 +243,6 @@ bool Macro::Load(obs_data_t *obj) obs_data_release(array_obj); } obs_data_array_release(actions); - return true; } @@ -245,6 +275,77 @@ bool Macro::SwitchesScene() 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(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(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_string(obj, "id", GetId().c_str()); From 326cc0c46fe137208313bde19a758066e30f9d60 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 28 May 2021 22:55:29 +0200 Subject: [PATCH 60/66] Add generic time constraint to macro conditions Almost every macro condition can benefit from having the option to specify a time constraint. Instead of adding it to each condition separately add it to all conditions with the option of excluding it when registering a new condition type. --- data/locale/en-US.ini | 4 + src/duration-control.cpp | 104 ++++++++++++++++++++++ src/headers/duration-control.hpp | 50 +++++++++++ src/headers/macro-condition-audio.hpp | 5 -- src/headers/macro-condition-edit.hpp | 6 ++ src/headers/macro-condition-idle.hpp | 3 +- src/headers/macro-condition-recording.hpp | 5 -- src/headers/macro-condition-scene.hpp | 5 -- src/headers/macro-condition-streaming.hpp | 5 -- src/headers/macro-condition-video.hpp | 8 +- src/headers/macro.hpp | 13 ++- src/macro-condition-audio.cpp | 26 +----- src/macro-condition-edit.cpp | 63 ++++++++++++- src/macro-condition-idle.cpp | 2 +- src/macro-condition-interval.cpp | 2 +- src/macro-condition-plugin-state.cpp | 2 +- src/macro-condition-recording.cpp | 37 +------- src/macro-condition-scene.cpp | 35 +------- src/macro-condition-streaming.cpp | 37 +------- src/macro-condition-video.cpp | 39 +------- src/macro.cpp | 26 ++++++ 21 files changed, 274 insertions(+), 203 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 3d24aa93..b3aabb8c 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -479,3 +479,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" diff --git a/src/duration-control.cpp b/src/duration-control.cpp index 72f28b32..35df1602 100644 --- a/src/duration-control.cpp +++ b/src/duration-control.cpp @@ -1,4 +1,5 @@ #include "headers/duration-control.hpp" +#include "headers/utility.hpp" #include "obs-module.h" #include @@ -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,104 @@ 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(_type)); + _dur.Save(obj, secondsName, unitName); +} + +void DurationConstraint::Load(obs_data_t *obj, const char *condName, + const char *secondsName, const char *unitName) +{ + _type = static_cast(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); + 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); + + QHBoxLayout *layout = new QHBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(11); + layout->addWidget(_condition); + layout->addWidget(_duration); + setLayout(layout); +} + +void DurationConstraintEdit::SetValue(DurationConstraint &value) +{ + _duration->SetDuration(value.GetDuration()); + _condition->setCurrentIndex(static_cast(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(value); + _duration->setVisible(cond != DurationCondition::NONE); + emit ConditionChanged(cond); +} diff --git a/src/headers/duration-control.hpp b/src/headers/duration-control.hpp index 32c6a172..59b4d9e0 100644 --- a/src/headers/duration-control.hpp +++ b/src/headers/duration-control.hpp @@ -53,3 +53,53 @@ 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); +signals: + void DurationChanged(double value); + void UnitChanged(DurationUnit u); + void ConditionChanged(DurationCondition value); + +private: + DurationSelection *_duration; + QComboBox *_condition; +}; diff --git a/src/headers/macro-condition-audio.hpp b/src/headers/macro-condition-audio.hpp index 849da3eb..4f9d5028 100644 --- a/src/headers/macro-condition-audio.hpp +++ b/src/headers/macro-condition-audio.hpp @@ -6,8 +6,6 @@ #include #include -#include "duration-control.hpp" - enum class AudioCondition { ABOVE, BELOW, @@ -33,7 +31,6 @@ public: OBSWeakSource _audioSource; int _volume = 0; AudioCondition _condition = AudioCondition::ABOVE; - Duration _duration; obs_volmeter_t *_volmeter = nullptr; private: @@ -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 _entryData; diff --git a/src/headers/macro-condition-edit.hpp b/src/headers/macro-condition-edit.hpp index 24f9b4fc..347b6666 100644 --- a/src/headers/macro-condition-edit.hpp +++ b/src/headers/macro-condition-edit.hpp @@ -15,6 +15,7 @@ struct MacroConditionInfo { TCreateMethod _createFunc; TCreateWidgetMethod _createWidgetFunc; std::string _name; + bool _useDurationConstraint = true; }; class MacroConditionFactory { @@ -27,6 +28,7 @@ public: 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 _methods; @@ -46,11 +48,15 @@ public: private slots: void LogicSelectionChanged(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 *_entryData; diff --git a/src/headers/macro-condition-idle.hpp b/src/headers/macro-condition-idle.hpp index f1fa0889..32a20187 100644 --- a/src/headers/macro-condition-idle.hpp +++ b/src/headers/macro-condition-idle.hpp @@ -1,8 +1,9 @@ #pragma once #include "macro.hpp" +#include "duration-control.hpp" + #include #include -#include "duration-control.hpp" class MacroConditionIdle : public MacroCondition { public: diff --git a/src/headers/macro-condition-recording.hpp b/src/headers/macro-condition-recording.hpp index 77af9719..70275e56 100644 --- a/src/headers/macro-condition-recording.hpp +++ b/src/headers/macro-condition-recording.hpp @@ -2,7 +2,6 @@ #include "macro.hpp" #include #include -#include "duration-control.hpp" enum class RecordState { STOP, @@ -22,7 +21,6 @@ public: } RecordState _recordState; - Duration _duration; private: static bool _registered; @@ -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 _entryData; private: diff --git a/src/headers/macro-condition-scene.hpp b/src/headers/macro-condition-scene.hpp index 433b2fd5..cfd0f624 100644 --- a/src/headers/macro-condition-scene.hpp +++ b/src/headers/macro-condition-scene.hpp @@ -2,7 +2,6 @@ #include "macro.hpp" #include #include -#include "duration-control.hpp" enum class SceneType { CURRENT, @@ -22,7 +21,6 @@ public: OBSWeakSource _scene; SceneType _type; - Duration _duration; private: static bool _registered; @@ -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 _entryData; private: diff --git a/src/headers/macro-condition-streaming.hpp b/src/headers/macro-condition-streaming.hpp index 62f76c1e..845823f9 100644 --- a/src/headers/macro-condition-streaming.hpp +++ b/src/headers/macro-condition-streaming.hpp @@ -2,7 +2,6 @@ #include "macro.hpp" #include #include -#include "duration-control.hpp" enum class StreamState { STOP, @@ -21,7 +20,6 @@ public: } StreamState _streamState; - Duration _duration; private: static bool _registered; @@ -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 _entryData; private: diff --git a/src/headers/macro-condition-video.hpp b/src/headers/macro-condition-video.hpp index 8693d463..6fdc57bf 100644 --- a/src/headers/macro-condition-video.hpp +++ b/src/headers/macro-condition-video.hpp @@ -1,10 +1,10 @@ #pragma once #include "macro.hpp" +#include "screenshot-helper.hpp" + #include #include #include -#include "duration-control.hpp" -#include "screenshot-helper.hpp" enum class VideoCondition { MATCH, @@ -30,7 +30,6 @@ public: OBSWeakSource _videoSource; VideoCondition _condition = VideoCondition::MATCH; - Duration _duration; std::string _file = obs_module_text("AdvSceneSwitcher.enterPath"); private: @@ -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; diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index 27595da8..ca8673df 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -1,4 +1,6 @@ #pragma once +#include "duration-control.hpp" + #include #include #include @@ -40,13 +42,22 @@ public: virtual bool Save(obs_data_t *obj) = 0; virtual bool Load(obs_data_t *obj) = 0; virtual std::string GetId() = 0; + LogicType GetLogicType() { return _logic; } void SetLogicType(LogicType logic) { _logic = logic; } - static const std::map 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 { diff --git a/src/macro-condition-audio.cpp b/src/macro-condition-audio.cpp index dbb0c83b..7fe0ce9f 100644 --- a/src/macro-condition-audio.cpp +++ b/src/macro-condition-audio.cpp @@ -35,11 +35,7 @@ bool MacroConditionAudio::CheckCondition() // Reset for next check _peak = -std::numeric_limits::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(_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(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,8 +124,6 @@ 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 &))); @@ -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(cond); } -void MacroConditionAudioEdit::DurationChanged(double seconds) -{ - if (_loading || !_entryData) { - return; - } - - std::lock_guard 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(_entryData->_condition)); - _duration->SetDuration(_entryData->_duration); UpdateVolmeterSource(); } diff --git a/src/macro-condition-edit.cpp b/src/macro-condition-edit.cpp index 9ebd22a9..b47e6b44 100644 --- a/src/macro-condition-edit.cpp +++ b/src/macro-condition-edit.cpp @@ -16,9 +16,9 @@ bool MacroConditionFactory::Register(const std::string &id, std::shared_ptr 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; } @@ -26,9 +26,9 @@ QWidget * MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent, std::shared_ptr 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; } @@ -50,6 +50,14 @@ std::string MacroConditionFactory::GetIdByName(const QString &name) 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) { @@ -85,18 +93,27 @@ MacroConditionEdit::MacroConditionEdit( _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(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); @@ -144,6 +161,10 @@ void MacroConditionEdit::UpdateEntryData(const std::string &id, bool collapse) logic_root_offset); } _section->SetContent(widget, collapse); + + _dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id)); + auto constraint = (*_entryData)->GetDurationConstraint(); + _dur->SetValue(constraint); } void MacroConditionEdit::ConditionSelectionChanged(const QString &text) @@ -154,6 +175,9 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text) std::string id = MacroConditionFactory::GetIdByName(text); + auto temp = DurationConstraint(); + _dur->SetValue(temp); + std::lock_guard lock(switcher->m); auto logic = (*_entryData)->GetLogicType(); _entryData->reset(); @@ -162,6 +186,37 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text) auto widget = MacroConditionFactory::CreateWidget(id, window(), *_entryData); _section->SetContent(widget, false); + _dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id)); +} + +void MacroConditionEdit::DurationChanged(double seconds) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + (*_entryData)->SetDuration(seconds); +} + +void MacroConditionEdit::DurationConditionChanged(DurationCondition cond) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + (*_entryData)->SetDurationCondition(cond); +} + +void MacroConditionEdit::DurationUnitChanged(DurationUnit unit) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + (*_entryData)->SetDurationUnit(unit); } void AdvSceneSwitcher::on_conditionAdd_clicked() diff --git a/src/macro-condition-idle.cpp b/src/macro-condition-idle.cpp index bc3d86c8..79fdad57 100644 --- a/src/macro-condition-idle.cpp +++ b/src/macro-condition-idle.cpp @@ -8,7 +8,7 @@ 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() { diff --git a/src/macro-condition-interval.cpp b/src/macro-condition-interval.cpp index fa37d4dc..7783fead 100644 --- a/src/macro-condition-interval.cpp +++ b/src/macro-condition-interval.cpp @@ -8,7 +8,7 @@ const std::string MacroConditionInterval::id = "interval"; bool MacroConditionInterval::_registered = MacroConditionFactory::Register( MacroConditionInterval::id, {MacroConditionInterval::Create, MacroConditionIntervalEdit::Create, - "AdvSceneSwitcher.condition.interval"}); + "AdvSceneSwitcher.condition.interval", false}); bool MacroConditionInterval::CheckCondition() { diff --git a/src/macro-condition-plugin-state.cpp b/src/macro-condition-plugin-state.cpp index ac049bfe..0fce3935 100644 --- a/src/macro-condition-plugin-state.cpp +++ b/src/macro-condition-plugin-state.cpp @@ -9,7 +9,7 @@ bool MacroConditionPluginState::_registered = MacroConditionFactory::Register( MacroConditionPluginState::id, {MacroConditionPluginState::Create, MacroConditionPluginStateEdit::Create, - "AdvSceneSwitcher.condition.pluginState"}); + "AdvSceneSwitcher.condition.pluginState", false}); static std::map pluginStateConditionTypes = { {PluginStateCondition::SCENESWITCHED, diff --git a/src/macro-condition-recording.cpp b/src/macro-condition-recording.cpp index 20d70260..38c40a64 100644 --- a/src/macro-condition-recording.cpp +++ b/src/macro-condition-recording.cpp @@ -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(_recordState)); - _duration.Save(obj); return true; } @@ -53,7 +46,6 @@ bool MacroConditionRecord::Load(obs_data_t *obj) { MacroCondition::Load(obj); _recordState = static_cast(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 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(value); } -void MacroConditionRecordEdit::DurationChanged(double seconds) -{ - if (_loading || !_entryData) { - return; - } - - std::lock_guard lock(switcher->m); - _entryData->_duration.seconds = seconds; -} - -void MacroConditionRecordEdit::DurationUnitChanged(DurationUnit unit) -{ - if (_loading || !_entryData) { - return; - } - - std::lock_guard lock(switcher->m); - _entryData->_duration.displayUnit = unit; -} - void MacroConditionRecordEdit::UpdateEntryData() { if (!_entryData) { @@ -134,5 +100,4 @@ void MacroConditionRecordEdit::UpdateEntryData() _recordState->setCurrentIndex( static_cast(_entryData->_recordState)); - _duration->SetDuration(_entryData->_duration); } diff --git a/src/macro-condition-scene.cpp b/src/macro-condition-scene.cpp index c0b99799..ba7b3395 100644 --- a/src/macro-condition-scene.cpp +++ b/src/macro-condition-scene.cpp @@ -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(_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(obs_data_get_int(obj, "type")); - _duration.Load(obj); return true; } @@ -67,17 +61,12 @@ 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))); populateSceneSelection(_sceneSelection); populateTypeSelection(_sceneType); @@ -86,7 +75,6 @@ MacroConditionSceneEdit::MacroConditionSceneEdit( std::unordered_map 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(value); } -void MacroConditionSceneEdit::DurationChanged(double seconds) -{ - if (_loading || !_entryData) { - return; - } - - std::lock_guard lock(switcher->m); - _entryData->_duration.seconds = seconds; -} - -void MacroConditionSceneEdit::DurationUnitChanged(DurationUnit unit) -{ - if (_loading || !_entryData) { - return; - } - - std::lock_guard 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(_entryData->_type)); - _duration->SetDuration(_entryData->_duration); } diff --git a/src/macro-condition-streaming.cpp b/src/macro-condition-streaming.cpp index a1c6a40e..a975a33f 100644 --- a/src/macro-condition-streaming.cpp +++ b/src/macro-condition-streaming.cpp @@ -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(_streamState)); - _duration.Save(obj); return true; } @@ -49,7 +42,6 @@ bool MacroConditionStream::Load(obs_data_t *obj) { MacroCondition::Load(obj); _streamState = static_cast(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 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(value); } -void MacroConditionStreamEdit::DurationChanged(double seconds) -{ - if (_loading || !_entryData) { - return; - } - - std::lock_guard lock(switcher->m); - _entryData->_duration.seconds = seconds; -} - -void MacroConditionStreamEdit::DurationUnitChanged(DurationUnit unit) -{ - if (_loading || !_entryData) { - return; - } - - std::lock_guard lock(switcher->m); - _entryData->_duration.displayUnit = unit; -} - void MacroConditionStreamEdit::UpdateEntryData() { if (!_entryData) { @@ -130,5 +96,4 @@ void MacroConditionStreamEdit::UpdateEntryData() _streamState->setCurrentIndex( static_cast(_entryData->_streamState)); - _duration->SetDuration(_entryData->_duration); } diff --git a/src/macro-condition-video.cpp b/src/macro-condition-video.cpp index 66d55f74..2120a796 100644 --- a/src/macro-condition-video.cpp +++ b/src/macro-condition-video.cpp @@ -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(_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(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,10 +140,6 @@ 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, @@ -167,7 +152,6 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( std::unordered_map 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 lock(switcher->m); - _entryData->_duration.seconds = seconds; -} - -void MacroConditionVideoEdit::DurationUnitChanged(DurationUnit unit) -{ - if (_loading || !_entryData) { - return; - } - - std::lock_guard 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(_entryData->_condition)); - _duration->SetDuration(_entryData->_duration); _filePath->setText(QString::fromStdString(_entryData->_file)); if (!requiresFileInput(_entryData->_condition)) { diff --git a/src/macro.cpp b/src/macro.cpp index 04da2b70..a6d46b4c 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -33,6 +33,10 @@ 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: @@ -350,15 +354,37 @@ bool MacroCondition::Save(obs_data_t *obj) { obs_data_set_string(obj, "id", GetId().c_str()); obs_data_set_int(obj, "logic", static_cast(_logic)); + _duration.Save(obj); return true; } bool MacroCondition::Load(obs_data_t *obj) { _logic = static_cast(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_string(obj, "id", GetId().c_str()); From c82c56dbb71f81cf3fecf59e396e2c11c994c5d9 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 30 May 2021 13:05:43 +0200 Subject: [PATCH 61/66] Remove {{duration}} placeholder text where longer needed --- data/locale/en-US.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index b3aabb8c..b8f8e851 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -82,13 +82,13 @@ AdvSceneSwitcher.logic.not="If not" AdvSceneSwitcher.condition.audio="Audio" AdvSceneSwitcher.condition.audio.state.below="Below" AdvSceneSwitcher.condition.audio.state.above="Above" -AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}} for {{duration}} seconds" +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" @@ -107,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" From b9f6e47a4927cb746f67833a9aebe8e7e8764690 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 30 May 2021 13:06:18 +0200 Subject: [PATCH 62/66] Add backwads compatability check to load() of DurationConstraint --- src/duration-control.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/duration-control.cpp b/src/duration-control.cpp index 35df1602..bd896f23 100644 --- a/src/duration-control.cpp +++ b/src/duration-control.cpp @@ -151,6 +151,14 @@ void DurationConstraint::Save(obs_data_t *obj, const char *condName, 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(DurationCondition::MORE)); + } + _type = static_cast(obs_data_get_int(obj, condName)); _dur.Load(obj, secondsName, unitName); } From 38161f63a3779e7945927baac2a30f3cbfa1353e Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Tue, 1 Jun 2021 21:09:53 +0200 Subject: [PATCH 63/66] Add helper getDataFilePath() Used to get absolute file path to files in the plugin's data directory --- src/headers/utility.hpp | 2 ++ src/utility.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp index c6f2b4c3..2a461120 100644 --- a/src/headers/utility.hpp +++ b/src/headers/utility.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "scene-group.hpp" bool WeakSourceValid(obs_weak_source_t *ws); @@ -22,6 +23,7 @@ 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::filesystem::path getDataFilePath(const std::string &file); /** * Populate layout with labels and widgets based on provided text diff --git a/src/utility.cpp b/src/utility.cpp index 7a94d3da..1f2283d2 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -218,6 +218,17 @@ std::string getSourceSettings(OBSWeakSource ws) return settings; } +std::filesystem::path getDataFilePath(const std::string &file) +{ + const char *root_path = obs_get_module_data_path(obs_current_module()); + if (root_path) { + auto ret = std::filesystem::u8path(root_path); + ret.append(file.data()); + return ret; + } + return ""; +} + bool DisplayMessage(const QString &msg, bool question) { if (question) { From 72f67705edce8b41c489e441f98298c573baced1 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Tue, 1 Jun 2021 21:10:50 +0200 Subject: [PATCH 64/66] Collapse time restriction to clock symbol if no restriction is set --- data/res/time.svg | 3 +++ src/duration-control.cpp | 23 +++++++++++++++++++++-- src/headers/duration-control.hpp | 5 +++++ src/headers/utility.hpp | 3 +-- src/utility.cpp | 10 ++++------ 5 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 data/res/time.svg diff --git a/data/res/time.svg b/data/res/time.svg new file mode 100644 index 00000000..cafc173a --- /dev/null +++ b/data/res/time.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/duration-control.cpp b/src/duration-control.cpp index bd896f23..3eee43eb 100644 --- a/src/duration-control.cpp +++ b/src/duration-control.cpp @@ -209,21 +209,28 @@ 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) @@ -246,6 +253,18 @@ void DurationConstraintEdit::SetDuration(const Duration &d) void DurationConstraintEdit::_ConditionChanged(int value) { auto cond = static_cast(value); - _duration->setVisible(cond != DurationCondition::NONE); + 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); +} diff --git a/src/headers/duration-control.hpp b/src/headers/duration-control.hpp index 59b4d9e0..3ff1d774 100644 --- a/src/headers/duration-control.hpp +++ b/src/headers/duration-control.hpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "obs-data.h" @@ -94,12 +95,16 @@ public: 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; }; diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp index 2a461120..6a54c970 100644 --- a/src/headers/utility.hpp +++ b/src/headers/utility.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "scene-group.hpp" bool WeakSourceValid(obs_weak_source_t *ws); @@ -23,7 +22,7 @@ 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::filesystem::path getDataFilePath(const std::string &file); +std::string getDataFilePath(const std::string &file); /** * Populate layout with labels and widgets based on provided text diff --git a/src/utility.cpp b/src/utility.cpp index 1f2283d2..8f06f9a5 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -218,13 +218,11 @@ std::string getSourceSettings(OBSWeakSource ws) return settings; } -std::filesystem::path getDataFilePath(const std::string &file) +std::string getDataFilePath(const std::string &file) { - const char *root_path = obs_get_module_data_path(obs_current_module()); - if (root_path) { - auto ret = std::filesystem::u8path(root_path); - ret.append(file.data()); - return ret; + std::string root_path = obs_get_module_data_path(obs_current_module()); + if (!root_path.empty()) { + return root_path + "/" + file; } return ""; } From bd00a3f97e4d324ffa93e937214da6412b656fa1 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 4 Jun 2021 10:25:41 +0200 Subject: [PATCH 65/66] Remove preprocessing / todo --- src/macro-condition-window.cpp | 5 ----- src/switch-window.cpp | 12 ------------ 2 files changed, 17 deletions(-) diff --git a/src/macro-condition-window.cpp b/src/macro-condition-window.cpp index 3a4d2407..e0289ba6 100644 --- a/src/macro-condition-window.cpp +++ b/src/macro-condition-window.cpp @@ -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; } diff --git a/src/switch-window.cpp b/src/switch-window.cpp index f9455382..ebd83322 100644 --- a/src/switch-window.cpp +++ b/src/switch-window.cpp @@ -390,13 +390,7 @@ void WindowSwitch::load(obs_data_t *obj) window = obs_data_get_string(obj, "windowTitle"); fullscreen = obs_data_get_bool(obj, "fullscreen"); -#if __APPLE__ - // TODO: - // not implemented on MacOS as I cannot test it - maximized = false; -#else maximized = obs_data_get_bool(obj, "maximized"); -#endif focus = obs_data_get_bool(obj, "focus") || !obs_data_has_user_value(obj, "focus"); } @@ -425,12 +419,6 @@ WindowSwitchWidget::WindowSwitchWidget(QWidget *parent, WindowSwitch *s) windows->setEditable(true); windows->setMaxVisibleItems(20); -#if __APPLE__ - // TODO: - // not implemented on MacOS as I cannot test it - maximized->setDisabled(true); - maximized->setVisible(false); -#endif if (s) { windows->setCurrentText(s->window.c_str()); From 047ca93486e9d3528a6168f37b5642b0ce15924d Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 6 Jun 2021 19:05:55 +0200 Subject: [PATCH 66/66] Clean up --- src/utility.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utility.cpp b/src/utility.cpp index 8f06f9a5..6a448ca9 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -1,4 +1,3 @@ -#pragma once #include "headers/utility.hpp" #include "headers/platform-funcs.hpp"