1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qdrawutil.h"
5#include "qbitmap.h"
6#include "qpixmapcache.h"
7#include "qpainter.h"
8#include "qpalette.h"
9#include <private/qpaintengineex_p.h>
10#include <qvarlengtharray.h>
11#include <qmath.h>
12#include <private/qhexstring_p.h>
13
14QT_BEGIN_NAMESPACE
15
16namespace {
17class PainterStateGuard {
18 Q_DISABLE_COPY_MOVE(PainterStateGuard)
19public:
20 explicit PainterStateGuard(QPainter *p) : m_painter(p) {}
21 ~PainterStateGuard()
22 {
23 for ( ; m_level > 0; --m_level)
24 m_painter->restore();
25 }
26
27 void save()
28 {
29 m_painter->save();
30 ++m_level;
31 }
32
33 void restore()
34 {
35 m_painter->restore();
36 --m_level;
37 }
38
39private:
40 QPainter *m_painter;
41 int m_level= 0;
42};
43} // namespace
44
45/*!
46 \headerfile <qdrawutil.h>
47 \inmodule QtWidgets
48 \title Drawing Utility Functions
49
50 \sa QPainter
51*/
52
53/*!
54 \fn void qDrawShadeLine(QPainter *painter, int x1, int y1, int x2, int y2,
55 const QPalette &palette, bool sunken,
56 int lineWidth, int midLineWidth)
57 \relates <qdrawutil.h>
58
59 Draws a horizontal (\a y1 == \a y2) or vertical (\a x1 == \a x2)
60 shaded line using the given \a painter. Note that nothing is
61 drawn if \a y1 != \a y2 and \a x1 != \a x2 (i.e. the line is
62 neither horizontal nor vertical).
63
64 The provided \a palette specifies the shading colors (\l
65 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
66 {QPalette::mid()}{middle} colors). The given \a lineWidth
67 specifies the line width for each of the lines; it is not the
68 total line width. The given \a midLineWidth specifies the width of
69 a middle line drawn in the QPalette::mid() color.
70
71 The line appears sunken if \a sunken is true, otherwise raised.
72
73 \warning This function does not look at QWidget::style() or
74 QApplication::style(). Use the drawing functions in QStyle to
75 make widgets that follow the current GUI style.
76
77
78 Alternatively you can use a QFrame widget and apply the
79 QFrame::setFrameStyle() function to display a shaded line:
80
81 \snippet code/src_gui_painting_qdrawutil.cpp 0
82
83 \sa qDrawShadeRect(), qDrawShadePanel(), QStyle
84*/
85
86void qDrawShadeLine(QPainter *p, int x1, int y1, int x2, int y2,
87 const QPalette &pal, bool sunken,
88 int lineWidth, int midLineWidth)
89{
90 if (Q_UNLIKELY(!p || lineWidth < 0 || midLineWidth < 0)) {
91 qWarning(msg: "qDrawShadeLine: Invalid parameters");
92 return;
93 }
94 PainterStateGuard painterGuard(p);
95 const qreal devicePixelRatio = p->device()->devicePixelRatio();
96 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
97 painterGuard.save();
98 const qreal inverseScale = qreal(1) / devicePixelRatio;
99 p->scale(sx: inverseScale, sy: inverseScale);
100 x1 = qRound(d: devicePixelRatio * x1);
101 y1 = qRound(d: devicePixelRatio * y1);
102 x2 = qRound(d: devicePixelRatio * x2);
103 y2 = qRound(d: devicePixelRatio * y2);
104 lineWidth = qRound(d: devicePixelRatio * lineWidth);
105 midLineWidth = qRound(d: devicePixelRatio * midLineWidth);
106 p->translate(dx: 0.5, dy: 0.5);
107 }
108 int tlw = lineWidth*2 + midLineWidth; // total line width
109 QPen oldPen = p->pen(); // save pen
110 if (sunken)
111 p->setPen(pal.color(cr: QPalette::Dark));
112 else
113 p->setPen(pal.light().color());
114 QPolygon a;
115 int i;
116 if (y1 == y2) { // horizontal line
117 int y = y1 - tlw/2;
118 if (x1 > x2) { // swap x1 and x2
119 int t = x1;
120 x1 = x2;
121 x2 = t;
122 }
123 x2--;
124 for (i=0; i<lineWidth; i++) { // draw top shadow
125 a.setPoints(nPoints: 3, firstx: x1+i, firsty: y+tlw-1-i,
126 x1+i, y+i,
127 x2-i, y+i);
128 p->drawPolyline(polyline: a);
129 }
130 if (midLineWidth > 0) {
131 p->setPen(pal.mid().color());
132 for (i=0; i<midLineWidth; i++) // draw lines in the middle
133 p->drawLine(x1: x1+lineWidth, y1: y+lineWidth+i,
134 x2: x2-lineWidth, y2: y+lineWidth+i);
135 }
136 if (sunken)
137 p->setPen(pal.light().color());
138 else
139 p->setPen(pal.dark().color());
140 for (i=0; i<lineWidth; i++) { // draw bottom shadow
141 a.setPoints(nPoints: 3, firstx: x1+i, firsty: y+tlw-i-1,
142 x2-i, y+tlw-i-1,
143 x2-i, y+i+1);
144 p->drawPolyline(polyline: a);
145 }
146 }
147 else if (x1 == x2) { // vertical line
148 int x = x1 - tlw/2;
149 if (y1 > y2) { // swap y1 and y2
150 int t = y1;
151 y1 = y2;
152 y2 = t;
153 }
154 y2--;
155 for (i=0; i<lineWidth; i++) { // draw left shadow
156 a.setPoints(nPoints: 3, firstx: x+i, firsty: y2,
157 x+i, y1+i,
158 x+tlw-1, y1+i);
159 p->drawPolyline(polyline: a);
160 }
161 if (midLineWidth > 0) {
162 p->setPen(pal.mid().color());
163 for (i=0; i<midLineWidth; i++) // draw lines in the middle
164 p->drawLine(x1: x+lineWidth+i, y1: y1+lineWidth, x2: x+lineWidth+i, y2);
165 }
166 if (sunken)
167 p->setPen(pal.light().color());
168 else
169 p->setPen(pal.dark().color());
170 for (i=0; i<lineWidth; i++) { // draw right shadow
171 a.setPoints(nPoints: 3, firstx: x+lineWidth, firsty: y2-i,
172 x+tlw-i-1, y2-i,
173 x+tlw-i-1, y1+lineWidth);
174 p->drawPolyline(polyline: a);
175 }
176 }
177 p->setPen(oldPen);
178}
179
180/*!
181 \fn void qDrawShadeRect(QPainter *painter, int x, int y, int width, int height,
182 const QPalette &palette, bool sunken,
183 int lineWidth, int midLineWidth,
184 const QBrush *fill)
185 \relates <qdrawutil.h>
186
187 Draws the shaded rectangle beginning at (\a x, \a y) with the
188 given \a width and \a height using the provided \a painter.
189
190 The provide \a palette specifies the shading colors (\l
191 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
192 {QPalette::mid()}{middle} colors. The given \a lineWidth
193 specifies the line width for each of the lines; it is not the
194 total line width. The \a midLineWidth specifies the width of a
195 middle line drawn in the QPalette::mid() color. The rectangle's
196 interior is filled with the \a fill brush unless \a fill is \nullptr.
197
198 The rectangle appears sunken if \a sunken is true, otherwise
199 raised.
200
201 \warning This function does not look at QWidget::style() or
202 QApplication::style(). Use the drawing functions in QStyle to make
203 widgets that follow the current GUI style.
204
205 Alternatively you can use a QFrame widget and apply the
206 QFrame::setFrameStyle() function to display a shaded rectangle:
207
208 \snippet code/src_gui_painting_qdrawutil.cpp 1
209
210 \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle
211*/
212
213void qDrawShadeRect(QPainter *p, int x, int y, int w, int h,
214 const QPalette &pal, bool sunken,
215 int lineWidth, int midLineWidth,
216 const QBrush *fill)
217{
218 if (w == 0 || h == 0)
219 return;
220 if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0 || midLineWidth < 0)) {
221 qWarning(msg: "qDrawShadeRect: Invalid parameters");
222 return;
223 }
224
225 PainterStateGuard painterGuard(p);
226 const qreal devicePixelRatio = p->device()->devicePixelRatio();
227 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
228 painterGuard.save();
229 const qreal inverseScale = qreal(1) / devicePixelRatio;
230 p->scale(sx: inverseScale, sy: inverseScale);
231 x = qRound(d: devicePixelRatio * x);
232 y = qRound(d: devicePixelRatio * y);
233 w = devicePixelRatio * w;
234 h = devicePixelRatio * h;
235 lineWidth = qRound(d: devicePixelRatio * lineWidth);
236 midLineWidth = qRound(d: devicePixelRatio * midLineWidth);
237 p->translate(dx: 0.5, dy: 0.5);
238 }
239
240 QPen oldPen = p->pen();
241 if (sunken)
242 p->setPen(pal.dark().color());
243 else
244 p->setPen(pal.light().color());
245 int x1=x, y1=y, x2=x+w-1, y2=y+h-1;
246
247 if (lineWidth == 1 && midLineWidth == 0) {// standard shade rectangle
248 p->drawRect(x: x1, y: y1, w: w-2, h: h-2);
249 if (sunken)
250 p->setPen(pal.light().color());
251 else
252 p->setPen(pal.dark().color());
253 QLineF lines[4] = { QLineF(x1+1, y1+1, x2-2, y1+1),
254 QLineF(x1+1, y1+2, x1+1, y2-2),
255 QLineF(x1, y2, x2, y2),
256 QLineF(x2,y1, x2,y2-1) };
257 p->drawLines(lines, lineCount: 4); // draw bottom/right lines
258 } else { // more complicated
259 int m = lineWidth+midLineWidth;
260 int i, j=0, k=m;
261 for (i=0; i<lineWidth; i++) { // draw top shadow
262 QLineF lines[4] = { QLineF(x1+i, y2-i, x1+i, y1+i),
263 QLineF(x1+i, y1+i, x2-i, y1+i),
264 QLineF(x1+k, y2-k, x2-k, y2-k),
265 QLineF(x2-k, y2-k, x2-k, y1+k) };
266 p->drawLines(lines, lineCount: 4);
267 k++;
268 }
269 p->setPen(pal.mid().color());
270 j = lineWidth*2;
271 for (i=0; i<midLineWidth; i++) { // draw lines in the middle
272 p->drawRect(x: x1+lineWidth+i, y: y1+lineWidth+i, w: w-j-1, h: h-j-1);
273 j += 2;
274 }
275 if (sunken)
276 p->setPen(pal.light().color());
277 else
278 p->setPen(pal.dark().color());
279 k = m;
280 for (i=0; i<lineWidth; i++) { // draw bottom shadow
281 QLineF lines[4] = { QLineF(x1+1+i, y2-i, x2-i, y2-i),
282 QLineF(x2-i, y2-i, x2-i, y1+i+1),
283 QLineF(x1+k, y2-k, x1+k, y1+k),
284 QLineF(x1+k, y1+k, x2-k, y1+k) };
285 p->drawLines(lines, lineCount: 4);
286 k++;
287 }
288 }
289 if (fill) {
290 QBrush oldBrush = p->brush();
291 int tlw = lineWidth + midLineWidth;
292 p->setPen(Qt::NoPen);
293 p->setBrush(*fill);
294 p->drawRect(x: x+tlw, y: y+tlw, w: w-2*tlw, h: h-2*tlw);
295 p->setBrush(oldBrush);
296 }
297 p->setPen(oldPen); // restore pen
298}
299
300
301/*!
302 \fn void qDrawShadePanel(QPainter *painter, int x, int y, int width, int height,
303 const QPalette &palette, bool sunken,
304 int lineWidth, const QBrush *fill)
305 \relates <qdrawutil.h>
306
307 Draws the shaded panel beginning at (\a x, \a y) with the given \a
308 width and \a height using the provided \a painter and the given \a
309 lineWidth.
310
311 The given \a palette specifies the shading colors (\l
312 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
313 {QPalette::mid()}{middle} colors). The panel's interior is filled
314 with the \a fill brush unless \a fill is \nullptr.
315
316 The panel appears sunken if \a sunken is true, otherwise raised.
317
318 \warning This function does not look at QWidget::style() or
319 QApplication::style(). Use the drawing functions in QStyle to make
320 widgets that follow the current GUI style.
321
322 Alternatively you can use a QFrame widget and apply the
323 QFrame::setFrameStyle() function to display a shaded panel:
324
325 \snippet code/src_gui_painting_qdrawutil.cpp 2
326
327 \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle
328*/
329
330void qDrawShadePanel(QPainter *p, int x, int y, int w, int h,
331 const QPalette &pal, bool sunken,
332 int lineWidth, const QBrush *fill)
333{
334 if (w == 0 || h == 0)
335 return;
336 if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0)) {
337 qWarning(msg: "qDrawShadePanel: Invalid parameters");
338 }
339
340 PainterStateGuard painterGuard(p);
341 const qreal devicePixelRatio = p->device()->devicePixelRatio();
342 bool isTranslated = false;
343 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
344 painterGuard.save();
345 const qreal inverseScale = qreal(1) / devicePixelRatio;
346 p->scale(sx: inverseScale, sy: inverseScale);
347 x = qRound(d: devicePixelRatio * x);
348 y = qRound(d: devicePixelRatio * y);
349 w = devicePixelRatio * w;
350 h = devicePixelRatio * h;
351 lineWidth = qRound(d: devicePixelRatio * lineWidth);
352 p->translate(dx: 0.5, dy: 0.5);
353 isTranslated = true;
354 }
355
356 QColor shade = pal.dark().color();
357 QColor light = pal.light().color();
358 if (fill) {
359 if (fill->color() == shade)
360 shade = pal.shadow().color();
361 if (fill->color() == light)
362 light = pal.midlight().color();
363 }
364 QPen oldPen = p->pen(); // save pen
365 QList<QLineF> lines;
366 lines.reserve(asize: 2*lineWidth);
367
368 if (sunken)
369 p->setPen(shade);
370 else
371 p->setPen(light);
372 int x1, y1, x2, y2;
373 int i;
374 x1 = x;
375 y1 = y2 = y;
376 x2 = x+w-2;
377 for (i=0; i<lineWidth; i++) { // top shadow
378 lines << QLineF(x1, y1++, x2--, y2++);
379 }
380 x2 = x1;
381 y1 = y+h-2;
382 for (i=0; i<lineWidth; i++) { // left shado
383 lines << QLineF(x1++, y1, x2++, y2--);
384 }
385 p->drawLines(lines);
386 lines.clear();
387 if (sunken)
388 p->setPen(light);
389 else
390 p->setPen(shade);
391 x1 = x;
392 y1 = y2 = y+h-1;
393 x2 = x+w-1;
394 for (i=0; i<lineWidth; i++) { // bottom shadow
395 lines << QLineF(x1++, y1--, x2, y2--);
396 }
397 x1 = x2;
398 y1 = y;
399 y2 = y+h-lineWidth-1;
400 for (i=0; i<lineWidth; i++) { // right shadow
401 lines << QLineF(x1--, y1++, x2--, y2);
402 }
403 p->drawLines(lines);
404 if (fill) { // fill with fill color
405 if (isTranslated)
406 p->translate(dx: -0.5, dy: -0.5);
407 p->fillRect(x: x+lineWidth, y: y+lineWidth, w: w-lineWidth*2, h: h-lineWidth*2, b: *fill);
408 }
409 p->setPen(oldPen); // restore pen
410}
411
412
413/*!
414 \internal
415 This function draws a rectangle with two pixel line width.
416 It is called from qDrawWinButton() and qDrawWinPanel().
417
418 c1..c4 and fill are used:
419
420 1 1 1 1 1 2
421 1 3 3 3 4 2
422 1 3 F F 4 2
423 1 3 F F 4 2
424 1 4 4 4 4 2
425 2 2 2 2 2 2
426*/
427
428static void qDrawWinShades(QPainter *p,
429 int x, int y, int w, int h,
430 const QColor &c1, const QColor &c2,
431 const QColor &c3, const QColor &c4,
432 const QBrush *fill)
433{
434 if (w < 2 || h < 2) // can't do anything with that
435 return;
436
437 PainterStateGuard painterGuard(p);
438 const qreal devicePixelRatio = p->device()->devicePixelRatio();
439 bool isTranslated = false;
440 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
441 painterGuard.save();
442 const qreal inverseScale = qreal(1) / devicePixelRatio;
443 p->scale(sx: inverseScale, sy: inverseScale);
444 x = qRound(d: devicePixelRatio * x);
445 y = qRound(d: devicePixelRatio * y);
446 w = devicePixelRatio * w;
447 h = devicePixelRatio * h;
448 p->translate(dx: 0.5, dy: 0.5);
449 isTranslated = true;
450 }
451
452 QPen oldPen = p->pen();
453 QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
454 p->setPen(c1);
455 p->drawPolyline(points: a, pointCount: 3);
456 QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
457 p->setPen(c2);
458 p->drawPolyline(points: b, pointCount: 3);
459 if (w > 4 && h > 4) {
460 QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
461 p->setPen(c3);
462 p->drawPolyline(points: c, pointCount: 3);
463 QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) };
464 p->setPen(c4);
465 p->drawPolyline(points: d, pointCount: 3);
466 if (fill) {
467 if (isTranslated)
468 p->translate(dx: -0.5, dy: -0.5);
469 p->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
470 }
471 }
472 p->setPen(oldPen);
473}
474
475
476/*!
477 \fn void qDrawWinButton(QPainter *painter, int x, int y, int width, int height,
478 const QPalette &palette, bool sunken,
479 const QBrush *fill)
480 \relates <qdrawutil.h>
481
482 Draws the Windows-style button specified by the given point (\a x,
483 \a y}, \a width and \a height using the provided \a painter with a
484 line width of 2 pixels. The button's interior is filled with the
485 \a{fill} brush unless \a fill is \nullptr.
486
487 The given \a palette specifies the shading colors (\l
488 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
489 {QPalette::mid()}{middle} colors).
490
491 The button appears sunken if \a sunken is true, otherwise raised.
492
493 \warning This function does not look at QWidget::style() or
494 QApplication::style()-> Use the drawing functions in QStyle to make
495 widgets that follow the current GUI style.
496
497 \sa qDrawWinPanel(), QStyle
498*/
499
500void qDrawWinButton(QPainter *p, int x, int y, int w, int h,
501 const QPalette &pal, bool sunken,
502 const QBrush *fill)
503{
504 if (sunken)
505 qDrawWinShades(p, x, y, w, h,
506 c1: pal.shadow().color(), c2: pal.light().color(), c3: pal.dark().color(),
507 c4: pal.button().color(), fill);
508 else
509 qDrawWinShades(p, x, y, w, h,
510 c1: pal.light().color(), c2: pal.shadow().color(), c3: pal.button().color(),
511 c4: pal.dark().color(), fill);
512}
513
514/*!
515 \fn void qDrawWinPanel(QPainter *painter, int x, int y, int width, int height,
516 const QPalette &palette, bool sunken,
517 const QBrush *fill)
518 \relates <qdrawutil.h>
519
520 Draws the Windows-style panel specified by the given point(\a x,
521 \a y), \a width and \a height using the provided \a painter with a
522 line width of 2 pixels. The button's interior is filled with the
523 \a fill brush unless \a fill is \nullptr.
524
525 The given \a palette specifies the shading colors. The panel
526 appears sunken if \a sunken is true, otherwise raised.
527
528 \warning This function does not look at QWidget::style() or
529 QApplication::style(). Use the drawing functions in QStyle to make
530 widgets that follow the current GUI style.
531
532 Alternatively you can use a QFrame widget and apply the
533 QFrame::setFrameStyle() function to display a shaded panel:
534
535 \snippet code/src_gui_painting_qdrawutil.cpp 3
536
537 \sa qDrawShadePanel(), qDrawWinButton(), QStyle
538*/
539
540void qDrawWinPanel(QPainter *p, int x, int y, int w, int h,
541 const QPalette &pal, bool sunken,
542 const QBrush *fill)
543{
544 if (sunken)
545 qDrawWinShades(p, x, y, w, h,
546 c1: pal.dark().color(), c2: pal.light().color(), c3: pal.shadow().color(),
547 c4: pal.midlight().color(), fill);
548 else
549 qDrawWinShades(p, x, y, w, h,
550 c1: pal.light().color(), c2: pal.shadow().color(), c3: pal.midlight().color(),
551 c4: pal.dark().color(), fill);
552}
553
554/*!
555 \fn void qDrawPlainRect(QPainter *painter, int x, int y, int width, int height, const QColor &lineColor,
556 int lineWidth, const QBrush *fill)
557 \relates <qdrawutil.h>
558
559 Draws the plain rectangle beginning at (\a x, \a y) with the given
560 \a width and \a height, using the specified \a painter, \a lineColor
561 and \a lineWidth. The rectangle's interior is filled with the \a
562 fill brush unless \a fill is \nullptr.
563
564 \warning This function does not look at QWidget::style() or
565 QApplication::style(). Use the drawing functions in QStyle to make
566 widgets that follow the current GUI style.
567
568 Alternatively you can use a QFrame widget and apply the
569 QFrame::setFrameStyle() function to display a plain rectangle:
570
571 \snippet code/src_gui_painting_qdrawutil.cpp 4
572
573 \sa qDrawShadeRect(), QStyle
574*/
575
576void qDrawPlainRect(QPainter *p, int x, int y, int w, int h, const QColor &c,
577 int lineWidth, const QBrush *fill)
578{
579 if (w == 0 || h == 0)
580 return;
581 if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0)) {
582 qWarning(msg: "qDrawPlainRect: Invalid parameters");
583 }
584
585 PainterStateGuard painterGuard(p);
586 const qreal devicePixelRatio = p->device()->devicePixelRatio();
587 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
588 painterGuard.save();
589 const qreal inverseScale = qreal(1) / devicePixelRatio;
590 p->scale(sx: inverseScale, sy: inverseScale);
591 x = qRound(d: devicePixelRatio * x);
592 y = qRound(d: devicePixelRatio * y);
593 w = devicePixelRatio * w;
594 h = devicePixelRatio * h;
595 lineWidth = qRound(d: devicePixelRatio * lineWidth);
596 p->translate(dx: 0.5, dy: 0.5);
597 }
598
599 QPen oldPen = p->pen();
600 QBrush oldBrush = p->brush();
601 p->setPen(c);
602 p->setBrush(Qt::NoBrush);
603 for (int i=0; i<lineWidth; i++)
604 p->drawRect(x: x+i, y: y+i, w: w-i*2 - 1, h: h-i*2 - 1);
605 if (fill) { // fill with fill color
606 p->setPen(Qt::NoPen);
607 p->setBrush(*fill);
608 p->drawRect(x: x+lineWidth, y: y+lineWidth, w: w-lineWidth*2, h: h-lineWidth*2);
609 }
610 p->setPen(oldPen);
611 p->setBrush(oldBrush);
612}
613
614/*****************************************************************************
615 Overloaded functions.
616 *****************************************************************************/
617
618/*!
619 \fn void qDrawShadeLine(QPainter *painter, const QPoint &p1, const QPoint &p2,
620 const QPalette &palette, bool sunken, int lineWidth, int midLineWidth)
621 \relates <qdrawutil.h>
622 \overload
623
624 Draws a horizontal or vertical shaded line between \a p1 and \a p2
625 using the given \a painter. Note that nothing is drawn if the line
626 between the points would be neither horizontal nor vertical.
627
628 The provided \a palette specifies the shading colors (\l
629 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
630 {QPalette::mid()}{middle} colors). The given \a lineWidth
631 specifies the line width for each of the lines; it is not the
632 total line width. The given \a midLineWidth specifies the width of
633 a middle line drawn in the QPalette::mid() color.
634
635 The line appears sunken if \a sunken is true, otherwise raised.
636
637 \warning This function does not look at QWidget::style() or
638 QApplication::style(). Use the drawing functions in QStyle to
639 make widgets that follow the current GUI style.
640
641
642 Alternatively you can use a QFrame widget and apply the
643 QFrame::setFrameStyle() function to display a shaded line:
644
645 \snippet code/src_gui_painting_qdrawutil.cpp 5
646
647 \sa qDrawShadeRect(), qDrawShadePanel(), QStyle
648*/
649
650void qDrawShadeLine(QPainter *p, const QPoint &p1, const QPoint &p2,
651 const QPalette &pal, bool sunken,
652 int lineWidth, int midLineWidth)
653{
654 qDrawShadeLine(p, x1: p1.x(), y1: p1.y(), x2: p2.x(), y2: p2.y(), pal, sunken,
655 lineWidth, midLineWidth);
656}
657
658/*!
659 \fn void qDrawShadeRect(QPainter *painter, const QRect &rect, const QPalette &palette,
660 bool sunken, int lineWidth, int midLineWidth, const QBrush *fill)
661 \relates <qdrawutil.h>
662 \overload
663
664 Draws the shaded rectangle specified by \a rect using the given \a painter.
665
666 The provide \a palette specifies the shading colors (\l
667 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
668 {QPalette::mid()}{middle} colors. The given \a lineWidth
669 specifies the line width for each of the lines; it is not the
670 total line width. The \a midLineWidth specifies the width of a
671 middle line drawn in the QPalette::mid() color. The rectangle's
672 interior is filled with the \a fill brush unless \a fill is \nullptr.
673
674 The rectangle appears sunken if \a sunken is true, otherwise
675 raised.
676
677 \warning This function does not look at QWidget::style() or
678 QApplication::style(). Use the drawing functions in QStyle to make
679 widgets that follow the current GUI style.
680
681 Alternatively you can use a QFrame widget and apply the
682 QFrame::setFrameStyle() function to display a shaded rectangle:
683
684 \snippet code/src_gui_painting_qdrawutil.cpp 6
685
686 \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle
687*/
688
689void qDrawShadeRect(QPainter *p, const QRect &r,
690 const QPalette &pal, bool sunken,
691 int lineWidth, int midLineWidth,
692 const QBrush *fill)
693{
694 qDrawShadeRect(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), pal, sunken,
695 lineWidth, midLineWidth, fill);
696}
697
698/*!
699 \fn void qDrawShadePanel(QPainter *painter, const QRect &rect, const QPalette &palette,
700 bool sunken, int lineWidth, const QBrush *fill)
701 \relates <qdrawutil.h>
702 \overload
703
704 Draws the shaded panel at the rectangle specified by \a rect using the
705 given \a painter and the given \a lineWidth.
706
707 The given \a palette specifies the shading colors (\l
708 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
709 {QPalette::mid()}{middle} colors). The panel's interior is filled
710 with the \a fill brush unless \a fill is \nullptr.
711
712 The panel appears sunken if \a sunken is true, otherwise raised.
713
714 \warning This function does not look at QWidget::style() or
715 QApplication::style(). Use the drawing functions in QStyle to make
716 widgets that follow the current GUI style.
717
718 Alternatively you can use a QFrame widget and apply the
719 QFrame::setFrameStyle() function to display a shaded panel:
720
721 \snippet code/src_gui_painting_qdrawutil.cpp 7
722
723 \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle
724*/
725
726void qDrawShadePanel(QPainter *p, const QRect &r,
727 const QPalette &pal, bool sunken,
728 int lineWidth, const QBrush *fill)
729{
730 qDrawShadePanel(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), pal, sunken,
731 lineWidth, fill);
732}
733
734/*!
735 \fn void qDrawWinButton(QPainter *painter, const QRect &rect, const QPalette &palette,
736 bool sunken, const QBrush *fill)
737 \relates <qdrawutil.h>
738 \overload
739
740 Draws the Windows-style button at the rectangle specified by \a rect using
741 the given \a painter with a line width of 2 pixels. The button's interior
742 is filled with the \a{fill} brush unless \a fill is \nullptr.
743
744 The given \a palette specifies the shading colors (\l
745 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
746 {QPalette::mid()}{middle} colors).
747
748 The button appears sunken if \a sunken is true, otherwise raised.
749
750 \warning This function does not look at QWidget::style() or
751 QApplication::style()-> Use the drawing functions in QStyle to make
752 widgets that follow the current GUI style.
753
754 \sa qDrawWinPanel(), QStyle
755*/
756
757void qDrawWinButton(QPainter *p, const QRect &r,
758 const QPalette &pal, bool sunken, const QBrush *fill)
759{
760 qDrawWinButton(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), pal, sunken, fill);
761}
762
763/*!
764 \fn void qDrawWinPanel(QPainter *painter, const QRect &rect, const QPalette &palette,
765 bool sunken, const QBrush *fill)
766 \relates <qdrawutil.h>
767 \overload
768
769 Draws the Windows-style panel at the rectangle specified by \a rect using
770 the given \a painter with a line width of 2 pixels. The button's interior
771 is filled with the \a fill brush unless \a fill is \nullptr.
772
773 The given \a palette specifies the shading colors. The panel
774 appears sunken if \a sunken is true, otherwise raised.
775
776 \warning This function does not look at QWidget::style() or
777 QApplication::style(). Use the drawing functions in QStyle to make
778 widgets that follow the current GUI style.
779
780 Alternatively you can use a QFrame widget and apply the
781 QFrame::setFrameStyle() function to display a shaded panel:
782
783 \snippet code/src_gui_painting_qdrawutil.cpp 8
784
785 \sa qDrawShadePanel(), qDrawWinButton(), QStyle
786*/
787
788void qDrawWinPanel(QPainter *p, const QRect &r,
789 const QPalette &pal, bool sunken, const QBrush *fill)
790{
791 qDrawWinPanel(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), pal, sunken, fill);
792}
793
794/*!
795 \fn void qDrawPlainRect(QPainter *painter, const QRect &rect, const QColor &lineColor, int lineWidth, const QBrush *fill)
796 \relates <qdrawutil.h>
797 \overload
798
799 Draws the plain rectangle specified by \a rect using the given \a painter,
800 \a lineColor and \a lineWidth. The rectangle's interior is filled with the
801 \a fill brush unless \a fill is \nullptr.
802
803 \warning This function does not look at QWidget::style() or
804 QApplication::style(). Use the drawing functions in QStyle to make
805 widgets that follow the current GUI style.
806
807 Alternatively you can use a QFrame widget and apply the
808 QFrame::setFrameStyle() function to display a plain rectangle:
809
810 \snippet code/src_gui_painting_qdrawutil.cpp 9
811
812 \sa qDrawShadeRect(), QStyle
813*/
814
815void qDrawPlainRect(QPainter *p, const QRect &r, const QColor &c,
816 int lineWidth, const QBrush *fill)
817{
818 qDrawPlainRect(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), c,
819 lineWidth, fill);
820}
821
822
823/*!
824 \class QTileRules
825 \since 4.6
826
827 \inmodule QtWidgets
828
829 \brief The QTileRules class provides the rules used to draw a
830 pixmap or image split into nine segments.
831
832 Spliiting is similar to \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images}.
833
834 \sa Qt::TileRule, QMargins
835*/
836
837/*! \fn QTileRules::QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule)
838 Constructs a QTileRules with the given \a horizontalRule and
839 \a verticalRule.
840 */
841
842/*! \fn QTileRules::QTileRules(Qt::TileRule rule)
843 Constructs a QTileRules with the given \a rule used for both
844 the horizontal rule and the vertical rule.
845 */
846
847/*!
848 \fn void qDrawBorderPixmap(QPainter *painter, const QRect &target, const QMargins &margins, const QPixmap &pixmap)
849 \relates <qdrawutil.h>
850 \since 4.6
851
852 \brief The qDrawBorderPixmap function is for drawing a pixmap into
853 the margins of a rectangle.
854
855 Draws the given \a pixmap into the given \a target rectangle, using the
856 given \a painter. The pixmap will be split into nine segments and drawn
857 according to the \a margins structure.
858*/
859
860typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray;
861
862/*!
863 \relates <qdrawutil.h>
864 \since 4.6
865
866 Draws the indicated \a sourceRect rectangle from the given \a pixmap into
867 the given \a targetRect rectangle, using the given \a painter. The pixmap
868 will be split into nine segments according to the given \a targetMargins
869 and \a sourceMargins structures. Finally, the pixmap will be drawn
870 according to the given \a rules.
871
872 This function is used to draw a scaled pixmap, similar to
873 \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images}
874
875 \sa Qt::TileRule, QTileRules, QMargins
876*/
877
878void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins,
879 const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins,
880 const QTileRules &rules
881#ifndef Q_QDOC
882 , QDrawBorderPixmap::DrawingHints hints
883#endif
884 )
885{
886 QPainter::PixmapFragment d;
887 d.opacity = 1.0;
888 d.rotation = 0.0;
889
890 QPixmapFragmentsArray opaqueData;
891 QPixmapFragmentsArray translucentData;
892
893 // source center
894 const int sourceCenterTop = sourceRect.top() + sourceMargins.top();
895 const int sourceCenterLeft = sourceRect.left() + sourceMargins.left();
896 const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1;
897 const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1;
898 const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft;
899 const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop;
900 // target center
901 const int targetCenterTop = targetRect.top() + targetMargins.top();
902 const int targetCenterLeft = targetRect.left() + targetMargins.left();
903 const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1;
904 const int targetCenterRight = targetRect.right() - targetMargins.right() + 1;
905 const int targetCenterWidth = targetCenterRight - targetCenterLeft;
906 const int targetCenterHeight = targetCenterBottom - targetCenterTop;
907
908 QVarLengthArray<qreal, 16> xTarget; // x-coordinates of target rectangles
909 QVarLengthArray<qreal, 16> yTarget; // y-coordinates of target rectangles
910
911 int columns = 3;
912 int rows = 3;
913 if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0)
914 columns = qMax(a: 3, b: 2 + qCeil(v: targetCenterWidth / qreal(sourceCenterWidth)));
915 if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0)
916 rows = qMax(a: 3, b: 2 + qCeil(v: targetCenterHeight / qreal(sourceCenterHeight)));
917
918 xTarget.resize(sz: columns + 1);
919 yTarget.resize(sz: rows + 1);
920
921 bool oldAA = painter->testRenderHint(hint: QPainter::Antialiasing);
922 if (painter->paintEngine()->type() != QPaintEngine::OpenGL
923 && painter->paintEngine()->type() != QPaintEngine::OpenGL2
924 && oldAA && painter->combinedTransform().type() != QTransform::TxNone) {
925 painter->setRenderHint(hint: QPainter::Antialiasing, on: false);
926 }
927
928 xTarget[0] = targetRect.left();
929 xTarget[1] = targetCenterLeft;
930 xTarget[columns - 1] = targetCenterRight;
931 xTarget[columns] = targetRect.left() + targetRect.width();
932
933 yTarget[0] = targetRect.top();
934 yTarget[1] = targetCenterTop;
935 yTarget[rows - 1] = targetCenterBottom;
936 yTarget[rows] = targetRect.top() + targetRect.height();
937
938 qreal dx = targetCenterWidth;
939 qreal dy = targetCenterHeight;
940
941 switch (rules.horizontal) {
942 case Qt::StretchTile:
943 dx = targetCenterWidth;
944 break;
945 case Qt::RepeatTile:
946 dx = sourceCenterWidth;
947 break;
948 case Qt::RoundTile:
949 dx = targetCenterWidth / qreal(columns - 2);
950 break;
951 }
952
953 for (int i = 2; i < columns - 1; ++i)
954 xTarget[i] = xTarget[i - 1] + dx;
955
956 switch (rules.vertical) {
957 case Qt::StretchTile:
958 dy = targetCenterHeight;
959 break;
960 case Qt::RepeatTile:
961 dy = sourceCenterHeight;
962 break;
963 case Qt::RoundTile:
964 dy = targetCenterHeight / qreal(rows - 2);
965 break;
966 }
967
968 for (int i = 2; i < rows - 1; ++i)
969 yTarget[i] = yTarget[i - 1] + dy;
970
971 // corners
972 if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left
973 d.x = (0.5 * (xTarget[1] + xTarget[0]));
974 d.y = (0.5 * (yTarget[1] + yTarget[0]));
975 d.sourceLeft = sourceRect.left();
976 d.sourceTop = sourceRect.top();
977 d.width = sourceMargins.left();
978 d.height = sourceMargins.top();
979 d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
980 d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
981 if (hints & QDrawBorderPixmap::OpaqueTopLeft)
982 opaqueData.append(t: d);
983 else
984 translucentData.append(t: d);
985 }
986 if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right
987 d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
988 d.y = (0.5 * (yTarget[1] + yTarget[0]));
989 d.sourceLeft = sourceCenterRight;
990 d.sourceTop = sourceRect.top();
991 d.width = sourceMargins.right();
992 d.height = sourceMargins.top();
993 d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
994 d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
995 if (hints & QDrawBorderPixmap::OpaqueTopRight)
996 opaqueData.append(t: d);
997 else
998 translucentData.append(t: d);
999 }
1000 if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left
1001 d.x = (0.5 * (xTarget[1] + xTarget[0]));
1002 d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1]));
1003 d.sourceLeft = sourceRect.left();
1004 d.sourceTop = sourceCenterBottom;
1005 d.width = sourceMargins.left();
1006 d.height = sourceMargins.bottom();
1007 d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
1008 d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
1009 if (hints & QDrawBorderPixmap::OpaqueBottomLeft)
1010 opaqueData.append(t: d);
1011 else
1012 translucentData.append(t: d);
1013 }
1014 if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right
1015 d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
1016 d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
1017 d.sourceLeft = sourceCenterRight;
1018 d.sourceTop = sourceCenterBottom;
1019 d.width = sourceMargins.right();
1020 d.height = sourceMargins.bottom();
1021 d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
1022 d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
1023 if (hints & QDrawBorderPixmap::OpaqueBottomRight)
1024 opaqueData.append(t: d);
1025 else
1026 translucentData.append(t: d);
1027 }
1028
1029 // horizontal edges
1030 if (targetCenterWidth > 0 && sourceCenterWidth > 0) {
1031 if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top
1032 QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData;
1033 d.sourceLeft = sourceCenterLeft;
1034 d.sourceTop = sourceRect.top();
1035 d.width = sourceCenterWidth;
1036 d.height = sourceMargins.top();
1037 d.y = (0.5 * (yTarget[1] + yTarget[0]));
1038 d.scaleX = dx / d.width;
1039 d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
1040 for (int i = 1; i < columns - 1; ++i) {
1041 d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
1042 data.append(t: d);
1043 }
1044 if (rules.horizontal == Qt::RepeatTile)
1045 data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
1046 }
1047 if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom
1048 QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData;
1049 d.sourceLeft = sourceCenterLeft;
1050 d.sourceTop = sourceCenterBottom;
1051 d.width = sourceCenterWidth;
1052 d.height = sourceMargins.bottom();
1053 d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
1054 d.scaleX = dx / d.width;
1055 d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
1056 for (int i = 1; i < columns - 1; ++i) {
1057 d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
1058 data.append(t: d);
1059 }
1060 if (rules.horizontal == Qt::RepeatTile)
1061 data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
1062 }
1063 }
1064
1065 // vertical edges
1066 if (targetCenterHeight > 0 && sourceCenterHeight > 0) {
1067 if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left
1068 QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData;
1069 d.sourceLeft = sourceRect.left();
1070 d.sourceTop = sourceCenterTop;
1071 d.width = sourceMargins.left();
1072 d.height = sourceCenterHeight;
1073 d.x = (0.5 * (xTarget[1] + xTarget[0]));
1074 d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
1075 d.scaleY = dy / d.height;
1076 for (int i = 1; i < rows - 1; ++i) {
1077 d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
1078 data.append(t: d);
1079 }
1080 if (rules.vertical == Qt::RepeatTile)
1081 data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
1082 }
1083 if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right
1084 QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData;
1085 d.sourceLeft = sourceCenterRight;
1086 d.sourceTop = sourceCenterTop;
1087 d.width = sourceMargins.right();
1088 d.height = sourceCenterHeight;
1089 d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
1090 d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
1091 d.scaleY = dy / d.height;
1092 for (int i = 1; i < rows - 1; ++i) {
1093 d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
1094 data.append(t: d);
1095 }
1096 if (rules.vertical == Qt::RepeatTile)
1097 data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
1098 }
1099 }
1100
1101 // center
1102 if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) {
1103 QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData;
1104 d.sourceLeft = sourceCenterLeft;
1105 d.sourceTop = sourceCenterTop;
1106 d.width = sourceCenterWidth;
1107 d.height = sourceCenterHeight;
1108 d.scaleX = dx / d.width;
1109 d.scaleY = dy / d.height;
1110
1111 qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX;
1112 qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY;
1113
1114 for (int j = 1; j < rows - 1; ++j) {
1115 d.y = (0.5 * (yTarget[j + 1] + yTarget[j]));
1116 for (int i = 1; i < columns - 1; ++i) {
1117 d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
1118 data.append(t: d);
1119 }
1120 if (rules.horizontal == Qt::RepeatTile)
1121 data[data.size() - 1].width = repeatWidth;
1122 }
1123 if (rules.vertical == Qt::RepeatTile) {
1124 for (int i = 1; i < columns - 1; ++i)
1125 data[data.size() - i].height = repeatHeight;
1126 }
1127 }
1128
1129 if (opaqueData.size())
1130 painter->drawPixmapFragments(fragments: opaqueData.data(), fragmentCount: opaqueData.size(), pixmap, hints: QPainter::OpaqueHint);
1131 if (translucentData.size())
1132 painter->drawPixmapFragments(fragments: translucentData.data(), fragmentCount: translucentData.size(), pixmap);
1133
1134 if (oldAA)
1135 painter->setRenderHint(hint: QPainter::Antialiasing, on: true);
1136}
1137
1138QT_END_NAMESPACE
1139

source code of qtbase/src/widgets/styles/qdrawutil.cpp