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"