1// Copyright (C) 2020 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 "qquickdrawutil.h"
5
6#include "qbitmap.h"
7#include "qpixmapcache.h"
8#include "qpainter.h"
9#include "qpalette.h"
10#include <private/qpaintengineex_p.h>
11#include <qvarlengtharray.h>
12#include <qmath.h>
13#include <private/qhexstring_p.h>
14
15QT_BEGIN_NAMESPACE
16
17namespace QQC2 {
18
19namespace {
20class PainterStateGuard {
21 Q_DISABLE_COPY_MOVE(PainterStateGuard)
22public:
23 explicit PainterStateGuard(QPainter *p) : m_painter(p) {}
24 ~PainterStateGuard()
25 {
26 for ( ; m_level > 0; --m_level)
27 m_painter->restore();
28 }
29
30 void save()
31 {
32 m_painter->save();
33 ++m_level;
34 }
35
36 void restore()
37 {
38 m_painter->restore();
39 --m_level;
40 }
41
42private:
43 QPainter *m_painter;
44 int m_level= 0;
45};
46} // namespace
47
48/*!
49 \headerfile <qdrawutil.h>
50 \title Drawing Utility Functions
51
52 \sa QPainter
53*/
54
55/*!
56 \fn void qDrawShadeLine(QPainter *painter, int x1, int y1, int x2, int y2,
57 const QPalette &palette, bool sunken,
58 int lineWidth, int midLineWidth)
59 \relates <qdrawutil.h>
60
61 Draws a horizontal (\a y1 == \a y2) or vertical (\a x1 == \a x2)
62 shaded line using the given \a painter. Note that nothing is
63 drawn if \a y1 != \a y2 and \a x1 != \a x2 (i.e. the line is
64 neither horizontal nor vertical).
65
66 The provided \a palette specifies the shading colors (\l
67 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
68 {QPalette::mid()}{middle} colors). The given \a lineWidth
69 specifies the line width for each of the lines; it is not the
70 total line width. The given \a midLineWidth specifies the width of
71 a middle line drawn in the QPalette::mid() color.
72
73 The line appears sunken if \a sunken is true, otherwise raised.
74
75 \warning This function does not look at QWidget::style() or
76 QApplication::style(). Use the drawing functions in QStyle to
77 make widgets that follow the current GUI style.
78
79
80 Alternatively you can use a QFrame widget and apply the
81 QFrame::setFrameStyle() function to display a shaded line:
82
83 \snippet code/src_gui_painting_qdrawutil.cpp 0
84
85 \sa qDrawShadeRect(), qDrawShadePanel(), QStyle
86*/
87
88void qDrawShadeLine(QPainter *p, int x1, int y1, int x2, int y2,
89 const QPalette &pal, bool sunken,
90 int lineWidth, int midLineWidth)
91{
92 if (Q_UNLIKELY(!p || lineWidth < 0 || midLineWidth < 0)) {
93 qWarning(msg: "qDrawShadeLine: Invalid parameters");
94 return;
95 }
96 PainterStateGuard painterGuard(p);
97 const qreal devicePixelRatio = p->device()->devicePixelRatio();
98 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
99 painterGuard.save();
100 const qreal inverseScale = qreal(1) / devicePixelRatio;
101 p->scale(sx: inverseScale, sy: inverseScale);
102 x1 = qRound(d: devicePixelRatio * x1);
103 y1 = qRound(d: devicePixelRatio * y1);
104 x2 = qRound(d: devicePixelRatio * x2);
105 y2 = qRound(d: devicePixelRatio * y2);
106 lineWidth = qRound(d: devicePixelRatio * lineWidth);
107 midLineWidth = qRound(d: devicePixelRatio * midLineWidth);
108 p->translate(dx: 0.5, dy: 0.5);
109 }
110 int tlw = lineWidth*2 + midLineWidth; // total line width
111 QPen oldPen = p->pen(); // save pen
112 if (sunken)
113 p->setPen(pal.color(cr: QPalette::Dark));
114 else
115 p->setPen(pal.light().color());
116 QPolygon a;
117 int i;
118 if (y1 == y2) { // horizontal line
119 int y = y1 - tlw/2;
120 if (x1 > x2) { // swap x1 and x2
121 int t = x1;
122 x1 = x2;
123 x2 = t;
124 }
125 x2--;
126 for (i=0; i<lineWidth; i++) { // draw top shadow
127 a.setPoints(nPoints: 3, firstx: x1+i, firsty: y+tlw-1-i,
128 x1+i, y+i,
129 x2-i, y+i);
130 p->drawPolyline(polyline: a);
131 }
132 if (midLineWidth > 0) {
133 p->setPen(pal.mid().color());
134 for (i=0; i<midLineWidth; i++) // draw lines in the middle
135 p->drawLine(x1: x1+lineWidth, y1: y+lineWidth+i,
136 x2: x2-lineWidth, y2: y+lineWidth+i);
137 }
138 if (sunken)
139 p->setPen(pal.light().color());
140 else
141 p->setPen(pal.dark().color());
142 for (i=0; i<lineWidth; i++) { // draw bottom shadow
143 a.setPoints(nPoints: 3, firstx: x1+i, firsty: y+tlw-i-1,
144 x2-i, y+tlw-i-1,
145 x2-i, y+i+1);
146 p->drawPolyline(polyline: a);
147 }
148 }
149 else if (x1 == x2) { // vertical line
150 int x = x1 - tlw/2;
151 if (y1 > y2) { // swap y1 and y2
152 int t = y1;
153 y1 = y2;
154 y2 = t;
155 }
156 y2--;
157 for (i=0; i<lineWidth; i++) { // draw left shadow
158 a.setPoints(nPoints: 3, firstx: x+i, firsty: y2,
159 x+i, y1+i,
160 x+tlw-1, y1+i);
161 p->drawPolyline(polyline: a);
162 }
163 if (midLineWidth > 0) {
164 p->setPen(pal.mid().color());
165 for (i=0; i<midLineWidth; i++) // draw lines in the middle
166 p->drawLine(x1: x+lineWidth+i, y1: y1+lineWidth, x2: x+lineWidth+i, y2);
167 }
168 if (sunken)
169 p->setPen(pal.light().color());
170 else
171 p->setPen(pal.dark().color());
172 for (i=0; i<lineWidth; i++) { // draw right shadow
173 a.setPoints(nPoints: 3, firstx: x+lineWidth, firsty: y2-i,
174 x+tlw-i-1, y2-i,
175 x+tlw-i-1, y1+lineWidth);
176 p->drawPolyline(polyline: a);
177 }
178 }
179 p->setPen(oldPen);
180}
181
182/*!
183 \fn void qDrawShadeRect(QPainter *painter, int x, int y, int width, int height,
184 const QPalette &palette, bool sunken,
185 int lineWidth, int midLineWidth,
186 const QBrush *fill)
187 \relates <qdrawutil.h>
188
189 Draws the shaded rectangle beginning at (\a x, \a y) with the
190 given \a width and \a height using the provided \a painter.
191
192 The provide \a palette specifies the shading colors (\l
193 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
194 {QPalette::mid()}{middle} colors. The given \a lineWidth
195 specifies the line width for each of the lines; it is not the
196 total line width. The \a midLineWidth specifies the width of a
197 middle line drawn in the QPalette::mid() color. The rectangle's
198 interior is filled with the \a fill brush unless \a fill is 0.
199
200 The rectangle appears sunken if \a sunken is true, otherwise
201 raised.
202
203 \warning This function does not look at QWidget::style() or
204 QApplication::style(). Use the drawing functions in QStyle to make
205 widgets that follow the current GUI style.
206
207 Alternatively you can use a QFrame widget and apply the
208 QFrame::setFrameStyle() function to display a shaded rectangle:
209
210 \snippet code/src_gui_painting_qdrawutil.cpp 1
211
212 \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle
213*/
214
215void qDrawShadeRect(QPainter *p, int x, int y, int w, int h,
216 const QPalette &pal, bool sunken,
217 int lineWidth, int midLineWidth,
218 const QBrush *fill)
219{
220 if (w == 0 || h == 0)
221 return;
222 if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0 || midLineWidth < 0)) {
223 qWarning(msg: "qDrawShadeRect: Invalid parameters");
224 return;
225 }
226
227 PainterStateGuard painterGuard(p);
228 const qreal devicePixelRatio = p->device()->devicePixelRatioF();
229 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
230 painterGuard.save();
231 const qreal inverseScale = qreal(1) / devicePixelRatio;
232 p->scale(sx: inverseScale, sy: inverseScale);
233 x = qRound(d: devicePixelRatio * x);
234 y = qRound(d: devicePixelRatio * y);
235 w = qRound(d: devicePixelRatio * w);
236 h = qRound(d: devicePixelRatio * h);
237 lineWidth = qRound(d: devicePixelRatio * lineWidth);
238 midLineWidth = qRound(d: devicePixelRatio * midLineWidth);
239 p->translate(dx: 0.5, dy: 0.5);
240 }
241
242 QPen oldPen = p->pen();
243 if (sunken)
244 p->setPen(pal.dark().color());
245 else
246 p->setPen(pal.light().color());
247 int x1=x, y1=y, x2=x+w-1, y2=y+h-1;
248
249 if (lineWidth == 1 && midLineWidth == 0) {// standard shade rectangle
250 p->drawRect(x: x1, y: y1, w: w-2, h: h-2);
251 if (sunken)
252 p->setPen(pal.light().color());
253 else
254 p->setPen(pal.dark().color());
255 QLineF lines[4] = { QLineF(x1+1, y1+1, x2-2, y1+1),
256 QLineF(x1+1, y1+2, x1+1, y2-2),
257 QLineF(x1, y2, x2, y2),
258 QLineF(x2,y1, x2,y2-1) };
259 p->drawLines(lines, lineCount: 4); // draw bottom/right lines
260 } else { // more complicated
261 int m = lineWidth+midLineWidth;
262 int i, j=0, k=m;
263 for (i=0; i<lineWidth; i++) { // draw top shadow
264 QLineF lines[4] = { QLineF(x1+i, y2-i, x1+i, y1+i),
265 QLineF(x1+i, y1+i, x2-i, y1+i),
266 QLineF(x1+k, y2-k, x2-k, y2-k),
267 QLineF(x2-k, y2-k, x2-k, y1+k) };
268 p->drawLines(lines, lineCount: 4);
269 k++;
270 }
271 p->setPen(pal.mid().color());
272 j = lineWidth*2;
273 for (i=0; i<midLineWidth; i++) { // draw lines in the middle
274 p->drawRect(x: x1+lineWidth+i, y: y1+lineWidth+i, w: w-j-1, h: h-j-1);
275 j += 2;
276 }
277 if (sunken)
278 p->setPen(pal.light().color());
279 else
280 p->setPen(pal.dark().color());
281 k = m;
282 for (i=0; i<lineWidth; i++) { // draw bottom shadow
283 QLineF lines[4] = { QLineF(x1+1+i, y2-i, x2-i, y2-i),
284 QLineF(x2-i, y2-i, x2-i, y1+i+1),
285 QLineF(x1+k, y2-k, x1+k, y1+k),
286 QLineF(x1+k, y1+k, x2-k, y1+k) };
287 p->drawLines(lines, lineCount: 4);
288 k++;
289 }
290 }
291 if (fill) {
292 QBrush oldBrush = p->brush();
293 int tlw = lineWidth + midLineWidth;
294 p->setPen(Qt::NoPen);
295 p->setBrush(*fill);
296 p->drawRect(x: x+tlw, y: y+tlw, w: w-2*tlw, h: h-2*tlw);
297 p->setBrush(oldBrush);
298 }
299 p->setPen(oldPen); // restore pen
300}
301
302
303/*!
304 \fn void qDrawShadePanel(QPainter *painter, int x, int y, int width, int height,
305 const QPalette &palette, bool sunken,
306 int lineWidth, const QBrush *fill)
307 \relates <qdrawutil.h>
308
309 Draws the shaded panel beginning at (\a x, \a y) with the given \a
310 width and \a height using the provided \a painter and the given \a
311 lineWidth.
312
313 The given \a palette specifies the shading colors (\l
314 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
315 {QPalette::mid()}{middle} colors). The panel's interior is filled
316 with the \a fill brush unless \a fill is 0.
317
318 The panel appears sunken if \a sunken is true, otherwise raised.
319
320 \warning This function does not look at QWidget::style() or
321 QApplication::style(). Use the drawing functions in QStyle to make
322 widgets that follow the current GUI style.
323
324 Alternatively you can use a QFrame widget and apply the
325 QFrame::setFrameStyle() function to display a shaded panel:
326
327 \snippet code/src_gui_painting_qdrawutil.cpp 2
328
329 \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle
330*/
331
332void qDrawShadePanel(QPainter *p, int x, int y, int w, int h,
333 const QPalette &pal, bool sunken,
334 int lineWidth, const QBrush *fill)
335{
336 if (w == 0 || h == 0)
337 return;
338 if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0)) {
339 qWarning(msg: "qDrawShadePanel: Invalid parameters");
340 }
341
342 PainterStateGuard painterGuard(p);
343 const qreal devicePixelRatio = p->device()->devicePixelRatioF();
344 bool isTranslated = false;
345 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
346 painterGuard.save();
347 const qreal inverseScale = qreal(1) / devicePixelRatio;
348 p->scale(sx: inverseScale, sy: inverseScale);
349 x = qRound(d: devicePixelRatio * x);
350 y = qRound(d: devicePixelRatio * y);
351 w = qRound(d: devicePixelRatio * w);
352 h = qRound(d: devicePixelRatio * h);
353 lineWidth = qRound(d: devicePixelRatio * lineWidth);
354 p->translate(dx: 0.5, dy: 0.5);
355 isTranslated = true;
356 }
357
358 QColor shade = pal.dark().color();
359 QColor light = pal.light().color();
360 if (fill) {
361 if (fill->color() == shade)
362 shade = pal.shadow().color();
363 if (fill->color() == light)
364 light = pal.midlight().color();
365 }
366 QPen oldPen = p->pen(); // save pen
367 QVector<QLineF> lines;
368 lines.reserve(asize: 2*lineWidth);
369
370 if (sunken)
371 p->setPen(shade);
372 else
373 p->setPen(light);
374 int x1, y1, x2, y2;
375 int i;
376 x1 = x;
377 y1 = y2 = y;
378 x2 = x+w-2;
379 for (i=0; i<lineWidth; i++) { // top shadow
380 lines << QLineF(x1, y1++, x2--, y2++);
381 }
382 x2 = x1;
383 y1 = y+h-2;
384 for (i=0; i<lineWidth; i++) { // left shado
385 lines << QLineF(x1++, y1, x2++, y2--);
386 }
387 p->drawLines(lines);
388 lines.clear();
389 if (sunken)
390 p->setPen(light);
391 else
392 p->setPen(shade);
393 x1 = x;
394 y1 = y2 = y+h-1;
395 x2 = x+w-1;
396 for (i=0; i<lineWidth; i++) { // bottom shadow
397 lines << QLineF(x1++, y1--, x2, y2--);
398 }
399 x1 = x2;
400 y1 = y;
401 y2 = y+h-lineWidth-1;
402 for (i=0; i<lineWidth; i++) { // right shadow
403 lines << QLineF(x1--, y1++, x2--, y2);
404 }
405 p->drawLines(lines);
406 if (fill) { // fill with fill color
407 if (isTranslated)
408 p->translate(dx: -0.5, dy: -0.5);
409 p->fillRect(x: x+lineWidth, y: y+lineWidth, w: w-lineWidth*2, h: h-lineWidth*2, b: *fill);
410 }
411 p->setPen(oldPen); // restore pen
412}
413
414
415/*!
416 \internal
417 This function draws a rectangle with two pixel line width.
418 It is called from qDrawWinButton() and qDrawWinPanel().
419
420 c1..c4 and fill are used:
421
422 1 1 1 1 1 2
423 1 3 3 3 4 2
424 1 3 F F 4 2
425 1 3 F F 4 2
426 1 4 4 4 4 2
427 2 2 2 2 2 2
428*/
429
430static void qDrawWinShades(QPainter *p,
431 int x, int y, int w, int h,
432 const QColor &c1, const QColor &c2,
433 const QColor &c3, const QColor &c4,
434 const QBrush *fill)
435{
436 if (w < 2 || h < 2) // can't do anything with that
437 return;
438
439 PainterStateGuard painterGuard(p);
440 const qreal devicePixelRatio = p->device()->devicePixelRatioF();
441 bool isTranslated = false;
442 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
443 painterGuard.save();
444 const qreal inverseScale = qreal(1) / devicePixelRatio;
445 p->scale(sx: inverseScale, sy: inverseScale);
446 x = qRound(d: devicePixelRatio * x);
447 y = qRound(d: devicePixelRatio * y);
448 w = qRound(d: devicePixelRatio * w);
449 h = qRound(d: devicePixelRatio * h);
450 p->translate(dx: 0.5, dy: 0.5);
451 isTranslated = true;
452 }
453
454 QPen oldPen = p->pen();
455 QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
456 p->setPen(c1);
457 p->drawPolyline(points: a, pointCount: 3);
458 QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
459 p->setPen(c2);
460 p->drawPolyline(points: b, pointCount: 3);
461 if (w > 4 && h > 4) {
462 QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
463 p->setPen(c3);
464 p->drawPolyline(points: c, pointCount: 3);
465 QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) };
466 p->setPen(c4);
467 p->drawPolyline(points: d, pointCount: 3);
468 if (fill) {
469 if (isTranslated)
470 p->translate(dx: -0.5, dy: -0.5);
471 p->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
472 }
473 }
474 p->setPen(oldPen);
475}
476
477
478/*!
479 \fn void qDrawWinButton(QPainter *painter, int x, int y, int width, int height,
480 const QPalette &palette, bool sunken,
481 const QBrush *fill)
482 \relates <qdrawutil.h>
483
484 Draws the Windows-style button specified by the given point (\a x,
485 \a y}, \a width and \a height using the provided \a painter with a
486 line width of 2 pixels. The button's interior is filled with the
487 \a{fill} brush unless \a fill is 0.
488
489 The given \a palette specifies the shading colors (\l
490 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
491 {QPalette::mid()}{middle} colors).
492
493 The button appears sunken if \a sunken is true, otherwise raised.
494
495 \warning This function does not look at QWidget::style() or
496 QApplication::style()-> Use the drawing functions in QStyle to make
497 widgets that follow the current GUI style.
498
499 \sa qDrawWinPanel(), QStyle
500*/
501
502void qDrawWinButton(QPainter *p, int x, int y, int w, int h,
503 const QPalette &pal, bool sunken,
504 const QBrush *fill)
505{
506 if (sunken)
507 qDrawWinShades(p, x, y, w, h,
508 c1: pal.shadow().color(), c2: pal.light().color(), c3: pal.dark().color(),
509 c4: pal.button().color(), fill);
510 else
511 qDrawWinShades(p, x, y, w, h,
512 c1: pal.light().color(), c2: pal.shadow().color(), c3: pal.button().color(),
513 c4: pal.dark().color(), fill);
514}
515
516/*!
517 \fn void qDrawWinPanel(QPainter *painter, int x, int y, int width, int height,
518 const QPalette &palette, bool sunken,
519 const QBrush *fill)
520 \relates <qdrawutil.h>
521
522 Draws the Windows-style panel specified by the given point(\a x,
523 \a y), \a width and \a height using the provided \a painter with a
524 line width of 2 pixels. The button's interior is filled with the
525 \a fill brush unless \a fill is 0.
526
527 The given \a palette specifies the shading colors. The panel
528 appears sunken if \a sunken is true, otherwise raised.
529
530 \warning This function does not look at QWidget::style() or
531 QApplication::style(). Use the drawing functions in QStyle to make
532 widgets that follow the current GUI style.
533
534 Alternatively you can use a QFrame widget and apply the
535 QFrame::setFrameStyle() function to display a shaded panel:
536
537 \snippet code/src_gui_painting_qdrawutil.cpp 3
538
539 \sa qDrawShadePanel(), qDrawWinButton(), QStyle
540*/
541
542void qDrawWinPanel(QPainter *p, int x, int y, int w, int h,
543 const QPalette &pal, bool sunken,
544 const QBrush *fill)
545{
546 if (sunken)
547 qDrawWinShades(p, x, y, w, h,
548 c1: pal.dark().color(), c2: pal.light().color(), c3: pal.shadow().color(),
549 c4: pal.midlight().color(), fill);
550 else
551 qDrawWinShades(p, x, y, w, h,
552 c1: pal.light().color(), c2: pal.shadow().color(), c3: pal.midlight().color(),
553 c4: pal.dark().color(), fill);
554}
555
556/*!
557 \fn void qDrawPlainRect(QPainter *painter, int x, int y, int width, int height, const QColor &lineColor,
558 int lineWidth, const QBrush *fill)
559 \relates <qdrawutil.h>
560
561 Draws the plain rectangle beginning at (\a x, \a y) with the given
562 \a width and \a height, using the specified \a painter, \a lineColor
563 and \a lineWidth. The rectangle's interior is filled with the \a
564 fill brush unless \a fill is 0.
565
566 \warning This function does not look at QWidget::style() or
567 QApplication::style(). Use the drawing functions in QStyle to make
568 widgets that follow the current GUI style.
569
570 Alternatively you can use a QFrame widget and apply the
571 QFrame::setFrameStyle() function to display a plain rectangle:
572
573 \snippet code/src_gui_painting_qdrawutil.cpp 4
574
575 \sa qDrawShadeRect(), QStyle
576*/
577
578void qDrawPlainRect(QPainter *p, int x, int y, int w, int h, const QColor &c,
579 int lineWidth, const QBrush *fill)
580{
581 if (w == 0 || h == 0)
582 return;
583 if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0)) {
584 qWarning(msg: "qDrawPlainRect: Invalid parameters");
585 }
586
587 PainterStateGuard painterGuard(p);
588 const qreal devicePixelRatio = p->device()->devicePixelRatioF();
589 if (!qFuzzyCompare(p1: devicePixelRatio, p2: qreal(1))) {
590 painterGuard.save();
591 const qreal inverseScale = qreal(1) / devicePixelRatio;
592 p->scale(sx: inverseScale, sy: inverseScale);
593 x = qRound(d: devicePixelRatio * x);
594 y = qRound(d: devicePixelRatio * y);
595 w = qRound(d: devicePixelRatio * w);
596 h = qRound(d: devicePixelRatio * h);
597 lineWidth = qRound(d: devicePixelRatio * lineWidth);
598 p->translate(dx: 0.5, dy: 0.5);
599 }
600
601 QPen oldPen = p->pen();
602 QBrush oldBrush = p->brush();
603 p->setPen(c);
604 p->setBrush(Qt::NoBrush);
605 for (int i=0; i<lineWidth; i++)
606 p->drawRect(x: x+i, y: y+i, w: w-i*2 - 1, h: h-i*2 - 1);
607 if (fill) { // fill with fill color
608 p->setPen(Qt::NoPen);
609 p->setBrush(*fill);
610 p->drawRect(x: x+lineWidth, y: y+lineWidth, w: w-lineWidth*2, h: h-lineWidth*2);
611 }
612 p->setPen(oldPen);
613 p->setBrush(oldBrush);
614}
615
616/*****************************************************************************
617 Overloaded functions.
618 *****************************************************************************/
619
620/*!
621 \fn void qDrawShadeLine(QPainter *painter, const QPoint &p1, const QPoint &p2,
622 const QPalette &palette, bool sunken, int lineWidth, int midLineWidth)
623 \relates <qdrawutil.h>
624 \overload
625
626 Draws a horizontal or vertical shaded line between \a p1 and \a p2
627 using the given \a painter. Note that nothing is drawn if the line
628 between the points would be neither horizontal nor vertical.
629
630 The provided \a palette specifies the shading colors (\l
631 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
632 {QPalette::mid()}{middle} colors). The given \a lineWidth
633 specifies the line width for each of the lines; it is not the
634 total line width. The given \a midLineWidth specifies the width of
635 a middle line drawn in the QPalette::mid() color.
636
637 The line appears sunken if \a sunken is true, otherwise raised.
638
639 \warning This function does not look at QWidget::style() or
640 QApplication::style(). Use the drawing functions in QStyle to
641 make widgets that follow the current GUI style.
642
643
644 Alternatively you can use a QFrame widget and apply the
645 QFrame::setFrameStyle() function to display a shaded line:
646
647 \snippet code/src_gui_painting_qdrawutil.cpp 5
648
649 \sa qDrawShadeRect(), qDrawShadePanel(), QStyle
650*/
651
652void qDrawShadeLine(QPainter *p, const QPoint &p1, const QPoint &p2,
653 const QPalette &pal, bool sunken,
654 int lineWidth, int midLineWidth)
655{
656 qDrawShadeLine(p, x1: p1.x(), y1: p1.y(), x2: p2.x(), y2: p2.y(), pal, sunken,
657 lineWidth, midLineWidth);
658}
659
660/*!
661 \fn void qDrawShadeRect(QPainter *painter, const QRect &rect, const QPalette &palette,
662 bool sunken, int lineWidth, int midLineWidth, const QBrush *fill)
663 \relates <qdrawutil.h>
664 \overload
665
666 Draws the shaded rectangle specified by \a rect using the given \a painter.
667
668 The provide \a palette specifies the shading colors (\l
669 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
670 {QPalette::mid()}{middle} colors. The given \a lineWidth
671 specifies the line width for each of the lines; it is not the
672 total line width. The \a midLineWidth specifies the width of a
673 middle line drawn in the QPalette::mid() color. The rectangle's
674 interior is filled with the \a fill brush unless \a fill is 0.
675
676 The rectangle appears sunken if \a sunken is true, otherwise
677 raised.
678
679 \warning This function does not look at QWidget::style() or
680 QApplication::style(). Use the drawing functions in QStyle to make
681 widgets that follow the current GUI style.
682
683 Alternatively you can use a QFrame widget and apply the
684 QFrame::setFrameStyle() function to display a shaded rectangle:
685
686 \snippet code/src_gui_painting_qdrawutil.cpp 6
687
688 \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle
689*/
690
691void qDrawShadeRect(QPainter *p, const QRect &r,
692 const QPalette &pal, bool sunken,
693 int lineWidth, int midLineWidth,
694 const QBrush *fill)
695{
696 qDrawShadeRect(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), pal, sunken,
697 lineWidth, midLineWidth, fill);
698}
699
700/*!
701 \fn void qDrawShadePanel(QPainter *painter, const QRect &rect, const QPalette &palette,
702 bool sunken, int lineWidth, const QBrush *fill)
703 \relates <qdrawutil.h>
704 \overload
705
706 Draws the shaded panel at the rectangle specified by \a rect using the
707 given \a painter and the given \a lineWidth.
708
709 The given \a palette specifies the shading colors (\l
710 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
711 {QPalette::mid()}{middle} colors). The panel's interior is filled
712 with the \a fill brush unless \a fill is 0.
713
714 The panel appears sunken if \a sunken is true, otherwise raised.
715
716 \warning This function does not look at QWidget::style() or
717 QApplication::style(). Use the drawing functions in QStyle to make
718 widgets that follow the current GUI style.
719
720 Alternatively you can use a QFrame widget and apply the
721 QFrame::setFrameStyle() function to display a shaded panel:
722
723 \snippet code/src_gui_painting_qdrawutil.cpp 7
724
725 \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle
726*/
727
728void qDrawShadePanel(QPainter *p, const QRect &r,
729 const QPalette &pal, bool sunken,
730 int lineWidth, const QBrush *fill)
731{
732 qDrawShadePanel(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), pal, sunken,
733 lineWidth, fill);
734}
735
736/*!
737 \fn void qDrawWinButton(QPainter *painter, const QRect &rect, const QPalette &palette,
738 bool sunken, const QBrush *fill)
739 \relates <qdrawutil.h>
740 \overload
741
742 Draws the Windows-style button at the rectangle specified by \a rect using
743 the given \a painter with a line width of 2 pixels. The button's interior
744 is filled with the \a{fill} brush unless \a fill is 0.
745
746 The given \a palette specifies the shading colors (\l
747 {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
748 {QPalette::mid()}{middle} colors).
749
750 The button appears sunken if \a sunken is true, otherwise raised.
751
752 \warning This function does not look at QWidget::style() or
753 QApplication::style()-> Use the drawing functions in QStyle to make
754 widgets that follow the current GUI style.
755
756 \sa qDrawWinPanel(), QStyle
757*/
758
759void qDrawWinButton(QPainter *p, const QRect &r,
760 const QPalette &pal, bool sunken, const QBrush *fill)
761{
762 qDrawWinButton(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), pal, sunken, fill);
763}
764
765/*!
766 \fn void qDrawWinPanel(QPainter *painter, const QRect &rect, const QPalette &palette,
767 bool sunken, const QBrush *fill)
768 \overload
769
770 Draws the Windows-style panel at the rectangle specified by \a rect using
771 the given \a painter with a line width of 2 pixels. The button's interior
772 is filled with the \a fill brush unless \a fill is 0.
773
774 The given \a palette specifies the shading colors. The panel
775 appears sunken if \a sunken is true, otherwise raised.
776
777 \warning This function does not look at QWidget::style() or
778 QApplication::style(). Use the drawing functions in QStyle to make
779 widgets that follow the current GUI style.
780
781 Alternatively you can use a QFrame widget and apply the
782 QFrame::setFrameStyle() function to display a shaded panel:
783
784 \snippet code/src_gui_painting_qdrawutil.cpp 8
785
786 \sa qDrawShadePanel(), qDrawWinButton(), QStyle
787*/
788
789void qDrawWinPanel(QPainter *p, const QRect &r,
790 const QPalette &pal, bool sunken, const QBrush *fill)
791{
792 qDrawWinPanel(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), pal, sunken, fill);
793}
794
795/*!
796 \fn void qDrawPlainRect(QPainter *painter, const QRect &rect, const QColor &lineColor, int lineWidth, const QBrush *fill)
797 \relates <qdrawutil.h>
798 \overload
799
800 Draws the plain rectangle specified by \a rect using the given \a painter,
801 \a lineColor and \a lineWidth. The rectangle's interior is filled with the
802 \a fill brush unless \a fill is 0.
803
804 \warning This function does not look at QWidget::style() or
805 QApplication::style(). Use the drawing functions in QStyle to make
806 widgets that follow the current GUI style.
807
808 Alternatively you can use a QFrame widget and apply the
809 QFrame::setFrameStyle() function to display a plain rectangle:
810
811 \snippet code/src_gui_painting_qdrawutil.cpp 9
812
813 \sa qDrawShadeRect(), QStyle
814*/
815
816void qDrawPlainRect(QPainter *p, const QRect &r, const QColor &c,
817 int lineWidth, const QBrush *fill)
818{
819 qDrawPlainRect(p, x: r.x(), y: r.y(), w: r.width(), h: r.height(), c,
820 lineWidth, fill);
821}
822
823
824/*!
825 \class QTileRules
826 \since 4.6
827
828 \inmodule QtWidgets
829
830 \brief The QTileRules class provides the rules used to draw a
831 pixmap or image split into nine segments.
832
833 Spliiting is similar to \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images}.
834
835 \sa Qt::TileRule, QMargins
836*/
837
838/*! \fn QTileRules::QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule)
839 Constructs a QTileRules with the given \a horizontalRule and
840 \a verticalRule.
841 */
842
843/*! \fn QTileRules::QTileRules(Qt::TileRule rule)
844 Constructs a QTileRules with the given \a rule used for both
845 the horizontal rule and the vertical rule.
846 */
847
848/*!
849 \fn void qDrawBorderPixmap(QPainter *painter, const QRect &target, const QMargins &margins, const QPixmap &pixmap)
850 \relates <qdrawutil.h>
851 \since 4.6
852
853 \brief The qDrawBorderPixmap function is for drawing a pixmap into
854 the margins of a rectangle.
855
856 Draws the given \a pixmap into the given \a target rectangle, using the
857 given \a painter. The pixmap will be split into nine segments and drawn
858 according to the \a margins structure.
859*/
860
861typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray;
862
863/*!
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
1138} // namespace QQC2
1139
1140QT_END_NAMESPACE
1141

source code of qtdeclarative/src/quicknativestyle/qstyle/qquickdrawutil.cpp