File indexing completed on 2024-05-19 05:14:38

0001 /*
0002   This file is part of KAddressBook.
0003 
0004   SPDX-FileCopyrightText: 2020 Konrad Czapla <kondzio89dev@gmail.com>
0005 
0006   SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "stylecontactlistdelegate.h"
0010 #include "contactinfoproxymodel.h"
0011 
0012 #include <Akonadi/ContactsTreeModel>
0013 
0014 #include <QApplication>
0015 #include <QImage>
0016 #include <QPainter>
0017 #include <QPainterPath>
0018 
0019 namespace
0020 {
0021 // SPDX-SnippetBegin
0022 // SPDX-FileCopyrightText: 2020 Carson Black <uhhadd@gmail.com>
0023 // SPDX-License-Identifier: LGPL-2.0-or-later
0024 
0025 /* clang-format off */
0026 const QList<QColor> c_colors = {
0027     QColor("#e93a9a"),
0028     QColor("#e93d58"),
0029     QColor("#e9643a"),
0030     QColor("#ef973c"),
0031     QColor("#e8cb2d"),
0032     QColor("#b6e521"),
0033     QColor("#3dd425"),
0034     QColor("#00d485"),
0035     QColor("#00d3b8"),
0036     QColor("#3daee9"),
0037     QColor("#b875dc"),
0038     QColor("#926ee4"),
0039 };
0040 /* clang-format on */
0041 
0042 QColor colorsFromString(const QString &string)
0043 {
0044     // We use a hash to get a "random" number that's always the same for
0045     // a given string.
0046     auto hash = qHash(string);
0047     // hash modulo the length of the colors list minus one will always get us a valid
0048     // index
0049     auto index = hash % (c_colors.length() - 1);
0050     // return a colour
0051     return c_colors[index];
0052 }
0053 
0054 // SPDX-SnippetEnd
0055 
0056 }
0057 
0058 StyleContactListDelegate::StyleContactListDelegate(QObject *parent)
0059     : QStyledItemDelegate(parent)
0060     , mKImageSize(50, 50)
0061 {
0062 }
0063 
0064 void StyleContactListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0065 {
0066     Q_ASSERT(index.isValid());
0067 
0068     if (Akonadi::ContactsTreeModel::Column::FullName == index.column()) {
0069         QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget);
0070 
0071         const QRectF optionRect = option.rect.marginsRemoved(QMargins() + static_cast<int>(mKMargin));
0072 
0073         QRectF pictureRect = QRectF(optionRect.topLeft(), mKImageSize);
0074         if (mKImageSize.width() > optionRect.size().width()) {
0075             const qreal width = option.rect.size().width();
0076             const qreal height = option.rect.size().height();
0077             const QMargins fitMargins(0, (qMax(width, height) - qMin(width, height)) / qreal(2), 0, (qMax(width, height) - qMin(width, height)) / qreal(2));
0078 
0079             pictureRect = optionRect.marginsRemoved(fitMargins);
0080         }
0081         QRectF nameTextRect(optionRect.topLeft(), QSize(optionRect.width(), optionRect.height() / qreal(2)));
0082         QRectF descriptionTextRect = nameTextRect;
0083         descriptionTextRect.moveBottomLeft(optionRect.bottomLeft());
0084 
0085         QMargins textMargin;
0086         switch (static_cast<int>(option.widget->layoutDirection())) {
0087         case Qt::LayoutDirection::LeftToRight:
0088             textMargin.setLeft(mKMargin);
0089 
0090             nameTextRect.setLeft(pictureRect.bottomRight().x());
0091             nameTextRect = nameTextRect.marginsRemoved(textMargin);
0092 
0093             descriptionTextRect.setLeft(pictureRect.bottomRight().x());
0094             descriptionTextRect = descriptionTextRect.marginsRemoved(textMargin);
0095             break;
0096         case Qt::LayoutDirection::RightToLeft:
0097             pictureRect.moveRight(optionRect.bottomRight().x());
0098             textMargin.setRight(mKMargin);
0099 
0100             nameTextRect.setRight(pictureRect.bottomLeft().x());
0101             nameTextRect = nameTextRect.marginsRemoved(textMargin);
0102 
0103             descriptionTextRect.setRight(pictureRect.bottomLeft().x());
0104             descriptionTextRect = descriptionTextRect.marginsRemoved(textMargin);
0105             break;
0106         }
0107 
0108         const auto name = index.data(Qt::DisplayRole).value<QString>();
0109 
0110         QPainterPath path;
0111         path.addEllipse(pictureRect);
0112         painter->save();
0113         painter->setClipPath(path);
0114         painter->setPen(QPen(colorsFromString(name), qreal(4)));
0115         painter->setRenderHint(QPainter::Antialiasing);
0116         painter->drawPath(path);
0117         painter->setFont(QFont(option.font.family(), 12, QFont::Bold, true));
0118 
0119         auto image(index.data(ContactInfoProxyModel::Roles::PictureRole).value<QImage>());
0120         if (image.isNull()) {
0121             const auto initials = index.data(ContactInfoProxyModel::Roles::InitialsRole).value<QString>();
0122             painter->drawText(pictureRect, Qt::AlignCenter, painter->fontMetrics().elidedText(initials, Qt::ElideRight, pictureRect.width() - qreal(10)));
0123         } else {
0124             const qreal dpr = qApp->devicePixelRatio();
0125             image = image.scaled(mKImageSize * dpr, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0126             image.setDevicePixelRatio(dpr);
0127             painter->drawImage(pictureRect, image);
0128         }
0129 
0130         painter->restore();
0131 
0132         if (!name.isEmpty()) {
0133             painter->setFont(QFont(option.font.family(), 11));
0134             painter->drawText(nameTextRect, Qt::AlignLeft | Qt::AlignVCenter, painter->fontMetrics().elidedText(name, Qt::ElideRight, nameTextRect.width()));
0135         }
0136 
0137         const auto description = index.data(ContactInfoProxyModel::Roles::DescriptionRole).value<QString>();
0138         if (!description.isEmpty()) {
0139             painter->setFont(QFont(option.font.family(), 8, -1, true));
0140             painter->drawText(descriptionTextRect,
0141                               Qt::AlignLeft | Qt::AlignVCenter,
0142                               painter->fontMetrics().elidedText(description, Qt::ElideRight, descriptionTextRect.width()));
0143         }
0144         return;
0145     }
0146     QStyledItemDelegate::paint(painter, option, index);
0147 }
0148 
0149 QSize StyleContactListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0150 {
0151     QSize size = QStyledItemDelegate::sizeHint(option, index);
0152     size.setHeight(mKImageSize.height() + (qreal(2) * mKMargin));
0153 
0154     return size;
0155 }
0156 
0157 #include "moc_stylecontactlistdelegate.cpp"