auto-connect + GUI per-channel indicators
- chat_core_connect_auto: direct ETCP connect from SQLite (no BGP) - auto_connect: parallel connect to peers with 3s timeout, retry 10s - RTT saved to node_addresses on disconnect - vertical status bar + circle badge with online count per channel - ChannelListView prevents deselection
This commit is contained in:
1 parent
1a0229fd4f
commit
dccf54b30b
22 files changed
+909
-10
No files matched your search
@@ -9,15 +9,30 @@ void ChannelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
||||
const QModelIndex &index) const {
|
||||
painter->save();
|
||||
|
||||
const int barWidth = 4;
|
||||
const int margin = 8;
|
||||
const int iconSize = 32;
|
||||
|
||||
int peersOnline = index.data(ChannelPeersOnlineRole).toInt();
|
||||
int autoActive = index.data(ChannelAutoConnectActiveRole).toInt();
|
||||
|
||||
/* ── background ── */
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
painter->fillRect(option.rect, option.palette.highlight());
|
||||
} else if (option.state & QStyle::State_MouseOver) {
|
||||
painter->fillRect(option.rect, option.palette.light());
|
||||
}
|
||||
|
||||
const int margin = 8;
|
||||
const int iconSize = 32;
|
||||
QRect iconRect(option.rect.left() + margin,
|
||||
/* ── vertical status bar (left edge, over background) ── */
|
||||
QColor barColor = Qt::transparent;
|
||||
if (peersOnline > 0) barColor = QColor("#4CAF50");
|
||||
else if (autoActive && peersOnline == 0) barColor = QColor("#FFC107");
|
||||
if (barColor.alpha() > 0) {
|
||||
QRect barRect(option.rect.left(), option.rect.top(), barWidth, option.rect.height());
|
||||
painter->fillRect(barRect, barColor);
|
||||
}
|
||||
|
||||
QRect iconRect(option.rect.left() + margin + barWidth,
|
||||
option.rect.top() + (option.rect.height() - iconSize) / 2,
|
||||
iconSize, iconSize);
|
||||
|
||||
@@ -26,6 +41,24 @@ void ChannelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
||||
icon.paint(painter, iconRect);
|
||||
}
|
||||
|
||||
/* ── online count badge (top-left of avatar, above it) ── */
|
||||
if (peersOnline > 0) {
|
||||
int badgeSize = 16;
|
||||
QRect badgeRect(iconRect.left() - 4, iconRect.top() - 6, badgeSize, badgeSize);
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
painter->setBrush(QColor("#4CAF50"));
|
||||
painter->setPen(QPen(Qt::white, 1.5));
|
||||
painter->drawEllipse(badgeRect);
|
||||
QFont badgeFont = option.font;
|
||||
badgeFont.setBold(true);
|
||||
badgeFont.setPixelSize(10);
|
||||
painter->setFont(badgeFont);
|
||||
painter->setPen(Qt::white);
|
||||
QString num = QString::number(peersOnline);
|
||||
if (peersOnline > 9) num = "9+";
|
||||
painter->drawText(badgeRect, Qt::AlignCenter, num);
|
||||
}
|
||||
|
||||
QRect textRect(iconRect.right() + margin, option.rect.top() + margin,
|
||||
option.rect.right() - iconRect.right() - 2 * margin,
|
||||
option.rect.height() - 2 * margin);
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
enum ChannelDataRole {
|
||||
ChannelNameRole = Qt::DisplayRole,
|
||||
ChannelIconRole = Qt::DecorationRole,
|
||||
ChannelLastMessageRole = Qt::UserRole + 1,
|
||||
ChannelChannelIdRole = Qt::UserRole + 2
|
||||
ChannelNameRole = Qt::DisplayRole,
|
||||
ChannelIconRole = Qt::DecorationRole,
|
||||
ChannelLastMessageRole = Qt::UserRole + 1,
|
||||
ChannelChannelIdRole = Qt::UserRole + 2,
|
||||
ChannelPeersOnlineRole = Qt::UserRole + 3, /* int: count of online peers */
|
||||
ChannelAutoConnectActiveRole = Qt::UserRole + 4 /* bool: 1 if auto-connect in progress */
|
||||
};
|
||||
|
||||
class ChannelDelegate : public QStyledItemDelegate {
|
||||
|
||||
@@ -8,6 +8,28 @@
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
|
||||
/* QListView that prevents deselection */
|
||||
class ChannelListView : public QListView {
|
||||
public:
|
||||
using QListView::QListView;
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* e) override {
|
||||
if (indexAt(e->pos()).isValid())
|
||||
QListView::mousePressEvent(e);
|
||||
}
|
||||
QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& idx, const QEvent* e) const override {
|
||||
if (idx.isValid())
|
||||
return QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current;
|
||||
return QItemSelectionModel::NoUpdate;
|
||||
}
|
||||
void selectionChanged(const QItemSelection& sel, const QItemSelection& desel) override {
|
||||
QListView::selectionChanged(sel, desel);
|
||||
if (selectionModel() && !selectionModel()->hasSelection() && currentIndex().isValid())
|
||||
selectionModel()->select(currentIndex(), QItemSelectionModel::ClearAndSelect);
|
||||
}
|
||||
};
|
||||
|
||||
static QIcon makeChannelIcon(const QColor &color, const QString &letter) {
|
||||
QPixmap pix(32, 32);
|
||||
@@ -29,7 +51,7 @@ static QIcon makeChannelIcon(const QColor &color, const QString &letter) {
|
||||
|
||||
ChannelList::ChannelList(DbManager* db, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_listView(new QListView(this))
|
||||
, m_listView(new ChannelListView(this))
|
||||
, m_model(new QStandardItemModel(this))
|
||||
, m_db(db)
|
||||
{
|
||||
@@ -161,3 +183,22 @@ void ChannelList::onContextMenu(const QPoint& pos) {
|
||||
if (menu.exec(m_listView->viewport()->mapToGlobal(pos)) == inviteAction)
|
||||
emit inviteRequested(cid);
|
||||
}
|
||||
|
||||
void ChannelList::setChannelPeersOnline(const QString& channelId, int count) {
|
||||
for (int row = 0; row < m_model->rowCount(); row++) {
|
||||
QStandardItem* item = m_model->item(row);
|
||||
if (item && item->data(ChannelChannelIdRole).toString() == channelId) {
|
||||
item->setData(count, ChannelPeersOnlineRole);
|
||||
item->setData(m_autoConnectActive ? 1 : 0, ChannelAutoConnectActiveRole);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelList::setAutoConnectActive(bool active) {
|
||||
m_autoConnectActive = active;
|
||||
for (int row = 0; row < m_model->rowCount(); row++) {
|
||||
QStandardItem* item = m_model->item(row);
|
||||
if (item) item->setData(active ? 1 : 0, ChannelAutoConnectActiveRole);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ public:
|
||||
|
||||
void loadChannels();
|
||||
void selectChannel(const QString& channelId);
|
||||
void setChannelPeersOnline(const QString& channelId, int count);
|
||||
void setAutoConnectActive(bool active);
|
||||
|
||||
signals:
|
||||
void channelSelected(const QString& channelId);
|
||||
@@ -29,4 +31,5 @@ private:
|
||||
QListView *m_listView;
|
||||
QStandardItemModel *m_model;
|
||||
DbManager *m_db;
|
||||
bool m_autoConnectActive = false;
|
||||
};
|
||||
@@ -46,6 +46,17 @@ static void onMembersChangedCallback(const char* ch_id, int ch_id_len) {
|
||||
s_mainWindow->onMembersChanged(ch_id);
|
||||
}
|
||||
|
||||
static void onAutoConnectStatusCallback(uint8_t status, uint16_t total_tried,
|
||||
uint16_t node_count, uint16_t connected) {
|
||||
if (s_mainWindow)
|
||||
s_mainWindow->onAutoConnectStatus(status, total_tried, node_count, connected);
|
||||
}
|
||||
|
||||
static void onChannelPeersOnlineCallback(const char* ch_id, int ch_id_len, uint16_t online) {
|
||||
if (s_mainWindow)
|
||||
s_mainWindow->onChannelPeersOnline(ch_id, ch_id_len, online);
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath,
|
||||
const QString& dbPath, const QString& debugFile,
|
||||
const QString& debugLevel, const QString& debugCategories)
|
||||
@@ -76,6 +87,8 @@ MainWindow::MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath,
|
||||
qDebug("MainWindow: MY_NODE_ID setMyNodeId(%016llx)", (unsigned long long)nid);
|
||||
if (s_mainWindow && s_mainWindow->m_db) s_mainWindow->m_db->setMyNodeId((quint64)nid);
|
||||
});
|
||||
gui_bridge_set_auto_connect_status_cb(onAutoConnectStatusCallback);
|
||||
gui_bridge_set_channel_peers_online_cb(onChannelPeersOnlineCallback);
|
||||
|
||||
setupNode();
|
||||
setupMessaging();
|
||||
@@ -258,6 +271,20 @@ void MainWindow::onMembersChanged(const char* ch_id) {
|
||||
m_accountList->refresh();
|
||||
}
|
||||
|
||||
void MainWindow::onAutoConnectStatus(uint8_t status, uint16_t /*total_tried*/,
|
||||
uint16_t /*node_count*/, uint16_t /*connected*/) {
|
||||
if (m_channelList) {
|
||||
m_channelList->setAutoConnectActive(status == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onChannelPeersOnline(const char* ch_id, int ch_id_len, uint16_t online) {
|
||||
if (m_channelList) {
|
||||
QString cid = QString::fromUtf8(ch_id, ch_id_len);
|
||||
m_channelList->setChannelPeersOnline(cid, (int)online);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::reloadChannels() {
|
||||
if (m_channelList) {
|
||||
m_channelList->loadChannels();
|
||||
|
||||
@@ -33,6 +33,8 @@ private slots:
|
||||
public:
|
||||
void onMessageReceived(const char* ch_id);
|
||||
void onMembersChanged(const char* ch_id);
|
||||
void onAutoConnectStatus(uint8_t status, uint16_t total, uint16_t totalNodes, uint16_t connected);
|
||||
void onChannelPeersOnline(const char* ch_id, int ch_id_len, uint16_t online);
|
||||
void reloadChannels();
|
||||
|
||||
private:
|
||||
|
||||
Reference in new issue
Block a user