aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-26 15:58:40 +0200
committerDanilo M. <danix@danix.xyz>2026-08-26 15:58:40 +0200
commitef4d48c56c39f19efc751488cc2df6ee5af24a7f (patch)
tree0a7fd1fd7dc1df9a2842a61d8416586698de7bfd /src
parentca5b6ca711e73bc22fdf108e06d82241e059c6ee (diff)
downloadqtmaildir-ef4d48c56c39f19efc751488cc2df6ee5af24a7f.tar.gz
qtmaildir-ef4d48c56c39f19efc751488cc2df6ee5af24a7f.zip
feat: fade the account colour across a card
Diffstat (limited to 'src')
-rw-r--r--src/carddelegate.cpp31
-rw-r--r--src/carddelegate.h15
2 files changed, 46 insertions, 0 deletions
diff --git a/src/carddelegate.cpp b/src/carddelegate.cpp
index 9a98c69..6541932 100644
--- a/src/carddelegate.cpp
+++ b/src/carddelegate.cpp
@@ -26,6 +26,7 @@
#include <QApplication>
#include <QDateTime>
#include <QGuiApplication>
+#include <QLinearGradient>
#include <QPainter>
#include <QRegularExpression>
#include <QStyle>
@@ -100,6 +101,18 @@ QColor CardDelegate::mutedChipColour(const QColor &chipColour)
return QColor::fromHslF(h, s * kSaturationScale, l, a);
}
+QRect CardDelegate::fadeRectFor(const QRect &card, const QRect &innermostSpine)
+{
+ // The EXCLUSIVE right edge, then a rect built from it: QRect::right() is
+ // inclusive, which is the trap CardLayout already documents.
+ const int end = card.left() + int(card.width() * kFadeFraction);
+ const int start = innermostSpine.isEmpty() ? card.left()
+ : innermostSpine.left();
+ if (end <= start)
+ return QRect();
+ return QRect(start, card.top(), end - start, card.height());
+}
+
QColor CardDelegate::accentLineColour(const QColor &accountColour)
{
if (!accountColour.isValid())
@@ -178,6 +191,24 @@ void CardDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
root.data(ThreadListModel::AccountColourRole).value<QColor>();
const QColor lineColour = accentLineColour(accountColour);
+ // The account's fade. Under everything but the chrome, so the selection
+ // highlight and the doomed-row tint still cover it: a selected row reading
+ // mostly as selection is expected, not a fault.
+ const QRect fade =
+ fadeRectFor(option.rect,
+ card.spines.isEmpty() ? QRect() : card.spines.last());
+ if (!fade.isEmpty() && accountColour.isValid()) {
+ QColor from = lineColour;
+ // A reply's wash is weaker than its root's, so an expanded thread
+ // reads as one block with the root leading it.
+ from.setAlphaF(card.accentRect.isEmpty() ? 0.14 : 0.30);
+ QLinearGradient gradient(fade.topLeft(), fade.topRight());
+ gradient.setColorAt(0.0, from);
+ from.setAlphaF(0.0);
+ gradient.setColorAt(1.0, from);
+ painter->fillRect(fade, gradient);
+ }
+
// The accent bar, thread cards only. Drawn after the chrome so the
// selection highlight cannot cover it: which account a card belongs to
// must stay readable on the row the user is looking at.
diff --git a/src/carddelegate.h b/src/carddelegate.h
index 1846359..5a0830c 100644
--- a/src/carddelegate.h
+++ b/src/carddelegate.h
@@ -77,6 +77,21 @@ public:
/// Falls back to threadLineColour() for a thread with no account tag.
static QColor accentLineColour(const QColor &accountColour);
+ /// Where the account's fade runs, given the card and the row's innermost
+ /// spine (an empty rect for a thread root, which has none).
+ ///
+ /// A root's fade starts at the card's left edge; a reply's starts at its
+ /// own spine, which IS its coloured left border, so the wash steps right
+ /// with the nesting. Both end at 60% of the card's width, so a deeper
+ /// reply's wash is shorter as well as further right.
+ ///
+ /// Static and rect-in, rect-out so the geometry is assertable without a
+ /// painter, for the same reason CardLayout is.
+ static QRect fadeRectFor(const QRect &card, const QRect &innermostSpine);
+
+ /// How far across the card the account's colour reaches.
+ static constexpr qreal kFadeFraction = 0.60;
+
/// A tag chip's colour, drained for the SIBLING tier (item 111).
///
/// Saturation only: hue stays so the tag is still recognisable, and