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

#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 &current, 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"