chatmessagedelegate.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. #include "chatmessagedelegate.h"
  2. #include "chatmessage.h"
  3. #include "thememanager.h"
  4. #include <QAbstractTextDocumentLayout>
  5. #include <QFontMetrics>
  6. #include <QPainter>
  7. #include <QPainterPath>
  8. #include <QTextDocument>
  9. #include <QTextLayout>
  10. ChatMessageDelegate::ChatMessageDelegate(QObject *parent)
  11. : QStyledItemDelegate(parent)
  12. , m_viewportWidth(600)
  13. {
  14. test.setWindowTitle("test ChatMessageDelegate");
  15. //test.show();
  16. }
  17. int ChatMessageDelegate::hitTestText(const QPoint &pos, const QString &text) const
  18. {
  19. QFont fn;
  20. QFontMetrics fm(fn);
  21. if (text.isEmpty() || pos.x() < 0 || pos.y() < 0)
  22. return -1;
  23. int maxWidth = viewportWidth() - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE
  24. - 2 * ChatConstants::BUBBLE_SPACING;
  25. // 使用QTextLayout计算文本布局
  26. QTextLayout textLayout(text);
  27. textLayout.setFont(QFont());
  28. QTextOption option;
  29. option.setWrapMode(QTextOption::WordWrap);
  30. option.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
  31. textLayout.setTextOption(option);
  32. // 计算文本布局
  33. textLayout.beginLayout();
  34. int y = 0;
  35. int lineIndex = 0;
  36. QVector<QTextLine> lines;
  37. while (true) {
  38. QTextLine line = textLayout.createLine();
  39. if (!line.isValid())
  40. break;
  41. line.setLineWidth(maxWidth);
  42. int lineHeight = line.height();
  43. line.setPosition(QPointF(0, y));
  44. lines.append(line);
  45. y += lineHeight;
  46. lineIndex++;
  47. }
  48. textLayout.endLayout();
  49. // 查找点击位置对应的行和字符
  50. for (int i = 0; i < lines.size(); ++i) {
  51. QTextLine line = lines[i];
  52. QRectF lineRect(0, line.y(), line.width(), line.height());
  53. if (pos.y() >= lineRect.top() && pos.y() <= lineRect.bottom()) {
  54. // 找到了行,现在找字符位置
  55. int xPos = qBound(0, pos.x(), int(line.width()));
  56. return line.xToCursor(xPos);
  57. }
  58. }
  59. // 如果点击在最后一行之后,返回文本末尾
  60. if (pos.y() > y && !lines.isEmpty())
  61. return text.length();
  62. return -1;
  63. }
  64. void ChatMessageDelegate::paint(QPainter *painter,
  65. const QStyleOptionViewItem &option,
  66. const QModelIndex &index) const
  67. {
  68. painter->setRenderHint(QPainter::Antialiasing);
  69. painter->setRenderHint(QPainter::TextAntialiasing);
  70. const ChatMessage message = index.data().value<ChatMessage>();
  71. // 系统消息特殊处理
  72. if (message.type == MessageType::System) {
  73. drawSystemMessage(painter, option, message);
  74. return;
  75. }
  76. // 计算名字高度
  77. QFont nameFont = option.font;
  78. nameFont.setPointSize(std::max(option.font.pointSize() - 2, 8));
  79. QFontMetrics nameFm(nameFont);
  80. int nameHeight = message.senderName.isEmpty() ? 0 : nameFm.height();
  81. int nameSpacing = message.senderName.isEmpty() ? 0 : 2;
  82. // 计算内容大小(文本或图片)
  83. QSize contentSize = calculateContentSize(message, option.fontMetrics);
  84. // 计算头像位置
  85. QRectF avatarRect;
  86. if (message.type == MessageType::Left) {
  87. avatarRect = QRectF(option.rect.left() + ChatConstants::BUBBLE_SPACING,
  88. option.rect.top() + nameHeight + nameSpacing,
  89. ChatConstants::AVATAR_SIZE,
  90. ChatConstants::AVATAR_SIZE);
  91. } else {
  92. avatarRect = QRectF(option.rect.right() - ChatConstants::AVATAR_SIZE
  93. - ChatConstants::BUBBLE_SPACING,
  94. option.rect.top() + nameHeight + nameSpacing,
  95. ChatConstants::AVATAR_SIZE,
  96. ChatConstants::AVATAR_SIZE);
  97. }
  98. // 计算气泡位置
  99. QRectF bubbleRect;
  100. if (message.type == MessageType::Left) {
  101. bubbleRect = QRectF(avatarRect.right() + ChatConstants::BUBBLE_SPACING,
  102. option.rect.top() + nameHeight + nameSpacing,
  103. contentSize.width() + 2 * ChatConstants::BUBBLE_PADDING,
  104. contentSize.height() + 2 * ChatConstants::BUBBLE_PADDING);
  105. } else {
  106. bubbleRect = QRectF(option.rect.right() - contentSize.width()
  107. - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE
  108. - 2 * ChatConstants::BUBBLE_SPACING,
  109. option.rect.top() + nameHeight + nameSpacing,
  110. contentSize.width() + 2 * ChatConstants::BUBBLE_PADDING,
  111. contentSize.height() + 2 * ChatConstants::BUBBLE_PADDING);
  112. }
  113. // 绘制名字
  114. if (!message.senderName.isEmpty()) {
  115. painter->save();
  116. painter->setFont(nameFont);
  117. painter->setPen(QColor(120, 120, 120));
  118. int nameX, nameWidth;
  119. QRect nameRect;
  120. if (message.type == MessageType::Left) {
  121. // 左侧消息,名字左对齐,紧贴头像右侧
  122. nameX = avatarRect.right() + ChatConstants::BUBBLE_SPACING;
  123. nameWidth = bubbleRect.width();
  124. nameRect = QRect(nameX, option.rect.top(), nameWidth, nameHeight);
  125. painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, message.senderName);
  126. } else {
  127. // 右侧消息,名字右对齐,紧贴头像左侧
  128. nameWidth = nameFm.horizontalAdvance(message.senderName);
  129. // 名字宽度不超过气泡宽度
  130. nameWidth = std::min(nameWidth, static_cast<int>(bubbleRect.width()));
  131. nameX = bubbleRect.right() - nameWidth;
  132. nameRect = QRect(nameX, option.rect.top(), nameWidth, nameHeight);
  133. painter->drawText(nameRect, Qt::AlignRight | Qt::AlignVCenter, message.senderName);
  134. }
  135. painter->restore();
  136. }
  137. // 绘制气泡
  138. drawBubble(painter, bubbleRect, message.type == MessageType::Left);
  139. // 绘制头像
  140. drawAvatar(painter, avatarRect, message.avatar);
  141. // 绘制内容(文本、图片或混合内容)
  142. QRectF contentRect = bubbleRect.adjusted(ChatConstants::BUBBLE_PADDING,
  143. ChatConstants::BUBBLE_PADDING,
  144. -ChatConstants::BUBBLE_PADDING,
  145. -ChatConstants::BUBBLE_PADDING);
  146. // 检查是否是当前选中的消息
  147. bool isCurrentMessage = (index == m_currentMessageIndex);
  148. if (message.hasImage() && message.hasText()) {
  149. // 混合内容:图片在上,文字在下
  150. QSize imageSize = calculateImageSize(message.imagePath, contentRect.width(), contentRect.height() * 0.7);
  151. QRectF imageRect(contentRect.left(), contentRect.top(), imageSize.width(), imageSize.height());
  152. QRectF textRect(contentRect.left(), imageRect.bottom() + 4, contentRect.width(), contentRect.height() - imageSize.height() - 4);
  153. drawImage(painter, imageRect, message.imagePath);
  154. painter->setPen(ThemeManager::instance().color("colorText"));
  155. drawTextWithSelection(painter, textRect, message.text, option.fontMetrics, isCurrentMessage);
  156. } else if (message.hasImage()) {
  157. // 纯图片消息
  158. drawImage(painter, contentRect, message.imagePath);
  159. } else {
  160. // 纯文本消息
  161. painter->setPen(ThemeManager::instance().color("colorText"));
  162. drawTextWithSelection(painter, contentRect, message.text, option.fontMetrics, isCurrentMessage);
  163. }
  164. }
  165. QSize ChatMessageDelegate::sizeHint(const QStyleOptionViewItem &option,
  166. const QModelIndex &index) const
  167. {
  168. const ChatMessage message = index.data().value<ChatMessage>();
  169. QSize contentSize = calculateContentSize(message, option.fontMetrics);
  170. // 系统消息特殊处理
  171. if (message.type == MessageType::System) {
  172. int width = qMin(contentSize.width() + 2 * ChatConstants::BUBBLE_PADDING, viewportWidth() / 2);
  173. int height = contentSize.height() + 2 * ChatConstants::BUBBLE_PADDING
  174. + ChatConstants::TIMESTAMP_HEIGHT + 2 * ChatConstants::BUBBLE_SPACING;
  175. return QSize(width, height);
  176. }
  177. // 计算名字高度
  178. QFont nameFont = option.font;
  179. nameFont.setPointSize(std::max(option.font.pointSize() - 2, 8));
  180. QFontMetrics nameFm(nameFont);
  181. int nameHeight = message.senderName.isEmpty() ? 0 : nameFm.height();
  182. int nameSpacing = message.senderName.isEmpty() ? 0 : 2;
  183. int width = contentSize.width() + 2 * ChatConstants::BUBBLE_PADDING + ChatConstants::AVATAR_SIZE
  184. + 2 * ChatConstants::BUBBLE_SPACING;
  185. int height = qMax(contentSize.height() + 2 * ChatConstants::BUBBLE_PADDING,
  186. ChatConstants::AVATAR_SIZE)
  187. + ChatConstants::TIMESTAMP_HEIGHT
  188. + nameHeight + nameSpacing;
  189. return QSize(qMin(width, viewportWidth()), height + ChatConstants::BUBBLE_SPACING);
  190. }
  191. void ChatMessageDelegate::setViewportWidth(int width)
  192. {
  193. m_viewportWidth = width - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE
  194. - 2 * ChatConstants::BUBBLE_SPACING;
  195. }
  196. void ChatMessageDelegate::drawBubble(QPainter *painter, const QRectF &rect, bool isLeft) const
  197. {
  198. QPainterPath path;
  199. path.addRoundedRect(rect, ChatConstants::BUBBLE_RADIUS, ChatConstants::BUBBLE_RADIUS);
  200. // 添加小三角
  201. const int triangleSize = 6;
  202. if (isLeft) {
  203. path.moveTo(rect.left(), rect.top() + ChatConstants::AVATAR_SIZE / 2 - triangleSize);
  204. path.lineTo(rect.left() - triangleSize, rect.top() + ChatConstants::AVATAR_SIZE / 2);
  205. path.lineTo(rect.left(), rect.top() + ChatConstants::AVATAR_SIZE / 2 + triangleSize);
  206. } else {
  207. path.moveTo(rect.right(), rect.top() + ChatConstants::AVATAR_SIZE / 2 - triangleSize);
  208. path.lineTo(rect.right() + triangleSize, rect.top() + ChatConstants::AVATAR_SIZE / 2);
  209. path.lineTo(rect.right(), rect.top() + ChatConstants::AVATAR_SIZE / 2 + triangleSize);
  210. }
  211. // 设置气泡颜色
  212. if (isLeft) {
  213. painter->fillPath(path, ThemeManager::instance().color("colorFillQuaternary"));
  214. } else {
  215. painter->fillPath(path, ThemeManager::instance().color("colorPrimaryBg"));
  216. }
  217. }
  218. void ChatMessageDelegate::drawAvatar(QPainter *painter,
  219. const QRectF &rect,
  220. const QString &avatarPath) const
  221. {
  222. // 检查缓存中是否已有头像
  223. if (!m_avatarCache.contains(avatarPath)) {
  224. QPixmap avatar(avatarPath);
  225. if (!avatar.isNull()) {
  226. avatar = avatar.scaled(ChatConstants::AVATAR_SIZE,
  227. ChatConstants::AVATAR_SIZE,
  228. Qt::KeepAspectRatio,
  229. Qt::SmoothTransformation);
  230. const_cast<ChatMessageDelegate *>(this)->m_avatarCache.insert(avatarPath, avatar);
  231. }
  232. }
  233. // 创建圆形裁剪区域
  234. QPainterPath path;
  235. path.addEllipse(rect);
  236. painter->setClipPath(path);
  237. if (m_avatarCache.contains(avatarPath)) {
  238. // 绘制缓存的头像
  239. painter->drawPixmap(rect.toRect(), m_avatarCache[avatarPath]);
  240. } else {
  241. // 绘制默认头像
  242. painter->setPen(Qt::NoPen);
  243. painter->setBrush(ThemeManager::instance().color("colorFill"));
  244. painter->drawEllipse(rect);
  245. }
  246. painter->setClipping(false);
  247. }
  248. void ChatMessageDelegate::drawSystemMessage(QPainter *painter,
  249. const QStyleOptionViewItem &option,
  250. const ChatMessage &message) const
  251. {
  252. // 为系统消息单独计算文本大小
  253. int maxWidth = viewportWidth() / (2.0 / 3.0);
  254. // 使用QTextDocument计算系统消息的实际大小
  255. QFont font = painter->font();
  256. int size = font.pointSize();
  257. font.setPointSize(std::max(size - 2, 8));
  258. QTextDocument doc;
  259. doc.setDefaultFont(font);
  260. doc.setDocumentMargin(0);
  261. doc.setTextWidth(maxWidth - 2 * ChatConstants::BUBBLE_PADDING);
  262. doc.setHtml(message.text.toHtmlEscaped().replace("\n", "<br>"));
  263. QSize textSize(doc.idealWidth(), doc.size().height());
  264. // 系统消息居中显示
  265. int bubbleWidth = qMin(textSize.width() + 2 * ChatConstants::BUBBLE_PADDING, maxWidth);
  266. // 计算气泡位置(居中)
  267. QRectF bubbleRect(option.rect.left() + (option.rect.width() - bubbleWidth) / 2,
  268. option.rect.top() + ChatConstants::BUBBLE_SPACING,
  269. bubbleWidth,
  270. textSize.height() + 2 * ChatConstants::BUBBLE_PADDING);
  271. // 绘制系统消息气泡(使用特殊样式)
  272. QPainterPath path;
  273. path.addRoundedRect(bubbleRect, ChatConstants::BUBBLE_RADIUS, ChatConstants::BUBBLE_RADIUS);
  274. // 使用半透明灰色背景
  275. painter->fillPath(path, QColor(200, 200, 200, 60));
  276. // 绘制文本
  277. QRectF textRect = bubbleRect.adjusted(ChatConstants::BUBBLE_PADDING,
  278. ChatConstants::BUBBLE_PADDING,
  279. -ChatConstants::BUBBLE_PADDING,
  280. -ChatConstants::BUBBLE_PADDING);
  281. // 使用特殊颜色
  282. painter->setPen(QColor(255, 0, 0));
  283. QTextCharFormat defaultFormat;
  284. defaultFormat.setForeground(ThemeManager::instance().color("colorText"));
  285. QTextCursor cursor(&doc);
  286. cursor.select(QTextCursor::Document);
  287. cursor.mergeCharFormat(defaultFormat);
  288. painter->save();
  289. painter->translate(textRect.topLeft());
  290. doc.drawContents(painter);
  291. painter->restore();
  292. // // // 绘制时间戳
  293. // QRectF timestampRect(bubbleRect.left(),
  294. // bubbleRect.bottom(),
  295. // bubbleRect.width(),
  296. // ChatConstants::TIMESTAMP_HEIGHT);
  297. // painter->setPen(ThemeManager::instance().color("colorTextSecondary"));
  298. // painter->setFont(QFont(option.font.family(), option.font.pointSize() - 2));
  299. // painter->drawText(timestampRect, Qt::AlignCenter, message.timestamp.toString("HH:mm"));
  300. }
  301. // 添加绘制带选择的文本方法
  302. void ChatMessageDelegate::drawTextWithSelection(QPainter *painter,
  303. const QRectF &rect,
  304. const QString &text,
  305. const QFontMetrics &fm,
  306. bool isCurrentMessage) const
  307. {
  308. // 如果不是当前消息或没有选择,直接绘制文本
  309. // 使用QTextDocument绘制多行文本
  310. QTextDocument doc;
  311. doc.setDefaultFont(painter->font());
  312. doc.setDocumentMargin(0);
  313. doc.setTextWidth(rect.width());
  314. doc.setHtml(text.toHtmlEscaped().replace("\n", "<br>"));
  315. // 设置默认文本格式(应用主题颜色)
  316. QTextCharFormat defaultFormat;
  317. defaultFormat.setForeground(ThemeManager::instance().color("colorText"));
  318. if (!isCurrentMessage || m_selectionStart < 0 || m_selectionEnd < 0
  319. || m_selectionStart == m_selectionEnd) {
  320. // 将普通文本转换为HTML,保留换行符
  321. QString htmlText = text.toHtmlEscaped().replace("\n", "<br>");
  322. doc.setHtml(htmlText);
  323. // 应用默认格式到整个文档
  324. QTextCursor cursor(&doc);
  325. cursor.select(QTextCursor::Document);
  326. cursor.mergeCharFormat(defaultFormat);
  327. painter->save();
  328. painter->translate(rect.topLeft());
  329. painter->setPen(ThemeManager::instance().color("colorText"));
  330. doc.drawContents(painter);
  331. painter->restore();
  332. return;
  333. }
  334. // 确保选择范围有效
  335. int start = qMin(m_selectionStart, m_selectionEnd);
  336. int end = qMax(m_selectionStart, m_selectionEnd);
  337. if (start >= text.length() || end <= 0) {
  338. QString htmlText = text.toHtmlEscaped().replace("\n", "<br>");
  339. doc.setHtml(htmlText);
  340. // 应用默认格式到整个文档
  341. QTextCursor cursor(&doc);
  342. cursor.select(QTextCursor::Document);
  343. cursor.mergeCharFormat(defaultFormat);
  344. painter->save();
  345. painter->translate(rect.topLeft());
  346. painter->setPen(ThemeManager::instance().color("colorText"));
  347. doc.drawContents(painter);
  348. painter->restore();
  349. return;
  350. }
  351. // 限制选择范围在文本长度内
  352. start = qMax(0, start);
  353. end = qMin(text.length(), end);
  354. // 将普通文本转换为HTML,保留换行符
  355. QString htmlText = text.toHtmlEscaped().replace("\n", "<br>");
  356. doc.setHtml(htmlText);
  357. // 首先应用默认格式到整个文档
  358. QTextCursor cursor(&doc);
  359. cursor.select(QTextCursor::Document);
  360. cursor.mergeCharFormat(defaultFormat);
  361. // 设置选择格式
  362. cursor.setPosition(start);
  363. cursor.setPosition(end, QTextCursor::KeepAnchor);
  364. QTextCharFormat selectionFormat;
  365. selectionFormat.setBackground(QColor(0, 120, 215, 128)); // 半透明蓝色背景
  366. selectionFormat.setForeground(Qt::white); // 白色文本
  367. cursor.mergeCharFormat(selectionFormat);
  368. // 绘制文本
  369. painter->save();
  370. painter->translate(rect.topLeft());
  371. doc.drawContents(painter);
  372. painter->restore();
  373. }
  374. QSize ChatMessageDelegate::calculateTextSize(const QFontMetrics &fm, const QString &text) const
  375. {
  376. int maxWidth = viewportWidth() - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE
  377. - 2 * ChatConstants::BUBBLE_SPACING;
  378. QRect textRect = fm.boundingRect(0,
  379. 0,
  380. maxWidth,
  381. INT_MAX,
  382. Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignVCenter,
  383. text);
  384. return textRect.size();
  385. }
  386. int ChatMessageDelegate::getPositionFromPoint(const QPoint &pos,
  387. const QString &text,
  388. const QFontMetrics &fm)
  389. {
  390. if (text.isEmpty() || pos.x() < 0 || pos.y() < 0)
  391. return -1;
  392. int maxWidth = viewportWidth() - 2 * ChatConstants::BUBBLE_PADDING;
  393. // 使用QTextDocument处理多行文本
  394. QTextDocument doc;
  395. doc.setDefaultFont(QFont());
  396. doc.setDocumentMargin(0);
  397. doc.setTextWidth(maxWidth - 52); // AVATAR_SIZE + BUBBLE_PADDING
  398. doc.setHtml(text.toHtmlEscaped().replace("\n", "<br>"));
  399. test.setDocument(&doc);
  400. // 获取点击位置对应的文本位置
  401. QAbstractTextDocumentLayout *layout = doc.documentLayout();
  402. int position = layout->hitTest(pos, Qt::FuzzyHit);
  403. return position;
  404. }
  405. void ChatMessageDelegate::drawImage(QPainter *painter, const QRectF &rect, const QString &imagePath) const
  406. {
  407. if (imagePath.isEmpty())
  408. return;
  409. // 检查缓存中是否已有图片
  410. if (!m_imageCache.contains(imagePath)) {
  411. QPixmap image(imagePath);
  412. if (!image.isNull()) {
  413. // 计算合适的缩放尺寸
  414. QSize rectSize = rect.size().toSize();
  415. QSize targetSize = image.size().scaled(rectSize.width(), rectSize.height(), Qt::KeepAspectRatio);
  416. image = image.scaled(targetSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
  417. const_cast<ChatMessageDelegate *>(this)->m_imageCache.insert(imagePath, image);
  418. }
  419. }
  420. if (m_imageCache.contains(imagePath)) {
  421. const QPixmap &image = m_imageCache[imagePath];
  422. // 居中绘制图片
  423. QRectF imageRect = rect;
  424. if (image.width() < rect.width()) {
  425. imageRect.setX(rect.x() + (rect.width() - image.width()) / 2);
  426. imageRect.setWidth(image.width());
  427. }
  428. if (image.height() < rect.height()) {
  429. imageRect.setY(rect.y() + (rect.height() - image.height()) / 2);
  430. imageRect.setHeight(image.height());
  431. }
  432. painter->setRenderHint(QPainter::SmoothPixmapTransform);
  433. painter->drawPixmap(imageRect.toRect(), image);
  434. // 绘制图片边框
  435. painter->setPen(QPen(QColor(200, 200, 200), 1));
  436. painter->setBrush(Qt::NoBrush);
  437. painter->drawRoundedRect(imageRect, 4, 4);
  438. } else {
  439. // 绘制占位符
  440. painter->setPen(Qt::NoPen);
  441. painter->setBrush(QColor(240, 240, 240));
  442. painter->drawRoundedRect(rect, 4, 4);
  443. painter->setPen(QColor(150, 150, 150));
  444. painter->drawText(rect, Qt::AlignCenter, "图片加载失败");
  445. }
  446. }
  447. QSize ChatMessageDelegate::calculateImageSize(const QString &imagePath, int maxWidth, int maxHeight) const
  448. {
  449. if (imagePath.isEmpty())
  450. return QSize(100, 100); // 默认尺寸
  451. QPixmap image(imagePath);
  452. if (image.isNull())
  453. return QSize(100, 100);
  454. QSize imageSize = image.size();
  455. // 限制最大尺寸
  456. if (imageSize.width() > maxWidth || imageSize.height() > maxHeight) {
  457. imageSize = imageSize.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio);
  458. }
  459. // 限制最小尺寸
  460. if (imageSize.width() < 50) imageSize.setWidth(50);
  461. if (imageSize.height() < 50) imageSize.setHeight(50);
  462. return imageSize;
  463. }
  464. QSize ChatMessageDelegate::calculateContentSize(const ChatMessage &message, const QFontMetrics &fm) const
  465. {
  466. if (message.hasImage() && message.hasText()) {
  467. // 混合内容:图片 + 文本
  468. int maxWidth = viewportWidth() - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE - 2 * ChatConstants::BUBBLE_SPACING;
  469. QSize imageSize = calculateImageSize(message.imagePath, maxWidth, 200);
  470. QSize textSize = calculateTextSize(fm, message.text);
  471. int width = qMax(imageSize.width(), textSize.width());
  472. int height = imageSize.height() + textSize.height() + 4; // 4px间距
  473. return QSize(width, height);
  474. } else if (message.hasImage()) {
  475. // 纯图片消息
  476. int maxWidth = viewportWidth() - 2 * ChatConstants::BUBBLE_PADDING - ChatConstants::AVATAR_SIZE - 2 * ChatConstants::BUBBLE_SPACING;
  477. return calculateImageSize(message.imagePath, maxWidth, 300);
  478. } else {
  479. // 纯文本消息
  480. return calculateTextSize(fm, message.text);
  481. }
  482. }