feat: show network detail view
Includes refactor to not using mainwindow.ui form. More flexible layout management with programmatic initialization.
This commit is contained in:
+5
-2
@@ -23,12 +23,13 @@ qt_add_executable(llink
|
|||||||
src/MainWindow.h
|
src/MainWindow.h
|
||||||
src/VerticalLabel.cpp
|
src/VerticalLabel.cpp
|
||||||
src/VerticalLabel.h
|
src/VerticalLabel.h
|
||||||
src/mainwindow.ui
|
|
||||||
src/authmanager.cpp
|
src/authmanager.cpp
|
||||||
src/networkmanager.cpp
|
src/networkmanager.cpp
|
||||||
src/authdialog.ui
|
src/authdialog.ui
|
||||||
src/authdialog.cpp
|
src/authdialog.cpp
|
||||||
src/settingsdialog.h src/settingsdialog.cpp src/settingsdialog.ui
|
src/settingsdialog.ui
|
||||||
|
src/settingsdialog.h
|
||||||
|
src/settingsdialog.cpp
|
||||||
src/models.h
|
src/models.h
|
||||||
src/store.h
|
src/store.h
|
||||||
src/store.cpp
|
src/store.cpp
|
||||||
@@ -42,6 +43,8 @@ qt_add_executable(llink
|
|||||||
src/createstreamdialog.cpp
|
src/createstreamdialog.cpp
|
||||||
src/memberpickerdialog.h
|
src/memberpickerdialog.h
|
||||||
src/memberpickerdialog.cpp
|
src/memberpickerdialog.cpp
|
||||||
|
src/networkdetailwidget.h
|
||||||
|
src/networkdetailwidget.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_resources(llink
|
qt_add_resources(llink
|
||||||
|
|||||||
+279
-59
@@ -7,37 +7,44 @@
|
|||||||
#include "authmanager.h"
|
#include "authmanager.h"
|
||||||
#include "createstreamdialog.h"
|
#include "createstreamdialog.h"
|
||||||
#include "memberpickerdialog.h"
|
#include "memberpickerdialog.h"
|
||||||
|
#include "networkdetailwidget.h"
|
||||||
#include "networkmanager.h"
|
#include "networkmanager.h"
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#include "textinputdialog.h"
|
#include "textinputdialog.h"
|
||||||
#include "ui_mainwindow.h"
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QItemSelectionModel>
|
#include <QItemSelectionModel>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QListWidget>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QStatusBar>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
|
#include <QTreeView>
|
||||||
#include <QSystemTrayIcon>
|
#include <QSystemTrayIcon>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
: QMainWindow(parent), m_trayIcon(nullptr), m_authManager(new AuthManager(this)), m_settingsDialog(nullptr),
|
: QMainWindow(parent), m_trayIcon(nullptr), m_authManager(new AuthManager(this)), m_settingsDialog(nullptr),
|
||||||
m_authDialog(nullptr), m_store(nullptr), m_networkStreamModel(nullptr), m_particleListModel(nullptr),
|
m_authDialog(nullptr), m_store(nullptr), m_networkStreamModel(nullptr), m_particleListModel(nullptr),
|
||||||
m_selectedParticleRow(-1), m_isDragging(false), m_textInputDialog(nullptr), m_createStreamDialog(nullptr)
|
m_selectedParticleRow(-1), m_isDragging(false), m_textInputDialog(nullptr), m_createStreamDialog(nullptr),
|
||||||
|
m_networkDetailWidget(nullptr)
|
||||||
{
|
{
|
||||||
m_authDialog = new AuthDialog(this);
|
m_authDialog = new AuthDialog(this);
|
||||||
m_textInputDialog = new TextInputDialog(this);
|
m_textInputDialog = new TextInputDialog(this);
|
||||||
connect(m_textInputDialog, &TextInputDialog::textSubmitted, this, &MainWindow::onTextMessageSubmitted);
|
connect(m_textInputDialog, &TextInputDialog::textSubmitted, this, &MainWindow::onTextMessageSubmitted);
|
||||||
|
|
||||||
ui = new Ui::MainWindow;
|
buildUi();
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
QWidget *topBar = ui->widget;
|
m_topBar->setCursor(Qt::OpenHandCursor);
|
||||||
topBar->setCursor(Qt::OpenHandCursor);
|
|
||||||
|
|
||||||
// Enable frameless window for edge-to-edge experience
|
// Enable frameless window for edge-to-edge experience
|
||||||
setWindowFlags(Qt::FramelessWindowHint | Qt::Window);
|
setWindowFlags(Qt::FramelessWindowHint | Qt::Window);
|
||||||
@@ -53,10 +60,10 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
QFont font = QFont();
|
QFont font = QFont();
|
||||||
font.setStyle(QFont::StyleItalic);
|
font.setStyle(QFont::StyleItalic);
|
||||||
m_currentPathLabel->setFont(font);
|
m_currentPathLabel->setFont(font);
|
||||||
ui->statusbar->addWidget(m_currentPathLabel);
|
m_statusBar->addWidget(m_currentPathLabel);
|
||||||
|
|
||||||
// Add spacer to push right-aligned items to the right
|
// Add spacer to push right-aligned items to the right
|
||||||
ui->statusbar->addPermanentWidget(new QLabel(""), 1);
|
m_statusBar->addPermanentWidget(new QLabel(""), 1);
|
||||||
|
|
||||||
// Ping indicator (green text)
|
// Ping indicator (green text)
|
||||||
m_pingLabel = new QLabel(this);
|
m_pingLabel = new QLabel(this);
|
||||||
@@ -64,7 +71,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
m_pingLabel->setMinimumWidth(100);
|
m_pingLabel->setMinimumWidth(100);
|
||||||
m_pingLabel->setAlignment(Qt::AlignCenter);
|
m_pingLabel->setAlignment(Qt::AlignCenter);
|
||||||
m_pingLabel->setStyleSheet(pingLabelStyle);
|
m_pingLabel->setStyleSheet(pingLabelStyle);
|
||||||
ui->statusbar->addPermanentWidget(m_pingLabel);
|
m_statusBar->addPermanentWidget(m_pingLabel);
|
||||||
|
|
||||||
// Position indicator (vim-style)
|
// Position indicator (vim-style)
|
||||||
m_positionLabel = new QLabel(this);
|
m_positionLabel = new QLabel(this);
|
||||||
@@ -72,7 +79,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
m_positionLabel->setMinimumWidth(50);
|
m_positionLabel->setMinimumWidth(50);
|
||||||
m_positionLabel->setAlignment(Qt::AlignCenter);
|
m_positionLabel->setAlignment(Qt::AlignCenter);
|
||||||
m_positionLabel->setStyleSheet(statusBarLabelStyle);
|
m_positionLabel->setStyleSheet(statusBarLabelStyle);
|
||||||
ui->statusbar->addPermanentWidget(m_positionLabel);
|
m_statusBar->addPermanentWidget(m_positionLabel);
|
||||||
|
|
||||||
// Storage indicator
|
// Storage indicator
|
||||||
m_storageLabel = new QLabel(this);
|
m_storageLabel = new QLabel(this);
|
||||||
@@ -80,7 +87,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
m_storageLabel->setMinimumWidth(60);
|
m_storageLabel->setMinimumWidth(60);
|
||||||
m_storageLabel->setAlignment(Qt::AlignCenter);
|
m_storageLabel->setAlignment(Qt::AlignCenter);
|
||||||
m_storageLabel->setStyleSheet(statusBarLabelStyle);
|
m_storageLabel->setStyleSheet(statusBarLabelStyle);
|
||||||
ui->statusbar->addPermanentWidget(m_storageLabel);
|
m_statusBar->addPermanentWidget(m_storageLabel);
|
||||||
|
|
||||||
// Setup ping timer for periodic server connectivity check
|
// Setup ping timer for periodic server connectivity check
|
||||||
m_pingTimer = new QTimer(this);
|
m_pingTimer = new QTimer(this);
|
||||||
@@ -98,9 +105,10 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
m_pingTimer->start(10000);
|
m_pingTimer->start(10000);
|
||||||
NetworkManager::instance().ping();
|
NetworkManager::instance().ping();
|
||||||
|
|
||||||
ui->splitter->setStretchFactor(0, 1);
|
m_splitter->setStretchFactor(0, 1);
|
||||||
ui->splitter->setStretchFactor(1, 4);
|
m_splitter->setStretchFactor(1, 4);
|
||||||
ui->splitter->setStretchFactor(2, 1);
|
m_splitter->setStretchFactor(2, 1);
|
||||||
|
m_splitter->setStretchFactor(3, 5);
|
||||||
|
|
||||||
setupModels();
|
setupModels();
|
||||||
|
|
||||||
@@ -116,10 +124,11 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
connect(m_authManager, &AuthManager::signedOut, m_authDialog, &AuthDialog::show);
|
connect(m_authManager, &AuthManager::signedOut, m_authDialog, &AuthDialog::show);
|
||||||
connect(m_authManager, &AuthManager::signedIn, this, [this]() {
|
connect(m_authManager, &AuthManager::signedIn, this, [this]() {
|
||||||
m_store->setCurrentUserEmail(m_authManager->sessionData()->email);
|
m_store->setCurrentUserEmail(m_authManager->sessionData()->email);
|
||||||
|
m_networkDetailWidget->setCurrentUserEmail(m_authManager->sessionData()->email);
|
||||||
m_store->loadStartupData();
|
m_store->loadStartupData();
|
||||||
});
|
});
|
||||||
connect(m_store, &Store::startupDataLoaded, this, [this]() {
|
connect(m_store, &Store::startupDataLoaded, this, [this]() {
|
||||||
ui->topLevelParticlesView->expandAll();
|
m_treeView->expandAll();
|
||||||
});
|
});
|
||||||
connect(m_store, &Store::particleUpdated, this, [this](const QString &, const QString &particleId) {
|
connect(m_store, &Store::particleUpdated, this, [this](const QString &, const QString &particleId) {
|
||||||
if (particleId == m_selectedParticleId)
|
if (particleId == m_selectedParticleId)
|
||||||
@@ -130,21 +139,25 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
QStringList acked;
|
QStringList acked;
|
||||||
for (const auto &email : particle->ackedByEmails)
|
for (const auto &email : particle->ackedByEmails)
|
||||||
acked.append(email);
|
acked.append(email);
|
||||||
ui->label_3->setText("Acked by: " + acked.join(", "));
|
m_ackLabel->setText("Acked by: " + acked.join(", "));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ui->label_3->setText("");
|
m_ackLabel->setText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// DocumentSend toolbar button opens text input dialog
|
// DocumentSend toolbar button opens text input dialog
|
||||||
connect(ui->toolButton_3, &QToolButton::clicked, this, &MainWindow::showTextInputDialog);
|
connect(m_sendTextButton, &QToolButton::clicked, this, &MainWindow::showTextInputDialog);
|
||||||
|
|
||||||
|
// Explicit connects for buttons (no auto-connect without .ui)
|
||||||
|
connect(m_settingsButton, &QToolButton::clicked, this, &MainWindow::on_settingsButton_clicked);
|
||||||
|
connect(m_exitButton, &QToolButton::clicked, this, &MainWindow::on_exitButton_clicked);
|
||||||
|
|
||||||
// "New stream" button
|
// "New stream" button
|
||||||
m_createStreamDialog = new CreateStreamDialog(this);
|
m_createStreamDialog = new CreateStreamDialog(this);
|
||||||
connect(ui->pushButton, &QPushButton::clicked, this, [this]() {
|
connect(m_newStreamButton, &QPushButton::clicked, this, [this]() {
|
||||||
if (m_selectedNetworkName.isEmpty())
|
if (m_selectedNetworkName.isEmpty())
|
||||||
return;
|
return;
|
||||||
const Network *network = m_store->networkById(m_selectedNetworkName);
|
const Network *network = m_store->networkById(m_selectedNetworkName);
|
||||||
@@ -160,7 +173,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
});
|
});
|
||||||
|
|
||||||
// "Add members" toolbar button (ListAdd icon in right sidebar)
|
// "Add members" toolbar button (ListAdd icon in right sidebar)
|
||||||
connect(ui->toolButton_7, &QToolButton::clicked, this, [this]() {
|
connect(m_addMembersButton, &QToolButton::clicked, this, [this]() {
|
||||||
if (m_selectedStreamId.isEmpty() || m_selectedNetworkName.isEmpty())
|
if (m_selectedStreamId.isEmpty() || m_selectedNetworkName.isEmpty())
|
||||||
return;
|
return;
|
||||||
const Network *network = m_store->networkById(m_selectedNetworkName);
|
const Network *network = m_store->networkById(m_selectedNetworkName);
|
||||||
@@ -180,12 +193,12 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
connect(op, &Operation::success, this, [this](const QJsonDocument &) {
|
connect(op, &Operation::success, this, [this](const QJsonDocument &) {
|
||||||
auto *fetchOp = m_store->fetchStream(m_selectedStreamId);
|
auto *fetchOp = m_store->fetchStream(m_selectedStreamId);
|
||||||
connect(fetchOp, &Operation::success, this, [this](const QJsonDocument &) {
|
connect(fetchOp, &Operation::success, this, [this](const QJsonDocument &) {
|
||||||
ui->membersList->clear();
|
m_membersList->clear();
|
||||||
const Stream *s = m_store->streamById(m_selectedStreamId);
|
const Stream *s = m_store->streamById(m_selectedStreamId);
|
||||||
if (s)
|
if (s)
|
||||||
{
|
{
|
||||||
for (const QString &email : s->memberEmails)
|
for (const QString &email : s->memberEmails)
|
||||||
ui->membersList->addItem(email.split("@")[0]);
|
m_membersList->addItem(email.split("@")[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -198,6 +211,196 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
setupTrayIcon();
|
setupTrayIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::buildUi()
|
||||||
|
{
|
||||||
|
resize(650, 500);
|
||||||
|
setWindowTitle("Flowy.llink");
|
||||||
|
|
||||||
|
auto *centralWidget = new QWidget(this);
|
||||||
|
setCentralWidget(centralWidget);
|
||||||
|
auto *mainLayout = new QVBoxLayout(centralWidget);
|
||||||
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
m_splitter = new QSplitter(Qt::Horizontal);
|
||||||
|
mainLayout->addWidget(m_splitter);
|
||||||
|
|
||||||
|
// ── Left sidebar ──
|
||||||
|
m_leftSidebar = new QWidget();
|
||||||
|
m_leftSidebar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
auto *leftLayout = new QVBoxLayout(m_leftSidebar);
|
||||||
|
leftLayout->setSpacing(5);
|
||||||
|
leftLayout->setContentsMargins(2, 2, 2, 2);
|
||||||
|
|
||||||
|
// Top bar (drag area)
|
||||||
|
m_topBar = new QWidget();
|
||||||
|
m_topBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||||
|
m_topBar->setMinimumHeight(20);
|
||||||
|
auto *topBarLayout = new QHBoxLayout(m_topBar);
|
||||||
|
topBarLayout->setSpacing(0);
|
||||||
|
topBarLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
m_settingsButton = new QToolButton();
|
||||||
|
m_settingsButton->setText("...");
|
||||||
|
m_settingsButton->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::HelpAbout));
|
||||||
|
topBarLayout->addWidget(m_settingsButton);
|
||||||
|
|
||||||
|
topBarLayout->addStretch();
|
||||||
|
|
||||||
|
m_exitButton = new QToolButton();
|
||||||
|
m_exitButton->setText("...");
|
||||||
|
m_exitButton->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit));
|
||||||
|
topBarLayout->addWidget(m_exitButton);
|
||||||
|
|
||||||
|
leftLayout->addWidget(m_topBar);
|
||||||
|
|
||||||
|
// Tree view
|
||||||
|
m_treeView = new QTreeView();
|
||||||
|
m_treeView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
leftLayout->addWidget(m_treeView);
|
||||||
|
|
||||||
|
// New stream button
|
||||||
|
m_newStreamButton = new QPushButton("New stream");
|
||||||
|
m_newStreamButton->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ListAdd));
|
||||||
|
leftLayout->addWidget(m_newStreamButton);
|
||||||
|
|
||||||
|
m_splitter->addWidget(m_leftSidebar);
|
||||||
|
|
||||||
|
// ── Content view ──
|
||||||
|
m_contentView = new QWidget();
|
||||||
|
m_contentView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
m_contentView->setMinimumWidth(300);
|
||||||
|
auto *contentLayout = new QVBoxLayout(m_contentView);
|
||||||
|
contentLayout->setSpacing(2);
|
||||||
|
contentLayout->setContentsMargins(0, 0, 0, 2);
|
||||||
|
|
||||||
|
// Content container with spacers and labels
|
||||||
|
auto *contentContainer = new QWidget();
|
||||||
|
contentContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
auto *contentContainerLayout = new QVBoxLayout(contentContainer);
|
||||||
|
contentContainerLayout->setSpacing(2);
|
||||||
|
contentContainerLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
contentContainerLayout->addStretch();
|
||||||
|
|
||||||
|
m_particleContentLabel = new QLabel();
|
||||||
|
QFont contentFont;
|
||||||
|
contentFont.setPointSize(36);
|
||||||
|
m_particleContentLabel->setFont(contentFont);
|
||||||
|
m_particleContentLabel->setMaximumSize(500, 300);
|
||||||
|
m_particleContentLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
m_particleContentLabel->setWordWrap(true);
|
||||||
|
m_particleContentLabel->setScaledContents(true);
|
||||||
|
contentContainerLayout->addWidget(m_particleContentLabel);
|
||||||
|
|
||||||
|
contentContainerLayout->addStretch();
|
||||||
|
|
||||||
|
m_creatorLabel = new QLabel();
|
||||||
|
QFont creatorFont;
|
||||||
|
creatorFont.setPointSize(15);
|
||||||
|
m_creatorLabel->setFont(creatorFont);
|
||||||
|
m_creatorLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
contentContainerLayout->addWidget(m_creatorLabel);
|
||||||
|
|
||||||
|
m_ackLabel = new QLabel();
|
||||||
|
m_ackLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
contentContainerLayout->addWidget(m_ackLabel);
|
||||||
|
|
||||||
|
contentLayout->addWidget(contentContainer);
|
||||||
|
|
||||||
|
// Content toolbar (empty, matches widget_4 from .ui)
|
||||||
|
auto *contentToolbar = new QWidget();
|
||||||
|
auto *contentToolbarLayout = new QHBoxLayout(contentToolbar);
|
||||||
|
contentToolbarLayout->setSpacing(2);
|
||||||
|
contentToolbarLayout->setContentsMargins(8, 0, 0, 0);
|
||||||
|
contentLayout->addWidget(contentToolbar);
|
||||||
|
|
||||||
|
m_splitter->addWidget(m_contentView);
|
||||||
|
|
||||||
|
// ── Right sidebar ──
|
||||||
|
m_rightSidebar = new QWidget();
|
||||||
|
m_rightSidebar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
auto *rightLayout = new QVBoxLayout(m_rightSidebar);
|
||||||
|
rightLayout->setSpacing(2);
|
||||||
|
rightLayout->setContentsMargins(2, 2, 2, 2);
|
||||||
|
|
||||||
|
// Particle list view
|
||||||
|
m_particlesView = new QListView();
|
||||||
|
m_particlesView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
auto sp1 = m_particlesView->sizePolicy();
|
||||||
|
sp1.setVerticalStretch(2);
|
||||||
|
m_particlesView->setSizePolicy(sp1);
|
||||||
|
rightLayout->addWidget(m_particlesView);
|
||||||
|
|
||||||
|
// Members list
|
||||||
|
m_membersList = new QListWidget();
|
||||||
|
m_membersList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
auto sp2 = m_membersList->sizePolicy();
|
||||||
|
sp2.setVerticalStretch(1);
|
||||||
|
m_membersList->setSizePolicy(sp2);
|
||||||
|
rightLayout->addWidget(m_membersList);
|
||||||
|
|
||||||
|
// "Hold space to speak" label
|
||||||
|
auto *holdSpaceLabel = new QLabel("Hold space to speak");
|
||||||
|
holdSpaceLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
QPalette holdPalette = holdSpaceLabel->palette();
|
||||||
|
QColor holdColor(153, 156, 145, 217);
|
||||||
|
holdPalette.setColor(QPalette::WindowText, holdColor);
|
||||||
|
holdSpaceLabel->setPalette(holdPalette);
|
||||||
|
rightLayout->addWidget(holdSpaceLabel);
|
||||||
|
|
||||||
|
// Empty toolbar row (widget_5)
|
||||||
|
auto *emptyToolbar = new QWidget();
|
||||||
|
auto *emptyToolbarLayout = new QHBoxLayout(emptyToolbar);
|
||||||
|
emptyToolbarLayout->setSpacing(2);
|
||||||
|
emptyToolbarLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
rightLayout->addWidget(emptyToolbar);
|
||||||
|
|
||||||
|
// RTL toolbar row (widget_3)
|
||||||
|
auto *rtlToolbar = new QWidget();
|
||||||
|
rtlToolbar->setLayoutDirection(Qt::RightToLeft);
|
||||||
|
auto *rtlToolbarLayout = new QHBoxLayout(rtlToolbar);
|
||||||
|
rtlToolbarLayout->setSpacing(2);
|
||||||
|
rtlToolbarLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
auto *editClearButton = new QToolButton();
|
||||||
|
editClearButton->setText("...");
|
||||||
|
editClearButton->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditClear));
|
||||||
|
rtlToolbarLayout->addWidget(editClearButton);
|
||||||
|
|
||||||
|
m_addMembersButton = new QToolButton();
|
||||||
|
m_addMembersButton->setText("...");
|
||||||
|
m_addMembersButton->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ListAdd));
|
||||||
|
rtlToolbarLayout->addWidget(m_addMembersButton);
|
||||||
|
|
||||||
|
auto *insertImageButton = new QToolButton();
|
||||||
|
insertImageButton->setText("...");
|
||||||
|
insertImageButton->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::InsertImage));
|
||||||
|
rtlToolbarLayout->addWidget(insertImageButton);
|
||||||
|
|
||||||
|
m_sendTextButton = new QToolButton();
|
||||||
|
m_sendTextButton->setText("...");
|
||||||
|
m_sendTextButton->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSend));
|
||||||
|
rtlToolbarLayout->addWidget(m_sendTextButton);
|
||||||
|
|
||||||
|
auto *cameraVideoButton = new QToolButton();
|
||||||
|
cameraVideoButton->setText("...");
|
||||||
|
cameraVideoButton->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::CameraVideo));
|
||||||
|
rtlToolbarLayout->addWidget(cameraVideoButton);
|
||||||
|
|
||||||
|
rightLayout->addWidget(rtlToolbar);
|
||||||
|
|
||||||
|
m_splitter->addWidget(m_rightSidebar);
|
||||||
|
|
||||||
|
// ── Network detail widget (hidden by default) ──
|
||||||
|
// Store is created in setupModels, so we pass nullptr now and set store later
|
||||||
|
// Actually, store is created in setupModels which is called after buildUi.
|
||||||
|
// We need to create it after store exists. We'll create it in the constructor after setupModels.
|
||||||
|
// For now just reserve the splitter slot — we'll add the widget after setupModels().
|
||||||
|
|
||||||
|
m_statusBar = new QStatusBar();
|
||||||
|
setStatusBar(m_statusBar);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::setupTrayIcon()
|
void MainWindow::setupTrayIcon()
|
||||||
{
|
{
|
||||||
m_trayIcon = new QSystemTrayIcon(this);
|
m_trayIcon = new QSystemTrayIcon(this);
|
||||||
@@ -221,7 +424,6 @@ void MainWindow::setupTrayIcon()
|
|||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
delete ui;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_exitButton_clicked()
|
void MainWindow::on_exitButton_clicked()
|
||||||
@@ -233,7 +435,7 @@ void MainWindow::on_settingsButton_clicked()
|
|||||||
{
|
{
|
||||||
if (!m_settingsDialog)
|
if (!m_settingsDialog)
|
||||||
{
|
{
|
||||||
m_settingsDialog = new SettingsDialog(m_authManager, this);
|
m_settingsDialog = new SettingsDialog(m_authManager, m_store, this);
|
||||||
}
|
}
|
||||||
m_settingsDialog->show();
|
m_settingsDialog->show();
|
||||||
}
|
}
|
||||||
@@ -243,38 +445,43 @@ void MainWindow::setupModels()
|
|||||||
// Create store (data loaded after sign-in via loadStartupData)
|
// Create store (data loaded after sign-in via loadStartupData)
|
||||||
m_store = new Store(this);
|
m_store = new Store(this);
|
||||||
|
|
||||||
|
// Now create the network detail widget (needs store)
|
||||||
|
m_networkDetailWidget = new NetworkDetailWidget(m_store);
|
||||||
|
m_splitter->addWidget(m_networkDetailWidget);
|
||||||
|
m_networkDetailWidget->hide();
|
||||||
|
|
||||||
// Create and set tree model
|
// Create and set tree model
|
||||||
m_networkStreamModel = new NetworkStreamModel(m_store, this);
|
m_networkStreamModel = new NetworkStreamModel(m_store, this);
|
||||||
ui->topLevelParticlesView->setModel(m_networkStreamModel);
|
m_treeView->setModel(m_networkStreamModel);
|
||||||
ui->topLevelParticlesView->setIndentation(5);
|
m_treeView->setIndentation(5);
|
||||||
ui->topLevelParticlesView->setHeaderHidden(true);
|
m_treeView->setHeaderHidden(true);
|
||||||
ui->topLevelParticlesView->expandAll();
|
m_treeView->expandAll();
|
||||||
|
|
||||||
// Configure column behavior: name stretches, unseen count is fixed width
|
// Configure column behavior: name stretches, unseen count is fixed width
|
||||||
ui->topLevelParticlesView->header()->setStretchLastSection(false);
|
m_treeView->header()->setStretchLastSection(false);
|
||||||
ui->topLevelParticlesView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
m_treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
ui->topLevelParticlesView->header()->setSectionResizeMode(1, QHeaderView::Fixed);
|
m_treeView->header()->setSectionResizeMode(1, QHeaderView::Fixed);
|
||||||
ui->topLevelParticlesView->setColumnWidth(1, 40);
|
m_treeView->setColumnWidth(1, 40);
|
||||||
|
|
||||||
// Create and set list model
|
// Create and set list model
|
||||||
m_particleListModel = new ParticleListModel(m_store, this);
|
m_particleListModel = new ParticleListModel(m_store, this);
|
||||||
ui->particlesView->setModel(m_particleListModel);
|
m_particlesView->setModel(m_particleListModel);
|
||||||
|
|
||||||
// Configure particle list view to use ellipses for long text
|
// Configure particle list view to use ellipses for long text
|
||||||
ui->particlesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
m_particlesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
ui->particlesView->setTextElideMode(Qt::ElideRight);
|
m_particlesView->setTextElideMode(Qt::ElideRight);
|
||||||
|
|
||||||
// Connect selection changes
|
// Connect selection changes
|
||||||
connect(ui->topLevelParticlesView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||||
&MainWindow::onTreeSelectionChanged);
|
&MainWindow::onTreeSelectionChanged);
|
||||||
|
|
||||||
// Connect particle selection changes
|
// Connect particle selection changes
|
||||||
connect(ui->particlesView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
connect(m_particlesView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||||
&MainWindow::onParticleSelectionChanged);
|
&MainWindow::onParticleSelectionChanged);
|
||||||
|
|
||||||
// Install event filter to capture key presses from child views
|
// Install event filter to capture key presses from child views
|
||||||
ui->topLevelParticlesView->installEventFilter(this);
|
m_treeView->installEventFilter(this);
|
||||||
ui->particlesView->installEventFilter(this);
|
m_particlesView->installEventFilter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onTreeSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
void MainWindow::onTreeSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||||
@@ -284,7 +491,10 @@ void MainWindow::onTreeSelectionChanged(const QItemSelection &selected, const QI
|
|||||||
if (selected.indexes().isEmpty())
|
if (selected.indexes().isEmpty())
|
||||||
{
|
{
|
||||||
m_particleListModel->setStreamId(QString());
|
m_particleListModel->setStreamId(QString());
|
||||||
ui->membersList->clear();
|
m_membersList->clear();
|
||||||
|
m_networkDetailWidget->hide();
|
||||||
|
m_contentView->show();
|
||||||
|
m_rightSidebar->show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +502,11 @@ void MainWindow::onTreeSelectionChanged(const QItemSelection &selected, const QI
|
|||||||
|
|
||||||
if (m_networkStreamModel->isStreamItem(index))
|
if (m_networkStreamModel->isStreamItem(index))
|
||||||
{
|
{
|
||||||
|
// Show normal 3-pane view
|
||||||
|
m_networkDetailWidget->hide();
|
||||||
|
m_contentView->show();
|
||||||
|
m_rightSidebar->show();
|
||||||
|
|
||||||
QString streamId = m_networkStreamModel->streamIdFromIndex(index);
|
QString streamId = m_networkStreamModel->streamIdFromIndex(index);
|
||||||
m_particleListModel->setStreamId(streamId);
|
m_particleListModel->setStreamId(streamId);
|
||||||
m_selectedStreamId = streamId;
|
m_selectedStreamId = streamId;
|
||||||
@@ -302,13 +517,13 @@ void MainWindow::onTreeSelectionChanged(const QItemSelection &selected, const QI
|
|||||||
m_selectedNetworkName = networkId;
|
m_selectedNetworkName = networkId;
|
||||||
|
|
||||||
// Show stream members
|
// Show stream members
|
||||||
ui->membersList->clear();
|
m_membersList->clear();
|
||||||
const Stream *stream = m_store->streamById(streamId);
|
const Stream *stream = m_store->streamById(streamId);
|
||||||
if (stream)
|
if (stream)
|
||||||
{
|
{
|
||||||
for (const QString &email : stream->memberEmails)
|
for (const QString &email : stream->memberEmails)
|
||||||
{
|
{
|
||||||
ui->membersList->addItem(email.split("@")[0]);
|
m_membersList->addItem(email.split("@")[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,13 +531,13 @@ void MainWindow::onTreeSelectionChanged(const QItemSelection &selected, const QI
|
|||||||
int unseenRow = m_particleListModel->firstUnseenRow();
|
int unseenRow = m_particleListModel->firstUnseenRow();
|
||||||
if (unseenRow >= 0)
|
if (unseenRow >= 0)
|
||||||
{
|
{
|
||||||
ui->particlesView->setCurrentIndex(m_particleListModel->index(unseenRow, 0));
|
m_particlesView->setCurrentIndex(m_particleListModel->index(unseenRow, 0));
|
||||||
ui->particlesView->setFocus();
|
m_particlesView->setFocus();
|
||||||
}
|
}
|
||||||
else if (m_particleListModel->rowCount() > 0)
|
else if (m_particleListModel->rowCount() > 0)
|
||||||
{
|
{
|
||||||
ui->particlesView->setCurrentIndex(m_particleListModel->index(m_particleListModel->rowCount() - 1, 0));
|
m_particlesView->setCurrentIndex(m_particleListModel->index(m_particleListModel->rowCount() - 1, 0));
|
||||||
ui->particlesView->setFocus();
|
m_particlesView->setFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // network selected
|
else // network selected
|
||||||
@@ -330,10 +545,16 @@ void MainWindow::onTreeSelectionChanged(const QItemSelection &selected, const QI
|
|||||||
m_particleListModel->setStreamId(QString());
|
m_particleListModel->setStreamId(QString());
|
||||||
m_selectedStreamId.clear();
|
m_selectedStreamId.clear();
|
||||||
m_selectedParticleRow = -1;
|
m_selectedParticleRow = -1;
|
||||||
ui->membersList->clear();
|
m_membersList->clear();
|
||||||
|
|
||||||
QString networkId = m_networkStreamModel->networkIdFromIndex(index);
|
QString networkId = m_networkStreamModel->networkIdFromIndex(index);
|
||||||
m_selectedNetworkName = networkId;
|
m_selectedNetworkName = networkId;
|
||||||
|
|
||||||
|
// Show network detail widget
|
||||||
|
m_networkDetailWidget->setNetwork(networkId);
|
||||||
|
m_contentView->hide();
|
||||||
|
m_rightSidebar->hide();
|
||||||
|
m_networkDetailWidget->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatusBar();
|
updateStatusBar();
|
||||||
@@ -345,9 +566,9 @@ void MainWindow::onParticleSelectionChanged(const QItemSelection &selected, cons
|
|||||||
|
|
||||||
if (selected.indexes().isEmpty())
|
if (selected.indexes().isEmpty())
|
||||||
{
|
{
|
||||||
ui->label->setText("");
|
m_particleContentLabel->setText("");
|
||||||
ui->label_2->setText("");
|
m_creatorLabel->setText("");
|
||||||
ui->label_3->setText("");
|
m_ackLabel->setText("");
|
||||||
m_selectedParticleId.clear();
|
m_selectedParticleId.clear();
|
||||||
m_selectedParticleRow = -1;
|
m_selectedParticleRow = -1;
|
||||||
updateStatusBar();
|
updateStatusBar();
|
||||||
@@ -359,16 +580,16 @@ void MainWindow::onParticleSelectionChanged(const QItemSelection &selected, cons
|
|||||||
const Particle *particle = m_particleListModel->particleAtRow(index.row());
|
const Particle *particle = m_particleListModel->particleAtRow(index.row());
|
||||||
if (particle)
|
if (particle)
|
||||||
{
|
{
|
||||||
ui->label_2->setText(particle->createdByEmail);
|
m_creatorLabel->setText(particle->createdByEmail);
|
||||||
m_selectedParticleId = particle->id;
|
m_selectedParticleId = particle->id;
|
||||||
|
|
||||||
if (particle->type == "text")
|
if (particle->type == "text")
|
||||||
{
|
{
|
||||||
ui->label->setText(particle->data.value("content").toString());
|
m_particleContentLabel->setText(particle->data.value("content").toString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ui->label->setText("");
|
m_particleContentLabel->setText("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show ack info
|
// Show ack info
|
||||||
@@ -377,11 +598,11 @@ void MainWindow::onParticleSelectionChanged(const QItemSelection &selected, cons
|
|||||||
QStringList acked;
|
QStringList acked;
|
||||||
for (const auto &email : particle->ackedByEmails)
|
for (const auto &email : particle->ackedByEmails)
|
||||||
acked.append(email);
|
acked.append(email);
|
||||||
ui->label_3->setText("Acked by: " + acked.join(", "));
|
m_ackLabel->setText("Acked by: " + acked.join(", "));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ui->label_3->setText("");
|
m_ackLabel->setText("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark seen on selection
|
// Mark seen on selection
|
||||||
@@ -532,8 +753,8 @@ void MainWindow::onTextMessageSubmitted(const QString &text)
|
|||||||
int lastRow = m_particleListModel->rowCount() - 1;
|
int lastRow = m_particleListModel->rowCount() - 1;
|
||||||
if (lastRow >= 0)
|
if (lastRow >= 0)
|
||||||
{
|
{
|
||||||
ui->particlesView->setCurrentIndex(m_particleListModel->index(lastRow, 0));
|
m_particlesView->setCurrentIndex(m_particleListModel->index(lastRow, 0));
|
||||||
ui->particlesView->setFocus();
|
m_particlesView->setFocus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -543,8 +764,7 @@ void MainWindow::mousePressEvent(QMouseEvent *event)
|
|||||||
if (event->button() == Qt::LeftButton)
|
if (event->button() == Qt::LeftButton)
|
||||||
{
|
{
|
||||||
// Check if the click is within the top bar widget area
|
// Check if the click is within the top bar widget area
|
||||||
QWidget *topBar = ui->widget; // The top bar widget from the UI
|
if (m_topBar && m_topBar->geometry().contains(event->pos()))
|
||||||
if (topBar && topBar->geometry().contains(event->pos()))
|
|
||||||
{
|
{
|
||||||
m_isDragging = true;
|
m_isDragging = true;
|
||||||
setCursor(Qt::ClosedHandCursor);
|
setCursor(Qt::ClosedHandCursor);
|
||||||
|
|||||||
+31
-6
@@ -15,19 +15,22 @@ class QSystemTrayIcon;
|
|||||||
class AuthManager;
|
class AuthManager;
|
||||||
class QItemSelection;
|
class QItemSelection;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
|
class QSplitter;
|
||||||
|
class QTreeView;
|
||||||
|
class QListView;
|
||||||
|
class QListWidget;
|
||||||
|
class QToolButton;
|
||||||
|
class QPushButton;
|
||||||
|
class QStatusBar;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class AuthDialog;
|
class AuthDialog;
|
||||||
class CreateStreamDialog;
|
class CreateStreamDialog;
|
||||||
class MemberPickerDialog;
|
class MemberPickerDialog;
|
||||||
|
class NetworkDetailWidget;
|
||||||
class SettingsDialog;
|
class SettingsDialog;
|
||||||
class TextInputDialog;
|
class TextInputDialog;
|
||||||
|
|
||||||
namespace Ui
|
|
||||||
{
|
|
||||||
class MainWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -53,12 +56,34 @@ protected:
|
|||||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void buildUi();
|
||||||
void setupTrayIcon();
|
void setupTrayIcon();
|
||||||
void setupModels();
|
void setupModels();
|
||||||
|
|
||||||
QSystemTrayIcon *m_trayIcon;
|
QSystemTrayIcon *m_trayIcon;
|
||||||
Ui::MainWindow *ui;
|
|
||||||
|
|
||||||
|
// ── UI widgets ──
|
||||||
|
QWidget *m_topBar;
|
||||||
|
QSplitter *m_splitter;
|
||||||
|
QToolButton *m_settingsButton;
|
||||||
|
QToolButton *m_exitButton;
|
||||||
|
QTreeView *m_treeView;
|
||||||
|
QPushButton *m_newStreamButton;
|
||||||
|
QLabel *m_particleContentLabel;
|
||||||
|
QLabel *m_creatorLabel;
|
||||||
|
QLabel *m_ackLabel;
|
||||||
|
QListView *m_particlesView;
|
||||||
|
QListWidget *m_membersList;
|
||||||
|
QToolButton *m_sendTextButton;
|
||||||
|
QToolButton *m_addMembersButton;
|
||||||
|
QStatusBar *m_statusBar;
|
||||||
|
|
||||||
|
QWidget *m_leftSidebar;
|
||||||
|
QWidget *m_contentView;
|
||||||
|
QWidget *m_rightSidebar;
|
||||||
|
NetworkDetailWidget *m_networkDetailWidget;
|
||||||
|
|
||||||
|
// ── Auth & dialogs ──
|
||||||
AuthManager *m_authManager;
|
AuthManager *m_authManager;
|
||||||
AuthDialog *m_authDialog;
|
AuthDialog *m_authDialog;
|
||||||
|
|
||||||
|
|||||||
@@ -1,520 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>MainWindow</class>
|
|
||||||
<widget class="QMainWindow" name="MainWindow">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>650</width>
|
|
||||||
<height>500</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Flowy.llink</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="centralwidget">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QSplitter" name="splitter">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="leftSidebar" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>5</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="widget" native="true">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="settingsButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="QIcon::ThemeIcon::HelpAbout"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="exitButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="QIcon::ThemeIcon::ApplicationExit"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QTreeView" name="topLevelParticlesView">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="pushButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>New stream</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="QIcon::ThemeIcon::ListAdd"/>
|
|
||||||
</property>
|
|
||||||
<property name="default">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="flat">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="contentView" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>300</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="contentContainer" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>500</width>
|
|
||||||
<height>300</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>36</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="lineWidth">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string></string>
|
|
||||||
</property>
|
|
||||||
<property name="scaledContents">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer_3">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>15</pointsize>
|
|
||||||
<bold>false</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string></string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string></string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="widget_4" native="true">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>8</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="rightSidebar" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QListView" name="particlesView">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>2</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QListWidget" name="membersList">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="palette">
|
|
||||||
<palette>
|
|
||||||
<active>
|
|
||||||
<colorrole role="WindowText">
|
|
||||||
<brush brushstyle="SolidPattern">
|
|
||||||
<color alpha="217">
|
|
||||||
<red>153</red>
|
|
||||||
<green>156</green>
|
|
||||||
<blue>145</blue>
|
|
||||||
</color>
|
|
||||||
</brush>
|
|
||||||
</colorrole>
|
|
||||||
</active>
|
|
||||||
<inactive>
|
|
||||||
<colorrole role="WindowText">
|
|
||||||
<brush brushstyle="SolidPattern">
|
|
||||||
<color alpha="217">
|
|
||||||
<red>153</red>
|
|
||||||
<green>156</green>
|
|
||||||
<blue>145</blue>
|
|
||||||
</color>
|
|
||||||
</brush>
|
|
||||||
</colorrole>
|
|
||||||
</inactive>
|
|
||||||
<disabled/>
|
|
||||||
</palette>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Hold space to speak</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="widget_5" native="true">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="widget_3" native="true">
|
|
||||||
<property name="layoutDirection">
|
|
||||||
<enum>Qt::LayoutDirection::RightToLeft</enum>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="toolButton_6">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="QIcon::ThemeIcon::EditClear"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="toolButton_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="QIcon::ThemeIcon::ListAdd"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="toolButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="QIcon::ThemeIcon::InsertImage"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="toolButton_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="QIcon::ThemeIcon::DocumentSend"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="toolButton_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="QIcon::ThemeIcon::CameraVideo"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QStatusBar" name="statusbar">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
#include "networkdetailwidget.h"
|
||||||
|
#include "store.h"
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QListWidget>
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
NetworkDetailWidget::NetworkDetailWidget(Store *store, QWidget *parent)
|
||||||
|
: QWidget(parent), m_store(store)
|
||||||
|
{
|
||||||
|
auto *layout = new QVBoxLayout(this);
|
||||||
|
layout->setContentsMargins(16, 16, 16, 16);
|
||||||
|
|
||||||
|
// Network name
|
||||||
|
m_nameLabel = new QLabel(this);
|
||||||
|
QFont nameFont;
|
||||||
|
nameFont.setPointSize(24);
|
||||||
|
m_nameLabel->setFont(nameFont);
|
||||||
|
layout->addWidget(m_nameLabel);
|
||||||
|
|
||||||
|
// Admin label
|
||||||
|
m_adminLabel = new QLabel(this);
|
||||||
|
layout->addWidget(m_adminLabel);
|
||||||
|
|
||||||
|
// Stream capacity progress bar
|
||||||
|
m_streamBar = new QProgressBar(this);
|
||||||
|
m_streamBar->setMinimum(0);
|
||||||
|
m_streamBar->setTextVisible(false);
|
||||||
|
layout->addWidget(m_streamBar);
|
||||||
|
|
||||||
|
m_streamBarLabel = new QLabel(this);
|
||||||
|
layout->addWidget(m_streamBarLabel);
|
||||||
|
|
||||||
|
layout->addStretch();
|
||||||
|
|
||||||
|
// Members header
|
||||||
|
auto *membersHeader = new QLabel("Members", this);
|
||||||
|
QFont headerFont;
|
||||||
|
headerFont.setBold(true);
|
||||||
|
membersHeader->setFont(headerFont);
|
||||||
|
layout->addWidget(membersHeader);
|
||||||
|
|
||||||
|
// Members list
|
||||||
|
m_membersList = new QListWidget(this);
|
||||||
|
layout->addWidget(m_membersList, 1);
|
||||||
|
|
||||||
|
// Button row
|
||||||
|
auto *buttonRow = new QHBoxLayout;
|
||||||
|
m_addMemberBtn = new QPushButton("Add Member", this);
|
||||||
|
buttonRow->addWidget(m_addMemberBtn);
|
||||||
|
buttonRow->addStretch();
|
||||||
|
layout->addLayout(buttonRow);
|
||||||
|
|
||||||
|
// Admin actions
|
||||||
|
connect(m_addMemberBtn, &QPushButton::clicked, this, [this]() {
|
||||||
|
bool ok;
|
||||||
|
QString email = QInputDialog::getText(this, "Add Member",
|
||||||
|
"Email address:", QLineEdit::Normal,
|
||||||
|
QString(), &ok);
|
||||||
|
if (ok && !email.isEmpty())
|
||||||
|
{
|
||||||
|
auto *op = m_store->addNetworkMembers(m_networkId, {email});
|
||||||
|
connect(op, &Operation::success, this, [this](const QJsonDocument &) {
|
||||||
|
m_store->fetchNetwork(m_networkId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Refresh when networks change
|
||||||
|
connect(m_store, &Store::networksChanged, this, [this]() {
|
||||||
|
if (!m_networkId.isEmpty())
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDetailWidget::setNetwork(const QString &networkId)
|
||||||
|
{
|
||||||
|
m_networkId = networkId;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDetailWidget::setCurrentUserEmail(const QString &email)
|
||||||
|
{
|
||||||
|
m_currentUserEmail = email;
|
||||||
|
if (!m_networkId.isEmpty())
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDetailWidget::refresh()
|
||||||
|
{
|
||||||
|
const Network *network = m_store->networkById(m_networkId);
|
||||||
|
if (!network)
|
||||||
|
{
|
||||||
|
m_nameLabel->setText("");
|
||||||
|
m_adminLabel->setText("");
|
||||||
|
m_streamBar->setValue(0);
|
||||||
|
m_streamBarLabel->setText("");
|
||||||
|
m_membersList->clear();
|
||||||
|
m_addMemberBtn->hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_nameLabel->setText(network->name);
|
||||||
|
m_adminLabel->setText("Admin: " + network->admin.emailPrefix);
|
||||||
|
|
||||||
|
m_streamBar->setMaximum(network->openStreamCapacity > 0 ? network->openStreamCapacity : 1);
|
||||||
|
m_streamBar->setValue(network->openStreamCount);
|
||||||
|
m_streamBarLabel->setText(QString("%1 / %2 open streams")
|
||||||
|
.arg(network->openStreamCount)
|
||||||
|
.arg(network->openStreamCapacity));
|
||||||
|
|
||||||
|
m_membersList->clear();
|
||||||
|
for (const auto &member : network->members)
|
||||||
|
{
|
||||||
|
m_membersList->addItem(member.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show admin controls only if current user is admin
|
||||||
|
bool isAdmin = (!m_currentUserEmail.isEmpty() &&
|
||||||
|
m_currentUserEmail == network->admin.email);
|
||||||
|
m_addMemberBtn->setVisible(isAdmin);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef NETWORKDETAILWIDGET_H
|
||||||
|
#define NETWORKDETAILWIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class Store;
|
||||||
|
class QLabel;
|
||||||
|
class QProgressBar;
|
||||||
|
class QListWidget;
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
|
class NetworkDetailWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit NetworkDetailWidget(Store *store, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void setNetwork(const QString &networkId);
|
||||||
|
void setCurrentUserEmail(const QString &email);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
Store *m_store;
|
||||||
|
QString m_networkId;
|
||||||
|
QString m_currentUserEmail;
|
||||||
|
|
||||||
|
QLabel *m_nameLabel;
|
||||||
|
QLabel *m_adminLabel;
|
||||||
|
QProgressBar *m_streamBar;
|
||||||
|
QLabel *m_streamBarLabel;
|
||||||
|
QListWidget *m_membersList;
|
||||||
|
QPushButton *m_addMemberBtn;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NETWORKDETAILWIDGET_H
|
||||||
@@ -125,14 +125,21 @@ QVariant NetworkStreamModel::data(const QModelIndex &index, int role) const
|
|||||||
|
|
||||||
if (networkIndex == 0xFFFFFFFF)
|
if (networkIndex == 0xFFFFFFFF)
|
||||||
{
|
{
|
||||||
|
int actualNetworkIndex = static_cast<int>(id & 0xFFFFFFFF);
|
||||||
// This is a network item
|
// This is a network item
|
||||||
if (role == Qt::DisplayRole && index.column() == 0)
|
if (role == Qt::DisplayRole && index.column() == 0)
|
||||||
{
|
{
|
||||||
int actualNetworkIndex = static_cast<int>(id & 0xFFFFFFFF);
|
|
||||||
if (actualNetworkIndex >= 0 && actualNetworkIndex < m_store->networks().size())
|
if (actualNetworkIndex >= 0 && actualNetworkIndex < m_store->networks().size())
|
||||||
{
|
{
|
||||||
return m_store->networks().at(actualNetworkIndex).name.toUpper();
|
return m_store->networks().at(actualNetworkIndex).name.toUpper();
|
||||||
}
|
}
|
||||||
|
} else if (role == Qt::DisplayRole && index.column() == 1)
|
||||||
|
{
|
||||||
|
if (actualNetworkIndex >= 0 && actualNetworkIndex < m_store->networks().size())
|
||||||
|
{
|
||||||
|
auto net = m_store->networks().at(actualNetworkIndex);
|
||||||
|
return QString("%1/%2").arg(net.openStreamCount).arg(net.openStreamCapacity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Networks don't show unseen count in column 1
|
// Networks don't show unseen count in column 1
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|||||||
+25
-2
@@ -1,15 +1,28 @@
|
|||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#include "authmanager.h"
|
#include "authmanager.h"
|
||||||
|
#include "store.h"
|
||||||
#include "ui_settingsdialog.h"
|
#include "ui_settingsdialog.h"
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(AuthManager *authManager, QWidget *parent)
|
SettingsDialog::SettingsDialog(AuthManager *authManager, Store *store, QWidget *parent)
|
||||||
: QDialog(parent), m_authManager(authManager), ui(new Ui::SettingsDialog)
|
: QDialog(parent), m_authManager(authManager), m_store(store), ui(new Ui::SettingsDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
this->setModal(true);
|
this->setModal(true);
|
||||||
|
|
||||||
|
// Populate list widget with tab items
|
||||||
|
ui->listWidget->addItem("Account");
|
||||||
|
ui->listWidget->addItem("Create Network");
|
||||||
|
|
||||||
|
// Wire list navigation to stacked widget
|
||||||
|
connect(ui->listWidget, &QListWidget::currentRowChanged,
|
||||||
|
ui->stackedWidget, &QStackedWidget::setCurrentIndex);
|
||||||
|
|
||||||
|
// Select first item by default
|
||||||
|
ui->listWidget->setCurrentRow(0);
|
||||||
|
|
||||||
connect(ui->signOutButton, &QPushButton::clicked, this, &SettingsDialog::on_signOutButton_clicked);
|
connect(ui->signOutButton, &QPushButton::clicked, this, &SettingsDialog::on_signOutButton_clicked);
|
||||||
|
connect(ui->createNetworkButton, &QPushButton::clicked, this, &SettingsDialog::onCreateNetworkClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog()
|
SettingsDialog::~SettingsDialog()
|
||||||
@@ -22,3 +35,13 @@ void SettingsDialog::on_signOutButton_clicked()
|
|||||||
m_authManager->signOut();
|
m_authManager->signOut();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::onCreateNetworkClicked()
|
||||||
|
{
|
||||||
|
QString name = ui->networkNameEdit->text().trimmed();
|
||||||
|
if (name.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_store->createNetwork(name);
|
||||||
|
ui->networkNameEdit->clear();
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
class AuthManager;
|
class AuthManager;
|
||||||
|
class Store;
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
@@ -15,15 +16,17 @@ class SettingsDialog : public QDialog
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SettingsDialog(AuthManager *, QWidget *parent = nullptr);
|
explicit SettingsDialog(AuthManager *, Store *, QWidget *parent = nullptr);
|
||||||
~SettingsDialog();
|
~SettingsDialog();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_signOutButton_clicked();
|
void on_signOutButton_clicked();
|
||||||
|
void onCreateNetworkClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::SettingsDialog *ui;
|
Ui::SettingsDialog *ui;
|
||||||
AuthManager *m_authManager;
|
AuthManager *m_authManager;
|
||||||
|
Store *m_store;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETTINGSDIALOG_H
|
#endif // SETTINGSDIALOG_H
|
||||||
|
|||||||
+87
-57
@@ -11,66 +11,96 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Dialog</string>
|
<string>Settings</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QListWidget" name="listWidget">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<property name="geometry">
|
<item>
|
||||||
<rect>
|
<widget class="QListWidget" name="listWidget">
|
||||||
<x>0</x>
|
<property name="maximumSize">
|
||||||
<y>0</y>
|
<size>
|
||||||
<width>221</width>
|
<width>220</width>
|
||||||
<height>701</height>
|
<height>16777215</height>
|
||||||
</rect>
|
</size>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QStackedWidget" name="stackedWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>220</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>561</width>
|
|
||||||
<height>701</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="page">
|
|
||||||
<widget class="QWidget" name="">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>220</x>
|
|
||||||
<y>310</y>
|
|
||||||
<width>85</width>
|
|
||||||
<height>64</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="authLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Hello, </string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="signOutButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Sign Out</string>
|
|
||||||
</property>
|
|
||||||
<property name="default">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="flat">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</item>
|
||||||
<widget class="QWidget" name="page_2"/>
|
<item>
|
||||||
</widget>
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
|
<widget class="QWidget" name="page">
|
||||||
|
<layout class="QVBoxLayout" name="accountLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="authLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Hello, </string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="signOutButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sign Out</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="page_2">
|
||||||
|
<layout class="QVBoxLayout" name="createNetworkLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="createNetworkLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create a new network</string>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>16</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="networkNameEdit">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Network name</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="createNetworkButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|||||||
@@ -793,6 +793,8 @@ Operation *Store::markParticlesSeen(const QStringList &particleIds)
|
|||||||
|
|
||||||
void Store::setCurrentUserEmail(const QString &email) { m_currentUserEmail = email; }
|
void Store::setCurrentUserEmail(const QString &email) { m_currentUserEmail = email; }
|
||||||
|
|
||||||
|
QString Store::currentUserEmail() const { return m_currentUserEmail; }
|
||||||
|
|
||||||
Operation *Store::ackParticle(const QString &particleId)
|
Operation *Store::ackParticle(const QString &particleId)
|
||||||
{
|
{
|
||||||
// Optimistic local update
|
// Optimistic local update
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ public:
|
|||||||
Operation *ackParticle(const QString &particleId);
|
Operation *ackParticle(const QString &particleId);
|
||||||
|
|
||||||
void setCurrentUserEmail(const QString &email);
|
void setCurrentUserEmail(const QString &email);
|
||||||
|
QString currentUserEmail() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// ── Data-change signals ──
|
// ── Data-change signals ──
|
||||||
|
|||||||
Reference in New Issue
Block a user