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