Files
MyChat_Client/messageshowarea.cpp
2025-05-29 21:31:28 +08:00

259 lines
8.5 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 "messageshowarea.h"
MessageShowArea::MessageShowArea() {
//初始化基本属性
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
this->setWidgetResizable(true);
//设置滚动条样式
this->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 2px; background-color: rgb(240, 240, 240); }");
this->horizontalScrollBar()->setStyleSheet("QScrollBar:horizontal { height: 0; }");
this->setStyleSheet("QScrollArea { border: none; }");
//创建container这样的widget作为包含内部元素的容器
container = new QWidget();
this->setWidget(container);
//给container添加界面布局管理器
QVBoxLayout* layout = new QVBoxLayout();
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
container->setLayout(layout);
// 添加测试数据
#if TEST_UI
bool k = true;
for(int i = 0; i < 8; i++) {
model::UserInfo userInfo;
userInfo.nickname = "xyz" + QString::number(i);
userInfo.avatar = QIcon(":/resource/image/defaultAvatar.png");
Message message = Message::makeMessage(model::TEXT_TYPE, "", userInfo, (QString("this is a test message...") + QString::number(i)).toUtf8(), "");
k = !k;
this->addMessage(k, message);
}
//测试长消息
model::UserInfo userInfo;
userInfo.nickname = "???";
const QString s = R"(The starry sky is just a few years ago. And the past may no longer exist,The only thing that remains is light years away, it's just an ethereal phantom.)";
userInfo.avatar = QIcon(":/resource/image/defaultAvatar.png");
Message message = Message::makeMessage(model::TEXT_TYPE, "", userInfo, s.toUtf8(), "");
this->addMessage(false, message);
#endif
}
void MessageShowArea::addFrontMessage(bool isLeft, const Message &message)
{
MessageItem* messageItem = MessageItem::makeMessageItem(isLeft, message);
QVBoxLayout* layout = dynamic_cast<QVBoxLayout*>(container->layout());
layout->insertWidget(0, messageItem);
}
void MessageShowArea::addMessage(bool isLeft, const Message &message)
{
MessageItem* messageItem = MessageItem::makeMessageItem(isLeft, message);
container->layout()->addWidget(messageItem);
}
void MessageShowArea::clear()
{
QLayout* layout = container->layout();
//要遍历布局管理器,删除里面的元素
for(int i = layout->count() - 1; i >= 0; i--) {
QLayoutItem* item = layout->takeAt(i);
if(item != nullptr && item->widget() != nullptr) {
delete item->widget();
}
}
}
////////////////////////////////////////////
/// 表示一个消息元素
////////////////////////////////////////////
MessageItem::MessageItem(bool isLeft)
:isLeft(isLeft)
{
}
MessageItem *MessageItem::makeMessageItem(bool isLeft, const Message &message)
{
//创建对象和布局管理器
MessageItem* messageItem = new MessageItem(isLeft);
QGridLayout* layout = new QGridLayout();
layout->setSpacing(10);
layout->setContentsMargins(30, 10, 40, 0);
//这个message最低不能低于100
messageItem->setMinimumHeight(100);
messageItem->setLayout(layout);
//创建头像
QPushButton* avatarBtn = new QPushButton();
avatarBtn->setFixedSize(40, 40);
avatarBtn->setIconSize(QSize(40, 40));
avatarBtn->setIcon(message.sender.avatar);
if(isLeft) {
layout->addWidget(avatarBtn, 0, 0, 2, 1, Qt::AlignCenter | Qt::AlignLeft);
} else {
layout->addWidget(avatarBtn, 0, 1, 2, 1, Qt::AlignCenter | Qt::AlignRight);
}
//创建名字和时间
QLabel* nameLabel = new QLabel();
nameLabel->setText(message.sender.nickname + " | " + message.time);
nameLabel->setAlignment(Qt::AlignBottom);
nameLabel->setStyleSheet("QLabel { font-size: 12px; color: rgb(178, 178, 178); }");
if(isLeft) {
layout->addWidget(nameLabel, 0, 1);
} else {
layout->addWidget(nameLabel, 0, 0, Qt::AlignRight);
}
//创建消息体
QWidget* contentWidget = nullptr;
switch(message.messageType) {
case model::TEXT_TYPE:
contentWidget = makeTextMessageItem(isLeft, message.content);
break;
case model::IMAGE_TYPE:
contentWidget = makeImageMessageItem();
break;
case model::FILE_TYPE:
contentWidget = makeFileMessageItem();
break;
case model::SPEECH_TYPE:
contentWidget = makeSpeechMessageItem();
break;
default:
LOG() << "error messageType: " << message.messageType;
}
if(isLeft) {
layout->addWidget(contentWidget, 1, 1);
} else {
layout->addWidget(contentWidget, 1, 0);
}
return messageItem;
}
QWidget *MessageItem::makeTextMessageItem(bool isLeft, const QString &text)
{
MessageContentLabel* messageContentLabel = new MessageContentLabel(text, isLeft);
return messageContentLabel;
}
QWidget *MessageItem::makeImageMessageItem()
{
return nullptr;
}
QWidget *MessageItem::makeFileMessageItem()
{
return nullptr;
}
QWidget *MessageItem::makeSpeechMessageItem()
{
return nullptr;
}
////////////////////////////////////////////
/// 创建类表示“文本消息”正文部分
////////////////////////////////////////////
MessageContentLabel::MessageContentLabel(const QString &text, bool isLeft)
:isLeft(isLeft)
{
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QFont font;
font.setFamily("微软雅黑");
font.setPixelSize(16);
this->label = new QLabel(this);
this->label->setText(text);
this->label->setFont(font);
this->label->setAlignment(Qt::AlignCenter | Qt::AlignLeft);
this->label->setWordWrap(true);
this->label->setStyleSheet("QLabel { padding: 0 10px; line-height: 1.2; background-color: transparent; }");
}
//这个函数会在该控件被显示时,自动的调用到
void MessageContentLabel::paintEvent(QPaintEvent *event)
{
(void) event;
//获取到父元素的宽度
QObject* object = this->parent();
if(!object->isWidgetType()) {
//说明当前的对象不是QWidget则不需要进行任何后续的绘制操作
return;
}
QWidget* parent = dynamic_cast<QWidget*>(object);
int width = parent->width() * 0.6;
//计算当前文本,如果单行防止放置需要多宽
QFontMetrics metrics(this->label->font());
int totalWidth = metrics.horizontalAdvance(this->label->text());
//计算出此处的行数是多少
int rows = (totalWidth / (width - 40)) + 1;
if(rows == 1) {
//若此时得到的行数只有一行
width = totalWidth + 40;
}
//根据行数来确定高度
//行数 × 行高(字体高度的 1.2 倍) + 上下内边距(各 10px
int height = rows * (this->label->font().pixelSize() * 1.2 ) + 20;
//绘制圆角矩形和箭头
QPainter painter(this);
QPainterPath path;
//设置抗锯齿
painter.setRenderHint(QPainter::Antialiasing);
if(isLeft) {
painter.setPen(QPen(QColor(255, 255, 255)));
painter.setBrush(QColor(255, 255, 255)); //白色填充
//绘制圆角矩形
painter.drawRoundedRect(10, 0, width, height, 10 ,10);
//绘制箭头
path.moveTo(10, 15);
path.lineTo(0, 20);
path.lineTo(10, 25);
path.closeSubpath(); //绘制的线形成闭合的多边形才能进行使用Brush填充颜色
painter.drawPath(path);//设置好,调用画笔进行绘制操作
this->label->setGeometry(10, 0, width, height);
} else {
painter.setPen(QPen(QColor(137, 217, 97)));
painter.setBrush(QColor(137, 217, 97));
//圆角矩形左侧边的横坐标位置
int leftPos = this->width() - width - 10; //10 用来容纳箭头的宽度
//圆角矩形右侧边的横坐标位置
int rightPos = this->width() - 10;
//绘制圆角矩形
painter.drawRoundedRect(leftPos, 0, width, height, 10, 10);
//绘制箭头
path.moveTo(rightPos, 15);
path.lineTo(rightPos + 10, 20);
path.lineTo(rightPos, 25);
path.closeSubpath(); //绘制的线形成闭合的多边形才能进行使用Brush填充颜色
painter.drawPath(path);//设置好,调用画笔进行绘制操作
this->label->setGeometry(leftPos, 0, width, height);
}
//重新设置父元素的高度,保证父元素足够的高,能够容纳下上述绘制消息的显示区域
//注意高度要涵盖之前的名字和时间的label的高度以及留一点的冗余的空间
parent->setFixedHeight(height + 50);
}