aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_threadlistmodel.cpp
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-03 17:17:25 +0200
committerDanilo M. <danix@danix.xyz>2026-08-03 17:17:25 +0200
commite45f68b04b5ee2400a7885d5f5b054a889061df5 (patch)
treebcf0a90e65dd2a85665b83694fdf4a39bf97c565 /tests/test_threadlistmodel.cpp
parent7f505624b1f385a89c9ff32e15f4d4e68595e5b8 (diff)
downloadqtmaildir-e45f68b04b5ee2400a7885d5f5b054a889061df5.tar.gz
qtmaildir-e45f68b04b5ee2400a7885d5f5b054a889061df5.zip
feat: show a paperclip for threads with attachments
An attachment was only discoverable by opening the thread. A narrow leftmost column now marks the threads that carry one. No new worker query is involved: notmuch applies the "attachment" tag while indexing, so ThreadSummary already holds what this needs. The marker is a glyph rather than an icon resource, which ships no new asset and inherits the row font, so it strikes through with a doomed thread like every other cell. It falls back to "*" where the system font cannot draw U+1F4CE, since an unrenderable codepoint reads as breakage rather than as a marker. Two silent Qt behaviours had to be handled, both found by probe: QHeaderView::restoreState() returns true for a blob saved against fewer columns and applies the old widths shifted one place right. Adding a column in front would therefore have mangled every existing saved layout with no error to detect it by. The column count is now stored beside the blob and a mismatch discards it, so the widths reset once on upgrade instead of landing on the wrong columns. QHeaderView's default minimumSectionSize is 58px on this platform, and setColumnWidth() clamps to it without reporting the smaller value back, so the column could not be narrow at all until it was lowered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'tests/test_threadlistmodel.cpp')
-rw-r--r--tests/test_threadlistmodel.cpp42
1 files changed, 42 insertions, 0 deletions
diff --git a/tests/test_threadlistmodel.cpp b/tests/test_threadlistmodel.cpp
index e8a5fa8..97f7fde 100644
--- a/tests/test_threadlistmodel.cpp
+++ b/tests/test_threadlistmodel.cpp
@@ -38,6 +38,7 @@ private slots:
void unreadStylingSurvivesAnAccountChip();
void accountChipUsesTheConfiguredColour();
void deletedThreadsAreRedAndStruckThrough();
+ void attachmentColumnIsFirstAndMarksOnlyTaggedThreads();
void spamThreadsAreOrangeAndStruckThrough();
void doomedStylingCoversEveryColumn();
void ordinaryThreadsCarryNoRowColour();
@@ -472,5 +473,46 @@ void TestThreadListModel::modelPassesQtTester()
model.clear();
}
+void TestThreadListModel::attachmentColumnIsFirstAndMarksOnlyTaggedThreads()
+{
+ // Leftmost, and narrow: the point is to see an attachment without opening
+ // the thread, which only works if the column is never scrolled away.
+ QCOMPARE(ThreadListModel::AttachmentColumn, 0);
+
+ ThreadSummary plain = makeThread(QStringLiteral("t1"),
+ QStringLiteral("no attachment"));
+ ThreadSummary withFile = makeThread(QStringLiteral("t2"),
+ QStringLiteral("has one"));
+ // notmuch applies this tag itself while indexing, so no MIME parsing and
+ // no extra worker query are involved.
+ withFile.tags.append(QStringLiteral("attachment"));
+
+ ThreadListModel model;
+ model.appendBatch({ plain, withFile });
+
+ const QModelIndex plainCell =
+ model.index(0, ThreadListModel::AttachmentColumn);
+ const QModelIndex fileCell =
+ model.index(1, ThreadListModel::AttachmentColumn);
+
+ QVERIFY(model.data(plainCell, Qt::DisplayRole).toString().isEmpty());
+ QCOMPARE(model.data(fileCell, Qt::DisplayRole).toString(),
+ ThreadListModel::attachmentGlyph());
+
+ // The glyph must be something a font can draw. An unrenderable codepoint
+ // shows as a tofu box, which reads as breakage rather than as a marker.
+ QVERIFY(!ThreadListModel::attachmentGlyph().isEmpty());
+
+ // Only the marked thread gets a tooltip, or an empty cell would claim to
+ // have an attachment on hover.
+ QVERIFY(model.data(plainCell, Qt::ToolTipRole).toString().isEmpty());
+ QVERIFY(!model.data(fileCell, Qt::ToolTipRole).toString().isEmpty());
+
+ // The header carries no text: a label would set a minimum width far wider
+ // than the icon and defeat the narrow column.
+ QVERIFY(model.headerData(ThreadListModel::AttachmentColumn, Qt::Horizontal,
+ Qt::DisplayRole).toString().isEmpty());
+}
+
QTEST_MAIN(TestThreadListModel)
#include "test_threadlistmodel.moc"