Coverage Report

Created: 2025-11-16 07:45

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