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 | |
28 | KLed::KLed(QWidget *parent) |
29 | : QWidget(parent) |
30 | , d(new KLedPrivate) |
31 | { |
32 | setColor(Qt::green); |
33 | updateAccessibleName(); |
34 | } |
35 | |
36 | KLed::KLed(const QColor &color, QWidget *parent) |
37 | : QWidget(parent) |
38 | , d(new KLedPrivate) |
39 | { |
40 | setColor(color); |
41 | updateAccessibleName(); |
42 | } |
43 | |
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 | |
58 | KLed::State KLed::state() const |
59 | { |
60 | return d->state; |
61 | } |
62 | |
63 | KLed::Shape KLed::shape() const |
64 | { |
65 | return d->shape; |
66 | } |
67 | |
68 | QColor KLed::color() const |
69 | { |
70 | return d->color; |
71 | } |
72 | |
73 | KLed::Look KLed::look() const |
74 | { |
75 | return d->look; |
76 | } |
77 | |
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 | |
89 | void KLed::setShape(Shape shape) |
90 | { |
91 | if (d->shape == shape) { |
92 | return; |
93 | } |
94 | |
95 | d->shape = shape; |
96 | updateCachedPixmap(); |
97 | } |
98 | |
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 | |
109 | void KLed::setDarkFactor(int darkFactor) |
110 | { |
111 | if (d->darkFactor == darkFactor) { |
112 | return; |
113 | } |
114 | |
115 | d->darkFactor = darkFactor; |
116 | updateCachedPixmap(); |
117 | } |
118 | |
119 | int KLed::darkFactor() const |
120 | { |
121 | return d->darkFactor; |
122 | } |
123 | |
124 | void KLed::setLook(Look look) |
125 | { |
126 | if (d->look == look) { |
127 | return; |
128 | } |
129 | |
130 | d->look = look; |
131 | updateCachedPixmap(); |
132 | } |
133 | |
134 | void KLed::toggle() |
135 | { |
136 | d->state = (d->state == On ? Off : On); |
137 | updateCachedPixmap(); |
138 | updateAccessibleName(); |
139 | } |
140 | |
141 | void KLed::on() |
142 | { |
143 | setState(On); |
144 | } |
145 | |
146 | void KLed::off() |
147 | { |
148 | setState(Off); |
149 | } |
150 | |
151 | void KLed::resizeEvent(QResizeEvent *) |
152 | { |
153 | updateCachedPixmap(); |
154 | } |
155 | |
156 | QSize KLed::sizeHint() const |
157 | { |
158 | QStyleOption option; |
159 | option.initFrom(w: this); |
160 | int iconSize = style()->pixelMetric(metric: QStyle::PM_SmallIconSize, option: &option, widget: this); |
161 | return QSize(iconSize, iconSize); |
162 | } |
163 | |
164 | QSize KLed::minimumSizeHint() const |
165 | { |
166 | return QSize(16, 16); |
167 | } |
168 | |
169 | void KLed::updateAccessibleName() |
170 | { |
171 | #ifndef QT_NO_ACCESSIBILITY |
172 | QString onName = tr(s: "LED on" , c: "Accessible name of a Led whose state is on" ); |
173 | QString offName = tr(s: "LED off" , c: "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 | |
184 | void KLed::updateCachedPixmap() |
185 | { |
186 | d->cachedPixmap[Off] = QPixmap(); |
187 | d->cachedPixmap[On] = QPixmap(); |
188 | update(); |
189 | } |
190 | |
191 | void KLed::paintEvent(QPaintEvent *) |
192 | { |
193 | if (!d->cachedPixmap[d->state].isNull()) { |
194 | QPainter painter(this); |
195 | painter.drawPixmap(x: 1, y: 1, pm: 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(a: width(), b: height()) - 2; |
203 | size = QSize(dim, dim); |
204 | } |
205 | QPointF center(size.width() / 2.0, size.height() / 2.0); |
206 | const int smallestSize = qMin(a: size.width(), b: size.height()); |
207 | QPainter painter; |
208 | |
209 | QImage image(size, QImage::Format_ARGB32_Premultiplied); |
210 | image.fill(pixel: 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(f: d->darkFactor); |
214 | fillGradient.setColorAt(pos: 0.0, color: fillColor.lighter(f: 250)); |
215 | fillGradient.setColorAt(pos: 0.5, color: fillColor.lighter(f: 130)); |
216 | fillGradient.setColorAt(pos: 1.0, color: fillColor); |
217 | |
218 | QConicalGradient borderGradient(center, d->look == Sunken ? 90 : -90); |
219 | QColor borderColor = palette().color(cr: 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(x: 0, y: 0, w: 1, h: 1, b: start); |
231 | p.setCompositionMode(QPainter::CompositionMode_SourceOver); |
232 | p.fillRect(x: 0, y: 0, w: 1, h: 1, b: glowOverlay); |
233 | p.end(); |
234 | |
235 | borderColor = img.pixel(x: 0, y: 0); |
236 | } |
237 | borderGradient.setColorAt(pos: 0.2, color: borderColor); |
238 | borderGradient.setColorAt(pos: 0.5, color: palette().color(cr: QPalette::Light)); |
239 | borderGradient.setColorAt(pos: 0.8, color: borderColor); |
240 | |
241 | painter.begin(&image); |
242 | painter.setRenderHint(hint: 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(rect: 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(x: 1, y: 1, pm: d->cachedPixmap[d->state]); |
258 | painter.end(); |
259 | } |
260 | |
261 | #include "moc_kled.cpp" |
262 | |