File indexing completed on 2024-04-28 03:59:12

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2006-2007 Sebastian Trueg <trueg@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kratingwidget.h"
0009 #include "kratingpainter.h"
0010 
0011 #include <QIcon>
0012 #include <QMouseEvent>
0013 #include <QPainter>
0014 #include <QPixmap>
0015 
0016 class KRatingWidgetPrivate
0017 {
0018 public:
0019     int rating = 0;
0020     int hoverRating = -1;
0021     int pixSize = 16;
0022 
0023     KRatingPainter ratingPainter;
0024 };
0025 
0026 KRatingWidget::KRatingWidget(QWidget *parent)
0027     : QFrame(parent)
0028     , d(new KRatingWidgetPrivate())
0029 {
0030     setMouseTracking(true);
0031 }
0032 
0033 KRatingWidget::~KRatingWidget() = default;
0034 
0035 void KRatingWidget::setCustomPixmap(const QPixmap &pix)
0036 {
0037     d->ratingPainter.setCustomPixmap(pix);
0038     update();
0039 }
0040 
0041 void KRatingWidget::setIcon(const QIcon &icon)
0042 {
0043     d->ratingPainter.setIcon(icon);
0044     update();
0045 }
0046 
0047 void KRatingWidget::setPixmapSize(int size)
0048 {
0049     d->pixSize = size;
0050     updateGeometry();
0051 }
0052 
0053 int KRatingWidget::spacing() const
0054 {
0055     return d->ratingPainter.spacing();
0056 }
0057 
0058 QIcon KRatingWidget::icon() const
0059 {
0060     return d->ratingPainter.icon();
0061 }
0062 
0063 void KRatingWidget::setSpacing(int s)
0064 {
0065     d->ratingPainter.setSpacing(s);
0066     update();
0067 }
0068 
0069 Qt::Alignment KRatingWidget::alignment() const
0070 {
0071     return d->ratingPainter.alignment();
0072 }
0073 
0074 void KRatingWidget::setAlignment(Qt::Alignment align)
0075 {
0076     d->ratingPainter.setAlignment(align);
0077     update();
0078 }
0079 
0080 Qt::LayoutDirection KRatingWidget::layoutDirection() const
0081 {
0082     return d->ratingPainter.layoutDirection();
0083 }
0084 
0085 void KRatingWidget::setLayoutDirection(Qt::LayoutDirection direction)
0086 {
0087     d->ratingPainter.setLayoutDirection(direction);
0088     update();
0089 }
0090 
0091 int KRatingWidget::rating() const
0092 {
0093     return d->rating;
0094 }
0095 
0096 int KRatingWidget::maxRating() const
0097 {
0098     return d->ratingPainter.maxRating();
0099 }
0100 
0101 bool KRatingWidget::halfStepsEnabled() const
0102 {
0103     return d->ratingPainter.halfStepsEnabled();
0104 }
0105 
0106 void KRatingWidget::setRating(int rating)
0107 {
0108     if (rating != d->rating) {
0109         d->rating = rating;
0110         d->hoverRating = rating;
0111         Q_EMIT ratingChanged(rating);
0112         update();
0113     }
0114 }
0115 
0116 void KRatingWidget::setMaxRating(int max)
0117 {
0118     d->ratingPainter.setMaxRating(max);
0119     update();
0120 }
0121 
0122 void KRatingWidget::setHalfStepsEnabled(bool enabled)
0123 {
0124     d->ratingPainter.setHalfStepsEnabled(enabled);
0125     update();
0126 }
0127 
0128 static inline int adjustedHoverRating(bool halfStep, int hoverRating, int rating)
0129 {
0130     // intentionally skip zero, or half step is disabled.
0131     if (!halfStep || hoverRating == 0) {
0132         return hoverRating;
0133     }
0134 
0135     // See bug 171343, if we click on a star we want it to be full star, click again
0136     // make it half, click third time make it clear.
0137 
0138     // round up hoverRating to next full star rating
0139     const int hoveredFullStarRating = hoverRating + (hoverRating % 2);
0140     // Check if the star under mouse is the last half or whole star of the rating.
0141     if (hoveredFullStarRating == rating || hoveredFullStarRating == rating + 1) {
0142         // If current pointed star is not empty, next rating will be rating - 1
0143         // if we point at 4th star and rating is 8 (4 star), next click will make it 7
0144         // if we point at 4th star and rating is 7 (3.5 star), next click will make it 6
0145         hoverRating = rating - 1;
0146     } else {
0147         // otherwise make it a full star rating
0148         hoverRating = hoveredFullStarRating;
0149     }
0150     return hoverRating;
0151 }
0152 
0153 void KRatingWidget::mousePressEvent(QMouseEvent *e)
0154 {
0155     if (e->button() == Qt::LeftButton) {
0156         d->hoverRating = adjustedHoverRating(halfStepsEnabled(), d->ratingPainter.ratingFromPosition(contentsRect(), e->pos()), d->rating);
0157         // avoid set a rating to something less than zero, it may happen if widget is scaled and
0158         // mouse is clicked outside the star region.
0159         if (d->hoverRating >= 0) {
0160             setRating(d->hoverRating);
0161         }
0162     }
0163 }
0164 
0165 void KRatingWidget::mouseMoveEvent(QMouseEvent *e)
0166 {
0167     // when moving the mouse we show the user what the result of clicking will be
0168     const int prevHoverRating = d->hoverRating;
0169     d->hoverRating = adjustedHoverRating(halfStepsEnabled(), d->ratingPainter.ratingFromPosition(contentsRect(), e->pos()), d->rating);
0170     if (d->hoverRating != prevHoverRating) {
0171         update();
0172     }
0173 }
0174 
0175 void KRatingWidget::leaveEvent(QEvent *)
0176 {
0177     d->hoverRating = -1;
0178     update();
0179 }
0180 
0181 void KRatingWidget::paintEvent(QPaintEvent *e)
0182 {
0183     QFrame::paintEvent(e);
0184     QPainter p(this);
0185     d->ratingPainter.setEnabled(isEnabled());
0186     d->ratingPainter.paint(&p, contentsRect(), d->rating, d->hoverRating);
0187 }
0188 
0189 QSize KRatingWidget::sizeHint() const
0190 {
0191     int numPix = d->ratingPainter.maxRating();
0192     if (d->ratingPainter.halfStepsEnabled()) {
0193         numPix /= 2;
0194     }
0195 
0196     QSize pixSize(d->pixSize, d->pixSize);
0197     if (!d->ratingPainter.customPixmap().isNull()) {
0198         pixSize = d->ratingPainter.customPixmap().size() / d->ratingPainter.customPixmap().devicePixelRatio();
0199     }
0200 
0201     return QSize(pixSize.width() * numPix + spacing() * (numPix - 1) + frameWidth() * 2, pixSize.height() + frameWidth() * 2);
0202 }
0203 
0204 void KRatingWidget::resizeEvent(QResizeEvent *e)
0205 {
0206     QFrame::resizeEvent(e);
0207 }
0208 
0209 #include "moc_kratingwidget.cpp"