@ 261105.1
current
/***************************************************************************
                               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();
}