#include "sessionfriendarea.h" #include "debug.h" #include "model/data.h" #include "model/datacenter.h" #include "mainwidget.h" using namespace model; SessionFriendArea::SessionFriendArea(QWidget *parent) : QScrollArea {parent} { //设置必要属性 //设置这个才能有滚动效果 this->setWidgetResizable(true); //设置滚动条相关样式 // this->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 2px; background-color: rgb(46, 46, 46); }"); //去除或隐藏水平滚动条 // this->horizontalScrollBar()->setStyleSheet("QScrollBar:horizontal { height: 0px; }"); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->verticalScrollBar()->setStyleSheet(R"( QScrollBar:vertical { background-color: rgb(228, 228, 228); /* 滚动条背景透明 */ width: 6px; /* 默认宽度 */ margin: 0px 0px 0px 0px; /* 边距清零 */ } QScrollBar::handle:vertical { background: #CCCCCC; /* 滑块颜色(浅灰色) */ min-height: 30px; /* 滑块最小高度 */ border-radius: 3px; /* 圆角 */ } QScrollBar::handle:vertical:hover { background: #999999; /* 悬停时滑块颜色加深 */ width: 10px; /* 悬停时宽度增大 */ } QScrollBar::handle:vertical:pressed { background: #666666; /* 按下时颜色更深 */ } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; /* 隐藏上下箭头按钮 */ background: none; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; /* 滚动条背景区域透明 */ } )"); //去除默认的黑边 this->setStyleSheet("border: 0px solid transparent;"); // 把widget创建出来 container = new QWidget(); container->setMinimumWidth(310); this->setWidget(container); //给这个widget添加布局管理器方便后续添加元素进去 QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->setAlignment(Qt::AlignTop); container->setLayout(layout); // //简单测试一下滚动的效果(just for testing...) // for(int i = 0; i < 50; i++) { // QPushButton* btn = new QPushButton(); // QString Sbtn = "button: " + QString::number(i); // btn->setText(Sbtn); // layout->addWidget(btn); // } //构造一些临时数据作为界面调试的依据 #if TEST_UI QIcon icon(":/resource/image/defaultAv.png"); for(int i = 0; i < 30; i++) { this->addItem(SessionItemType, QString::number(i), icon, "张三" + QString::number(i), "test: last message..." + QString::number(i)); } // LOG() << "hello world!"; #endif } SessionFriendItem::SessionFriendItem(QWidget* owner, const QIcon& avatar, const QString& name, const QString& text) :owner(owner) { this->setFixedHeight(70); this->setStyleSheet("QWidget { background-color: rgb(228, 228, 228); }"); //创建网格布局管理器 QGridLayout* layout = new QGridLayout(); layout->setContentsMargins(20, 0, 0, 0); // layout->setSpacing(0); layout->setHorizontalSpacing(10); layout->setVerticalSpacing(0); this->setLayout(layout); //创建头像 QPushButton* avatarBtn = new QPushButton(); avatarBtn->setFixedSize(50, 50); avatarBtn->setIconSize(QSize(50, 50)); avatarBtn->setIcon(avatar); avatarBtn->setStyleSheet("QPushButton {border: none;} "); avatarBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); //创建名字 QLabel* nameLabel = new QLabel(); nameLabel->setText(name); nameLabel->setStyleSheet("QLabel { font-size: 18px; font-weight: 600; }"); nameLabel->setFixedHeight(35); nameLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); //创建消息预览的label messageLabel = new QLabel(); messageLabel->setText(text); messageLabel->setStyleSheet("QLabel { font-size: 12px; font-weight: 600; color: rgb(153, 153, 153); }"); messageLabel->setFixedHeight(35); messageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); //添加到网格布局 layout->addWidget(avatarBtn, 0, 0, 2, 2); layout->addWidget(nameLabel, 0, 2, 1, 20); layout->addWidget(messageLabel, 1, 2, 1, 25); } void SessionFriendItem::paintEvent(QPaintEvent *event) { (void) event; QStyleOption opt; opt.initFrom(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void SessionFriendItem::mousePressEvent(QMouseEvent *event) { (void) event; select(); } void SessionFriendItem::enterEvent(QEnterEvent *event) { (void) event; if(this->selected) { return; } this->setStyleSheet("QWidget { background-color: rgb(215, 215, 215); }"); } void SessionFriendItem::leaveEvent(QEvent *event) { (void) event; if(this->selected) { return; } this->setStyleSheet("QWidget { background-color: rgb(228, 228, 228); }"); } void SessionFriendItem::select() { //遍历其他元素(还原背景色) const QObjectList children = this->parentWidget()->children(); for(QObject* child : children) { if(!child->isWidgetType()) { continue; } //说明是widget 那就将child强转为SessionFriendItem SessionFriendItem* item = dynamic_cast(child); if(item->selected) { item->selected = false; item->setStyleSheet("QWidget { background-color: rgb(228, 228, 228); }"); } } //鼠标点击时会触发这个事件 //点击时修改背景色 this->setStyleSheet("QWidget { background-color: rgb(196, 196, 196); }"); this->selected = true; //调用Active this->active(); } void SessionFriendItem::active() { //父类不写 //并不需要实现任何逻辑 } void SessionFriendArea::clear() { QLayout* layout = container->layout(); //遍历布局管理器中的所有元素,依次从布局管理器中删除 for(int i = layout->count() - 1; i >= 0; i--) { //takeAt就能够移除对应下标的元素,但还需要释放对象 QLayoutItem* item = layout->takeAt(i); //进行释放 if(item->widget()) { //把这个移除内容的widget进行释放 delete item->widget(); } } } ////////////////////////////////////////////////////////////////////////////////// //此时这个函数添加的就不是SessionFriendItem了,而是其子类 //SessionItem FriendItem ApplyItem void SessionFriendArea::addItem(ItemType itemType, const QString& id, const QIcon &avatar, const QString &name, const QString &text) { // //构造一个Item // SessionFriendItem* Item = new SessionFriendItem(this, avatar, name, text); // //将Item添加到container中 // container->layout()->addWidget(Item); SessionFriendItem* item = nullptr; switch (itemType) { case SessionItemType: item = new SessionItem(this, id, avatar, name, text); break; case FriendItemType: item = new FriendItem(this, id, avatar, name, text); break; case ApplyItemType: item = new ApplyItem(this, id, avatar, name); break; default: LOG() << "Error ItemType: " << itemType; return; } container->layout()->addWidget(item); } void SessionFriendArea::clickItem(int index) { if(index < 0 || index > container->layout()->count()) { LOG() << "The subscript of the clicked element is out of range... index:" << index; return; } QLayoutItem* layoutItem = container->layout()->itemAt(index); if(layoutItem == nullptr || layoutItem->widget() == nullptr) { LOG() << "The specified element does not exist... index: " << index; return; } SessionFriendItem* item = dynamic_cast(layoutItem->widget()); item->select(); } //////////////////////////////////////// /// 会话Item的实现 //////////////////////////////////////// SessionItem::SessionItem(QWidget *owner, const QString &chatSessionId, const QIcon &avatar, const QString &name, const QString &lastmessage) :SessionFriendItem(owner, avatar, name, lastmessage), chatSessionId(chatSessionId), text(lastmessage) { //处理更新最后一个消息的信号 DataCenter* dataCenter = DataCenter::getInstance(); connect(dataCenter, &DataCenter::updateLastMessage, this, &SessionItem::updateLastMessage); //需要显示出未读消息的数目,需持久化,否则重启即丢失 int unread = dataCenter->getUnread(chatSessionId); if (unread > 0) { //说明存在未读消息 //this->messageLabel->setText(QString("[未读%1条]").arg(unread) + lastmessage); QString coloredPart = QString("[未读%1条]").arg(unread).toHtmlEscaped(); QString escapedText = lastmessage.toHtmlEscaped(); messageLabel->setText(QString( "%1" "%2" ).arg(coloredPart, escapedText)); } } void SessionItem::updateLastMessage(const QString& chatSessionId) { DataCenter* dataCenter = DataCenter::getInstance(); //先判定,信号中的会话id得和当前元素自身持有放到会话id一致,才真正处理 if (this->chatSessionId != chatSessionId) { //当前SessionItem不是正在发消息的SessionItem return; } //chatSession匹配,真正更新最后一条消息 //把最后一条消息获取到 QList* messageList = dataCenter->getRecentMessageList(chatSessionId); if (messageList == nullptr || messageList->size() == 0) { //当前会话没有任何消息,无需更新 return; } const Message& lastMessage = messageList->back(); //明确显示的文本内容 if (lastMessage.messageType == TEXT_TYPE) { text = lastMessage.content; } else if (lastMessage.messageType == IMAGE_TYPE) { text = "[图片]"; } else if (lastMessage.messageType == FILE_TYPE) { text = "[文件]"; } else if (lastMessage.messageType == SPEECH_TYPE) { text = "[语音]"; } else { LOG() << "错误的消息类型"; return; } //把这个内容显示到界面上 //后续还要考虑到“未读消息”情况, if (chatSessionId == dataCenter->getCurrentSessionId()) { this->messageLabel->setText(text); } else { int unread = dataCenter->getUnread(chatSessionId); if (unread > 0) { //说明存在未读消息 //this->messageLabel->setText(QString("[未读%1条]").arg(unread) + text); QString coloredPart = QString("[未读%1条]").arg(unread).toHtmlEscaped(); QString escapedText = text.toHtmlEscaped(); messageLabel->setText(QString( "%1" "%2" ).arg(coloredPart, escapedText)); } } } void SessionItem::active() { //点击之后,要加载会话历史消息的列表 LOG() << "click SessionItem... chatSessionId: " << chatSessionId; //加载会话历史消息,会涉及到当前内存数据的操作,又会涉及到网络通信,以及UI界面的变更 MainWidget* mainWidget = MainWidget::getInstance(); mainWidget->loadRecentMessage(chatSessionId); //清空未读消息数据,并更新显示 DataCenter* dataCenter = DataCenter::getInstance(); dataCenter->clearUnread(chatSessionId); //更新界面的显示->把未读x条干掉 this->messageLabel->setText(text); } //////////////////////////////////////// /// 好友Item的实现 //////////////////////////////////////// FriendItem::FriendItem(QWidget *owner, const QString &userId, const QIcon &avatar, const QString &name, const QString &description) :SessionFriendItem(owner, avatar, name, description), userId(userId) { } void FriendItem::active() { //点击之后,要激活对应的会话列元素 LOG() << "click FriendItem... userId: " << userId; MainWidget* mainWidget = MainWidget::getInstance(); mainWidget->switchSession(userId); } //////////////////////////////////////// /// 好友申请Item的实现 //////////////////////////////////////// ApplyItem::ApplyItem(QWidget *owner, const QString &userId, const QIcon &avatar, const QString &name) :SessionFriendItem(owner, avatar, name, ""), userId(userId) { //移除父类的messageLabel QGridLayout* layout = dynamic_cast(this->layout()); layout->removeWidget(messageLabel); //要记得释放内存,否则会内存泄露 delete messageLabel; //创建两个按钮 QPushButton* acceptBtn = new QPushButton(); acceptBtn->setStyleSheet("QPushButton {color: rgb(255, 255, 255); background-color: rgb(7, 193, 96); border: none; border-radius: 5px; } "); acceptBtn->setText("同意"); acceptBtn->setFixedSize(40, 20); QPushButton* rejectBtn = new QPushButton(); rejectBtn->setStyleSheet("QPushButton {color: rgb(250, 81, 81); background-color: rgb(190, 190, 190); border: none; border-radius: 5px; } "); rejectBtn->setText("拒绝"); rejectBtn->setFixedSize(40, 20); //添加到布局管理器中 layout->addWidget(acceptBtn, 1, 2, 1, 4); layout->addWidget(rejectBtn, 1, 6, 1, 4); //添加信号槽 connect(acceptBtn, &QPushButton::clicked, this, &ApplyItem::acceptFriendApply); connect(rejectBtn, &QPushButton::clicked, this, &ApplyItem::rejectFriendApply); } void ApplyItem::acceptFriendApply() { //发送网络请求,告知服务器,同意了好友的申请 DataCenter* dataCenter = DataCenter::getInstance(); //看同意了谁的好友申请 dataCenter->acceptFriendApplyAsync(this->userId); } void ApplyItem::rejectFriendApply() { DataCenter* dataCenter = DataCenter::getInstance(); dataCenter->rejectFriendApplyAsync(this->userId); } void ApplyItem::active() { //本就不需要实现任何内容 LOG() << "click ApplyItem... userId: " << userId; }