File indexing completed on 2024-04-28 15:32: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 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0036 void KRatingWidget::setPixmap(const QPixmap &pix)
0037 {
0038     setCustomPixmap(pix);
0039 }
0040 #endif
0041 
0042 void KRatingWidget::setCustomPixmap(const QPixmap &pix)
0043 {
0044     d->ratingPainter.setCustomPixmap(pix);
0045     update();
0046 }
0047 
0048 void KRatingWidget::setIcon(const QIcon &icon)
0049 {
0050     d->ratingPainter.setIcon(icon);
0051     update();
0052 }
0053 
0054 void KRatingWidget::setPixmapSize(int size)
0055 {
0056     d->pixSize = size;
0057     updateGeometry();
0058 }
0059 
0060 int KRatingWidget::spacing() const
0061 {
0062     return d->ratingPainter.spacing();
0063 }
0064 
0065 QIcon KRatingWidget::icon() const
0066 {
0067     return d->ratingPainter.icon();
0068 }
0069 
0070 void KRatingWidget::setSpacing(int s)
0071 {
0072     d->ratingPainter.setSpacing(s);
0073     update();
0074 }
0075 
0076 Qt::Alignment KRatingWidget::alignment() const
0077 {
0078     return d->ratingPainter.alignment();
0079 }
0080 
0081 void KRatingWidget::setAlignment(Qt::Alignment align)
0082 {
0083     d->ratingPainter.setAlignment(align);
0084     update();
0085 }
0086 
0087 Qt::LayoutDirection KRatingWidget::layoutDirection() const
0088 {
0089     return d->ratingPainter.layoutDirection();
0090 }
0091 
0092 void KRatingWidget::setLayoutDirection(Qt::LayoutDirection direction)
0093 {
0094     d->ratingPainter.setLayoutDirection(direction);
0095     update();
0096 }
0097 
0098 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 85)
0099 unsigned int KRatingWidget::rating() const
0100 #else
0101 int KRatingWidget::rating() const
0102 #endif
0103 {
0104     return d->rating;
0105 }
0106 
0107 int KRatingWidget::maxRating() const
0108 {
0109     return d->ratingPainter.maxRating();
0110 }
0111 
0112 bool KRatingWidget::halfStepsEnabled() const
0113 {
0114     return d->ratingPainter.halfStepsEnabled();
0115 }
0116 
0117 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0118 void KRatingWidget::setRating(unsigned int rating)
0119 {
0120     setRating((int)rating);
0121 }
0122 #endif
0123 
0124 void KRatingWidget::setRating(int rating)
0125 {
0126     if (rating != d->rating) {
0127         d->rating = rating;
0128         d->hoverRating = rating;
0129         Q_EMIT ratingChanged(rating);
0130 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 85)
0131         Q_EMIT ratingChanged((unsigned int)rating);
0132 #endif
0133         update();
0134     }
0135 }
0136 
0137 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0138 void KRatingWidget::setMaxRating(unsigned int max)
0139 {
0140     setMaxRating((int)max);
0141 }
0142 #endif
0143 
0144 void KRatingWidget::setMaxRating(int max)
0145 {
0146     d->ratingPainter.setMaxRating(max);
0147     update();
0148 }
0149 
0150 void KRatingWidget::setHalfStepsEnabled(bool enabled)
0151 {
0152     d->ratingPainter.setHalfStepsEnabled(enabled);
0153     update();
0154 }
0155 
0156 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0157 void KRatingWidget::setOnlyPaintFullSteps(bool fs)
0158 {
0159     setHalfStepsEnabled(!fs);
0160 }
0161 #endif
0162 
0163 static inline int adjustedHoverRating(bool halfStep, int hoverRating, int rating)
0164 {
0165     // intentionally skip zero, or half step is disabled.
0166     if (!halfStep || hoverRating == 0) {
0167         return hoverRating;
0168     }
0169 
0170     // See bug 171343, if we click on a star we want it to be full star, click again
0171     // make it half, click third time make it clear.
0172 
0173     // round up hoverRating to next full star rating
0174     const int hoveredFullStarRating = hoverRating + (hoverRating % 2);
0175     // Check if the star under mouse is the last half or whole star of the rating.
0176     if (hoveredFullStarRating == rating || hoveredFullStarRating == rating + 1) {
0177         // If current pointed star is not empty, next rating will be rating - 1
0178         // if we point at 4th star and rating is 8 (4 star), next click will make it 7
0179         // if we point at 4th star and rating is 7 (3.5 star), next click will make it 6
0180         hoverRating = rating - 1;
0181     } else {
0182         // otherwise make it a full star rating
0183         hoverRating = hoveredFullStarRating;
0184     }
0185     return hoverRating;
0186 }
0187 
0188 void KRatingWidget::mousePressEvent(QMouseEvent *e)
0189 {
0190     if (e->button() == Qt::LeftButton) {
0191         d->hoverRating = adjustedHoverRating(halfStepsEnabled(), d->ratingPainter.ratingFromPosition(contentsRect(), e->pos()), d->rating);
0192         // avoid set a rating to something less than zero, it may happen if widget is scaled and
0193         // mouse is clicked outside the star region.
0194         if (d->hoverRating >= 0) {
0195             setRating(d->hoverRating);
0196         }
0197     }
0198 }
0199 
0200 void KRatingWidget::mouseMoveEvent(QMouseEvent *e)
0201 {
0202     // when moving the mouse we show the user what the result of clicking will be
0203     const int prevHoverRating = d->hoverRating;
0204     d->hoverRating = adjustedHoverRating(halfStepsEnabled(), d->ratingPainter.ratingFromPosition(contentsRect(), e->pos()), d->rating);
0205     if (d->hoverRating != prevHoverRating) {
0206         update();
0207     }
0208 }
0209 
0210 void KRatingWidget::leaveEvent(QEvent *)
0211 {
0212     d->hoverRating = -1;
0213     update();
0214 }
0215 
0216 void KRatingWidget::paintEvent(QPaintEvent *e)
0217 {
0218     QFrame::paintEvent(e);
0219     QPainter p(this);
0220     d->ratingPainter.setEnabled(isEnabled());
0221     d->ratingPainter.paint(&p, contentsRect(), d->rating, d->hoverRating);
0222 }
0223 
0224 QSize KRatingWidget::sizeHint() const
0225 {
0226     int numPix = d->ratingPainter.maxRating();
0227     if (d->ratingPainter.halfStepsEnabled()) {
0228         numPix /= 2;
0229     }
0230 
0231     QSize pixSize(d->pixSize, d->pixSize);
0232     if (!d->ratingPainter.customPixmap().isNull()) {
0233         pixSize = d->ratingPainter.customPixmap().size() / d->ratingPainter.customPixmap().devicePixelRatio();
0234     }
0235 
0236     return QSize(pixSize.width() * numPix + spacing() * (numPix - 1) + frameWidth() * 2, pixSize.height() + frameWidth() * 2);
0237 }
0238 
0239 void KRatingWidget::resizeEvent(QResizeEvent *e)
0240 {
0241     QFrame::resizeEvent(e);
0242 }
0243 
0244 #include "moc_kratingwidget.cpp"