#include "messageshowarea.h" #include "mainwidget.h" #include "userinfowidget.h" #include 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 //测试长消息 model::UserInfo userInfo; userInfo.nickname = "???"; userInfo.userId = "4862"; userInfo.phone = "00000000000"; userInfo.description = "A test description."; 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); bool k = true; for(int i = 0; i < 8; i++) { model::UserInfo userInfo; userInfo.userId = QString::number(i); userInfo.phone = "12345678900"; userInfo.description = "A test description."; 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); } #endif } void MessageShowArea::addFrontMessage(bool isLeft, const Message &message) { MessageItem* messageItem = MessageItem::makeMessageItem(isLeft, message); QVBoxLayout* layout = dynamic_cast(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(); } } } void MessageShowArea::scrollToEnd() { //拿到滚动区域的滚动条 //获取到滚动条的最大值并设置 //为了使滚动到正确的位置,即能够在界面绘制好后进行滚动设置 //这里选择给滚动条加个延时 QTimer* timer = new QTimer(); connect(timer, &QTimer::timeout, this, [=]() { //获取到垂直滚动条的最大值 int maxValue = this->verticalScrollBar()->maximum(); //设置滚动条滚动的位置 this->verticalScrollBar()->setValue(maxValue); timer->stop(); timer->deleteLater(); }); timer->start(500); } //////////////////////////////////////////// /// 表示一个消息元素 //////////////////////////////////////////// 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); avatarBtn->setStyleSheet("QPushButton { border: none; }"); 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); } //连接信号槽,处理用户点击头像的操作 connect(avatarBtn, &QPushButton::clicked, messageItem, [=]() { MainWidget* mainWidget = MainWidget::getInstance(); UserInfoWidget* userInfoWidget = new UserInfoWidget(message.sender, mainWidget); userInfoWidget->setStyleSheet("QDialog { background-color: rgb(245, 245, 245); }"); userInfoWidget->exec(); }); 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(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); }