From ef4d48c56c39f19efc751488cc2df6ee5af24a7f Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 26 Aug 2026 15:58:40 +0200 Subject: feat: fade the account colour across a card --- src/carddelegate.cpp | 31 +++++++++++++++++++++++++++++++ src/carddelegate.h | 15 +++++++++++++++ 2 files changed, 46 insertions(+) (limited to 'src') 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 #include #include +#include #include #include #include @@ -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(); 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 -- cgit v1.2.3