fix: single-line queue items — Title · Artist on left, duration right

Compact single row: title in medium weight, separator dot and artist
name dimmed, duration right-aligned. Height scales with font.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
joren
2026-03-24 10:37:17 +01:00
parent 6f4029c4bb
commit c8086e1f2f

View File

@@ -23,8 +23,7 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const override QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const override
{ {
const int lineH = QFontMetrics(option.font).height(); return QSize(0, QFontMetrics(option.font).height() + 10);
return QSize(0, lineH * 2 + 10);
} }
void paint(QPainter *p, const QStyleOptionViewItem &option, void paint(QPainter *p, const QStyleOptionViewItem &option,
@@ -32,72 +31,85 @@ public:
{ {
p->save(); p->save();
// Background (selection / hover / alternate)
QStyle *style = option.widget ? option.widget->style() : QApplication::style(); QStyle *style = option.widget ? option.widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget);
const bool isPlayNext = index.data(IsPlayNextRole).toBool(); const bool isPlayNext = index.data(IsPlayNextRole).toBool();
const QString title = index.data(Qt::DisplayRole).toString(); const QString title = index.data(Qt::DisplayRole).toString();
const QString artist = index.data(ArtistRole).toString(); const QString artist = index.data(ArtistRole).toString();
const int dur = index.data(DurationRole).toInt(); const int dur = index.data(DurationRole).toInt();
const QRect r = option.rect.adjusted(10, 0, -10, 0); const QRect r = option.rect.adjusted(10, 0, -10, 0);
// Duration string, right-aligned // Duration right-aligned
QString durStr; QString durStr;
if (dur > 0) { if (dur > 0) {
const int m = dur / 60, s = dur % 60; const int m = dur / 60, s = dur % 60;
durStr = QStringLiteral("%1:%2").arg(m).arg(s, 2, 10, QLatin1Char('0')); durStr = QStringLiteral("%1:%2").arg(m).arg(s, 2, 10, QLatin1Char('0'));
} }
// Colours
const QPalette &pal = option.palette; const QPalette &pal = option.palette;
const bool selected = option.state & QStyle::State_Selected; const bool selected = option.state & QStyle::State_Selected;
QColor titleColor = selected ? pal.highlightedText().color() : pal.text().color(); QColor titleColor = selected ? pal.highlightedText().color() : pal.text().color();
QColor artistColor = titleColor; QColor dimColor = titleColor;
artistColor.setAlpha(150); dimColor.setAlpha(150);
QColor durColor = artistColor;
if (isPlayNext && !selected) { if (isPlayNext && !selected)
titleColor = titleColor.lighter(130); titleColor = titleColor.lighter(130);
}
// Fonts
QFont titleFont = option.font; QFont titleFont = option.font;
titleFont.setWeight(QFont::Medium); titleFont.setWeight(QFont::Medium);
QFont subFont = option.font; QFont subFont = option.font;
subFont.setPointSizeF(option.font.pointSizeF() * 0.85); subFont.setPointSizeF(option.font.pointSizeF() * 0.85);
// Layout: title row vertically centred in top half, artist in bottom half // Draw duration on the far right
const int halfH = r.height() / 2; int durW = 0;
const QRect topR = QRect(r.left(), r.top(), r.width(), halfH);
const QRect botR = QRect(r.left(), r.top() + halfH, r.width(), halfH);
// Duration — draw right-aligned in top row
if (!durStr.isEmpty()) { if (!durStr.isEmpty()) {
durW = QFontMetrics(subFont).horizontalAdvance(durStr) + 6;
p->setFont(subFont); p->setFont(subFont);
p->setPen(durColor); p->setPen(dimColor);
p->drawText(topR, Qt::AlignRight | Qt::AlignVCenter, durStr); p->drawText(QRect(r.right() - durW, r.top(), durW, r.height()),
Qt::AlignRight | Qt::AlignVCenter, durStr);
} }
// Reserve space for duration in title row // Available width for title + separator + artist
const int durW = durStr.isEmpty() ? 0 const int available = r.width() - durW;
: QFontMetrics(subFont).horizontalAdvance(durStr) + 6;
const QRect titleR = topR.adjusted(0, 0, -durW, 0); const QFontMetrics titleFm(titleFont);
const QFontMetrics subFm(subFont);
const QString sep = artist.isEmpty() ? QString() : QStringLiteral(" · ");
const int sepW = sep.isEmpty() ? 0 : subFm.horizontalAdvance(sep);
// Title gets up to 60% of available, artist gets the rest
const int maxTitle = qMax(0, available * 6 / 10 - sepW);
const int maxArtist = qMax(0, available - sepW
- qMin(titleFm.horizontalAdvance(title), maxTitle));
const QString elidedTitle = titleFm.elidedText(title, Qt::ElideRight, maxTitle);
const int drawnTitleW = titleFm.horizontalAdvance(elidedTitle);
int x = r.left();
// Title // Title
p->setFont(titleFont); p->setFont(titleFont);
p->setPen(titleColor); p->setPen(titleColor);
p->drawText(titleR, Qt::AlignLeft | Qt::AlignVCenter, p->drawText(x, r.top(), drawnTitleW, r.height(),
p->fontMetrics().elidedText(title, Qt::ElideRight, titleR.width())); Qt::AlignLeft | Qt::AlignVCenter, elidedTitle);
x += drawnTitleW;
// Artist // Separator + artist
if (!artist.isEmpty()) { if (!artist.isEmpty()) {
p->setFont(subFont); p->setFont(subFont);
p->setPen(artistColor); p->setPen(dimColor);
p->drawText(botR, Qt::AlignLeft | Qt::AlignVCenter, p->drawText(x, r.top(), sepW, r.height(),
p->fontMetrics().elidedText(artist, Qt::ElideRight, botR.width())); Qt::AlignLeft | Qt::AlignVCenter, sep);
x += sepW;
const QString elidedArtist = subFm.elidedText(artist, Qt::ElideRight, maxArtist);
p->drawText(x, r.top(), maxArtist, r.height(),
Qt::AlignLeft | Qt::AlignVCenter, elidedArtist);
} }
p->restore(); p->restore();