File indexing completed on 2024-04-28 15:32:14

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kselector.h"
0009 
0010 #include <QPaintEvent>
0011 #include <QPainter>
0012 #include <QPixmap>
0013 #include <QStyle>
0014 #include <QStyleOption>
0015 
0016 //-----------------------------------------------------------------------------
0017 /*
0018  * 1D value selector with contents drawn by derived class.
0019  * See KColorDialog for example.
0020  */
0021 
0022 #define ARROWSIZE 5
0023 
0024 class KSelectorPrivate
0025 {
0026 public:
0027     bool m_indent = true;
0028     QStyle::PrimitiveElement arrowPE = QStyle::PE_IndicatorArrowLeft;
0029 };
0030 
0031 class KGradientSelectorPrivate
0032 {
0033 public:
0034     KGradientSelectorPrivate(KGradientSelector *qq)
0035         : q(qq)
0036     {
0037     }
0038 
0039     KGradientSelector *q;
0040     QLinearGradient gradient;
0041     QString text1;
0042     QString text2;
0043 };
0044 
0045 KSelector::KSelector(QWidget *parent)
0046     : QAbstractSlider(parent)
0047     , d(new KSelectorPrivate)
0048 {
0049     setOrientation(Qt::Horizontal);
0050 }
0051 
0052 KSelector::KSelector(Qt::Orientation o, QWidget *parent)
0053     : QAbstractSlider(parent)
0054     , d(new KSelectorPrivate)
0055 {
0056     setOrientation(o);
0057     if (o == Qt::Horizontal) {
0058         setArrowDirection(Qt::UpArrow);
0059     }
0060 }
0061 
0062 KSelector::~KSelector() = default;
0063 
0064 void KSelector::setIndent(bool i)
0065 {
0066     d->m_indent = i;
0067 }
0068 
0069 bool KSelector::indent() const
0070 {
0071     return d->m_indent;
0072 }
0073 
0074 QRect KSelector::contentsRect() const
0075 {
0076     int w = indent() ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0;
0077     // TODO: is the height:width ratio of an indicator arrow always 2:1? hm.
0078     int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
0079 
0080     if (orientation() == Qt::Vertical) {
0081         if (arrowDirection() == Qt::RightArrow) {
0082             return QRect(w + ARROWSIZE, //
0083                          iw,
0084                          width() - w * 2 - ARROWSIZE,
0085                          height() - iw * 2);
0086         } else {
0087             return QRect(w, //
0088                          iw,
0089                          width() - w * 2 - ARROWSIZE,
0090                          height() - iw * 2);
0091         }
0092     } else { // Qt::Horizontal
0093         if (arrowDirection() == Qt::UpArrow) {
0094             return QRect(iw, //
0095                          w,
0096                          width() - 2 * iw,
0097                          height() - w * 2 - ARROWSIZE);
0098         } else {
0099             return QRect(iw, //
0100                          w + ARROWSIZE,
0101                          width() - 2 * iw,
0102                          height() - w * 2 - ARROWSIZE);
0103         }
0104     }
0105 }
0106 
0107 void KSelector::paintEvent(QPaintEvent *)
0108 {
0109     QPainter painter;
0110     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
0111     int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
0112 
0113     painter.begin(this);
0114 
0115     if (indent()) {
0116         QStyleOptionFrame opt;
0117         opt.initFrom(this);
0118         opt.state = QStyle::State_Sunken;
0119         if (orientation() == Qt::Vertical) {
0120             opt.rect.adjust(0, iw - w, -5, w - iw);
0121         } else {
0122             opt.rect.adjust(iw - w, 0, w - iw, -5);
0123         }
0124         QBrush oldBrush = painter.brush();
0125         painter.setBrush(Qt::NoBrush);
0126         style()->drawPrimitive(QStyle::PE_Frame, &opt, &painter, this);
0127         painter.setBrush(oldBrush);
0128     }
0129 
0130     drawContents(&painter);
0131 
0132     QPoint pos = calcArrowPos(value());
0133     drawArrow(&painter, pos);
0134 
0135     painter.end();
0136 }
0137 
0138 void KSelector::mousePressEvent(QMouseEvent *e)
0139 {
0140     setSliderDown(true);
0141     moveArrow(e->pos());
0142 }
0143 
0144 void KSelector::mouseMoveEvent(QMouseEvent *e)
0145 {
0146     moveArrow(e->pos());
0147 }
0148 
0149 void KSelector::mouseReleaseEvent(QMouseEvent *e)
0150 {
0151     moveArrow(e->pos());
0152     setSliderDown(false);
0153 }
0154 
0155 void KSelector::wheelEvent(QWheelEvent *e)
0156 {
0157     int val = value() + e->angleDelta().y() / 120;
0158     setSliderDown(true);
0159     setValue(val);
0160     setSliderDown(false);
0161 }
0162 
0163 void KSelector::moveArrow(const QPoint &pos)
0164 {
0165     int val;
0166     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
0167     int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
0168 
0169     if (orientation() == Qt::Vertical) {
0170         val = (maximum() - minimum()) * (height() - pos.y() - iw) / (height() - iw * 2) + minimum();
0171     } else {
0172         val = (maximum() - minimum()) * (pos.x() - iw) / (width() - iw * 2) + minimum();
0173     }
0174 
0175     setValue(val);
0176     update();
0177 }
0178 
0179 QPoint KSelector::calcArrowPos(int val)
0180 {
0181     QPoint p;
0182     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
0183     int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
0184 
0185     if (orientation() == Qt::Vertical) {
0186         p.setY(height() - iw - 1 - (height() - 2 * iw - 1) * val / (maximum() - minimum()));
0187 
0188         if (d->arrowPE == QStyle::PE_IndicatorArrowRight) {
0189             p.setX(0);
0190         } else {
0191             p.setX(width() - 5);
0192         }
0193     } else {
0194         p.setX(iw + (width() - 2 * iw - 1) * val / (maximum() - minimum()));
0195 
0196         if (d->arrowPE == QStyle::PE_IndicatorArrowDown) {
0197             p.setY(0);
0198         } else {
0199             p.setY(height() - 5);
0200         }
0201     }
0202 
0203     return p;
0204 }
0205 
0206 void KSelector::setArrowDirection(Qt::ArrowType direction)
0207 {
0208     switch (direction) {
0209     case Qt::UpArrow:
0210         if (orientation() == Qt::Horizontal) {
0211             d->arrowPE = QStyle::PE_IndicatorArrowUp;
0212         } else {
0213             d->arrowPE = QStyle::PE_IndicatorArrowLeft;
0214         }
0215         break;
0216     case Qt::DownArrow:
0217         if (orientation() == Qt::Horizontal) {
0218             d->arrowPE = QStyle::PE_IndicatorArrowDown;
0219         } else {
0220             d->arrowPE = QStyle::PE_IndicatorArrowRight;
0221         }
0222         break;
0223     case Qt::LeftArrow:
0224         if (orientation() == Qt::Vertical) {
0225             d->arrowPE = QStyle::PE_IndicatorArrowLeft;
0226         } else {
0227             d->arrowPE = QStyle::PE_IndicatorArrowDown;
0228         }
0229         break;
0230     case Qt::RightArrow:
0231         if (orientation() == Qt::Vertical) {
0232             d->arrowPE = QStyle::PE_IndicatorArrowRight;
0233         } else {
0234             d->arrowPE = QStyle::PE_IndicatorArrowUp;
0235         }
0236         break;
0237 
0238     case Qt::NoArrow:
0239         break;
0240     }
0241 }
0242 
0243 Qt::ArrowType KSelector::arrowDirection() const
0244 {
0245     switch (d->arrowPE) {
0246     case QStyle::PE_IndicatorArrowUp:
0247         return Qt::UpArrow;
0248     case QStyle::PE_IndicatorArrowDown:
0249         return Qt::DownArrow;
0250     case QStyle::PE_IndicatorArrowRight:
0251         return Qt::RightArrow;
0252     case QStyle::PE_IndicatorArrowLeft:
0253     default:
0254         return Qt::LeftArrow;
0255     }
0256 }
0257 
0258 void KSelector::drawContents(QPainter *)
0259 {
0260 }
0261 
0262 void KSelector::drawArrow(QPainter *painter, const QPoint &pos)
0263 {
0264     painter->setPen(QPen());
0265     painter->setBrush(QBrush(palette().color(QPalette::ButtonText)));
0266 
0267     QStyleOption o;
0268 
0269     if (orientation() == Qt::Vertical) {
0270         o.rect = QRect(pos.x(), pos.y() - ARROWSIZE / 2, ARROWSIZE, ARROWSIZE);
0271     } else {
0272         o.rect = QRect(pos.x() - ARROWSIZE / 2, pos.y(), ARROWSIZE, ARROWSIZE);
0273     }
0274     style()->drawPrimitive(d->arrowPE, &o, painter, this);
0275 }
0276 
0277 //----------------------------------------------------------------------------
0278 
0279 KGradientSelector::KGradientSelector(QWidget *parent)
0280     : KSelector(parent)
0281     , d(new KGradientSelectorPrivate(this))
0282 {
0283 }
0284 
0285 KGradientSelector::KGradientSelector(Qt::Orientation o, QWidget *parent)
0286     : KSelector(o, parent)
0287     , d(new KGradientSelectorPrivate(this))
0288 {
0289 }
0290 
0291 KGradientSelector::~KGradientSelector() = default;
0292 
0293 void KGradientSelector::drawContents(QPainter *painter)
0294 {
0295     d->gradient.setStart(contentsRect().topLeft());
0296     if (orientation() == Qt::Vertical) {
0297         d->gradient.setFinalStop(contentsRect().bottomLeft());
0298     } else {
0299         d->gradient.setFinalStop(contentsRect().topRight());
0300     }
0301     QBrush gradientBrush(d->gradient);
0302 
0303     if (!gradientBrush.isOpaque()) {
0304         QPixmap chessboardPattern(16, 16);
0305         QPainter patternPainter(&chessboardPattern);
0306         patternPainter.fillRect(0, 0, 8, 8, Qt::black);
0307         patternPainter.fillRect(8, 8, 8, 8, Qt::black);
0308         patternPainter.fillRect(0, 8, 8, 8, Qt::white);
0309         patternPainter.fillRect(8, 0, 8, 8, Qt::white);
0310         patternPainter.end();
0311         painter->fillRect(contentsRect(), QBrush(chessboardPattern));
0312     }
0313     painter->fillRect(contentsRect(), gradientBrush);
0314 
0315     if (orientation() == Qt::Vertical) {
0316         int yPos = contentsRect().top() + painter->fontMetrics().ascent() + 2;
0317         int xPos = contentsRect().left() + (contentsRect().width() - painter->fontMetrics().horizontalAdvance(d->text2)) / 2;
0318         QPen pen(qGray(firstColor().rgb()) > 180 ? Qt::black : Qt::white);
0319         painter->setPen(pen);
0320         painter->drawText(xPos, yPos, d->text2);
0321 
0322         yPos = contentsRect().bottom() - painter->fontMetrics().descent() - 2;
0323         xPos = contentsRect().left() + (contentsRect().width() - painter->fontMetrics().horizontalAdvance(d->text1)) / 2;
0324         pen.setColor(qGray(secondColor().rgb()) > 180 ? Qt::black : Qt::white);
0325         painter->setPen(pen);
0326         painter->drawText(xPos, yPos, d->text1);
0327     } else {
0328         int yPos = contentsRect().bottom() - painter->fontMetrics().descent() - 2;
0329 
0330         QPen pen(qGray(firstColor().rgb()) > 180 ? Qt::black : Qt::white);
0331         painter->setPen(pen);
0332         painter->drawText(contentsRect().left() + 2, yPos, d->text1);
0333 
0334         pen.setColor(qGray(secondColor().rgb()) > 180 ? Qt::black : Qt::white);
0335         painter->setPen(pen);
0336         painter->drawText(contentsRect().right() - painter->fontMetrics().horizontalAdvance(d->text2) - 2, yPos, d->text2);
0337     }
0338 }
0339 
0340 QSize KGradientSelector::minimumSize() const
0341 {
0342     return sizeHint();
0343 }
0344 
0345 void KGradientSelector::setStops(const QGradientStops &stops)
0346 {
0347     d->gradient.setStops(stops);
0348     update();
0349 }
0350 
0351 QGradientStops KGradientSelector::stops() const
0352 {
0353     return d->gradient.stops();
0354 }
0355 
0356 void KGradientSelector::setColors(const QColor &col1, const QColor &col2)
0357 {
0358     d->gradient.setColorAt(0.0, col1);
0359     d->gradient.setColorAt(1.0, col2);
0360     update();
0361 }
0362 
0363 void KGradientSelector::setText(const QString &t1, const QString &t2)
0364 {
0365     d->text1 = t1;
0366     d->text2 = t2;
0367     update();
0368 }
0369 
0370 void KGradientSelector::setFirstColor(const QColor &col)
0371 {
0372     d->gradient.setColorAt(0.0, col);
0373     update();
0374 }
0375 
0376 void KGradientSelector::setSecondColor(const QColor &col)
0377 {
0378     d->gradient.setColorAt(1.0, col);
0379     update();
0380 }
0381 
0382 void KGradientSelector::setFirstText(const QString &t)
0383 {
0384     d->text1 = t;
0385     update();
0386 }
0387 
0388 void KGradientSelector::setSecondText(const QString &t)
0389 {
0390     d->text2 = t;
0391     update();
0392 }
0393 
0394 QColor KGradientSelector::firstColor() const
0395 {
0396     return d->gradient.stops().first().second;
0397 }
0398 
0399 QColor KGradientSelector::secondColor() const
0400 {
0401     return d->gradient.stops().last().second;
0402 }
0403 
0404 QString KGradientSelector::firstText() const
0405 {
0406     return d->text1;
0407 }
0408 
0409 QString KGradientSelector::secondText() const
0410 {
0411     return d->text2;
0412 }
0413 
0414 #include "moc_kselector.cpp"