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 }