/***************************************************************************
themeswatchdelegate.cpp
-------------------
begin : 2026
copyright : (C) 2009-2026 by Andre Simon
email : a.simon@mailbox.org
***************************************************************************/
/*
This file is part of Highlight.
Highlight is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Highlight is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Highlight. If not, see <http://www.gnu.org/licenses/>.
*/
#include "themeswatchdelegate.h"
#include "thememodel.h"
#include <QApplication>
#include <QPainter>
#include <QPainterPath>
namespace
{
const int Margin = 4;
const int Radius = 4;
/** One bar of a fake code line: colour role and relative width */
struct Bar
{
int role;
qreal width;
};
}
ThemeSwatchDelegate::ThemeSwatchDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
QSize ThemeSwatchDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex & /*index*/) const
{
return QSize(CardWidth + 2 * Margin,
SwatchHeight + option.fontMetrics.height() + 4 * Margin);
}
void ThemeSwatchDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
const QPalette &pal = option.palette;
const bool selected = option.state & QStyle::State_Selected;
const bool hovered = option.state & QStyle::State_MouseOver;
const bool enabled = option.state & QStyle::State_Enabled;
QRectF card = QRectF(option.rect).adjusted(Margin / 2.0, Margin / 2.0, -Margin / 2.0, -Margin / 2.0);
// card frame
if (selected) {
painter->setPen(QPen(pal.color(QPalette::Highlight), 2));
painter->setBrush(pal.color(QPalette::Highlight).lighter(170));
} else {
painter->setPen(QPen(hovered ? pal.color(QPalette::Highlight) : pal.color(QPalette::Mid), 1));
painter->setBrush(pal.color(QPalette::Base));
}
painter->drawRoundedRect(card, Radius, Radius);
// swatch with background colour and some coloured "code" bars
QRectF swatch(card.left() + Margin, card.top() + Margin,
card.width() - 2 * Margin, SwatchHeight);
QColor bg = index.data(ThemeListModel::BackgroundRole).value<QColor>();
painter->setPen(QPen(pal.color(QPalette::Mid), 1));
painter->setBrush(bg);
painter->drawRoundedRect(swatch, Radius, Radius);
const QList<QList<Bar>> lines = {
{ { ThemeListModel::KeywordRole, 0.18 }, { ThemeListModel::TypeRole, 0.22 }, { ThemeListModel::ForegroundRole, 0.25 } },
{ { ThemeListModel::ForegroundRole, 0.12 }, { ThemeListModel::StringRole, 0.40 }, { ThemeListModel::NumberRole, 0.10 } },
{ { ThemeListModel::KeywordRole, 0.14 }, { ThemeListModel::NumberRole, 0.12 }, { ThemeListModel::ForegroundRole, 0.20 } },
{ { ThemeListModel::CommentRole, 0.70 } }
};
const qreal pad = 6.0;
const qreal gap = 4.0;
const qreal lineHeight = (SwatchHeight - 2 * pad) / lines.size();
const qreal barHeight = lineHeight * 0.55;
const qreal usable = swatch.width() - 2 * pad;
painter->setPen(Qt::NoPen);
for (int l = 0; l < lines.size(); ++l) {
qreal x = swatch.left() + pad;
qreal y = swatch.top() + pad + l * lineHeight + (lineHeight - barHeight) / 2;
for (const Bar &bar : lines.at(l)) {
qreal w = usable * bar.width;
painter->setBrush(index.data(bar.role).value<QColor>());
painter->drawRoundedRect(QRectF(x, y, w, barHeight), barHeight / 2, barHeight / 2);
x += w + gap;
}
}
// theme name
QRectF textRect(card.left() + Margin, swatch.bottom() + Margin,
card.width() - 2 * Margin, option.fontMetrics.height());
QString name = option.fontMetrics.elidedText(index.data(Qt::DisplayRole).toString(),
Qt::ElideRight, int(textRect.width()));
QPalette::ColorGroup group = enabled ? QPalette::Normal : QPalette::Disabled;
// When selected the card background is Highlight.lighter(170) – a pale colour.
// In dark mode QPalette::Text is near-white, which is unreadable on that background,
// so force a dark caption colour for the selected state.
QColor captionColor = selected ? QColor(Qt::black) : pal.color(group, QPalette::Text);
painter->setPen(captionColor);
painter->setFont(option.font);
painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignVCenter, name);
if (!enabled) {
QColor veil = pal.color(QPalette::Window);
veil.setAlpha(140);
painter->setPen(Qt::NoPen);
painter->setBrush(veil);
painter->drawRoundedRect(card, Radius, Radius);
}
painter->restore();
}