summaryrefslogtreecommitdiffstats
path: root/src/tagchip.h
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-07 16:26:22 +0200
committerDanilo M. <danix@danix.xyz>2026-08-07 16:26:22 +0200
commit39cbde74a560407e24b05e171df883421aa2153e (patch)
treea7390b036f57507b17aa4291511ce33d3f297f81 /src/tagchip.h
parentde884b036689b253d10ff48daa3a05cca20ba61d (diff)
downloadqtmaildir-39cbde74a560407e24b05e171df883421aa2153e.tar.gz
qtmaildir-39cbde74a560407e24b05e171df883421aa2153e.zip
feat(ui): show each thread's tags under its row
The thread list was uniform and cramped: every row one line tall, with nothing to say what a thread was about before opening it. Rows are now roughly double height, carrying a strip of tag chips beneath the text, with alternating row colours and a star column for flagged threads beside the existing paperclip. The strip is painted by the VIEW rather than by a delegate, which is why ThreadListView exists. A delegate is handed one cell's rectangle and cannot paint outside its column, so a strip drawn from the subject column stops at that column's edge, losing the last tags of a well-tagged thread, and starts at its left edge, putting the chips under the subject instead of under the row. Tags the row already shows another way are left out: inbox as structure, unread as the dimming, flagged as the star, attachment as the paperclip, and the account as the chip in the subject cell. Sorted, since notmuch's order is not guaranteed stable and a row whose chips reordered between repaints would flicker. Six defects were introduced and fixed on the way here, all of them one consequence: a QTableView paints per cell, and a row-wide strip is not a cell. SubjectDelegate installed view-wide drew the account chip into every column, since AccountLabelRole belongs to the row; it is split into RowStyleDelegate for every column and SubjectDelegate for the subject alone, with a Q_ASSERT guarding that. Row height returned from sizeHint did nothing, because a table takes one height per row. The strip painted from x=0 over the marker columns, via a protected viewportMargins() that returns 0. Measuring the text band and the strip with one font put the pills over the date. Alternating colours and the selection are per-cell too, so the band showed bare viewport background until the view filled it, honouring the model's own BackgroundRole first so a deleted row is not cut in half. And that fill spanned the full width, cutting the centred marker glyphs at their midpoint. Closes item 5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/tagchip.h')
-rw-r--r--src/tagchip.h62
1 files changed, 52 insertions, 10 deletions
diff --git a/src/tagchip.h b/src/tagchip.h
index f43e3ff..cc3b5de 100644
--- a/src/tagchip.h
+++ b/src/tagchip.h
@@ -19,6 +19,7 @@
#pragma once
#include <QColor>
+#include <QFont>
#include <QRect>
#include <QSize>
#include <QString>
@@ -32,10 +33,13 @@ class QFontMetrics;
namespace TagChip {
/// Padding inside a chip and the gap between two of them.
-constexpr int kPaddingX = 6;
+///
+/// kPaddingX allows for the rounded ends: the corner radius is half the chip's
+/// height, so the leftmost and rightmost few pixels of the fill are curve
+/// rather than usable width, and text set closer would touch it.
+constexpr int kPaddingX = 9;
constexpr int kPaddingY = 1;
constexpr int kSpacing = 4;
-constexpr int kRadius = 3;
QSize sizeFor(const QFontMetrics &metrics, const QString &text);
@@ -46,25 +50,63 @@ void paint(QPainter *painter, const QRect &rect, const QString &text,
} // namespace TagChip
+/// Makes the selection highlight outrank a model-supplied foreground colour.
+///
+/// Qt resolves Qt::ForegroundRole into the palette's Text roles and its
+/// painting then prefers those over HighlightedText, so a model that supplies
+/// a foreground wins even on a selected row. That is wrong for the read/unread
+/// dimming, whose colour is blended against the UNSELECTED background: over
+/// the selection highlight it lands as grey on the highlight colour, close to
+/// unreadable.
+///
+/// Applied to the columns that have no delegate of their own; SubjectDelegate
+/// inherits it for the subject column.
+class RowStyleDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ using QStyledItemDelegate::QStyledItemDelegate;
+
+protected:
+ void initStyleOption(QStyleOptionViewItem *option,
+ const QModelIndex &index) const override;
+};
+
/// Item delegate for the subject column: draws the account chip in front of
/// the subject text, so which mailbox a thread came from reads at a glance
/// without a tags column spelling it out.
-class SubjectDelegate : public QStyledItemDelegate
+/// **Install on the subject column only.** It reads AccountLabelRole, which is
+/// a property of the row rather than of a cell, so as a view-wide delegate it
+/// draws the account chip into every column.
+class SubjectDelegate : public RowStyleDelegate
{
Q_OBJECT
public:
- using QStyledItemDelegate::QStyledItemDelegate;
+ using RowStyleDelegate::RowStyleDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
-protected:
- /// Makes the selection highlight outrank a model-supplied foreground.
+ /// Vertical breathing room above the subject and below the pill row.
+ static constexpr int kRowPadding = 4;
+
+ /// The font the pill strip is drawn in: a size down from the row's own.
///
- /// Qt's own resolution does the opposite, which leaves a dimmed read
- /// thread painting grey over the selection colour.
- void initStyleOption(QStyleOptionViewItem *option,
- const QModelIndex &index) const override;
+ /// At the same size the pills read as a second row of content competing
+ /// with the subject, rather than as annotation beneath it. Derived from
+ /// the row font rather than fixed, so it follows the desktop's font size.
+ static QFont pillFont(const QFont &rowFont);
+
+ /// The height every row gets, tall enough for the subject and a pill strip
+ /// beneath it. The view applies this itself: a QTableView takes one height
+ /// for the whole row, so leaving it to a single column's sizeHint would
+ /// let whichever column the view happens to ask decide.
+ static int rowHeightFor(const QFont &rowFont);
+
+protected:
+ /// The height of the band the subject text occupies. Everything below it
+ /// belongs to ThreadListView's row-wide pill strip.
+ static int subjectBandHeight(const QStyleOptionViewItem &option);
};