diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-16 21:58:18 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-16 21:58:18 +0200 |
| commit | 019117aa8e52ce39cab58f77b57a9a67f510696f (patch) | |
| tree | d96c71e0e43a777dcdbce05cb7f0e58f135139b1 /src/cardlayout.cpp | |
| parent | b405e3288bf625bd478204a065572e41a93fb4c3 (diff) | |
| download | qtmaildir-019117aa8e52ce39cab58f77b57a9a67f510696f.tar.gz qtmaildir-019117aa8e52ce39cab58f77b57a9a67f510696f.zip | |
feat(ui): act on the message a row displays, not its whole thread
A thread's card has rendered one message since item 66, but every tag
action still acted on the entire conversation. Delete, Archive,
Important, Mark spam and Toggle unread now act on the message the card
shows; the whole-thread versions move to a "Whole thread" submenu in the
Message menu and the thread list's context menu, on Ctrl+Alt+<key>.
Closes items 87, 88, 105, 106, 107, 108, 109, 110 and 111.
The defects fixed along the way, several found by reading rather than by
report:
- threadAt(current.row()) answered about the wrong thread for a reply
row, because a tree numbers rows per parent. The audit found four live
sites, not the one reported: Delete and Toggle unread each chose their
DIRECTION from an unrelated thread, and the tag dialog counted the
wrong thread's tags. threadFor(index) replaces them.
- A message-scoped write made no optimistic model update and no reply
row carried a doomed cue, so acting on a reply moved the pending-edit
count and changed nothing on screen.
- Both toggles read the state of a reply's THREAD, which a
message-scoped write never changes, so they were one-way: the second
press re-sent a tag the message already had.
- flushHeldEdits() re-sent only thread-scoped edits, so a tag change
made on one message during a sync was applied to the row, counted as
unsynced, and then dropped without ever being written.
- applyTagChange() updated a thread's summary but not its loaded
replies, leaving an expanded thread's rows describing a state the
database no longer held.
- A thread's first message is not among its children, so both
message-scoped lookups missed it: acting on a root card repainted
nothing and emptied the message pane's chip row.
- ThreadSummary::tags is notmuch's union over the thread, so a card
standing for one message drew tags belonging to its siblings. The
worker now reads that message's own tags in the walk that already
finds its id, so the split is known before a row is ever opened.
The card shows both tiers: its own message's tags at full size, the rest
of the conversation's smaller and muted, so nothing appears to vanish
when a row is selected.
Auto mark-read is message-scoped as a result, and now arms for a reply,
which it never did. With maildir.synchronize_flags on, the old
thread-wide write reached the server for mail that had never been
displayed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/cardlayout.cpp')
| -rw-r--r-- | src/cardlayout.cpp | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/src/cardlayout.cpp b/src/cardlayout.cpp index 0febb4b..3719591 100644 --- a/src/cardlayout.cpp +++ b/src/cardlayout.cpp @@ -107,6 +107,32 @@ QFont CardLayout::smallFont(const QFont &cardFont) return small; } +QFont CardLayout::siblingFont(const QFont &cardFont) +{ + // A FRACTION of the card's font, not a fixed number of points off it. + // + // Subtracting one point was the first attempt and the user reported the + // tiers as indistinguishable. The reason is arithmetic: their desktop is + // 14pt, so the two chip tiers were 13 and 12, a 7% step. Subtraction gives + // a step whose size depends on the desktop font, which is exactly backwards + // — it is largest where the text is already small enough to be fragile. + // + // 0.70 of the card font, against smallFont()'s one point off, so on a 14pt + // desktop the tiers are 13 and 10. Chosen with the user against rendered + // sizes rather than picked. + constexpr qreal kSiblingScale = 0.70; + + QFont small = cardFont; + // pointSizeF() returns -1 for a font set in PIXELS, which qt6ct does, and + // scaling -1 asks for an invalid size that Qt silently ignores, leaving + // both tiers identical. Same split as smallFont(), same reason. + if (small.pointSizeF() > 0.0) + small.setPointSizeF(qMax(6.0, cardFont.pointSizeF() * kSiblingScale)); + else if (small.pixelSize() > 0) + small.setPixelSize(qMax(8, qRound(cardFont.pixelSize() * kSiblingScale))); + return small; +} + int CardLayout::heightFor(const QFont &font) { const QFontMetrics metrics(font); |
