feat: show fake data network, stream, and particles data
This commit is contained in:
@@ -29,6 +29,12 @@ qt_add_executable(llink
|
||||
src/authdialog.ui
|
||||
src/authdialog.cpp
|
||||
src/settingsdialog.h src/settingsdialog.cpp src/settingsdialog.ui
|
||||
src/data.h
|
||||
src/data.cpp
|
||||
src/networkstreammodel.h
|
||||
src/networkstreammodel.cpp
|
||||
src/particlelistmodel.h
|
||||
src/particlelistmodel.cpp
|
||||
)
|
||||
|
||||
qt_add_resources(llink
|
||||
|
||||
+31
-31
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 16.0.2, 2026-01-27T16:56:03. -->
|
||||
<!-- Written by QtCreator 16.0.2, 2026-01-28T08:11:26. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
@@ -106,15 +106,15 @@
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_BUILD_TYPE:STRING=Debug
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
|
||||
-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_BUILD_TYPE:STRING=Debug
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON</value>
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="int" key="EnableQmlDebugging">0</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/llink/build/Qt_6_8_6_for_macOS-Debug</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
@@ -164,15 +164,15 @@
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_BUILD_TYPE:STRING=Release
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
|
||||
-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_BUILD_TYPE:STRING=Release
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON</value>
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/llink/build/Qt_6_8_6_for_macOS-Release</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
@@ -219,15 +219,15 @@
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
|
||||
-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON</value>
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/llink/build/Qt_6_8_6_for_macOS-RelWithDebInfo</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
@@ -274,15 +274,15 @@
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
|
||||
-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON</value>
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="int" key="EnableQmlDebugging">0</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/llink/build/Qt_6_8_6_for_macOS-Profile</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
@@ -330,15 +330,15 @@
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_BUILD_TYPE:STRING=MinSizeRel
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
|
||||
-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_BUILD_TYPE:STRING=MinSizeRel
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON</value>
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/talksik/Documents/code/llink/build/Qt_6_8_6_for_macOS-MinSizeRel</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
|
||||
+71
-1
@@ -8,6 +8,8 @@
|
||||
#include "settingsdialog.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QHeaderView>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QSystemTrayIcon>
|
||||
@@ -15,7 +17,7 @@
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent), m_trayIcon(nullptr), m_authManager(new AuthManager(this)), m_settingsDialog(nullptr),
|
||||
m_authDialog(nullptr)
|
||||
m_authDialog(nullptr), m_data(nullptr), m_networkStreamModel(nullptr), m_particleListModel(nullptr)
|
||||
{
|
||||
m_authDialog = new AuthDialog(m_authManager, this);
|
||||
|
||||
@@ -27,6 +29,8 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
ui->splitter->setStretchFactor(1, 1);
|
||||
ui->splitter->setStretchFactor(2, 2);
|
||||
|
||||
setupModels();
|
||||
|
||||
m_authDialog->setModal(true);
|
||||
|
||||
QTimer::singleShot(3000, this, [this]() {
|
||||
@@ -95,3 +99,69 @@ void MainWindow::on_settingsButton_clicked()
|
||||
}
|
||||
m_settingsDialog->show();
|
||||
}
|
||||
|
||||
void MainWindow::setupModels()
|
||||
{
|
||||
// Create data with fake data
|
||||
m_data = new Data(this);
|
||||
|
||||
// Create and set tree model
|
||||
m_networkStreamModel = new NetworkStreamModel(m_data, this);
|
||||
ui->topLevelParticlesView->setModel(m_networkStreamModel);
|
||||
ui->topLevelParticlesView->setHeaderHidden(true);
|
||||
ui->topLevelParticlesView->expandAll();
|
||||
|
||||
// Configure column behavior: name stretches, unseen count is fixed width
|
||||
ui->topLevelParticlesView->header()->setStretchLastSection(false);
|
||||
ui->topLevelParticlesView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
ui->topLevelParticlesView->header()->setSectionResizeMode(1, QHeaderView::Fixed);
|
||||
ui->topLevelParticlesView->setColumnWidth(1, 40);
|
||||
|
||||
// Create and set list model
|
||||
m_particleListModel = new ParticleListModel(m_data, this);
|
||||
ui->particlesView->setModel(m_particleListModel);
|
||||
|
||||
// Connect selection changes
|
||||
connect(ui->topLevelParticlesView->selectionModel(),
|
||||
&QItemSelectionModel::selectionChanged,
|
||||
this,
|
||||
&MainWindow::onTreeSelectionChanged);
|
||||
}
|
||||
|
||||
void MainWindow::onTreeSelectionChanged(
|
||||
const QItemSelection &selected,
|
||||
const QItemSelection &deselected)
|
||||
{
|
||||
Q_UNUSED(deselected);
|
||||
|
||||
if (selected.indexes().isEmpty())
|
||||
{
|
||||
m_particleListModel->setStreamId(QString());
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex index = selected.indexes().first();
|
||||
|
||||
if (m_networkStreamModel->isStreamItem(index))
|
||||
{
|
||||
QString streamId = m_networkStreamModel->streamIdFromIndex(index);
|
||||
m_particleListModel->setStreamId(streamId);
|
||||
|
||||
// Auto-select first unseen particle (or first particle if all seen)
|
||||
int unseenRow = m_particleListModel->firstUnseenRow();
|
||||
if (unseenRow >= 0)
|
||||
{
|
||||
ui->particlesView->setCurrentIndex(m_particleListModel->index(unseenRow, 0));
|
||||
ui->particlesView->setFocus();
|
||||
}
|
||||
else if (m_particleListModel->rowCount() > 0)
|
||||
{
|
||||
ui->particlesView->setCurrentIndex(m_particleListModel->index(0, 0));
|
||||
ui->particlesView->setFocus();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_particleListModel->setStreamId(QString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
#define LLINK_MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "data.h"
|
||||
#include "networkstreammodel.h"
|
||||
#include "particlelistmodel.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSystemTrayIcon;
|
||||
class AuthManager;
|
||||
class QItemSelection;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class AuthDialog;
|
||||
@@ -33,8 +37,11 @@ public:
|
||||
|
||||
void on_settingsButton_clicked();
|
||||
|
||||
void onTreeSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
||||
|
||||
private:
|
||||
void setupTrayIcon();
|
||||
void setupModels();
|
||||
|
||||
QSystemTrayIcon *m_trayIcon;
|
||||
Ui::MainWindow *ui;
|
||||
@@ -43,6 +50,11 @@ public:
|
||||
AuthDialog *m_authDialog;
|
||||
|
||||
SettingsDialog *m_settingsDialog;
|
||||
|
||||
// Data and models
|
||||
Data *m_data;
|
||||
NetworkStreamModel *m_networkStreamModel;
|
||||
ParticleListModel *m_particleListModel;
|
||||
};
|
||||
|
||||
#endif //LLINK_MAINWINDOW_H
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
#include "data.h"
|
||||
|
||||
Data::Data(QObject *parent) : QObject(parent)
|
||||
{
|
||||
initializeFakeData();
|
||||
}
|
||||
|
||||
void Data::initializeFakeData()
|
||||
{
|
||||
// Network 1: Private
|
||||
Network net1;
|
||||
net1.id = "net-private";
|
||||
net1.name = "Private";
|
||||
net1.openStreamCount = 2;
|
||||
net1.openStreamCapacity = 5;
|
||||
net1.countHumans = 1;
|
||||
m_networks.append(net1);
|
||||
|
||||
// Stream: Personal Notes
|
||||
Stream stream1;
|
||||
stream1.id = "stream-notes";
|
||||
stream1.name = "Personal Notes";
|
||||
stream1.status = Stream::OPEN;
|
||||
stream1.unseenCount = 3;
|
||||
m_streams[net1.id].append(stream1);
|
||||
|
||||
// Particles for Personal Notes
|
||||
m_particles[stream1.id].append({"p1", "text", true});
|
||||
m_particles[stream1.id].append({"p2", "link", false});
|
||||
m_particles[stream1.id].append({"p3", "image", true});
|
||||
m_particles[stream1.id].append({"p4", "text", false});
|
||||
m_particles[stream1.id].append({"p5", "link", true});
|
||||
|
||||
// Stream: Quick Captures
|
||||
Stream stream2;
|
||||
stream2.id = "stream-captures";
|
||||
stream2.name = "Quick Captures";
|
||||
stream2.status = Stream::OPEN;
|
||||
stream2.unseenCount = 0;
|
||||
m_streams[net1.id].append(stream2);
|
||||
|
||||
// Particles for Quick Captures
|
||||
m_particles[stream2.id].append({"p6", "text", true});
|
||||
m_particles[stream2.id].append({"p7", "image", true});
|
||||
|
||||
// Network 2: Flowy Labs, Inc
|
||||
Network net2;
|
||||
net2.id = "net-flowy";
|
||||
net2.name = "Flowy Labs, Inc";
|
||||
net2.openStreamCount = 3;
|
||||
net2.openStreamCapacity = 10;
|
||||
net2.countHumans = 12;
|
||||
m_networks.append(net2);
|
||||
|
||||
// Stream: Engineering
|
||||
Stream stream3;
|
||||
stream3.id = "stream-eng";
|
||||
stream3.name = "wild-shape";
|
||||
stream3.status = Stream::OPEN;
|
||||
stream3.unseenCount = 5;
|
||||
m_streams[net2.id].append(stream3);
|
||||
|
||||
// Particles for Engineering
|
||||
m_particles[stream3.id].append({"p8", "text", false});
|
||||
m_particles[stream3.id].append({"p9", "link", true});
|
||||
m_particles[stream3.id].append({"p10", "text", false});
|
||||
m_particles[stream3.id].append({"p11", "image", true});
|
||||
m_particles[stream3.id].append({"p12", "text", false});
|
||||
m_particles[stream3.id].append({"p13", "link", false});
|
||||
m_particles[stream3.id].append({"p14", "text", true});
|
||||
m_particles[stream3.id].append({"p15", "image", false});
|
||||
|
||||
// Stream: Design
|
||||
Stream stream4;
|
||||
stream4.id = "stream-design";
|
||||
stream4.name = "arch-rival";
|
||||
stream4.status = Stream::OPEN;
|
||||
stream4.unseenCount = 2;
|
||||
m_streams[net2.id].append(stream4);
|
||||
|
||||
// Particles for Design
|
||||
m_particles[stream4.id].append({"p16", "image", false});
|
||||
m_particles[stream4.id].append({"p17", "text", true});
|
||||
m_particles[stream4.id].append({"p18", "link", false});
|
||||
m_particles[stream4.id].append({"p19", "image", true});
|
||||
|
||||
// Stream: Archive
|
||||
Stream stream5;
|
||||
stream5.id = "stream-archive";
|
||||
stream5.name = "anthem-project";
|
||||
stream5.status = Stream::CLOSED;
|
||||
stream5.unseenCount = 0;
|
||||
m_streams[net2.id].append(stream5);
|
||||
|
||||
// Particles for Archive
|
||||
for (int i = 20; i < 35; i++)
|
||||
{
|
||||
m_particles[stream5.id].append({QString("p%1").arg(i), "text", true});
|
||||
}
|
||||
}
|
||||
|
||||
const QList<Network>& Data::networks() const
|
||||
{
|
||||
return m_networks;
|
||||
}
|
||||
|
||||
const QList<Stream>& Data::streamsForNetwork(const QString &networkId) const
|
||||
{
|
||||
static QList<Stream> emptyList;
|
||||
auto it = m_streams.find(networkId);
|
||||
if (it == m_streams.end())
|
||||
{
|
||||
return emptyList;
|
||||
}
|
||||
return it.value();
|
||||
}
|
||||
|
||||
const QList<Particle>& Data::particlesForStream(const QString &streamId) const
|
||||
{
|
||||
static QList<Particle> emptyList;
|
||||
auto it = m_particles.find(streamId);
|
||||
if (it == m_particles.end())
|
||||
{
|
||||
return emptyList;
|
||||
}
|
||||
return it.value();
|
||||
}
|
||||
|
||||
int Data::networkCount() const
|
||||
{
|
||||
return m_networks.size();
|
||||
}
|
||||
|
||||
int Data::streamCount(const QString &networkId) const
|
||||
{
|
||||
return m_streams.value(networkId).size();
|
||||
}
|
||||
|
||||
int Data::particleCount(const QString &streamId) const
|
||||
{
|
||||
return m_particles.value(streamId).size();
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#ifndef DATA_H
|
||||
#define DATA_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
|
||||
struct Network
|
||||
{
|
||||
QString id;
|
||||
QString name;
|
||||
int openStreamCount;
|
||||
int openStreamCapacity;
|
||||
int countHumans;
|
||||
};
|
||||
|
||||
struct Stream
|
||||
{
|
||||
QString id;
|
||||
QString name;
|
||||
enum StreamStatus
|
||||
{
|
||||
OPEN,
|
||||
CLOSED,
|
||||
};
|
||||
StreamStatus status;
|
||||
int unseenCount;
|
||||
};
|
||||
|
||||
struct Particle
|
||||
{
|
||||
QString id;
|
||||
QString type;
|
||||
bool seen;
|
||||
};
|
||||
|
||||
class Data : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Data(QObject *parent = nullptr);
|
||||
|
||||
// Accessors for models
|
||||
const QList<Network>& networks() const;
|
||||
const QList<Stream>& streamsForNetwork(const QString &networkId) const;
|
||||
const QList<Particle>& particlesForStream(const QString &streamId) const;
|
||||
|
||||
int networkCount() const;
|
||||
int streamCount(const QString &networkId) const;
|
||||
int particleCount(const QString &streamId) const;
|
||||
|
||||
signals:
|
||||
void networksChanged();
|
||||
void streamsChanged(const QString &networkId);
|
||||
void particlesChanged(const QString &streamId);
|
||||
|
||||
private:
|
||||
void initializeFakeData();
|
||||
QList<Network> m_networks;
|
||||
// Network -> Streams
|
||||
QMap<QString, QList<Stream>> m_streams;
|
||||
// Stream -> Particles
|
||||
QMap<QString, QList<Particle>> m_particles;
|
||||
};
|
||||
|
||||
#endif // DATA_H
|
||||
+95
-42
@@ -14,7 +14,7 @@
|
||||
<string>Flowy.llink</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -41,61 +41,114 @@
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>28</height>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QToolButton" name="exitButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>770</x>
|
||||
<y>0</y>
|
||||
<width>26</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="QIcon::ThemeIcon::ApplicationExit"/>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QToolButton" name="settingsButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>740</x>
|
||||
<y>0</y>
|
||||
<width>26</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="QIcon::ThemeIcon::HelpAbout"/>
|
||||
</property>
|
||||
</widget>
|
||||
<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="settingsButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="QIcon::ThemeIcon::HelpAbout"/>
|
||||
</property>
|
||||
</widget>
|
||||
</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="QSplitter" name="splitter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTreeView" name="topLevelParticlesView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<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="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>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_2">
|
||||
<property name="text">
|
||||
<string>Create network</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QListView" name="particlesView">
|
||||
<property name="sizePolicy">
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "networkstreammodel.h"
|
||||
|
||||
NetworkStreamModel::NetworkStreamModel(Data *data, QObject *parent)
|
||||
: QAbstractItemModel(parent), m_data(data)
|
||||
{
|
||||
}
|
||||
|
||||
QModelIndex NetworkStreamModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent))
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
if (!parent.isValid())
|
||||
{
|
||||
// Top-level item (Network)
|
||||
// Encode: networkIndex in lower 32 bits, -1 in upper 32 bits
|
||||
quintptr id = static_cast<quintptr>(row) | (static_cast<quintptr>(0xFFFFFFFF) << 32);
|
||||
return createIndex(row, column, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Child item (Stream)
|
||||
// Decode parent to get network index
|
||||
quintptr parentId = parent.internalId();
|
||||
int networkIndex = static_cast<int>(parentId & 0xFFFFFFFF);
|
||||
|
||||
// Encode: streamIndex in lower 32 bits, networkIndex in upper 32 bits
|
||||
quintptr id = static_cast<quintptr>(row) | (static_cast<quintptr>(networkIndex) << 32);
|
||||
return createIndex(row, column, id);
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex NetworkStreamModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
if (!child.isValid())
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
quintptr id = child.internalId();
|
||||
int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);
|
||||
|
||||
// If networkIndex is 0xFFFFFFFF, this is a network item (no parent)
|
||||
if (networkIndex == 0xFFFFFFFF)
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
// This is a stream item, return its parent network
|
||||
quintptr parentId = static_cast<quintptr>(networkIndex) | (static_cast<quintptr>(0xFFFFFFFF) << 32);
|
||||
return createIndex(networkIndex, 0, parentId);
|
||||
}
|
||||
|
||||
int NetworkStreamModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid())
|
||||
{
|
||||
// Root level - return number of networks
|
||||
return m_data->networkCount();
|
||||
}
|
||||
|
||||
quintptr id = parent.internalId();
|
||||
int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);
|
||||
|
||||
// If this is a network item, return number of streams
|
||||
if (networkIndex == 0xFFFFFFFF)
|
||||
{
|
||||
int actualNetworkIndex = static_cast<int>(id & 0xFFFFFFFF);
|
||||
if (actualNetworkIndex >= 0 && actualNetworkIndex < m_data->networks().size())
|
||||
{
|
||||
const QString &networkId = m_data->networks().at(actualNetworkIndex).id;
|
||||
return m_data->streamCount(networkId);
|
||||
}
|
||||
}
|
||||
|
||||
// Stream items have no children
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NetworkStreamModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return 2; // Column 0: name, Column 1: unseen count
|
||||
}
|
||||
|
||||
QVariant NetworkStreamModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
quintptr id = index.internalId();
|
||||
int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);
|
||||
|
||||
if (networkIndex == 0xFFFFFFFF)
|
||||
{
|
||||
// This is a network item
|
||||
if (role == Qt::DisplayRole && index.column() == 0)
|
||||
{
|
||||
int actualNetworkIndex = static_cast<int>(id & 0xFFFFFFFF);
|
||||
if (actualNetworkIndex >= 0 && actualNetworkIndex < m_data->networks().size())
|
||||
{
|
||||
return m_data->networks().at(actualNetworkIndex).name;
|
||||
}
|
||||
}
|
||||
// Networks don't show unseen count in column 1
|
||||
return QVariant();
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a stream item
|
||||
int streamIndex = static_cast<int>(id & 0xFFFFFFFF);
|
||||
if (networkIndex >= 0 && networkIndex < m_data->networks().size())
|
||||
{
|
||||
const QString &networkId = m_data->networks().at(networkIndex).id;
|
||||
const QList<Stream> &streams = m_data->streamsForNetwork(networkId);
|
||||
|
||||
if (streamIndex >= 0 && streamIndex < streams.size())
|
||||
{
|
||||
const Stream &stream = streams.at(streamIndex);
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
if (index.column() == 0)
|
||||
{
|
||||
// Column 0: stream name
|
||||
return stream.name;
|
||||
}
|
||||
else if (index.column() == 1)
|
||||
{
|
||||
// Column 1: unseen count (only show if > 0)
|
||||
if (stream.unseenCount > 0)
|
||||
{
|
||||
return stream.unseenCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (role == Qt::TextAlignmentRole && index.column() == 1)
|
||||
{
|
||||
// Right-align the unseen count
|
||||
return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QString NetworkStreamModel::streamIdFromIndex(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
quintptr id = index.internalId();
|
||||
int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);
|
||||
|
||||
// Check if this is a stream item (not a network)
|
||||
if (networkIndex != 0xFFFFFFFF)
|
||||
{
|
||||
int streamIndex = static_cast<int>(id & 0xFFFFFFFF);
|
||||
if (networkIndex >= 0 && networkIndex < m_data->networks().size())
|
||||
{
|
||||
const QString &networkId = m_data->networks().at(networkIndex).id;
|
||||
const QList<Stream> &streams = m_data->streamsForNetwork(networkId);
|
||||
|
||||
if (streamIndex >= 0 && streamIndex < streams.size())
|
||||
{
|
||||
return streams.at(streamIndex).id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool NetworkStreamModel::isStreamItem(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
quintptr id = index.internalId();
|
||||
int networkIndex = static_cast<int>((id >> 32) & 0xFFFFFFFF);
|
||||
|
||||
// If networkIndex is not 0xFFFFFFFF, this is a stream item
|
||||
return networkIndex != 0xFFFFFFFF;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef NETWORKSTREAMMODEL_H
|
||||
#define NETWORKSTREAMMODEL_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include "data.h"
|
||||
|
||||
class NetworkStreamModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NetworkStreamModel(Data *data, QObject *parent = nullptr);
|
||||
|
||||
// Required QAbstractItemModel interface
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex &child) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
// Helper methods for MainWindow
|
||||
QString streamIdFromIndex(const QModelIndex &index) const;
|
||||
bool isStreamItem(const QModelIndex &index) const;
|
||||
|
||||
private:
|
||||
Data *m_data;
|
||||
};
|
||||
|
||||
#endif // NETWORKSTREAMMODEL_H
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "particlelistmodel.h"
|
||||
|
||||
ParticleListModel::ParticleListModel(Data *data, QObject *parent)
|
||||
: QAbstractListModel(parent), m_data(data), m_currentStreamId()
|
||||
{
|
||||
}
|
||||
|
||||
int ParticleListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_currentStreamId.isEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_data->particleCount(m_currentStreamId);
|
||||
}
|
||||
|
||||
QVariant ParticleListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || role != Qt::DisplayRole)
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (m_currentStreamId.isEmpty())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const QList<Particle> &particles = m_data->particlesForStream(m_currentStreamId);
|
||||
|
||||
if (index.row() < 0 || index.row() >= particles.size())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const Particle &particle = particles.at(index.row());
|
||||
|
||||
// Show dot (•) for unseen, nothing for seen
|
||||
if (particle.seen)
|
||||
{
|
||||
return particle.type;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QString("%1 •").arg(particle.type);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleListModel::setStreamId(const QString &streamId)
|
||||
{
|
||||
beginResetModel();
|
||||
m_currentStreamId = streamId;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int ParticleListModel::firstUnseenRow() const
|
||||
{
|
||||
if (m_currentStreamId.isEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
const QList<Particle> &particles = m_data->particlesForStream(m_currentStreamId);
|
||||
for (int i = 0; i < particles.size(); ++i)
|
||||
{
|
||||
if (!particles.at(i).seen)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // All seen or empty
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef PARTICLELISTMODEL_H
|
||||
#define PARTICLELISTMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include "data.h"
|
||||
|
||||
class ParticleListModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ParticleListModel(Data *data, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
void setStreamId(const QString &streamId);
|
||||
int firstUnseenRow() const; // Returns -1 if none unseen
|
||||
|
||||
private:
|
||||
Data *m_data;
|
||||
QString m_currentStreamId;
|
||||
};
|
||||
|
||||
#endif // PARTICLELISTMODEL_H
|
||||
Reference in New Issue
Block a user