You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
11 KiB
271 lines
11 KiB
#include "channellist.h" |
|
#include "channeldelegate.h" |
|
#include "../db/db_manager.h" |
|
#include <QVBoxLayout> |
|
#include <QHBoxLayout> |
|
#include <QLabel> |
|
#include <QPushButton> |
|
#include <QPainter> |
|
#include <QPixmap> |
|
#include <QMouseEvent> |
|
|
|
extern "C" { |
|
#include "../../lib/debug_config.h" |
|
} |
|
|
|
/* QListView that prevents deselection, handles invite plus-button clicks */ |
|
class ChannelListView : public QListView { |
|
Q_OBJECT |
|
public: |
|
using QListView::QListView; |
|
|
|
signals: |
|
void inviteClicked(const QString& channelId); |
|
|
|
protected: |
|
void mousePressEvent(QMouseEvent* e) override { |
|
QModelIndex idx = indexAt(e->pos()); |
|
if (!idx.isValid()) return; |
|
QRect plusRect = ChannelDelegate::inviteButtonRect(visualRect(idx)); |
|
if (plusRect.contains(e->pos())) { |
|
emit inviteClicked(idx.data(ChannelChannelIdRole).toString()); |
|
return; |
|
} |
|
QListView::mousePressEvent(e); |
|
} |
|
void mouseMoveEvent(QMouseEvent* e) override { |
|
int prevRow = m_hoveredInviteRow; |
|
m_hoveredInviteRow = -1; |
|
QModelIndex idx = indexAt(e->pos()); |
|
if (idx.isValid()) { |
|
QRect plusRect = ChannelDelegate::inviteButtonRect(visualRect(idx)); |
|
if (plusRect.contains(e->pos())) |
|
m_hoveredInviteRow = idx.row(); |
|
} |
|
if (m_hoveredInviteRow != prevRow) { |
|
model()->setData(model()->index(prevRow, 0), 0, ChannelInviteBtnHoveredRole); |
|
model()->setData(model()->index(m_hoveredInviteRow, 0), 1, ChannelInviteBtnHoveredRole); |
|
} |
|
setCursor(m_hoveredInviteRow >= 0 ? Qt::PointingHandCursor : Qt::ArrowCursor); |
|
QListView::mouseMoveEvent(e); |
|
} |
|
QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& idx, const QEvent* e) const override { |
|
if (idx.isValid()) |
|
return QItemSelectionModel::ClearAndSelect; |
|
return QItemSelectionModel::NoUpdate; |
|
} |
|
void selectionChanged(const QItemSelection& sel, const QItemSelection& desel) override { |
|
QListView::selectionChanged(sel, desel); |
|
if (selectionModel() && !selectionModel()->hasSelection() && currentIndex().isValid() |
|
&& currentIndex().row() < model()->rowCount()) |
|
selectionModel()->select(currentIndex(), QItemSelectionModel::ClearAndSelect); |
|
} |
|
|
|
private: |
|
int m_hoveredInviteRow = -1; |
|
}; |
|
|
|
static QIcon makeChannelIcon(const QColor &color, const QString &letter) { |
|
QPixmap pix(32, 32); |
|
pix.fill(Qt::transparent); |
|
QPainter p(&pix); |
|
p.setRenderHint(QPainter::Antialiasing); |
|
p.setBrush(color); |
|
p.setPen(Qt::NoPen); |
|
p.drawRoundedRect(0, 0, 32, 32, 8, 8); |
|
p.setPen(Qt::white); |
|
QFont f = p.font(); |
|
f.setBold(true); |
|
f.setPointSize(14); |
|
p.setFont(f); |
|
p.drawText(QRect(0, 0, 32, 32), Qt::AlignCenter, letter); |
|
p.end(); |
|
return QIcon(pix); |
|
} |
|
|
|
ChannelList::ChannelList(DbManager* db, QWidget *parent) |
|
: QWidget(parent) |
|
, m_listView(new ChannelListView(this)) |
|
, m_model(new QStandardItemModel(this)) |
|
, m_db(db) |
|
{ |
|
auto *layout = new QVBoxLayout(this); |
|
layout->setContentsMargins(0, 0, 0, 0); |
|
layout->setSpacing(0); |
|
|
|
auto *header = new QLabel("Channels", this); |
|
header->setAlignment(Qt::AlignCenter); |
|
header->setStyleSheet("font-weight: bold; padding: 8px; background: palette(window);"); |
|
layout->addWidget(header); |
|
|
|
m_listView->setModel(m_model); |
|
m_listView->setItemDelegate(new ChannelDelegate(this)); |
|
m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers); |
|
m_listView->setSelectionMode(QAbstractItemView::SingleSelection); |
|
m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
|
m_listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); |
|
m_listView->setMouseTracking(true); |
|
layout->addWidget(m_listView); |
|
|
|
auto *footer = new QWidget(this); |
|
auto *fl = new QHBoxLayout(footer); |
|
fl->setContentsMargins(6, 4, 6, 4); |
|
fl->setSpacing(6); |
|
|
|
auto *gearBtn = new QPushButton(QString::fromUtf8("\xE2\x9A\x99"), footer); |
|
gearBtn->setFixedSize(28, 28); |
|
gearBtn->setToolTip(QString::fromUtf8("Настройки")); |
|
gearBtn->setStyleSheet( |
|
"QPushButton { border: none; font-size: 18px; background: transparent; color: gray; }" |
|
"QPushButton:hover { color: palette(text); }"); |
|
fl->addWidget(gearBtn); |
|
|
|
connect(gearBtn, &QPushButton::clicked, this, [this] { emit settingsRequested(); }); |
|
|
|
fl->addStretch(); |
|
|
|
auto *createBtn = new QPushButton("GRP", footer); |
|
createBtn->setFixedSize(36, 28); |
|
createBtn->setToolTip(QString::fromUtf8("Создать группу")); |
|
createBtn->setStyleSheet( |
|
"QPushButton { border: 1.5px solid #27AE60; border-radius: 6px;" |
|
" font-size: 11px; font-weight: bold; color: #27AE60; background: transparent; }" |
|
"QPushButton:hover { background: #27AE60; color: white; }"); |
|
connect(createBtn, &QPushButton::clicked, this, [this] { emit createGroupRequested(); }); |
|
fl->addWidget(createBtn); |
|
|
|
auto *plusBtn = new QPushButton("+", footer); |
|
plusBtn->setFixedSize(28, 28); |
|
plusBtn->setToolTip(QString::fromUtf8("Присоединиться")); |
|
plusBtn->setStyleSheet( |
|
"QPushButton { border: 1.5px solid #3390EC; border-radius: 14px;" |
|
" font-size: 18px; font-weight: bold; color: #3390EC; background: transparent; }" |
|
"QPushButton:hover { background: #3390EC; color: white; }"); |
|
connect(plusBtn, &QPushButton::clicked, this, [this] { emit joinRequested(); }); |
|
fl->addWidget(plusBtn); |
|
|
|
layout->addWidget(footer); |
|
|
|
connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged, |
|
this, [this](const QModelIndex ¤t, const QModelIndex &) { |
|
if (current.isValid()) { |
|
QString cid = current.data(ChannelChannelIdRole).toString(); |
|
emit channelSelected(cid); |
|
} |
|
}); |
|
connect(static_cast<ChannelListView*>(m_listView), &ChannelListView::inviteClicked, |
|
this, &ChannelList::inviteRequested); |
|
} |
|
|
|
void ChannelList::loadChannels() { |
|
QString selectedCid; |
|
if (m_listView->currentIndex().isValid()) |
|
selectedCid = m_listView->currentIndex().data(ChannelChannelIdRole).toString(); |
|
|
|
auto* sel = m_listView->selectionModel(); |
|
sel->blockSignals(true); |
|
m_model->clear(); |
|
if (m_db && m_db->isOpen()) { |
|
int sortMode = m_db->getUiStateInt("channel_sort", 0); |
|
auto channels = m_db->getChannels(sortMode == 1 ? ChannelSortMode::Recent : ChannelSortMode::Alphabetical); |
|
static const QColor colors[] = { |
|
QColor(74,144,226), QColor(126,211,33), QColor(245,166,35), |
|
QColor(208,2,27), QColor(144,19,254), QColor(0,184,148) |
|
}; |
|
int ci = 0; |
|
|
|
for (const auto& ch : channels) { |
|
auto letter = ch.name.mid(0, 1).toUpper(); |
|
if (letter.isEmpty()) letter = "#"; |
|
auto *item = new QStandardItem(); |
|
item->setData(ch.name, ChannelNameRole); |
|
item->setData(ch.channelId, ChannelChannelIdRole); |
|
item->setData(m_db->getChannelLastMessage(ch.channelId), ChannelLastMessageRole); |
|
item->setData(ch.unreadCount, ChannelUnreadCountRole); |
|
item->setData(ch.lastMsgAt, ChannelLastMsgAtRole); |
|
item->setData(makeChannelIcon(colors[ci % 6], letter), ChannelIconRole); |
|
m_model->appendRow(item); |
|
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "ChannelList::loadChannels row=%d cid='%s' name='%s' unread=%d lastMsgAt=%lld sort=%d", |
|
m_model->rowCount()-1, qPrintable(ch.channelId), qPrintable(ch.name), ch.unreadCount, |
|
(long long)ch.lastMsgAt, sortMode); |
|
ci++; |
|
} |
|
} |
|
sel->blockSignals(false); |
|
|
|
if (!selectedCid.isEmpty()) { |
|
selectChannel(selectedCid); |
|
} else if (m_model->rowCount() > 0) { |
|
QModelIndex idx = m_model->index(0, 0); |
|
sel->select(idx, QItemSelectionModel::ClearAndSelect); |
|
sel->setCurrentIndex(idx, QItemSelectionModel::NoUpdate); |
|
} |
|
} |
|
|
|
void ChannelList::selectChannel(const QString& channelId) { |
|
for (int row = 0; row < m_model->rowCount(); row++) { |
|
if (m_model->item(row)->data(ChannelChannelIdRole).toString() == channelId) { |
|
QModelIndex idx = m_model->index(row, 0); |
|
m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect); |
|
m_listView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::NoUpdate); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
void ChannelList::setChannelPeersOnline(const QString& channelId, int count) { |
|
int found = 0; |
|
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "ChannelList::setChannelPeersOnline search='%s' online=%d rows=%d", |
|
qPrintable(channelId), count, m_model->rowCount()); |
|
for (int row = 0; row < m_model->rowCount(); row++) { |
|
QStandardItem* item = m_model->item(row); |
|
if (item) { |
|
QString itemCid = item->data(ChannelChannelIdRole).toString(); |
|
int match = (itemCid == channelId) ? 1 : 0; |
|
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, " row=%d cid='%s' match=%d", row, qPrintable(itemCid), match); |
|
if (match) { |
|
item->setData(count, ChannelPeersOnlineRole); |
|
item->setData(m_autoConnectActive ? 1 : 0, ChannelAutoConnectActiveRole); |
|
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, " SET online=%d", count); |
|
found = 1; |
|
break; |
|
} |
|
} |
|
} |
|
if (!found) |
|
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, " NOT FOUND in model"); |
|
} |
|
|
|
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); |
|
} |
|
} |
|
|
|
void ChannelList::setChannelUnreadCount(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) { |
|
int old = item->data(ChannelUnreadCountRole).toInt(); |
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "ChannelList::setChannelUnreadCount ch=%s %d->%d", |
|
qPrintable(channelId), old, count); |
|
item->setData(count, ChannelUnreadCountRole); |
|
emit m_model->dataChanged(m_model->index(row, 0), m_model->index(row, 0)); |
|
return; |
|
} |
|
} |
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "ChannelList::setChannelUnreadCount ch=%s not found", qPrintable(channelId)); |
|
} |
|
|
|
int ChannelList::getChannelUnreadCount(const QString& channelId) const { |
|
for (int row = 0; row < m_model->rowCount(); row++) { |
|
QStandardItem* item = m_model->item(row); |
|
if (item && item->data(ChannelChannelIdRole).toString() == channelId) |
|
return item->data(ChannelUnreadCountRole).toInt(); |
|
} |
|
return 0; |
|
} |
|
|
|
#include "channellist.moc"
|
|
|