From a6bcce5a63692451944adc90779f75453005f6e2 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Thu, 25 Apr 2024 19:49:07 +0200 Subject: [PATCH] Add websocket connections tab --- data/locale/en-US.ini | 15 ++ plugins/base/CMakeLists.txt | 4 +- plugins/base/utils/websocket-tab.cpp | 243 +++++++++++++++++++++++++++ plugins/base/utils/websocket-tab.hpp | 20 +++ 4 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 plugins/base/utils/websocket-tab.cpp create mode 100644 plugins/base/utils/websocket-tab.hpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index b3a39b1f..28d0dca7 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -88,6 +88,21 @@ AdvSceneSwitcher.variableTab.lastChanged.text="%1 seconds ago" AdvSceneSwitcher.variableTab.lastChanged.text.none="No change since launch" AdvSceneSwitcher.variableTab.lastChanged.tooltip="Times changed: %1\n\nPrevious value: %2" +; Websocket Connections Tab +AdvSceneSwitcher.websocketConnectionTab.title="Websocket Connections" +AdvSceneSwitcher.websocketConnectionTab.help="Websocket connections can be used to communicate with other OBS instances or programs.\n\nClick on the highlighted plus symbol to add a new connection." +AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip="Add new webscoket connection" +AdvSceneSwitcher.websocketConnectionTab.websocketConnectionRemoveButton.tooltip="Remove selected websocket connections" +AdvSceneSwitcher.websocketConnectionTab.name.header="Name" +AdvSceneSwitcher.websocketConnectionTab.address.header="Address" +AdvSceneSwitcher.websocketConnectionTab.port.header="Port" +AdvSceneSwitcher.websocketConnectionTab.protocol.header="Is using OBS protocol?" +AdvSceneSwitcher.websocketConnectionTab.protocol.yes="Yes" +AdvSceneSwitcher.websocketConnectionTab.protocol.no="No" +AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?" +AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 connections?" + + ; Macro Tab AdvSceneSwitcher.macroTab.title="Macro" AdvSceneSwitcher.macroTab.macros="Macros" diff --git a/plugins/base/CMakeLists.txt b/plugins/base/CMakeLists.txt index b7de6ad4..124f169b 100644 --- a/plugins/base/CMakeLists.txt +++ b/plugins/base/CMakeLists.txt @@ -175,7 +175,9 @@ target_sources( utils/transition-selection.cpp utils/transition-selection.hpp utils/websocket-helpers.cpp - utils/websocket-helpers.hpp) + utils/websocket-helpers.hpp + utils/websocket-tab.cpp + utils/websocket-tab.hpp) if(OS_WINDOWS) target_sources(${PROJECT_NAME} PRIVATE utils/windows/windows.cpp) diff --git a/plugins/base/utils/websocket-tab.cpp b/plugins/base/utils/websocket-tab.cpp new file mode 100644 index 00000000..d5a52fda --- /dev/null +++ b/plugins/base/utils/websocket-tab.cpp @@ -0,0 +1,243 @@ +#include "websocket-tab.hpp" +#include "connection-manager.hpp" +#include "obs-module-helper.hpp" +#include "sync-helpers.hpp" +#include "tab-helpers.hpp" +#include "ui-helpers.hpp" + +#include + +namespace advss { + +static bool registerTab(); +static void setupTab(QTabWidget *); +static bool registerTabDone = registerTab(); + +static WSConnectionsTable *tabWidget = nullptr; + +static bool registerTab() +{ + AddSetupTabCallback("websocketConnectionTab", + WSConnectionsTable::Create, setupTab); + return true; +} + +static void setTabVisible(QTabWidget *tabWidget, bool visible) +{ + SetTabVisibleByName( + tabWidget, visible, + obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.title")); +} + +WSConnectionsTable *WSConnectionsTable::Create() +{ + tabWidget = new WSConnectionsTable(); + return tabWidget; +} + +void WSConnectionsTable::Add() +{ + auto newConnection = std::make_shared(); + auto accepted = WSConnectionSettingsDialog::AskForSettings( + this, *newConnection); + if (!accepted) { + return; + } + + { + auto lock = LockContext(); + auto &connections = GetConnections(); + connections.emplace_back(newConnection); + } + + ConnectionSelectionSignalManager::Instance()->Add( + QString::fromStdString(newConnection->Name())); +} + +void WSConnectionsTable::Remove() +{ + auto selectedRows = + tabWidget->Table()->selectionModel()->selectedRows(); + if (selectedRows.empty()) { + return; + } + + QStringList connectionNames; + for (auto row : selectedRows) { + auto cell = tabWidget->Table()->item(row.row(), 0); + if (!cell) { + continue; + } + + connectionNames << cell->text(); + } + + int connectionNameCount = connectionNames.size(); + if (connectionNameCount == 1) { + QString deleteWarning = obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text"); + if (!DisplayMessage(deleteWarning.arg(connectionNames.at(0)), + true)) { + return; + } + } else { + QString deleteWarning = obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text"); + if (!DisplayMessage(deleteWarning.arg(connectionNameCount), + true)) { + return; + } + } + + { + auto lock = LockContext(); + RemoveItemsByName(GetConnections(), connectionNames); + } + + for (const auto &name : connectionNames) { + ConnectionSelectionSignalManager::Instance()->Remove(name); + } +} + +static QString formatProtocolUsageText(WSConnection *connection) +{ + return obs_module_text( + connection->IsUsingOBSProtocol() + ? "AdvSceneSwitcher.websocketConnectionTab.protocol.yes" + : "AdvSceneSwitcher.websocketConnectionTab.protocol.no"); +} + +static QStringList getCellLabels(WSConnection *connection, bool addName = true) +{ + assert(connection); + + auto result = QStringList(); + if (addName) { + result << QString::fromStdString(connection->Name()); + } + result << QString::fromStdString(connection->GetURI()) + << QString::number(connection->GetPort()) + << formatProtocolUsageText(connection); + return result; +} + +static void openSettingsDialog() +{ + auto selectedRows = + tabWidget->Table()->selectionModel()->selectedRows(); + if (selectedRows.empty()) { + return; + } + + auto cell = tabWidget->Table()->item(selectedRows.last().row(), 0); + if (!cell) { + return; + } + + auto weakConnection = GetWeakConnectionByQString(cell->text()); + auto connection = weakConnection.lock(); + if (!connection) { + return; + } + + auto oldName = connection->Name(); + bool accepted = WSConnectionSettingsDialog::AskForSettings( + tabWidget->Table(), *connection.get()); + if (accepted && oldName != connection->Name()) { + ConnectionSelectionSignalManager::Instance()->Rename( + QString::fromStdString(oldName), + QString::fromStdString(connection->Name())); + } +} + +static const QStringList headers = + QStringList() + << obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.name.header") + << obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.address.header") + << obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.port.header") + << obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.protocol.header"); + +WSConnectionsTable::WSConnectionsTable(QTabWidget *parent) + : ResourceTable( + parent, + obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.help"), + obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip"), + obs_module_text( + "AdvSceneSwitcher.websocketConnectionTab.websocketConnectionRemoveButton.tooltip"), + headers, openSettingsDialog) +{ + for (const auto &connection : GetConnections()) { + auto c = std::static_pointer_cast(connection); + AddItemTableRow(Table(), getCellLabels(c.get())); + } + + SetHelpVisible(GetConnections().empty()); +} + +static void updateConnectionStatus(QTableWidget *table) +{ + for (int row = 0; row < table->rowCount(); row++) { + auto item = table->item(row, 0); + if (!item) { + continue; + } + + auto weakConnection = GetWeakConnectionByQString(item->text()); + auto connection = weakConnection.lock(); + if (!connection) { + continue; + } + + UpdateItemTableRow(table, row, + getCellLabels(connection.get(), false)); + } +} + +static void setupTab(QTabWidget *tab) +{ + if (GetConnections().empty()) { + setTabVisible(tab, false); + } + + QWidget::connect(ConnectionSelectionSignalManager::Instance(), + &ConnectionSelectionSignalManager::Rename, + [](const QString &oldName, const QString &newName) { + RenameItemTableRow(tabWidget->Table(), oldName, + newName); + }); + QWidget::connect( + ConnectionSelectionSignalManager::Instance(), + &ConnectionSelectionSignalManager::Add, + [tab](const QString &name) { + AddItemTableRow( + tabWidget->Table(), + getCellLabels(GetConnectionByName(name))); + tabWidget->SetHelpVisible(false); + tabWidget->HighlightAddButton(false); + setTabVisible(tab, true); + }); + QWidget::connect(ConnectionSelectionSignalManager::Instance(), + &ConnectionSelectionSignalManager::Remove, + [](const QString &name) { + RemoveItemTableRow(tabWidget->Table(), name); + if (tabWidget->Table()->rowCount() == 0) { + tabWidget->SetHelpVisible(true); + tabWidget->HighlightAddButton(true); + } + }); + + auto timer = new QTimer(tabWidget); + timer->setInterval(1000); + QWidget::connect(timer, &QTimer::timeout, + []() { updateConnectionStatus(tabWidget->Table()); }); + timer->start(); +} + +} // namespace advss diff --git a/plugins/base/utils/websocket-tab.hpp b/plugins/base/utils/websocket-tab.hpp new file mode 100644 index 00000000..85d58fe6 --- /dev/null +++ b/plugins/base/utils/websocket-tab.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "resource-table.hpp" + +namespace advss { + +class WSConnectionsTable final : public ResourceTable { + Q_OBJECT + +public: + static WSConnectionsTable *Create(); + +private slots: + void Add(); + void Remove(); + +private: + WSConnectionsTable(QTabWidget *parent = nullptr); +}; + +} // namespace advss