KWin
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
plastikbutton.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2003-2005 Sandro Giessl <sandro@giessl.com>
4
5 based on the window decoration "Web":
6 SPDX-FileCopyrightText: 2001 Rik Hemsley (rikkus) <rik@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10#include "plastikbutton.h"
11#include <KColorScheme>
12#include <KConfigGroup>
13#include <KSharedConfig>
14#include <QPainter>
15
16namespace KWin
17{
18
20 : QQuickImageProvider(Pixmap)
21{
22}
23
24QPixmap PlastikButtonProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
25{
26 int origSize = requestedSize.isValid() ? std::min(requestedSize.width(), requestedSize.height()) : 10;
27 if (size) {
28 *size = QSize(origSize, origSize);
29 }
30 QStringList idParts = id.split(QStringLiteral("/"));
31 if (idParts.isEmpty()) {
32 // incorrect id
33 return QQuickImageProvider::requestPixmap(id, size, requestedSize);
34 }
35 bool active = false;
36 bool toggled = false;
37 bool shadow = false;
38 if (idParts.length() > 1 && idParts.at(1) == QStringLiteral("true")) {
39 active = true;
40 }
41 if (idParts.length() > 2 && idParts.at(2) == QStringLiteral("true")) {
42 toggled = true;
43 }
44 if (idParts.length() > 3 && idParts.at(3) == QStringLiteral("true")) {
45 shadow = true;
46 }
47 ButtonIcon button;
48 switch (static_cast<DecorationButton>(idParts[0].toInt())) {
49 case DecorationButtonClose:
50 button = CloseIcon;
51 break;
52 case DecorationButtonMaximizeRestore:
53 if (toggled) {
54 button = MaxRestoreIcon;
55 } else {
56 button = MaxIcon;
57 }
58 break;
59 case DecorationButtonMinimize:
60 button = MinIcon;
61 break;
62 case DecorationButtonQuickHelp:
63 button = HelpIcon;
64 break;
65 case DecorationButtonOnAllDesktops:
66 if (toggled) {
67 button = NotOnAllDesktopsIcon;
68 } else {
69 button = OnAllDesktopsIcon;
70 }
71 break;
72 case DecorationButtonKeepAbove:
73 if (toggled) {
74 button = NoKeepAboveIcon;
75 } else {
76 button = KeepAboveIcon;
77 }
78 break;
79 case DecorationButtonKeepBelow:
80 if (toggled) {
81 button = NoKeepBelowIcon;
82 } else {
83 button = KeepBelowIcon;
84 }
85 break;
86 case DecorationButtonShade:
87 if (toggled) {
88 button = UnShadeIcon;
89 } else {
90 button = ShadeIcon;
91 }
92 break;
93 case DecorationButtonApplicationMenu:
94 button = AppMenuIcon;
95 break;
96 default:
97 // not recognized icon
98 return QQuickImageProvider::requestPixmap(id, size, requestedSize);
99 }
100 return icon(button, origSize, active, shadow);
101}
102
103QPixmap PlastikButtonProvider::icon(ButtonIcon icon, int size, bool active, bool shadow)
104{
105 if (size % 2 == 0) {
106 --size;
107 }
108
109 QPixmap image(size, size);
110 image.fill(Qt::transparent);
111 QPainter p(&image);
112 KConfigGroup wmConfig(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), QStringLiteral("WM"));
113 const QColor color = wmConfig.readEntry("activeForeground", QPalette().color(QPalette::Active, QPalette::HighlightedText));
114
115 if (shadow) {
116 p.setPen(KColorScheme::shade(color, KColorScheme::ShadowShade));
117 } else {
118 p.setPen(color);
119 }
120
121 const QRect r = image.rect();
122
123 // line widths
124 int lwTitleBar = 1;
125 if (r.width() > 16) {
126 lwTitleBar = 4;
127 } else if (r.width() > 4) {
128 lwTitleBar = 2;
129 }
130 int lwArrow = 1;
131 if (r.width() > 16) {
132 lwArrow = 4;
133 } else if (r.width() > 7) {
134 lwArrow = 2;
135 }
136
137 switch (icon) {
138 case CloseIcon: {
139 int lineWidth = 1;
140 if (r.width() > 16) {
141 lineWidth = 3;
142 } else if (r.width() > 4) {
143 lineWidth = 2;
144 }
145
146 drawObject(p, DiagonalLine, r.x(), r.y(), r.width(), lineWidth);
147 drawObject(p, CrossDiagonalLine, r.x(), r.bottom(), r.width(), lineWidth);
148
149 break;
150 }
151
152 case MaxIcon: {
153 int lineWidth2 = 1; // frame
154 if (r.width() > 16) {
155 lineWidth2 = 2;
156 } else if (r.width() > 4) {
157 lineWidth2 = 1;
158 }
159
160 drawObject(p, HorizontalLine, r.x(), r.top(), r.width(), lwTitleBar);
161 drawObject(p, HorizontalLine, r.x(), r.bottom() - (lineWidth2 - 1), r.width(), lineWidth2);
162 drawObject(p, VerticalLine, r.x(), r.top(), r.height(), lineWidth2);
163 drawObject(p, VerticalLine, r.right() - (lineWidth2 - 1), r.top(), r.height(), lineWidth2);
164
165 break;
166 }
167
168 case MaxRestoreIcon: {
169 int lineWidth2 = 1; // frame
170 if (r.width() > 16) {
171 lineWidth2 = 2;
172 } else if (r.width() > 4) {
173 lineWidth2 = 1;
174 }
175
176 int margin1, margin2;
177 margin1 = margin2 = lineWidth2 * 2;
178 if (r.width() < 8) {
179 margin1 = 1;
180 }
181
182 // background window
183 drawObject(p, HorizontalLine, r.x() + margin1, r.top(), r.width() - margin1, lineWidth2);
184 drawObject(p, HorizontalLine, r.right() - margin2, r.bottom() - (lineWidth2 - 1) - margin1, margin2, lineWidth2);
185 drawObject(p, VerticalLine, r.x() + margin1, r.top(), margin2, lineWidth2);
186 drawObject(p, VerticalLine, r.right() - (lineWidth2 - 1), r.top(), r.height() - margin1, lineWidth2);
187
188 // foreground window
189 drawObject(p, HorizontalLine, r.x(), r.top() + margin2, r.width() - margin2, lwTitleBar);
190 drawObject(p, HorizontalLine, r.x(), r.bottom() - (lineWidth2 - 1), r.width() - margin2, lineWidth2);
191 drawObject(p, VerticalLine, r.x(), r.top() + margin2, r.height(), lineWidth2);
192 drawObject(p, VerticalLine, r.right() - (lineWidth2 - 1) - margin2, r.top() + margin2, r.height(), lineWidth2);
193
194 break;
195 }
196
197 case MinIcon: {
198 drawObject(p, HorizontalLine, r.x(), r.bottom() - (lwTitleBar - 1), r.width(), lwTitleBar);
199
200 break;
201 }
202
203 case HelpIcon: {
204 int center = r.x() + r.width() / 2 - 1;
205 int side = r.width() / 4;
206
207 // paint a question mark... code is quite messy, to be cleaned up later...! :o
208
209 if (r.width() > 16) {
210 int lineWidth = 3;
211
212 // top bar
213 drawObject(p, HorizontalLine, center - side + 3, r.y(), 2 * side - 3 - 1, lineWidth);
214 // top bar rounding
215 drawObject(p, CrossDiagonalLine, center - side - 1, r.y() + 5, 6, lineWidth);
216 drawObject(p, DiagonalLine, center + side - 3, r.y(), 5, lineWidth);
217 // right bar
218 drawObject(p, VerticalLine, center + side + 2 - lineWidth, r.y() + 3, r.height() - (2 * lineWidth + side + 2 + 1), lineWidth);
219 // bottom bar
220 drawObject(p, CrossDiagonalLine, center, r.bottom() - 2 * lineWidth, side + 2, lineWidth);
221 drawObject(p, HorizontalLine, center, r.bottom() - 3 * lineWidth + 2, lineWidth, lineWidth);
222 // the dot
223 drawObject(p, HorizontalLine, center, r.bottom() - (lineWidth - 1), lineWidth, lineWidth);
224 } else if (r.width() > 8) {
225 int lineWidth = 2;
226
227 // top bar
228 drawObject(p, HorizontalLine, center - (side - 1), r.y(), 2 * side - 1, lineWidth);
229 // top bar rounding
230 if (r.width() > 9) {
231 drawObject(p, CrossDiagonalLine, center - side - 1, r.y() + 3, 3, lineWidth);
232 } else {
233 drawObject(p, CrossDiagonalLine, center - side - 1, r.y() + 2, 3, lineWidth);
234 }
235 drawObject(p, DiagonalLine, center + side - 1, r.y(), 3, lineWidth);
236 // right bar
237 drawObject(p, VerticalLine, center + side + 2 - lineWidth, r.y() + 2, r.height() - (2 * lineWidth + side + 1), lineWidth);
238 // bottom bar
239 drawObject(p, CrossDiagonalLine, center, r.bottom() - 2 * lineWidth + 1, side + 2, lineWidth);
240 // the dot
241 drawObject(p, HorizontalLine, center, r.bottom() - (lineWidth - 1), lineWidth, lineWidth);
242 } else {
243 int lineWidth = 1;
244
245 // top bar
246 drawObject(p, HorizontalLine, center - (side - 1), r.y(), 2 * side, lineWidth);
247 // top bar rounding
248 drawObject(p, CrossDiagonalLine, center - side - 1, r.y() + 1, 2, lineWidth);
249 // right bar
250 drawObject(p, VerticalLine, center + side + 1, r.y(), r.height() - (side + 2 + 1), lineWidth);
251 // bottom bar
252 drawObject(p, CrossDiagonalLine, center, r.bottom() - 2, side + 2, lineWidth);
253 // the dot
254 drawObject(p, HorizontalLine, center, r.bottom(), 1, 1);
255 }
256
257 break;
258 }
259
260 case NotOnAllDesktopsIcon: {
261 int lwMark = r.width() - lwTitleBar * 2 - 2;
262 if (lwMark < 1) {
263 lwMark = 3;
264 }
265
266 drawObject(p, HorizontalLine, r.x() + (r.width() - lwMark) / 2, r.y() + (r.height() - lwMark) / 2, lwMark, lwMark);
267
268 // Fall through to OnAllDesktopsIcon intended!
269 Q_FALLTHROUGH();
270 }
271 case OnAllDesktopsIcon: {
272 // horizontal bars
273 drawObject(p, HorizontalLine, r.x() + lwTitleBar, r.y(), r.width() - 2 * lwTitleBar, lwTitleBar);
274 drawObject(p, HorizontalLine, r.x() + lwTitleBar, r.bottom() - (lwTitleBar - 1), r.width() - 2 * lwTitleBar, lwTitleBar);
275 // vertical bars
276 drawObject(p, VerticalLine, r.x(), r.y() + lwTitleBar, r.height() - 2 * lwTitleBar, lwTitleBar);
277 drawObject(p, VerticalLine, r.right() - (lwTitleBar - 1), r.y() + lwTitleBar, r.height() - 2 * lwTitleBar, lwTitleBar);
278
279 break;
280 }
281
282 case NoKeepAboveIcon: {
283 int center = r.x() + r.width() / 2;
284
285 // arrow
286 drawObject(p, CrossDiagonalLine, r.x(), center + 2 * lwArrow, center - r.x(), lwArrow);
287 drawObject(p, DiagonalLine, r.x() + center, r.y() + 1 + 2 * lwArrow, center - r.x(), lwArrow);
288 if (lwArrow > 1) {
289 drawObject(p, HorizontalLine, center - (lwArrow - 2), r.y() + 2 * lwArrow, (lwArrow - 2) * 2, lwArrow);
290 }
291
292 // Fall through to KeepAboveIcon intended!
293 Q_FALLTHROUGH();
294 }
295 case KeepAboveIcon: {
296 int center = r.x() + r.width() / 2;
297
298 // arrow
299 drawObject(p, CrossDiagonalLine, r.x(), center, center - r.x(), lwArrow);
300 drawObject(p, DiagonalLine, r.x() + center, r.y() + 1, center - r.x(), lwArrow);
301 if (lwArrow > 1) {
302 drawObject(p, HorizontalLine, center - (lwArrow - 2), r.y(), (lwArrow - 2) * 2, lwArrow);
303 }
304
305 break;
306 }
307
308 case NoKeepBelowIcon: {
309 int center = r.x() + r.width() / 2;
310
311 // arrow
312 drawObject(p, DiagonalLine, r.x(), center - 2 * lwArrow, center - r.x(), lwArrow);
313 drawObject(p, CrossDiagonalLine, r.x() + center, r.bottom() - 1 - 2 * lwArrow, center - r.x(), lwArrow);
314 if (lwArrow > 1) {
315 drawObject(p, HorizontalLine, center - (lwArrow - 2), r.bottom() - (lwArrow - 1) - 2 * lwArrow, (lwArrow - 2) * 2, lwArrow);
316 }
317
318 // Fall through to KeepBelowIcon intended!
319 Q_FALLTHROUGH();
320 }
321 case KeepBelowIcon: {
322 int center = r.x() + r.width() / 2;
323
324 // arrow
325 drawObject(p, DiagonalLine, r.x(), center, center - r.x(), lwArrow);
326 drawObject(p, CrossDiagonalLine, r.x() + center, r.bottom() - 1, center - r.x(), lwArrow);
327 if (lwArrow > 1) {
328 drawObject(p, HorizontalLine, center - (lwArrow - 2), r.bottom() - (lwArrow - 1), (lwArrow - 2) * 2, lwArrow);
329 }
330
331 break;
332 }
333
334 case ShadeIcon: {
335 drawObject(p, HorizontalLine, r.x(), r.y(), r.width(), lwTitleBar);
336
337 break;
338 }
339
340 case UnShadeIcon: {
341 int lw1 = 1;
342 int lw2 = 1;
343 if (r.width() > 16) {
344 lw1 = 4;
345 lw2 = 2;
346 } else if (r.width() > 7) {
347 lw1 = 2;
348 lw2 = 1;
349 }
350
351 int h = std::max((r.width() / 2), (lw1 + 2 * lw2));
352
353 // horizontal bars
354 drawObject(p, HorizontalLine, r.x(), r.y(), r.width(), lw1);
355 drawObject(p, HorizontalLine, r.x(), r.x() + h - (lw2 - 1), r.width(), lw2);
356 // vertical bars
357 drawObject(p, VerticalLine, r.x(), r.y(), h, lw2);
358 drawObject(p, VerticalLine, r.right() - (lw2 - 1), r.y(), h, lw2);
359
360 break;
361 }
362 case AppMenuIcon: {
363 drawObject(p, HorizontalLine, r.x(), r.top() + (lwTitleBar - 1), r.width(), lwTitleBar);
364 drawObject(p, HorizontalLine, r.x(), r.center().y(), r.width(), lwTitleBar);
365 drawObject(p, HorizontalLine, r.x(), r.bottom() - (lwTitleBar - 1), r.width(), lwTitleBar);
366 break;
367 }
368
369 default:
370 break;
371 }
372
373 p.end();
374
375 return image;
376}
377
378void PlastikButtonProvider::drawObject(QPainter &p, Object object, int x, int y, int length, int lineWidth)
379{
380 switch (object) {
381 case DiagonalLine:
382 if (lineWidth <= 1) {
383 for (int i = 0; i < length; ++i) {
384 p.drawPoint(x + i, y + i);
385 }
386 } else if (lineWidth <= 2) {
387 for (int i = 0; i < length; ++i) {
388 p.drawPoint(x + i, y + i);
389 }
390 for (int i = 0; i < (length - 1); ++i) {
391 p.drawPoint(x + 1 + i, y + i);
392 p.drawPoint(x + i, y + 1 + i);
393 }
394 } else {
395 for (int i = 1; i < (length - 1); ++i) {
396 p.drawPoint(x + i, y + i);
397 }
398 for (int i = 0; i < (length - 1); ++i) {
399 p.drawPoint(x + 1 + i, y + i);
400 p.drawPoint(x + i, y + 1 + i);
401 }
402 for (int i = 0; i < (length - 2); ++i) {
403 p.drawPoint(x + 2 + i, y + i);
404 p.drawPoint(x + i, y + 2 + i);
405 }
406 }
407 break;
408 case CrossDiagonalLine:
409 if (lineWidth <= 1) {
410 for (int i = 0; i < length; ++i) {
411 p.drawPoint(x + i, y - i);
412 }
413 } else if (lineWidth <= 2) {
414 for (int i = 0; i < length; ++i) {
415 p.drawPoint(x + i, y - i);
416 }
417 for (int i = 0; i < (length - 1); ++i) {
418 p.drawPoint(x + 1 + i, y - i);
419 p.drawPoint(x + i, y - 1 - i);
420 }
421 } else {
422 for (int i = 1; i < (length - 1); ++i) {
423 p.drawPoint(x + i, y - i);
424 }
425 for (int i = 0; i < (length - 1); ++i) {
426 p.drawPoint(x + 1 + i, y - i);
427 p.drawPoint(x + i, y - 1 - i);
428 }
429 for (int i = 0; i < (length - 2); ++i) {
430 p.drawPoint(x + 2 + i, y - i);
431 p.drawPoint(x + i, y - 2 - i);
432 }
433 }
434 break;
435 case HorizontalLine:
436 for (int i = 0; i < lineWidth; ++i) {
437 p.drawLine(x, y + i, x + length - 1, y + i);
438 }
439 break;
440 case VerticalLine:
441 for (int i = 0; i < lineWidth; ++i) {
442 p.drawLine(x + i, y, x + i, y + length - 1);
443 }
444 break;
445 default:
446 break;
447 }
448}
449
450} // namespace
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override