feat: media_async + media_index modules, media_files table, tests
- media_async: thread-per-task async engine with primitives (sha256, sign, copy, uuid, file_size) - media_index: media_files DB table with register_async for indexing chunks - chat_msg: async media submission via register_async, simplified chat_msg_submit fields - voice_recorder/attachment_sender: write single file (no chunk splitting on caller side) - inputbar/messagelist: updated to new simplified submit interface - test_media_async: 4-thread stress test (200K ops / 2 sec, 0 errors) - test_media_index: 9 cases covering commit edge cases + async flow - build: Makefile.am, CMakeLists.txt, utun_sources.cmake updated
This commit is contained in:
1 parent
ce5a84da24
commit
65dbfa6813
43 files changed
+1983
-391
No files matched your search
@@ -185,6 +185,16 @@ static int measureTextHeight(const QFont &font, const QString &text, int width)
|
||||
return r.height();
|
||||
}
|
||||
|
||||
static QString formatFileSize(qint64 bytes) {
|
||||
if (bytes < 1024) return QString("%1B").arg(bytes);
|
||||
double kb = bytes / 1024.0;
|
||||
if (kb < 1024.0) return QString("%1KB").arg(kb, 0, 'f', kb < 10.0 ? 1 : 0);
|
||||
double mb = kb / 1024.0;
|
||||
if (mb < 1024.0) return QString("%1MB").arg(mb, 0, 'f', mb < 10.0 ? 1 : 0);
|
||||
double gb = mb / 1024.0;
|
||||
return QString("%1GB").arg(gb, 0, 'f', 1);
|
||||
}
|
||||
|
||||
static int calcMaxLineWidth(const QFont &font, const QString &text, int availW) {
|
||||
int maxW = 0;
|
||||
int pos = 0;
|
||||
@@ -259,6 +269,35 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
|
||||
return L;
|
||||
}
|
||||
|
||||
/* File attachment detection */
|
||||
L.isFile = (contentType == "application/octet-stream");
|
||||
if (L.isFile) {
|
||||
QFontMetrics fm(option.font);
|
||||
int th = fm.height();
|
||||
int ih = 24; /* icon height */
|
||||
L.bubbleWidth = 230;
|
||||
int bubbleH = kPadTop + ih + 2 + th + kPadBot;
|
||||
L.totalHeight = qMax(bubbleH, kAvatar) + kItemGap;
|
||||
|
||||
int contentEdge = L.isOutgoing
|
||||
? L.viewWidth - (L.narrow ? kMarginH : (kMarginH + kAvatar + kGap))
|
||||
: (L.narrow ? kMarginH : (kMarginH + kAvatar + kGap));
|
||||
int bx = L.isOutgoing ? (contentEdge - L.bubbleWidth) : contentEdge;
|
||||
L.bubbleRect = QRect(bx, 0, L.bubbleWidth, bubbleH);
|
||||
|
||||
L.textRect = QRect(bx + kPadH + ih + 8, kPadTop, L.bubbleWidth - 2*kPadH - ih - 8, th);
|
||||
L.statusRect = QRect(bx + kPadH + ih + 8 - 48, kPadTop + th + 2, L.bubbleWidth - 2*kPadH - ih - 8 + 48 - 40, th);
|
||||
|
||||
if (!L.narrow) {
|
||||
int avatarY = L.bubbleRect.bottom() - kAvatar;
|
||||
if (!L.isOutgoing)
|
||||
L.avatarRect = QRect(kMarginH, avatarY, kAvatar, kAvatar);
|
||||
else
|
||||
L.avatarRect = QRect(L.viewWidth - kMarginH - kAvatar, avatarY, kAvatar, kAvatar);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
L.emojiCount = detectEmojiOnly(text);
|
||||
L.emojiOnly = (L.emojiCount >= 1);
|
||||
|
||||
@@ -758,6 +797,46 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
||||
return;
|
||||
}
|
||||
|
||||
if (L.isFile) {
|
||||
drawBubble(painter, L, option);
|
||||
if (!L.narrow) drawAvatar(painter, L, index);
|
||||
|
||||
/* File icon */
|
||||
QFont iconFont = option.font;
|
||||
iconFont.setPointSize(iconFont.pointSize() + 6);
|
||||
painter->setFont(iconFont);
|
||||
painter->setPen(option.palette.text().color());
|
||||
QRect iconRect(L.bubbleRect.left() + kPadH, kPadTop, 24, 24);
|
||||
painter->drawText(iconRect, Qt::AlignCenter, QString::fromUtf8("\xF0\x9F\x93\x84"));
|
||||
|
||||
QFont fileFont = option.font;
|
||||
fileFont.setBold(true);
|
||||
painter->setFont(fileFont);
|
||||
painter->setPen(option.palette.text().color());
|
||||
QString displayName = index.data(MsgFileDisplayNameRole).toString();
|
||||
painter->drawText(L.textRect, Qt::AlignLeft | Qt::AlignVCenter, displayName);
|
||||
|
||||
QFont sizeFont = option.font;
|
||||
sizeFont.setPointSize(sizeFont.pointSize() - 2);
|
||||
painter->setFont(sizeFont);
|
||||
painter->setPen(QColor(0x80, 0x80, 0x80));
|
||||
qint64 fsz = index.data(MsgFileSizeRole).toLongLong();
|
||||
QString sizeStr = formatFileSize(fsz);
|
||||
int nameH = QFontMetrics(fileFont).height();
|
||||
QRect sizeRect(L.textRect.left(), L.textRect.top() + nameH, L.textRect.width(), QFontMetrics(sizeFont).height());
|
||||
painter->drawText(sizeRect, Qt::AlignLeft | Qt::AlignVCenter, sizeStr);
|
||||
|
||||
/* Timestamp */
|
||||
QFont tsFont = option.font;
|
||||
tsFont.setPointSize(tsFont.pointSize() - 1);
|
||||
painter->setFont(tsFont);
|
||||
painter->drawText(QRect(L.textRect.right() - 40, L.textRect.top() + nameH, 40, QFontMetrics(sizeFont).height()),
|
||||
Qt::AlignRight | Qt::AlignVCenter, index.data(MsgTimeRole).toString());
|
||||
|
||||
painter->restore();
|
||||
return;
|
||||
}
|
||||
|
||||
auto *cv = qobject_cast<const ChatView *>(option.widget);
|
||||
bool singleTextSel = false;
|
||||
bool multiRowSel = false;
|
||||
|
||||
Reference in new issue
Block a user