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 QtGui 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 "qcssutil_p.h"
41#include "private/qcssparser_p.h"
42#include "qpainter.h"
43#include <qmath.h>
44
45#ifndef QT_NO_CSSPARSER
46
47QT_BEGIN_NAMESPACE
48
49using namespace QCss;
50
51static QPen qPenFromStyle(const QBrush& b, qreal width, BorderStyle s)
52{
53 Qt::PenStyle ps = Qt::NoPen;
54
55 switch (s) {
56 case BorderStyle_Dotted:
57 ps = Qt::DotLine;
58 break;
59 case BorderStyle_Dashed:
60 ps = width == 1 ? Qt::DotLine : Qt::DashLine;
61 break;
62 case BorderStyle_DotDash:
63 ps = Qt::DashDotLine;
64 break;
65 case BorderStyle_DotDotDash:
66 ps = Qt::DashDotDotLine;
67 break;
68 case BorderStyle_Inset:
69 case BorderStyle_Outset:
70 case BorderStyle_Solid:
71 ps = Qt::SolidLine;
72 break;
73 default:
74 break;
75 }
76
77 return QPen(b, width, ps, Qt::FlatCap);
78}
79
80void qDrawRoundedCorners(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2,
81 const QSizeF& r1, const QSizeF& r2,
82 Edge edge, BorderStyle s, QBrush c)
83{
84 const qreal pw = (edge == TopEdge || edge == BottomEdge) ? y2-y1 : x2-x1;
85 if (s == BorderStyle_Double) {
86 qreal wby3 = pw/3;
87 switch (edge) {
88 case TopEdge:
89 case BottomEdge:
90 qDrawRoundedCorners(p, x1, y1, x2, y2: y1+wby3, r1, r2, edge, s: BorderStyle_Solid, c);
91 qDrawRoundedCorners(p, x1, y1: y2-wby3, x2, y2, r1, r2, edge, s: BorderStyle_Solid, c);
92 break;
93 case LeftEdge:
94 qDrawRoundedCorners(p, x1, y1: y1+1, x2: x1+wby3, y2, r1, r2, edge: LeftEdge, s: BorderStyle_Solid, c);
95 qDrawRoundedCorners(p, x1: x2-wby3, y1: y1+1, x2, y2, r1, r2, edge: LeftEdge, s: BorderStyle_Solid, c);
96 break;
97 case RightEdge:
98 qDrawRoundedCorners(p, x1, y1: y1+1, x2: x1+wby3, y2, r1, r2, edge: RightEdge, s: BorderStyle_Solid, c);
99 qDrawRoundedCorners(p, x1: x2-wby3, y1: y1+1, x2, y2, r1, r2, edge: RightEdge, s: BorderStyle_Solid, c);
100 break;
101 default:
102 break;
103 }
104 return;
105 } else if (s == BorderStyle_Ridge || s == BorderStyle_Groove) {
106 BorderStyle s1, s2;
107 if (s == BorderStyle_Groove) {
108 s1 = BorderStyle_Inset;
109 s2 = BorderStyle_Outset;
110 } else {
111 s1 = BorderStyle_Outset;
112 s2 = BorderStyle_Inset;
113 }
114 int pwby2 = qRound(d: pw/2);
115 switch (edge) {
116 case TopEdge:
117 qDrawRoundedCorners(p, x1, y1, x2, y2: y1 + pwby2, r1, r2, edge: TopEdge, s: s1, c);
118 qDrawRoundedCorners(p, x1, y1: y1 + pwby2, x2, y2, r1, r2, edge: TopEdge, s: s2, c);
119 break;
120 case BottomEdge:
121 qDrawRoundedCorners(p, x1, y1: y1 + pwby2, x2, y2, r1, r2, edge: BottomEdge, s: s1, c);
122 qDrawRoundedCorners(p, x1, y1, x2, y2: y2-pwby2, r1, r2, edge: BottomEdge, s: s2, c);
123 break;
124 case LeftEdge:
125 qDrawRoundedCorners(p, x1, y1, x2: x1 + pwby2, y2, r1, r2, edge: LeftEdge, s: s1, c);
126 qDrawRoundedCorners(p, x1: x1 + pwby2, y1, x2, y2, r1, r2, edge: LeftEdge, s: s2, c);
127 break;
128 case RightEdge:
129 qDrawRoundedCorners(p, x1: x1 + pwby2, y1, x2, y2, r1, r2, edge: RightEdge, s: s1, c);
130 qDrawRoundedCorners(p, x1, y1, x2: x2 - pwby2, y2, r1, r2, edge: RightEdge, s: s2, c);
131 break;
132 default:
133 break;
134 }
135 } else if ((s == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
136 || (s == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
137 c = c.color().lighter();
138
139 p->save();
140 qreal pwby2 = pw/2;
141 p->setBrush(Qt::NoBrush);
142 QPen pen = qPenFromStyle(b: c, width: pw, s);
143 pen.setCapStyle(Qt::SquareCap); // this eliminates the offby1 errors that we might hit below
144 p->setPen(pen);
145 switch (edge) {
146 case TopEdge:
147 if (!r1.isEmpty())
148 p->drawArc(rect: QRectF(x1 - r1.width() + pwby2, y1 + pwby2,
149 2*r1.width() - pw, 2*r1.height() - pw), a: 135*16, alen: -45*16);
150 if (!r2.isEmpty())
151 p->drawArc(rect: QRectF(x2 - r2.width() + pwby2, y1 + pwby2,
152 2*r2.width() - pw, 2*r2.height() - pw), a: 45*16, alen: 45*16);
153 break;
154 case BottomEdge:
155 if (!r1.isEmpty())
156 p->drawArc(rect: QRectF(x1 - r1.width() + pwby2, y2 - 2*r1.height() + pwby2,
157 2*r1.width() - pw, 2*r1.height() - pw), a: -90 * 16, alen: -45 * 16);
158 if (!r2.isEmpty())
159 p->drawArc(rect: QRectF(x2 - r2.width() + pwby2, y2 - 2*r2.height() + pwby2,
160 2*r2.width() - pw, 2*r2.height() - pw), a: -90 * 16, alen: 45 * 16);
161 break;
162 case LeftEdge:
163 if (!r1.isEmpty())
164 p->drawArc(rect: QRectF(x1 + pwby2, y1 - r1.height() + pwby2,
165 2*r1.width() - pw, 2*r1.height() - pw), a: 135*16, alen: 45*16);
166 if (!r2.isEmpty())
167 p->drawArc(rect: QRectF(x1 + pwby2, y2 - r2.height() + pwby2,
168 2*r2.width() - pw, 2*r2.height() - pw), a: 180*16, alen: 45*16);
169 break;
170 case RightEdge:
171 if (!r1.isEmpty())
172 p->drawArc(rect: QRectF(x2 - 2*r1.width() + pwby2, y1 - r1.height() + pwby2,
173 2*r1.width() - pw, 2*r1.height() - pw), a: 45*16, alen: -45*16);
174 if (!r2.isEmpty())
175 p->drawArc(rect: QRectF(x2 - 2*r2.width() + pwby2, y2 - r2.height() + pwby2,
176 2*r2.width() - pw, 2*r2.height() - pw), a: 315*16, alen: 45*16);
177 break;
178 default:
179 break;
180 }
181 p->restore();
182}
183
184
185void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, qreal dw2,
186 QCss::Edge edge, QCss::BorderStyle style, QBrush c)
187{
188 p->save();
189 const qreal width = (edge == TopEdge || edge == BottomEdge) ? (y2-y1) : (x2-x1);
190
191 if (width <= 2 && style == BorderStyle_Double)
192 style = BorderStyle_Solid;
193
194 switch (style) {
195 case BorderStyle_Inset:
196 case BorderStyle_Outset:
197 if ((style == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
198 || (style == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
199 c = c.color().lighter();
200 Q_FALLTHROUGH();
201 case BorderStyle_Solid: {
202 p->setPen(Qt::NoPen);
203 p->setBrush(c);
204 if (width == 1 || (dw1 == 0 && dw2 == 0)) {
205 p->drawRect(rect: QRectF(x1, y1, x2-x1, y2-y1));
206 } else { // draw trapezoid
207 QPolygonF quad;
208 switch (edge) {
209 case TopEdge:
210 quad << QPointF(x1, y1) << QPointF(x1 + dw1, y2)
211 << QPointF(x2 - dw2, y2) << QPointF(x2, y1);
212 break;
213 case BottomEdge:
214 quad << QPointF(x1 + dw1, y1) << QPointF(x1, y2)
215 << QPointF(x2, y2) << QPointF(x2 - dw2, y1);
216 break;
217 case LeftEdge:
218 quad << QPointF(x1, y1) << QPointF(x1, y2)
219 << QPointF(x2, y2 - dw2) << QPointF(x2, y1 + dw1);
220 break;
221 case RightEdge:
222 quad << QPointF(x1, y1 + dw1) << QPointF(x1, y2 - dw2)
223 << QPointF(x2, y2) << QPointF(x2, y1);
224 break;
225 default:
226 break;
227 }
228 p->drawConvexPolygon(poly: quad);
229 }
230 break;
231 }
232 case BorderStyle_Dotted:
233 case BorderStyle_Dashed:
234 case BorderStyle_DotDash:
235 case BorderStyle_DotDotDash:
236 p->setPen(qPenFromStyle(b: c, width, s: style));
237 if (width == 1)
238 p->drawLine(l: QLineF(x1, y1, x2 - 1, y2 - 1));
239 else if (edge == TopEdge || edge == BottomEdge)
240 p->drawLine(l: QLineF(x1 + width/2, (y1 + y2)/2, x2 - width/2, (y1 + y2)/2));
241 else
242 p->drawLine(l: QLineF((x1+x2)/2, y1 + width/2, (x1+x2)/2, y2 - width/2));
243 break;
244
245 case BorderStyle_Double: {
246 int wby3 = qRound(d: width/3);
247 int dw1by3 = qRound(d: dw1/3);
248 int dw2by3 = qRound(d: dw2/3);
249 switch (edge) {
250 case TopEdge:
251 qDrawEdge(p, x1, y1, x2, y2: y1 + wby3, dw1: dw1by3, dw2: dw2by3, edge: TopEdge, style: BorderStyle_Solid, c);
252 qDrawEdge(p, x1: x1 + dw1 - dw1by3, y1: y2 - wby3, x2: x2 - dw2 + dw1by3, y2,
253 dw1: dw1by3, dw2: dw2by3, edge: TopEdge, style: BorderStyle_Solid, c);
254 break;
255 case LeftEdge:
256 qDrawEdge(p, x1, y1, x2: x1 + wby3, y2, dw1: dw1by3, dw2: dw2by3, edge: LeftEdge, style: BorderStyle_Solid, c);
257 qDrawEdge(p, x1: x2 - wby3, y1: y1 + dw1 - dw1by3, x2, y2: y2 - dw2 + dw2by3, dw1: dw1by3, dw2: dw2by3,
258 edge: LeftEdge, style: BorderStyle_Solid, c);
259 break;
260 case BottomEdge:
261 qDrawEdge(p, x1: x1 + dw1 - dw1by3, y1, x2: x2 - dw2 + dw2by3, y2: y1 + wby3, dw1: dw1by3, dw2: dw2by3,
262 edge: BottomEdge, style: BorderStyle_Solid, c);
263 qDrawEdge(p, x1, y1: y2 - wby3, x2, y2, dw1: dw1by3, dw2: dw2by3, edge: BottomEdge, style: BorderStyle_Solid, c);
264 break;
265 case RightEdge:
266 qDrawEdge(p, x1: x2 - wby3, y1, x2, y2, dw1: dw1by3, dw2: dw2by3, edge: RightEdge, style: BorderStyle_Solid, c);
267 qDrawEdge(p, x1, y1: y1 + dw1 - dw1by3, x2: x1 + wby3, y2: y2 - dw2 + dw2by3, dw1: dw1by3, dw2: dw2by3,
268 edge: RightEdge, style: BorderStyle_Solid, c);
269 break;
270 default:
271 break;
272 }
273 break;
274 }
275 case BorderStyle_Ridge:
276 case BorderStyle_Groove: {
277 BorderStyle s1, s2;
278 if (style == BorderStyle_Groove) {
279 s1 = BorderStyle_Inset;
280 s2 = BorderStyle_Outset;
281 } else {
282 s1 = BorderStyle_Outset;
283 s2 = BorderStyle_Inset;
284 }
285 int dw1by2 = qFloor(v: dw1/2), dw2by2 = qFloor(v: dw2/2);
286 int wby2 = qRound(d: width/2);
287 switch (edge) {
288 case TopEdge:
289 qDrawEdge(p, x1, y1, x2, y2: y1 + wby2, dw1: dw1by2, dw2: dw2by2, edge: TopEdge, style: s1, c);
290 qDrawEdge(p, x1: x1 + dw1by2, y1: y1 + wby2, x2: x2 - dw2by2, y2, dw1: dw1by2, dw2: dw2by2, edge: TopEdge, style: s2, c);
291 break;
292 case BottomEdge:
293 qDrawEdge(p, x1, y1: y1 + wby2, x2, y2, dw1: dw1by2, dw2: dw2by2, edge: BottomEdge, style: s1, c);
294 qDrawEdge(p, x1: x1 + dw1by2, y1, x2: x2 - dw2by2, y2: y1 + wby2, dw1: dw1by2, dw2: dw2by2, edge: BottomEdge, style: s2, c);
295 break;
296 case LeftEdge:
297 qDrawEdge(p, x1, y1, x2: x1 + wby2, y2, dw1: dw1by2, dw2: dw2by2, edge: LeftEdge, style: s1, c);
298 qDrawEdge(p, x1: x1 + wby2, y1: y1 + dw1by2, x2, y2: y2 - dw2by2, dw1: dw1by2, dw2: dw2by2, edge: LeftEdge, style: s2, c);
299 break;
300 case RightEdge:
301 qDrawEdge(p, x1: x1 + wby2, y1, x2, y2, dw1: dw1by2, dw2: dw2by2, edge: RightEdge, style: s1, c);
302 qDrawEdge(p, x1, y1: y1 + dw1by2, x2: x1 + wby2, y2: y2 - dw2by2, dw1: dw1by2, dw2: dw2by2, edge: RightEdge, style: s2, c);
303 break;
304 default:
305 break;
306 }
307 }
308 default:
309 break;
310 }
311 p->restore();
312}
313
314void qNormalizeRadii(const QRect &br, const QSize *radii,
315 QSize *tlr, QSize *trr, QSize *blr, QSize *brr)
316{
317 *tlr = radii[0].expandedTo(otherSize: QSize(0, 0));
318 *trr = radii[1].expandedTo(otherSize: QSize(0, 0));
319 *blr = radii[2].expandedTo(otherSize: QSize(0, 0));
320 *brr = radii[3].expandedTo(otherSize: QSize(0, 0));
321 if (tlr->width() + trr->width() > br.width())
322 *tlr = *trr = QSize(0, 0);
323 if (blr->width() + brr->width() > br.width())
324 *blr = *brr = QSize(0, 0);
325 if (tlr->height() + blr->height() > br.height())
326 *tlr = *blr = QSize(0, 0);
327 if (trr->height() + brr->height() > br.height())
328 *trr = *brr = QSize(0, 0);
329}
330
331// Determines if Edge e1 draws over Edge e2. Depending on this trapezoids or rectangles are drawn
332static bool paintsOver(const QCss::BorderStyle *styles, const QBrush *colors, QCss::Edge e1, QCss::Edge e2)
333{
334 QCss::BorderStyle s1 = styles[e1];
335 QCss::BorderStyle s2 = styles[e2];
336
337 if (s2 == BorderStyle_None || colors[e2] == Qt::transparent)
338 return true;
339
340 if ((s1 == BorderStyle_Solid && s2 == BorderStyle_Solid) && (colors[e1] == colors[e2])
341 && colors[e1].isOpaque()) {
342 return true;
343 }
344
345 return false;
346}
347
348void qDrawBorder(QPainter *p, const QRect &rect, const QCss::BorderStyle *styles,
349 const int *borders, const QBrush *colors, const QSize *radii)
350{
351 const QRectF br(rect);
352 QSize tlr, trr, blr, brr;
353 qNormalizeRadii(br: rect, radii, tlr: &tlr, trr: &trr, blr: &blr, brr: &brr);
354
355 // Drawn in increasing order of precendence
356 if (styles[BottomEdge] != BorderStyle_None && borders[BottomEdge] > 0) {
357 qreal dw1 = (blr.width() || paintsOver(styles, colors, e1: BottomEdge, e2: LeftEdge)) ? 0 : borders[LeftEdge];
358 qreal dw2 = (brr.width() || paintsOver(styles, colors, e1: BottomEdge, e2: RightEdge)) ? 0 : borders[RightEdge];
359 qreal x1 = br.x() + blr.width();
360 qreal y1 = br.y() + br.height() - borders[BottomEdge];
361 qreal x2 = br.x() + br.width() - brr.width();
362 qreal y2 = br.y() + br.height() ;
363
364 qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, edge: BottomEdge, style: styles[BottomEdge], c: colors[BottomEdge]);
365 if (blr.width() || brr.width())
366 qDrawRoundedCorners(p, x1, y1, x2, y2, r1: blr, r2: brr, edge: BottomEdge, s: styles[BottomEdge], c: colors[BottomEdge]);
367 }
368 if (styles[RightEdge] != BorderStyle_None && borders[RightEdge] > 0) {
369 qreal dw1 = (trr.height() || paintsOver(styles, colors, e1: RightEdge, e2: TopEdge)) ? 0 : borders[TopEdge];
370 qreal dw2 = (brr.height() || paintsOver(styles, colors, e1: RightEdge, e2: BottomEdge)) ? 0 : borders[BottomEdge];
371 qreal x1 = br.x() + br.width() - borders[RightEdge];
372 qreal y1 = br.y() + trr.height();
373 qreal x2 = br.x() + br.width();
374 qreal y2 = br.y() + br.height() - brr.height();
375
376 qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, edge: RightEdge, style: styles[RightEdge], c: colors[RightEdge]);
377 if (trr.height() || brr.height())
378 qDrawRoundedCorners(p, x1, y1, x2, y2, r1: trr, r2: brr, edge: RightEdge, s: styles[RightEdge], c: colors[RightEdge]);
379 }
380 if (styles[LeftEdge] != BorderStyle_None && borders[LeftEdge] > 0) {
381 qreal dw1 = (tlr.height() || paintsOver(styles, colors, e1: LeftEdge, e2: TopEdge)) ? 0 : borders[TopEdge];
382 qreal dw2 = (blr.height() || paintsOver(styles, colors, e1: LeftEdge, e2: BottomEdge)) ? 0 : borders[BottomEdge];
383 qreal x1 = br.x();
384 qreal y1 = br.y() + tlr.height();
385 qreal x2 = br.x() + borders[LeftEdge];
386 qreal y2 = br.y() + br.height() - blr.height();
387
388 qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, edge: LeftEdge, style: styles[LeftEdge], c: colors[LeftEdge]);
389 if (tlr.height() || blr.height())
390 qDrawRoundedCorners(p, x1, y1, x2, y2, r1: tlr, r2: blr, edge: LeftEdge, s: styles[LeftEdge], c: colors[LeftEdge]);
391 }
392 if (styles[TopEdge] != BorderStyle_None && borders[TopEdge] > 0) {
393 qreal dw1 = (tlr.width() || paintsOver(styles, colors, e1: TopEdge, e2: LeftEdge)) ? 0 : borders[LeftEdge];
394 qreal dw2 = (trr.width() || paintsOver(styles, colors, e1: TopEdge, e2: RightEdge)) ? 0 : borders[RightEdge];
395 qreal x1 = br.x() + tlr.width();
396 qreal y1 = br.y();
397 qreal x2 = br.left() + br.width() - trr.width();
398 qreal y2 = br.y() + borders[TopEdge];
399
400 qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, edge: TopEdge, style: styles[TopEdge], c: colors[TopEdge]);
401 if (tlr.width() || trr.width())
402 qDrawRoundedCorners(p, x1, y1, x2, y2, r1: tlr, r2: trr, edge: TopEdge, s: styles[TopEdge], c: colors[TopEdge]);
403 }
404}
405
406#endif //QT_NO_CSSPARSER
407
408QT_END_NAMESPACE
409

source code of qtbase/src/gui/painting/qcssutil.cpp