Branch data Line data Source code
1 : : #include "LabelDelegate.h"
2 : :
3 : : #include <QPainter>
4 : :
5 : : #include "ui/MailListModel.h"
6 : : #include "ui/ThemeManager.h"
7 : :
8 : 64 : LabelDelegate::LabelDelegate(QObject *parent)
9 : 64 : : QStyledItemDelegate(parent) {}
10 : :
11 : 2386 : void LabelDelegate::paint(QPainter *painter,
12 : : const QStyleOptionViewItem &option,
13 : : const QModelIndex &index) const {
14 : : // Let the base class handle selection highlighting, fonts, etc.
15 [ + - ]: 2386 : QStyleOptionViewItem opt = option;
16 [ + - ]: 2386 : initStyleOption(&opt, index);
17 : :
18 [ + - ]: 2386 : auto &tm = ThemeManager::instance();
19 : :
20 : : // Draw selection background
21 [ + - ]: 2386 : painter->save();
22 [ + + ]: 2386 : if (opt.state & QStyle::State_Selected) {
23 [ + - + - : 154 : painter->fillRect(opt.rect, QColor(tm.color("@bg_selected")));
+ - ]
24 [ - + ]: 2232 : } else if (opt.state & QStyle::State_MouseOver) {
25 [ # # # # : 0 : painter->fillRect(opt.rect, QColor(tm.color("@bg_hover")));
# # ]
26 : : }
27 : :
28 : : // Sprint 70: row separator removed — selection/hover backgrounds
29 : : // carry the separation alone (see SPRINT-70.md T-70.1).
30 : :
31 : : // Draw subject text
32 [ + - + - ]: 2386 : QString subject = index.data(Qt::DisplayRole).toString();
33 [ + - + - ]: 2386 : QStringList labels = index.data(MailListModel::LabelsRole).toStringList();
34 : :
35 : 2386 : QRect textRect = opt.rect.adjusted(8, 0, 0, 0);
36 : :
37 : : // Set font (bold for unread)
38 [ + - ]: 2386 : QFont font = opt.font;
39 [ + - ]: 2386 : QVariant fontVar = index.data(Qt::FontRole);
40 [ + - + + ]: 2386 : if (fontVar.isValid()) {
41 [ + - ]: 949 : font = fontVar.value<QFont>();
42 : : }
43 [ + - ]: 2386 : painter->setFont(font);
44 : :
45 : : // Text color
46 [ + - + - : 2386 : painter->setPen(QColor(tm.color("@text_primary")));
+ - ]
47 : :
48 : : // Draw subject text, measure how much space it used
49 [ + - ]: 2386 : QFontMetrics fm(font);
50 : : QString elidedSubject = fm.elidedText(
51 : : subject, Qt::ElideRight,
52 [ + + + - ]: 2386 : labels.isEmpty() ? textRect.width() : textRect.width() - 120);
53 [ + - ]: 2386 : painter->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, elidedSubject);
54 : :
55 : : // Draw label chips after subject text
56 [ + + ]: 2386 : if (!labels.isEmpty()) {
57 [ + - ]: 9 : int chipX = textRect.left() + fm.horizontalAdvance(elidedSubject) + 8;
58 : 9 : int chipY = textRect.center().y() - 7;
59 : 9 : int chipHeight = 14;
60 : :
61 [ + - + - ]: 9 : QFont chipFont = painter->font();
62 [ + - ]: 9 : qreal basePt = chipFont.pointSizeF();
63 [ + + ]: 9 : if (basePt > 0) {
64 [ + - ]: 2 : chipFont.setPointSizeF(basePt * 0.8);
65 : : } else {
66 : : // Pixel-sized font: scale via pixel size
67 [ + - ]: 7 : int px = chipFont.pixelSize();
68 [ + - ]: 7 : if (px > 0)
69 [ + - ]: 7 : chipFont.setPixelSize(qMax(1, static_cast<int>(px * 0.8)));
70 : : }
71 [ + - ]: 9 : chipFont.setBold(false);
72 [ + - ]: 9 : painter->setFont(chipFont);
73 [ + - ]: 9 : QFontMetrics chipFm(chipFont);
74 : :
75 [ + + ]: 16 : for (int i = 0; i < labels.size(); ++i) {
76 [ + - ]: 9 : const auto &label = labels[i];
77 [ + - ]: 9 : QString name = displayName(label);
78 [ + - ]: 9 : QColor color = colorForLabel(label);
79 : :
80 [ + - ]: 9 : int textWidth = chipFm.horizontalAdvance(name);
81 : 9 : int chipWidth = textWidth + 10;
82 : :
83 : : // Check if this chip fits; reserve space for "+N" if more follow
84 : 9 : int remaining = labels.size() - i - 1;
85 : 9 : int reservedWidth = 0;
86 [ + + ]: 9 : if (remaining > 0) {
87 [ + - ]: 4 : QString overflowText = QStringLiteral("+%1").arg(remaining);
88 [ + - ]: 2 : reservedWidth = chipFm.horizontalAdvance(overflowText) + 14;
89 : 2 : }
90 : :
91 [ + + ]: 9 : if (chipX + chipWidth > opt.rect.right() - 4 - reservedWidth) {
92 : : // Not enough room — render "+N" overflow indicator
93 : 2 : int overflowCount = labels.size() - i;
94 [ + - ]: 4 : QString overflowText = QStringLiteral("+%1").arg(overflowCount);
95 [ + - ]: 2 : int owWidth = chipFm.horizontalAdvance(overflowText) + 10;
96 : 2 : QRect owRect(chipX, chipY, owWidth, chipHeight);
97 : :
98 [ + - ]: 2 : painter->setRenderHint(QPainter::Antialiasing, true);
99 [ + - ]: 2 : painter->setPen(Qt::NoPen);
100 [ + - + - : 2 : painter->setBrush(ThemeManager::labelPalette().at(6)); // gray
+ - ]
101 [ + - ]: 2 : painter->drawRoundedRect(owRect, 3, 3);
102 [ + - ]: 2 : painter->setPen(Qt::white);
103 [ + - ]: 2 : painter->drawText(owRect, Qt::AlignCenter, overflowText);
104 : 2 : break;
105 : 2 : }
106 : :
107 : 7 : QRect chipRect(chipX, chipY, chipWidth, chipHeight);
108 : :
109 : : // Draw chip background (rounded)
110 [ + - ]: 7 : painter->setRenderHint(QPainter::Antialiasing, true);
111 [ + - ]: 7 : painter->setPen(Qt::NoPen);
112 [ + - + - ]: 7 : painter->setBrush(color);
113 [ + - ]: 7 : painter->drawRoundedRect(chipRect, 3, 3);
114 : :
115 : : // Draw chip text (white on colored background)
116 [ + - ]: 7 : painter->setPen(Qt::white);
117 [ + - ]: 7 : painter->drawText(chipRect, Qt::AlignCenter, name);
118 : :
119 : 7 : chipX += chipWidth + 4;
120 [ + + ]: 9 : }
121 : 9 : }
122 : :
123 [ + - ]: 2386 : painter->restore();
124 : 2386 : }
125 : :
126 : 202 : QSize LabelDelegate::sizeHint(const QStyleOptionViewItem &option,
127 : : const QModelIndex &index) const {
128 : : // Sprint 70: removed the qMax(base.height(), 22) floor that forced
129 : : // Subject-column rows to a 22px minimum. Row height is now driven
130 : : // purely by font metrics + QSS padding (1px vertical in main.qss),
131 : : // giving ~17-18px rows at 13px font. The Subject delegate no longer
132 : : // dictates the tallest column; setUniformRowHeights(true) on the
133 : : // QTreeView ensures all columns agree on the same compact height.
134 : 202 : return QStyledItemDelegate::sizeHint(option, index);
135 : : }
136 : :
137 : 24 : QColor LabelDelegate::colorForLabel(const QString &label) {
138 : : // Standard Thunderbird/IMAP label colors (shared palette, 67.B3):
139 : : // red, orange, green, blue, purple, gold, gray
140 [ + - ]: 24 : const QList<QColor> palette = ThemeManager::labelPalette();
141 [ + + ]: 24 : if (label == QLatin1String("$label1"))
142 : 1 : return palette.at(0); // Red
143 [ + + ]: 23 : if (label == QLatin1String("$label2"))
144 : 2 : return palette.at(1); // Orange
145 [ - + ]: 21 : if (label == QLatin1String("$label3"))
146 : 0 : return palette.at(2); // Green
147 [ - + ]: 21 : if (label == QLatin1String("$label4"))
148 : 0 : return palette.at(3); // Blue
149 [ - + ]: 21 : if (label == QLatin1String("$label5"))
150 : 0 : return palette.at(4); // Purple
151 [ + + ]: 21 : if (label.compare(QLatin1String("$Important"), Qt::CaseInsensitive) == 0)
152 : 5 : return palette.at(5); // Gold
153 [ - + ]: 16 : if (label.compare(QLatin1String("Junk"), Qt::CaseInsensitive) == 0)
154 : 0 : return palette.at(6); // Gray
155 : :
156 : : // Hash-based color for unknown labels
157 : 16 : uint hash = qHash(label);
158 : 16 : int hue = hash % 360;
159 [ + - ]: 16 : return QColor::fromHsl(hue, 140, 100);
160 : 24 : }
161 : :
162 : 19 : QString LabelDelegate::displayName(const QString &label) {
163 : : // Map internal names to friendly display names
164 [ + + ]: 19 : if (label == QLatin1String("$label1"))
165 : 1 : return tr("Important");
166 [ + + ]: 18 : if (label == QLatin1String("$label2"))
167 : 2 : return tr("Work");
168 [ - + ]: 16 : if (label == QLatin1String("$label3"))
169 : 0 : return tr("Personal");
170 [ - + ]: 16 : if (label == QLatin1String("$label4"))
171 : 0 : return tr("To Do");
172 [ - + ]: 16 : if (label == QLatin1String("$label5"))
173 : 0 : return tr("Later");
174 [ + + ]: 16 : if (label.compare(QLatin1String("$Important"), Qt::CaseInsensitive) == 0)
175 : 5 : return QStringLiteral("Important");
176 : :
177 : : // Strip leading $ for custom labels
178 [ + - - + ]: 11 : if (label.startsWith('$'))
179 : 0 : return label.mid(1);
180 : :
181 : 11 : return label;
182 : : }
|