Files
MyChat_Client/sessionfriendarea.cpp
2025-06-25 13:00:28 +08:00

410 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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, 25);
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<SessionFriendItem*>(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<SessionFriendItem*>(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(
"<span style=\"color: rgb(221, 43, 43);\">%1</span>"
"<span style=\"color: rgb(153, 153, 153);\">%2</span>"
).arg(coloredPart, escapedText));
}
}
void SessionItem::updateLastMessage(const QString& chatSessionId)
{
DataCenter* dataCenter = DataCenter::getInstance();
//先判定信号中的会话id得和当前元素自身持有放到会话id一致才真正处理
if (this->chatSessionId != chatSessionId) {
//当前SessionItem不是正在发消息的SessionItem
return;
}
//chatSession匹配真正更新最后一条消息
//把最后一条消息获取到
QList<Message>* 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(
"<span style=\"color: rgb(221, 43, 43);\">%1</span>"
"<span style=\"color: rgb(153, 153, 153);\">%2</span>"
).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<QGridLayout*>(this->layout());
layout->removeWidget(messageLabel);
//要记得释放内存,否则会内存泄露
delete messageLabel;
//创建两个按钮
QPushButton* acceptBtn = new QPushButton();
acceptBtn->setStyleSheet("QPushButton {color: rgb(0, 0, 0); } ");
acceptBtn->setText("同意");
QPushButton* rejectBtn = new QPushButton();
rejectBtn->setStyleSheet("QPushButton {color: rgb(0, 0, 0); } ");
rejectBtn->setText("拒绝");
//添加到布局管理器中
layout->addWidget(acceptBtn, 1, 2, 1, 3);
layout->addWidget(rejectBtn, 1, 5, 1, 3);
}
void ApplyItem::active()
{
//本就不需要实现任何内容
LOG() << "click ApplyItem... userId: " << userId;
}