#include "messageeditarea.h" #include "mainwidget.h" #include "soundrecorder.h" #include "model/datacenter.h" #include "toast.h" using namespace model; MessageEditArea::MessageEditArea(QWidget *parent) : QWidget{parent} { //设置必要的属性 this->setFixedHeight(250); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); //创建垂直方向的布局管理器 QVBoxLayout* vlayout = new QVBoxLayout(); vlayout->setSpacing(0); vlayout->setContentsMargins(0, 0, 30, 10); this->setLayout(vlayout); //创建水平方向的布局管理器 QHBoxLayout* hlayout = new QHBoxLayout(); hlayout->setSpacing(0); hlayout->setContentsMargins(10, 0, 0, 0); hlayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); vlayout->addLayout(hlayout); //将上方的四个按钮,创建好并添加到layout中 QString btnStyle = R"(QPushButton { background-color: rgb(245, 245, 245); border: none; } QPushButton:pressed {background-color: rgb(255, 255, 255); })"; QSize btnSize(35, 35); QSize iconSize(25, 25); sendImageBtn = new QPushButton(); sendImageBtn->setFixedSize(btnSize); sendImageBtn->setIconSize(iconSize); sendImageBtn->setIcon(QIcon(":/resource/image/image.png")); sendImageBtn->setStyleSheet(btnStyle); hlayout->addWidget(sendImageBtn); sendFileBtn = new QPushButton(); sendFileBtn->setFixedSize(btnSize); sendFileBtn->setIconSize(iconSize); sendFileBtn->setIcon(QIcon(":/resource/image/file.png")); sendFileBtn->setStyleSheet(btnStyle); hlayout->addWidget(sendFileBtn); sendSpeechBtn = new QPushButton(); sendSpeechBtn->setFixedSize(btnSize); sendSpeechBtn->setIconSize(iconSize); sendSpeechBtn->setIcon(QIcon(":/resource/image/sound.png")); sendSpeechBtn->setStyleSheet(btnStyle); hlayout->addWidget(sendSpeechBtn); showHistoryBtn = new QPushButton(); showHistoryBtn->setFixedSize(btnSize); showHistoryBtn->setIconSize(iconSize); showHistoryBtn->setIcon(QIcon(":/resource/image/history.png")); showHistoryBtn->setStyleSheet(btnStyle); hlayout->addWidget(showHistoryBtn); //添加多行编辑框 textEdit = new QPlainTextEdit(); textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); textEdit->setStyleSheet(R"( QPlainTextEdit { color: black; border: none; background-color: transparent; font-size: 14px; padding: 10px; } )"); textEdit->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; /* 滚动条背景区域透明 */ } )"); vlayout->addWidget(textEdit); //添加发送消息文本的按钮 sendTextButton = new QPushButton(); sendTextButton->setText("发送"); sendTextButton->setFixedSize(120, 40); //仿Wechat的标准样式 sendTextButton->setStyleSheet(R"(QPushButton { font-size: 16px; color: rgb(7, 193, 96); border: none; background-color: rgb(233, 233, 233); border-radius: 5px; } QPushButton:hover { background-color: rgb(210, 210, 210); } QPushButton:pressed { background-color: rgb(190, 190, 190); } )"); //// 创建阴影效果 // shadowEffect = new QGraphicsDropShadowEffect; // shadowEffect->setBlurRadius(12); // shadowEffect->setOffset(0, 5); // shadowEffect->setColor(QColor(0, 0, 0, 150)); // sendTextButton->setGraphicsEffect(shadowEffect); // //// 设置按钮样式表 // sendTextButton->setStyleSheet(R"( // QPushButton { // background: qlineargradient(spread:pad, // x1:0, y1:0, x2:1, y2:0, // stop:0 #9b59b6, // stop:1 #8e44ad); // border-radius: 10px; // color: white; // font: bold 14px; // border: none; // margin: 5px; // box-shadow: 0 5px 15px rgba(0,0,0,0.3); // } // QPushButton:hover { // background: qlineargradient(spread:pad, // x1:0, y1:0, x2:1, y2:0, // stop:0 #e67e22, // stop:1 #d35400); // box-shadow: 0 7px 20px rgba(0,0,0,0.4); // } // QPushButton:pressed { // background: qlineargradient(spread:pad, // x1:0, y1:0, x2:1, y2:0, // stop:0 #e74c3c, // stop:1 #c0392b); // } //)"); // // // 连接按钮的按下和释放事件 // connect(sendTextButton, &QPushButton::pressed, [=]() { // QPropertyAnimation* anim = new QPropertyAnimation(shadowEffect, "offset"); // anim->setDuration(100); // anim->setStartValue(shadowEffect->offset()); // anim->setEndValue(QPointF(0, 1)); // anim->start(QAbstractAnimation::DeleteWhenStopped); // }); // // connect(sendTextButton, &QPushButton::released, [=]() { // QPropertyAnimation* anim = new QPropertyAnimation(shadowEffect, "offset"); // anim->setDuration(150); // anim->setStartValue(shadowEffect->offset()); // anim->setEndValue(QPointF(0, sendTextButton->underMouse() ? 3 : 5)); // 根据是否悬停决定最终位置 // anim->setEasingCurve(QEasingCurve::OutBack); // anim->start(QAbstractAnimation::DeleteWhenStopped); // }); // // // 安装事件过滤器 // sendTextButton->installEventFilter(this); vlayout->addWidget(sendTextButton, 0, Qt::AlignRight | Qt::AlignVCenter); //添加录制中 tipLabel = new QLabel(); tipLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); tipLabel->setText("录音中..."); tipLabel->setAlignment(Qt::AlignCenter); tipLabel->setFont(QFont("微软雅黑", 24, 600)); vlayout->addWidget(tipLabel); tipLabel->hide(); //统一初始化信号槽 initSignalSlot(); } void MessageEditArea::initSignalSlot() { DataCenter* dataCenter = DataCenter::getInstance(); //关联“显示历史消息窗口”信号槽 connect(showHistoryBtn, &QPushButton::clicked, this, [=]() { if (dataCenter->getCurrentSessionId().isEmpty()) { return; } HistoryMessageWidget* historyMessageWidget = new HistoryMessageWidget(this); historyMessageWidget->exec(); }); //关联“发送文本消息”信号槽 connect(sendTextButton, &QPushButton::clicked, this, &MessageEditArea::sendTextMessage); connect(dataCenter, &DataCenter::sendMessageDone, this, &MessageEditArea::addSelfMessage); //关联收到消息的信号槽 connect(dataCenter, &DataCenter::receiveMessageDone, this, &MessageEditArea::addOtherMessage); //关联发送图片 connect(sendImageBtn, &QPushButton::clicked, this, &MessageEditArea::clickSendImageBtn); //关联发送文件 connect(sendFileBtn, &QPushButton::clicked, this, &MessageEditArea::clickSendFileBtn); //关联发送语音 connect(sendSpeechBtn, &QPushButton::pressed, this, &MessageEditArea::soundRecordPressed); connect(sendSpeechBtn, &QPushButton::released, this, &MessageEditArea::soundRecordReleased); SoundRecorder* soundRecorder = SoundRecorder::getInstance(); connect(soundRecorder, &SoundRecorder::soundRecordDone, this, &MessageEditArea::soundSpeech); } void MessageEditArea::sendTextMessage() { //先确认当前是否有会话被选中 DataCenter* dataCenter = DataCenter::getInstance(); if (dataCenter->getCurrentSessionId().isEmpty()) { LOG() << "当前未选中任何会话,不发送任何消息"; //上述日志只是在开发阶段能看到,程序发布出去,就无法看到了 //因此需要让普通用户也能看到提示 Toast::showMessage("当前未选中会话,不发送任何消息"); return; } //获取到输入框的内容,没输入,则不做任何操作 const QString& content = textEdit->toPlainText().trimmed(); if (content.isEmpty()) { LOG() << "输入框为空"; return; } //清空输入框已有的内容 textEdit->setPlainText(""); //通过网络发送数据给服务器 dataCenter->sendTextMessageAsync(dataCenter->getCurrentSessionId(), content); } //针对自己发送消息的操作,做处理,把自己发送的消息显示到界面上 void MessageEditArea::addSelfMessage(MessageType messageType, const QByteArray& content, const QString& extraInfo) { DataCenter* dataCenter = DataCenter::getInstance(); const QString& currentChatSessionId = dataCenter->getCurrentSessionId(); //构造出一个消息对象 Message message = Message::makeMessage(messageType, currentChatSessionId, *dataCenter->getMyselfsync(), content, extraInfo); dataCenter->addMessage(message); //把这个新的消息,显示到消息展示区 MainWidget* mainWidget = MainWidget::getInstance(); MessageShowArea* messageShowArea = mainWidget->getMessageShowArea(); messageShowArea->addMessage(false, message); //控制消息显示区,滚动条,滚动到末尾 messageShowArea->scrollToEnd(); //发送信号,通知会话列表,更新最后一条消息 emit dataCenter->updateLastMessage(currentChatSessionId); } void MessageEditArea::addOtherMessage(const model::Message& message) { //通过主界面,拿到消息展示区 MainWidget* mainWidget = MainWidget::getInstance(); MessageShowArea* messageShowArea = mainWidget->getMessageShowArea(); //把收到的新消息,添加到消息展示区 messageShowArea->addMessage(true, message); //控制消息展示区的滚动条,把窗口滚动到末尾 messageShowArea->scrollToEnd(); //提示一个收到消息 Toast::showMessage("收到新消息!"); } void MessageEditArea::clickSendImageBtn() { DataCenter* dataCenter = DataCenter::getInstance(); //判定当前是否有选中的会话 if (dataCenter->getCurrentSessionId().isEmpty()) { //没有选中会话 LOG() << "当前尚未选中任何会话"; return; } //弹出文件对话框 QString filter = "Image Files (*.png *.jpg *.jpeg *.mp4)"; QString imagePath = QFileDialog::getOpenFileName(this, "选择图片", QDir::homePath(), filter); if (imagePath.isEmpty()) { LOG() << "用户取消选择"; return; } //读取图片内容 QByteArray imageContent = loadFileToByteArray(imagePath); //发送请求 dataCenter->sendImageMessageAsync(dataCenter->getCurrentSessionId(), imageContent); } void MessageEditArea::clickSendFileBtn() { DataCenter* dataCenter = DataCenter::getInstance(); //判定当前是否有选中的会话 if (dataCenter->getCurrentSessionId().isEmpty()) { //没有选中会话 LOG() << "当前尚未选中任何会话"; return; } //弹出对话框,选择文件 QString filter = "*"; QString path = QFileDialog::getOpenFileName(this, "选择文件", QDir::homePath(), filter); if (path.isEmpty()) { LOG() << "用户取消选择"; return; } //读取文件内容(暂时没有考虑非常大的文件) //如果针对大文件,编写专门的网络通信接口,实现分片传输效果 QByteArray content = loadFileToByteArray(path); //传输文件,还需要获取到文件名 QFileInfo fileInfo(path); const QString& filename = fileInfo.fileName(); //发送消息 dataCenter->sendFileMessageAsync(dataCenter->getCurrentSessionId(), filename, content); } void MessageEditArea::soundRecordPressed() { DataCenter* dataCenter = DataCenter::getInstance(); //判定当前是否有选中的会话 if (dataCenter->getCurrentSessionId().isEmpty()) { //没有选中会话 LOG() << "当前尚未选中任何会话"; return; } //切换语音按钮的图标 sendSpeechBtn->setIcon(QIcon(":/resource/image/sound_active.png")); //开始录音 SoundRecorder* soundRecorder = SoundRecorder::getInstance(); soundRecorder->startRecord(); tipLabel->show(); textEdit->hide(); } void MessageEditArea::soundRecordReleased() { DataCenter* dataCenter = DataCenter::getInstance(); //判定当前是否有选中的会话 if (dataCenter->getCurrentSessionId().isEmpty()) { //没有选中会话 LOG() << "当前尚未选中任何会话"; return; } //切换语音按钮的图标 sendSpeechBtn->setIcon(QIcon(":/resource/image/sound.png")); //开始录音 SoundRecorder* soundRecorder = SoundRecorder::getInstance(); tipLabel->hide(); textEdit->show(); soundRecorder->stopRecord(); } void MessageEditArea::soundSpeech(const QString& path) { DataCenter* dataCenter = DataCenter::getInstance(); QByteArray content = loadFileToByteArray(path); if (content.isEmpty()) { LOG() << "语音文件加载失败"; return; } dataCenter->sendSpeechMessageAsync(dataCenter->getCurrentSessionId(), content); } //bool MessageEditArea::eventFilter(QObject* obj, QEvent* event) //{ // if (obj == sendTextButton) { // if (event->type() == QEvent::Enter) { // // 鼠标进入事件 // QPropertyAnimation* hoverAnim = new QPropertyAnimation(shadowEffect, "offset"); // hoverAnim->setDuration(200); // hoverAnim->setStartValue(shadowEffect->offset()); // hoverAnim->setEndValue(QPointF(0, 3)); // hoverAnim->start(QAbstractAnimation::DeleteWhenStopped); // return true; // } // else if (event->type() == QEvent::Leave) { // // 鼠标离开事件 // QPropertyAnimation* hoverAnim = new QPropertyAnimation(shadowEffect, "offset"); // hoverAnim->setDuration(200); // hoverAnim->setStartValue(shadowEffect->offset()); // hoverAnim->setEndValue(QPointF(0, 5)); // hoverAnim->start(QAbstractAnimation::DeleteWhenStopped); // return true; // } // } // return QObject::eventFilter(obj, event); //}