File indexing completed on 2024-05-19 04:07:52
0001 /* 0002 SPDX-FileCopyrightText: 2009 Stefan Majewsky <majewsky@gmx.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "collection-delegate.h" 0008 #include "collection.h" 0009 #include "puzzlestructs.h" 0010 0011 #include <QAbstractItemView> 0012 #include <QApplication> 0013 #include <QFont> 0014 #include <QFontMetrics> 0015 #include <QPainter> 0016 #include <KLocalizedString> 0017 0018 const QSize Palapeli::PuzzleMetadata::ThumbnailBaseSize(64, 64); 0019 0020 namespace Metrics 0021 { 0022 const int Padding = 6; 0023 } 0024 0025 Palapeli::CollectionDelegate::CollectionDelegate (QObject* parent) 0026 : QStyledItemDelegate(parent) 0027 { 0028 QAbstractItemView* view = qobject_cast<QAbstractItemView*>(parent); 0029 if (view) { 0030 view->setItemDelegate(this); 0031 m_viewport = view->viewport(); 0032 } 0033 } 0034 0035 QRect Palapeli::CollectionDelegate::thumbnailRect (const QRect& baseRect) const 0036 { 0037 QRect thumbnailBaseRect (QPoint(Metrics::Padding + baseRect.left(), 0), 0038 Palapeli::PuzzleMetadata::ThumbnailBaseSize); 0039 thumbnailBaseRect.moveCenter (QPoint(thumbnailBaseRect.center().x(), 0040 baseRect.center().y())); 0041 if (QApplication::isRightToLeft()) { 0042 thumbnailBaseRect.moveRight (baseRect.right() - Metrics::Padding); 0043 } 0044 return thumbnailBaseRect; 0045 } 0046 0047 void Palapeli::CollectionDelegate::paint ( 0048 QPainter* painter, 0049 const QStyleOptionViewItem& option, 0050 const QModelIndex& index) const 0051 { 0052 QRect baseRect = option.rect; // Total space available for item. 0053 0054 // Calculate item's column number in list-view. Add 1 in odd-numbered rows. 0055 int nItemsPerRow = qMax (m_viewport->width() / baseRect.width(), 1); 0056 int oddColumn = index.row() % nItemsPerRow; 0057 oddColumn = oddColumn + ((index.row() / nItemsPerRow) % 2); 0058 0059 // Draw background of item. 0060 QColor bkgColor; 0061 if (option.state & QStyle::State_Selected) { 0062 bkgColor = option.palette.color (QPalette::Highlight); 0063 } else if (oddColumn % 2) { 0064 // The shading alternates along each row in the list view and 0065 // each odd-numbered row starts with a shaded item, so we have 0066 // a checkerboard pattern, regardless of whether nItemsPerRow 0067 // is odd or even (see the above calculation). 0068 bkgColor = option.palette.color (QPalette::AlternateBase); 0069 } else { 0070 bkgColor = option.palette.color (QPalette::Base); 0071 } 0072 painter->fillRect (option.rect, bkgColor); 0073 0074 // Draw thumbnail. 0075 QRect thumbnailBaseRect = this->thumbnailRect (baseRect); 0076 const QPixmap thumbnail = index.data (Palapeli::Collection::ThumbnailRole) 0077 .value<QPixmap>(); 0078 QRect thumbnailRect (thumbnailBaseRect.topLeft(), thumbnail.size()); 0079 thumbnailRect.translate ( // Center inside thumbnailBaseRect. 0080 (thumbnailBaseRect.width() - thumbnailRect.width()) / 2, 0081 (thumbnailBaseRect.height() - thumbnailRect.height()) / 2); 0082 painter->drawPixmap (thumbnailRect.topLeft(), thumbnail); 0083 0084 // Calculate the maximum space available for text lines. 0085 QRect textBaseRect (baseRect); 0086 textBaseRect.setWidth (baseRect.width() - thumbnailBaseRect.width() 0087 - 2*Metrics::Padding); 0088 if (option.direction == Qt::RightToLeft) { 0089 textBaseRect.moveRight (thumbnailBaseRect.left() - Metrics::Padding); 0090 textBaseRect.adjust (Metrics::Padding, Metrics::Padding, 0091 0, -Metrics::Padding); 0092 } 0093 else { 0094 textBaseRect.moveLeft (thumbnailBaseRect.right() + Metrics::Padding); 0095 textBaseRect.adjust (0, Metrics::Padding, 0096 -Metrics::Padding, -Metrics::Padding); 0097 } 0098 0099 // Find the contents and sizes for the text lines. 0100 QStringList texts; QList<QFont> fonts; 0101 { 0102 QString name = index.data(Palapeli::Collection::NameRole).toString(); 0103 const int pieceCount = index.data (Palapeli::Collection::PieceCountRole) 0104 .toInt(); 0105 if (name.isEmpty()) { 0106 name = i18n("[No name]"); 0107 } 0108 if (pieceCount > 0) { 0109 name = ki18ncp ( 0110 "Puzzle description, %2 = name string, %1 = piece count", 0111 "%2 (%1 piece)", 0112 "%2 (%1 pieces)") 0113 .subs(pieceCount).subs(name).toString(); 0114 } 0115 texts << name; 0116 QFont theFont (painter->font()); 0117 theFont.setBold(true); 0118 fonts << theFont; 0119 }{ 0120 QString comment = index.data (Palapeli::Collection::CommentRole) 0121 .toString(); 0122 if (!comment.isEmpty()) { 0123 texts << comment; 0124 fonts << painter->font(); 0125 } 0126 }{ 0127 QString author = index.data (Palapeli::Collection::AuthorRole) 0128 .toString(); 0129 if (!author.isEmpty()) { 0130 const QString authorString = ki18nc ( 0131 "Author attribution, e.g. \"by Jack\"", 0132 "by %1") 0133 .subs(author).toString(); 0134 texts << authorString; 0135 QFont theFont (painter->font()); 0136 theFont.setItalic(true); 0137 fonts << theFont; 0138 } 0139 } 0140 QList<QRect> textRects; 0141 int totalTextHeight = 0; 0142 QRect maxRect (QPoint(0, 0), textBaseRect.size()); 0143 for (int i = 0; i < texts.count(); ++i) { 0144 QFontMetrics fm(fonts[i]); 0145 textRects << fm.boundingRect (maxRect, Qt::AlignLeft | Qt::AlignTop | 0146 Qt::TextWordWrap, texts[i]); 0147 totalTextHeight += textRects[i].height(); 0148 } 0149 0150 // Vertically center however many text lines there are. 0151 textBaseRect.setHeight (totalTextHeight); 0152 textBaseRect.moveTop (baseRect.top() + 0153 (baseRect.height() - textBaseRect.height()) / 2); 0154 0155 // Draw the text lines. 0156 QRect currentTextRect (textBaseRect); 0157 painter->save(); 0158 for (int i = 0; i < texts.count(); ++i) { 0159 painter->setFont(fonts[i]); 0160 const QRect& textRect = textRects[i]; 0161 currentTextRect.setHeight(textRect.height()); 0162 painter->drawText (currentTextRect, 0163 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, 0164 texts[i]); 0165 currentTextRect.moveTop (currentTextRect.bottom()); 0166 } 0167 painter->restore(); 0168 } 0169 0170 QSize Palapeli::CollectionDelegate::sizeHint ( 0171 const QStyleOptionViewItem& option, 0172 const QModelIndex& index) const 0173 { 0174 Q_UNUSED(index) 0175 0176 // Fit a varying number of columns into the list-view. 0177 int minWidth = 4 * (Palapeli::PuzzleMetadata::ThumbnailBaseSize.width() + 0178 Metrics::Padding); 0179 int viewportWidth = m_viewport->width(); 0180 int hintWidth = minWidth; 0181 int nItemsPerRow = viewportWidth / minWidth; 0182 0183 // Expand the hinted width, so that the columns will fill the viewport. 0184 if (nItemsPerRow > 0) { 0185 // The 0:1 adjustment works around a graphics glitch, when nItemsPerRow 0186 // exactly divides viewportWidth and instead of nItemsPerRow columns 0187 // we suddenly get one column less, plus a large empty space, even 0188 // though QListView::spacing() is zero. 0189 viewportWidth = viewportWidth - ((viewportWidth % nItemsPerRow) ? 0:1); 0190 hintWidth = viewportWidth / nItemsPerRow; 0191 } 0192 0193 // Set the height to contain the thumbnail or 4 lines of text. 0194 int hintHeight = Palapeli::PuzzleMetadata::ThumbnailBaseSize.height(); 0195 int fontHeight = option.fontMetrics.height(); 0196 if (hintHeight < fontHeight*4) { 0197 hintHeight = fontHeight*4; 0198 } 0199 0200 return QSize(hintWidth, hintHeight + 2 * Metrics::Padding); 0201 }