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

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1998 Jörg Habenicht <j.habenicht@europemail.com>
0004     SPDX-FileCopyrightText: 2010 Christoph Feck <cfeck@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "kled.h"
0010 
0011 #include <QImage>
0012 #include <QPainter>
0013 #include <QStyle>
0014 #include <QStyleOption>
0015 
0016 class KLedPrivate
0017 {
0018 public:
0019     int darkFactor = 300;
0020     QColor color;
0021     KLed::State state = KLed::On;
0022     KLed::Look look = KLed::Raised;
0023     KLed::Shape shape = KLed::Circular;
0024 
0025     QPixmap cachedPixmap[2]; // for both states
0026 };
0027 
0028 KLed::KLed(QWidget *parent)
0029     : QWidget(parent)
0030     , d(new KLedPrivate)
0031 {
0032     setColor(Qt::green);
0033     updateAccessibleName();
0034 }
0035 
0036 KLed::KLed(const QColor &color, QWidget *parent)
0037     : QWidget(parent)
0038     , d(new KLedPrivate)
0039 {
0040     setColor(color);
0041     updateAccessibleName();
0042 }
0043 
0044 KLed::KLed(const QColor &color, State state, Look look, Shape shape, QWidget *parent)
0045     : QWidget(parent)
0046     , d(new KLedPrivate)
0047 {
0048     d->state = (state == Off ? Off : On);
0049     d->look = look;
0050     d->shape = shape;
0051 
0052     setColor(color);
0053     updateAccessibleName();
0054 }
0055 
0056 KLed::~KLed() = default;
0057 
0058 KLed::State KLed::state() const
0059 {
0060     return d->state;
0061 }
0062 
0063 KLed::Shape KLed::shape() const
0064 {
0065     return d->shape;
0066 }
0067 
0068 QColor KLed::color() const
0069 {
0070     return d->color;
0071 }
0072 
0073 KLed::Look KLed::look() const
0074 {
0075     return d->look;
0076 }
0077 
0078 void KLed::setState(State state)
0079 {
0080     if (d->state == state) {
0081         return;
0082     }
0083 
0084     d->state = (state == Off ? Off : On);
0085     updateCachedPixmap();
0086     updateAccessibleName();
0087 }
0088 
0089 void KLed::setShape(Shape shape)
0090 {
0091     if (d->shape == shape) {
0092         return;
0093     }
0094 
0095     d->shape = shape;
0096     updateCachedPixmap();
0097 }
0098 
0099 void KLed::setColor(const QColor &color)
0100 {
0101     if (d->color == color) {
0102         return;
0103     }
0104 
0105     d->color = color;
0106     updateCachedPixmap();
0107 }
0108 
0109 void KLed::setDarkFactor(int darkFactor)
0110 {
0111     if (d->darkFactor == darkFactor) {
0112         return;
0113     }
0114 
0115     d->darkFactor = darkFactor;
0116     updateCachedPixmap();
0117 }
0118 
0119 int KLed::darkFactor() const
0120 {
0121     return d->darkFactor;
0122 }
0123 
0124 void KLed::setLook(Look look)
0125 {
0126     if (d->look == look) {
0127         return;
0128     }
0129 
0130     d->look = look;
0131     updateCachedPixmap();
0132 }
0133 
0134 void KLed::toggle()
0135 {
0136     d->state = (d->state == On ? Off : On);
0137     updateCachedPixmap();
0138     updateAccessibleName();
0139 }
0140 
0141 void KLed::on()
0142 {
0143     setState(On);
0144 }
0145 
0146 void KLed::off()
0147 {
0148     setState(Off);
0149 }
0150 
0151 void KLed::resizeEvent(QResizeEvent *)
0152 {
0153     updateCachedPixmap();
0154 }
0155 
0156 QSize KLed::sizeHint() const
0157 {
0158     QStyleOption option;
0159     option.initFrom(this);
0160     int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, &option, this);
0161     return QSize(iconSize, iconSize);
0162 }
0163 
0164 QSize KLed::minimumSizeHint() const
0165 {
0166     return QSize(16, 16);
0167 }
0168 
0169 void KLed::updateAccessibleName()
0170 {
0171 #ifndef QT_NO_ACCESSIBILITY
0172     QString onName = tr("LED on", "Accessible name of a Led whose state is on");
0173     QString offName = tr("LED off", "Accessible name of a Led whose state is off");
0174     QString lastName = accessibleName();
0175 
0176     if (lastName.isEmpty() || lastName == onName || lastName == offName) {
0177         // Accessible name has not been manually set.
0178 
0179         setAccessibleName(d->state == On ? onName : offName);
0180     }
0181 #endif
0182 }
0183 
0184 void KLed::updateCachedPixmap()
0185 {
0186     d->cachedPixmap[Off] = QPixmap();
0187     d->cachedPixmap[On] = QPixmap();
0188     update();
0189 }
0190 
0191 void KLed::paintEvent(QPaintEvent *)
0192 {
0193     if (!d->cachedPixmap[d->state].isNull()) {
0194         QPainter painter(this);
0195         painter.drawPixmap(1, 1, d->cachedPixmap[d->state]);
0196         return;
0197     }
0198 
0199     QSize size(width() - 2, height() - 2);
0200     if (d->shape == Circular) {
0201         // Make sure the LED is round
0202         const int dim = qMin(width(), height()) - 2;
0203         size = QSize(dim, dim);
0204     }
0205     QPointF center(size.width() / 2.0, size.height() / 2.0);
0206     const int smallestSize = qMin(size.width(), size.height());
0207     QPainter painter;
0208 
0209     QImage image(size, QImage::Format_ARGB32_Premultiplied);
0210     image.fill(0);
0211 
0212     QRadialGradient fillGradient(center, smallestSize / 2.0, QPointF(center.x(), size.height() / 3.0));
0213     const QColor fillColor = d->state != Off ? d->color : d->color.darker(d->darkFactor);
0214     fillGradient.setColorAt(0.0, fillColor.lighter(250));
0215     fillGradient.setColorAt(0.5, fillColor.lighter(130));
0216     fillGradient.setColorAt(1.0, fillColor);
0217 
0218     QConicalGradient borderGradient(center, d->look == Sunken ? 90 : -90);
0219     QColor borderColor = palette().color(QPalette::Dark);
0220     if (d->state == On) {
0221         QColor glowOverlay = fillColor;
0222         glowOverlay.setAlpha(80);
0223 
0224         // This isn't the fastest way, but should be "fast enough".
0225         // It's also the only safe way to use QPainter::CompositionMode
0226         QImage img(1, 1, QImage::Format_ARGB32_Premultiplied);
0227         QPainter p(&img);
0228         QColor start = borderColor;
0229         start.setAlpha(255); // opaque
0230         p.fillRect(0, 0, 1, 1, start);
0231         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
0232         p.fillRect(0, 0, 1, 1, glowOverlay);
0233         p.end();
0234 
0235         borderColor = img.pixel(0, 0);
0236     }
0237     borderGradient.setColorAt(0.2, borderColor);
0238     borderGradient.setColorAt(0.5, palette().color(QPalette::Light));
0239     borderGradient.setColorAt(0.8, borderColor);
0240 
0241     painter.begin(&image);
0242     painter.setRenderHint(QPainter::Antialiasing);
0243     painter.setBrush(d->look == Flat ? QBrush(fillColor) : QBrush(fillGradient));
0244     const QBrush penBrush = (d->look == Flat) ? QBrush(borderColor) : QBrush(borderGradient);
0245     const qreal penWidth = smallestSize / 8.0;
0246     painter.setPen(QPen(penBrush, penWidth));
0247     QRectF r(penWidth / 2.0, penWidth / 2.0, size.width() - penWidth, size.height() - penWidth);
0248     if (d->shape == Rectangular) {
0249         painter.drawRect(r);
0250     } else {
0251         painter.drawEllipse(r);
0252     }
0253     painter.end();
0254 
0255     d->cachedPixmap[d->state] = QPixmap::fromImage(image);
0256     painter.begin(this);
0257     painter.drawPixmap(1, 1, d->cachedPixmap[d->state]);
0258     painter.end();
0259 }
0260 
0261 #include "moc_kled.cpp"