diff --git a/src/gui/file/filedelegate.cpp b/src/gui/file/filedelegate.cpp index a269c0c5..40f93176 100644 --- a/src/gui/file/filedelegate.cpp +++ b/src/gui/file/filedelegate.cpp @@ -1,39 +1,46 @@ /*************************************************************************** - * Copyright (C) 2004-2018 by Thomas Fischer * + * Copyright (C) 2004-2019 by Thomas Fischer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ***************************************************************************/ #include "filedelegate.h" #include +#include #include #include #include #include "widgets/starrating.h" void FileDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyledItemDelegate::paint(painter, option, index); static const int numTotalStars = 8; bool ok = false; double percent = index.data(FileModel::NumberRole).toDouble(&ok); if (ok) { const FieldDescription &fd = BibTeXFields::instance().at(index.column()); - if (fd.upperCamelCase.toLower() == Entry::ftStarRating) - StarRating::paintStars(painter, KIconLoader::DefaultState, numTotalStars, percent, option.rect); + if (fd.upperCamelCase.toLower() == Entry::ftStarRating) { + static KRatingPainter ratingPainter; + ratingPainter.setAlignment(Qt::AlignVCenter | Qt::AlignLeft); + ratingPainter.setHalfStepsEnabled(false); + ratingPainter.setMaxRating(numTotalStars); + ratingPainter.setLayoutDirection(qobject_cast(parent())->layoutDirection()); + ratingPainter.paint(painter, option.rect, static_cast(percent / 100.0 * numTotalStars)); + } } } diff --git a/src/gui/widgets/starrating.cpp b/src/gui/widgets/starrating.cpp index 11e348ba..b0ca338c 100644 --- a/src/gui/widgets/starrating.cpp +++ b/src/gui/widgets/starrating.cpp @@ -1,304 +1,259 @@ /***************************************************************************** * Copyright (C) 2004-2019 by Thomas Fischer * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * *****************************************************************************/ #include "starrating.h" #include #include #include #include #include #include #include #include #include +#include class StarRating::Private { private: StarRating *p; public: static const int paintMargin; bool isReadOnly; double percent; int maxNumberOfStars; int spacing; const QString unsetStarsText; QLabel *labelPercent; QPushButton *clearButton; QPoint mouseLocation; Private(int mnos, StarRating *parent) : p(parent), isReadOnly(false), percent(-1.0), maxNumberOfStars(mnos), unsetStarsText(i18n("Not set")) { QHBoxLayout *layout = new QHBoxLayout(p); spacing = qMax(layout->spacing(), 8); layout->setContentsMargins(0, 0, 0, 0); labelPercent = new QLabel(p); layout->addWidget(labelPercent, 0, Qt::AlignRight | Qt::AlignVCenter); const QFontMetrics fm(labelPercent->fontMetrics()); #if QT_VERSION >= 0x050b00 labelPercent->setFixedWidth(fm.horizontalAdvance(unsetStarsText)); #else // QT_VERSION >= 0x050b00 labelPercent->setFixedWidth(fm.width(unsetStarsText)); #endif // QT_VERSION >= 0x050b00 labelPercent->setAlignment(Qt::AlignRight | Qt::AlignVCenter); labelPercent->setText(unsetStarsText); labelPercent->installEventFilter(parent); layout->addStretch(1); clearButton = new QPushButton(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-rtl")), QString(), p); layout->addWidget(clearButton, 0, Qt::AlignRight | Qt::AlignVCenter); connect(clearButton, &QPushButton::clicked, p, &StarRating::clear); clearButton->installEventFilter(parent); } QRect starsInside() const { const int starRectHeight = qMin(labelPercent->height() * 3 / 2, clearButton->height()); return QRect(QPoint(labelPercent->width() + spacing, (p->height() - starRectHeight) / 2), QSize(p->width() - 2 * spacing - clearButton->width() - labelPercent->width(), starRectHeight)); } double percentForPosition(const QPoint pos, int numTotalStars, const QRect inside) { const int starSize = qMin(inside.height() - 2 * Private::paintMargin, (inside.width() - 2 * Private::paintMargin) / numTotalStars); const int width = starSize * numTotalStars; const int x = pos.x() - Private::paintMargin - inside.left(); const double percent = x * 100.0 / width; return qMax(0.0, qMin(100.0, percent)); } }; const int StarRating::Private::paintMargin = 2; StarRating::StarRating(int maxNumberOfStars, QWidget *parent) : QWidget(parent), d(new Private(maxNumberOfStars, this)) { QTimer::singleShot(250, this, &StarRating::buttonHeight); setMouseTracking(true); } void StarRating::paintEvent(QPaintEvent *ev) { QWidget::paintEvent(ev); QPainter p(this); const QRect r = d->starsInside(); const double percent = d->mouseLocation.isNull() ? d->percent : d->percentForPosition(d->mouseLocation, d->maxNumberOfStars, r); + static KRatingPainter ratingPainter; + ratingPainter.setAlignment(Qt::AlignVCenter | Qt::AlignLeft); + ratingPainter.setHalfStepsEnabled(false); + ratingPainter.setMaxRating(d->maxNumberOfStars); + ratingPainter.setLayoutDirection(qobject_cast(parent())->layoutDirection()); + if (percent >= 0.0) { - paintStars(&p, KIconLoader::DefaultState, d->maxNumberOfStars, percent, d->starsInside()); + ratingPainter.paint(&p, d->starsInside(), static_cast(percent / 100.0 * d->maxNumberOfStars)); if (d->maxNumberOfStars < 10) d->labelPercent->setText(QString::number(percent * d->maxNumberOfStars / 100.0, 'f', 1)); else d->labelPercent->setText(QString::number(percent * d->maxNumberOfStars / 100)); } else { - p.setOpacity(0.7); - paintStars(&p, KIconLoader::DisabledState, d->maxNumberOfStars, 0.0, d->starsInside()); + p.setOpacity(0.5); + ratingPainter.paint(&p, d->starsInside(), static_cast(percent / 100.0 * d->maxNumberOfStars)); d->labelPercent->setText(d->unsetStarsText); } ev->accept(); } void StarRating::mouseReleaseEvent(QMouseEvent *ev) { QWidget::mouseReleaseEvent(ev); if (!d->isReadOnly && ev->button() == Qt::LeftButton) { d->mouseLocation = QPoint(); const double newPercent = d->percentForPosition(ev->pos(), d->maxNumberOfStars, d->starsInside()); setValue(newPercent); emit modified(); ev->accept(); } } void StarRating::mouseMoveEvent(QMouseEvent *ev) { QWidget::mouseMoveEvent(ev); if (!d->isReadOnly) { d->mouseLocation = ev->pos(); if (d->mouseLocation.x() < d->labelPercent->width() || d->mouseLocation.x() > width() - d->clearButton->width()) d->mouseLocation = QPoint(); update(); ev->accept(); } } void StarRating::leaveEvent(QEvent *ev) { QWidget::leaveEvent(ev); if (!d->isReadOnly) { d->mouseLocation = QPoint(); update(); ev->accept(); } } bool StarRating::eventFilter(QObject *obj, QEvent *event) { if (obj != d->labelPercent && obj != d->clearButton) return false; if ((event->type() == QEvent::MouseMove || event->type() == QEvent::Enter) && d->mouseLocation != QPoint()) { d->mouseLocation = QPoint(); update(); } return false; } double StarRating::value() const { return d->percent; } void StarRating::setValue(double percent) { if (d->isReadOnly) return; ///< disallow modifications if read-only if (percent >= 0.0 && percent <= 100.0) { d->percent = percent; update(); } } void StarRating::unsetValue() { if (d->isReadOnly) return; ///< disallow modifications if read-only d->mouseLocation = QPoint(); d->percent = -1.0; update(); } void StarRating::setReadOnly(bool isReadOnly) { d->isReadOnly = isReadOnly; d->clearButton->setEnabled(!isReadOnly); setMouseTracking(!isReadOnly); } void StarRating::clear() { if (d->isReadOnly) return; ///< disallow modifications if read-only unsetValue(); emit modified(); } void StarRating::buttonHeight() { const QSizePolicy sp = d->clearButton->sizePolicy(); /// Allow clear button to take as much vertical space as available d->clearButton->setSizePolicy(sp.horizontalPolicy(), QSizePolicy::MinimumExpanding); } -void StarRating::paintStars(QPainter *painter, KIconLoader::States defaultState, int numTotalStars, double percent, const QRect inside) -{ - painter->save(); ///< Save the current painter's state; at this function's end restored - - /// Calculate a single star's width/height - /// so that all stars fit into the "inside" rectangle - const int starSize = qMin(inside.height() - 2 * Private::paintMargin, (inside.width() - 2 * Private::paintMargin) / numTotalStars); - - /// First, draw active/golden/glowing stars (on the left side) - - /// Create a pixmap of a single active/golden/glowing star - QPixmap starPixmap = KIconLoader::global()->loadIcon(QStringLiteral("rating"), KIconLoader::Small, starSize, defaultState); - /// Calculate vertical position (same for all stars) - const int y = inside.top() + (inside.height() - starSize) / 2; - - /// Number of full golden stars - int numActiveStars = static_cast(percent * numTotalStars / 100); - /// Number of golden pixels of the star that is - /// partially golden and partially grey - int coloredPartWidth = static_cast((percent * numTotalStars / 100 - numActiveStars) * starSize); - - /// Horizontal position of first star - int x = inside.left() + Private::paintMargin; - - int i = 0; ///< start with first star - /// Draw active (colored) stars - for (; i < numActiveStars; ++i, x += starSize) - painter->drawPixmap(x, y, starPixmap); - - if (coloredPartWidth > 0) { - /// One star is partially colored, so draw star's golden left half - painter->drawPixmap(x, y, starPixmap, 0, 0, coloredPartWidth, 0); - } - - /// Second, draw grey/disabled stars (on the right side) - /// To do so, replace the previously used golden star pixmal with a grey/disabled one - starPixmap = KIconLoader::global()->loadIcon(QStringLiteral("rating"), KIconLoader::Small, starSize, KIconLoader::DisabledState); - - if (coloredPartWidth > 0) { - /// One star is partially grey, so draw star's grey right half - painter->drawPixmap(x + coloredPartWidth, y, starPixmap, coloredPartWidth, 0, starSize - coloredPartWidth, 0); - x += starSize; - ++i; - } - - /// Draw the remaining inactive (grey) stars - for (; i < numTotalStars; ++i, x += starSize) - painter->drawPixmap(x, y, starPixmap); - - painter->restore(); ///< Restore the painter's state as saved at this function's beginning -} - bool StarRatingFieldInput::reset(const Value &value) { bool result = false; const QString text = PlainTextValue::text(value); if (text.isEmpty()) { unsetValue(); result = true; } else { const double number = text.toDouble(&result); if (result && number >= 0.0 && number <= 100.0) { setValue(number); result = true; } else { /// Some value provided that cannot be interpreted unsetValue(); } } return result; } bool StarRatingFieldInput::apply(Value &v) const { v.clear(); const double percent = value(); if (percent >= 0.0 && percent <= 100) v.append(QSharedPointer(new PlainText(QString::number(percent, 'f', 2)))); return true; } bool StarRatingFieldInput::validate(QWidget **, QString &) const { return true; } diff --git a/src/gui/widgets/starrating.h b/src/gui/widgets/starrating.h index b3b60b58..4d7b6306 100644 --- a/src/gui/widgets/starrating.h +++ b/src/gui/widgets/starrating.h @@ -1,151 +1,136 @@ /***************************************************************************** * Copyright (C) 2004-2019 by Thomas Fischer <fischer@unix-ag.uni-kl.de> * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see <https://www.gnu.org/licenses/>. * *****************************************************************************/ #ifndef KBIBTEX_GUI_STARRATING_H #define KBIBTEX_GUI_STARRATING_H #include <QWidget> -#include <KIconLoader> - #include <Value> #include "kbibtexgui_export.h" class QLabel; class QPaintEvent; class QMouseEvent; class QPushButton; -// TODO replace StarRating and StarRatingFieldInput with KRatingWidget and KRatingPainter - /** * A widget which shows a number of stars in a horizonal row. * A floating-point value between 0.0 and n (n=number of stars) can be * assigned to this widget; based on this value, the corresponding * number of stars on the left side will be colored golden, the stars * on the right side will be shown in grey. * * @author Thomas Fischer <fischer@unix-ag.uni-kl.de> */ class KBIBTEXGUI_EXPORT StarRating : public QWidget { Q_OBJECT public: /** * Create a star rating widget with a given number of stars. * * @param maxNumberOfStars number of stars (recommended value is 8) * @param parent parent widget */ explicit StarRating(int maxNumberOfStars, QWidget *parent = nullptr); /** * Get the current rating in percent (i.e >=0.0 and <=100.0). * If no rating has been set (e.g. by a previous call of @see unsetValue), * the return value will be negative. * @return either percent between 0.0 and 100.0, or a negative value */ double value() const; /** * Set the rating in percent (valid only >=0.0 and <=100.0). * @param percent value between 0.0 and 100.0 */ void setValue(double percent); /** * Remove any value assigned to this widget. * No stars will be highlighted and some "no value set" text * will be shown. * @see value will return a negative value. */ void unsetValue(); /** * Set this widget in read-only or read-writeable mode. * @param isReadOnly @c true if widget is to be read-only, @c false if modifyable */ void setReadOnly(bool isReadOnly); - /** - * Paint a horizonal sequence of stars on a painter. - * - * @param painter painter to draw on - * @param defaultState how icons shall be drawn; common values are KIconLoader::DefaultState and KIconLoader::DisabledState - * @param numTotalStars maximum/total number of stars - * @param percent percent value of "glowing" starts, to be >=0.0 and <= 100.0 - * @param inside fit and paint stars inside this rectangle on the painter - */ - static void paintStars(QPainter *painter, KIconLoader::States defaultState, int numTotalStars, double percent, const QRect inside); - signals: void modified(); protected: void paintEvent(QPaintEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; void mouseMoveEvent(QMouseEvent *) override; void leaveEvent(QEvent *) override; bool eventFilter(QObject *obj, QEvent *event) override; private slots: void clear(); void buttonHeight(); private: class Private; Private *const d; }; /** * A specialization of @see StarRating mimicing a FieldInput widget. * As part of this specialization, @see apply and @see reset functions * to write to or read from a Value object. * * @author Thomas Fischer <fischer@unix-ag.uni-kl.de> */ class KBIBTEXGUI_EXPORT StarRatingFieldInput : public StarRating { Q_OBJECT public: explicit StarRatingFieldInput(int maxNumberOfStars, QWidget *parent = nullptr) : StarRating(maxNumberOfStars, parent) { /* nothing */ } /** * Set this widget's state based on the provided Value object * @param value Value object to evaluate * @return @c true if the Value could be interpreted into a star rating, @c false otherwise */ bool reset(const Value &value); /** * Write this widget's value into the provided Value object. * @param value Value object to modify * @return @c true if the apply operation succeeded, @c false otherwise (should not happen) */ bool apply(Value &value) const; bool validate(QWidget **widgetWithIssue, QString &message) const; }; #endif // KBIBTEX_GUI_STARRATING_H