From 21a788b48bc2534587a20740551e5c57b321e406 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 13 Sep 2020 00:03:38 +0200 Subject: [PATCH] Improve time tab (#34) * add support for weekdays and allow reordering on time tab * add option to use time relative to streaming/recording start on time tab --- forms/advanced-scene-switcher.ui | 103 ++++++++++++++++- src/advanced-scene-switcher.cpp | 18 +++ src/headers/advanced-scene-switcher.hpp | 4 +- src/headers/switcher-data-structs.hpp | 7 +- src/headers/utility.hpp | 55 +++++++++- src/time-switch.cpp | 140 ++++++++++++++++++++---- 6 files changed, 292 insertions(+), 35 deletions(-) diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index c3aebde7..84328e3b 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -2917,13 +2917,23 @@ Time - + + + + + + 100 + 0 + + + + - At + at @@ -2989,12 +2999,12 @@ true - true + false - + @@ -3027,6 +3037,91 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 22 + 22 + + + + + + + + ../../../forms/images/up.svg../../../forms/images/up.svg + + + true + + + upArrowIconSmall + + + + + + + + 22 + 22 + + + + + + + + ../../../forms/images/down.svg../../../forms/images/down.svg + + + true + + + downArrowIconSmall + + + diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index 0f5daf80..205814c0 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -367,6 +367,16 @@ void handleSceneChange(SwitcherData *s) s->autoStartedRecently = false; } +void setLiveTime(SwitcherData *s) +{ + s->liveTime = QDateTime::currentDateTime(); +} + +void resetLiveTime(SwitcherData *s) +{ + s->liveTime = QDateTime(); +} + static void OBSEvent(enum obs_frontend_event event, void *switcher) { switch (event) { @@ -376,6 +386,14 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher) case OBS_FRONTEND_EVENT_SCENE_CHANGED: handleSceneChange((SwitcherData *)switcher); break; + case OBS_FRONTEND_EVENT_RECORDING_STARTED: + case OBS_FRONTEND_EVENT_STREAMING_STARTED: + setLiveTime((SwitcherData *)switcher); + break; + case OBS_FRONTEND_EVENT_RECORDING_STOPPED: + case OBS_FRONTEND_EVENT_STREAMING_STOPPED: + resetLiveTime((SwitcherData *)switcher); + break; default: break; } diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index dc333f7b..d76facf8 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -42,7 +42,7 @@ public: int executableFindByData(const QString &exe); int IgnoreIdleWindowsFindByData(const QString &window); int randomFindByData(const QString &scene); - int timeFindByData(const QString &timeStr); + int timeFindByData(const timeTrigger &trigger, const QTime &time); void UpdateNonMatchingScene(const QString &name); void UpdateAutoStopScene(const QString &name); @@ -172,6 +172,8 @@ public slots: void on_timeSwitches_currentRowChanged(int idx); void on_timeAdd_clicked(); void on_timeRemove_clicked(); + void on_timeUp_clicked(); + void on_timeDown_clicked(); void on_priorityUp_clicked(); void on_priorityDown_clicked(); diff --git a/src/headers/switcher-data-structs.hpp b/src/headers/switcher-data-structs.hpp index 5bf53612..16a9efc8 100644 --- a/src/headers/switcher-data-structs.hpp +++ b/src/headers/switcher-data-structs.hpp @@ -235,16 +235,18 @@ struct MediaSwitch { struct TimeSwitch { OBSWeakSource scene; OBSWeakSource transition; + timeTrigger trigger; QTime time; bool matched; bool usePreviousScene; std::string timeSwitchStr; inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_, - QTime time_, bool usePreviousScene_, - std::string timeSwitchStr_) + timeTrigger trigger_, QTime time_, + bool usePreviousScene_, std::string timeSwitchStr_) : scene(scene_), transition(transition_), + trigger(trigger_), time(time_), usePreviousScene(usePreviousScene_), timeSwitchStr(timeSwitchStr_) @@ -324,6 +326,7 @@ struct SwitcherData { std::vector mediaSwitches; std::vector timeSwitches; + QDateTime liveTime; std::vector functionNamesByPriority = std::vector{ DEFAULT_PRIORITY_0, DEFAULT_PRIORITY_1, DEFAULT_PRIORITY_2, diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp index e78758dc..0ca9d980 100644 --- a/src/headers/utility.hpp +++ b/src/headers/utility.hpp @@ -2,7 +2,6 @@ #include #include #include -#include "switcher-data-structs.hpp" static inline bool WeakSourceValid(obs_weak_source_t *ws) { @@ -170,12 +169,58 @@ MakeMediaSwitchName(const QString &source, const QString &scene, return switchName; } +typedef enum { + ANY_DAY = 0, + MONDAY = 1, + TUSEDAY = 2, + WEDNESDAY = 3, + THURSDAY = 4, + FRIDAY = 5, + SATURDAY = 6, + SUNDAY = 7, + LIVE = 8 +} timeTrigger; + static inline QString MakeTimeSwitchName(const QString &scene, - const QString &transition, QTime &time) + const QString &transition, + const timeTrigger &trigger, + const QTime &time) { - QString switchName = QStringLiteral("At ") + time.toString() + - QStringLiteral(" switch to ") + scene + - QStringLiteral(" using ") + transition; + QString switchName; + + switch (trigger) { + case ANY_DAY: + switchName = QStringLiteral("On any weekday"); + break; + case MONDAY: + switchName = QStringLiteral("Mondays"); + break; + case TUSEDAY: + switchName = QStringLiteral("Tusedays"); + break; + case WEDNESDAY: + switchName = QStringLiteral("Wednesdays"); + break; + case THURSDAY: + switchName = QStringLiteral("Thursdays"); + break; + case FRIDAY: + switchName = QStringLiteral("Fridays"); + break; + case SATURDAY: + switchName = QStringLiteral("Saturdays"); + break; + case SUNDAY: + switchName = QStringLiteral("Sundays"); + break; + case LIVE: + switchName = QStringLiteral( + "Relative to starting streaming / recording"); + break; + } + switchName += QStringLiteral(" at ") + time.toString() + + QStringLiteral(" switch to ") + scene + + QStringLiteral(" using ") + transition; return switchName; } diff --git a/src/time-switch.cpp b/src/time-switch.cpp index dba2fe27..f23198c3 100644 --- a/src/time-switch.cpp +++ b/src/time-switch.cpp @@ -1,5 +1,16 @@ #include "headers/advanced-scene-switcher.hpp" +static std::vector> triggerTable = { + {"On any day", timeTrigger::ANY_DAY}, + {"Mondays", timeTrigger::MONDAY}, + {"Tuesdays", timeTrigger::TUSEDAY}, + {"Wednesdays", timeTrigger::WEDNESDAY}, + {"Thursdays", timeTrigger::THURSDAY}, + {"Fridays", timeTrigger::FRIDAY}, + {"Saturdays", timeTrigger::SATURDAY}, + {"Sundays", timeTrigger::SUNDAY}, + {"Atfer streaming/recording start", timeTrigger::LIVE}}; + void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx) { if (loading) @@ -18,6 +29,7 @@ void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx) QString transitionName = GetWeakSourceName(s.transition).c_str(); ui->timeScenes->setCurrentText(sceneName); + ui->timeTrigger->setCurrentIndex(s.trigger); ui->timeEdit->setTime(s.time); ui->timeTransitions->setCurrentText(transitionName); break; @@ -25,9 +37,10 @@ void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx) } } -int SceneSwitcher::timeFindByData(const QString &timeStr) +int SceneSwitcher::timeFindByData(const timeTrigger &trigger, const QTime &time) { - QRegExp rx("At " + timeStr + " switch to .*"); + QRegExp rx(MakeTimeSwitchName(QStringLiteral(".*"), + QStringLiteral(".*"), trigger, time)); int count = ui->timeSwitches->count(); for (int i = 0; i < count; i++) { @@ -53,15 +66,25 @@ void SceneSwitcher::on_timeAdd_clicked() OBSWeakSource source = GetWeakSourceByQString(sceneName); OBSWeakSource transition = GetWeakTransitionByQString(transitionName); - QString text = MakeTimeSwitchName(sceneName, transitionName, time); + std::string triggerStr = ui->timeTrigger->currentText().toStdString(); + auto it = std::find_if( + triggerTable.begin(), triggerTable.end(), + [&triggerStr]( + const std::pair &element) { + return element.first == triggerStr; + }); + timeTrigger trigger = it->second; + + QString text = + MakeTimeSwitchName(sceneName, transitionName, trigger, time); QVariant v = QVariant::fromValue(text); - int idx = timeFindByData(time.toString()); + int idx = timeFindByData(trigger, time); if (idx == -1) { std::lock_guard lock(switcher->m); switcher->timeSwitches.emplace_back( - source, transition, time, + source, transition, trigger, time, (sceneName == QString(PREVIOUS_SCENE_NAME)), text.toUtf8().constData()); @@ -76,7 +99,7 @@ void SceneSwitcher::on_timeAdd_clicked() { std::lock_guard lock(switcher->m); for (auto &s : switcher->timeSwitches) { - if (s.time == time) { + if (s.trigger == trigger && s.time == time) { s.scene = source; s.transition = transition; s.usePreviousScene = @@ -88,8 +111,6 @@ void SceneSwitcher::on_timeAdd_clicked() } } } - - ui->timeSwitches->sortItems(); } } @@ -119,24 +140,83 @@ void SceneSwitcher::on_timeRemove_clicked() delete item; } +void SceneSwitcher::on_timeUp_clicked() +{ + int index = ui->timeSwitches->currentRow(); + if (index != -1 && index != 0) { + ui->timeSwitches->insertItem(index - 1, + ui->timeSwitches->takeItem(index)); + ui->timeSwitches->setCurrentRow(index - 1); + + std::lock_guard lock(switcher->m); + + iter_swap(switcher->timeSwitches.begin() + index, + switcher->timeSwitches.begin() + index - 1); + } +} + +void SceneSwitcher::on_timeDown_clicked() +{ + int index = ui->timeSwitches->currentRow(); + if (index != -1 && index != ui->timeSwitches->count() - 1) { + ui->timeSwitches->insertItem(index + 1, + ui->timeSwitches->takeItem(index)); + ui->timeSwitches->setCurrentRow(index + 1); + + std::lock_guard lock(switcher->m); + + iter_swap(switcher->timeSwitches.begin() + index, + switcher->timeSwitches.begin() + index + 1); + } +} + +bool timesAreInInterval(QTime &time1, QTime &time2, int &interval) +{ + bool ret = false; + QTime validSwitchTimeWindow = time1.addMSecs(interval); + + ret = time1 <= time2 && time2 <= validSwitchTimeWindow; + // check for overflow + if (!ret && validSwitchTimeWindow.msecsSinceStartOfDay() < interval) { + ret = time2 >= time1 || time2 <= validSwitchTimeWindow; + } + return ret; +} + +bool checkLiveTime(TimeSwitch &s, QDateTime &start, int &interval) +{ + if (start.isNull()) + return false; + + QDateTime now = QDateTime::currentDateTime(); + QTime timePassed = QTime(0, 0).addMSecs(start.msecsTo(now)); + + return timesAreInInterval(s.time, timePassed, interval); +} + +bool checkRegularTime(TimeSwitch &s, int &interval) +{ + bool match = false; + if (s.trigger != ANY_DAY && + s.trigger != QDate::currentDate().dayOfWeek()) + return false; + + QTime now = QTime::currentTime(); + + return timesAreInInterval(s.time, now, interval); +} + void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene, OBSWeakSource &transition) { if (timeSwitches.size() == 0) return; - QTime now = QTime::currentTime(); - for (TimeSwitch &s : timeSwitches) { - - QTime validSwitchTimeWindow = s.time.addMSecs(interval); - - match = s.time <= now && now <= validSwitchTimeWindow; - if (!match && - validSwitchTimeWindow.msecsSinceStartOfDay() < interval) { - // check for overflow - match = now >= s.time || now <= validSwitchTimeWindow; - } + if (s.trigger == LIVE) + match = checkLiveTime(s, liveTime, interval); + else + match = checkRegularTime(s, interval); if (match) { scene = (s.usePreviousScene) ? previousScene : s.scene; @@ -172,6 +252,7 @@ void SwitcherData::saveTimeSwitches(obs_data_t *obj) : sceneName); obs_data_set_string(array_obj, "transition", transitionName); + obs_data_set_int(array_obj, "trigger", s.trigger); obs_data_set_string( array_obj, "time", s.time.toString().toStdString().c_str()); @@ -199,17 +280,19 @@ void SwitcherData::loadTimeSwitches(obs_data_t *obj) const char *scene = obs_data_get_string(array_obj, "scene"); const char *transition = obs_data_get_string(array_obj, "transition"); + timeTrigger trigger = + (timeTrigger)obs_data_get_int(array_obj, "trigger"); QTime time = QTime::fromString( obs_data_get_string(array_obj, "time")); std::string timeSwitchStr = - MakeTimeSwitchName(scene, transition, time) + MakeTimeSwitchName(scene, transition, trigger, time) .toUtf8() .constData(); switcher->timeSwitches.emplace_back( GetWeakSourceByName(scene), - GetWeakTransitionByName(transition), time, + GetWeakTransitionByName(transition), trigger, time, (strcmp(scene, PREVIOUS_SCENE_NAME) == 0), timeSwitchStr); @@ -223,13 +306,24 @@ void SceneSwitcher::setupTimeTab() populateSceneSelection(ui->timeScenes, true); populateTransitionSelection(ui->timeTransitions); + for (auto t : triggerTable) { + ui->timeTrigger->addItem(t.first.c_str()); + } + + // assuming the streaming / recording entry is always last + ui->timeTrigger->setItemData( + (int)triggerTable.size() - 1, + "The time relative to the start of streaming / recording will be used", + Qt::ToolTipRole); + for (auto &s : switcher->timeSwitches) { std::string sceneName = (s.usePreviousScene) ? PREVIOUS_SCENE_NAME : GetWeakSourceName(s.scene); std::string transitionName = GetWeakSourceName(s.transition); - QString listText = MakeTimeSwitchName( - sceneName.c_str(), transitionName.c_str(), s.time); + QString listText = MakeTimeSwitchName(sceneName.c_str(), + transitionName.c_str(), + s.trigger, s.time); QListWidgetItem *item = new QListWidgetItem(listText, ui->timeSwitches);