mirror of
https://gitee.com/Zhaoxin59/my-chat_-client.git
synced 2026-02-13 16:41:48 +08:00
424 lines
15 KiB
C++
424 lines
15 KiB
C++
#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);
|
||
//}
|