#include "chatwindow.h" #include "appevent.h" #include "widgets/chatView/chat1/chatview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // MultiFormatInputEdit 实现 MultiFormatInputEdit::MultiFormatInputEdit(QWidget *parent) : QTextEdit(parent) { setAcceptDrops(true); setPlaceholderText("输入消息... (支持粘贴图片)"); setMaximumHeight(120); setMinimumHeight(60); // 连接文本变化信号 connect(this, &QTextEdit::textChanged, this, &MultiFormatInputEdit::onTextChanged); } QString MultiFormatInputEdit::getPlainText() const { // 获取纯文本,但保留图片占位符信息 QString text = toPlainText().trimmed(); return text; } QStringList MultiFormatInputEdit::getImagePaths() const { return m_imagePaths; } void MultiFormatInputEdit::insertImage(const QString &imagePath) { if (imagePath.isEmpty() || m_imagePaths.contains(imagePath)) return; m_imagePaths.append(imagePath); insertImageIntoDocument(imagePath); emit imageInserted(imagePath); emit contentChanged(); } void MultiFormatInputEdit::clearContent() { clear(); m_imagePaths.clear(); m_imageResourceMap.clear(); document()->clear(); emit contentChanged(); } bool MultiFormatInputEdit::validateContent() const { QString text = getPlainText(); // 检查是否有内容 if (text.isEmpty() && m_imagePaths.isEmpty()) { return false; } // 检查文本长度 if (text.length() > 5000) { return false; } // 检查图片文件是否存在 for (const QString &imagePath : m_imagePaths) { if (!QFileInfo::exists(imagePath)) { return false; } } return true; } void MultiFormatInputEdit::removeImage(const QString &imagePath) { if (!m_imagePaths.contains(imagePath)) return; m_imagePaths.removeAll(imagePath); // 从文档中移除图片资源 if (m_imageResourceMap.contains(imagePath)) { QString resourceName = m_imageResourceMap[imagePath]; document()->addResource(QTextDocument::ImageResource, QUrl(resourceName), QVariant()); m_imageResourceMap.remove(imagePath); } emit imageRemoved(imagePath); emit contentChanged(); } void MultiFormatInputEdit::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) { event->acceptProposedAction(); } else { QTextEdit::dragEnterEvent(event); } } void MultiFormatInputEdit::dropEvent(QDropEvent *event) { const QMimeData *mimeData = event->mimeData(); if (mimeData->hasUrls()) { const QList urls = mimeData->urls(); for (const QUrl &url : urls) { if (url.isLocalFile()) { QString filePath = url.toLocalFile(); if (isImageFormat(filePath)) { insertImage(filePath); } } } event->acceptProposedAction(); } else if (mimeData->hasImage()) { QPixmap pixmap = qvariant_cast(mimeData->imageData()); if (!pixmap.isNull()) { QString tempPath = saveImageToTemp(pixmap); if (!tempPath.isEmpty()) { insertImage(tempPath); } } event->acceptProposedAction(); } else { QTextEdit::dropEvent(event); } } void MultiFormatInputEdit::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { if (event->modifiers() & Qt::ControlModifier) { // Ctrl+Enter 发送消息 emit sendRequested(); return; } else { // 普通回车换行 QTextEdit::keyPressEvent(event); } } else { QTextEdit::keyPressEvent(event); } } void MultiFormatInputEdit::insertFromMimeData(const QMimeData *source) { if (source->hasImage()) { QPixmap pixmap = qvariant_cast(source->imageData()); if (!pixmap.isNull()) { QString tempPath = saveImageToTemp(pixmap); if (!tempPath.isEmpty()) { insertImage(tempPath); } } } else { QTextEdit::insertFromMimeData(source); } } void MultiFormatInputEdit::contextMenuEvent(QContextMenuEvent *event) { QMenu *menu = createStandardContextMenu(); // 检查是否点击在图片上 QTextCursor cursor = cursorForPosition(event->pos()); QTextCharFormat format = cursor.charFormat(); if (format.isImageFormat()) { QTextImageFormat imageFormat = format.toImageFormat(); QString imageName = imageFormat.name(); // 查找对应的图片路径 QString imagePath; for (auto it = m_imageResourceMap.begin(); it != m_imageResourceMap.end(); ++it) { if (it.value() == imageName) { imagePath = it.key(); break; } } if (!imagePath.isEmpty()) { menu->addSeparator(); QAction *removeAction = menu->addAction("删除图片"); connect(removeAction, &QAction::triggered, [this, imagePath]() { removeImage(imagePath); }); } } menu->exec(event->globalPos()); delete menu; } void MultiFormatInputEdit::onTextChanged() { emit contentChanged(); } void MultiFormatInputEdit::processImageData(const QByteArray &imageData, const QString &format) { QPixmap pixmap; if (pixmap.loadFromData(imageData, format.toUtf8().constData())) { QString tempPath = saveImageToTemp(pixmap, format); if (!tempPath.isEmpty()) { insertImage(tempPath); } } } bool MultiFormatInputEdit::isImageFormat(const QString &fileName) const { QStringList imageFormats = {"png", "jpg", "jpeg", "gif", "bmp", "tiff", "webp"}; QString suffix = QFileInfo(fileName).suffix().toLower(); return imageFormats.contains(suffix); } QString MultiFormatInputEdit::saveImageToTemp(const QPixmap &pixmap, const QString &format) { QString tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation); QDir().mkpath(tempDir); QString fileName = QString("chat_image_%1.%2") .arg(QDateTime::currentMSecsSinceEpoch()) .arg(format.toLower()); QString filePath = QDir(tempDir).absoluteFilePath(fileName); if (pixmap.save(filePath, format.toUtf8().constData())) { return filePath; } return QString(); } void MultiFormatInputEdit::insertImageIntoDocument(const QString &imagePath) { QPixmap pixmap(imagePath); if (pixmap.isNull()) return; // 缩放图片以适应输入框 int maxWidth = width() - 20; // 留出边距 int maxHeight = 80; // 限制高度 if (pixmap.width() > maxWidth || pixmap.height() > maxHeight) { pixmap = pixmap.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); } // 生成唯一的资源名称 QString resourceName = generateResourceName(imagePath); m_imageResourceMap[imagePath] = resourceName; // 将图片添加到文档资源 document()->addResource(QTextDocument::ImageResource, QUrl(resourceName), pixmap); // 在光标位置插入图片 QTextCursor cursor = textCursor(); QTextImageFormat imageFormat; imageFormat.setName(resourceName); imageFormat.setWidth(pixmap.width()); imageFormat.setHeight(pixmap.height()); cursor.insertImage(imageFormat); cursor.insertText(" "); // 在图片后添加空格,方便编辑 setTextCursor(cursor); } QString MultiFormatInputEdit::generateResourceName(const QString &imagePath) { return QString("image_%1_%2").arg(QUuid::createUuid().toString(QUuid::WithoutBraces)) .arg(QFileInfo(imagePath).baseName()); } // ChatWindow 实现 ChatWindow::ChatWindow(WebSocketClient *webSocketClient, QWidget *parent) : QWidget(parent) , m_webSocketClient(webSocketClient) { setupUI(); connectSignals(); } void ChatWindow::setupUI() { setWindowTitle("聊天窗口"); resize(800, 600); QVBoxLayout *mainLayout = new QVBoxLayout(this); // 消息显示区域 m_messageView = new ChatView(this); mainLayout->addWidget(m_messageView, 1); setupInputArea(); } void ChatWindow::setupInputArea() { // 输入区域容器 QWidget *inputContainer = new QWidget; QVBoxLayout *inputLayout = new QVBoxLayout(inputContainer); inputLayout->setContentsMargins(5, 5, 5, 5); inputLayout->setSpacing(5); // 输入框 m_inputEdit = new MultiFormatInputEdit(this); inputLayout->addWidget(m_inputEdit); // 按钮区域 QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); m_imageButton = new QPushButton("图片", this); m_fileButton = new QPushButton("文件", this); m_sendButton = new QPushButton("发送", this); buttonLayout->addWidget(m_imageButton); buttonLayout->addWidget(m_fileButton); buttonLayout->addWidget(m_sendButton); inputLayout->addLayout(buttonLayout); // 添加到主布局 layout()->addWidget(inputContainer); // 隐藏文件按钮(暂未实现) m_fileButton->hide(); } void ChatWindow::connectSignals() { // 输入框信号 connect(m_inputEdit, &MultiFormatInputEdit::sendRequested, this, &ChatWindow::onSendClicked); connect(m_inputEdit, &MultiFormatInputEdit::contentChanged, this, &ChatWindow::onInputContentChanged); connect(m_inputEdit, &MultiFormatInputEdit::imageInserted, this, &ChatWindow::onImageInserted); // 按钮信号 connect(m_sendButton, &QPushButton::clicked, this, &ChatWindow::onSendClicked); connect(m_imageButton, &QPushButton::clicked, this, &ChatWindow::onImageClicked); connect(m_fileButton, &QPushButton::clicked, this, &ChatWindow::onFileClicked); // WebSocket信号 if (m_webSocketClient) { connect(m_webSocketClient, &WebSocketClient::messageReceived, [this](const ChatMessage &message) { if (message.hasImage()) { // 处理图片消息 if (message.isLeft()) { m_messageView->addImageMessage(message.imagePath, message.avatar, message.senderName, true, message.text); } else { m_messageView->addImageMessage(message.imagePath, message.avatar, message.senderName, false, message.text); } } else { // 处理纯文本消息 if (message.isLeft()) { m_messageView->addMessage(message.text, message.avatar, message.senderName, true); } else { m_messageView->addMessage(message.text, message.avatar, message.senderName, false); } } }); connect(m_webSocketClient, &WebSocketClient::connectionStateChanged, [this](bool connected) { if (connected) { m_messageView->addSystemMessage("已连接到服务器"); } else { m_messageView->addSystemMessage("与服务器断开连接"); } }); } } void ChatWindow::initWebsocket(const QString &roomId) { m_roomId = roomId; if (m_webSocketClient) { m_webSocketClient->connectToRoom(roomId.isEmpty() ? "default_room" : roomId); } } void ChatWindow::onSendClicked() { if (validateInput()) { sendMessage(); } } void ChatWindow::onImageClicked() { QString fileName = QFileDialog::getOpenFileName( this, "选择图片", "", "图片文件 (*.png *.jpg *.jpeg *.bmp *.gif *.tiff *.webp)" ); if (!fileName.isEmpty()) { m_inputEdit->insertImage(fileName); } } void ChatWindow::onFileClicked() { // 文件发送功能待实现 QMessageBox::information(this, "提示", "文件发送功能暂未实现"); } void ChatWindow::onRecallClicked() { // 撤回消息功能待实现 } void ChatWindow::onInputContentChanged() { // 更新发送按钮状态 m_sendButton->setEnabled(m_inputEdit->validateContent()); } void ChatWindow::onImageInserted(const QString &imagePath) { Q_UNUSED(imagePath) // 图片已直接显示在输入框中,无需额外处理 } void ChatWindow::sendMessage() { QString text = m_inputEdit->getPlainText(); QStringList imagePaths = m_inputEdit->getImagePaths(); // 发送文本消息 if (!text.isEmpty()) { m_messageView->addMessage(text, "", "我", false); if (m_webSocketClient) { m_webSocketClient->sendMessage(text); } } // 发送图片消息 for (const QString &imagePath : imagePaths) { sendImageMessage(imagePath, text); } // 清空输入框 m_inputEdit->clearContent(); } void ChatWindow::sendImageMessage(const QString &imagePath, const QString &text) { if (QFileInfo::exists(imagePath)) { m_messageView->addImageMessage(imagePath, "", "我", false, text); if (m_webSocketClient) { m_webSocketClient->sendImageMessage(imagePath, text); } } } bool ChatWindow::validateInput() const { return m_inputEdit->validateContent(); } void ChatWindow::closeEvent(QCloseEvent *event) { event->ignore(); emit windowCloseRequested(); }