diff --git a/buildspec.json b/buildspec.json index 49280c7..16de67b 100644 --- a/buildspec.json +++ b/buildspec.json @@ -38,7 +38,7 @@ }, "name": "osi-branch-output", "displayName": "Branch Output Plugin", - "version": "0.9.5", + "version": "0.9.6", "author": "OPENSPHERE Inc.", "website": "https://opensphere.co.jp/", "email": "info@opensphere.co.jp", diff --git a/data/locale/ca-ES.ini b/data/locale/ca-ES.ini index 6a1e536..ee41577 100644 --- a/data/locale/ca-ES.ini +++ b/data/locale/ca-ES.ini @@ -27,3 +27,5 @@ Status.Reconnecting="Tornant a connectar" Status.Inactive="Inactiu" Status.Active="Actiu" Reset="Restableix" +EnableAll="Activa-ho tot" +DisableAll="Desactiva-ho tot" diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index 27b2ede..15e7ccd 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -26,4 +26,6 @@ Status.Live="Live" Status.Reconnecting="Wiederverbindung" Status.Inactive="Inaktiv" Status.Active="Aktiv" -Reset="Zurücksetzen" \ No newline at end of file +Reset="Zurücksetzen" +EnableAll="Alle aktivieren" +DisableAll="Alle deaktivieren" \ No newline at end of file diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 0477952..c1e4550 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -26,4 +26,6 @@ Status.Live="Live" Status.Reconnecting="Reconnecting" Status.Inactive="Inactive" Status.Active="Active" -Reset="Reset" \ No newline at end of file +Reset="Reset" +EnableAll="Activate All" +DisableAll="Deactivate All" diff --git a/data/locale/fr-FR.ini b/data/locale/fr-FR.ini index 80d5e52..930488c 100644 --- a/data/locale/fr-FR.ini +++ b/data/locale/fr-FR.ini @@ -26,4 +26,6 @@ Status.Live="En direct" Status.Reconnecting="Reconnexion" Status.Inactive="Inactif" Status.Active="Actif" -Reset="Réinitialiser" \ No newline at end of file +Reset="Réinitialiser" +EnableAll="Activer tout" +DisableAll="Désactiver tout" \ No newline at end of file diff --git a/data/locale/ja-JP.ini b/data/locale/ja-JP.ini index 045f2a0..3b1f851 100644 --- a/data/locale/ja-JP.ini +++ b/data/locale/ja-JP.ini @@ -26,4 +26,6 @@ Status.Live="配信中" Status.Reconnecting="再接続中" Status.Inactive="非アクティブ" Status.Active="アクティブ" -Reset="リセット" \ No newline at end of file +Reset="リセット" +EnableAll="全て有効化" +DisableAll="全て無効化" diff --git a/data/locale/ko-KR.ini b/data/locale/ko-KR.ini index 90a1e98..04e9228 100644 --- a/data/locale/ko-KR.ini +++ b/data/locale/ko-KR.ini @@ -26,4 +26,6 @@ Status.Live="살다" Status.Reconnecting="다시 연결" Status.Inactive="비활성" Status.Active="활동적인" -Reset="리셋" \ No newline at end of file +Reset="리셋" +EnableAll="모두 활성화" +DisableAll="모두 무효화" diff --git a/data/locale/ro-RO.ini b/data/locale/ro-RO.ini index 52ab61f..8b7e48b 100644 --- a/data/locale/ro-RO.ini +++ b/data/locale/ro-RO.ini @@ -26,4 +26,6 @@ Status.Live="Trăi" Status.Reconnecting="Reconectare" Status.Inactive="Inactiv" Status.Active="Activ" -Reset="Resetați" \ No newline at end of file +Reset="Resetați" +EnableAll="Activați toate" +DisableAll="Dezactivați toate" \ No newline at end of file diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index 5b81ff7..4461079 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -26,4 +26,6 @@ Status.Live="Жить" Status.Reconnecting="Повторное подключение" Status.Inactive="Неактивный" Status.Active="Активный" -Reset="Перезагрузить" \ No newline at end of file +Reset="Перезагрузить" +EnableAll="Активировать все" +DisableAll="Деактивировать все" \ No newline at end of file diff --git a/data/locale/uk-UA.ini b/data/locale/uk-UA.ini index 28eb0f2..8eabbce 100644 --- a/data/locale/uk-UA.ini +++ b/data/locale/uk-UA.ini @@ -26,4 +26,6 @@ Status.Live="Жити" Status.Reconnecting="Повторне підключення" Status.Inactive="Неактивний" Status.Active="Активний" -Reset="Скидання" \ No newline at end of file +Reset="Скидання" +EnableAll="Активувати всі" +DisableAll="Деактивувати всі" \ No newline at end of file diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index 6ee6fe8..77afca0 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -26,4 +26,6 @@ Status.Live="活的" Status.Reconnecting="重新连接" Status.Inactive="不活跃" Status.Active="积极的" -Reset="重置" \ No newline at end of file +Reset="重置" +EnableAll="全部启用" +DisableAll="全部停用" diff --git a/src/dock/output-status.cpp b/src/dock/output-status.cpp index 549e5b5..3643bcd 100644 --- a/src/dock/output-status.cpp +++ b/src/dock/output-status.cpp @@ -26,6 +26,8 @@ with this program. If not, see #include #include #include +#include +#include #include #include "output-status.hpp" @@ -53,13 +55,13 @@ BranchOutputStatus::BranchOutputStatus(QWidget *parent) : QFrame(parent), timer( outputTable->setColumnCount(7); int col = 0; - outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QTStr("SourceName"))); outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QTStr("FilterName"))); + outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QTStr("SourceName"))); outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QTStr("Status"))); outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QTStr("DropFrames"))); outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QTStr("SentDataSize"))); outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QTStr("BitRate"))); - outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QTStr(""))); + outputTable->setHorizontalHeaderItem(col++, new QTableWidgetItem(QString::fromUtf8(""))); QObject::connect(&timer, &QTimer::timeout, this, &BranchOutputStatus::Update); @@ -68,45 +70,79 @@ BranchOutputStatus::BranchOutputStatus(QWidget *parent) : QFrame(parent), timer( timer.start(); } + // Tool buttons + auto enableAllButton = new QPushButton(QTStr("EnableAll")); + connect(enableAllButton, &QPushButton::clicked, [this]() { SetEabnleAll(true); }); + + auto disableAllButton = new QPushButton(QTStr("DisableAll")); + connect(disableAllButton, &QPushButton::clicked, [this]() { SetEabnleAll(false); }); + + auto buttonsContainerLayout = new QHBoxLayout(); + buttonsContainerLayout->addWidget(enableAllButton); + buttonsContainerLayout->addWidget(disableAllButton); + buttonsContainerLayout->addStretch(); + QVBoxLayout *outputContainerLayout = new QVBoxLayout(); outputContainerLayout->addWidget(outputTable); - + outputContainerLayout->addLayout(buttonsContainerLayout); this->setLayout(outputContainerLayout); } BranchOutputStatus::~BranchOutputStatus() {} -void BranchOutputStatus::AddOutputLabels(QString parentName, filter_t *filter) +void BranchOutputStatus::AddOutputLabels(filter_t *filter) { + auto parent = obs_filter_get_parent(filter->source); OutputLabels ol; ol.filter = filter; - ol.parentName = new QTableWidgetItem(parentName); - ol.name = new QTableWidgetItem(QTStr(obs_source_get_name(filter->source))); - ol.status = new QLabel(QTStr("Status.Inactive")); - ol.droppedFrames = new QLabel(QTStr("")); - ol.megabytesSent = new QLabel(QTStr("")); - ol.bitrate = new QLabel(QTStr("")); + ol.filterItem = new FilterItem(QString::fromUtf8(obs_source_get_name(filter->source)), filter->source, this); + ol.parentName = new QTableWidgetItem(QString::fromUtf8(obs_source_get_name(parent))); + ol.status = new QLabel(QTStr("Status.Inactive"), this); + ol.droppedFrames = new QLabel(QString::fromUtf8(""), this); + ol.megabytesSent = new QLabel(QString::fromUtf8(""), this); + ol.bitrate = new QLabel(QString::fromUtf8(""), this); auto col = 0; auto row = (int)outputLabels.size(); outputTable->setRowCount(row + 1); + outputTable->setCellWidget(row, col++, ol.filterItem); outputTable->setItem(row, col++, ol.parentName); - outputTable->setItem(row, col++, ol.name); outputTable->setCellWidget(row, col++, ol.status); outputTable->setCellWidget(row, col++, ol.droppedFrames); outputTable->setCellWidget(row, col++, ol.megabytesSent); outputTable->setCellWidget(row, col++, ol.bitrate); + outputTable->setRowHeight(row, 32); + outputLabels.push_back(ol); // Setup reset button + auto resetButtonContainer = new QWidget(this); + auto resetButtonLayout = new QHBoxLayout(); + resetButtonLayout->setContentsMargins(0, 0, 0, 0); + resetButtonContainer->setLayout(resetButtonLayout); + auto resetButton = new QPushButton(QTStr("Reset"), this); connect(resetButton, &QPushButton::clicked, [this, row]() { outputLabels[row].Reset(); }); + resetButton->setProperty("toolButton", true); resetButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - resetButton->setMinimumHeight(27); - outputTable->setCellWidget(row, col, resetButton); + + resetButtonLayout->addWidget(resetButton); + outputTable->setCellWidget(row, col, resetButtonContainer); + + // Listen signal for filter update + signal_handler_connect( + obs_source_get_signal_handler(filter->source), "rename", BranchOutputStatus::OutputLabels::FilterRenamed, + outputLabels[row].filterItem + ); + + // Listen signal for parent update + signal_handler_connect( + obs_source_get_signal_handler(parent), "rename", BranchOutputStatus::OutputLabels::ParentRenamed, + outputLabels[row].parentName + ); } void BranchOutputStatus::RemoveOutputLabels(filter_t *filter) @@ -115,6 +151,17 @@ void BranchOutputStatus::RemoveOutputLabels(filter_t *filter) if (outputLabels[i].filter == filter) { outputLabels.removeAt(i); outputTable->removeRow(i); + + signal_handler_disconnect( + obs_source_get_signal_handler(filter->source), "rename", + BranchOutputStatus::OutputLabels::FilterRenamed, outputLabels[i].filterItem + ); + + signal_handler_disconnect( + obs_source_get_signal_handler(obs_filter_get_parent(filter->source)), "rename", + BranchOutputStatus::OutputLabels::ParentRenamed, outputLabels[i].parentName + ); + break; } } @@ -137,6 +184,13 @@ void BranchOutputStatus::hideEvent(QHideEvent *) timer.stop(); } +void BranchOutputStatus::SetEabnleAll(bool enabled) +{ + for (int i = 0; i < outputLabels.size(); i++) { + obs_source_set_enabled(outputLabels[i].filter->source, enabled); + } +} + // Imitate UI/window-basic-stats.cpp void setThemeID(QWidget *widget, const QString &themeID) { @@ -150,6 +204,8 @@ void setThemeID(QWidget *widget, const QString &themeID) } } +// BranchOutputStatus::OutputLabels structure + // Imitate UI/window-basic-stats.cpp void BranchOutputStatus::OutputLabels::Update(bool rec) { @@ -257,3 +313,60 @@ void BranchOutputStatus::OutputLabels::Reset() megabytesSent->setText(QString("0 MiB")); bitrate->setText(QString("0 kb/s")); } + +void BranchOutputStatus::OutputLabels::FilterRenamed(void *data, calldata_t *cd) +{ + auto item = (FilterItem *)data; + auto newName = calldata_string(cd, "new_name"); + item->SetText(QString::fromUtf8(newName)); +} + +void BranchOutputStatus::OutputLabels::ParentRenamed(void *data, calldata_t *cd) +{ + auto label = (QTableWidgetItem *)data; + auto newName = calldata_string(cd, "new_name"); + label->setText(QString::fromUtf8(newName)); +} + +// FilterItem class + +FilterItem::FilterItem(QString text, obs_source_t *_source, QWidget *parent) : QWidget(parent) +{ + setMinimumHeight(27); + source = _source; + + visibilityCheckbox = new QCheckBox(this); + visibilityCheckbox->setProperty("visibilityCheckBox", true); + visibilityCheckbox->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + visibilityCheckbox->setChecked(obs_source_enabled(source)); + + connect(visibilityCheckbox, &QCheckBox::clicked, [this](bool visible) { obs_source_set_enabled(source, visible); }); + + name = new QLabel(text, this); + + auto checkboxLayout = new QHBoxLayout(); + checkboxLayout->setContentsMargins(0, 0, 0, 0); + checkboxLayout->addWidget(visibilityCheckbox); + checkboxLayout->addWidget(name); + setLayout(checkboxLayout); + + // Listen signal for filter enabled/disabled + signal_handler_connect(obs_source_get_signal_handler(source), "enable", FilterItem::VisibilityChanged, this); +} + +FilterItem::~FilterItem() +{ + signal_handler_disconnect(obs_source_get_signal_handler(source), "enable", FilterItem::VisibilityChanged, this); +} + +void FilterItem::SetText(QString text) +{ + name->setText(text); +} + +void FilterItem::VisibilityChanged(void *data, calldata_t *cd) +{ + auto item = (FilterItem *)data; + auto enabled = calldata_bool(cd, "enabled"); + item->visibilityCheckbox->setChecked(enabled); +} diff --git a/src/dock/output-status.hpp b/src/dock/output-status.hpp index 30e6580..f94bdff 100644 --- a/src/dock/output-status.hpp +++ b/src/dock/output-status.hpp @@ -28,15 +28,33 @@ class QTableWidget; class QTableWidgetItem; class QLabel; class QString; +class QCheckBox; struct filter_t; +class FilterItem : public QWidget { + Q_OBJECT + + obs_source_t *source; + + QCheckBox *visibilityCheckbox; + QLabel *name; + +public: + FilterItem(QString text, obs_source_t *_source, QWidget *parent = (QWidget *)nullptr); + ~FilterItem(); + + void SetText(QString text); + + static void VisibilityChanged(void *data, calldata_t *cd); +}; + class BranchOutputStatus : public QFrame { Q_OBJECT struct OutputLabels { filter_t *filter; + FilterItem *filterItem; QTableWidgetItem *parentName; - QTableWidgetItem *name; QLabel *status; QLabel *droppedFrames; QLabel *megabytesSent; @@ -51,6 +69,9 @@ class BranchOutputStatus : public QFrame { void Update(bool rec); void Reset(); + static void FilterRenamed(void *data, calldata_t *cd); + static void ParentRenamed(void *data, calldata_t *cd); + long double kbps = 0.0l; }; @@ -64,8 +85,9 @@ class BranchOutputStatus : public QFrame { BranchOutputStatus(QWidget *parent = (QWidget *)nullptr); ~BranchOutputStatus(); - void AddOutputLabels(QString parentName, filter_t *filter); + void AddOutputLabels(filter_t *filter); void RemoveOutputLabels(filter_t *filter); + void SetEabnleAll(bool enabled); protected: virtual void showEvent(QShowEvent *event) override; diff --git a/src/plugin-main.cpp b/src/plugin-main.cpp index 51fb33f..459b41b 100644 --- a/src/plugin-main.cpp +++ b/src/plugin-main.cpp @@ -513,11 +513,11 @@ void video_tick(void *data, float) BranchOutputStatus *status_dock = nullptr; -void filter_add(void *data, obs_source_t *parent) +void filter_add(void *data, obs_source_t *) { // Register to output status dock auto filter = (filter_t *)data; - status_dock->AddOutputLabels(QTStr(obs_source_get_name(parent)), filter); + status_dock->AddOutputLabels(filter); } void filter_remove(void *data, obs_source_t *)