1 /*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1998 Jörg Habenicht <j.habenicht@europemail.com>
4 SPDX-FileCopyrightText: 2010 Christoph Feck <cfeck@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
9 #include "kled.h"
10
11 #include <QImage>
12 #include <QPainter>
13 #include <QStyle>
14 #include <QStyleOption>
15
16 class KLedPrivate
17 {
18 public:
19 int darkFactor = 300;
20 QColor color;
21 KLed::State state = KLed::On;
22 KLed::Look look = KLed::Raised;
23 KLed::Shape shape = KLed::Circular;
24
25 QPixmap cachedPixmap[2]; // for both states
26 };
27
KLed(QWidget * parent)28 KLed::KLed(QWidget *parent)
29 : QWidget(parent)
30 , d(new KLedPrivate)
31 {
32 setColor(Qt::green);
33 updateAccessibleName();
34 }
35
KLed(const QColor & color,QWidget * parent)36 KLed::KLed(const QColor &color, QWidget *parent)
37 : QWidget(parent)
38 , d(new KLedPrivate)
39 {
40 setColor(color);
41 updateAccessibleName();
42 }
43
KLed(const QColor & color,State state,Look look,Shape shape,QWidget * parent)44 KLed::KLed(const QColor &color, State state, Look look, Shape shape, QWidget *parent)
45 : QWidget(parent)
46 , d(new KLedPrivate)
47 {
48 d->state = (state == Off ? Off : On);
49 d->look = look;
50 d->shape = shape;
51
52 setColor(color);
53 updateAccessibleName();
54 }
55
56 KLed::~KLed() = default;
57
state() const58 KLed::State KLed::state() const
59 {
60 return d->state;
61 }
62
shape() const63 KLed::Shape KLed::shape() const
64 {
65 return d->shape;
66 }
67
color() const68 QColor KLed::color() const
69 {
70 return d->color;
71 }
72
look() const73 KLed::Look KLed::look() const
74 {
75 return d->look;
76 }
77
setState(State state)78 void KLed::setState(State state)
79 {
80 if (d->state == state) {
81 return;
82 }
83
84 d->state = (state == Off ? Off : On);
85 updateCachedPixmap();
86 updateAccessibleName();
87 }
88
setShape(Shape shape)89 void KLed::setShape(Shape shape)
90 {
91 if (d->shape == shape) {
92 return;
93 }
94
95 d->shape = shape;
96 updateCachedPixmap();
97 }
98
setColor(const QColor & color)99 void KLed::setColor(const QColor &color)
100 {
101 if (d->color == color) {
102 return;
103 }
104
105 d->color = color;
106 updateCachedPixmap();
107 }
108
setDarkFactor(int darkFactor)109 void KLed::setDarkFactor(int darkFactor)
110 {
111 if (d->darkFactor == darkFactor) {
112 return;
113 }
114
115 d->darkFactor = darkFactor;
116 updateCachedPixmap();
117 }
118
darkFactor() const119 int KLed::darkFactor() const
120 {
121 return d->darkFactor;
122 }
123
setLook(Look look)124 void KLed::setLook(Look look)
125 {
126 if (d->look == look) {
127 return;
128 }
129
130 d->look = look;
131 updateCachedPixmap();
132 }
133
toggle()134 void KLed::toggle()
135 {
136 d->state = (d->state == On ? Off : On);
137 updateCachedPixmap();
138 updateAccessibleName();
139 }
140
on()141 void KLed::on()
142 {
143 setState(On);
144 }
145
off()146 void KLed::off()
147 {
148 setState(Off);
149 }
150
resizeEvent(QResizeEvent *)151 void KLed::resizeEvent(QResizeEvent *)
152 {
153 updateCachedPixmap();
154 }
155
sizeHint() const156 QSize KLed::sizeHint() const
157 {
158 QStyleOption option;
159 option.initFrom(this);
160 int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, &option, this);
161 return QSize(iconSize, iconSize);
162 }
163
minimumSizeHint() const164 QSize KLed::minimumSizeHint() const
165 {
166 return QSize(16, 16);
167 }
168
updateAccessibleName()169 void KLed::updateAccessibleName()
170 {
171 #ifndef QT_NO_ACCESSIBILITY
172 QString onName = tr("LED on", "Accessible name of a Led whose state is on");
173 QString offName = tr("LED off", "Accessible name of a Led whose state is off");
174 QString lastName = accessibleName();
175
176 if (lastName.isEmpty() || lastName == onName || lastName == offName) {
177 // Accessible name has not been manually set.
178
179 setAccessibleName(d->state == On ? onName : offName);
180 }
181 #endif
182 }
183
updateCachedPixmap()184 void KLed::updateCachedPixmap()
185 {
186 d->cachedPixmap[Off] = QPixmap();
187 d->cachedPixmap[On] = QPixmap();
188 update();
189 }
190
paintEvent(QPaintEvent *)191 void KLed::paintEvent(QPaintEvent *)
192 {
193 if (!d->cachedPixmap[d->state].isNull()) {
194 QPainter painter(this);
195 painter.drawPixmap(1, 1, d->cachedPixmap[d->state]);
196 return;
197 }
198
199 QSize size(width() - 2, height() - 2);
200 if (d->shape == Circular) {
201 // Make sure the LED is round
202 const int dim = qMin(width(), height()) - 2;
203 size = QSize(dim, dim);
204 }
205 QPointF center(size.width() / 2.0, size.height() / 2.0);
206 const int smallestSize = qMin(size.width(), size.height());
207 QPainter painter;
208
209 QImage image(size, QImage::Format_ARGB32_Premultiplied);
210 image.fill(0);
211
212 QRadialGradient fillGradient(center, smallestSize / 2.0, QPointF(center.x(), size.height() / 3.0));
213 const QColor fillColor = d->state != Off ? d->color : d->color.darker(d->darkFactor);
214 fillGradient.setColorAt(0.0, fillColor.lighter(250));
215 fillGradient.setColorAt(0.5, fillColor.lighter(130));
216 fillGradient.setColorAt(1.0, fillColor);
217
218 QConicalGradient borderGradient(center, d->look == Sunken ? 90 : -90);
219 QColor borderColor = palette().color(QPalette::Dark);
220 if (d->state == On) {
221 QColor glowOverlay = fillColor;
222 glowOverlay.setAlpha(80);
223
224 // This isn't the fastest way, but should be "fast enough".
225 // It's also the only safe way to use QPainter::CompositionMode
226 QImage img(1, 1, QImage::Format_ARGB32_Premultiplied);
227 QPainter p(&img);
228 QColor start = borderColor;
229 start.setAlpha(255); // opaque
230 p.fillRect(0, 0, 1, 1, start);
231 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
232 p.fillRect(0, 0, 1, 1, glowOverlay);
233 p.end();
234
235 borderColor = img.pixel(0, 0);
236 }
237 borderGradient.setColorAt(0.2, borderColor);
238 borderGradient.setColorAt(0.5, palette().color(QPalette::Light));
239 borderGradient.setColorAt(0.8, borderColor);
240
241 painter.begin(&image);
242 painter.setRenderHint(QPainter::Antialiasing);
243 painter.setBrush(d->look == Flat ? QBrush(fillColor) : QBrush(fillGradient));
244 const QBrush penBrush = (d->look == Flat) ? QBrush(borderColor) : QBrush(borderGradient);
245 const qreal penWidth = smallestSize / 8.0;
246 painter.setPen(QPen(penBrush, penWidth));
247 QRectF r(penWidth / 2.0, penWidth / 2.0, size.width() - penWidth, size.height() - penWidth);
248 if (d->shape == Rectangular) {
249 painter.drawRect(r);
250 } else {
251 painter.drawEllipse(r);
252 }
253 painter.end();
254
255 d->cachedPixmap[d->state] = QPixmap::fromImage(image);
256 painter.begin(this);
257 painter.drawPixmap(1, 1, d->cachedPixmap[d->state]);
258 painter.end();
259 }
260