summaryrefslogtreecommitdiffstats
path: root/tests/test_threadlistmodel.cpp
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-03 15:41:16 +0200
committerDanilo M. <danix@danix.xyz>2026-08-03 15:41:16 +0200
commitcd0be6dc5e4741c2ad180d6bf6a275f000ab943a (patch)
treec2c704065c398ba33d9549bfb025d30dbb0a46c4 /tests/test_threadlistmodel.cpp
parentab16342f6181f69e5cb1ef528f1dd0eb433bb74b (diff)
downloadqtmaildir-cd0be6dc5e4741c2ad180d6bf6a275f000ab943a.tar.gz
qtmaildir-cd0be6dc5e4741c2ad180d6bf6a275f000ab943a.zip
feat: render tags as chips instead of a text column
Spelled out per row, tags ran to 500 pixels of largely repeated text and took most of the thread list's width. The column is gone; tags render as coloured chips split by what they actually mean. An account tag says which mailbox a thread arrived in, and draws as a chip in front of the subject. A functional tag says what state a thread is in, and those fill one row under the message pane. One row keeps the message area from shifting between threads with different tag counts, so whatever does not fit collapses into a +N chip that names the rest in its tooltip. TagColors resolves a colour by exact tag first, then by top-level prefix, so a single "shopping" entry covers shopping/amazon and shopping/nike while shopping/amazon can still override its own. That matters at 96 tags. Built-in defaults cover the usual state tags, and anything left unconfigured falls back to a hash of the name, stable so a chip does not change colour as the list scrolls.
Diffstat (limited to 'tests/test_threadlistmodel.cpp')
-rw-r--r--tests/test_threadlistmodel.cpp88
1 files changed, 81 insertions, 7 deletions
diff --git a/tests/test_threadlistmodel.cpp b/tests/test_threadlistmodel.cpp
index 98c477c..e8a5fa8 100644
--- a/tests/test_threadlistmodel.cpp
+++ b/tests/test_threadlistmodel.cpp
@@ -34,6 +34,9 @@ private slots:
void subjectShowsMessageCountOnlyForRealThreads();
void unreadThreadsRenderBold();
void tagsAreTheFirstColumnAndSubjectTheLast();
+ void accountTagBecomesAChipLabel();
+ void unreadStylingSurvivesAnAccountChip();
+ void accountChipUsesTheConfiguredColour();
void deletedThreadsAreRedAndStruckThrough();
void spamThreadsAreOrangeAndStruckThrough();
void doomedStylingCoversEveryColumn();
@@ -118,9 +121,11 @@ void TestThreadListModel::reportsSubjectAndAuthors()
const QModelIndex date = model.index(0, ThreadListModel::DateColumn);
QVERIFY(!model.data(date, Qt::DisplayRole).toString().isEmpty());
- const QModelIndex tags = model.index(0, ThreadListModel::TagsColumn);
- QCOMPARE(model.data(tags, Qt::DisplayRole).toString(),
- QStringLiteral("inbox unread"));
+ // Tags are no longer a column; they reach the strip under the message
+ // pane through a role instead.
+ const QModelIndex subject = model.index(0, ThreadListModel::SubjectColumn);
+ QCOMPARE(model.data(subject, ThreadListModel::TagsRole).toStringList(),
+ QStringList({ QStringLiteral("inbox"), QStringLiteral("unread") }));
}
void TestThreadListModel::subjectShowsMessageCountOnlyForRealThreads()
@@ -163,17 +168,86 @@ void TestThreadListModel::tagsAreTheFirstColumnAndSubjectTheLast()
// Subject stretches to fill the view, so whatever sits after it is pushed
// off-screen. Tags used to be there, which is why acting on a thread
// looked like it did nothing: the only column that changed was invisible.
- QCOMPARE(ThreadListModel::TagsColumn, 0);
QCOMPARE(ThreadListModel::SubjectColumn, ThreadListModel::ColumnCount - 1);
ThreadListModel model;
model.appendBatch({ makeThread(QStringLiteral("t1"), QStringLiteral("hello")) });
- QCOMPARE(model.headerData(ThreadListModel::TagsColumn, Qt::Horizontal,
- Qt::DisplayRole).toString(),
- QStringLiteral("Tags"));
QCOMPARE(model.headerData(ThreadListModel::SubjectColumn, Qt::Horizontal,
Qt::DisplayRole).toString(),
QStringLiteral("Subject"));
+
+ // No tags column at all: spelling out a dozen tags per row consumed most
+ // of the list's width and was unreadable.
+ for (int column = 0; column < ThreadListModel::ColumnCount; ++column) {
+ QVERIFY(model.headerData(column, Qt::Horizontal, Qt::DisplayRole)
+ .toString() != QStringLiteral("Tags"));
+ }
+}
+
+void TestThreadListModel::accountTagBecomesAChipLabel()
+{
+ // The account tag is a different taxonomy from a functional one: which
+ // mailbox the thread arrived in. It renders as a chip in front of the
+ // subject, so the model exposes its label and colour separately.
+ ThreadListModel model;
+ ThreadSummary thread = makeThread(QStringLiteral("t1"), QStringLiteral("hello"));
+ thread.tags = QStringList{ QStringLiteral("inbox"),
+ QStringLiteral("account-gmail-danixland") };
+ model.appendBatch({ thread });
+
+ const QModelIndex subject = model.index(0, ThreadListModel::SubjectColumn);
+ QCOMPARE(model.data(subject, ThreadListModel::AccountLabelRole).toString(),
+ QStringLiteral("gmail-danixland"));
+ QVERIFY(model.data(subject, ThreadListModel::AccountColourRole)
+ .value<QColor>().isValid());
+
+ // A thread with no account tag gets no chip rather than an empty one.
+ ThreadListModel plain;
+ ThreadSummary untagged = makeThread(QStringLiteral("t2"), QStringLiteral("hi"));
+ untagged.tags = QStringList{ QStringLiteral("inbox") };
+ plain.appendBatch({ untagged });
+ QVERIFY(plain.data(plain.index(0, ThreadListModel::SubjectColumn),
+ ThreadListModel::AccountLabelRole).toString().isEmpty());
+}
+
+void TestThreadListModel::unreadStylingSurvivesAnAccountChip()
+{
+ // The subject cell is drawn by a delegate when the thread has an account
+ // chip. The delegate paints the text itself, so it has to keep honouring
+ // the model's font: otherwise an unread thread stops rendering bold for
+ // exactly those threads that carry an account tag, which is all of them.
+ ThreadListModel model;
+ ThreadSummary thread = makeThread(QStringLiteral("t1"), QStringLiteral("hello"));
+ thread.tags = QStringList{ QStringLiteral("inbox"), QStringLiteral("unread"),
+ QStringLiteral("account-gmail-danixland") };
+ model.appendBatch({ thread });
+
+ const QModelIndex subject = model.index(0, ThreadListModel::SubjectColumn);
+ QVERIFY(!model.data(subject, ThreadListModel::AccountLabelRole)
+ .toString().isEmpty());
+
+ const QVariant font = model.data(subject, Qt::FontRole);
+ QVERIFY2(font.isValid(), "unread thread with an account tag has no font");
+ QVERIFY2(font.value<QFont>().bold(), "unread thread is not bold");
+}
+
+void TestThreadListModel::accountChipUsesTheConfiguredColour()
+{
+ // The colour comes from the account's own stanza, so a configured one must
+ // reach the chip rather than the generated fallback.
+ TagColors colours;
+ colours.setAccountColour(QStringLiteral("gmail-danixland"),
+ QColor(QStringLiteral("#cc0000")));
+
+ ThreadListModel model;
+ model.setTagColors(&colours);
+ ThreadSummary thread = makeThread(QStringLiteral("t1"), QStringLiteral("hello"));
+ thread.tags = QStringList{ QStringLiteral("account-gmail-danixland") };
+ model.appendBatch({ thread });
+
+ QCOMPARE(model.data(model.index(0, ThreadListModel::SubjectColumn),
+ ThreadListModel::AccountColourRole).value<QColor>(),
+ QColor(QStringLiteral("#cc0000")));
}
void TestThreadListModel::deletedThreadsAreRedAndStruckThrough()