zhuizhu vor 7 Monaten
Ursprung
Commit
581655942c

+ 1 - 0
LearningSmartClient.pro

@@ -84,6 +84,7 @@ HEADERS += \
     widgets/chatView/chat1/chatmessagedelegate.h \
     widgets/chatView/chat1/chatmessagemodel.h \
     widgets/chatView/chat1/chatview.h \
+    widgets/chatView/chat1/test_image_chat.h \
     widgets/chatView/chatwindow.h \
     widgets/chatView/independentchatwindow.h \
     widgets/colorlistwidget.h \

+ 27 - 0
widgets/chatView/chat1/chatmessage.h

@@ -4,6 +4,7 @@
 #include <QDateTime>
 #include <QObject>
 #include <QString>
+#include <QSize>
 
 namespace ChatConstants {
 static constexpr int AVATAR_SIZE = 40;
@@ -22,6 +23,13 @@ enum class MessageType {
     Private // 私聊消息
 };
 
+// 添加内容类型枚举
+enum class ContentType {
+    Text,   // 纯文本消息
+    Image,  // 图片消息
+    Mixed   // 混合内容(文本+图片)
+};
+
 class ChatMessage
 {
 public:
@@ -32,6 +40,10 @@ public:
     QString roomId;
     QString avatar;
     MessageType type = MessageType::Left;
+    ContentType contentType = ContentType::Text;  // 内容类型
+    QString imagePath;                            // 图片路径
+    QSize imageSize;                              // 图片尺寸
+    QString thumbnailPath;                        // 缩略图路径
     QDateTime timestamp = QDateTime::currentDateTime();
     bool isRead = false;
 
@@ -42,9 +54,24 @@ public:
         , type(type)
         , timestamp(timestamp)
     {}
+    
+    // 图片消息构造函数
+    ChatMessage(const QString &imagePath,
+                const QSize &imageSize,
+                MessageType type = MessageType::Left,
+                const QString &text = QString())
+        : text(text)
+        , type(type)
+        , contentType(text.isEmpty() ? ContentType::Image : ContentType::Mixed)
+        , imagePath(imagePath)
+        , imageSize(imageSize)
+    {}
     bool isLeft() const { return type == MessageType::Left; }
     bool isRight() const { return type == MessageType::Right; }
     bool isSystem() const { return type == MessageType::System; }
+    bool hasImage() const { return contentType == ContentType::Image || contentType == ContentType::Mixed; }
+    bool hasText() const { return contentType == ContentType::Text || contentType == ContentType::Mixed; }
+    bool isImageOnly() const { return contentType == ContentType::Image; }
 };
 
 Q_DECLARE_METATYPE(ChatMessage)

+ 130 - 20
widgets/chatView/chat1/chatmessagedelegate.cpp

@@ -100,8 +100,8 @@ void ChatMessageDelegate::paint(QPainter *painter,
     int nameHeight = message.senderName.isEmpty() ? 0 : nameFm.height();
     int nameSpacing = message.senderName.isEmpty() ? 0 : 2;
 
-    // 计算各个元素的位置
-    QSize textSize = calculateTextSize(option.fontMetrics, message.text);
+    // 计算内容大小(文本或图片)
+    QSize contentSize = calculateContentSize(message, option.fontMetrics);
 
     // 计算头像位置
     QRectF avatarRect;
@@ -123,15 +123,15 @@ void ChatMessageDelegate::paint(QPainter *painter,
     if (message.type == MessageType::Left) {
         bubbleRect = QRectF(avatarRect.right() + ChatConstants::BUBBLE_SPACING,
                             option.rect.top() + nameHeight + nameSpacing,
-                            textSize.width() + 2 * ChatConstants::BUBBLE_PADDING,
-                            textSize.height() + 2 * ChatConstants::BUBBLE_PADDING);
+                            contentSize.width() + 2 * ChatConstants::BUBBLE_PADDING,
+                            contentSize.height() + 2 * ChatConstants::BUBBLE_PADDING);
     } else {
-        bubbleRect = QRectF(option.rect.right() - textSize.width()
+        bubbleRect = QRectF(option.rect.right() - contentSize.width()
                                 - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE
                                 - 2 * ChatConstants::BUBBLE_SPACING,
                             option.rect.top() + nameHeight + nameSpacing,
-                            textSize.width() + 2 * ChatConstants::BUBBLE_PADDING,
-                            textSize.height() + 2 * ChatConstants::BUBBLE_PADDING);
+                            contentSize.width() + 2 * ChatConstants::BUBBLE_PADDING,
+                            contentSize.height() + 2 * ChatConstants::BUBBLE_PADDING);
     }
 
     // 绘制名字
@@ -165,28 +165,44 @@ void ChatMessageDelegate::paint(QPainter *painter,
     // 绘制头像
     drawAvatar(painter, avatarRect, message.avatar);
 
-    // 绘制文本(带选择高亮)
-    QRectF textRect = bubbleRect.adjusted(ChatConstants::BUBBLE_PADDING,
-                                          ChatConstants::BUBBLE_PADDING,
-                                          -ChatConstants::BUBBLE_PADDING,
-                                          -ChatConstants::BUBBLE_PADDING);
-    painter->setPen(ThemeManager::instance().color("colorText"));
-
+    // 绘制内容(文本、图片或混合内容)
+    QRectF contentRect = bubbleRect.adjusted(ChatConstants::BUBBLE_PADDING,
+                                             ChatConstants::BUBBLE_PADDING,
+                                             -ChatConstants::BUBBLE_PADDING,
+                                             -ChatConstants::BUBBLE_PADDING);
+    
     // 检查是否是当前选中的消息
     bool isCurrentMessage = (index == m_currentMessageIndex);
-    drawTextWithSelection(painter, textRect, message.text, option.fontMetrics, isCurrentMessage);
+    
+    if (message.hasImage() && message.hasText()) {
+        // 混合内容:图片在上,文字在下
+        QSize imageSize = calculateImageSize(message.imagePath, contentRect.width(), contentRect.height() * 0.7);
+        QRectF imageRect(contentRect.left(), contentRect.top(), imageSize.width(), imageSize.height());
+        QRectF textRect(contentRect.left(), imageRect.bottom() + 4, contentRect.width(), contentRect.height() - imageSize.height() - 4);
+        
+        drawImage(painter, imageRect, message.imagePath);
+        painter->setPen(ThemeManager::instance().color("colorText"));
+        drawTextWithSelection(painter, textRect, message.text, option.fontMetrics, isCurrentMessage);
+    } else if (message.hasImage()) {
+        // 纯图片消息
+        drawImage(painter, contentRect, message.imagePath);
+    } else {
+        // 纯文本消息
+        painter->setPen(ThemeManager::instance().color("colorText"));
+        drawTextWithSelection(painter, contentRect, message.text, option.fontMetrics, isCurrentMessage);
+    }
 }
 
 QSize ChatMessageDelegate::sizeHint(const QStyleOptionViewItem &option,
                                     const QModelIndex &index) const
 {
     const ChatMessage message = index.data().value<ChatMessage>();
-    QSize textSize = calculateTextSize(option.fontMetrics, message.text);
+    QSize contentSize = calculateContentSize(message, option.fontMetrics);
 
     // 系统消息特殊处理
     if (message.type == MessageType::System) {
-        int width = qMin(textSize.width() + 2 * ChatConstants::BUBBLE_PADDING, viewportWidth() / 2);
-        int height = textSize.height() + 2 * ChatConstants::BUBBLE_PADDING
+        int width = qMin(contentSize.width() + 2 * ChatConstants::BUBBLE_PADDING, viewportWidth() / 2);
+        int height = contentSize.height() + 2 * ChatConstants::BUBBLE_PADDING
                      + ChatConstants::TIMESTAMP_HEIGHT + 2 * ChatConstants::BUBBLE_SPACING;
         return QSize(width, height);
     }
@@ -198,9 +214,9 @@ QSize ChatMessageDelegate::sizeHint(const QStyleOptionViewItem &option,
     int nameHeight = message.senderName.isEmpty() ? 0 : nameFm.height();
     int nameSpacing = message.senderName.isEmpty() ? 0 : 2;
 
-    int width = textSize.width() + 2 * ChatConstants::BUBBLE_PADDING + ChatConstants::AVATAR_SIZE
+    int width = contentSize.width() + 2 * ChatConstants::BUBBLE_PADDING + ChatConstants::AVATAR_SIZE
                 + 2 * ChatConstants::BUBBLE_SPACING;
-    int height = qMax(textSize.height() + 2 * ChatConstants::BUBBLE_PADDING,
+    int height = qMax(contentSize.height() + 2 * ChatConstants::BUBBLE_PADDING,
                       ChatConstants::AVATAR_SIZE)
                  + ChatConstants::TIMESTAMP_HEIGHT
                  + nameHeight + nameSpacing;
@@ -465,3 +481,97 @@ int ChatMessageDelegate::getPositionFromPoint(const QPoint &pos,
     int position = layout->hitTest(pos, Qt::FuzzyHit);
     return position;
 }
+
+void ChatMessageDelegate::drawImage(QPainter *painter, const QRectF &rect, const QString &imagePath) const
+{
+    if (imagePath.isEmpty())
+        return;
+
+    // 检查缓存中是否已有图片
+    if (!m_imageCache.contains(imagePath)) {
+        QPixmap image(imagePath);
+        if (!image.isNull()) {
+            // 计算合适的缩放尺寸
+            QSize rectSize = rect.size().toSize();
+            QSize targetSize = image.size().scaled(rectSize.width(), rectSize.height(), Qt::KeepAspectRatio);
+            image = image.scaled(targetSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+            const_cast<ChatMessageDelegate *>(this)->m_imageCache.insert(imagePath, image);
+        }
+    }
+
+    if (m_imageCache.contains(imagePath)) {
+        const QPixmap &image = m_imageCache[imagePath];
+        // 居中绘制图片
+        QRectF imageRect = rect;
+        if (image.width() < rect.width()) {
+            imageRect.setX(rect.x() + (rect.width() - image.width()) / 2);
+            imageRect.setWidth(image.width());
+        }
+        if (image.height() < rect.height()) {
+            imageRect.setY(rect.y() + (rect.height() - image.height()) / 2);
+            imageRect.setHeight(image.height());
+        }
+        
+        painter->setRenderHint(QPainter::SmoothPixmapTransform);
+        painter->drawPixmap(imageRect.toRect(), image);
+        
+        // 绘制图片边框
+        painter->setPen(QPen(QColor(200, 200, 200), 1));
+        painter->setBrush(Qt::NoBrush);
+        painter->drawRoundedRect(imageRect, 4, 4);
+    } else {
+        // 绘制占位符
+        painter->setPen(Qt::NoPen);
+        painter->setBrush(QColor(240, 240, 240));
+        painter->drawRoundedRect(rect, 4, 4);
+        
+        painter->setPen(QColor(150, 150, 150));
+        painter->drawText(rect, Qt::AlignCenter, "图片加载失败");
+    }
+}
+
+QSize ChatMessageDelegate::calculateImageSize(const QString &imagePath, int maxWidth, int maxHeight) const
+{
+    if (imagePath.isEmpty())
+        return QSize(100, 100); // 默认尺寸
+
+    QPixmap image(imagePath);
+    if (image.isNull())
+        return QSize(100, 100);
+
+    QSize imageSize = image.size();
+    
+    // 限制最大尺寸
+    if (imageSize.width() > maxWidth || imageSize.height() > maxHeight) {
+        imageSize = imageSize.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio);
+    }
+    
+    // 限制最小尺寸
+    if (imageSize.width() < 50) imageSize.setWidth(50);
+    if (imageSize.height() < 50) imageSize.setHeight(50);
+    
+    return imageSize;
+}
+
+QSize ChatMessageDelegate::calculateContentSize(const ChatMessage &message, const QFontMetrics &fm) const
+{
+    if (message.hasImage() && message.hasText()) {
+        // 混合内容:图片 + 文本
+        int maxWidth = viewportWidth() - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE - 2 * ChatConstants::BUBBLE_SPACING;
+        
+        QSize imageSize = calculateImageSize(message.imagePath, maxWidth, 200);
+        QSize textSize = calculateTextSize(fm, message.text);
+        
+        int width = qMax(imageSize.width(), textSize.width());
+        int height = imageSize.height() + textSize.height() + 4; // 4px间距
+        
+        return QSize(width, height);
+    } else if (message.hasImage()) {
+        // 纯图片消息
+        int maxWidth = viewportWidth() - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE - 2 * ChatConstants::BUBBLE_SPACING;
+        return calculateImageSize(message.imagePath, maxWidth, 300);
+    } else {
+        // 纯文本消息
+        return calculateTextSize(fm, message.text);
+    }
+}

+ 5 - 0
widgets/chatView/chat1/chatmessagedelegate.h

@@ -81,8 +81,13 @@ private:
     void drawSystemMessage(QPainter *painter,
                            const QStyleOptionViewItem &option,
                            const ChatMessage &message) const;
+    // 添加图片渲染方法
+    void drawImage(QPainter *painter, const QRectF &rect, const QString &imagePath) const;
+    QSize calculateImageSize(const QString &imagePath, int maxWidth, int maxHeight) const;
+    QSize calculateContentSize(const ChatMessage &message, const QFontMetrics &fm) const;
 
     QHash<QString, QPixmap> m_avatarCache;
+    mutable QHash<QString, QPixmap> m_imageCache;  // 图片缓存
 
     int m_viewportWidth;
     TestWidget test;

+ 15 - 0
widgets/chatView/chat1/chatview.cpp

@@ -6,6 +6,7 @@
 #include <QApplication>
 #include <QClipboard>
 #include <QMouseEvent>
+#include <QPixmap>
 #include <QTimer>
 #include <utility>
 
@@ -286,6 +287,20 @@ void ChatView::addSystemMessage(const QString &text)
     scrollToBottom();
 }
 
+void ChatView::addImageMessage(const QString &imagePath, const QString &avatar, const QString &senderName, bool isLeft, const QString &text)
+{
+    // 获取图片尺寸
+    QPixmap image(imagePath);
+    QSize imageSize = image.isNull() ? QSize(100, 100) : image.size();
+    
+    ChatMessage message(imagePath, imageSize, isLeft ? MessageType::Left : MessageType::Right, text);
+    message.avatar = avatar;
+    message.senderName = senderName;
+    
+    m_model->addMessage(message);
+    scrollToBottom();
+}
+
 void ChatView::clear()
 {
     m_model->clear();

+ 1 - 0
widgets/chatView/chat1/chatview.h

@@ -11,6 +11,7 @@ class ChatView : public QListView
 public:
     explicit ChatView(QWidget *parent = nullptr);
     void addMessage(const QString &text, const QString &avatar, const QString &senderName, bool isLeft);
+    void addImageMessage(const QString &imagePath, const QString &avatar, const QString &senderName, bool isLeft, const QString &text = QString());
     void addSystemMessage(const QString &text);
     void clear();
 

+ 121 - 0
widgets/chatView/chat1/test_image_chat.h

@@ -0,0 +1,121 @@
+#include <QApplication>
+#include <QWidget>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QLineEdit>
+#include <QFileDialog>
+#include <QLabel>
+#include <QTimer>
+#include "chatview.h"
+
+class ImageChatTestWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    ImageChatTestWidget(QWidget *parent = nullptr)
+        : QWidget(parent)
+    {
+        setupUI();
+        connectSignals();
+        
+        // 添加一些测试消息
+        addTestMessages();
+    }
+
+private slots:
+    void sendTextMessage()
+    {
+        QString text = m_textEdit->text();
+        if (!text.isEmpty()) {
+            m_chatView->addMessage(text, ":/icons/user.png", "我", false);
+            m_textEdit->clear();
+            
+            // 模拟回复
+            QTimer::singleShot(1000, [this, text]() {
+                m_chatView->addMessage("收到:" + text, ":/icons/bot.png", "助手", true);
+            });
+        }
+    }
+    
+    void sendImageMessage()
+    {
+        QString imagePath = QFileDialog::getOpenFileName(this, 
+            "选择图片", 
+            "", 
+            "图片文件 (*.png *.jpg *.jpeg *.bmp *.gif)");
+            
+        if (!imagePath.isEmpty()) {
+            QString text = m_textEdit->text();
+            m_chatView->addImageMessage(imagePath, ":/icons/user.png", "我", false, text);
+            m_textEdit->clear();
+            
+            // 模拟回复
+            QTimer::singleShot(1000, [this]() {
+                m_chatView->addMessage("收到了你的图片!", ":/icons/bot.png", "助手", true);
+            });
+        }
+    }
+
+private:
+    void setupUI()
+    {
+        setWindowTitle("图片聊天测试");
+        resize(600, 800);
+        
+        QVBoxLayout *mainLayout = new QVBoxLayout(this);
+        
+        // 聊天视图
+        m_chatView = new ChatView(this);
+        mainLayout->addWidget(m_chatView);
+        
+        // 输入区域
+        QHBoxLayout *inputLayout = new QHBoxLayout();
+        
+        m_textEdit = new QLineEdit(this);
+        m_textEdit->setPlaceholderText("输入消息...");
+        inputLayout->addWidget(m_textEdit);
+        
+        m_sendTextBtn = new QPushButton("发送文字", this);
+        inputLayout->addWidget(m_sendTextBtn);
+        
+        m_sendImageBtn = new QPushButton("发送图片", this);
+        inputLayout->addWidget(m_sendImageBtn);
+        
+        mainLayout->addLayout(inputLayout);
+    }
+    
+    void connectSignals()
+    {
+        connect(m_sendTextBtn, &QPushButton::clicked, this, &ImageChatTestWidget::sendTextMessage);
+        connect(m_sendImageBtn, &QPushButton::clicked, this, &ImageChatTestWidget::sendImageMessage);
+        connect(m_textEdit, &QLineEdit::returnPressed, this, &ImageChatTestWidget::sendTextMessage);
+    }
+    
+    void addTestMessages()
+    {
+        m_chatView->addSystemMessage("欢迎使用图片聊天功能测试!");
+        m_chatView->addMessage("你好!这是一条普通的文本消息。", ":/icons/bot.png", "助手", true);
+        m_chatView->addMessage("你好!我可以发送图片了吗?", ":/icons/user.png", "我", false);
+        m_chatView->addMessage("当然可以!点击'发送图片'按钮选择图片文件。", ":/icons/bot.png", "助手", true);
+    }
+
+private:
+    ChatView *m_chatView;
+    QLineEdit *m_textEdit;
+    QPushButton *m_sendTextBtn;
+    QPushButton *m_sendImageBtn;
+};
+
+// #include "test_image_chat.moc"
+
+// int main(int argc, char *argv[])
+// {
+//     QApplication app(argc, argv);
+    
+//     ImageChatTestWidget widget;
+//     widget.show();
+    
+//     return app.exec();
+// }