1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qglobal.h>
5#include "qstylesheetstyle_p.h"
6
7#if QT_CONFIG(style_stylesheet)
8
9#include "private/qcssutil_p.h"
10#include <qdebug.h>
11#include <qdir.h>
12#include <qapplication.h>
13#if QT_CONFIG(menu)
14#include <qmenu.h>
15#endif
16#if QT_CONFIG(menubar)
17#include <qmenubar.h>
18#endif
19#include <qpainter.h>
20#include <qstyleoption.h>
21#if QT_CONFIG(lineedit)
22#include <qlineedit.h>
23#endif
24#if QT_CONFIG(textedit)
25#include <qtextedit.h>
26#include <qplaintextedit.h>
27#endif
28#include <private/qwindowsstyle_p.h>
29#if QT_CONFIG(combobox)
30#include <qcombobox.h>
31#endif
32#include "private/qcssparser_p.h"
33#include "private/qmath_p.h"
34#include <qabstractscrollarea.h>
35#include "private/qabstractscrollarea_p.h"
36#if QT_CONFIG(tooltip)
37#include <qtooltip.h>
38#endif
39#include <qshareddata.h>
40#if QT_CONFIG(toolbutton)
41#include <qtoolbutton.h>
42#endif
43#if QT_CONFIG(scrollbar)
44#include <qscrollbar.h>
45#endif
46#if QT_CONFIG(abstractslider)
47#include <qabstractslider.h>
48#endif
49#include <qstring.h>
50#include <qfile.h>
51#if QT_CONFIG(checkbox)
52#include <qcheckbox.h>
53#endif
54#if QT_CONFIG(itemviews)
55#include <qheaderview.h>
56#endif
57#include <private/qwindowsstyle_p_p.h>
58#if QT_CONFIG(animation)
59#include <private/qstyleanimation_p.h>
60#endif
61#if QT_CONFIG(tabbar)
62#include <private/qtabbar_p.h>
63#endif
64#include <QMetaProperty>
65#if QT_CONFIG(mainwindow)
66#include <qmainwindow.h>
67#endif
68#if QT_CONFIG(dockwidget)
69#include <qdockwidget.h>
70#endif
71#if QT_CONFIG(mdiarea)
72#include <qmdisubwindow.h>
73#endif
74#if QT_CONFIG(dialog)
75#include <qdialog.h>
76#endif
77#include <private/qwidget_p.h>
78#if QT_CONFIG(spinbox)
79#include <QAbstractSpinBox>
80#endif
81#if QT_CONFIG(label)
82#include <QLabel>
83#endif
84#include "qdrawutil.h"
85
86#include <limits.h>
87#if QT_CONFIG(toolbar)
88#include <QtWidgets/qtoolbar.h>
89#endif
90#if QT_CONFIG(pushbutton)
91#include <QtWidgets/qpushbutton.h>
92#endif
93
94#include <QtGui/qpainterpath.h>
95#include <QtGui/qscreen.h>
96
97#include <QtCore/private/qduplicatetracker_p.h>
98
99QT_BEGIN_NAMESPACE
100
101using namespace Qt::StringLiterals;
102
103using namespace QCss;
104
105
106class QStyleSheetStylePrivate : public QWindowsStylePrivate
107{
108 Q_DECLARE_PUBLIC(QStyleSheetStyle)
109public:
110 QStyleSheetStylePrivate() { }
111};
112
113
114static QStyleSheetStyleCaches *styleSheetCaches = nullptr;
115
116/* RECURSION_GUARD:
117 * the QStyleSheetStyle is a proxy. If used with others proxy style, we may end up with something like:
118 * QStyleSheetStyle -> ProxyStyle -> QStyleSheetStyle -> OriginalStyle
119 * Recursion may happen if the style call the widget()->style() again.
120 * Not to mention the performance penalty of having two lookup of rules.
121 *
122 * The first instance of QStyleSheetStyle will set globalStyleSheetStyle to itself. The second one
123 * will notice the globalStyleSheetStyle is not istelf and call its base style directly.
124 */
125static const QStyleSheetStyle *globalStyleSheetStyle = nullptr;
126class QStyleSheetStyleRecursionGuard
127{
128 public:
129 QStyleSheetStyleRecursionGuard(const QStyleSheetStyle *that)
130 : guarded(globalStyleSheetStyle == nullptr)
131 {
132 if (guarded) globalStyleSheetStyle = that;
133 }
134 ~QStyleSheetStyleRecursionGuard() { if (guarded) globalStyleSheetStyle = nullptr; }
135 bool guarded;
136};
137#define RECURSION_GUARD(RETURN) \
138 if (globalStyleSheetStyle != 0 && globalStyleSheetStyle != this) { RETURN; } \
139 QStyleSheetStyleRecursionGuard recursion_guard(this);
140
141enum PseudoElement {
142 PseudoElement_None,
143 PseudoElement_DownArrow,
144 PseudoElement_UpArrow,
145 PseudoElement_LeftArrow,
146 PseudoElement_RightArrow,
147 PseudoElement_Indicator,
148 PseudoElement_ExclusiveIndicator,
149 PseudoElement_PushButtonMenuIndicator,
150 PseudoElement_ComboBoxDropDown,
151 PseudoElement_ComboBoxArrow,
152 PseudoElement_Item,
153 PseudoElement_SpinBoxUpButton,
154 PseudoElement_SpinBoxUpArrow,
155 PseudoElement_SpinBoxDownButton,
156 PseudoElement_SpinBoxDownArrow,
157 PseudoElement_GroupBoxTitle,
158 PseudoElement_GroupBoxIndicator,
159 PseudoElement_ToolButtonMenu,
160 PseudoElement_ToolButtonMenuArrow,
161 PseudoElement_ToolButtonMenuIndicator,
162 PseudoElement_ToolBoxTab,
163 PseudoElement_ScrollBarSlider,
164 PseudoElement_ScrollBarAddPage,
165 PseudoElement_ScrollBarSubPage,
166 PseudoElement_ScrollBarAddLine,
167 PseudoElement_ScrollBarSubLine,
168 PseudoElement_ScrollBarFirst,
169 PseudoElement_ScrollBarLast,
170 PseudoElement_ScrollBarUpArrow,
171 PseudoElement_ScrollBarDownArrow,
172 PseudoElement_ScrollBarLeftArrow,
173 PseudoElement_ScrollBarRightArrow,
174 PseudoElement_SplitterHandle,
175 PseudoElement_ToolBarHandle,
176 PseudoElement_ToolBarSeparator,
177 PseudoElement_MenuScroller,
178 PseudoElement_MenuTearoff,
179 PseudoElement_MenuCheckMark,
180 PseudoElement_MenuSeparator,
181 PseudoElement_MenuIcon,
182 PseudoElement_MenuRightArrow,
183 PseudoElement_TreeViewBranch,
184 PseudoElement_HeaderViewSection,
185 PseudoElement_HeaderViewUpArrow,
186 PseudoElement_HeaderViewDownArrow,
187 PseudoElement_ProgressBarChunk,
188 PseudoElement_TabBarTab,
189 PseudoElement_TabBarScroller,
190 PseudoElement_TabBarTear,
191 PseudoElement_SliderGroove,
192 PseudoElement_SliderHandle,
193 PseudoElement_SliderAddPage,
194 PseudoElement_SliderSubPage,
195 PseudoElement_SliderTickmark,
196 PseudoElement_TabWidgetPane,
197 PseudoElement_TabWidgetTabBar,
198 PseudoElement_TabWidgetLeftCorner,
199 PseudoElement_TabWidgetRightCorner,
200 PseudoElement_DockWidgetTitle,
201 PseudoElement_DockWidgetCloseButton,
202 PseudoElement_DockWidgetFloatButton,
203 PseudoElement_DockWidgetSeparator,
204 PseudoElement_MdiCloseButton,
205 PseudoElement_MdiMinButton,
206 PseudoElement_MdiNormalButton,
207 PseudoElement_TitleBar,
208 PseudoElement_TitleBarCloseButton,
209 PseudoElement_TitleBarMinButton,
210 PseudoElement_TitleBarMaxButton,
211 PseudoElement_TitleBarShadeButton,
212 PseudoElement_TitleBarUnshadeButton,
213 PseudoElement_TitleBarNormalButton,
214 PseudoElement_TitleBarContextHelpButton,
215 PseudoElement_TitleBarSysMenu,
216 PseudoElement_ViewItem,
217 PseudoElement_ViewItemIcon,
218 PseudoElement_ViewItemText,
219 PseudoElement_ViewItemIndicator,
220 PseudoElement_ScrollAreaCorner,
221 PseudoElement_TabBarTabCloseButton,
222 NumPseudoElements
223};
224
225struct PseudoElementInfo {
226 QStyle::SubControl subControl;
227 const char name[19];
228};
229
230static const PseudoElementInfo knownPseudoElements[NumPseudoElements] = {
231 { .subControl: QStyle::SC_None, .name: "" },
232 { .subControl: QStyle::SC_None, .name: "down-arrow" },
233 { .subControl: QStyle::SC_None, .name: "up-arrow" },
234 { .subControl: QStyle::SC_None, .name: "left-arrow" },
235 { .subControl: QStyle::SC_None, .name: "right-arrow" },
236 { .subControl: QStyle::SC_None, .name: "indicator" },
237 { .subControl: QStyle::SC_None, .name: "indicator" },
238 { .subControl: QStyle::SC_None, .name: "menu-indicator" },
239 { .subControl: QStyle::SC_ComboBoxArrow, .name: "drop-down" },
240 { .subControl: QStyle::SC_ComboBoxArrow, .name: "down-arrow" },
241 { .subControl: QStyle::SC_None, .name: "item" },
242 { .subControl: QStyle::SC_SpinBoxUp, .name: "up-button" },
243 { .subControl: QStyle::SC_SpinBoxUp, .name: "up-arrow" },
244 { .subControl: QStyle::SC_SpinBoxDown, .name: "down-button" },
245 { .subControl: QStyle::SC_SpinBoxDown, .name: "down-arrow" },
246 { .subControl: QStyle::SC_GroupBoxLabel, .name: "title" },
247 { .subControl: QStyle::SC_GroupBoxCheckBox, .name: "indicator" },
248 { .subControl: QStyle::SC_ToolButtonMenu, .name: "menu-button" },
249 { .subControl: QStyle::SC_ToolButtonMenu, .name: "menu-arrow" },
250 { .subControl: QStyle::SC_None, .name: "menu-indicator" },
251 { .subControl: QStyle::SC_None, .name: "tab" },
252 { .subControl: QStyle::SC_ScrollBarSlider, .name: "handle" },
253 { .subControl: QStyle::SC_ScrollBarAddPage, .name: "add-page" },
254 { .subControl: QStyle::SC_ScrollBarSubPage, .name: "sub-page" },
255 { .subControl: QStyle::SC_ScrollBarAddLine, .name: "add-line" },
256 { .subControl: QStyle::SC_ScrollBarSubLine, .name: "sub-line" },
257 { .subControl: QStyle::SC_ScrollBarFirst, .name: "first" },
258 { .subControl: QStyle::SC_ScrollBarLast, .name: "last" },
259 { .subControl: QStyle::SC_ScrollBarSubLine, .name: "up-arrow" },
260 { .subControl: QStyle::SC_ScrollBarAddLine, .name: "down-arrow" },
261 { .subControl: QStyle::SC_ScrollBarSubLine, .name: "left-arrow" },
262 { .subControl: QStyle::SC_ScrollBarAddLine, .name: "right-arrow" },
263 { .subControl: QStyle::SC_None, .name: "handle" },
264 { .subControl: QStyle::SC_None, .name: "handle" },
265 { .subControl: QStyle::SC_None, .name: "separator" },
266 { .subControl: QStyle::SC_None, .name: "scroller" },
267 { .subControl: QStyle::SC_None, .name: "tearoff" },
268 { .subControl: QStyle::SC_None, .name: "indicator" },
269 { .subControl: QStyle::SC_None, .name: "separator" },
270 { .subControl: QStyle::SC_None, .name: "icon" },
271 { .subControl: QStyle::SC_None, .name: "right-arrow" },
272 { .subControl: QStyle::SC_None, .name: "branch" },
273 { .subControl: QStyle::SC_None, .name: "section" },
274 { .subControl: QStyle::SC_None, .name: "down-arrow" },
275 { .subControl: QStyle::SC_None, .name: "up-arrow" },
276 { .subControl: QStyle::SC_None, .name: "chunk" },
277 { .subControl: QStyle::SC_None, .name: "tab" },
278 { .subControl: QStyle::SC_None, .name: "scroller" },
279 { .subControl: QStyle::SC_None, .name: "tear" },
280 { .subControl: QStyle::SC_SliderGroove, .name: "groove" },
281 { .subControl: QStyle::SC_SliderHandle, .name: "handle" },
282 { .subControl: QStyle::SC_None, .name: "add-page" },
283 { .subControl: QStyle::SC_None, .name: "sub-page"},
284 { .subControl: QStyle::SC_SliderTickmarks, .name: "tick-mark" },
285 { .subControl: QStyle::SC_None, .name: "pane" },
286 { .subControl: QStyle::SC_None, .name: "tab-bar" },
287 { .subControl: QStyle::SC_None, .name: "left-corner" },
288 { .subControl: QStyle::SC_None, .name: "right-corner" },
289 { .subControl: QStyle::SC_None, .name: "title" },
290 { .subControl: QStyle::SC_None, .name: "close-button" },
291 { .subControl: QStyle::SC_None, .name: "float-button" },
292 { .subControl: QStyle::SC_None, .name: "separator" },
293 { .subControl: QStyle::SC_MdiCloseButton, .name: "close-button" },
294 { .subControl: QStyle::SC_MdiMinButton, .name: "minimize-button" },
295 { .subControl: QStyle::SC_MdiNormalButton, .name: "normal-button" },
296 { .subControl: QStyle::SC_TitleBarLabel, .name: "title" },
297 { .subControl: QStyle::SC_TitleBarCloseButton, .name: "close-button" },
298 { .subControl: QStyle::SC_TitleBarMinButton, .name: "minimize-button" },
299 { .subControl: QStyle::SC_TitleBarMaxButton, .name: "maximize-button" },
300 { .subControl: QStyle::SC_TitleBarShadeButton, .name: "shade-button" },
301 { .subControl: QStyle::SC_TitleBarUnshadeButton, .name: "unshade-button" },
302 { .subControl: QStyle::SC_TitleBarNormalButton, .name: "normal-button" },
303 { .subControl: QStyle::SC_TitleBarContextHelpButton, .name: "contexthelp-button" },
304 { .subControl: QStyle::SC_TitleBarSysMenu, .name: "sys-menu" },
305 { .subControl: QStyle::SC_None, .name: "item" },
306 { .subControl: QStyle::SC_None, .name: "icon" },
307 { .subControl: QStyle::SC_None, .name: "text" },
308 { .subControl: QStyle::SC_None, .name: "indicator" },
309 { .subControl: QStyle::SC_None, .name: "corner" },
310 { .subControl: QStyle::SC_None, .name: "close-button" },
311};
312
313
314struct QStyleSheetBorderImageData : public QSharedData
315{
316 QStyleSheetBorderImageData()
317 : horizStretch(QCss::TileMode_Unknown), vertStretch(QCss::TileMode_Unknown)
318 {
319 for (int i = 0; i < 4; i++)
320 cuts[i] = -1;
321 }
322 int cuts[4];
323 QPixmap pixmap;
324 QImage image;
325 QCss::TileMode horizStretch, vertStretch;
326};
327
328struct QStyleSheetBackgroundData : public QSharedData
329{
330 QStyleSheetBackgroundData(const QBrush& b, const QPixmap& p, QCss::Repeat r,
331 Qt::Alignment a, QCss::Origin o, Attachment t, QCss::Origin c)
332 : brush(b), pixmap(p), repeat(r), position(a), origin(o), attachment(t), clip(c) { }
333
334 bool isTransparent() const {
335 if (brush.style() != Qt::NoBrush)
336 return !brush.isOpaque();
337 return pixmap.isNull() ? false : pixmap.hasAlpha();
338 }
339 QBrush brush;
340 QPixmap pixmap;
341 QCss::Repeat repeat;
342 Qt::Alignment position;
343 QCss::Origin origin;
344 QCss::Attachment attachment;
345 QCss::Origin clip;
346};
347
348struct QStyleSheetBorderData : public QSharedData
349{
350 QStyleSheetBorderData() : bi(nullptr)
351 {
352 for (int i = 0; i < 4; i++) {
353 borders[i] = 0;
354 styles[i] = QCss::BorderStyle_None;
355 }
356 }
357
358 QStyleSheetBorderData(int *b, QBrush *c, QCss::BorderStyle *s, QSize *r) : bi(nullptr)
359 {
360 for (int i = 0; i < 4; i++) {
361 borders[i] = b[i];
362 styles[i] = s[i];
363 colors[i] = c[i];
364 radii[i] = r[i];
365 }
366 }
367
368 int borders[4];
369 QBrush colors[4];
370 QCss::BorderStyle styles[4];
371 QSize radii[4]; // topleft, topright, bottomleft, bottomright
372
373 const QStyleSheetBorderImageData *borderImage() const
374 { return bi; }
375 bool hasBorderImage() const { return bi!=nullptr; }
376
377 QSharedDataPointer<QStyleSheetBorderImageData> bi;
378
379 bool isOpaque() const
380 {
381 for (int i = 0; i < 4; i++) {
382 if (styles[i] == QCss::BorderStyle_Native || styles[i] == QCss::BorderStyle_None)
383 continue;
384 if (styles[i] >= QCss::BorderStyle_Dotted && styles[i] <= QCss::BorderStyle_DotDotDash
385 && styles[i] != BorderStyle_Solid)
386 return false;
387 if (!colors[i].isOpaque())
388 return false;
389 if (!radii[i].isEmpty())
390 return false;
391 }
392 if (bi != nullptr && bi->pixmap.hasAlpha())
393 return false;
394 return true;
395 }
396};
397
398
399struct QStyleSheetOutlineData : public QStyleSheetBorderData
400{
401 QStyleSheetOutlineData()
402 {
403 for (int i = 0; i < 4; i++) {
404 offsets[i] = 0;
405 }
406 }
407
408 QStyleSheetOutlineData(int *b, QBrush *c, QCss::BorderStyle *s, QSize *r, int *o)
409 : QStyleSheetBorderData(b, c, s, r)
410 {
411 for (int i = 0; i < 4; i++) {
412 offsets[i] = o[i];
413 }
414 }
415
416 int offsets[4];
417};
418
419struct QStyleSheetBoxData : public QSharedData
420{
421 QStyleSheetBoxData(int *m, int *p, int s) : spacing(s)
422 {
423 for (int i = 0; i < 4; i++) {
424 margins[i] = m[i];
425 paddings[i] = p[i];
426 }
427 }
428
429 int margins[4];
430 int paddings[4];
431
432 int spacing;
433};
434
435struct QStyleSheetPaletteData : public QSharedData
436{
437 QStyleSheetPaletteData(const QBrush &foreground,
438 const QBrush &selectedForeground,
439 const QBrush &selectedBackground,
440 const QBrush &alternateBackground,
441 const QBrush &placeHolderTextForeground,
442 const QBrush &accent)
443 : foreground(foreground)
444 , selectionForeground(selectedForeground)
445 , selectionBackground(selectedBackground)
446 , alternateBackground(alternateBackground)
447 , placeholderForeground(placeHolderTextForeground)
448 , accent(accent)
449 { }
450
451 QBrush foreground;
452 QBrush selectionForeground;
453 QBrush selectionBackground;
454 QBrush alternateBackground;
455 QBrush placeholderForeground;
456 QBrush accent;
457};
458
459struct QStyleSheetGeometryData : public QSharedData
460{
461 QStyleSheetGeometryData(int w, int h, int minw, int minh, int maxw, int maxh)
462 : minWidth(minw), minHeight(minh), width(w), height(h), maxWidth(maxw), maxHeight(maxh) { }
463
464 int minWidth, minHeight, width, height, maxWidth, maxHeight;
465};
466
467struct QStyleSheetPositionData : public QSharedData
468{
469 QStyleSheetPositionData(int l, int t, int r, int b, Origin o, Qt::Alignment p, QCss::PositionMode m, Qt::Alignment a = { })
470 : left(l), top(t), bottom(b), right(r), origin(o), position(p), mode(m), textAlignment(a) { }
471
472 int left, top, bottom, right;
473 Origin origin;
474 Qt::Alignment position;
475 QCss::PositionMode mode;
476 Qt::Alignment textAlignment;
477};
478
479struct QStyleSheetImageData : public QSharedData
480{
481 QStyleSheetImageData(const QIcon &i, Qt::Alignment a, const QSize &sz)
482 : icon(i), alignment(a), size(sz) { }
483
484 QIcon icon;
485 Qt::Alignment alignment;
486 QSize size;
487};
488
489class QRenderRule
490{
491public:
492 QRenderRule() : features(0), hasFont(false), pal(nullptr), b(nullptr), bg(nullptr), bd(nullptr), ou(nullptr), geo(nullptr), p(nullptr), img(nullptr), clipset(0) { }
493 QRenderRule(const QList<QCss::Declaration> &, const QObject *);
494
495 QRect borderRect(const QRect &r) const;
496 QRect outlineRect(const QRect &r) const;
497 QRect paddingRect(const QRect &r) const;
498 QRect contentsRect(const QRect &r) const;
499
500 enum { Margin = 1, Border = 2, Padding = 4, All=Margin|Border|Padding };
501 QRect boxRect(const QRect &r, int flags = All) const;
502 QSize boxSize(const QSize &s, int flags = All) const;
503 QRect originRect(const QRect &rect, Origin origin) const;
504
505 QPainterPath borderClip(QRect rect);
506 void drawBorder(QPainter *, const QRect&);
507 void drawOutline(QPainter *, const QRect&);
508 void drawBorderImage(QPainter *, const QRect&);
509 void drawBackground(QPainter *, const QRect&, const QPoint& = QPoint(0, 0));
510 void drawBackgroundImage(QPainter *, const QRect&, QPoint = QPoint(0, 0));
511 void drawFrame(QPainter *, const QRect&);
512 void drawImage(QPainter *p, const QRect &rect);
513 void drawRule(QPainter *, const QRect&);
514 void configurePalette(QPalette *, QPalette::ColorGroup, const QWidget *, bool);
515 void configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette::ColorRole br);
516
517 const QStyleSheetPaletteData *palette() const { return pal; }
518 const QStyleSheetBoxData *box() const { return b; }
519 const QStyleSheetBackgroundData *background() const { return bg; }
520 const QStyleSheetBorderData *border() const { return bd; }
521 const QStyleSheetOutlineData *outline() const { return ou; }
522 const QStyleSheetGeometryData *geometry() const { return geo; }
523 const QStyleSheetPositionData *position() const { return p; }
524 const QStyleSheetImageData *icon() const { return iconPtr; }
525
526 bool hasModification() const;
527
528 bool hasPalette() const { return pal != nullptr; }
529 bool hasBackground() const { return bg != nullptr && (!bg->pixmap.isNull() || bg->brush.style() != Qt::NoBrush); }
530 bool hasGradientBackground() const { return bg && bg->brush.style() >= Qt::LinearGradientPattern
531 && bg->brush.style() <= Qt::ConicalGradientPattern; }
532
533 bool hasNativeBorder() const {
534 return bd == nullptr
535 || (!bd->hasBorderImage() && bd->styles[0] == BorderStyle_Native);
536 }
537
538 bool hasNativeOutline() const {
539 return (ou == nullptr
540 || (!ou->hasBorderImage() && ou->styles[0] == BorderStyle_Native));
541 }
542
543 bool baseStyleCanDraw() const {
544 if (!hasBackground() || (background()->brush.style() == Qt::NoBrush && bg->pixmap.isNull()))
545 return true;
546 if (bg && !bg->pixmap.isNull())
547 return false;
548 if (hasGradientBackground())
549 return features & StyleFeature_BackgroundGradient;
550 return features & StyleFeature_BackgroundColor;
551 }
552
553 bool hasBox() const { return b != nullptr; }
554 bool hasBorder() const { return bd != nullptr; }
555 bool hasOutline() const { return ou != nullptr; }
556 bool hasPosition() const { return p != nullptr; }
557 bool hasGeometry() const { return geo != nullptr; }
558 bool hasDrawable() const { return !hasNativeBorder() || hasBackground() || hasImage(); }
559 bool hasImage() const { return img != nullptr; }
560 bool hasIcon() const { return iconPtr != nullptr; }
561
562 QSize minimumContentsSize() const
563 { return geo ? QSize(geo->minWidth, geo->minHeight) : QSize(0, 0); }
564 QSize minimumSize() const
565 { return boxSize(s: minimumContentsSize()); }
566
567 QSize contentsSize() const
568 { return geo ? QSize(geo->width, geo->height)
569 : ((img && img->size.isValid()) ? img->size : QSize()); }
570 QSize contentsSize(const QSize &sz) const
571 {
572 QSize csz = contentsSize();
573 if (csz.width() == -1) csz.setWidth(sz.width());
574 if (csz.height() == -1) csz.setHeight(sz.height());
575 return csz;
576 }
577 bool hasContentsSize() const
578 { return (geo && (geo->width != -1 || geo->height != -1)) || (img && img->size.isValid()); }
579
580 QSize size() const { return boxSize(s: contentsSize()); }
581 QSize size(const QSize &sz) const { return boxSize(s: contentsSize(sz)); }
582 QSize adjustSize(const QSize &sz)
583 {
584 if (!geo)
585 return sz;
586 QSize csz = contentsSize();
587 if (csz.width() == -1) csz.setWidth(sz.width());
588 if (csz.height() == -1) csz.setHeight(sz.height());
589 if (geo->maxWidth != -1 && csz.width() > geo->maxWidth) csz.setWidth(geo->maxWidth);
590 if (geo->maxHeight != -1 && csz.height() > geo->maxHeight) csz.setHeight(geo->maxHeight);
591 csz=csz.expandedTo(otherSize: QSize(geo->minWidth, geo->minHeight));
592 return csz;
593 }
594
595 bool hasStyleHint(const QString &sh) const { return styleHints.contains(key: sh); }
596 QVariant styleHint(const QString &sh) const { return styleHints.value(key: sh); }
597
598 void fixupBorder(int);
599
600 // Shouldn't be here
601 void setClip(QPainter *p, const QRect &rect);
602 void unsetClip(QPainter *);
603
604public:
605 int features;
606 QBrush defaultBackground;
607 QFont font; // Be careful using this font directly. Prefer using font.resolve( )
608 bool hasFont;
609
610 QHash<QString, QVariant> styleHints;
611
612 QSharedDataPointer<QStyleSheetPaletteData> pal;
613 QSharedDataPointer<QStyleSheetBoxData> b;
614 QSharedDataPointer<QStyleSheetBackgroundData> bg;
615 QSharedDataPointer<QStyleSheetBorderData> bd;
616 QSharedDataPointer<QStyleSheetOutlineData> ou;
617 QSharedDataPointer<QStyleSheetGeometryData> geo;
618 QSharedDataPointer<QStyleSheetPositionData> p;
619 QSharedDataPointer<QStyleSheetImageData> img;
620 QSharedDataPointer<QStyleSheetImageData> iconPtr;
621
622 int clipset;
623 QPainterPath clipPath;
624};
625Q_DECLARE_TYPEINFO(QRenderRule, Q_RELOCATABLE_TYPE);
626
627///////////////////////////////////////////////////////////////////////////////////////////
628static const char knownStyleHints[][45] = {
629 "activate-on-singleclick",
630 "alignment",
631 "arrow-keys-navigate-into-children",
632 "backward-icon",
633 "button-layout",
634 "cd-icon",
635 "combobox-list-mousetracking",
636 "combobox-popup",
637 "computer-icon",
638 "desktop-icon",
639 "dialog-apply-icon",
640 "dialog-cancel-icon",
641 "dialog-close-icon",
642 "dialog-discard-icon",
643 "dialog-help-icon",
644 "dialog-no-icon",
645 "dialog-ok-icon",
646 "dialog-open-icon",
647 "dialog-reset-icon",
648 "dialog-save-icon",
649 "dialog-yes-icon",
650 "dialogbuttonbox-buttons-have-icons",
651 "directory-closed-icon",
652 "directory-icon",
653 "directory-link-icon",
654 "directory-open-icon",
655 "dither-disable-text",
656 "dockwidget-close-icon",
657 "downarrow-icon",
658 "dvd-icon",
659 "etch-disabled-text",
660 "file-icon",
661 "file-link-icon",
662 "filedialog-backward-icon", // unused
663 "filedialog-contentsview-icon",
664 "filedialog-detailedview-icon",
665 "filedialog-end-icon",
666 "filedialog-infoview-icon",
667 "filedialog-listview-icon",
668 "filedialog-new-directory-icon",
669 "filedialog-parent-directory-icon",
670 "filedialog-start-icon",
671 "floppy-icon",
672 "forward-icon",
673 "gridline-color",
674 "harddisk-icon",
675 "home-icon",
676 "lineedit-clear-button-icon",
677 "icon-size",
678 "leftarrow-icon",
679 "lineedit-password-character",
680 "lineedit-password-mask-delay",
681 "mdi-fill-space-on-maximize",
682 "menu-scrollable",
683 "menubar-altkey-navigation",
684 "menubar-separator",
685 "messagebox-critical-icon",
686 "messagebox-information-icon",
687 "messagebox-question-icon",
688 "messagebox-text-interaction-flags",
689 "messagebox-warning-icon",
690 "mouse-tracking",
691 "network-icon",
692 "opacity",
693 "paint-alternating-row-colors-for-empty-area",
694 "rightarrow-icon",
695 "scrollbar-contextmenu",
696 "scrollbar-leftclick-absolute-position",
697 "scrollbar-middleclick-absolute-position",
698 "scrollbar-roll-between-buttons",
699 "scrollbar-scroll-when-pointer-leaves-control",
700 "scrollview-frame-around-contents",
701 "show-decoration-selected",
702 "spinbox-click-autorepeat-rate",
703 "spincontrol-disable-on-bounds",
704 "tabbar-elide-mode",
705 "tabbar-prefer-no-arrows",
706 "titlebar-close-icon",
707 "titlebar-contexthelp-icon",
708 "titlebar-maximize-icon",
709 "titlebar-menu-icon",
710 "titlebar-minimize-icon",
711 "titlebar-normal-icon",
712 "titlebar-shade-icon",
713 "titlebar-show-tooltips-on-buttons",
714 "titlebar-unshade-icon",
715 "toolbutton-popup-delay",
716 "trash-icon",
717 "uparrow-icon",
718 "widget-animation-duration"
719};
720
721static const int numKnownStyleHints = sizeof(knownStyleHints)/sizeof(knownStyleHints[0]);
722
723static QList<QVariant> subControlLayout(const QString& layout)
724{
725 QList<QVariant> buttons;
726 for (int i = 0; i < layout.size(); i++) {
727 int button = layout[i].toLatin1();
728 switch (button) {
729 case 'm':
730 buttons.append(t: PseudoElement_MdiMinButton);
731 buttons.append(t: PseudoElement_TitleBarMinButton);
732 break;
733 case 'M':
734 buttons.append(t: PseudoElement_TitleBarMaxButton);
735 break;
736 case 'X':
737 buttons.append(t: PseudoElement_MdiCloseButton);
738 buttons.append(t: PseudoElement_TitleBarCloseButton);
739 break;
740 case 'N':
741 buttons.append(t: PseudoElement_MdiNormalButton);
742 buttons.append(t: PseudoElement_TitleBarNormalButton);
743 break;
744 case 'I':
745 buttons.append(t: PseudoElement_TitleBarSysMenu);
746 break;
747 case 'T':
748 buttons.append(t: PseudoElement_TitleBar);
749 break;
750 case 'H':
751 buttons.append(t: PseudoElement_TitleBarContextHelpButton);
752 break;
753 case 'S':
754 buttons.append(t: PseudoElement_TitleBarShadeButton);
755 break;
756 default:
757 buttons.append(t: button);
758 break;
759 }
760 }
761 return buttons;
762}
763
764namespace {
765 struct ButtonInfo {
766 QRenderRule rule;
767 int element;
768 int offset;
769 int where;
770 int width;
771 };
772}
773template <> class QTypeInfo<ButtonInfo> : public QTypeInfoMerger<ButtonInfo, QRenderRule, int> {};
774
775QHash<QStyle::SubControl, QRect> QStyleSheetStyle::titleBarLayout(const QWidget *w, const QStyleOptionTitleBar *tb) const
776{
777 QHash<QStyle::SubControl, QRect> layoutRects;
778 const bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
779 const bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
780 QRenderRule subRule = renderRule(w, tb);
781 QRect cr = subRule.contentsRect(r: tb->rect);
782 QList<QVariant> layout = subRule.styleHint(sh: "button-layout"_L1).toList();
783 if (layout.isEmpty())
784 layout = subControlLayout(layout: "I(T)HSmMX"_L1);
785
786 int offsets[3] = { 0, 0, 0 };
787 enum Where { Left, Right, Center, NoWhere } where = Left;
788 QList<ButtonInfo> infos;
789 const int numLayouts = layout.size();
790 infos.reserve(size: numLayouts);
791 for (int i = 0; i < numLayouts; i++) {
792 const int element = layout[i].toInt();
793 if (element == '(') {
794 where = Center;
795 } else if (element == ')') {
796 where = Right;
797 } else {
798 ButtonInfo info;
799 info.element = element;
800 switch (element) {
801 case PseudoElement_TitleBar:
802 if (!(tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)))
803 continue;
804 break;
805 case PseudoElement_TitleBarContextHelpButton:
806 if (!(tb->titleBarFlags & Qt::WindowContextHelpButtonHint))
807 continue;
808 break;
809 case PseudoElement_TitleBarMinButton:
810 if (!(tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
811 continue;
812 if (isMinimized)
813 info.element = PseudoElement_TitleBarNormalButton;
814 break;
815 case PseudoElement_TitleBarMaxButton:
816 if (!(tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
817 continue;
818 if (isMaximized)
819 info.element = PseudoElement_TitleBarNormalButton;
820 break;
821 case PseudoElement_TitleBarShadeButton:
822 if (!(tb->titleBarFlags & Qt::WindowShadeButtonHint))
823 continue;
824 if (isMinimized)
825 info.element = PseudoElement_TitleBarUnshadeButton;
826 break;
827 case PseudoElement_TitleBarCloseButton:
828 case PseudoElement_TitleBarSysMenu:
829 if (!(tb->titleBarFlags & Qt::WindowSystemMenuHint))
830 continue;
831 break;
832 default:
833 continue;
834 }
835 if (info.element == PseudoElement_TitleBar) {
836 info.width = tb->fontMetrics.horizontalAdvance(tb->text) + 6;
837 subRule.geo = new QStyleSheetGeometryData(info.width, tb->fontMetrics.height(), -1, -1, -1, -1);
838 } else {
839 subRule = renderRule(w, tb, info.element);
840 info.width = subRule.size().width();
841 }
842 info.rule = subRule;
843 info.offset = offsets[where];
844 info.where = where;
845 offsets[where] += info.width;
846
847 infos.append(t: std::move(info));
848 }
849 }
850
851 for (int i = 0; i < infos.size(); i++) {
852 const ButtonInfo &info = infos[i];
853 QRect lr = cr;
854 switch (info.where) {
855 case Center: {
856 lr.setLeft(cr.left() + offsets[Left]);
857 lr.setRight(cr.right() - offsets[Right]);
858 QRect r(0, 0, offsets[Center], lr.height());
859 r.moveCenter(p: lr.center());
860 r.setLeft(r.left()+info.offset);
861 r.setWidth(info.width);
862 lr = r;
863 break; }
864 case Left:
865 lr.translate(dx: info.offset, dy: 0);
866 lr.setWidth(info.width);
867 break;
868 case Right:
869 lr.moveLeft(pos: cr.right() + 1 - offsets[Right] + info.offset);
870 lr.setWidth(info.width);
871 break;
872 default:
873 break;
874 }
875 QStyle::SubControl control = knownPseudoElements[info.element].subControl;
876 layoutRects[control] = positionRect(w, rule2: info.rule, pe: info.element, originRect: lr, dir: tb->direction);
877 }
878
879 return layoutRects;
880}
881
882static QStyle::StandardPixmap subControlIcon(int pe)
883{
884 switch (pe) {
885 case PseudoElement_MdiCloseButton: return QStyle::SP_TitleBarCloseButton;
886 case PseudoElement_MdiMinButton: return QStyle::SP_TitleBarMinButton;
887 case PseudoElement_MdiNormalButton: return QStyle::SP_TitleBarNormalButton;
888 case PseudoElement_TitleBarCloseButton: return QStyle::SP_TitleBarCloseButton;
889 case PseudoElement_TitleBarMinButton: return QStyle::SP_TitleBarMinButton;
890 case PseudoElement_TitleBarMaxButton: return QStyle::SP_TitleBarMaxButton;
891 case PseudoElement_TitleBarShadeButton: return QStyle::SP_TitleBarShadeButton;
892 case PseudoElement_TitleBarUnshadeButton: return QStyle::SP_TitleBarUnshadeButton;
893 case PseudoElement_TitleBarNormalButton: return QStyle::SP_TitleBarNormalButton;
894 case PseudoElement_TitleBarContextHelpButton: return QStyle::SP_TitleBarContextHelpButton;
895 default: break;
896 }
897 return QStyle::SP_CustomBase;
898}
899
900QRenderRule::QRenderRule(const QList<Declaration> &declarations, const QObject *object)
901 : features(0),
902 hasFont(false),
903 pal(nullptr),
904 b(nullptr),
905 bg(nullptr),
906 bd(nullptr),
907 ou(nullptr),
908 geo(nullptr),
909 p(nullptr),
910 img(nullptr),
911 clipset(0)
912{
913 QPalette palette = QGuiApplication::palette(); // ###: ideally widget's palette
914 ValueExtractor v(declarations, palette);
915 features = v.extractStyleFeatures();
916
917 int w = -1, h = -1, minw = -1, minh = -1, maxw = -1, maxh = -1;
918 if (v.extractGeometry(w: &w, h: &h, minw: &minw, minh: &minh, maxw: &maxw, maxh: &maxh))
919 geo = new QStyleSheetGeometryData(w, h, minw, minh, maxw, maxh);
920
921 int left = 0, top = 0, right = 0, bottom = 0;
922 Origin origin = Origin_Unknown;
923 Qt::Alignment position;
924 QCss::PositionMode mode = PositionMode_Unknown;
925 Qt::Alignment textAlignment;
926 if (v.extractPosition(l: &left, t: &top, r: &right, b: &bottom, &origin, &position, &mode, &textAlignment))
927 p = new QStyleSheetPositionData(left, top, right, bottom, origin, position, mode, textAlignment);
928
929 int margins[4], paddings[4], spacing = -1;
930 for (int i = 0; i < 4; i++)
931 margins[i] = paddings[i] = 0;
932 if (v.extractBox(margins, paddings, spacing: &spacing))
933 b = new QStyleSheetBoxData(margins, paddings, spacing);
934
935 int borders[4];
936 QBrush colors[4];
937 QCss::BorderStyle styles[4];
938 QSize radii[4];
939 for (int i = 0; i < 4; i++) {
940 borders[i] = 0;
941 styles[i] = BorderStyle_None;
942 }
943 if (v.extractBorder(borders, colors, Styles: styles, radii))
944 bd = new QStyleSheetBorderData(borders, colors, styles, radii);
945
946 int offsets[4];
947 for (int i = 0; i < 4; i++) {
948 borders[i] = offsets[i] = 0;
949 styles[i] = BorderStyle_None;
950 }
951 if (v.extractOutline(borders, colors, Styles: styles, radii, offsets))
952 ou = new QStyleSheetOutlineData(borders, colors, styles, radii, offsets);
953
954 QBrush brush;
955 QString uri;
956 Repeat repeat = Repeat_XY;
957 Qt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft;
958 Attachment attachment = Attachment_Scroll;
959 origin = Origin_Padding;
960 Origin clip = Origin_Border;
961 if (v.extractBackground(&brush, &uri, &repeat, &alignment, &origin, &attachment, &clip)) {
962 QPixmap pixmap = QStyleSheetStyle::loadPixmap(fileName: uri, context: object);
963 if (!uri.isEmpty() && pixmap.isNull())
964 qWarning(msg: "Could not create pixmap from %s", qPrintable(QDir::toNativeSeparators(uri)));
965 bg = new QStyleSheetBackgroundData(brush, pixmap, repeat, alignment, origin, attachment, clip);
966 }
967
968 QBrush foreground;
969 QBrush selectedForeground;
970 QBrush selectedBackground;
971 QBrush alternateBackground;
972 QBrush placeHolderTextForeground;
973 QBrush accent;
974 if (v.extractPalette(foreground: &foreground, selectedForeground: &selectedForeground, selectedBackground: &selectedBackground,
975 alternateBackground: &alternateBackground, placeHolderTextForeground: &placeHolderTextForeground, accent: &accent)) {
976 pal = new QStyleSheetPaletteData(foreground, selectedForeground, selectedBackground,
977 alternateBackground, placeHolderTextForeground, accent);
978 }
979
980 QIcon imgIcon;
981 alignment = Qt::AlignCenter;
982 QSize imgSize;
983 if (v.extractImage(icon: &imgIcon, a: &alignment, size: &imgSize))
984 img = new QStyleSheetImageData(imgIcon, alignment, imgSize);
985
986 QIcon icon;
987 QSize size;
988 if (v.extractIcon(icon: &icon, size: &size))
989 iconPtr = new QStyleSheetImageData(icon, Qt::AlignCenter, size);
990
991 int adj = -255;
992 hasFont = v.extractFont(font: &font, fontSizeAdjustment: &adj);
993
994#if QT_CONFIG(tooltip)
995 if (object && qstrcmp(str1: object->metaObject()->className(), str2: "QTipLabel") == 0)
996 palette = QToolTip::palette();
997#endif
998
999 for (int i = 0; i < declarations.size(); i++) {
1000 const Declaration& decl = declarations.at(i);
1001 if (decl.d->propertyId == BorderImage) {
1002 QString uri;
1003 QCss::TileMode horizStretch, vertStretch;
1004 int cuts[4];
1005
1006 decl.borderImageValue(image: &uri, cuts, h: &horizStretch, v: &vertStretch);
1007 if (uri.isEmpty() || uri == "none"_L1) {
1008 if (bd && bd->bi)
1009 bd->bi->pixmap = QPixmap();
1010 } else {
1011 if (!bd)
1012 bd = new QStyleSheetBorderData;
1013 if (!bd->bi)
1014 bd->bi = new QStyleSheetBorderImageData;
1015
1016 QStyleSheetBorderImageData *bi = bd->bi;
1017 bi->pixmap = QStyleSheetStyle::loadPixmap(fileName: uri, context: object);
1018 for (int i = 0; i < 4; i++)
1019 bi->cuts[i] = cuts[i];
1020 bi->horizStretch = horizStretch;
1021 bi->vertStretch = vertStretch;
1022 }
1023 } else if (decl.d->propertyId == QtBackgroundRole) {
1024 if (bg && bg->brush.style() != Qt::NoBrush)
1025 continue;
1026 int role = decl.d->values.at(i: 0).variant.toInt();
1027 if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
1028 defaultBackground = palette.color(cr: (QPalette::ColorRole)(role-Value_FirstColorRole));
1029 } else if (decl.d->property.startsWith(s: "qproperty-"_L1, cs: Qt::CaseInsensitive)) {
1030 // intentionally left blank...
1031 } else if (decl.d->propertyId == UnknownProperty) {
1032 bool knownStyleHint = false;
1033 for (int i = 0; i < numKnownStyleHints; i++) {
1034 QLatin1StringView styleHint(knownStyleHints[i]);
1035 if (decl.d->property.compare(other: styleHint) == 0) {
1036 QString hintName = QString(styleHint);
1037 QVariant hintValue;
1038 if (hintName.endsWith(s: "alignment"_L1)) {
1039 hintValue = (int) decl.alignmentValue();
1040 } else if (hintName.endsWith(s: "color"_L1)) {
1041 hintValue = (int) decl.colorValue().rgba();
1042 } else if (hintName.endsWith(s: "size"_L1)) {
1043 // Check only for the 'em' case
1044 const QString valueString = decl.d->values.at(i: 0).variant.toString();
1045 const bool isEmSize = valueString.endsWith(s: u"em", cs: Qt::CaseInsensitive);
1046 if (isEmSize || valueString.endsWith(s: u"ex", cs: Qt::CaseInsensitive)) {
1047 // 1em == size of font; 1ex == xHeight of font
1048 // See lengthValueFromData helper in qcssparser.cpp
1049 QFont fontForSize(font);
1050 // if no font is specified, then use the widget font if possible
1051 if (const QWidget *widget; !hasFont && (widget = qobject_cast<const QWidget*>(o: object)))
1052 fontForSize = widget->font();
1053
1054 const QFontMetrics fontMetrics(fontForSize);
1055 qreal pixelSize = isEmSize ? fontMetrics.height() : fontMetrics.xHeight();
1056
1057 // Transform size according to the 'em'/'ex' value
1058 qreal emexSize = {};
1059 if (decl.realValue(r: &emexSize, unit: isEmSize ? "em" : "ex") && emexSize > 0) {
1060 pixelSize *= emexSize;
1061 const QSizeF newSize(pixelSize, pixelSize);
1062 decl.d->parsed = QVariant::fromValue<QSizeF>(value: newSize);
1063 hintValue = newSize;
1064 } else {
1065 qWarning(msg: "Invalid '%s' size for %s. Skipping.",
1066 isEmSize ? "em" : "ex", qPrintable(valueString));
1067 }
1068 } else {
1069 // Normal case where we receive a 'px' or 'pt' unit
1070 hintValue = decl.sizeValue();
1071 }
1072 } else if (hintName.endsWith(s: "icon"_L1)) {
1073 hintValue = decl.iconValue();
1074 } else if (hintName == "button-layout"_L1 && decl.d->values.size() != 0
1075 && decl.d->values.at(i: 0).type == QCss::Value::String) {
1076 hintValue = subControlLayout(layout: decl.d->values.at(i: 0).variant.toString());
1077 } else {
1078 int integer;
1079 decl.intValue(i: &integer);
1080 hintValue = integer;
1081 }
1082 styleHints[decl.d->property] = hintValue;
1083 knownStyleHint = true;
1084 break;
1085 }
1086 }
1087 if (!knownStyleHint)
1088 qWarning(msg: "Unknown property %s", qPrintable(decl.d->property));
1089 }
1090 }
1091
1092 if (hasBorder()) {
1093 if (const QWidget *widget = qobject_cast<const QWidget *>(o: object)) {
1094 QStyleSheetStyle *style = const_cast<QStyleSheetStyle *>(globalStyleSheetStyle);
1095 if (!style)
1096 style = qt_styleSheet(style: widget->style());
1097 if (style)
1098 fixupBorder(style->nativeFrameWidth(widget));
1099 }
1100 if (border()->hasBorderImage())
1101 defaultBackground = QBrush();
1102 }
1103}
1104
1105QRect QRenderRule::borderRect(const QRect& r) const
1106{
1107 if (!hasBox())
1108 return r;
1109 const int* m = box()->margins;
1110 return r.adjusted(xp1: m[LeftEdge], yp1: m[TopEdge], xp2: -m[RightEdge], yp2: -m[BottomEdge]);
1111}
1112
1113QRect QRenderRule::outlineRect(const QRect& r) const
1114{
1115 QRect br = borderRect(r);
1116 if (!hasOutline())
1117 return br;
1118 const int *b = outline()->borders;
1119 return r.adjusted(xp1: b[LeftEdge], yp1: b[TopEdge], xp2: -b[RightEdge], yp2: -b[BottomEdge]);
1120}
1121
1122QRect QRenderRule::paddingRect(const QRect& r) const
1123{
1124 QRect br = borderRect(r);
1125 if (!hasBorder())
1126 return br;
1127 const int *b = border()->borders;
1128 return br.adjusted(xp1: b[LeftEdge], yp1: b[TopEdge], xp2: -b[RightEdge], yp2: -b[BottomEdge]);
1129}
1130
1131QRect QRenderRule::contentsRect(const QRect& r) const
1132{
1133 QRect pr = paddingRect(r);
1134 if (!hasBox())
1135 return pr;
1136 const int *p = box()->paddings;
1137 return pr.adjusted(xp1: p[LeftEdge], yp1: p[TopEdge], xp2: -p[RightEdge], yp2: -p[BottomEdge]);
1138}
1139
1140QRect QRenderRule::boxRect(const QRect& cr, int flags) const
1141{
1142 QRect r = cr;
1143 if (hasBox()) {
1144 if (flags & Margin) {
1145 const int *m = box()->margins;
1146 r.adjust(dx1: -m[LeftEdge], dy1: -m[TopEdge], dx2: m[RightEdge], dy2: m[BottomEdge]);
1147 }
1148 if (flags & Padding) {
1149 const int *p = box()->paddings;
1150 r.adjust(dx1: -p[LeftEdge], dy1: -p[TopEdge], dx2: p[RightEdge], dy2: p[BottomEdge]);
1151 }
1152 }
1153 if (hasBorder() && (flags & Border)) {
1154 const int *b = border()->borders;
1155 r.adjust(dx1: -b[LeftEdge], dy1: -b[TopEdge], dx2: b[RightEdge], dy2: b[BottomEdge]);
1156 }
1157 return r;
1158}
1159
1160QSize QRenderRule::boxSize(const QSize &cs, int flags) const
1161{
1162 QSize bs = boxRect(cr: QRect(QPoint(0, 0), cs), flags).size();
1163 if (cs.width() < 0) bs.setWidth(-1);
1164 if (cs.height() < 0) bs.setHeight(-1);
1165 return bs;
1166}
1167
1168void QRenderRule::fixupBorder(int nativeWidth)
1169{
1170 if (bd == nullptr)
1171 return;
1172
1173 if (!bd->hasBorderImage() || bd->bi->pixmap.isNull()) {
1174 bd->bi = nullptr;
1175 // ignore the color, border of edges that have none border-style
1176 QBrush color = pal ? pal->foreground : QBrush();
1177 const bool hasRadius = bd->radii[0].isValid() || bd->radii[1].isValid()
1178 || bd->radii[2].isValid() || bd->radii[3].isValid();
1179 for (int i = 0; i < 4; i++) {
1180 if ((bd->styles[i] == BorderStyle_Native) && hasRadius)
1181 bd->styles[i] = BorderStyle_None;
1182
1183 switch (bd->styles[i]) {
1184 case BorderStyle_None:
1185 // border-style: none forces width to be 0
1186 bd->colors[i] = QBrush();
1187 bd->borders[i] = 0;
1188 break;
1189 case BorderStyle_Native:
1190 if (bd->borders[i] == 0)
1191 bd->borders[i] = nativeWidth;
1192 Q_FALLTHROUGH();
1193 default:
1194 if (bd->colors[i].style() == Qt::NoBrush) // auto-acquire 'color'
1195 bd->colors[i] = color;
1196 break;
1197 }
1198 }
1199
1200 return;
1201 }
1202
1203 // inspect the border image
1204 QStyleSheetBorderImageData *bi = bd->bi;
1205 if (bi->cuts[0] == -1) {
1206 for (int i = 0; i < 4; i++) // assume, cut = border
1207 bi->cuts[i] = int(border()->borders[i]);
1208 }
1209}
1210
1211void QRenderRule::drawBorderImage(QPainter *p, const QRect& rect)
1212{
1213 setClip(p, rect);
1214 static const Qt::TileRule tileMode2TileRule[] = {
1215 Qt::StretchTile, Qt::RoundTile, Qt::StretchTile, Qt::RepeatTile, Qt::StretchTile };
1216
1217 const QStyleSheetBorderImageData *borderImageData = border()->borderImage();
1218 const int *targetBorders = border()->borders;
1219 const int *sourceBorders = borderImageData->cuts;
1220 QMargins sourceMargins(sourceBorders[LeftEdge], sourceBorders[TopEdge],
1221 sourceBorders[RightEdge], sourceBorders[BottomEdge]);
1222 QMargins targetMargins(targetBorders[LeftEdge], targetBorders[TopEdge],
1223 targetBorders[RightEdge], targetBorders[BottomEdge]);
1224
1225 bool wasSmoothPixmapTransform = p->renderHints() & QPainter::SmoothPixmapTransform;
1226 p->setRenderHint(hint: QPainter::SmoothPixmapTransform);
1227 qDrawBorderPixmap(painter: p, targetRect: rect, targetMargins, pixmap: borderImageData->pixmap,
1228 sourceRect: QRect(QPoint(), borderImageData->pixmap.size()), sourceMargins,
1229 rules: QTileRules(tileMode2TileRule[borderImageData->horizStretch], tileMode2TileRule[borderImageData->vertStretch]));
1230 p->setRenderHint(hint: QPainter::SmoothPixmapTransform, on: wasSmoothPixmapTransform);
1231 unsetClip(p);
1232}
1233
1234QRect QRenderRule::originRect(const QRect &rect, Origin origin) const
1235{
1236 switch (origin) {
1237 case Origin_Padding:
1238 return paddingRect(r: rect);
1239 case Origin_Border:
1240 return borderRect(r: rect);
1241 case Origin_Content:
1242 return contentsRect(r: rect);
1243 case Origin_Margin:
1244 default:
1245 return rect;
1246 }
1247}
1248
1249void QRenderRule::drawBackgroundImage(QPainter *p, const QRect &rect, QPoint off)
1250{
1251 if (!hasBackground())
1252 return;
1253
1254 const QPixmap& bgp = background()->pixmap;
1255 if (bgp.isNull())
1256 return;
1257
1258 setClip(p, rect: borderRect(r: rect));
1259
1260 if (background()->origin != background()->clip) {
1261 p->save();
1262 p->setClipRect(originRect(rect, origin: background()->clip), op: Qt::IntersectClip);
1263 }
1264
1265 if (background()->attachment == Attachment_Fixed)
1266 off = QPoint(0, 0);
1267
1268 QSize bgpSize = bgp.size() / bgp.devicePixelRatio();
1269 int bgpHeight = bgpSize.height();
1270 int bgpWidth = bgpSize.width();
1271 QRect r = originRect(rect, origin: background()->origin);
1272 QRect aligned = QStyle::alignedRect(direction: Qt::LeftToRight, alignment: background()->position, size: bgpSize, rectangle: r);
1273 QRect inter = aligned.translated(p: -off).intersected(other: r);
1274
1275 switch (background()->repeat) {
1276 case Repeat_Y:
1277 p->drawTiledPixmap(x: inter.x(), y: r.y(), w: inter.width(), h: r.height(), pm: bgp,
1278 sx: inter.x() - aligned.x() + off.x(),
1279 sy: bgpHeight - int(aligned.y() - r.y()) % bgpHeight + off.y());
1280 break;
1281 case Repeat_X:
1282 p->drawTiledPixmap(x: r.x(), y: inter.y(), w: r.width(), h: inter.height(), pm: bgp,
1283 sx: bgpWidth - int(aligned.x() - r.x())%bgpWidth + off.x(),
1284 sy: inter.y() - aligned.y() + off.y());
1285 break;
1286 case Repeat_XY:
1287 p->drawTiledPixmap(rect: r, pm: bgp,
1288 offset: QPoint(bgpWidth - int(aligned.x() - r.x())% bgpWidth + off.x(),
1289 bgpHeight - int(aligned.y() - r.y())%bgpHeight + off.y()));
1290 break;
1291 case Repeat_None:
1292 default:
1293 p->drawPixmap(x: inter.x(), y: inter.y(), pm: bgp, sx: inter.x() - aligned.x() + off.x(),
1294 sy: inter.y() - aligned.y() + off.y(), sw: bgp.width() , sh: bgp.height());
1295 break;
1296 }
1297
1298
1299 if (background()->origin != background()->clip)
1300 p->restore();
1301
1302 unsetClip(p);
1303}
1304
1305void QRenderRule::drawOutline(QPainter *p, const QRect &rect)
1306{
1307 if (!hasOutline())
1308 return;
1309
1310 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1311 p->setRenderHint(hint: QPainter::Antialiasing);
1312 qDrawBorder(p, rect, styles: ou->styles, borders: ou->borders, colors: ou->colors, radii: ou->radii);
1313 p->setRenderHint(hint: QPainter::Antialiasing, on: wasAntialiased);
1314}
1315
1316void QRenderRule::drawBorder(QPainter *p, const QRect& rect)
1317{
1318 if (!hasBorder())
1319 return;
1320
1321 if (border()->hasBorderImage()) {
1322 drawBorderImage(p, rect);
1323 return;
1324 }
1325
1326 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1327 p->setRenderHint(hint: QPainter::Antialiasing);
1328 qDrawBorder(p, rect, styles: bd->styles, borders: bd->borders, colors: bd->colors, radii: bd->radii);
1329 p->setRenderHint(hint: QPainter::Antialiasing, on: wasAntialiased);
1330}
1331
1332QPainterPath QRenderRule::borderClip(QRect r)
1333{
1334 if (!hasBorder())
1335 return QPainterPath();
1336
1337 QSize tlr, trr, blr, brr;
1338 qNormalizeRadii(br: r, radii: bd->radii, tlr: &tlr, trr: &trr, blr: &blr, brr: &brr);
1339 if (tlr.isNull() && trr.isNull() && blr.isNull() && brr.isNull())
1340 return QPainterPath();
1341
1342 const QRectF rect(r);
1343 const int *borders = border()->borders;
1344 QPainterPath path;
1345 qreal curY = rect.y() + borders[TopEdge]/2.0;
1346 path.moveTo(x: rect.x() + tlr.width(), y: curY);
1347 path.lineTo(x: rect.right() - trr.width(), y: curY);
1348 qreal curX = rect.right() - borders[RightEdge]/2.0;
1349 path.arcTo(x: curX - 2*trr.width() + borders[RightEdge], y: curY,
1350 w: trr.width()*2 - borders[RightEdge], h: trr.height()*2 - borders[TopEdge], startAngle: 90, arcLength: -90);
1351
1352 path.lineTo(x: curX, y: rect.bottom() - brr.height());
1353 curY = rect.bottom() - borders[BottomEdge]/2.0;
1354 path.arcTo(x: curX - 2*brr.width() + borders[RightEdge], y: curY - 2*brr.height() + borders[BottomEdge],
1355 w: brr.width()*2 - borders[RightEdge], h: brr.height()*2 - borders[BottomEdge], startAngle: 0, arcLength: -90);
1356
1357 path.lineTo(x: rect.x() + blr.width(), y: curY);
1358 curX = rect.left() + borders[LeftEdge]/2.0;
1359 path.arcTo(x: curX, y: rect.bottom() - 2*blr.height() + borders[BottomEdge]/2.0,
1360 w: blr.width()*2 - borders[LeftEdge], h: blr.height()*2 - borders[BottomEdge], startAngle: 270, arcLength: -90);
1361
1362 path.lineTo(x: curX, y: rect.top() + tlr.height());
1363 path.arcTo(x: curX, y: rect.top() + borders[TopEdge]/2.0,
1364 w: tlr.width()*2 - borders[LeftEdge], h: tlr.height()*2 - borders[TopEdge], startAngle: 180, arcLength: -90);
1365
1366 path.closeSubpath();
1367 return path;
1368}
1369
1370/*! \internal
1371 Clip the painter to the border (in case we are using radius border)
1372 */
1373void QRenderRule::setClip(QPainter *p, const QRect &rect)
1374{
1375 if (clipset++)
1376 return;
1377 clipPath = borderClip(r: rect);
1378 if (!clipPath.isEmpty()) {
1379 p->save();
1380 p->setClipPath(path: clipPath, op: Qt::IntersectClip);
1381 }
1382}
1383
1384void QRenderRule::unsetClip(QPainter *p)
1385{
1386 if (--clipset)
1387 return;
1388 if (!clipPath.isEmpty())
1389 p->restore();
1390}
1391
1392void QRenderRule::drawBackground(QPainter *p, const QRect& rect, const QPoint& off)
1393{
1394 QBrush brush = hasBackground() ? background()->brush : QBrush();
1395 if (brush.style() == Qt::NoBrush)
1396 brush = defaultBackground;
1397
1398 if (brush.style() != Qt::NoBrush) {
1399 Origin origin = hasBackground() ? background()->clip : Origin_Border;
1400 // ### fix for gradients
1401 const QPainterPath &borderPath = borderClip(r: originRect(rect, origin));
1402 if (!borderPath.isEmpty()) {
1403 // Drawn instead of being used as clipping path for better visual quality
1404 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1405 p->setRenderHint(hint: QPainter::Antialiasing);
1406 p->fillPath(path: borderPath, brush);
1407 p->setRenderHint(hint: QPainter::Antialiasing, on: wasAntialiased);
1408 } else {
1409 p->fillRect(originRect(rect, origin), brush);
1410 }
1411 }
1412
1413 drawBackgroundImage(p, rect, off);
1414}
1415
1416void QRenderRule::drawFrame(QPainter *p, const QRect& rect)
1417{
1418 drawBackground(p, rect);
1419 if (hasBorder())
1420 drawBorder(p, rect: borderRect(r: rect));
1421}
1422
1423void QRenderRule::drawImage(QPainter *p, const QRect &rect)
1424{
1425 if (!hasImage())
1426 return;
1427 img->icon.paint(painter: p, rect, alignment: img->alignment);
1428}
1429
1430void QRenderRule::drawRule(QPainter *p, const QRect& rect)
1431{
1432 drawFrame(p, rect);
1433 drawImage(p, rect: contentsRect(r: rect));
1434}
1435
1436// *shudder* , *horror*, *whoa* <-- what you might feel when you see the functions below
1437void QRenderRule::configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette::ColorRole br)
1438{
1439 if (bg && bg->brush.style() != Qt::NoBrush) {
1440 if (br != QPalette::NoRole)
1441 p->setBrush(acr: br, abrush: bg->brush);
1442 p->setBrush(acr: QPalette::Window, abrush: bg->brush);
1443 if (bg->brush.style() == Qt::SolidPattern) {
1444 p->setBrush(acr: QPalette::Light, abrush: bg->brush.color().lighter(f: 115));
1445 p->setBrush(acr: QPalette::Midlight, abrush: bg->brush.color().lighter(f: 107));
1446 p->setBrush(acr: QPalette::Dark, abrush: bg->brush.color().darker(f: 150));
1447 p->setBrush(acr: QPalette::Shadow, abrush: bg->brush.color().darker(f: 300));
1448 }
1449 }
1450
1451 if (!hasPalette())
1452 return;
1453
1454 if (pal->foreground.style() != Qt::NoBrush) {
1455 if (fr != QPalette::NoRole)
1456 p->setBrush(acr: fr, abrush: pal->foreground);
1457 p->setBrush(acr: QPalette::WindowText, abrush: pal->foreground);
1458 p->setBrush(acr: QPalette::Text, abrush: pal->foreground);
1459 }
1460 if (pal->selectionBackground.style() != Qt::NoBrush)
1461 p->setBrush(acr: QPalette::Highlight, abrush: pal->selectionBackground);
1462 if (pal->selectionForeground.style() != Qt::NoBrush)
1463 p->setBrush(acr: QPalette::HighlightedText, abrush: pal->selectionForeground);
1464 if (pal->alternateBackground.style() != Qt::NoBrush)
1465 p->setBrush(acr: QPalette::AlternateBase, abrush: pal->alternateBackground);
1466}
1467
1468void setDefault(QPalette *palette, QPalette::ColorGroup group, QPalette::ColorRole role,
1469 const QBrush &defaultBrush, const QWidget *widget)
1470{
1471 const QPalette &widgetPalette = widget->palette();
1472 if (widgetPalette.isBrushSet(cg: group, cr: role))
1473 palette->setBrush(cg: group, cr: role, brush: widgetPalette.brush(cg: group, cr: role));
1474 else
1475 palette->setBrush(cg: group, cr: role, brush: defaultBrush);
1476}
1477
1478void QRenderRule::configurePalette(QPalette *p, QPalette::ColorGroup cg, const QWidget *w, bool embedded)
1479{
1480 if (bg && bg->brush.style() != Qt::NoBrush) {
1481 p->setBrush(cg, cr: QPalette::Base, brush: bg->brush); // for windows, windowxp
1482 p->setBrush(cg, cr: QPalette::Button, brush: bg->brush); // for plastique
1483 p->setBrush(cg, cr: w->backgroundRole(), brush: bg->brush);
1484 p->setBrush(cg, cr: QPalette::Window, brush: bg->brush);
1485 }
1486
1487 if (embedded) {
1488 /* For embedded widgets (ComboBox, SpinBox and ScrollArea) we want the embedded widget
1489 * to be transparent when we have a transparent background or border image */
1490 if ((hasBackground() && background()->isTransparent())
1491 || (hasBorder() && border()->hasBorderImage() && !border()->borderImage()->pixmap.isNull()))
1492 p->setBrush(cg, cr: w->backgroundRole(), brush: Qt::NoBrush);
1493 }
1494
1495 if (!hasPalette())
1496 return;
1497
1498 if (pal->foreground.style() != Qt::NoBrush) {
1499 setDefault(palette: p, group: cg, role: QPalette::ButtonText, defaultBrush: pal->foreground, widget: w);
1500 setDefault(palette: p, group: cg, role: w->foregroundRole(), defaultBrush: pal->foreground, widget: w);
1501 setDefault(palette: p, group: cg, role: QPalette::WindowText, defaultBrush: pal->foreground, widget: w);
1502 setDefault(palette: p, group: cg, role: QPalette::Text, defaultBrush: pal->foreground, widget: w);
1503 QColor phColor(pal->foreground.color());
1504 phColor.setAlpha((phColor.alpha() + 1) / 2);
1505 QBrush placeholder = pal->foreground;
1506 placeholder.setColor(phColor);
1507 setDefault(palette: p, group: cg, role: QPalette::PlaceholderText, defaultBrush: placeholder, widget: w);
1508 }
1509 if (pal->selectionBackground.style() != Qt::NoBrush)
1510 p->setBrush(cg, cr: QPalette::Highlight, brush: pal->selectionBackground);
1511 if (pal->selectionForeground.style() != Qt::NoBrush)
1512 p->setBrush(cg, cr: QPalette::HighlightedText, brush: pal->selectionForeground);
1513 if (pal->alternateBackground.style() != Qt::NoBrush)
1514 p->setBrush(cg, cr: QPalette::AlternateBase, brush: pal->alternateBackground);
1515 if (pal->placeholderForeground.style() != Qt::NoBrush)
1516 p->setBrush(cg, cr: QPalette::PlaceholderText, brush: pal->placeholderForeground);
1517 if (pal->accent.style() != Qt::NoBrush)
1518 p->setBrush(cg, cr: QPalette::Accent, brush: pal->accent);
1519}
1520
1521bool QRenderRule::hasModification() const
1522{
1523 return hasPalette() ||
1524 hasBackground() ||
1525 hasGradientBackground() ||
1526 !hasNativeBorder() ||
1527 !hasNativeOutline() ||
1528 hasBox() ||
1529 hasPosition() ||
1530 hasGeometry() ||
1531 hasImage() ||
1532 hasFont ||
1533 !styleHints.isEmpty();
1534}
1535
1536///////////////////////////////////////////////////////////////////////////////
1537// Style rules
1538#define OBJECT_PTR(x) (static_cast<QObject *>(x.ptr))
1539
1540static inline QObject *parentObject(const QObject *obj)
1541{
1542#if QT_CONFIG(tooltip)
1543 if (qobject_cast<const QLabel *>(object: obj) && qstrcmp(str1: obj->metaObject()->className(), str2: "QTipLabel") == 0) {
1544 QObject *p = qvariant_cast<QObject *>(v: obj->property(name: "_q_stylesheet_parent"));
1545 if (p)
1546 return p;
1547 }
1548#endif
1549 return obj->parent();
1550}
1551
1552class QStyleSheetStyleSelector : public StyleSelector
1553{
1554public:
1555 QStyleSheetStyleSelector() { }
1556
1557 QStringList nodeNames(NodePtr node) const override
1558 {
1559 if (isNullNode(node))
1560 return QStringList();
1561 const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject();
1562#if QT_CONFIG(tooltip)
1563 if (qstrcmp(str1: metaObject->className(), str2: "QTipLabel") == 0)
1564 return QStringList("QToolTip"_L1);
1565#endif
1566 QStringList result;
1567 do {
1568 result += QString::fromLatin1(ba: metaObject->className()).replace(before: u':', after: u'-');
1569 metaObject = metaObject->superClass();
1570 } while (metaObject != nullptr);
1571 return result;
1572 }
1573 QString attributeValue(NodePtr node, const QCss::AttributeSelector& aSelector) const override
1574 {
1575 if (isNullNode(node))
1576 return QString();
1577
1578 const QString &name = aSelector.name;
1579 QHash<QString, QString> &cache = m_attributeCache[OBJECT_PTR(node)];
1580 QHash<QString, QString>::const_iterator cacheIt = cache.constFind(key: name);
1581 if (cacheIt != cache.constEnd())
1582 return cacheIt.value();
1583
1584 QVariant value;
1585 QString valueStr;
1586 QObject *obj = OBJECT_PTR(node);
1587 const int propertyIndex = obj->metaObject()->indexOfProperty(name: name.toLatin1());
1588 if (propertyIndex == -1) {
1589 value = obj->property(name: name.toLatin1()); // might be a dynamic property
1590 if (!value.isValid()) {
1591 if (name == "class"_L1) {
1592 QString className = QString::fromLatin1(ba: obj->metaObject()->className());
1593 if (className.contains(c: u':'))
1594 className.replace(before: u':', after: u'-');
1595 valueStr = className;
1596 } else if (name == "style"_L1) {
1597 QWidget *w = qobject_cast<QWidget *>(o: obj);
1598 QStyleSheetStyle *proxy = w ? qt_styleSheet(style: w->style()) : nullptr;
1599 if (proxy)
1600 valueStr = QString::fromLatin1(ba: proxy->baseStyle()->metaObject()->className());
1601 }
1602 }
1603 } else {
1604 const QMetaProperty property = obj->metaObject()->property(index: propertyIndex);
1605 value = property.read(obj);
1606 // support Qt 5 selector syntax, which required the integer enum value
1607 if (property.isEnumType()) {
1608 bool isNumber;
1609 aSelector.value.toInt(ok: &isNumber);
1610 if (isNumber)
1611 value.convert(type: QMetaType::fromType<int>());
1612 }
1613 }
1614 if (value.isValid()) {
1615 valueStr = (value.userType() == QMetaType::QStringList
1616 || value.userType() == QMetaType::QVariantList)
1617 ? value.toStringList().join(sep: u' ')
1618 : value.toString();
1619 }
1620 cache[name] = valueStr;
1621 return valueStr;
1622 }
1623 bool nodeNameEquals(NodePtr node, const QString& nodeName) const override
1624 {
1625 if (isNullNode(node))
1626 return false;
1627 const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject();
1628#if QT_CONFIG(tooltip)
1629 if (qstrcmp(str1: metaObject->className(), str2: "QTipLabel") == 0)
1630 return nodeName == "QToolTip"_L1;
1631#endif
1632 do {
1633 const auto *uc = reinterpret_cast<const char16_t *>(nodeName.constData());
1634 const auto *e = uc + nodeName.size();
1635 const uchar *c = (const uchar *)metaObject->className();
1636 while (*c && uc != e && (*uc == *c || (*c == ':' && *uc == '-'))) {
1637 ++uc;
1638 ++c;
1639 }
1640 if (uc == e && !*c)
1641 return true;
1642 metaObject = metaObject->superClass();
1643 } while (metaObject != nullptr);
1644 return false;
1645 }
1646 bool hasAttributes(NodePtr) const override
1647 { return true; }
1648 QStringList nodeIds(NodePtr node) const override
1649 { return isNullNode(node) ? QStringList() : QStringList(OBJECT_PTR(node)->objectName()); }
1650 bool isNullNode(NodePtr node) const override
1651 { return node.ptr == nullptr; }
1652 NodePtr parentNode(NodePtr node) const override
1653 { NodePtr n; n.ptr = isNullNode(node) ? nullptr : parentObject(OBJECT_PTR(node)); return n; }
1654 NodePtr previousSiblingNode(NodePtr) const override
1655 { NodePtr n; n.ptr = nullptr; return n; }
1656 NodePtr duplicateNode(NodePtr node) const override
1657 { return node; }
1658 void freeNode(NodePtr) const override
1659 { }
1660
1661private:
1662 mutable QHash<const QObject *, QHash<QString, QString> > m_attributeCache;
1663};
1664
1665QList<QCss::StyleRule> QStyleSheetStyle::styleRules(const QObject *obj) const
1666{
1667 QHash<const QObject *, QList<StyleRule>>::const_iterator cacheIt =
1668 styleSheetCaches->styleRulesCache.constFind(key: obj);
1669 if (cacheIt != styleSheetCaches->styleRulesCache.constEnd())
1670 return cacheIt.value();
1671
1672 if (!initObject(obj)) {
1673 return QList<StyleRule>();
1674 }
1675
1676 QStyleSheetStyleSelector styleSelector;
1677
1678 StyleSheet defaultSs;
1679 QHash<const void *, StyleSheet>::const_iterator defaultCacheIt = styleSheetCaches->styleSheetCache.constFind(key: baseStyle());
1680 if (defaultCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1681 defaultSs = getDefaultStyleSheet();
1682 QStyle *bs = baseStyle();
1683 styleSheetCaches->styleSheetCache.insert(key: bs, value: defaultSs);
1684 QObject::connect(sender: bs, SIGNAL(destroyed(QObject*)), receiver: styleSheetCaches, SLOT(styleDestroyed(QObject*)), Qt::UniqueConnection);
1685 } else {
1686 defaultSs = defaultCacheIt.value();
1687 }
1688 styleSelector.styleSheets += defaultSs;
1689
1690 if (!qApp->styleSheet().isEmpty()) {
1691 StyleSheet appSs;
1692 QHash<const void *, StyleSheet>::const_iterator appCacheIt = styleSheetCaches->styleSheetCache.constFind(qApp);
1693 if (appCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1694 QString ss = qApp->styleSheet();
1695 if (ss.startsWith(s: "file:///"_L1))
1696 ss.remove(i: 0, len: 8);
1697 parser.init(css: ss, qApp->styleSheet() != ss);
1698 if (Q_UNLIKELY(!parser.parse(&appSs)))
1699 qWarning(msg: "Could not parse application stylesheet");
1700 appSs.origin = StyleSheetOrigin_Inline;
1701 appSs.depth = 1;
1702 styleSheetCaches->styleSheetCache.insert(qApp, value: appSs);
1703 } else {
1704 appSs = appCacheIt.value();
1705 }
1706 styleSelector.styleSheets += appSs;
1707 }
1708
1709 QList<QCss::StyleSheet> objectSs;
1710 for (const QObject *o = obj; o; o = parentObject(obj: o)) {
1711 QString styleSheet = o->property(name: "styleSheet").toString();
1712 if (styleSheet.isEmpty())
1713 continue;
1714 StyleSheet ss;
1715 QHash<const void *, StyleSheet>::const_iterator objCacheIt = styleSheetCaches->styleSheetCache.constFind(key: o);
1716 if (objCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1717 parser.init(css: styleSheet);
1718 if (!parser.parse(styleSheet: &ss)) {
1719 parser.init(css: "* {"_L1 + styleSheet + u'}');
1720 if (Q_UNLIKELY(!parser.parse(&ss)))
1721 qWarning() << "Could not parse stylesheet of object" << o;
1722 }
1723 ss.origin = StyleSheetOrigin_Inline;
1724 styleSheetCaches->styleSheetCache.insert(key: o, value: ss);
1725 } else {
1726 ss = objCacheIt.value();
1727 }
1728 objectSs.append(t: ss);
1729 }
1730
1731 for (int i = 0; i < objectSs.size(); i++)
1732 objectSs[i].depth = objectSs.size() - i + 2;
1733
1734 styleSelector.styleSheets += objectSs;
1735
1736 StyleSelector::NodePtr n;
1737 n.ptr = const_cast<QObject *>(obj);
1738 QList<QCss::StyleRule> rules = styleSelector.styleRulesForNode(node: n);
1739 styleSheetCaches->styleRulesCache.insert(key: obj, value: rules);
1740 return rules;
1741}
1742
1743/////////////////////////////////////////////////////////////////////////////////////////
1744// Rendering rules
1745static QList<Declaration> declarations(const QList<StyleRule> &styleRules, const QString &part,
1746 quint64 pseudoClass = PseudoClass_Unspecified)
1747{
1748 QList<Declaration> decls;
1749 for (int i = 0; i < styleRules.size(); i++) {
1750 const Selector& selector = styleRules.at(i).selectors.at(i: 0);
1751 // Rules with pseudo elements don't cascade. This is an intentional
1752 // diversion for CSS
1753 if (part.compare(s: selector.pseudoElement(), cs: Qt::CaseInsensitive) != 0)
1754 continue;
1755 quint64 negated = 0;
1756 quint64 cssClass = selector.pseudoClass(negated: &negated);
1757 if ((pseudoClass == PseudoClass_Any) || (cssClass == PseudoClass_Unspecified)
1758 || ((((cssClass & pseudoClass) == cssClass)) && ((negated & pseudoClass) == 0)))
1759 decls += styleRules.at(i).declarations;
1760 }
1761 return decls;
1762}
1763
1764int QStyleSheetStyle::nativeFrameWidth(const QWidget *w)
1765{
1766 QStyle *base = baseStyle();
1767
1768#if QT_CONFIG(spinbox)
1769 if (qobject_cast<const QAbstractSpinBox *>(object: w))
1770 return base->pixelMetric(metric: QStyle::PM_SpinBoxFrameWidth, option: nullptr, widget: w);
1771#endif
1772
1773#if QT_CONFIG(combobox)
1774 if (qobject_cast<const QComboBox *>(object: w))
1775 return base->pixelMetric(metric: QStyle::PM_ComboBoxFrameWidth, option: nullptr, widget: w);
1776#endif
1777
1778#if QT_CONFIG(menu)
1779 if (qobject_cast<const QMenu *>(object: w))
1780 return base->pixelMetric(metric: QStyle::PM_MenuPanelWidth, option: nullptr, widget: w);
1781#endif
1782
1783#if QT_CONFIG(menubar)
1784 if (qobject_cast<const QMenuBar *>(object: w))
1785 return base->pixelMetric(metric: QStyle::PM_MenuBarPanelWidth, option: nullptr, widget: w);
1786#endif
1787#ifndef QT_NO_FRAME
1788 if (const QFrame *frame = qobject_cast<const QFrame *>(object: w)) {
1789 if (frame->frameShape() == QFrame::NoFrame)
1790 return 0;
1791 }
1792#endif
1793
1794 if (qstrcmp(str1: w->metaObject()->className(), str2: "QTipLabel") == 0)
1795 return base->pixelMetric(metric: QStyle::PM_ToolTipLabelFrameWidth, option: nullptr, widget: w);
1796
1797 return base->pixelMetric(metric: QStyle::PM_DefaultFrameWidth, option: nullptr, widget: w);
1798}
1799
1800static quint64 pseudoClass(QStyle::State state)
1801{
1802 quint64 pc = 0;
1803 if (state & QStyle::State_Enabled) {
1804 pc |= PseudoClass_Enabled;
1805 if (state & QStyle::State_MouseOver)
1806 pc |= PseudoClass_Hover;
1807 } else {
1808 pc |= PseudoClass_Disabled;
1809 }
1810 if (state & QStyle::State_Active)
1811 pc |= PseudoClass_Active;
1812 if (state & QStyle::State_Window)
1813 pc |= PseudoClass_Window;
1814 if (state & QStyle::State_Sunken)
1815 pc |= PseudoClass_Pressed;
1816 if (state & QStyle::State_HasFocus)
1817 pc |= PseudoClass_Focus;
1818 if (state & QStyle::State_On)
1819 pc |= (PseudoClass_On | PseudoClass_Checked);
1820 if (state & QStyle::State_Off)
1821 pc |= (PseudoClass_Off | PseudoClass_Unchecked);
1822 if (state & QStyle::State_NoChange)
1823 pc |= PseudoClass_Indeterminate;
1824 if (state & QStyle::State_Selected)
1825 pc |= PseudoClass_Selected;
1826 if (state & QStyle::State_Horizontal)
1827 pc |= PseudoClass_Horizontal;
1828 else
1829 pc |= PseudoClass_Vertical;
1830 if (state & (QStyle::State_Open | QStyle::State_On | QStyle::State_Sunken))
1831 pc |= PseudoClass_Open;
1832 else
1833 pc |= PseudoClass_Closed;
1834 if (state & QStyle::State_Children)
1835 pc |= PseudoClass_Children;
1836 if (state & QStyle::State_Sibling)
1837 pc |= PseudoClass_Sibling;
1838 if (state & QStyle::State_ReadOnly)
1839 pc |= PseudoClass_ReadOnly;
1840 if (state & QStyle::State_Item)
1841 pc |= PseudoClass_Item;
1842#ifdef QT_KEYPAD_NAVIGATION
1843 if (state & QStyle::State_HasEditFocus)
1844 pc |= PseudoClass_EditFocus;
1845#endif
1846 return pc;
1847}
1848
1849static void qt_check_if_internal_object(const QObject **obj, int *element)
1850{
1851#if !QT_CONFIG(dockwidget)
1852 Q_UNUSED(obj);
1853 Q_UNUSED(element);
1854#else
1855 if (*obj && qstrcmp(str1: (*obj)->metaObject()->className(), str2: "QDockWidgetTitleButton") == 0) {
1856 if ((*obj)->objectName() == "qt_dockwidget_closebutton"_L1) {
1857 *element = PseudoElement_DockWidgetCloseButton;
1858 } else if ((*obj)->objectName() == "qt_dockwidget_floatbutton"_L1) {
1859 *element = PseudoElement_DockWidgetFloatButton;
1860 }
1861 *obj = (*obj)->parent();
1862 }
1863#endif
1864}
1865
1866QRenderRule QStyleSheetStyle::renderRule(const QObject *obj, int element, quint64 state) const
1867{
1868 qt_check_if_internal_object(obj: &obj, element: &element);
1869 QHash<quint64, QRenderRule> &cache = styleSheetCaches->renderRulesCache[obj][element];
1870 QHash<quint64, QRenderRule>::const_iterator cacheIt = cache.constFind(key: state);
1871 if (cacheIt != cache.constEnd())
1872 return cacheIt.value();
1873
1874 if (!initObject(obj))
1875 return QRenderRule();
1876
1877 quint64 stateMask = 0;
1878 const QList<StyleRule> rules = styleRules(obj);
1879 for (int i = 0; i < rules.size(); i++) {
1880 const Selector& selector = rules.at(i).selectors.at(i: 0);
1881 quint64 negated = 0;
1882 stateMask |= selector.pseudoClass(negated: &negated);
1883 stateMask |= negated;
1884 }
1885
1886 cacheIt = cache.constFind(key: state & stateMask);
1887 if (cacheIt != cache.constEnd()) {
1888 QRenderRule newRule = cacheIt.value();
1889 cache[state] = newRule;
1890 return newRule;
1891 }
1892
1893
1894 const QString part = QLatin1StringView(knownPseudoElements[element].name);
1895 QList<Declaration> decls = declarations(styleRules: rules, part, pseudoClass: state);
1896 QRenderRule newRule(decls, obj);
1897 cache[state] = newRule;
1898 if ((state & stateMask) != state)
1899 cache[state&stateMask] = newRule;
1900 return newRule;
1901}
1902
1903QRenderRule QStyleSheetStyle::renderRule(const QObject *obj, const QStyleOption *opt, int pseudoElement) const
1904{
1905 quint64 extraClass = 0;
1906 QStyle::State state = opt ? opt->state : QStyle::State(QStyle::State_None);
1907
1908 if (const QStyleOptionComplex *complex = qstyleoption_cast<const QStyleOptionComplex *>(opt)) {
1909 if (pseudoElement != PseudoElement_None) {
1910 // if not an active subcontrol, just pass enabled/disabled
1911 QStyle::SubControl subControl = knownPseudoElements[pseudoElement].subControl;
1912
1913 if (!(complex->activeSubControls & subControl))
1914 state &= (QStyle::State_Enabled | QStyle::State_Horizontal | QStyle::State_HasFocus);
1915 }
1916
1917 switch (pseudoElement) {
1918 case PseudoElement_ComboBoxDropDown:
1919 case PseudoElement_ComboBoxArrow:
1920 state |= (complex->state & (QStyle::State_On|QStyle::State_ReadOnly));
1921 break;
1922 case PseudoElement_SpinBoxUpButton:
1923 case PseudoElement_SpinBoxDownButton:
1924 case PseudoElement_SpinBoxUpArrow:
1925 case PseudoElement_SpinBoxDownArrow:
1926#if QT_CONFIG(spinbox)
1927 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
1928 bool on = false;
1929 bool up = pseudoElement == PseudoElement_SpinBoxUpButton
1930 || pseudoElement == PseudoElement_SpinBoxUpArrow;
1931 if ((sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) && up)
1932 on = true;
1933 else if ((sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) && !up)
1934 on = true;
1935 state |= (on ? QStyle::State_On : QStyle::State_Off);
1936 }
1937#endif // QT_CONFIG(spinbox)
1938 break;
1939 case PseudoElement_GroupBoxTitle:
1940 state |= (complex->state & (QStyle::State_MouseOver | QStyle::State_Sunken));
1941 break;
1942 case PseudoElement_ToolButtonMenu:
1943 case PseudoElement_ToolButtonMenuArrow:
1944 case PseudoElement_ToolButtonMenuIndicator:
1945 state |= complex->state & QStyle::State_MouseOver;
1946 if (complex->state & QStyle::State_Sunken ||
1947 complex->activeSubControls & QStyle::SC_ToolButtonMenu)
1948 state |= QStyle::State_Sunken;
1949 break;
1950 case PseudoElement_SliderGroove:
1951 state |= complex->state & QStyle::State_MouseOver;
1952 break;
1953 default:
1954 break;
1955 }
1956
1957 if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
1958 // QStyle::State_On is set when the popup is being shown
1959 // Propagate EditField Pressed state
1960 if (pseudoElement == PseudoElement_None
1961 && (complex->activeSubControls & QStyle::SC_ComboBoxEditField)
1962 && (!(state & QStyle::State_MouseOver))) {
1963 state |= QStyle::State_Sunken;
1964 }
1965
1966 if (!combo->frame)
1967 extraClass |= PseudoClass_Frameless;
1968 if (!combo->editable)
1969 extraClass |= PseudoClass_ReadOnly;
1970 else
1971 extraClass |= PseudoClass_Editable;
1972#if QT_CONFIG(spinbox)
1973 } else if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
1974 if (!spin->frame)
1975 extraClass |= PseudoClass_Frameless;
1976#endif // QT_CONFIG(spinbox)
1977 } else if (const QStyleOptionGroupBox *gb = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
1978 if (gb->features & QStyleOptionFrame::Flat)
1979 extraClass |= PseudoClass_Flat;
1980 if (gb->lineWidth == 0)
1981 extraClass |= PseudoClass_Frameless;
1982 } else if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
1983 if (tb->titleBarState & Qt::WindowMinimized) {
1984 extraClass |= PseudoClass_Minimized;
1985 }
1986 else if (tb->titleBarState & Qt::WindowMaximized)
1987 extraClass |= PseudoClass_Maximized;
1988 }
1989 } else {
1990 // handle simple style options
1991 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
1992 if (mi->menuItemType == QStyleOptionMenuItem::DefaultItem)
1993 extraClass |= PseudoClass_Default;
1994 if (mi->checkType == QStyleOptionMenuItem::Exclusive)
1995 extraClass |= PseudoClass_Exclusive;
1996 else if (mi->checkType == QStyleOptionMenuItem::NonExclusive)
1997 extraClass |= PseudoClass_NonExclusive;
1998 if (mi->checkType != QStyleOptionMenuItem::NotCheckable)
1999 extraClass |= (mi->checked) ? (PseudoClass_On|PseudoClass_Checked)
2000 : (PseudoClass_Off|PseudoClass_Unchecked);
2001 } else if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
2002 if (hdr->position == QStyleOptionHeader::OnlyOneSection)
2003 extraClass |= PseudoClass_OnlyOne;
2004 else if (hdr->position == QStyleOptionHeader::Beginning)
2005 extraClass |= PseudoClass_First;
2006 else if (hdr->position == QStyleOptionHeader::End)
2007 extraClass |= PseudoClass_Last;
2008 else if (hdr->position == QStyleOptionHeader::Middle)
2009 extraClass |= PseudoClass_Middle;
2010
2011 if (hdr->selectedPosition == QStyleOptionHeader::NextAndPreviousAreSelected)
2012 extraClass |= (PseudoClass_NextSelected | PseudoClass_PreviousSelected);
2013 else if (hdr->selectedPosition == QStyleOptionHeader::NextIsSelected)
2014 extraClass |= PseudoClass_NextSelected;
2015 else if (hdr->selectedPosition == QStyleOptionHeader::PreviousIsSelected)
2016 extraClass |= PseudoClass_PreviousSelected;
2017#if QT_CONFIG(tabwidget)
2018 } else if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
2019 switch (tab->shape) {
2020 case QTabBar::RoundedNorth:
2021 case QTabBar::TriangularNorth:
2022 extraClass |= PseudoClass_Top;
2023 break;
2024 case QTabBar::RoundedSouth:
2025 case QTabBar::TriangularSouth:
2026 extraClass |= PseudoClass_Bottom;
2027 break;
2028 case QTabBar::RoundedEast:
2029 case QTabBar::TriangularEast:
2030 extraClass |= PseudoClass_Right;
2031 break;
2032 case QTabBar::RoundedWest:
2033 case QTabBar::TriangularWest:
2034 extraClass |= PseudoClass_Left;
2035 break;
2036 default:
2037 break;
2038 }
2039#endif
2040#if QT_CONFIG(tabbar)
2041 } else if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
2042 if (tab->position == QStyleOptionTab::OnlyOneTab)
2043 extraClass |= PseudoClass_OnlyOne;
2044 else if (tab->position == QStyleOptionTab::Beginning)
2045 extraClass |= PseudoClass_First;
2046 else if (tab->position == QStyleOptionTab::End)
2047 extraClass |= PseudoClass_Last;
2048 else if (tab->position == QStyleOptionTab::Middle)
2049 extraClass |= PseudoClass_Middle;
2050
2051 if (tab->selectedPosition == QStyleOptionTab::NextIsSelected)
2052 extraClass |= PseudoClass_NextSelected;
2053 else if (tab->selectedPosition == QStyleOptionTab::PreviousIsSelected)
2054 extraClass |= PseudoClass_PreviousSelected;
2055
2056 switch (tab->shape) {
2057 case QTabBar::RoundedNorth:
2058 case QTabBar::TriangularNorth:
2059 extraClass |= PseudoClass_Top;
2060 break;
2061 case QTabBar::RoundedSouth:
2062 case QTabBar::TriangularSouth:
2063 extraClass |= PseudoClass_Bottom;
2064 break;
2065 case QTabBar::RoundedEast:
2066 case QTabBar::TriangularEast:
2067 extraClass |= PseudoClass_Right;
2068 break;
2069 case QTabBar::RoundedWest:
2070 case QTabBar::TriangularWest:
2071 extraClass |= PseudoClass_Left;
2072 break;
2073 default:
2074 break;
2075 }
2076#endif // QT_CONFIG(tabbar)
2077 } else if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2078 if (btn->features & QStyleOptionButton::Flat)
2079 extraClass |= PseudoClass_Flat;
2080 if (btn->features & QStyleOptionButton::DefaultButton)
2081 extraClass |= PseudoClass_Default;
2082 } else if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
2083 if (frm->lineWidth == 0)
2084 extraClass |= PseudoClass_Frameless;
2085 if (frm->features & QStyleOptionFrame::Flat)
2086 extraClass |= PseudoClass_Flat;
2087 }
2088#if QT_CONFIG(toolbar)
2089 else if (const QStyleOptionToolBar *tb = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
2090 if (tb->toolBarArea == Qt::LeftToolBarArea)
2091 extraClass |= PseudoClass_Left;
2092 else if (tb->toolBarArea == Qt::RightToolBarArea)
2093 extraClass |= PseudoClass_Right;
2094 else if (tb->toolBarArea == Qt::TopToolBarArea)
2095 extraClass |= PseudoClass_Top;
2096 else if (tb->toolBarArea == Qt::BottomToolBarArea)
2097 extraClass |= PseudoClass_Bottom;
2098
2099 if (tb->positionWithinLine == QStyleOptionToolBar::Beginning)
2100 extraClass |= PseudoClass_First;
2101 else if (tb->positionWithinLine == QStyleOptionToolBar::Middle)
2102 extraClass |= PseudoClass_Middle;
2103 else if (tb->positionWithinLine == QStyleOptionToolBar::End)
2104 extraClass |= PseudoClass_Last;
2105 else if (tb->positionWithinLine == QStyleOptionToolBar::OnlyOne)
2106 extraClass |= PseudoClass_OnlyOne;
2107 }
2108#endif // QT_CONFIG(toolbar)
2109#if QT_CONFIG(toolbox)
2110 else if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) {
2111 if (tb->position == QStyleOptionToolBox::OnlyOneTab)
2112 extraClass |= PseudoClass_OnlyOne;
2113 else if (tb->position == QStyleOptionToolBox::Beginning)
2114 extraClass |= PseudoClass_First;
2115 else if (tb->position == QStyleOptionToolBox::End)
2116 extraClass |= PseudoClass_Last;
2117 else if (tb->position == QStyleOptionToolBox::Middle)
2118 extraClass |= PseudoClass_Middle;
2119
2120 if (tb->selectedPosition == QStyleOptionToolBox::NextIsSelected)
2121 extraClass |= PseudoClass_NextSelected;
2122 else if (tb->selectedPosition == QStyleOptionToolBox::PreviousIsSelected)
2123 extraClass |= PseudoClass_PreviousSelected;
2124 }
2125#endif // QT_CONFIG(toolbox)
2126#if QT_CONFIG(dockwidget)
2127 else if (const QStyleOptionDockWidget *dw = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
2128 if (dw->verticalTitleBar)
2129 extraClass |= PseudoClass_Vertical;
2130 else
2131 extraClass |= PseudoClass_Horizontal;
2132 if (dw->closable)
2133 extraClass |= PseudoClass_Closable;
2134 if (dw->floatable)
2135 extraClass |= PseudoClass_Floatable;
2136 if (dw->movable)
2137 extraClass |= PseudoClass_Movable;
2138 }
2139#endif // QT_CONFIG(dockwidget)
2140#if QT_CONFIG(itemviews)
2141 else if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
2142 if (vopt->features & QStyleOptionViewItem::Alternate)
2143 extraClass |= PseudoClass_Alternate;
2144 if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne)
2145 extraClass |= PseudoClass_OnlyOne;
2146 else if (vopt->viewItemPosition == QStyleOptionViewItem::Beginning)
2147 extraClass |= PseudoClass_First;
2148 else if (vopt->viewItemPosition == QStyleOptionViewItem::End)
2149 extraClass |= PseudoClass_Last;
2150 else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle)
2151 extraClass |= PseudoClass_Middle;
2152
2153 }
2154#endif
2155#if QT_CONFIG(textedit)
2156 else if (const QPlainTextEdit *edit = qobject_cast<const QPlainTextEdit *>(object: obj)) {
2157 extraClass |= (edit->isReadOnly() ? PseudoClass_ReadOnly : PseudoClass_Editable);
2158 }
2159 else if (const QTextEdit *edit = qobject_cast<const QTextEdit *>(object: obj)) {
2160 extraClass |= (edit->isReadOnly() ? PseudoClass_ReadOnly : PseudoClass_Editable);
2161 }
2162#endif
2163#if QT_CONFIG(lineedit)
2164 // LineEdit sets Sunken flag to indicate Sunken frame (argh)
2165 if (const QLineEdit *lineEdit = qobject_cast<const QLineEdit *>(object: obj)) {
2166 state &= ~QStyle::State_Sunken;
2167 if (lineEdit->hasFrame()) {
2168 extraClass &= ~PseudoClass_Frameless;
2169 } else {
2170 extraClass |= PseudoClass_Frameless;
2171 }
2172 } else
2173#endif
2174 if (const QFrame *frm = qobject_cast<const QFrame *>(object: obj)) {
2175 if (frm->lineWidth() == 0)
2176 extraClass |= PseudoClass_Frameless;
2177 }
2178 }
2179
2180 return renderRule(obj, element: pseudoElement, state: pseudoClass(state) | extraClass);
2181}
2182
2183bool QStyleSheetStyle::hasStyleRule(const QObject *obj, int part) const
2184{
2185 QHash<int, bool> &cache = styleSheetCaches->hasStyleRuleCache[obj];
2186 QHash<int, bool>::const_iterator cacheIt = cache.constFind(key: part);
2187 if (cacheIt != cache.constEnd())
2188 return cacheIt.value();
2189
2190 if (!initObject(obj))
2191 return false;
2192
2193 const QList<StyleRule> &rules = styleRules(obj);
2194 if (part == PseudoElement_None) {
2195 bool result = obj && !rules.isEmpty();
2196 cache[part] = result;
2197 return result;
2198 }
2199
2200 auto pseudoElement = QLatin1StringView(knownPseudoElements[part].name);
2201 for (int i = 0; i < rules.size(); i++) {
2202 const Selector& selector = rules.at(i).selectors.at(i: 0);
2203 if (pseudoElement.compare(other: selector.pseudoElement(), cs: Qt::CaseInsensitive) == 0) {
2204 cache[part] = true;
2205 return true;
2206 }
2207 }
2208
2209 cache[part] = false;
2210 return false;
2211}
2212
2213static Origin defaultOrigin(int pe)
2214{
2215 switch (pe) {
2216 case PseudoElement_ScrollBarAddPage:
2217 case PseudoElement_ScrollBarSubPage:
2218 case PseudoElement_ScrollBarAddLine:
2219 case PseudoElement_ScrollBarSubLine:
2220 case PseudoElement_ScrollBarFirst:
2221 case PseudoElement_ScrollBarLast:
2222 case PseudoElement_GroupBoxTitle:
2223 case PseudoElement_GroupBoxIndicator: // never used
2224 case PseudoElement_ToolButtonMenu:
2225 case PseudoElement_SliderAddPage:
2226 case PseudoElement_SliderSubPage:
2227 return Origin_Border;
2228
2229 case PseudoElement_SpinBoxUpButton:
2230 case PseudoElement_SpinBoxDownButton:
2231 case PseudoElement_PushButtonMenuIndicator:
2232 case PseudoElement_ComboBoxDropDown:
2233 case PseudoElement_ToolButtonMenuIndicator:
2234 case PseudoElement_MenuCheckMark:
2235 case PseudoElement_MenuIcon:
2236 case PseudoElement_MenuRightArrow:
2237 return Origin_Padding;
2238
2239 case PseudoElement_Indicator:
2240 case PseudoElement_ExclusiveIndicator:
2241 case PseudoElement_ComboBoxArrow:
2242 case PseudoElement_ScrollBarSlider:
2243 case PseudoElement_ScrollBarUpArrow:
2244 case PseudoElement_ScrollBarDownArrow:
2245 case PseudoElement_ScrollBarLeftArrow:
2246 case PseudoElement_ScrollBarRightArrow:
2247 case PseudoElement_SpinBoxUpArrow:
2248 case PseudoElement_SpinBoxDownArrow:
2249 case PseudoElement_ToolButtonMenuArrow:
2250 case PseudoElement_HeaderViewUpArrow:
2251 case PseudoElement_HeaderViewDownArrow:
2252 case PseudoElement_SliderGroove:
2253 case PseudoElement_SliderHandle:
2254 return Origin_Content;
2255
2256 default:
2257 return Origin_Margin;
2258 }
2259}
2260
2261static Qt::Alignment defaultPosition(int pe)
2262{
2263 switch (pe) {
2264 case PseudoElement_Indicator:
2265 case PseudoElement_ExclusiveIndicator:
2266 case PseudoElement_MenuCheckMark:
2267 case PseudoElement_MenuIcon:
2268 return Qt::AlignLeft | Qt::AlignVCenter;
2269
2270 case PseudoElement_ScrollBarAddLine:
2271 case PseudoElement_ScrollBarLast:
2272 case PseudoElement_SpinBoxDownButton:
2273 case PseudoElement_PushButtonMenuIndicator:
2274 case PseudoElement_ToolButtonMenuIndicator:
2275 return Qt::AlignRight | Qt::AlignBottom;
2276
2277 case PseudoElement_ScrollBarSubLine:
2278 case PseudoElement_ScrollBarFirst:
2279 case PseudoElement_SpinBoxUpButton:
2280 case PseudoElement_ComboBoxDropDown:
2281 case PseudoElement_ToolButtonMenu:
2282 case PseudoElement_DockWidgetCloseButton:
2283 case PseudoElement_DockWidgetFloatButton:
2284 return Qt::AlignRight | Qt::AlignTop;
2285
2286 case PseudoElement_ScrollBarUpArrow:
2287 case PseudoElement_ScrollBarDownArrow:
2288 case PseudoElement_ScrollBarLeftArrow:
2289 case PseudoElement_ScrollBarRightArrow:
2290 case PseudoElement_SpinBoxUpArrow:
2291 case PseudoElement_SpinBoxDownArrow:
2292 case PseudoElement_ComboBoxArrow:
2293 case PseudoElement_DownArrow:
2294 case PseudoElement_UpArrow:
2295 case PseudoElement_LeftArrow:
2296 case PseudoElement_RightArrow:
2297 case PseudoElement_ToolButtonMenuArrow:
2298 case PseudoElement_SliderGroove:
2299 return Qt::AlignCenter;
2300
2301 case PseudoElement_GroupBoxTitle:
2302 case PseudoElement_GroupBoxIndicator: // never used
2303 return Qt::AlignLeft | Qt::AlignTop;
2304
2305 case PseudoElement_HeaderViewUpArrow:
2306 case PseudoElement_HeaderViewDownArrow:
2307 case PseudoElement_MenuRightArrow:
2308 return Qt::AlignRight | Qt::AlignVCenter;
2309
2310 default:
2311 return { };
2312 }
2313}
2314
2315QSize QStyleSheetStyle::defaultSize(const QWidget *w, QSize sz, const QRect& rect, int pe) const
2316{
2317 QStyle *base = baseStyle();
2318
2319 switch (pe) {
2320 case PseudoElement_Indicator:
2321 case PseudoElement_MenuCheckMark:
2322 if (sz.width() == -1)
2323 sz.setWidth(base->pixelMetric(metric: PM_IndicatorWidth, option: nullptr, widget: w));
2324 if (sz.height() == -1)
2325 sz.setHeight(base->pixelMetric(metric: PM_IndicatorHeight, option: nullptr, widget: w));
2326 break;
2327
2328 case PseudoElement_ExclusiveIndicator:
2329 case PseudoElement_GroupBoxIndicator:
2330 if (sz.width() == -1)
2331 sz.setWidth(base->pixelMetric(metric: PM_ExclusiveIndicatorWidth, option: nullptr, widget: w));
2332 if (sz.height() == -1)
2333 sz.setHeight(base->pixelMetric(metric: PM_ExclusiveIndicatorHeight, option: nullptr, widget: w));
2334 break;
2335
2336 case PseudoElement_PushButtonMenuIndicator: {
2337 int pm = base->pixelMetric(metric: PM_MenuButtonIndicator, option: nullptr, widget: w);
2338 if (sz.width() == -1)
2339 sz.setWidth(pm);
2340 if (sz.height() == -1)
2341 sz.setHeight(pm);
2342 }
2343 break;
2344
2345 case PseudoElement_ComboBoxDropDown:
2346 if (sz.width() == -1)
2347 sz.setWidth(16);
2348 break;
2349
2350 case PseudoElement_ComboBoxArrow:
2351 case PseudoElement_DownArrow:
2352 case PseudoElement_UpArrow:
2353 case PseudoElement_LeftArrow:
2354 case PseudoElement_RightArrow:
2355 case PseudoElement_ToolButtonMenuArrow:
2356 case PseudoElement_ToolButtonMenuIndicator:
2357 case PseudoElement_MenuRightArrow:
2358 if (sz.width() == -1)
2359 sz.setWidth(13);
2360 if (sz.height() == -1)
2361 sz.setHeight(13);
2362 break;
2363
2364 case PseudoElement_SpinBoxUpButton:
2365 case PseudoElement_SpinBoxDownButton:
2366 if (sz.width() == -1)
2367 sz.setWidth(16);
2368 if (sz.height() == -1)
2369 sz.setHeight(rect.height()/2);
2370 break;
2371
2372 case PseudoElement_ToolButtonMenu:
2373 if (sz.width() == -1)
2374 sz.setWidth(base->pixelMetric(metric: PM_MenuButtonIndicator, option: nullptr, widget: w));
2375 break;
2376
2377 case PseudoElement_HeaderViewUpArrow:
2378 case PseudoElement_HeaderViewDownArrow: {
2379 int pm = base->pixelMetric(metric: PM_HeaderMargin, option: nullptr, widget: w);
2380 if (sz.width() == -1)
2381 sz.setWidth(pm);
2382 if (sz.height() == 1)
2383 sz.setHeight(pm);
2384 break;
2385 }
2386
2387 case PseudoElement_ScrollBarFirst:
2388 case PseudoElement_ScrollBarLast:
2389 case PseudoElement_ScrollBarAddLine:
2390 case PseudoElement_ScrollBarSubLine:
2391 case PseudoElement_ScrollBarSlider: {
2392 int pm = pixelMetric(metric: QStyle::PM_ScrollBarExtent, option: nullptr, widget: w);
2393 if (sz.width() == -1)
2394 sz.setWidth(pm);
2395 if (sz.height() == -1)
2396 sz.setHeight(pm);
2397 break;
2398 }
2399
2400 case PseudoElement_DockWidgetCloseButton:
2401 case PseudoElement_DockWidgetFloatButton: {
2402 int iconSize = pixelMetric(metric: PM_SmallIconSize, option: nullptr, widget: w);
2403 return QSize(iconSize, iconSize);
2404 }
2405
2406 default:
2407 break;
2408 }
2409
2410 // expand to rectangle
2411 if (sz.height() == -1)
2412 sz.setHeight(rect.height());
2413 if (sz.width() == -1)
2414 sz.setWidth(rect.width());
2415
2416 return sz;
2417}
2418
2419static PositionMode defaultPositionMode(int pe)
2420{
2421 switch (pe) {
2422 case PseudoElement_ScrollBarFirst:
2423 case PseudoElement_ScrollBarLast:
2424 case PseudoElement_ScrollBarAddLine:
2425 case PseudoElement_ScrollBarSubLine:
2426 case PseudoElement_ScrollBarAddPage:
2427 case PseudoElement_ScrollBarSubPage:
2428 case PseudoElement_ScrollBarSlider:
2429 case PseudoElement_SliderGroove:
2430 case PseudoElement_SliderHandle:
2431 case PseudoElement_TabWidgetPane:
2432 return PositionMode_Absolute;
2433 default:
2434 return PositionMode_Static;
2435 }
2436}
2437
2438QRect QStyleSheetStyle::positionRect(const QWidget *w, const QRenderRule &rule2, int pe,
2439 const QRect &originRect, Qt::LayoutDirection dir) const
2440{
2441 const QStyleSheetPositionData *p = rule2.position();
2442 PositionMode mode = (p && p->mode != PositionMode_Unknown) ? p->mode : defaultPositionMode(pe);
2443 Qt::Alignment position = (p && p->position != 0) ? p->position : defaultPosition(pe);
2444 QRect r;
2445
2446 if (mode != PositionMode_Absolute) {
2447 QSize sz = defaultSize(w, sz: rule2.size(), rect: originRect, pe);
2448 sz = sz.expandedTo(otherSize: rule2.minimumContentsSize());
2449 r = QStyle::alignedRect(direction: dir, alignment: position, size: sz, rectangle: originRect);
2450 if (p) {
2451 int left = p->left ? p->left : -p->right;
2452 int top = p->top ? p->top : -p->bottom;
2453 r.translate(dx: dir == Qt::LeftToRight ? left : -left, dy: top);
2454 }
2455 } else {
2456 r = p ? originRect.adjusted(xp1: dir == Qt::LeftToRight ? p->left : p->right, yp1: p->top,
2457 xp2: dir == Qt::LeftToRight ? -p->right : -p->left, yp2: -p->bottom)
2458 : originRect;
2459 if (rule2.hasContentsSize()) {
2460 QSize sz = rule2.size().expandedTo(otherSize: rule2.minimumContentsSize());
2461 if (sz.width() == -1) sz.setWidth(r.width());
2462 if (sz.height() == -1) sz.setHeight(r.height());
2463 r = QStyle::alignedRect(direction: dir, alignment: position, size: sz, rectangle: r);
2464 }
2465 }
2466 return r;
2467}
2468
2469QRect QStyleSheetStyle::positionRect(const QWidget *w, const QRenderRule& rule1, const QRenderRule& rule2, int pe,
2470 const QRect& rect, Qt::LayoutDirection dir) const
2471{
2472 const QStyleSheetPositionData *p = rule2.position();
2473 Origin origin = (p && p->origin != Origin_Unknown) ? p->origin : defaultOrigin(pe);
2474 QRect originRect = rule1.originRect(rect, origin);
2475 return positionRect(w, rule2, pe, originRect, dir);
2476}
2477
2478
2479/** \internal
2480 For widget that have an embedded widget (such as combobox) return that embedded widget.
2481 otherwise return the widget itself
2482 */
2483static QWidget *embeddedWidget(QWidget *w)
2484{
2485#if QT_CONFIG(combobox)
2486 if (QComboBox *cmb = qobject_cast<QComboBox *>(object: w)) {
2487 if (cmb->isEditable())
2488 return cmb->lineEdit();
2489 else
2490 return cmb;
2491 }
2492#endif
2493
2494#if QT_CONFIG(spinbox)
2495 if (QAbstractSpinBox *sb = qobject_cast<QAbstractSpinBox *>(object: w))
2496 return sb->findChild<QLineEdit *>();
2497#endif
2498
2499#if QT_CONFIG(scrollarea)
2500 if (QAbstractScrollArea *sa = qobject_cast<QAbstractScrollArea *>(object: w))
2501 return sa->viewport();
2502#endif
2503
2504 return w;
2505}
2506
2507/** \internal
2508 Returns the widget whose style rules apply to \a w.
2509
2510 When \a w is an embedded widget, this is the container widget.
2511 For example, if w is a line edit embedded in a combobox, this returns the combobox.
2512 When \a w is not embedded, this function return \a w itself.
2513
2514*/
2515static QWidget *containerWidget(const QWidget *w)
2516{
2517#if QT_CONFIG(lineedit)
2518 if (qobject_cast<const QLineEdit *>(object: w)) {
2519 //if the QLineEdit is an embeddedWidget, we need the rule of the real widget
2520#if QT_CONFIG(combobox)
2521 if (qobject_cast<const QComboBox *>(object: w->parentWidget()))
2522 return w->parentWidget();
2523#endif
2524#if QT_CONFIG(spinbox)
2525 if (qobject_cast<const QAbstractSpinBox *>(object: w->parentWidget()))
2526 return w->parentWidget();
2527#endif
2528 }
2529#endif // QT_CONFIG(lineedit)
2530
2531#if QT_CONFIG(scrollarea)
2532 if (const QAbstractScrollArea *sa = qobject_cast<const QAbstractScrollArea *>(object: w->parentWidget())) {
2533 if (sa->viewport() == w)
2534 return w->parentWidget();
2535 }
2536#endif
2537
2538 return const_cast<QWidget *>(w);
2539}
2540
2541/** \internal
2542 returns \c true if the widget can NOT be styled directly
2543 */
2544static bool unstylable(const QWidget *w)
2545{
2546 if (w->windowType() == Qt::Desktop)
2547 return true;
2548
2549 if (!w->styleSheet().isEmpty())
2550 return false;
2551
2552 if (containerWidget(w) != w)
2553 return true;
2554
2555#ifndef QT_NO_FRAME
2556 // detect QComboBoxPrivateContainer
2557 else if (qobject_cast<const QFrame *>(object: w)) {
2558 if (0
2559#if QT_CONFIG(combobox)
2560 || qobject_cast<const QComboBox *>(object: w->parentWidget())
2561#endif
2562 )
2563 return true;
2564 }
2565#endif
2566
2567#if QT_CONFIG(tabbar)
2568 if (w->metaObject() == &QWidget::staticMetaObject
2569 && qobject_cast<const QTabBar*>(object: w->parentWidget()))
2570 return true; // The moving tab of a QTabBar
2571#endif
2572
2573 return false;
2574}
2575
2576static quint64 extendedPseudoClass(const QWidget *w)
2577{
2578 quint64 pc = w->isWindow() ? quint64(PseudoClass_Window) : 0;
2579#if QT_CONFIG(abstractslider)
2580 if (const QAbstractSlider *slider = qobject_cast<const QAbstractSlider *>(object: w)) {
2581 pc |= ((slider->orientation() == Qt::Vertical) ? PseudoClass_Vertical : PseudoClass_Horizontal);
2582 } else
2583#endif
2584#if QT_CONFIG(combobox)
2585 if (const QComboBox *combo = qobject_cast<const QComboBox *>(object: w)) {
2586 if (combo->isEditable())
2587 pc |= (combo->isEditable() ? PseudoClass_Editable : PseudoClass_ReadOnly);
2588 } else
2589#endif
2590#if QT_CONFIG(lineedit)
2591 if (const QLineEdit *edit = qobject_cast<const QLineEdit *>(object: w)) {
2592 pc |= (edit->isReadOnly() ? PseudoClass_ReadOnly : PseudoClass_Editable);
2593 } else
2594#endif
2595#if QT_CONFIG(textedit)
2596 if (const QTextEdit *edit = qobject_cast<const QTextEdit *>(object: w)) {
2597 pc |= (edit->isReadOnly() ? PseudoClass_ReadOnly : PseudoClass_Editable);
2598 } else
2599 if (const QPlainTextEdit *edit = qobject_cast<const QPlainTextEdit *>(object: w)) {
2600 pc |= (edit->isReadOnly() ? PseudoClass_ReadOnly : PseudoClass_Editable);
2601 } else
2602#endif
2603 {}
2604 return pc;
2605}
2606
2607// sets up the geometry of the widget. We set a dynamic property when
2608// we modify the min/max size of the widget. The min/max size is restored
2609// to their original value when a new stylesheet that does not contain
2610// the CSS properties is set and when the widget has this dynamic property set.
2611// This way we don't trample on users who had setup a min/max size in code and
2612// don't use stylesheets at all.
2613void QStyleSheetStyle::setGeometry(QWidget *w)
2614{
2615 QRenderRule rule = renderRule(obj: w, element: PseudoElement_None, state: PseudoClass_Enabled | extendedPseudoClass(w));
2616 const QStyleSheetGeometryData *geo = rule.geometry();
2617 if (w->property(name: "_q_stylesheet_minw").toBool()
2618 && ((!rule.hasGeometry() || geo->minWidth == -1))) {
2619 w->setMinimumWidth(0);
2620 w->setProperty(name: "_q_stylesheet_minw", value: QVariant());
2621 }
2622 if (w->property(name: "_q_stylesheet_minh").toBool()
2623 && ((!rule.hasGeometry() || geo->minHeight == -1))) {
2624 w->setMinimumHeight(0);
2625 w->setProperty(name: "_q_stylesheet_minh", value: QVariant());
2626 }
2627 if (w->property(name: "_q_stylesheet_maxw").toBool()
2628 && ((!rule.hasGeometry() || geo->maxWidth == -1))) {
2629 w->setMaximumWidth(QWIDGETSIZE_MAX);
2630 w->setProperty(name: "_q_stylesheet_maxw", value: QVariant());
2631 }
2632 if (w->property(name: "_q_stylesheet_maxh").toBool()
2633 && ((!rule.hasGeometry() || geo->maxHeight == -1))) {
2634 w->setMaximumHeight(QWIDGETSIZE_MAX);
2635 w->setProperty(name: "_q_stylesheet_maxh", value: QVariant());
2636 }
2637
2638
2639 if (rule.hasGeometry()) {
2640 if (geo->minWidth != -1) {
2641 w->setProperty(name: "_q_stylesheet_minw", value: true);
2642 w->setMinimumWidth(rule.boxSize(cs: QSize(qMax(a: geo->width, b: geo->minWidth), 0)).width());
2643 }
2644 if (geo->minHeight != -1) {
2645 w->setProperty(name: "_q_stylesheet_minh", value: true);
2646 w->setMinimumHeight(rule.boxSize(cs: QSize(0, qMax(a: geo->height, b: geo->minHeight))).height());
2647 }
2648 if (geo->maxWidth != -1) {
2649 w->setProperty(name: "_q_stylesheet_maxw", value: true);
2650 w->setMaximumWidth(rule.boxSize(cs: QSize(qMin(a: geo->width == -1 ? QWIDGETSIZE_MAX : geo->width,
2651 b: geo->maxWidth == -1 ? QWIDGETSIZE_MAX : geo->maxWidth), 0)).width());
2652 }
2653 if (geo->maxHeight != -1) {
2654 w->setProperty(name: "_q_stylesheet_maxh", value: true);
2655 w->setMaximumHeight(rule.boxSize(cs: QSize(0, qMin(a: geo->height == -1 ? QWIDGETSIZE_MAX : geo->height,
2656 b: geo->maxHeight == -1 ? QWIDGETSIZE_MAX : geo->maxHeight))).height());
2657 }
2658 }
2659}
2660
2661void QStyleSheetStyle::setProperties(QWidget *w)
2662{
2663 // The final occurrence of each property is authoritative.
2664 // Set value for each property in the order of property final occurrence
2665 // since properties interact.
2666
2667 const QList<Declaration> decls = declarations(styleRules: styleRules(obj: w), part: QString());
2668 QList<int> finals; // indices in reverse order of each property's final occurrence
2669
2670 {
2671 // scan decls for final occurrence of each "qproperty"
2672 QDuplicateTracker<QString> propertySet(decls.size());
2673 for (int i = decls.size() - 1; i >= 0; --i) {
2674 const QString property = decls.at(i).d->property;
2675 if (!property.startsWith(s: "qproperty-"_L1, cs: Qt::CaseInsensitive))
2676 continue;
2677 if (!propertySet.hasSeen(s: property))
2678 finals.append(t: i);
2679 }
2680 }
2681
2682 for (int i = finals.size() - 1; i >= 0; --i) {
2683 const Declaration &decl = decls.at(i: finals[i]);
2684 QStringView property = decl.d->property;
2685 property = property.mid(pos: 10); // strip "qproperty-"
2686 const auto propertyL1 = property.toLatin1();
2687
2688 const QMetaObject *metaObject = w->metaObject();
2689 int index = metaObject->indexOfProperty(name: propertyL1);
2690 if (Q_UNLIKELY(index == -1)) {
2691 qWarning() << w << " does not have a property named " << property;
2692 continue;
2693 }
2694 const QMetaProperty metaProperty = metaObject->property(index);
2695 if (Q_UNLIKELY(!metaProperty.isWritable() || !metaProperty.isDesignable())) {
2696 qWarning() << w << " cannot design property named " << property;
2697 continue;
2698 }
2699
2700 QVariant v;
2701 const QVariant value = w->property(name: propertyL1);
2702 switch (value.userType()) {
2703 case QMetaType::QIcon: v = decl.iconValue(); break;
2704 case QMetaType::QImage: v = QImage(decl.uriValue()); break;
2705 case QMetaType::QPixmap: v = QPixmap(decl.uriValue()); break;
2706 case QMetaType::QRect: v = decl.rectValue(); break;
2707 case QMetaType::QSize: v = decl.sizeValue(); break;
2708 case QMetaType::QColor: v = decl.colorValue(); break;
2709 case QMetaType::QBrush: v = decl.brushValue(); break;
2710#ifndef QT_NO_SHORTCUT
2711 case QMetaType::QKeySequence: v = QKeySequence(decl.d->values.at(i: 0).variant.toString()); break;
2712#endif
2713 default: v = decl.d->values.at(i: 0).variant; break;
2714 }
2715
2716 if (propertyL1 == QByteArrayView("styleSheet") && value == v)
2717 continue;
2718
2719 w->setProperty(name: propertyL1, value: v);
2720 }
2721}
2722
2723void QStyleSheetStyle::setPalette(QWidget *w)
2724{
2725 struct RuleRoleMap {
2726 int state;
2727 QPalette::ColorGroup group;
2728 } map[3] = {
2729 { .state: int(PseudoClass_Active | PseudoClass_Enabled), .group: QPalette::Active },
2730 { .state: PseudoClass_Disabled, .group: QPalette::Disabled },
2731 { .state: PseudoClass_Enabled, .group: QPalette::Inactive }
2732 };
2733
2734 const bool useStyleSheetPropagationInWidgetStyles =
2735 QCoreApplication::testAttribute(attribute: Qt::AA_UseStyleSheetPropagationInWidgetStyles);
2736
2737 QPalette p;
2738 if (!useStyleSheetPropagationInWidgetStyles)
2739 p = w->palette();
2740
2741 QWidget *ew = embeddedWidget(w);
2742
2743 for (int i = 0; i < 3; i++) {
2744 QRenderRule rule = renderRule(obj: w, element: PseudoElement_None, state: map[i].state | extendedPseudoClass(w));
2745 if (i == 0) {
2746 if (!w->property(name: "_q_styleSheetWidgetFont").isValid()) {
2747 saveWidgetFont(w, font: w->d_func()->localFont());
2748 }
2749 updateStyleSheetFont(w);
2750 if (ew != w)
2751 updateStyleSheetFont(w: ew);
2752 }
2753
2754 rule.configurePalette(p: &p, cg: map[i].group, w: ew, embedded: ew != w);
2755 }
2756
2757 if (!useStyleSheetPropagationInWidgetStyles || p.resolveMask() != 0) {
2758 QPalette wp = w->palette();
2759 styleSheetCaches->customPaletteWidgets.insert(key: w, value: {.oldWidgetValue: wp, .resolveMask: p.resolveMask()});
2760
2761 if (useStyleSheetPropagationInWidgetStyles) {
2762 p = p.resolve(other: wp);
2763 p.setResolveMask(p.resolveMask() | wp.resolveMask());
2764 }
2765
2766 w->setPalette(p);
2767 if (ew != w)
2768 ew->setPalette(p);
2769 }
2770}
2771
2772void QStyleSheetStyle::unsetPalette(QWidget *w)
2773{
2774 const bool useStyleSheetPropagationInWidgetStyles =
2775 QCoreApplication::testAttribute(attribute: Qt::AA_UseStyleSheetPropagationInWidgetStyles);
2776
2777 const auto it = styleSheetCaches->customPaletteWidgets.find(key: w);
2778 if (it != styleSheetCaches->customPaletteWidgets.end()) {
2779 auto customizedPalette = std::move(*it);
2780 styleSheetCaches->customPaletteWidgets.erase(it);
2781
2782 QPalette original;
2783 if (useStyleSheetPropagationInWidgetStyles)
2784 original = std::move(customizedPalette).reverted(current: w->palette());
2785 else
2786 original = customizedPalette.oldWidgetValue;
2787
2788 w->setPalette(original);
2789 QWidget *ew = embeddedWidget(w);
2790 if (ew != w)
2791 ew->setPalette(original);
2792 }
2793
2794 if (useStyleSheetPropagationInWidgetStyles) {
2795 unsetStyleSheetFont(w);
2796 QWidget *ew = embeddedWidget(w);
2797 if (ew != w)
2798 unsetStyleSheetFont(ew);
2799 } else {
2800 QVariant oldFont = w->property(name: "_q_styleSheetWidgetFont");
2801 if (oldFont.isValid()) {
2802 w->setFont(qvariant_cast<QFont>(v: oldFont));
2803 }
2804 }
2805
2806 if (styleSheetCaches->autoFillDisabledWidgets.contains(value: w)) {
2807 embeddedWidget(w)->setAutoFillBackground(true);
2808 styleSheetCaches->autoFillDisabledWidgets.remove(value: w);
2809 }
2810}
2811
2812void QStyleSheetStyle::unsetStyleSheetFont(QWidget *w) const
2813{
2814 const auto it = styleSheetCaches->customFontWidgets.find(key: w);
2815 if (it != styleSheetCaches->customFontWidgets.end()) {
2816 auto customizedFont = std::move(*it);
2817 styleSheetCaches->customFontWidgets.erase(it);
2818 w->setFont(std::move(customizedFont).reverted(current: w->font()));
2819 }
2820}
2821
2822static void updateObjects(const QList<const QObject *>& objects)
2823{
2824 if (!styleSheetCaches->styleRulesCache.isEmpty() || !styleSheetCaches->hasStyleRuleCache.isEmpty() || !styleSheetCaches->renderRulesCache.isEmpty()) {
2825 for (const QObject *object : objects) {
2826 styleSheetCaches->styleRulesCache.remove(key: object);
2827 styleSheetCaches->hasStyleRuleCache.remove(key: object);
2828 styleSheetCaches->renderRulesCache.remove(key: object);
2829 }
2830 }
2831
2832 QEvent event(QEvent::StyleChange);
2833 for (const QObject *object : objects) {
2834 if (auto widget = qobject_cast<QWidget*>(o: const_cast<QObject*>(object))) {
2835 widget->style()->polish(widget);
2836 QCoreApplication::sendEvent(receiver: widget, event: &event);
2837 QList<const QObject *> children;
2838 children.reserve(size: widget->children().size() + 1);
2839 for (auto child: std::as_const(t: widget->children()))
2840 children.append(t: child);
2841 updateObjects(objects: children);
2842 }
2843 }
2844}
2845
2846/////////////////////////////////////////////////////////////////////////////////////////
2847// The stylesheet style
2848int QStyleSheetStyle::numinstances = 0;
2849
2850QStyleSheetStyle::QStyleSheetStyle(QStyle *base)
2851 : QWindowsStyle(*new QStyleSheetStylePrivate), base(base), refcount(1)
2852{
2853 ++numinstances;
2854 if (numinstances == 1) {
2855 styleSheetCaches = new QStyleSheetStyleCaches;
2856 }
2857}
2858
2859QStyleSheetStyle::~QStyleSheetStyle()
2860{
2861 --numinstances;
2862 if (numinstances == 0) {
2863 delete styleSheetCaches;
2864 }
2865}
2866QStyle *QStyleSheetStyle::baseStyle() const
2867{
2868 if (base)
2869 return base;
2870 if (QStyleSheetStyle *me = qt_styleSheet(style: QApplication::style()))
2871 return me->base;
2872 return QApplication::style();
2873}
2874
2875void QStyleSheetStyleCaches::objectDestroyed(QObject *o)
2876{
2877 styleRulesCache.remove(key: o);
2878 hasStyleRuleCache.remove(key: o);
2879 renderRulesCache.remove(key: o);
2880 customPaletteWidgets.remove(key: (const QWidget *)o);
2881 customFontWidgets.remove(key: static_cast<QWidget *>(o));
2882 styleSheetCache.remove(key: o);
2883 autoFillDisabledWidgets.remove(value: (const QWidget *)o);
2884}
2885
2886void QStyleSheetStyleCaches::styleDestroyed(QObject *o)
2887{
2888 styleSheetCache.remove(key: o);
2889}
2890
2891/*!
2892 * Make sure that the cache will be clean by connecting destroyed if needed.
2893 * return false if the widget is not stylable;
2894 */
2895bool QStyleSheetStyle::initObject(const QObject *obj) const
2896{
2897 if (!obj)
2898 return false;
2899 if (const QWidget *w = qobject_cast<const QWidget*>(o: obj)) {
2900 if (w->testAttribute(attribute: Qt::WA_StyleSheet))
2901 return true;
2902 if (unstylable(w))
2903 return false;
2904 const_cast<QWidget *>(w)->setAttribute(Qt::WA_StyleSheet, on: true);
2905 }
2906
2907 QObject::connect(sender: obj, SIGNAL(destroyed(QObject*)), receiver: styleSheetCaches, SLOT(objectDestroyed(QObject*)), Qt::UniqueConnection);
2908 return true;
2909}
2910
2911void QStyleSheetStyle::polish(QWidget *w)
2912{
2913 baseStyle()->polish(widget: w);
2914 RECURSION_GUARD(return)
2915
2916 if (!initObject(obj: w))
2917 return;
2918
2919 if (styleSheetCaches->styleRulesCache.contains(key: w)) {
2920 // the widget accessed its style pointer before polish (or repolish)
2921 // (example: the QAbstractSpinBox constructor ask for the stylehint)
2922 styleSheetCaches->styleRulesCache.remove(key: w);
2923 styleSheetCaches->hasStyleRuleCache.remove(key: w);
2924 styleSheetCaches->renderRulesCache.remove(key: w);
2925 styleSheetCaches->styleSheetCache.remove(key: w);
2926 }
2927 setGeometry(w);
2928 setProperties(w);
2929 unsetPalette(w);
2930 setPalette(w);
2931
2932 //set the WA_Hover attribute if one of the selector depends of the hover state
2933 QList<StyleRule> rules = styleRules(obj: w);
2934 for (int i = 0; i < rules.size(); i++) {
2935 const Selector& selector = rules.at(i).selectors.at(i: 0);
2936 quint64 negated = 0;
2937 quint64 cssClass = selector.pseudoClass(negated: &negated);
2938 if ( cssClass & PseudoClass_Hover || negated & PseudoClass_Hover) {
2939 w->setAttribute(Qt::WA_Hover);
2940 embeddedWidget(w)->setAttribute(Qt::WA_Hover);
2941 embeddedWidget(w)->setMouseTracking(true);
2942 }
2943 }
2944
2945
2946#if QT_CONFIG(scrollarea)
2947 if (QAbstractScrollArea *sa = qobject_cast<QAbstractScrollArea *>(object: w)) {
2948 QRenderRule rule = renderRule(obj: sa, element: PseudoElement_None, state: PseudoClass_Enabled);
2949 if ((rule.hasBorder() && rule.border()->hasBorderImage())
2950 || (rule.hasBackground() && !rule.background()->pixmap.isNull())) {
2951 QObject::connect(sender: sa->horizontalScrollBar(), SIGNAL(valueChanged(int)),
2952 receiver: sa, SLOT(update()), Qt::UniqueConnection);
2953 QObject::connect(sender: sa->verticalScrollBar(), SIGNAL(valueChanged(int)),
2954 receiver: sa, SLOT(update()), Qt::UniqueConnection);
2955 }
2956 }
2957#endif
2958
2959 QRenderRule rule = renderRule(obj: w, element: PseudoElement_None, state: PseudoClass_Any);
2960
2961 w->setAttribute(Qt::WA_StyleSheetTarget, on: rule.hasModification());
2962
2963 if (rule.hasDrawable() || rule.hasBox()) {
2964 if (w->metaObject() == &QWidget::staticMetaObject
2965#if QT_CONFIG(itemviews)
2966 || qobject_cast<QHeaderView *>(object: w)
2967#endif
2968#if QT_CONFIG(tabbar)
2969 || qobject_cast<QTabBar *>(object: w)
2970#endif
2971#ifndef QT_NO_FRAME
2972 || qobject_cast<QFrame *>(object: w)
2973#endif
2974#if QT_CONFIG(mainwindow)
2975 || qobject_cast<QMainWindow *>(object: w)
2976#endif
2977#if QT_CONFIG(mdiarea)
2978 || qobject_cast<QMdiSubWindow *>(object: w)
2979#endif
2980#if QT_CONFIG(menubar)
2981 || qobject_cast<QMenuBar *>(object: w)
2982#endif
2983#if QT_CONFIG(dialog)
2984 || qobject_cast<QDialog *>(object: w)
2985#endif
2986 ) {
2987 w->setAttribute(Qt::WA_StyledBackground, on: true);
2988 }
2989 QWidget *ew = embeddedWidget(w);
2990 if (ew->autoFillBackground()) {
2991 ew->setAutoFillBackground(false);
2992 styleSheetCaches->autoFillDisabledWidgets.insert(value: w);
2993 if (ew != w) { //eg. viewport of a scrollarea
2994 //(in order to draw the background anyway in case we don't.)
2995 ew->setAttribute(Qt::WA_StyledBackground, on: true);
2996 }
2997 }
2998 if (!rule.hasBackground() || rule.background()->isTransparent() || rule.hasBox()
2999 || (!rule.hasNativeBorder() && !rule.border()->isOpaque()))
3000 w->setAttribute(Qt::WA_OpaquePaintEvent, on: false);
3001 if (rule.hasBox() || !rule.hasNativeBorder()
3002#if QT_CONFIG(pushbutton)
3003 || (qobject_cast<QPushButton *>(object: w))
3004#endif
3005 )
3006 w->setAttribute(Qt::WA_MacShowFocusRect, on: false);
3007 }
3008}
3009
3010void QStyleSheetStyle::polish(QApplication *app)
3011{
3012 baseStyle()->polish(application: app);
3013}
3014
3015void QStyleSheetStyle::polish(QPalette &pal)
3016{
3017 baseStyle()->polish(palette&: pal);
3018}
3019
3020void QStyleSheetStyle::repolish(QWidget *w)
3021{
3022 QList<const QObject *> children;
3023 children.reserve(size: w->children().size() + 1);
3024 for (auto child: std::as_const(t: w->children()))
3025 children.append(t: child);
3026 children.append(t: w);
3027 styleSheetCaches->styleSheetCache.remove(key: w);
3028 updateObjects(objects: children);
3029}
3030
3031void QStyleSheetStyle::repolish(QApplication *app)
3032{
3033 Q_UNUSED(app);
3034 const QList<const QObject*> allObjects = styleSheetCaches->styleRulesCache.keys();
3035 styleSheetCaches->styleSheetCache.remove(qApp);
3036 styleSheetCaches->styleRulesCache.clear();
3037 styleSheetCaches->hasStyleRuleCache.clear();
3038 styleSheetCaches->renderRulesCache.clear();
3039 updateObjects(objects: allObjects);
3040}
3041
3042void QStyleSheetStyle::unpolish(QWidget *w)
3043{
3044 if (!w || !w->testAttribute(attribute: Qt::WA_StyleSheet)) {
3045 baseStyle()->unpolish(widget: w);
3046 return;
3047 }
3048
3049 styleSheetCaches->styleRulesCache.remove(key: w);
3050 styleSheetCaches->hasStyleRuleCache.remove(key: w);
3051 styleSheetCaches->renderRulesCache.remove(key: w);
3052 styleSheetCaches->styleSheetCache.remove(key: w);
3053 unsetPalette(w);
3054 setGeometry(w);
3055 w->setAttribute(Qt::WA_StyleSheetTarget, on: false);
3056 w->setAttribute(Qt::WA_StyleSheet, on: false);
3057 QObject::disconnect(sender: w, signal: nullptr, receiver: this, member: nullptr);
3058#if QT_CONFIG(scrollarea)
3059 if (QAbstractScrollArea *sa = qobject_cast<QAbstractScrollArea *>(object: w)) {
3060 QObject::disconnect(sender: sa->horizontalScrollBar(), SIGNAL(valueChanged(int)),
3061 receiver: sa, SLOT(update()));
3062 QObject::disconnect(sender: sa->verticalScrollBar(), SIGNAL(valueChanged(int)),
3063 receiver: sa, SLOT(update()));
3064 }
3065#endif
3066 baseStyle()->unpolish(widget: w);
3067}
3068
3069void QStyleSheetStyle::unpolish(QApplication *app)
3070{
3071 baseStyle()->unpolish(application: app);
3072 RECURSION_GUARD(return)
3073 styleSheetCaches->styleRulesCache.clear();
3074 styleSheetCaches->hasStyleRuleCache.clear();
3075 styleSheetCaches->renderRulesCache.clear();
3076 styleSheetCaches->styleSheetCache.remove(qApp);
3077}
3078
3079void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
3080 const QWidget *w) const
3081{
3082 RECURSION_GUARD(baseStyle()->drawComplexControl(cc, opt, p, w); return)
3083
3084 QRenderRule rule = renderRule(obj: w, opt);
3085
3086 switch (cc) {
3087 case CC_ComboBox:
3088 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
3089 QStyleOptionComboBox cmbOpt(*cmb);
3090 cmbOpt.rect = rule.borderRect(r: opt->rect);
3091 if (rule.hasNativeBorder()) {
3092 rule.drawBackgroundImage(p, rect: cmbOpt.rect);
3093 rule.configurePalette(p: &cmbOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3094 bool customDropDown = (opt->subControls & QStyle::SC_ComboBoxArrow)
3095 && (hasStyleRule(obj: w, part: PseudoElement_ComboBoxDropDown) || hasStyleRule(obj: w, part: PseudoElement_ComboBoxArrow));
3096 if (customDropDown)
3097 cmbOpt.subControls &= ~QStyle::SC_ComboBoxArrow;
3098 if (rule.baseStyleCanDraw()) {
3099 baseStyle()->drawComplexControl(cc, opt: &cmbOpt, p, widget: w);
3100 } else {
3101 QWindowsStyle::drawComplexControl(cc, opt: &cmbOpt, p, w);
3102 }
3103 if (!customDropDown)
3104 return;
3105 } else {
3106 rule.drawRule(p, rect: opt->rect);
3107 }
3108
3109 if (opt->subControls & QStyle::SC_ComboBoxArrow) {
3110 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ComboBoxDropDown);
3111 if (subRule.hasDrawable()) {
3112 QRect r = subControlRect(cc: CC_ComboBox, opt, sc: SC_ComboBoxArrow, w);
3113 subRule.drawRule(p, rect: r);
3114 QRenderRule subRule2 = renderRule(obj: w, opt, pseudoElement: PseudoElement_ComboBoxArrow);
3115 r = positionRect(w, rule1: subRule, rule2: subRule2, pe: PseudoElement_ComboBoxArrow, rect: r, dir: opt->direction);
3116 subRule2.drawRule(p, rect: r);
3117 } else {
3118 rule.configurePalette(p: &cmbOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3119 cmbOpt.subControls = QStyle::SC_ComboBoxArrow;
3120 QWindowsStyle::drawComplexControl(cc, opt: &cmbOpt, p, w);
3121 }
3122 }
3123
3124 return;
3125 }
3126 break;
3127
3128#if QT_CONFIG(spinbox)
3129 case CC_SpinBox:
3130 if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
3131 QStyleOptionSpinBox spinOpt(*spin);
3132 rule.configurePalette(p: &spinOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3133 rule.configurePalette(p: &spinOpt.palette, fr: QPalette::Text, br: QPalette::Base);
3134 spinOpt.rect = rule.borderRect(r: opt->rect);
3135 bool customUp = true, customDown = true;
3136 QRenderRule upRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxUpButton);
3137 QRenderRule downRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxDownButton);
3138 bool upRuleMatch = upRule.hasGeometry() || upRule.hasPosition();
3139 bool downRuleMatch = downRule.hasGeometry() || downRule.hasPosition();
3140 if (rule.hasNativeBorder() && !upRuleMatch && !downRuleMatch) {
3141 rule.drawBackgroundImage(p, rect: spinOpt.rect);
3142 customUp = (opt->subControls & QStyle::SC_SpinBoxUp)
3143 && (hasStyleRule(obj: w, part: PseudoElement_SpinBoxUpButton) || hasStyleRule(obj: w, part: PseudoElement_UpArrow));
3144 if (customUp)
3145 spinOpt.subControls &= ~QStyle::SC_SpinBoxUp;
3146 customDown = (opt->subControls & QStyle::SC_SpinBoxDown)
3147 && (hasStyleRule(obj: w, part: PseudoElement_SpinBoxDownButton) || hasStyleRule(obj: w, part: PseudoElement_DownArrow));
3148 if (customDown)
3149 spinOpt.subControls &= ~QStyle::SC_SpinBoxDown;
3150 if (rule.baseStyleCanDraw()) {
3151 baseStyle()->drawComplexControl(cc, opt: &spinOpt, p, widget: w);
3152 } else {
3153 QWindowsStyle::drawComplexControl(cc, opt: &spinOpt, p, w);
3154 }
3155 if (!customUp && !customDown)
3156 return;
3157 } else {
3158 rule.drawRule(p, rect: opt->rect);
3159 }
3160
3161 if ((opt->subControls & QStyle::SC_SpinBoxUp) && customUp) {
3162 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxUpButton);
3163 if (subRule.hasDrawable()) {
3164 QRect r = subControlRect(cc: CC_SpinBox, opt, sc: SC_SpinBoxUp, w);
3165 subRule.drawRule(p, rect: r);
3166 QRenderRule subRule2 = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxUpArrow);
3167 r = positionRect(w, rule1: subRule, rule2: subRule2, pe: PseudoElement_SpinBoxUpArrow, rect: r, dir: opt->direction);
3168 subRule2.drawRule(p, rect: r);
3169 } else {
3170 spinOpt.subControls = QStyle::SC_SpinBoxUp;
3171 QWindowsStyle::drawComplexControl(cc, opt: &spinOpt, p, w);
3172 }
3173 }
3174
3175 if ((opt->subControls & QStyle::SC_SpinBoxDown) && customDown) {
3176 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxDownButton);
3177 if (subRule.hasDrawable()) {
3178 QRect r = subControlRect(cc: CC_SpinBox, opt, sc: SC_SpinBoxDown, w);
3179 subRule.drawRule(p, rect: r);
3180 QRenderRule subRule2 = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxDownArrow);
3181 r = positionRect(w, rule1: subRule, rule2: subRule2, pe: PseudoElement_SpinBoxDownArrow, rect: r, dir: opt->direction);
3182 subRule2.drawRule(p, rect: r);
3183 } else {
3184 spinOpt.subControls = QStyle::SC_SpinBoxDown;
3185 QWindowsStyle::drawComplexControl(cc, opt: &spinOpt, p, w);
3186 }
3187 }
3188 return;
3189 }
3190 break;
3191#endif // QT_CONFIG(spinbox)
3192
3193 case CC_GroupBox:
3194 if (const QStyleOptionGroupBox *gb = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
3195
3196 QRect labelRect, checkBoxRect, titleRect, frameRect;
3197 bool hasTitle = (gb->subControls & QStyle::SC_GroupBoxCheckBox) || !gb->text.isEmpty();
3198
3199 if (!rule.hasDrawable() && (!hasTitle || !hasStyleRule(obj: w, part: PseudoElement_GroupBoxTitle))
3200 && !hasStyleRule(obj: w, part: PseudoElement_Indicator) && !rule.hasBox() && !rule.hasFont && !rule.hasPalette()) {
3201 // let the native style draw the combobox if there is no style for it.
3202 break;
3203 }
3204 rule.drawBackground(p, rect: opt->rect);
3205
3206 QRenderRule titleRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_GroupBoxTitle);
3207 bool clipSet = false;
3208
3209 if (hasTitle) {
3210 labelRect = subControlRect(cc: CC_GroupBox, opt, sc: SC_GroupBoxLabel, w);
3211 //Some native style (such as mac) may return a too small rectangle (because they use smaller fonts), so we may need to expand it a little bit.
3212 labelRect.setSize(labelRect.size().expandedTo(otherSize: ParentStyle::subControlRect(cc: CC_GroupBox, opt, sc: SC_GroupBoxLabel, w).size()));
3213 if (gb->subControls & QStyle::SC_GroupBoxCheckBox) {
3214 checkBoxRect = subControlRect(cc: CC_GroupBox, opt, sc: SC_GroupBoxCheckBox, w);
3215 titleRect = titleRule.boxRect(cr: checkBoxRect.united(r: labelRect));
3216 } else {
3217 titleRect = titleRule.boxRect(cr: labelRect);
3218 }
3219 if (!titleRule.hasBackground() || !titleRule.background()->isTransparent()) {
3220 clipSet = true;
3221 p->save();
3222 p->setClipRegion(QRegion(opt->rect) - titleRect);
3223 }
3224 }
3225
3226 frameRect = subControlRect(cc: CC_GroupBox, opt, sc: SC_GroupBoxFrame, w);
3227 QStyleOptionFrame frame;
3228 frame.QStyleOption::operator=(other: *gb);
3229 frame.features = gb->features;
3230 frame.lineWidth = gb->lineWidth;
3231 frame.midLineWidth = gb->midLineWidth;
3232 frame.rect = frameRect;
3233 drawPrimitive(pe: PE_FrameGroupBox, opt: &frame, p, w);
3234
3235 if (clipSet)
3236 p->restore();
3237
3238 // draw background and frame of the title
3239 if (hasTitle)
3240 titleRule.drawRule(p, rect: titleRect);
3241
3242 // draw the indicator
3243 if (gb->subControls & QStyle::SC_GroupBoxCheckBox) {
3244 QStyleOptionButton box;
3245 box.QStyleOption::operator=(other: *gb);
3246 box.rect = checkBoxRect;
3247 drawPrimitive(pe: PE_IndicatorCheckBox, opt: &box, p, w);
3248 }
3249
3250 // draw the text
3251 if (!gb->text.isEmpty()) {
3252 int alignment = int(Qt::AlignCenter | Qt::TextShowMnemonic);
3253 if (!styleHint(sh: QStyle::SH_UnderlineShortcut, opt, w)) {
3254 alignment |= Qt::TextHideMnemonic;
3255 }
3256
3257 QPalette pal = gb->palette;
3258 if (gb->textColor.isValid())
3259 pal.setColor(acr: QPalette::WindowText, acolor: gb->textColor);
3260 titleRule.configurePalette(p: &pal, fr: QPalette::WindowText, br: QPalette::Window);
3261 drawItemText(painter: p, rect: labelRect, alignment, pal, enabled: gb->state & State_Enabled,
3262 text: gb->text, textRole: QPalette::WindowText);
3263
3264 if (gb->state & State_HasFocus) {
3265 QStyleOptionFocusRect fropt;
3266 fropt.QStyleOption::operator=(other: *gb);
3267 fropt.rect = labelRect;
3268 drawPrimitive(pe: PE_FrameFocusRect, opt: &fropt, p, w);
3269 }
3270 }
3271
3272 return;
3273 }
3274 break;
3275
3276 case CC_ToolButton:
3277 if (const QStyleOptionToolButton *tool = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
3278 QStyleOptionToolButton toolOpt(*tool);
3279 rule.configurePalette(p: &toolOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3280 toolOpt.font = rule.font.resolve(toolOpt.font);
3281 toolOpt.rect = rule.borderRect(r: opt->rect);
3282 const auto customArrowElement = [tool]{
3283 switch (tool->arrowType) {
3284 case Qt::DownArrow: return PseudoElement_DownArrow;
3285 case Qt::UpArrow: return PseudoElement_UpArrow;
3286 case Qt::LeftArrow: return PseudoElement_LeftArrow;
3287 case Qt::RightArrow: return PseudoElement_RightArrow;
3288 default: break;
3289 }
3290 return PseudoElement_None;
3291 };
3292 // if arrow/menu/indicators are requested, either draw them using the available rule,
3293 // or let the base style draw them; but not both
3294 const bool drawArrow = tool->features & QStyleOptionToolButton::Arrow;
3295 bool customArrow = drawArrow && hasStyleRule(obj: w, part: customArrowElement());
3296 if (customArrow) {
3297 toolOpt.features &= ~QStyleOptionToolButton::Arrow;
3298 toolOpt.text = QString(); // we need to draw the arrow and the text ourselves
3299 }
3300 bool drawDropDown = tool->features & QStyleOptionToolButton::MenuButtonPopup;
3301 bool customDropDown = drawDropDown && hasStyleRule(obj: w, part: PseudoElement_ToolButtonMenu);
3302 bool customDropDownArrow = false;
3303 bool drawMenuIndicator = tool->features & QStyleOptionToolButton::HasMenu;
3304 if (customDropDown) {
3305 toolOpt.subControls &= ~QStyle::SC_ToolButtonMenu;
3306 customDropDownArrow = hasStyleRule(obj: w, part: PseudoElement_ToolButtonMenuArrow);
3307 if (customDropDownArrow)
3308 toolOpt.features &= ~(QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu);
3309 }
3310 const bool customMenuIndicator = (!drawDropDown && drawMenuIndicator)
3311 && hasStyleRule(obj: w, part: PseudoElement_ToolButtonMenuIndicator);
3312 if (customMenuIndicator)
3313 toolOpt.features &= ~QStyleOptionToolButton::HasMenu;
3314
3315 if (rule.hasNativeBorder()) {
3316 if (tool->subControls & SC_ToolButton) {
3317 //in some case (eg. the button is "auto raised") the style doesn't draw the background
3318 //so we need to draw the background.
3319 // use the same condition as in QCommonStyle
3320 State bflags = tool->state & ~State_Sunken;
3321 if (bflags & State_AutoRaise && (!(bflags & State_MouseOver) || !(bflags & State_Enabled)))
3322 bflags &= ~State_Raised;
3323 if (tool->state & State_Sunken && tool->activeSubControls & SC_ToolButton)
3324 bflags |= State_Sunken;
3325 if (!(bflags & (State_Sunken | State_On | State_Raised)))
3326 rule.drawBackground(p, rect: toolOpt.rect);
3327 }
3328
3329 QStyleOptionToolButton nativeToolOpt(toolOpt);
3330 // don't draw natively if we have a custom rule for menu indicators and/or buttons
3331 if (customMenuIndicator)
3332 nativeToolOpt.features &= ~(QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu);
3333 if (customDropDown || customDropDownArrow)
3334 nativeToolOpt.features &= ~(QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu | QStyleOptionToolButton::MenuButtonPopup);
3335 // Let base or windows style draw the button, which will include the menu-button
3336 if (rule.baseStyleCanDraw() && !(tool->features & QStyleOptionToolButton::Arrow))
3337 baseStyle()->drawComplexControl(cc, opt: &nativeToolOpt, p, widget: w);
3338 else
3339 QWindowsStyle::drawComplexControl(cc, opt: &nativeToolOpt, p, w);
3340 // if we did draw natively, don't draw custom
3341 if (nativeToolOpt.features & (QStyleOptionToolButton::Menu | QStyleOptionToolButton::HasMenu))
3342 drawMenuIndicator = false;
3343 if (nativeToolOpt.features & QStyleOptionToolButton::MenuButtonPopup && !customDropDownArrow)
3344 drawDropDown = false;
3345 } else {
3346 rule.drawRule(p, rect: opt->rect);
3347 toolOpt.rect = rule.contentsRect(r: opt->rect);
3348 if (rule.hasFont)
3349 toolOpt.font = rule.font.resolve(toolOpt.font);
3350 drawControl(element: CE_ToolButtonLabel, opt: &toolOpt, p, w);
3351 }
3352
3353 const QRect cr = toolOpt.rect;
3354 // Draw DropDownButton unless drawn before
3355 if (drawDropDown) {
3356 if (opt->subControls & QStyle::SC_ToolButtonMenu) {
3357 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolButtonMenu);
3358 QRect menuButtonRect = subControlRect(cc: CC_ToolButton, opt, sc: QStyle::SC_ToolButtonMenu, w);
3359 if (subRule.hasDrawable()) {
3360 subRule.drawRule(p, rect: menuButtonRect);
3361 } else {
3362 toolOpt.rect = menuButtonRect;
3363 baseStyle()->drawPrimitive(pe: PE_IndicatorButtonDropDown, opt: &toolOpt, p, w);
3364 }
3365
3366 if (customDropDownArrow || drawMenuIndicator) {
3367 QRenderRule arrowRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolButtonMenuArrow);
3368 QRect arrowRect = arrowRule.hasGeometry()
3369 ? positionRect(w, rule2: arrowRule, pe: PseudoElement_ToolButtonMenuArrow, originRect: menuButtonRect, dir: toolOpt.direction)
3370 : arrowRule.contentsRect(r: menuButtonRect);
3371 if (arrowRule.hasDrawable()) {
3372 arrowRule.drawRule(p, rect: arrowRect);
3373 } else {
3374 toolOpt.rect = arrowRect;
3375 baseStyle()->drawPrimitive(pe: QStyle::PE_IndicatorArrowDown, opt: &toolOpt, p, w);
3376 }
3377 }
3378 }
3379 } else if (drawMenuIndicator) {
3380 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolButtonMenuIndicator);
3381
3382 // content padding does not impact the indicator, so use the original rect to
3383 // calculate position of the sub element within the toplevel rule
3384 QRect r = positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_ToolButtonMenuIndicator, rect: opt->rect, dir: toolOpt.direction);
3385 if (subRule.hasDrawable()) {
3386 subRule.drawRule(p, rect: r);
3387 } else {
3388 toolOpt.rect = r;
3389 baseStyle()->drawPrimitive(pe: QStyle::PE_IndicatorArrowDown, opt: &toolOpt, p, w);
3390 }
3391 }
3392 toolOpt.rect = cr;
3393
3394 // If we don't have a custom arrow, then the arrow will have been rendered
3395 // already by the base style when drawing the label.
3396 if (customArrow) {
3397 const auto arrowElement = customArrowElement();
3398 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: arrowElement);
3399 QRect arrowRect = subRule.hasGeometry() ? positionRect(w, rule2: subRule, pe: arrowElement, originRect: toolOpt.rect, dir: toolOpt.direction)
3400 : subRule.contentsRect(r: toolOpt.rect);
3401
3402 switch (toolOpt.toolButtonStyle) {
3403 case Qt::ToolButtonIconOnly:
3404 break;
3405 case Qt::ToolButtonTextOnly:
3406 case Qt::ToolButtonTextBesideIcon:
3407 case Qt::ToolButtonTextUnderIcon: {
3408 // The base style needs to lay out the contents and will render the styled
3409 // arrow icons, unless the geometry is defined in the style sheet.
3410 toolOpt.text = tool->text;
3411 if (!subRule.hasGeometry())
3412 toolOpt.features |= QStyleOptionToolButton::Arrow;
3413 drawControl(element: CE_ToolButtonLabel, opt: &toolOpt, p, w);
3414 if (!subRule.hasGeometry())
3415 return;
3416 break;
3417 }
3418 case Qt::ToolButtonFollowStyle:
3419 // QToolButton handles this, so must never happen
3420 Q_ASSERT(false);
3421 break;
3422 }
3423 subRule.drawRule(p, rect: arrowRect);
3424 }
3425 return;
3426 }
3427 break;
3428
3429#if QT_CONFIG(scrollbar)
3430 case CC_ScrollBar:
3431 if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
3432 if (!rule.hasDrawable()) {
3433 QStyleOptionSlider sbOpt(*sb);
3434 sbOpt.rect = rule.borderRect(r: opt->rect);
3435 rule.drawBackgroundImage(p, rect: opt->rect);
3436 baseStyle()->drawComplexControl(cc, opt: &sbOpt, p, widget: w);
3437 } else {
3438 rule.drawRule(p, rect: opt->rect);
3439 QWindowsStyle::drawComplexControl(cc, opt, p, w);
3440 }
3441 return;
3442 }
3443 break;
3444#endif // QT_CONFIG(scrollbar)
3445
3446#if QT_CONFIG(slider)
3447 case CC_Slider:
3448 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
3449 rule.drawRule(p, rect: opt->rect);
3450
3451 QRenderRule grooveSubRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SliderGroove);
3452 QRenderRule handleSubRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SliderHandle);
3453 if (!grooveSubRule.hasDrawable()) {
3454 QStyleOptionSlider slOpt(*slider);
3455 bool handleHasRule = handleSubRule.hasDrawable();
3456 // If the style specifies a different handler rule, draw the groove without the handler.
3457 if (handleHasRule)
3458 slOpt.subControls &= ~SC_SliderHandle;
3459 baseStyle()->drawComplexControl(cc, opt: &slOpt, p, widget: w);
3460 if (!handleHasRule)
3461 return;
3462 }
3463
3464 QRect gr = subControlRect(cc, opt, sc: SC_SliderGroove, w);
3465 if (slider->subControls & SC_SliderGroove) {
3466 grooveSubRule.drawRule(p, rect: gr);
3467 }
3468
3469 if (slider->subControls & SC_SliderHandle) {
3470 QRect hr = subControlRect(cc, opt, sc: SC_SliderHandle, w);
3471
3472 QRenderRule subRule1 = renderRule(obj: w, opt, pseudoElement: PseudoElement_SliderSubPage);
3473 if (subRule1.hasDrawable()) {
3474 QRect r(gr.topLeft(),
3475 slider->orientation == Qt::Horizontal
3476 ? QPoint(hr.x()+hr.width()/2, gr.y()+gr.height() - 1)
3477 : QPoint(gr.x()+gr.width() - 1, hr.y()+hr.height()/2));
3478 subRule1.drawRule(p, rect: r);
3479 }
3480
3481 QRenderRule subRule2 = renderRule(obj: w, opt, pseudoElement: PseudoElement_SliderAddPage);
3482 if (subRule2.hasDrawable()) {
3483 QRect r(slider->orientation == Qt::Horizontal
3484 ? QPoint(hr.x()+hr.width()/2+1, gr.y())
3485 : QPoint(gr.x(), hr.y()+hr.height()/2+1),
3486 gr.bottomRight());
3487 subRule2.drawRule(p, rect: r);
3488 }
3489
3490 handleSubRule.drawRule(p, rect: handleSubRule.boxRect(cr: hr, flags: Margin));
3491 }
3492
3493 if (slider->subControls & SC_SliderTickmarks) {
3494 // TODO...
3495 }
3496
3497 return;
3498 }
3499 break;
3500#endif // QT_CONFIG(slider)
3501
3502 case CC_MdiControls:
3503 if (hasStyleRule(obj: w, part: PseudoElement_MdiCloseButton)
3504 || hasStyleRule(obj: w, part: PseudoElement_MdiNormalButton)
3505 || hasStyleRule(obj: w, part: PseudoElement_MdiMinButton)) {
3506 QList<QVariant> layout = rule.styleHint(sh: "button-layout"_L1).toList();
3507 if (layout.isEmpty())
3508 layout = subControlLayout(layout: "mNX"_L1);
3509
3510 QStyleOptionComplex optCopy(*opt);
3511 optCopy.subControls = { };
3512 for (int i = 0; i < layout.size(); i++) {
3513 int layoutButton = layout[i].toInt();
3514 if (layoutButton < PseudoElement_MdiCloseButton
3515 || layoutButton > PseudoElement_MdiNormalButton)
3516 continue;
3517 QStyle::SubControl control = knownPseudoElements[layoutButton].subControl;
3518 if (!(opt->subControls & control))
3519 continue;
3520 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: layoutButton);
3521 if (subRule.hasDrawable()) {
3522 QRect rect = subRule.boxRect(cr: subControlRect(cc: CC_MdiControls, opt, sc: control, w), flags: Margin);
3523 subRule.drawRule(p, rect);
3524 QIcon icon = standardIcon(standardIcon: subControlIcon(pe: layoutButton), opt);
3525 icon.paint(painter: p, rect: subRule.contentsRect(r: rect), alignment: Qt::AlignCenter);
3526 } else {
3527 optCopy.subControls |= control;
3528 }
3529 }
3530
3531 if (optCopy.subControls)
3532 baseStyle()->drawComplexControl(cc: CC_MdiControls, opt: &optCopy, p, widget: w);
3533 return;
3534 }
3535 break;
3536
3537 case CC_TitleBar:
3538 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
3539 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TitleBar);
3540 if (!subRule.hasDrawable() && !subRule.hasBox() && !subRule.hasBorder())
3541 break;
3542 subRule.drawRule(p, rect: opt->rect);
3543 QHash<QStyle::SubControl, QRect> layout = titleBarLayout(w, tb);
3544
3545 QRect ir;
3546 ir = layout[SC_TitleBarLabel];
3547 if (ir.isValid()) {
3548 if (subRule.hasPalette())
3549 p->setPen(subRule.palette()->foreground.color());
3550 p->fillRect(r: ir, c: Qt::white);
3551 p->drawText(x: ir.x(), y: ir.y(), w: ir.width(), h: ir.height(), flags: Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, str: tb->text);
3552 }
3553
3554 QPixmap pm;
3555
3556 ir = layout[SC_TitleBarSysMenu];
3557 if (ir.isValid()) {
3558 QRenderRule subSubRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TitleBarSysMenu);
3559 subSubRule.drawRule(p, rect: ir);
3560 ir = subSubRule.contentsRect(r: ir);
3561 if (!tb->icon.isNull()) {
3562 tb->icon.paint(painter: p, rect: ir);
3563 } else {
3564 int iconSize = pixelMetric(metric: PM_SmallIconSize, option: tb, widget: w);
3565 pm = standardIcon(standardIcon: SP_TitleBarMenuButton, opt: nullptr, widget: w).pixmap(w: iconSize, h: iconSize);
3566 drawItemPixmap(painter: p, rect: ir, alignment: Qt::AlignCenter, pixmap: pm);
3567 }
3568 }
3569
3570 ir = layout[SC_TitleBarCloseButton];
3571 if (ir.isValid()) {
3572 QRenderRule subSubRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TitleBarCloseButton);
3573 subSubRule.drawRule(p, rect: ir);
3574
3575 QSize sz = subSubRule.contentsRect(r: ir).size();
3576 if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool)
3577 pm = standardIcon(standardIcon: SP_DockWidgetCloseButton, opt: nullptr, widget: w).pixmap(size: sz);
3578 else
3579 pm = standardIcon(standardIcon: SP_TitleBarCloseButton, opt: nullptr, widget: w).pixmap(size: sz);
3580 drawItemPixmap(painter: p, rect: ir, alignment: Qt::AlignCenter, pixmap: pm);
3581 }
3582
3583 int pes[] = {
3584 PseudoElement_TitleBarMaxButton,
3585 PseudoElement_TitleBarMinButton,
3586 PseudoElement_TitleBarNormalButton,
3587 PseudoElement_TitleBarShadeButton,
3588 PseudoElement_TitleBarUnshadeButton,
3589 PseudoElement_TitleBarContextHelpButton
3590 };
3591
3592 for (unsigned int i = 0; i < sizeof(pes)/sizeof(int); i++) {
3593 int pe = pes[i];
3594 QStyle::SubControl sc = knownPseudoElements[pe].subControl;
3595 ir = layout[sc];
3596 if (!ir.isValid())
3597 continue;
3598 QRenderRule subSubRule = renderRule(obj: w, opt, pseudoElement: pe);
3599 subSubRule.drawRule(p, rect: ir);
3600 pm = standardIcon(standardIcon: subControlIcon(pe), opt: nullptr, widget: w).pixmap(size: subSubRule.contentsRect(r: ir).size());
3601 drawItemPixmap(painter: p, rect: ir, alignment: Qt::AlignCenter, pixmap: pm);
3602 }
3603
3604 return;
3605 }
3606 break;
3607
3608
3609 default:
3610 break;
3611 }
3612
3613 baseStyle()->drawComplexControl(cc, opt, p, widget: w);
3614}
3615
3616void QStyleSheetStyle::renderMenuItemIcon(const QStyleOptionMenuItem *mi, QPainter *p, const QWidget *w,
3617 const QRect &rect, QRenderRule &subRule) const
3618{
3619 const QIcon::Mode mode = mi->state & QStyle::State_Enabled
3620 ? (mi->state & QStyle::State_Selected ? QIcon::Active : QIcon::Normal)
3621 : QIcon::Disabled;
3622 const bool checked = mi->checkType != QStyleOptionMenuItem::NotCheckable && mi->checked;
3623 const QPixmap pixmap(mi->icon.pixmap(extent: pixelMetric(metric: PM_SmallIconSize), mode,
3624 state: checked ? QIcon::On : QIcon::Off));
3625 const int pixw = pixmap.width() / pixmap.devicePixelRatio();
3626 const int pixh = pixmap.height() / pixmap.devicePixelRatio();
3627 QRenderRule iconRule = renderRule(obj: w, opt: mi, pseudoElement: PseudoElement_MenuIcon);
3628 if (!iconRule.hasGeometry()) {
3629 iconRule.geo = new QStyleSheetGeometryData(pixw, pixh, pixw, pixh, -1, -1);
3630 } else {
3631 iconRule.geo->width = pixw;
3632 iconRule.geo->height = pixh;
3633 }
3634 QRect iconRect = positionRect(w, rule1: subRule, rule2: iconRule, pe: PseudoElement_MenuIcon, rect, dir: mi->direction);
3635 if (mi->direction == Qt::LeftToRight)
3636 iconRect.moveLeft(pos: iconRect.left());
3637 else
3638 iconRect.moveRight(pos: iconRect.right());
3639 iconRule.drawRule(p, rect: iconRect);
3640 QRect pmr(0, 0, pixw, pixh);
3641 pmr.moveCenter(p: iconRect.center());
3642 p->drawPixmap(p: pmr.topLeft(), pm: pixmap);
3643}
3644
3645void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p,
3646 const QWidget *w) const
3647{
3648 RECURSION_GUARD(baseStyle()->drawControl(ce, opt, p, w); return)
3649
3650 QRenderRule rule = renderRule(obj: w, opt);
3651 int pe1 = PseudoElement_None, pe2 = PseudoElement_None;
3652 bool fallback = false;
3653
3654 switch (ce) {
3655 case CE_ToolButtonLabel:
3656 if (const QStyleOptionToolButton *btn = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
3657 if (rule.hasBox() || btn->features & QStyleOptionToolButton::Arrow) {
3658 QWindowsStyle::drawControl(element: ce, opt, p, w);
3659 } else {
3660 QStyleOptionToolButton butOpt(*btn);
3661 rule.configurePalette(p: &butOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3662 baseStyle()->drawControl(element: ce, opt: &butOpt, p, w);
3663 }
3664 return;
3665 }
3666 break;
3667
3668 case CE_FocusFrame:
3669 if (!rule.hasNativeBorder()) {
3670 rule.drawBorder(p, rect: opt->rect);
3671 return;
3672 }
3673 break;
3674
3675 case CE_PushButton:
3676 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3677 if (rule.hasDrawable() || rule.hasBox() || rule.hasPosition() || rule.hasPalette() ||
3678 ((btn->features & QStyleOptionButton::HasMenu) && hasStyleRule(obj: w, part: PseudoElement_PushButtonMenuIndicator))) {
3679 ParentStyle::drawControl(element: ce, opt, p, w);
3680 return;
3681 }
3682 }
3683 break;
3684 case CE_PushButtonBevel:
3685 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3686 QStyleOptionButton btnOpt(*btn);
3687 btnOpt.rect = rule.borderRect(r: opt->rect);
3688 if (rule.hasNativeBorder()) {
3689 rule.drawBackgroundImage(p, rect: btnOpt.rect);
3690 rule.configurePalette(p: &btnOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3691 bool customMenu = (btn->features & QStyleOptionButton::HasMenu
3692 && hasStyleRule(obj: w, part: PseudoElement_PushButtonMenuIndicator));
3693 if (customMenu)
3694 btnOpt.features &= ~QStyleOptionButton::HasMenu;
3695 if (rule.baseStyleCanDraw()) {
3696 baseStyle()->drawControl(element: ce, opt: &btnOpt, p, w);
3697 } else {
3698 QWindowsStyle::drawControl(element: ce, opt: &btnOpt, p, w);
3699 }
3700 rule.drawImage(p, rect: rule.contentsRect(r: opt->rect));
3701 if (!customMenu)
3702 return;
3703 } else {
3704 rule.drawRule(p, rect: opt->rect);
3705 }
3706
3707 if (btn->features & QStyleOptionButton::HasMenu) {
3708 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_PushButtonMenuIndicator);
3709 QRect ir = positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_PushButtonMenuIndicator,
3710 rect: baseStyle()->subElementRect(subElement: SE_PushButtonBevel, option: btn, widget: w), dir: opt->direction);
3711 if (subRule.hasDrawable()) {
3712 subRule.drawRule(p, rect: ir);
3713 } else {
3714 btnOpt.rect = ir;
3715 baseStyle()->drawPrimitive(pe: PE_IndicatorArrowDown, opt: &btnOpt, p, w);
3716 }
3717 }
3718 }
3719 return;
3720
3721 case CE_PushButtonLabel:
3722 if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3723 QStyleOptionButton butOpt(*button);
3724 rule.configurePalette(p: &butOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3725
3726 const QFont oldFont = p->font();
3727 if (rule.hasFont)
3728 p->setFont(rule.font.resolve(p->font()));
3729
3730 if (rule.hasPosition() || rule.hasIcon()) {
3731 uint tf = Qt::TextShowMnemonic;
3732 QRect textRect = button->rect;
3733
3734 const uint horizontalAlignMask = Qt::AlignHCenter | Qt::AlignLeft | Qt::AlignRight;
3735 const uint verticalAlignMask = Qt::AlignVCenter | Qt::AlignTop | Qt::AlignBottom;
3736
3737 if (rule.hasPosition() && rule.position()->textAlignment != 0) {
3738 Qt::Alignment textAlignment = rule.position()->textAlignment;
3739 tf |= (textAlignment & verticalAlignMask) ? (textAlignment & verticalAlignMask) : Qt::AlignVCenter;
3740 tf |= (textAlignment & horizontalAlignMask) ? (textAlignment & horizontalAlignMask) : Qt::AlignHCenter;
3741 if (!styleHint(sh: SH_UnderlineShortcut, opt: button, w))
3742 tf |= Qt::TextHideMnemonic;
3743 } else {
3744 tf |= Qt::AlignVCenter | Qt::AlignHCenter;
3745 }
3746
3747 QIcon icon = rule.hasIcon() ? rule.icon()->icon : button->icon;
3748 if (!icon.isNull()) {
3749 //Group both icon and text
3750 QRect iconRect;
3751 QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
3752 if (mode == QIcon::Normal && button->state & State_HasFocus)
3753 mode = QIcon::Active;
3754 QIcon::State state = QIcon::Off;
3755 if (button->state & State_On)
3756 state = QIcon::On;
3757
3758 QPixmap pixmap = icon.pixmap(size: button->iconSize, mode, state);
3759 int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
3760 int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
3761 int labelWidth = pixmapWidth;
3762 int labelHeight = pixmapHeight;
3763 int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint()
3764 int textWidth = button->fontMetrics.boundingRect(r: opt->rect, flags: tf, text: button->text).width();
3765 if (!button->text.isEmpty())
3766 labelWidth += (textWidth + iconSpacing);
3767
3768 //Determine label alignment:
3769 if (tf & Qt::AlignLeft) { /*left*/
3770 iconRect = QRect(textRect.x(), textRect.y() + (textRect.height() - labelHeight) / 2,
3771 pixmapWidth, pixmapHeight);
3772 } else if (tf & Qt::AlignHCenter) { /* center */
3773 iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2,
3774 textRect.y() + (textRect.height() - labelHeight) / 2,
3775 pixmapWidth, pixmapHeight);
3776 } else { /*right*/
3777 iconRect = QRect(textRect.x() + textRect.width() - labelWidth,
3778 textRect.y() + (textRect.height() - labelHeight) / 2,
3779 pixmapWidth, pixmapHeight);
3780 }
3781
3782 iconRect = visualRect(direction: button->direction, boundingRect: textRect, logicalRect: iconRect);
3783
3784 // Left align, adjust the text-rect according to the icon instead
3785 tf &= ~horizontalAlignMask;
3786 tf |= Qt::AlignLeft;
3787
3788 if (button->direction == Qt::RightToLeft)
3789 textRect.setRight(iconRect.left() - iconSpacing);
3790 else
3791 textRect.setLeft(iconRect.left() + iconRect.width() + iconSpacing);
3792
3793 if (button->state & (State_On | State_Sunken))
3794 iconRect.translate(dx: pixelMetric(metric: PM_ButtonShiftHorizontal, option: opt, widget: w),
3795 dy: pixelMetric(metric: PM_ButtonShiftVertical, option: opt, widget: w));
3796 p->drawPixmap(r: iconRect, pm: pixmap);
3797 }
3798
3799 if (button->state & (State_On | State_Sunken))
3800 textRect.translate(dx: pixelMetric(metric: PM_ButtonShiftHorizontal, option: opt, widget: w),
3801 dy: pixelMetric(metric: PM_ButtonShiftVertical, option: opt, widget: w));
3802
3803 if (button->features & QStyleOptionButton::HasMenu) {
3804 int indicatorSize = pixelMetric(metric: PM_MenuButtonIndicator, option: button, widget: w);
3805 if (button->direction == Qt::LeftToRight)
3806 textRect = textRect.adjusted(xp1: 0, yp1: 0, xp2: -indicatorSize, yp2: 0);
3807 else
3808 textRect = textRect.adjusted(xp1: indicatorSize, yp1: 0, xp2: 0, yp2: 0);
3809 }
3810 drawItemText(painter: p, rect: textRect, alignment: tf, pal: butOpt.palette, enabled: (button->state & State_Enabled),
3811 text: button->text, textRole: QPalette::ButtonText);
3812 } else {
3813 ParentStyle::drawControl(element: ce, opt: &butOpt, p, w);
3814 }
3815
3816 if (rule.hasFont)
3817 p->setFont(oldFont);
3818 }
3819 return;
3820
3821 case CE_RadioButton:
3822 case CE_CheckBox:
3823 if (rule.hasBox() || !rule.hasNativeBorder() || rule.hasDrawable() || hasStyleRule(obj: w, part: PseudoElement_Indicator)) {
3824 rule.drawRule(p, rect: opt->rect);
3825 ParentStyle::drawControl(element: ce, opt, p, w);
3826 return;
3827 } else if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3828 QStyleOptionButton butOpt(*btn);
3829 rule.configurePalette(p: &butOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3830 baseStyle()->drawControl(element: ce, opt: &butOpt, p, w);
3831 return;
3832 }
3833 break;
3834 case CE_RadioButtonLabel:
3835 case CE_CheckBoxLabel:
3836 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3837 QStyleOptionButton butOpt(*btn);
3838 rule.configurePalette(p: &butOpt.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3839 ParentStyle::drawControl(element: ce, opt: &butOpt, p, w);
3840 }
3841 return;
3842
3843 case CE_Splitter:
3844 pe1 = PseudoElement_SplitterHandle;
3845 break;
3846
3847 case CE_ToolBar:
3848 if (rule.hasBackground()) {
3849 rule.drawBackground(p, rect: opt->rect);
3850 }
3851 if (rule.hasBorder()) {
3852 rule.drawBorder(p, rect: rule.borderRect(r: opt->rect));
3853 } else {
3854#if QT_CONFIG(toolbar)
3855 if (const QStyleOptionToolBar *tb = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
3856 QStyleOptionToolBar newTb(*tb);
3857 newTb.rect = rule.borderRect(r: opt->rect);
3858 baseStyle()->drawControl(element: ce, opt: &newTb, p, w);
3859 }
3860#endif // QT_CONFIG(toolbar)
3861 }
3862 return;
3863
3864 case CE_MenuEmptyArea:
3865 case CE_MenuBarEmptyArea:
3866 if (rule.hasDrawable()) {
3867 // Drawn by PE_Widget
3868 return;
3869 }
3870 break;
3871
3872 case CE_MenuTearoff:
3873 case CE_MenuScroller:
3874 if (const QStyleOptionMenuItem *m = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
3875 QStyleOptionMenuItem mi(*m);
3876 int pe = ce == CE_MenuTearoff ? PseudoElement_MenuTearoff : PseudoElement_MenuScroller;
3877 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: pe);
3878 mi.rect = subRule.contentsRect(r: opt->rect);
3879 rule.configurePalette(p: &mi.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3880 subRule.configurePalette(p: &mi.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3881
3882 if (subRule.hasDrawable()) {
3883 subRule.drawRule(p, rect: opt->rect);
3884 } else {
3885 baseStyle()->drawControl(element: ce, opt: &mi, p, w);
3886 }
3887 }
3888 return;
3889
3890 case CE_MenuItem:
3891 if (const QStyleOptionMenuItem *m = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
3892 QStyleOptionMenuItem mi(*m);
3893
3894 int pseudo = (mi.menuItemType == QStyleOptionMenuItem::Separator) ? PseudoElement_MenuSeparator : PseudoElement_Item;
3895 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: pseudo);
3896 mi.rect = subRule.contentsRect(r: opt->rect);
3897 rule.configurePalette(p: &mi.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3898 rule.configurePalette(p: &mi.palette, fr: QPalette::HighlightedText, br: QPalette::Highlight);
3899 subRule.configurePalette(p: &mi.palette, fr: QPalette::ButtonText, br: QPalette::Button);
3900 subRule.configurePalette(p: &mi.palette, fr: QPalette::HighlightedText, br: QPalette::Highlight);
3901 QFont oldFont = p->font();
3902 if (subRule.hasFont)
3903 p->setFont(subRule.font.resolve(mi.font));
3904 else
3905 p->setFont(mi.font);
3906
3907 // We fall back to drawing with the style sheet code whenever at least one of the
3908 // items are styled in an incompatible way, such as having a background image.
3909 QRenderRule allRules = renderRule(obj: w, element: PseudoElement_Item, state: PseudoClass_Any);
3910
3911 if ((pseudo == PseudoElement_MenuSeparator) && subRule.hasDrawable()) {
3912 subRule.drawRule(p, rect: opt->rect);
3913 } else if ((pseudo == PseudoElement_Item)
3914 && (allRules.hasBox() || allRules.hasBorder() || subRule.hasFont
3915 || (allRules.background() && !allRules.background()->pixmap.isNull()))) {
3916 subRule.drawRule(p, rect: opt->rect);
3917 if (subRule.hasBackground()) {
3918 mi.palette.setBrush(acr: QPalette::Highlight, abrush: Qt::NoBrush);
3919 mi.palette.setBrush(acr: QPalette::Button, abrush: Qt::NoBrush);
3920 } else {
3921 mi.palette.setBrush(acr: QPalette::Highlight, abrush: mi.palette.brush(cr: QPalette::Button));
3922 }
3923 mi.palette.setBrush(acr: QPalette::HighlightedText, abrush: mi.palette.brush(cr: QPalette::ButtonText));
3924
3925 int textRectOffset = m->maxIconWidth;
3926 if (!mi.icon.isNull()) {
3927 renderMenuItemIcon(mi: &mi, p, w, rect: opt->rect, subRule);
3928 } else if (mi.menuHasCheckableItems) {
3929 const bool checkable = mi.checkType != QStyleOptionMenuItem::NotCheckable;
3930 const bool checked = checkable ? mi.checked : false;
3931
3932 const QRenderRule subSubRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_MenuCheckMark);
3933 const QRect cmRect = positionRect(w, rule1: subRule, rule2: subSubRule, pe: PseudoElement_MenuCheckMark, rect: opt->rect, dir: opt->direction);
3934 if (checkable && (subSubRule.hasDrawable() || checked)) {
3935 QStyleOptionMenuItem newMi = mi;
3936 if (opt->state & QStyle::State_Enabled)
3937 newMi.state |= State_Enabled;
3938 if (mi.checked)
3939 newMi.state |= State_On;
3940 newMi.rect = cmRect;
3941 drawPrimitive(pe: PE_IndicatorMenuCheckMark, opt: &newMi, p, w);
3942 }
3943 textRectOffset = std::max(a: textRectOffset, b: cmRect.width());
3944 }
3945
3946 QRect textRect = subRule.contentsRect(r: opt->rect);
3947 textRect.setLeft(textRect.left() + textRectOffset);
3948 textRect.setWidth(textRect.width() - mi.reservedShortcutWidth);
3949 const QRect vTextRect = visualRect(direction: opt->direction, boundingRect: m->rect, logicalRect: textRect);
3950
3951 QStringView s(mi.text);
3952 p->setPen(mi.palette.buttonText().color());
3953 if (!s.isEmpty()) {
3954 int text_flags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
3955 if (!styleHint(sh: SH_UnderlineShortcut, opt: &mi, w))
3956 text_flags |= Qt::TextHideMnemonic;
3957 qsizetype t = s.indexOf(c: u'\t');
3958 if (t >= 0) {
3959 QRect vShortcutRect = visualRect(direction: opt->direction, boundingRect: mi.rect,
3960 logicalRect: QRect(textRect.topRight(), QPoint(mi.rect.right(), textRect.bottom())));
3961 p->drawText(r: vShortcutRect, flags: text_flags, text: s.mid(pos: t + 1).toString());
3962 s = s.left(n: t);
3963 }
3964 p->drawText(r: vTextRect, flags: text_flags, text: s.left(n: t).toString());
3965 }
3966
3967 if (mi.menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
3968 PrimitiveElement arrow = (opt->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
3969 QRenderRule subRule2 = renderRule(obj: w, opt, pseudoElement: PseudoElement_MenuRightArrow);
3970 mi.rect = positionRect(w, rule1: subRule, rule2: subRule2, pe: PseudoElement_MenuRightArrow, rect: opt->rect, dir: mi.direction);
3971 drawPrimitive(pe: arrow, opt: &mi, p, w);
3972 }
3973 } else if (!mi.icon.isNull() && hasStyleRule(obj: w, part: PseudoElement_MenuIcon)) {
3974 // we wouldn't be here if the item itself would be styled, so now we only want
3975 // the text from the default style, and then draw the icon ourselves.
3976 QStyleOptionMenuItem newMi = mi;
3977 newMi.icon = {};
3978 newMi.checkType = QStyleOptionMenuItem::NotCheckable;
3979 if (rule.baseStyleCanDraw() && subRule.baseStyleCanDraw())
3980 baseStyle()->drawControl(element: ce, opt: &newMi, p, w);
3981 else
3982 ParentStyle::drawControl(element: ce, opt: &newMi, p, w);
3983 renderMenuItemIcon(mi: &mi, p, w, rect: opt->rect, subRule);
3984 } else if (hasStyleRule(obj: w, part: PseudoElement_MenuCheckMark) || hasStyleRule(obj: w, part: PseudoElement_MenuRightArrow)) {
3985 QWindowsStyle::drawControl(element: ce, opt: &mi, p, w);
3986 if (mi.checkType != QStyleOptionMenuItem::NotCheckable && !mi.checked) {
3987 // We have a style defined, but QWindowsStyle won't draw anything if not checked.
3988 // So we mimic what QWindowsStyle would do.
3989 int checkcol = qMax<int>(a: mi.maxIconWidth, b: QWindowsStylePrivate::windowsCheckMarkWidth);
3990 QRect vCheckRect = visualRect(direction: opt->direction, boundingRect: mi.rect, logicalRect: QRect(mi.rect.x(), mi.rect.y(), checkcol, mi.rect.height()));
3991 if (mi.state.testFlag(flag: State_Enabled) && mi.state.testFlag(flag: State_Selected)) {
3992 qDrawShadePanel(p, r: vCheckRect, pal: mi.palette, sunken: true, lineWidth: 1, fill: &mi.palette.brush(cr: QPalette::Button));
3993 } else {
3994 QBrush fill(mi.palette.light().color(), Qt::Dense4Pattern);
3995 qDrawShadePanel(p, r: vCheckRect, pal: mi.palette, sunken: true, lineWidth: 1, fill: &fill);
3996 }
3997 QRenderRule subSubRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_MenuCheckMark);
3998 if (subSubRule.hasDrawable()) {
3999 QStyleOptionMenuItem newMi(mi);
4000 newMi.rect = visualRect(direction: opt->direction, boundingRect: mi.rect, logicalRect: QRect(mi.rect.x() + QWindowsStylePrivate::windowsItemFrame,
4001 mi.rect.y() + QWindowsStylePrivate::windowsItemFrame,
4002 checkcol - 2 * QWindowsStylePrivate::windowsItemFrame,
4003 mi.rect.height() - 2 * QWindowsStylePrivate::windowsItemFrame));
4004 drawPrimitive(pe: PE_IndicatorMenuCheckMark, opt: &newMi, p, w);
4005 }
4006 }
4007 } else {
4008 if (rule.hasDrawable() && !subRule.hasDrawable() && !(opt->state & QStyle::State_Selected)) {
4009 mi.palette.setColor(acr: QPalette::Window, acolor: Qt::transparent);
4010 mi.palette.setColor(acr: QPalette::Button, acolor: Qt::transparent);
4011 }
4012 if (rule.baseStyleCanDraw() && subRule.baseStyleCanDraw()) {
4013 baseStyle()->drawControl(element: ce, opt: &mi, p, w);
4014 } else {
4015 ParentStyle::drawControl(element: ce, opt: &mi, p, w);
4016 }
4017 }
4018
4019 p->setFont(oldFont);
4020
4021 return;
4022 }
4023 return;
4024
4025 case CE_MenuBarItem:
4026 if (const QStyleOptionMenuItem *m = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
4027 QStyleOptionMenuItem mi(*m);
4028 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_Item);
4029 mi.rect = subRule.contentsRect(r: opt->rect);
4030 rule.configurePalette(p: &mi.palette, fr: QPalette::ButtonText, br: QPalette::Button);
4031 subRule.configurePalette(p: &mi.palette, fr: QPalette::ButtonText, br: QPalette::Button);
4032
4033 if (subRule.hasDrawable()) {
4034 subRule.drawRule(p, rect: opt->rect);
4035 QCommonStyle::drawControl(element: ce, opt: &mi, p, w); // deliberate bypass of the base
4036 } else {
4037 if (rule.hasDrawable() && !(opt->state & QStyle::State_Selected)) {
4038 // So that the menu bar background is not hidden by the items
4039 mi.palette.setColor(acr: QPalette::Window, acolor: Qt::transparent);
4040 mi.palette.setColor(acr: QPalette::Button, acolor: Qt::transparent);
4041 }
4042 baseStyle()->drawControl(element: ce, opt: &mi, p, w);
4043 }
4044 }
4045 return;
4046
4047#if QT_CONFIG(combobox)
4048 case CE_ComboBoxLabel:
4049 if (!rule.hasBox())
4050 break;
4051 if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
4052 QRect editRect = subControlRect(cc: CC_ComboBox, opt: cb, sc: SC_ComboBoxEditField, w);
4053 p->save();
4054 p->setClipRect(editRect);
4055 if (!cb->currentIcon.isNull()) {
4056 int spacing = rule.hasBox() ? rule.box()->spacing : -1;
4057 if (spacing == -1)
4058 spacing = 6;
4059 QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
4060 QPixmap pixmap = cb->currentIcon.pixmap(size: cb->iconSize, mode);
4061 QRect iconRect(editRect);
4062 iconRect.setWidth(cb->iconSize.width());
4063 iconRect = alignedRect(direction: cb->direction,
4064 alignment: Qt::AlignLeft | Qt::AlignVCenter,
4065 size: iconRect.size(), rectangle: editRect);
4066 drawItemPixmap(painter: p, rect: iconRect, alignment: Qt::AlignCenter, pixmap);
4067
4068 if (cb->direction == Qt::RightToLeft)
4069 editRect.translate(dx: -spacing - cb->iconSize.width(), dy: 0);
4070 else
4071 editRect.translate(dx: cb->iconSize.width() + spacing, dy: 0);
4072 }
4073 if (!cb->currentText.isEmpty() && !cb->editable) {
4074 QPalette styledPalette(cb->palette);
4075 rule.configurePalette(p: &styledPalette, fr: QPalette::Text, br: QPalette::Base);
4076 drawItemText(painter: p, rect: editRect.adjusted(xp1: 0, yp1: 0, xp2: 0, yp2: 0), alignment: cb->textAlignment, pal: styledPalette,
4077 enabled: cb->state & State_Enabled, text: cb->currentText, textRole: QPalette::Text);
4078 }
4079 p->restore();
4080 return;
4081 }
4082 break;
4083#endif // QT_CONFIG(combobox)
4084
4085 case CE_Header:
4086 if (hasStyleRule(obj: w, part: PseudoElement_HeaderViewUpArrow)
4087 || hasStyleRule(obj: w, part: PseudoElement_HeaderViewDownArrow)) {
4088 ParentStyle::drawControl(element: ce, opt, p, w);
4089 return;
4090 }
4091 if (hasStyleRule(obj: w, part: PseudoElement_HeaderViewSection)) {
4092 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_HeaderViewSection);
4093 if (!subRule.hasNativeBorder() || !subRule.baseStyleCanDraw()
4094 || subRule.hasBackground() || subRule.hasPalette() || subRule.hasFont || subRule.hasBorder()) {
4095 ParentStyle::drawControl(element: ce, opt, p, w);
4096 return;
4097 }
4098 }
4099 break;
4100 case CE_HeaderSection:
4101 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
4102 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_HeaderViewSection);
4103 if (subRule.hasNativeBorder()) {
4104 QStyleOptionHeader hdr(*header);
4105 subRule.configurePalette(p: &hdr.palette, fr: QPalette::ButtonText, br: QPalette::Button);
4106
4107 if (subRule.baseStyleCanDraw()) {
4108 baseStyle()->drawControl(element: CE_HeaderSection, opt: &hdr, p, w);
4109 } else {
4110 QWindowsStyle::drawControl(element: CE_HeaderSection, opt: &hdr, p, w);
4111 }
4112 } else {
4113 subRule.drawRule(p, rect: opt->rect);
4114 }
4115 return;
4116 }
4117 break;
4118
4119 case CE_HeaderLabel:
4120 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
4121 QStyleOptionHeaderV2 hdr;
4122 QStyleOptionHeader &v1Copy = hdr;
4123 if (auto v2 = qstyleoption_cast<const QStyleOptionHeaderV2 *>(opt))
4124 hdr = *v2;
4125 else
4126 v1Copy = *header;
4127 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_HeaderViewSection);
4128 if (hasStyleRule(obj: w, part: PseudoElement_HeaderViewUpArrow)
4129 || hasStyleRule(obj: w, part: PseudoElement_HeaderViewDownArrow)) {
4130 const QRect arrowRect = subElementRect(r: SE_HeaderArrow, opt, widget: w);
4131 if (hdr.orientation == Qt::Horizontal)
4132 hdr.rect.setWidth(hdr.rect.width() - arrowRect.width());
4133 else
4134 hdr.rect.setHeight(hdr.rect.height() - arrowRect.height());
4135 }
4136 subRule.configurePalette(p: &hdr.palette, fr: QPalette::ButtonText, br: QPalette::Button);
4137 if (subRule.hasFont) {
4138 QFont oldFont = p->font();
4139 p->setFont(subRule.font.resolve(p->font()));
4140 ParentStyle::drawControl(element: ce, opt: &hdr, p, w);
4141 p->setFont(oldFont);
4142 } else {
4143 baseStyle()->drawControl(element: ce, opt: &hdr, p, w);
4144 }
4145 return;
4146 }
4147 break;
4148
4149 case CE_HeaderEmptyArea:
4150 if (rule.hasDrawable()) {
4151 return;
4152 }
4153 break;
4154
4155 case CE_ProgressBar:
4156 QWindowsStyle::drawControl(element: ce, opt, p, w);
4157 return;
4158
4159 case CE_ProgressBarGroove:
4160 if (!rule.hasNativeBorder()) {
4161 rule.drawRule(p, rect: rule.boxRect(cr: opt->rect, flags: Margin));
4162 return;
4163 }
4164 break;
4165
4166 case CE_ProgressBarContents: {
4167 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ProgressBarChunk);
4168 if (subRule.hasDrawable()) {
4169 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
4170 p->save();
4171 p->setClipRect(pb->rect);
4172
4173 qint64 minimum = qint64(pb->minimum);
4174 qint64 maximum = qint64(pb->maximum);
4175 qint64 progress = qint64(pb->progress);
4176 bool vertical = !(pb->state & QStyle::State_Horizontal);
4177 bool inverted = pb->invertedAppearance;
4178
4179 QTransform m;
4180 QRect rect = pb->rect;
4181 if (vertical) {
4182 rect = QRect(rect.y(), rect.x(), rect.height(), rect.width());
4183 m.rotate(a: 90);
4184 m.translate(dx: 0, dy: -(rect.height() + rect.y()*2));
4185 }
4186
4187 bool reverse = ((!vertical && (pb->direction == Qt::RightToLeft)) || vertical);
4188 if (inverted)
4189 reverse = !reverse;
4190 const bool indeterminate = pb->minimum == pb->maximum;
4191 const auto fillRatio = indeterminate ? 0.50 : double(progress - minimum) / (maximum - minimum);
4192 const auto fillWidth = static_cast<int>(rect.width() * fillRatio);
4193 int chunkWidth = fillWidth;
4194 if (subRule.hasContentsSize()) {
4195 QSize sz = subRule.size();
4196 chunkWidth = (opt->state & QStyle::State_Horizontal) ? sz.width() : sz.height();
4197 }
4198
4199 QRect r = rect;
4200#if QT_CONFIG(animation)
4201 Q_D(const QWindowsStyle);
4202#endif
4203 if (pb->minimum == 0 && pb->maximum == 0) {
4204 int chunkCount = fillWidth/chunkWidth;
4205 int offset = 0;
4206#if QT_CONFIG(animation)
4207 if (QProgressStyleAnimation *animation = qobject_cast<QProgressStyleAnimation*>(object: d->animation(target: opt->styleObject)))
4208 offset = animation->animationStep() * 8 % rect.width();
4209 else
4210 d->startAnimation(animation: new QProgressStyleAnimation(d->animationFps, opt->styleObject));
4211#endif
4212 int x = reverse ? r.left() + r.width() - offset - chunkWidth : r.x() + offset;
4213 while (chunkCount > 0) {
4214 r.setRect(ax: x, ay: rect.y(), aw: chunkWidth, ah: rect.height());
4215 r = m.mapRect(QRectF(r)).toRect();
4216 subRule.drawRule(p, rect: r);
4217 x += reverse ? -chunkWidth : chunkWidth;
4218 if (reverse ? x < rect.left() : x > rect.right())
4219 break;
4220 --chunkCount;
4221 }
4222
4223 r = rect;
4224 x = reverse ? r.right() - (r.left() - x - chunkWidth)
4225 : r.left() + (x - r.right() - chunkWidth);
4226 while (chunkCount > 0) {
4227 r.setRect(ax: x, ay: rect.y(), aw: chunkWidth, ah: rect.height());
4228 r = m.mapRect(QRectF(r)).toRect();
4229 subRule.drawRule(p, rect: r);
4230 x += reverse ? -chunkWidth : chunkWidth;
4231 --chunkCount;
4232 };
4233 } else if (chunkWidth > 0) {
4234 const auto ceil = [](qreal x) { return int(x) + (x > 0 && x != int(x)); };
4235 const int chunkCount = ceil(qreal(fillWidth)/chunkWidth);
4236 int x = reverse ? r.left() + r.width() - chunkWidth : r.x();
4237
4238 for (int i = 0; i < chunkCount; ++i) {
4239 r.setRect(ax: x, ay: rect.y(), aw: chunkWidth, ah: rect.height());
4240 r = m.mapRect(QRectF(r)).toRect();
4241 subRule.drawRule(p, rect: r);
4242 x += reverse ? -chunkWidth : chunkWidth;
4243 }
4244#if QT_CONFIG(animation)
4245 d->stopAnimation(target: opt->styleObject);
4246#endif
4247 }
4248
4249 p->restore();
4250 return;
4251 }
4252 }
4253 }
4254 break;
4255
4256 case CE_ProgressBarLabel:
4257 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
4258 if (rule.hasBox() || rule.hasBorder() || hasStyleRule(obj: w, part: PseudoElement_ProgressBarChunk)) {
4259 drawItemText(painter: p, rect: pb->rect, alignment: pb->textAlignment | Qt::TextSingleLine, pal: pb->palette,
4260 enabled: pb->state & State_Enabled, text: pb->text, textRole: QPalette::Text);
4261 } else {
4262 QStyleOptionProgressBar pbCopy(*pb);
4263 rule.configurePalette(p: &pbCopy.palette, fr: QPalette::HighlightedText, br: QPalette::Highlight);
4264 baseStyle()->drawControl(element: ce, opt: &pbCopy, p, w);
4265 }
4266 return;
4267 }
4268 break;
4269
4270 case CE_SizeGrip:
4271 if (const QStyleOptionSizeGrip *sgOpt = qstyleoption_cast<const QStyleOptionSizeGrip *>(opt)) {
4272 if (rule.hasDrawable()) {
4273 rule.drawFrame(p, rect: opt->rect);
4274 p->save();
4275 static constexpr int rotation[] = { 180, 270, 90, 0 };
4276 if (rotation[sgOpt->corner]) {
4277 p->translate(offset: opt->rect.center());
4278 p->rotate(a: rotation[sgOpt->corner]);
4279 p->translate(offset: -opt->rect.center());
4280 }
4281 rule.drawImage(p, rect: opt->rect);
4282 p->restore();
4283 } else {
4284 QStyleOptionSizeGrip sg(*sgOpt);
4285 sg.rect = rule.contentsRect(r: opt->rect);
4286 baseStyle()->drawControl(element: CE_SizeGrip, opt: &sg, p, w);
4287 }
4288 return;
4289 }
4290 break;
4291
4292 case CE_ToolBoxTab:
4293 QWindowsStyle::drawControl(element: ce, opt, p, w);
4294 return;
4295
4296 case CE_ToolBoxTabShape: {
4297 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolBoxTab);
4298 if (subRule.hasDrawable()) {
4299 subRule.drawRule(p, rect: opt->rect);
4300 return;
4301 }
4302 }
4303 break;
4304
4305 case CE_ToolBoxTabLabel:
4306 if (const QStyleOptionToolBox *box = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) {
4307 QStyleOptionToolBox boxCopy(*box);
4308 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolBoxTab);
4309 subRule.configurePalette(p: &boxCopy.palette, fr: QPalette::ButtonText, br: QPalette::Button);
4310 QFont oldFont = p->font();
4311 if (subRule.hasFont)
4312 p->setFont(subRule.font.resolve(p->font()));
4313 boxCopy.rect = subRule.contentsRect(r: opt->rect);
4314 if (subRule.hasImage()) {
4315 // the image is already drawn with CE_ToolBoxTabShape, adjust rect here
4316 const int iconExtent = proxy()->pixelMetric(metric: QStyle::PM_SmallIconSize, option: box, widget: w);
4317 boxCopy.rect.setLeft(boxCopy.rect.left() + iconExtent);
4318 }
4319 QWindowsStyle::drawControl(element: ce, opt: &boxCopy, p , w);
4320 if (subRule.hasFont)
4321 p->setFont(oldFont);
4322 return;
4323 }
4324 break;
4325
4326 case CE_ScrollBarAddPage:
4327 pe1 = PseudoElement_ScrollBarAddPage;
4328 break;
4329
4330 case CE_ScrollBarSubPage:
4331 pe1 = PseudoElement_ScrollBarSubPage;
4332 break;
4333
4334 case CE_ScrollBarAddLine:
4335 pe1 = PseudoElement_ScrollBarAddLine;
4336 pe2 = (opt->state & QStyle::State_Horizontal) ? PseudoElement_ScrollBarRightArrow : PseudoElement_ScrollBarDownArrow;
4337 fallback = true;
4338 break;
4339
4340 case CE_ScrollBarSubLine:
4341 pe1 = PseudoElement_ScrollBarSubLine;
4342 pe2 = (opt->state & QStyle::State_Horizontal) ? PseudoElement_ScrollBarLeftArrow : PseudoElement_ScrollBarUpArrow;
4343 fallback = true;
4344 break;
4345
4346 case CE_ScrollBarFirst:
4347 pe1 = PseudoElement_ScrollBarFirst;
4348 break;
4349
4350 case CE_ScrollBarLast:
4351 pe1 = PseudoElement_ScrollBarLast;
4352 break;
4353
4354 case CE_ScrollBarSlider:
4355 pe1 = PseudoElement_ScrollBarSlider;
4356 fallback = true;
4357 break;
4358
4359#if QT_CONFIG(itemviews)
4360 case CE_ItemViewItem:
4361 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
4362 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ViewItem);
4363 QStyleOptionViewItem optCopy(*vopt);
4364 if (subRule.hasDrawable()) {
4365 subRule.configurePalette(p: &optCopy.palette, fr: vopt->state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text,
4366 br: vopt->state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base);
4367 QWindowsStyle::drawControl(element: ce, opt: &optCopy, p, w);
4368 } else {
4369 p->save();
4370 if (hasStyleRule(obj: w, part: PseudoElement_Indicator)) {
4371 // there is a rule for the indicator, but no rule for the item itself (otherwise
4372 // the previous path would have been taken); only draw the indicator using the
4373 // rule (via QWindows/QCommonStyle), then let the base style handle the rest.
4374 QStyleOptionViewItem optIndicator(*vopt);
4375 subRule.configurePalette(p: &optIndicator.palette,
4376 fr: vopt->state & QStyle::State_Selected
4377 ? QPalette::HighlightedText
4378 : QPalette::Text,
4379 br: vopt->state & QStyle::State_Selected
4380 ? QPalette::Highlight
4381 : QPalette::Base);
4382 // only draw the indicator; no text or background
4383 optIndicator.backgroundBrush = Qt::NoBrush; // no background
4384 optIndicator.text.clear();
4385 QWindowsStyle::drawControl(element: ce, opt: &optIndicator, p, w);
4386
4387 // If the indicator has an icon, it has been drawn now.
4388 // => don't draw it again.
4389 optCopy.icon = QIcon();
4390
4391 // Now draw text, background, and highlight, but not the indicator with the
4392 // base style. Since we can't turn off HasCheckIndicator to prevent the base
4393 // style from drawing the check indicator again (it would change how the item
4394 // gets laid out) we have to clip the indicator that's already been painted.
4395 const QRect checkRect = subElementRect(r: QStyle::SE_ItemViewItemCheckIndicator,
4396 opt: &optIndicator, widget: w);
4397 const QRegion clipRegion = QRegion(p->hasClipping() ? p->clipRegion()
4398 : QRegion(optIndicator.rect))
4399 - checkRect;
4400 p->setClipRegion(clipRegion);
4401 }
4402 subRule.configurePalette(p: &optCopy.palette, fr: QPalette::Text, br: QPalette::NoRole);
4403 baseStyle()->drawControl(element: ce, opt: &optCopy, p, w);
4404 p->restore();
4405 }
4406 return;
4407 }
4408 break;
4409#endif // QT_CONFIG(itemviews)
4410
4411#if QT_CONFIG(tabbar)
4412 case CE_TabBarTab:
4413 if (hasStyleRule(obj: w, part: PseudoElement_TabBarTab)) {
4414 QWindowsStyle::drawControl(element: ce, opt, p, w);
4415 return;
4416 }
4417 break;
4418
4419 case CE_TabBarTabLabel:
4420 case CE_TabBarTabShape:
4421 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
4422 const auto foregroundRole = w ? w->foregroundRole() : QPalette::WindowText;
4423 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabBarTab);
4424 QRect r = positionRect(w, rule2: subRule, pe: PseudoElement_TabBarTab, originRect: opt->rect, dir: opt->direction);
4425 if (ce == CE_TabBarTabShape && subRule.hasDrawable() && tab->shape < QTabBar::TriangularNorth) {
4426 subRule.drawRule(p, rect: r);
4427 return;
4428 }
4429 QStyleOptionTab tabCopy(*tab);
4430 subRule.configurePalette(p: &tabCopy.palette, fr: foregroundRole, br: QPalette::Base);
4431 QFont oldFont = p->font();
4432 if (subRule.hasFont)
4433 p->setFont(subRule.font.resolve(p->font()));
4434 if (subRule.hasBox() || !subRule.hasNativeBorder()) {
4435 tabCopy.rect = ce == CE_TabBarTabShape ? subRule.borderRect(r)
4436 : subRule.contentsRect(r);
4437 QWindowsStyle::drawControl(element: ce, opt: &tabCopy, p, w);
4438 } else {
4439 baseStyle()->drawControl(element: ce, opt: &tabCopy, p, w);
4440 }
4441 if (subRule.hasFont)
4442 p->setFont(oldFont);
4443
4444 return;
4445 }
4446 break;
4447#endif // QT_CONFIG(tabbar)
4448
4449 case CE_ColumnViewGrip:
4450 if (rule.hasDrawable()) {
4451 rule.drawRule(p, rect: opt->rect);
4452 return;
4453 }
4454 break;
4455
4456 case CE_DockWidgetTitle:
4457 if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
4458 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_DockWidgetTitle);
4459 if (!subRule.hasDrawable() && !subRule.hasPosition())
4460 break;
4461 if (subRule.hasDrawable()) {
4462 subRule.drawRule(p, rect: opt->rect);
4463 } else {
4464 QStyleOptionDockWidget dwCopy(*dwOpt);
4465 dwCopy.title = QString();
4466 baseStyle()->drawControl(element: ce, opt: &dwCopy, p, w);
4467 }
4468
4469 if (!dwOpt->title.isEmpty()) {
4470 QRect r = subElementRect(r: SE_DockWidgetTitleBarText, opt, widget: w);
4471 if (dwOpt->verticalTitleBar) {
4472 r = r.transposed();
4473 p->save();
4474 p->translate(dx: r.left(), dy: r.top() + r.width());
4475 p->rotate(a: -90);
4476 p->translate(dx: -r.left(), dy: -r.top());
4477 }
4478 r = subRule.contentsRect(r);
4479
4480 Qt::Alignment alignment;
4481 if (subRule.hasPosition())
4482 alignment = subRule.position()->textAlignment;
4483 if (alignment == 0)
4484 alignment = Qt::AlignLeft;
4485
4486 QString titleText = p->fontMetrics().elidedText(text: dwOpt->title, mode: Qt::ElideRight, width: r.width());
4487 drawItemText(painter: p, rect: r,
4488 alignment, pal: dwOpt->palette,
4489 enabled: dwOpt->state & State_Enabled, text: titleText,
4490 textRole: QPalette::WindowText);
4491
4492 if (dwOpt->verticalTitleBar)
4493 p->restore();
4494 }
4495
4496 return;
4497 }
4498 break;
4499 case CE_ShapedFrame:
4500 if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
4501 if (rule.hasNativeBorder()) {
4502 QStyleOptionFrame frmOpt(*frm);
4503 rule.configurePalette(p: &frmOpt.palette, fr: QPalette::Text, br: QPalette::Base);
4504 frmOpt.rect = rule.borderRect(r: frmOpt.rect);
4505 baseStyle()->drawControl(element: ce, opt: &frmOpt, p, w);
4506 }
4507 // else, borders are already drawn in PE_Widget
4508 }
4509 return;
4510
4511
4512 default:
4513 break;
4514 }
4515
4516 if (pe1 != PseudoElement_None) {
4517 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: pe1);
4518 if (subRule.bg != nullptr || subRule.hasDrawable()) {
4519 //We test subRule.bg directly because hasBackground() would return false for background:none.
4520 //But we still don't want the default drawning in that case (example for QScrollBar::add-page) (task 198926)
4521 subRule.drawRule(p, rect: opt->rect);
4522 } else if (fallback) {
4523 QWindowsStyle::drawControl(element: ce, opt, p, w);
4524 pe2 = PseudoElement_None;
4525 } else {
4526 baseStyle()->drawControl(element: ce, opt, p, w);
4527 }
4528 if (pe2 != PseudoElement_None) {
4529 QRenderRule subSubRule = renderRule(obj: w, opt, pseudoElement: pe2);
4530 QRect r = positionRect(w, rule1: subRule, rule2: subSubRule, pe: pe2, rect: opt->rect, dir: opt->direction);
4531 subSubRule.drawRule(p, rect: r);
4532 }
4533 return;
4534 }
4535
4536 baseStyle()->drawControl(element: ce, opt, p, w);
4537}
4538
4539void QStyleSheetStyle::drawItemPixmap(QPainter *p, const QRect &rect, int alignment, const
4540 QPixmap &pixmap) const
4541{
4542 baseStyle()->drawItemPixmap(painter: p, rect, alignment, pixmap);
4543}
4544
4545void QStyleSheetStyle::drawItemText(QPainter *painter, const QRect& rect, int alignment, const QPalette &pal,
4546 bool enabled, const QString& text, QPalette::ColorRole textRole) const
4547{
4548 baseStyle()->drawItemText(painter, rect, flags: alignment, pal, enabled, text, textRole);
4549}
4550
4551void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
4552 const QWidget *w) const
4553{
4554 RECURSION_GUARD(baseStyle()->drawPrimitive(pe, opt, p, w); return)
4555
4556 int pseudoElement = PseudoElement_None;
4557 QRenderRule rule = renderRule(obj: w, opt);
4558 QRect rect = opt->rect;
4559
4560 switch (pe) {
4561
4562 case PE_FrameStatusBarItem: {
4563 QRenderRule subRule = renderRule(obj: w ? w->parentWidget() : nullptr, opt, pseudoElement: PseudoElement_Item);
4564 if (subRule.hasDrawable()) {
4565 subRule.drawRule(p, rect: opt->rect);
4566 return;
4567 }
4568 break;
4569 }
4570
4571 case PE_IndicatorArrowDown:
4572 pseudoElement = PseudoElement_DownArrow;
4573 break;
4574
4575 case PE_IndicatorArrowUp:
4576 pseudoElement = PseudoElement_UpArrow;
4577 break;
4578
4579 case PE_IndicatorRadioButton:
4580 pseudoElement = PseudoElement_ExclusiveIndicator;
4581 break;
4582
4583 case PE_IndicatorItemViewItemCheck:
4584 pseudoElement = PseudoElement_ViewItemIndicator;
4585 break;
4586
4587 case PE_IndicatorCheckBox:
4588 pseudoElement = PseudoElement_Indicator;
4589 break;
4590
4591 case PE_IndicatorHeaderArrow:
4592 if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
4593 pseudoElement = hdr->sortIndicator == QStyleOptionHeader::SortUp
4594 ? PseudoElement_HeaderViewUpArrow
4595 : PseudoElement_HeaderViewDownArrow;
4596 }
4597 break;
4598
4599 case PE_PanelButtonTool:
4600 case PE_PanelButtonCommand:
4601#if QT_CONFIG(abstractbutton)
4602 if (qobject_cast<const QAbstractButton *>(object: w) && rule.hasBackground() && rule.hasNativeBorder()) {
4603 //the window style will draw the borders
4604 ParentStyle::drawPrimitive(pe, opt, p, w);
4605 if (!rule.background()->pixmap.isNull() || rule.hasImage()) {
4606 rule.drawRule(p, rect: rule.boxRect(cr: opt->rect, flags: QRenderRule::Margin).adjusted(xp1: 1,yp1: 1,xp2: -1,yp2: -1));
4607 }
4608 return;
4609 }
4610#endif
4611 if (!rule.hasNativeBorder()) {
4612 rule.drawRule(p, rect: rule.boxRect(cr: opt->rect, flags: QRenderRule::Margin));
4613 return;
4614 }
4615 break;
4616
4617 case PE_IndicatorButtonDropDown: {
4618 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolButtonMenu);
4619 if (!subRule.hasNativeBorder()) {
4620 rule.drawBorder(p, rect: opt->rect);
4621 return;
4622 }
4623 break;
4624 }
4625
4626 case PE_FrameDefaultButton:
4627 if (rule.hasNativeBorder()) {
4628 if (rule.baseStyleCanDraw())
4629 break;
4630 QWindowsStyle::drawPrimitive(pe, opt, p, w);
4631 }
4632 return;
4633
4634 case PE_FrameWindow:
4635 case PE_FrameDockWidget:
4636 case PE_Frame:
4637 if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
4638 if (rule.hasNativeBorder()) {
4639 QStyleOptionFrame frmOpt(*frm);
4640 rule.configurePalette(p: &frmOpt.palette, fr: QPalette::Text, br: QPalette::Base);
4641 baseStyle()->drawPrimitive(pe, opt: &frmOpt, p, w);
4642 } else {
4643 rule.drawBorder(p, rect: rule.borderRect(r: opt->rect));
4644 }
4645 }
4646 return;
4647
4648 case PE_PanelLineEdit:
4649 if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
4650 // Fall back to container widget's render rule
4651 if (w) {
4652 if (QWidget *container = containerWidget(w); container != w) {
4653 QRenderRule containerRule = renderRule(obj: container, opt);
4654 if (!containerRule.hasNativeBorder() || !containerRule.baseStyleCanDraw())
4655 return;
4656 rule = containerRule;
4657 }
4658 }
4659
4660 if (rule.hasNativeBorder()) {
4661 QStyleOptionFrame frmOpt(*frm);
4662 rule.configurePalette(p: &frmOpt.palette, fr: QPalette::Text, br: QPalette::Base);
4663 frmOpt.rect = rule.borderRect(r: frmOpt.rect);
4664 if (rule.baseStyleCanDraw()) {
4665 rule.drawBackgroundImage(p, rect: opt->rect);
4666 baseStyle()->drawPrimitive(pe, opt: &frmOpt, p, w);
4667 } else {
4668 rule.drawBackground(p, rect: opt->rect);
4669 if (frmOpt.lineWidth > 0)
4670 baseStyle()->drawPrimitive(pe: PE_FrameLineEdit, opt: &frmOpt, p, w);
4671 }
4672 } else {
4673 rule.drawRule(p, rect: opt->rect);
4674 }
4675 }
4676 return;
4677
4678 case PE_Widget:
4679 if (w && !rule.hasDrawable()) {
4680 QWidget *container = containerWidget(w);
4681 if (styleSheetCaches->autoFillDisabledWidgets.contains(value: container)
4682 && (container == w || !renderRule(obj: container, opt).hasBackground())) {
4683 //we do not have a background, but we disabled the autofillbackground anyway. so fill the background now.
4684 // (this may happen if we have rules like :focus)
4685 p->fillRect(opt->rect, opt->palette.brush(cr: w->backgroundRole()));
4686 }
4687 break;
4688 }
4689#if QT_CONFIG(scrollarea)
4690 if (const QAbstractScrollArea *sa = qobject_cast<const QAbstractScrollArea *>(object: w)) {
4691 const QAbstractScrollAreaPrivate *sap = sa->d_func();
4692 rule.drawBackground(p, rect: opt->rect, off: sap->contentsOffset());
4693 if (rule.hasBorder()) {
4694 QRect brect = rule.borderRect(r: opt->rect);
4695 if (styleHint(sh: QStyle::SH_ScrollView_FrameOnlyAroundContents, opt, w)) {
4696 QRect r = brect.adjusted(xp1: 0, yp1: 0, xp2: sa->verticalScrollBar()->isVisible() ? -sa->verticalScrollBar()->width() : 0,
4697 yp2: sa->horizontalScrollBar()->isVisible() ? -sa->horizontalScrollBar()->height() : 0);
4698 brect = QStyle::visualRect(direction: opt->direction, boundingRect: brect, logicalRect: r);
4699 }
4700 rule.drawBorder(p, rect: brect);
4701 }
4702 break;
4703 }
4704#endif
4705 Q_FALLTHROUGH();
4706 case PE_PanelMenu:
4707 case PE_PanelStatusBar:
4708 if (rule.hasDrawable()) {
4709 rule.drawRule(p, rect: opt->rect);
4710 return;
4711 }
4712 break;
4713
4714 case PE_FrameMenu:
4715 if (rule.hasDrawable()) {
4716 // Drawn by PE_PanelMenu
4717 return;
4718 }
4719 break;
4720
4721 case PE_PanelMenuBar:
4722 if (rule.hasDrawable()) {
4723 // Drawn by PE_Widget
4724 return;
4725 }
4726 break;
4727
4728 case PE_IndicatorToolBarSeparator:
4729 case PE_IndicatorToolBarHandle: {
4730 PseudoElement ps = pe == PE_IndicatorToolBarHandle ? PseudoElement_ToolBarHandle : PseudoElement_ToolBarSeparator;
4731 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: ps);
4732 if (subRule.hasDrawable()) {
4733 subRule.drawRule(p, rect: opt->rect);
4734 return;
4735 }
4736 }
4737 break;
4738
4739 case PE_IndicatorMenuCheckMark:
4740 pseudoElement = PseudoElement_MenuCheckMark;
4741 break;
4742
4743 case PE_IndicatorArrowLeft:
4744 pseudoElement = PseudoElement_LeftArrow;
4745 break;
4746
4747 case PE_IndicatorArrowRight:
4748 pseudoElement = PseudoElement_RightArrow;
4749 break;
4750
4751 case PE_IndicatorColumnViewArrow:
4752#if QT_CONFIG(itemviews)
4753 if (const QStyleOptionViewItem *viewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
4754 bool reverse = (viewOpt->direction == Qt::RightToLeft);
4755 pseudoElement = reverse ? PseudoElement_LeftArrow : PseudoElement_RightArrow;
4756 } else
4757#endif
4758 {
4759 pseudoElement = PseudoElement_RightArrow;
4760 }
4761 break;
4762
4763#if QT_CONFIG(itemviews)
4764 case PE_IndicatorBranch:
4765 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
4766 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TreeViewBranch);
4767 if (subRule.hasDrawable()) {
4768 proxy()->drawPrimitive(pe: PE_PanelItemViewRow, opt: vopt, p, w);
4769 subRule.drawRule(p, rect: opt->rect);
4770 } else {
4771 baseStyle()->drawPrimitive(pe, opt: vopt, p, w);
4772 }
4773 }
4774 return;
4775#endif // QT_CONFIG(itemviews)
4776
4777 case PE_PanelTipLabel:
4778 if (!rule.hasDrawable())
4779 break;
4780
4781 if (const QStyleOptionFrame *frmOpt = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
4782 if (rule.hasNativeBorder()) {
4783 rule.drawBackground(p, rect: opt->rect);
4784 QStyleOptionFrame optCopy(*frmOpt);
4785 optCopy.rect = rule.borderRect(r: opt->rect);
4786 optCopy.palette.setBrush(acr: QPalette::Window, abrush: Qt::NoBrush); // oh dear
4787 baseStyle()->drawPrimitive(pe, opt: &optCopy, p, w);
4788 } else {
4789 rule.drawRule(p, rect: opt->rect);
4790 }
4791 }
4792 return;
4793
4794 case PE_FrameGroupBox:
4795 if (rule.hasNativeBorder())
4796 break;
4797 rule.drawBorder(p, rect: opt->rect);
4798 return;
4799
4800#if QT_CONFIG(tabwidget)
4801 case PE_FrameTabWidget:
4802 if (const QStyleOptionTabWidgetFrame *frm = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
4803 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabWidgetPane);
4804 if (subRule.hasNativeBorder()) {
4805 subRule.drawBackground(p, rect: opt->rect);
4806 QStyleOptionTabWidgetFrame frmCopy(*frm);
4807 subRule.configurePalette(p: &frmCopy.palette, fr: QPalette::WindowText, br: QPalette::Window);
4808 baseStyle()->drawPrimitive(pe, opt: &frmCopy, p, w);
4809 } else {
4810 subRule.drawRule(p, rect: opt->rect);
4811 }
4812 return;
4813 }
4814 break;
4815#endif // QT_CONFIG(tabwidget)
4816
4817 case PE_IndicatorProgressChunk:
4818 pseudoElement = PseudoElement_ProgressBarChunk;
4819 break;
4820
4821 case PE_IndicatorTabTear:
4822 pseudoElement = PseudoElement_TabBarTear;
4823 break;
4824
4825 case PE_FrameFocusRect:
4826 if (!rule.hasNativeOutline()) {
4827 rule.drawOutline(p, rect: opt->rect);
4828 return;
4829 }
4830 break;
4831
4832 case PE_IndicatorDockWidgetResizeHandle:
4833 pseudoElement = PseudoElement_DockWidgetSeparator;
4834 break;
4835
4836 case PE_PanelItemViewRow:
4837 // For compatibility reasons, QTreeView draws different parts of
4838 // the background of an item row separately, before calling the
4839 // delegate to draw the item. The row background of an item is
4840 // however not separately styleable through a style sheet, but
4841 // only indirectly through the background of the item. To get the
4842 // same background for all parts drawn by QTreeView, we have to
4843 // use the background rule for the item here.
4844 if (renderRule(obj: w, opt, pseudoElement: PseudoElement_ViewItem).hasBackground())
4845 pseudoElement = PseudoElement_ViewItem;
4846 break;
4847 case PE_PanelItemViewItem:
4848 pseudoElement = PseudoElement_ViewItem;
4849 break;
4850
4851 case PE_PanelScrollAreaCorner:
4852 pseudoElement = PseudoElement_ScrollAreaCorner;
4853 break;
4854
4855 case PE_IndicatorSpinDown:
4856 case PE_IndicatorSpinMinus:
4857 pseudoElement = PseudoElement_SpinBoxDownArrow;
4858 break;
4859
4860 case PE_IndicatorSpinUp:
4861 case PE_IndicatorSpinPlus:
4862 pseudoElement = PseudoElement_SpinBoxUpArrow;
4863 break;
4864#if QT_CONFIG(tabbar)
4865 case PE_IndicatorTabClose:
4866 if (w) {
4867 // QMacStyle needs a real widget, not its parent - to implement
4868 // 'document mode' properly, drawing nothing if a tab is not hovered.
4869 baseStyle()->setProperty(name: "_q_styleSheetRealCloseButton", value: QVariant::fromValue(value: (void *)w));
4870 w = w->parentWidget(); //match on the QTabBar instead of the CloseButton
4871 }
4872 pseudoElement = PseudoElement_TabBarTabCloseButton;
4873#endif
4874
4875 default:
4876 break;
4877 }
4878
4879 if (pseudoElement != PseudoElement_None) {
4880 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement);
4881 if (subRule.hasDrawable()) {
4882 subRule.drawRule(p, rect);
4883 } else {
4884 baseStyle()->drawPrimitive(pe, opt, p, w);
4885 }
4886 } else {
4887 baseStyle()->drawPrimitive(pe, opt, p, w);
4888 }
4889
4890 if (baseStyle()->property(name: "_q_styleSheetRealCloseButton").toBool())
4891 baseStyle()->setProperty(name: "_q_styleSheetRealCloseButton", value: QVariant());
4892}
4893
4894QPixmap QStyleSheetStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap& pixmap,
4895 const QStyleOption *option) const
4896{
4897 return baseStyle()->generatedIconPixmap(iconMode, pixmap, opt: option);
4898}
4899
4900QStyle::SubControl QStyleSheetStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
4901 const QPoint &pt, const QWidget *w) const
4902{
4903 RECURSION_GUARD(return baseStyle()->hitTestComplexControl(cc, opt, pt, w))
4904 switch (cc) {
4905 case CC_TitleBar:
4906 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
4907 QRenderRule rule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TitleBar);
4908 if (rule.hasDrawable() || rule.hasBox() || rule.hasBorder()) {
4909 QHash<QStyle::SubControl, QRect> layout = titleBarLayout(w, tb);
4910 QRect r;
4911 QStyle::SubControl sc = QStyle::SC_None;
4912 uint ctrl = SC_TitleBarSysMenu;
4913 while (ctrl <= SC_TitleBarLabel) {
4914 r = layout[QStyle::SubControl(ctrl)];
4915 if (r.isValid() && r.contains(p: pt)) {
4916 sc = QStyle::SubControl(ctrl);
4917 break;
4918 }
4919 ctrl <<= 1;
4920 }
4921 return sc;
4922 }
4923 }
4924 break;
4925
4926 case CC_MdiControls:
4927 if (hasStyleRule(obj: w, part: PseudoElement_MdiCloseButton)
4928 || hasStyleRule(obj: w, part: PseudoElement_MdiNormalButton)
4929 || hasStyleRule(obj: w, part: PseudoElement_MdiMinButton))
4930 return QWindowsStyle::hitTestComplexControl(cc, opt, pt, w);
4931 break;
4932
4933 case CC_ScrollBar: {
4934 QRenderRule rule = renderRule(obj: w, opt);
4935 if (!rule.hasDrawable() && !rule.hasBox())
4936 break;
4937 }
4938 Q_FALLTHROUGH();
4939 case CC_SpinBox:
4940 case CC_GroupBox:
4941 case CC_ComboBox:
4942 case CC_Slider:
4943 case CC_ToolButton:
4944 return QWindowsStyle::hitTestComplexControl(cc, opt, pt, w);
4945 default:
4946 break;
4947 }
4948
4949 return baseStyle()->hitTestComplexControl(cc, opt, pt, widget: w);
4950}
4951
4952QRect QStyleSheetStyle::itemPixmapRect(const QRect &rect, int alignment, const QPixmap &pixmap) const
4953{
4954 return baseStyle()->itemPixmapRect(r: rect, flags: alignment, pixmap);
4955}
4956
4957QRect QStyleSheetStyle::itemTextRect(const QFontMetrics &metrics, const QRect& rect, int alignment,
4958 bool enabled, const QString& text) const
4959{
4960 return baseStyle()->itemTextRect(fm: metrics, r: rect, flags: alignment, enabled, text);
4961}
4962
4963int QStyleSheetStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWidget *w) const
4964{
4965 RECURSION_GUARD(return baseStyle()->pixelMetric(m, opt, w))
4966
4967 QRenderRule rule = renderRule(obj: w, opt);
4968 QRenderRule subRule;
4969
4970 switch (m) {
4971 case PM_MenuButtonIndicator:
4972#if QT_CONFIG(toolbutton)
4973 // QToolButton adds this directly to the width
4974 if (qobject_cast<const QToolButton *>(object: w)) {
4975 if (rule.hasBox() || !rule.hasNativeBorder())
4976 return 0;
4977 if (const auto *tbOpt = qstyleoption_cast<const QStyleOptionToolButton*>(opt)) {
4978 if (tbOpt->features & QStyleOptionToolButton::MenuButtonPopup)
4979 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolButtonMenu);
4980 else
4981 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolButtonMenuIndicator);
4982 if (subRule.hasContentsSize())
4983 return subRule.size().width();
4984 }
4985 break;
4986 }
4987#endif
4988 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_PushButtonMenuIndicator);
4989 if (subRule.hasContentsSize())
4990 return subRule.size().width();
4991 break;
4992
4993 case PM_ButtonShiftHorizontal:
4994 case PM_ButtonShiftVertical:
4995 case PM_ButtonMargin:
4996 case PM_ButtonDefaultIndicator:
4997 if (rule.hasBox())
4998 return 0;
4999 break;
5000
5001 case PM_DefaultFrameWidth:
5002 if (!rule.hasNativeBorder())
5003 return rule.border()->borders[LeftEdge];
5004 break;
5005
5006 case PM_ExclusiveIndicatorWidth:
5007 case PM_IndicatorWidth:
5008 case PM_ExclusiveIndicatorHeight:
5009 case PM_IndicatorHeight:
5010 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_Indicator);
5011 if (subRule.hasContentsSize()) {
5012 return (m == PM_ExclusiveIndicatorWidth) || (m == PM_IndicatorWidth)
5013 ? subRule.size().width() : subRule.size().height();
5014 }
5015 break;
5016
5017 case PM_DockWidgetFrameWidth:
5018 case PM_ToolTipLabelFrameWidth: // border + margin + padding (support only one width)
5019 if (!rule.hasDrawable())
5020 break;
5021
5022 return (rule.border() ? rule.border()->borders[LeftEdge] : 0)
5023 + (rule.hasBox() ? rule.box()->margins[LeftEdge] + rule.box()->paddings[LeftEdge]: 0);
5024
5025 case PM_ToolBarFrameWidth:
5026 if (rule.hasBorder() || rule.hasBox())
5027 return (rule.border() ? rule.border()->borders[LeftEdge] : 0)
5028 + (rule.hasBox() ? rule.box()->paddings[LeftEdge]: 0);
5029 break;
5030
5031 case PM_MenuPanelWidth:
5032 case PM_MenuBarPanelWidth:
5033 if (rule.hasBorder() || rule.hasBox())
5034 return (rule.border() ? rule.border()->borders[LeftEdge] : 0)
5035 + (rule.hasBox() ? rule.box()->margins[LeftEdge]: 0);
5036 break;
5037
5038
5039 case PM_MenuHMargin:
5040 case PM_MenuBarHMargin:
5041 if (rule.hasBox())
5042 return rule.box()->paddings[LeftEdge];
5043 break;
5044
5045 case PM_MenuVMargin:
5046 case PM_MenuBarVMargin:
5047 if (rule.hasBox())
5048 return rule.box()->paddings[TopEdge];
5049 break;
5050
5051 case PM_DockWidgetTitleBarButtonMargin:
5052 case PM_ToolBarItemMargin:
5053 if (rule.hasBox())
5054 return rule.box()->margins[TopEdge];
5055 break;
5056
5057 case PM_ToolBarItemSpacing:
5058 case PM_MenuBarItemSpacing:
5059 if (rule.hasBox() && rule.box()->spacing != -1)
5060 return rule.box()->spacing;
5061 break;
5062
5063 case PM_MenuTearoffHeight:
5064 case PM_MenuScrollerHeight: {
5065 PseudoElement ps = m == PM_MenuTearoffHeight ? PseudoElement_MenuTearoff : PseudoElement_MenuScroller;
5066 subRule = renderRule(obj: w, opt, pseudoElement: ps);
5067 if (subRule.hasContentsSize())
5068 return subRule.size().height();
5069 break;
5070 }
5071
5072 case PM_ToolBarExtensionExtent:
5073 break;
5074
5075 case PM_SplitterWidth:
5076 case PM_ToolBarSeparatorExtent:
5077 case PM_ToolBarHandleExtent: {
5078 PseudoElement ps;
5079 if (m == PM_ToolBarHandleExtent) ps = PseudoElement_ToolBarHandle;
5080 else if (m == PM_SplitterWidth) ps = PseudoElement_SplitterHandle;
5081 else ps = PseudoElement_ToolBarSeparator;
5082 subRule = renderRule(obj: w, opt, pseudoElement: ps);
5083 if (subRule.hasContentsSize()) {
5084 QSize sz = subRule.size();
5085 return (opt && opt->state & QStyle::State_Horizontal) ? sz.width() : sz.height();
5086 }
5087 break;
5088 }
5089
5090 case PM_RadioButtonLabelSpacing:
5091 if (rule.hasBox() && rule.box()->spacing != -1)
5092 return rule.box()->spacing;
5093 break;
5094 case PM_CheckBoxLabelSpacing:
5095#if QT_CONFIG(checkbox)
5096 if (qobject_cast<const QCheckBox *>(object: w)) {
5097 if (rule.hasBox() && rule.box()->spacing != -1)
5098 return rule.box()->spacing;
5099 }
5100#endif
5101 // assume group box
5102 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_GroupBoxTitle);
5103 if (subRule.hasBox() && subRule.box()->spacing != -1)
5104 return subRule.box()->spacing;
5105 break;
5106
5107#if QT_CONFIG(scrollbar)
5108 case PM_ScrollBarExtent:
5109 if (rule.hasContentsSize()) {
5110 QSize sz = rule.size();
5111 if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt))
5112 return sb->orientation == Qt::Horizontal ? sz.height() : sz.width();
5113 return sz.width() == -1 ? sz.height() : sz.width();
5114 }
5115 break;
5116
5117 case PM_ScrollBarSliderMin:
5118 if (hasStyleRule(obj: w, part: PseudoElement_ScrollBarSlider)) {
5119 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ScrollBarSlider);
5120 QSize msz = subRule.minimumSize();
5121 if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt))
5122 return sb->orientation == Qt::Horizontal ? msz.width() : msz.height();
5123 return msz.width() == -1 ? msz.height() : msz.width();
5124 }
5125 break;
5126
5127 case PM_ScrollView_ScrollBarSpacing:
5128 if (!rule.hasNativeBorder() || rule.hasBox())
5129 return 0;
5130 break;
5131
5132 case PM_ScrollView_ScrollBarOverlap:
5133 if (!proxy()->styleHint(stylehint: SH_ScrollBar_Transient, opt, widget: w))
5134 return 0;
5135 break;
5136#endif // QT_CONFIG(scrollbar)
5137
5138
5139 case PM_ProgressBarChunkWidth:
5140 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ProgressBarChunk);
5141 if (subRule.hasContentsSize()) {
5142 QSize sz = subRule.size();
5143 return (opt->state & QStyle::State_Horizontal)
5144 ? sz.width() : sz.height();
5145 }
5146 break;
5147
5148#if QT_CONFIG(tabwidget)
5149 case PM_TabBarTabHSpace:
5150 case PM_TabBarTabVSpace:
5151 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabBarTab);
5152 if (subRule.hasBox() || subRule.hasBorder())
5153 return 0;
5154 break;
5155
5156 case PM_TabBarScrollButtonWidth:
5157 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabBarScroller);
5158 if (subRule.hasContentsSize()) {
5159 QSize sz = subRule.size();
5160 return (sz.width() != -1 ? sz.width() : sz.height()) / 2;
5161 }
5162 break;
5163
5164 case PM_TabBarTabShiftHorizontal:
5165 case PM_TabBarTabShiftVertical:
5166 subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabBarTab);
5167 if (subRule.hasBox())
5168 return 0;
5169 break;
5170
5171 case PM_TabBarBaseOverlap: {
5172 const QWidget *tabWidget = qobject_cast<const QTabWidget *>(object: w);
5173 if (!tabWidget && w)
5174 tabWidget = w->parentWidget();
5175 if (hasStyleRule(obj: tabWidget, part: PseudoElement_TabWidgetPane)) {
5176 return 0;
5177 }
5178 break;
5179 }
5180#endif // QT_CONFIG(tabwidget)
5181
5182 case PM_SliderThickness: // horizontal slider's height (sizeHint)
5183 case PM_SliderLength: // minimum length of slider
5184 if (rule.hasContentsSize()) {
5185 bool horizontal = opt->state & QStyle::State_Horizontal;
5186 if (m == PM_SliderThickness) {
5187 QSize sz = rule.size();
5188 return horizontal ? sz.height() : sz.width();
5189 } else {
5190 QSize msz = rule.minimumContentsSize();
5191 return horizontal ? msz.width() : msz.height();
5192 }
5193 }
5194 break;
5195
5196 case PM_SliderControlThickness: {
5197 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SliderHandle);
5198 if (!subRule.hasContentsSize())
5199 break;
5200 QSize size = subRule.size();
5201 return (opt->state & QStyle::State_Horizontal) ? size.height() : size.width();
5202 }
5203
5204 case PM_ToolBarIconSize:
5205 case PM_ListViewIconSize:
5206 case PM_IconViewIconSize:
5207 case PM_TabBarIconSize:
5208 case PM_MessageBoxIconSize:
5209 case PM_ButtonIconSize:
5210 case PM_SmallIconSize:
5211 if (rule.hasStyleHint(sh: "icon-size"_L1))
5212 return rule.styleHint(sh: "icon-size"_L1).toSize().width();
5213 break;
5214
5215 case PM_DockWidgetTitleMargin: {
5216 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_DockWidgetTitle);
5217 if (!subRule.hasBox())
5218 break;
5219 return (subRule.border() ? subRule.border()->borders[TopEdge] : 0)
5220 + (subRule.hasBox() ? subRule.box()->margins[TopEdge] + subRule.box()->paddings[TopEdge]: 0);
5221 }
5222
5223 case PM_DockWidgetSeparatorExtent: {
5224 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_DockWidgetSeparator);
5225 if (!subRule.hasContentsSize())
5226 break;
5227 QSize sz = subRule.size();
5228 return qMax(a: sz.width(), b: sz.height());
5229 }
5230
5231 case PM_TitleBarHeight: {
5232 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TitleBar);
5233 if (subRule.hasContentsSize())
5234 return subRule.size().height();
5235 else if (subRule.hasBox() || subRule.hasBorder()) {
5236 QFontMetrics fm = opt ? opt->fontMetrics : w->fontMetrics();
5237 return subRule.size(sz: QSize(0, fm.height())).height();
5238 }
5239 break;
5240 }
5241
5242 case PM_MdiSubWindowFrameWidth:
5243 if (rule.hasBox() || rule.hasBorder()) {
5244 return (rule.border() ? rule.border()->borders[LeftEdge] : 0)
5245 + (rule.hasBox() ? rule.box()->paddings[LeftEdge]+rule.box()->margins[LeftEdge]: 0);
5246 }
5247 break;
5248
5249 case PM_MdiSubWindowMinimizedWidth: {
5250 QRenderRule subRule = renderRule(obj: w, element: PseudoElement_None, state: PseudoClass_Minimized);
5251 int width = subRule.size().width();
5252 if (width != -1)
5253 return width;
5254 break;
5255 }
5256 default:
5257 break;
5258 }
5259
5260 return baseStyle()->pixelMetric(metric: m, option: opt, widget: w);
5261}
5262
5263QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
5264 const QSize &csz, const QWidget *w) const
5265{
5266 RECURSION_GUARD(return baseStyle()->sizeFromContents(ct, opt, csz, w))
5267
5268 QRenderRule rule = renderRule(obj: w, opt);
5269 QSize sz = rule.adjustSize(sz: csz);
5270
5271 switch (ct) {
5272#if QT_CONFIG(spinbox)
5273 case CT_SpinBox:
5274 if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
5275 if (spinbox->buttonSymbols != QAbstractSpinBox::NoButtons) {
5276 // Add some space for the up/down buttons
5277 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxUpButton);
5278 if (subRule.hasDrawable()) {
5279 QRect r = positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_SpinBoxUpButton,
5280 rect: opt->rect, dir: opt->direction);
5281 sz.rwidth() += r.width();
5282 } else {
5283 QSize defaultUpSize = defaultSize(w, sz: subRule.size(), rect: spinbox->rect, pe: PseudoElement_SpinBoxUpButton);
5284 sz.rwidth() += defaultUpSize.width();
5285 }
5286 }
5287 if (rule.hasBox() || rule.hasBorder() || !rule.hasNativeBorder())
5288 sz = rule.boxSize(cs: sz);
5289 return sz;
5290 }
5291 break;
5292#endif // QT_CONFIG(spinbox)
5293 case CT_ToolButton:
5294 if (rule.hasBox() || !rule.hasNativeBorder() || !rule.baseStyleCanDraw())
5295 sz += QSize(3, 3); // ### broken QToolButton
5296 Q_FALLTHROUGH();
5297 case CT_ComboBox:
5298 case CT_PushButton:
5299 if (rule.hasBox() || !rule.hasNativeBorder()) {
5300 if (ct == CT_ComboBox) {
5301 //add some space for the drop down.
5302 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ComboBoxDropDown);
5303 QRect comboRect = positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_ComboBoxDropDown, rect: opt->rect, dir: opt->direction);
5304 //+2 because there is hardcoded margins in QCommonStyle::drawControl(CE_ComboBoxLabel)
5305 sz += QSize(comboRect.width() + 2, 0);
5306 }
5307 return rule.boxSize(cs: sz);
5308 }
5309 sz = rule.baseStyleCanDraw() ? baseStyle()->sizeFromContents(ct, opt, contentsSize: sz, w)
5310 : QWindowsStyle::sizeFromContents(ct, opt, contentsSize: sz, widget: w);
5311 return rule.boxSize(cs: sz, flags: Margin);
5312
5313 case CT_HeaderSection: {
5314 if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
5315 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_HeaderViewSection);
5316 if (subRule.hasGeometry() || subRule.hasBox() || !subRule.hasNativeBorder() || subRule.hasFont) {
5317 sz = subRule.adjustSize(sz: csz);
5318 if (!sz.isValid()) {
5319 // Try to set the missing values based on the base style.
5320 const auto baseSize = baseStyle()->sizeFromContents(ct, opt, contentsSize: sz, w);
5321 if (sz.width() < 0)
5322 sz.setWidth(baseSize.width());
5323 if (sz.height() < 0)
5324 sz.setHeight(baseSize.height());
5325 }
5326 if (!subRule.hasGeometry()) {
5327 QSize nativeContentsSize;
5328 bool nullIcon = hdr->icon.isNull();
5329 const int margin = pixelMetric(m: QStyle::PM_HeaderMargin, opt: hdr, w);
5330 int iconSize = nullIcon ? 0 : pixelMetric(m: QStyle::PM_SmallIconSize, opt: hdr, w);
5331 QFontMetrics fm = hdr->fontMetrics;
5332 if (subRule.hasFont) {
5333 QFont styleFont = w ? subRule.font.resolve(w->font()) : subRule.font;
5334 fm = QFontMetrics(styleFont);
5335 }
5336 const QSize txt = fm.size(flags: 0, str: hdr->text);
5337 nativeContentsSize.setHeight(margin + qMax(a: iconSize, b: txt.height()) + margin);
5338 nativeContentsSize.setWidth((nullIcon ? 0 : margin) + iconSize
5339 + (hdr->text.isNull() ? 0 : margin) + txt.width() + margin);
5340 sz = sz.expandedTo(otherSize: nativeContentsSize);
5341 }
5342 return subRule.size(sz);
5343 }
5344 sz = subRule.baseStyleCanDraw() ? baseStyle()->sizeFromContents(ct, opt, contentsSize: sz, w)
5345 : QWindowsStyle::sizeFromContents(ct, opt, contentsSize: sz, widget: w);
5346 if (hasStyleRule(obj: w, part: PseudoElement_HeaderViewDownArrow)
5347 || hasStyleRule(obj: w, part: PseudoElement_HeaderViewUpArrow)) {
5348 const QRect arrowRect = subElementRect(r: SE_HeaderArrow, opt, widget: w);
5349 if (hdr->orientation == Qt::Horizontal)
5350 sz.rwidth() += arrowRect.width();
5351 else
5352 sz.rheight() += arrowRect.height();
5353 }
5354 return sz;
5355 }
5356 }
5357 break;
5358 case CT_GroupBox:
5359 case CT_LineEdit:
5360#if QT_CONFIG(spinbox)
5361 if (qobject_cast<QAbstractSpinBox *>(object: w ? w->parentWidget() : nullptr))
5362 return csz; // we only care about the size hint of the line edit
5363#endif
5364 if (rule.hasBox() || !rule.hasNativeBorder()) {
5365 return rule.boxSize(cs: sz);
5366 }
5367 break;
5368
5369 case CT_CheckBox:
5370 case CT_RadioButton:
5371 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
5372 if (rule.hasBox() || rule.hasBorder() || hasStyleRule(obj: w, part: PseudoElement_Indicator)) {
5373 bool isRadio = (ct == CT_RadioButton);
5374 int iw = pixelMetric(m: isRadio ? PM_ExclusiveIndicatorWidth
5375 : PM_IndicatorWidth, opt: btn, w);
5376 int ih = pixelMetric(m: isRadio ? PM_ExclusiveIndicatorHeight
5377 : PM_IndicatorHeight, opt: btn, w);
5378
5379 int spacing = pixelMetric(m: isRadio ? PM_RadioButtonLabelSpacing
5380 : PM_CheckBoxLabelSpacing, opt: btn, w);
5381 sz.setWidth(sz.width() + iw + spacing);
5382 sz.setHeight(qMax(a: sz.height(), b: ih));
5383 return rule.boxSize(cs: sz);
5384 }
5385 }
5386 break;
5387
5388 case CT_Menu:
5389 case CT_MenuBar: // already has everything!
5390 case CT_ScrollBar:
5391 if (rule.hasBox() || rule.hasBorder())
5392 return sz;
5393 break;
5394
5395 case CT_MenuItem:
5396 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
5397 PseudoElement pe = (mi->menuItemType == QStyleOptionMenuItem::Separator)
5398 ? PseudoElement_MenuSeparator : PseudoElement_Item;
5399 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: pe);
5400 if ((pe == PseudoElement_MenuSeparator) && subRule.hasContentsSize()) {
5401 return QSize(sz.width(), subRule.size().height());
5402 }
5403 if ((pe == PseudoElement_Item) && (subRule.hasBox() || subRule.hasBorder() || subRule.hasFont)) {
5404 QSize sz(csz);
5405 if (mi->text.contains(c: u'\t'))
5406 sz.rwidth() += 12; //as in QCommonStyle
5407 if (!mi->icon.isNull()) {
5408 const int pmSmall = pixelMetric(m: PM_SmallIconSize);
5409 const QSize pmSize = mi->icon.actualSize(size: QSize(pmSmall, pmSmall));
5410 sz.rwidth() += std::max(a: mi->maxIconWidth, b: pmSize.width()) + 4;
5411 } else if (mi->menuHasCheckableItems) {
5412 QRenderRule subSubRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_MenuCheckMark);
5413 QRect checkmarkRect = positionRect(w, rule1: subRule, rule2: subSubRule, pe: PseudoElement_MenuCheckMark, rect: opt->rect, dir: opt->direction);
5414 sz.rwidth() += std::max(a: mi->maxIconWidth, b: checkmarkRect.width()) + 4;
5415 } else {
5416 sz.rwidth() += mi->maxIconWidth;
5417 }
5418 if (subRule.hasFont) {
5419 QFontMetrics fm(subRule.font.resolve(mi->font));
5420 const QRect r = fm.boundingRect(r: QRect(), flags: Qt::TextSingleLine | Qt::TextShowMnemonic, text: mi->text);
5421 sz = sz.expandedTo(otherSize: r.size());
5422 }
5423 return subRule.boxSize(cs: subRule.adjustSize(sz));
5424 }
5425 }
5426 break;
5427
5428 case CT_Splitter:
5429 case CT_MenuBarItem: {
5430 PseudoElement pe = (ct == CT_Splitter) ? PseudoElement_SplitterHandle : PseudoElement_Item;
5431 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: pe);
5432 if (subRule.hasBox() || subRule.hasBorder())
5433 return subRule.boxSize(cs: sz);
5434 break;
5435 }
5436
5437 case CT_ProgressBar:
5438 case CT_SizeGrip:
5439 return (rule.hasContentsSize())
5440 ? rule.size(sz)
5441 : rule.boxSize(cs: baseStyle()->sizeFromContents(ct, opt, contentsSize: sz, w));
5442 break;
5443
5444 case CT_Slider:
5445 if (rule.hasBorder() || rule.hasBox() || rule.hasGeometry())
5446 return rule.boxSize(cs: sz);
5447 break;
5448
5449#if QT_CONFIG(tabbar)
5450 case CT_TabBarTab: {
5451 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabBarTab);
5452 if (subRule.hasBox() || !subRule.hasNativeBorder() || subRule.hasFont) {
5453 int spaceForIcon = 0;
5454 bool vertical = false;
5455 QString text;
5456 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
5457 if (!tab->icon.isNull())
5458 spaceForIcon = 6 /* icon offset */ + 4 /* spacing */ + 2 /* magic */; // ###: hardcoded to match with common style
5459 vertical = verticalTabs(shape: tab->shape);
5460 text = tab->text;
5461 }
5462 if (subRule.hasBox() || !subRule.hasNativeBorder())
5463 sz = csz + QSize(vertical ? 0 : spaceForIcon, vertical ? spaceForIcon : 0);
5464 if (subRule.hasFont) {
5465 // first we remove the space needed for the text using the default font
5466 const QSize oldTextSize = opt->fontMetrics.size(flags: Qt::TextShowMnemonic, str: text);
5467 (vertical ? sz.rheight() : sz.rwidth()) -= oldTextSize.width();
5468
5469 // then we add the space needed when using the rule font to the relevant
5470 // dimension, and constraint the other dimension to the maximum to make
5471 // sure we don't grow, but also don't clip icons or buttons.
5472 const QFont ruleFont = subRule.font.resolve(w->font());
5473 const QFontMetrics fm(ruleFont);
5474 const QSize textSize = fm.size(flags: Qt::TextShowMnemonic, str: text);
5475 if (vertical) {
5476 sz.rheight() += textSize.width();
5477 sz.rwidth() = qMax(a: textSize.height(), b: sz.width());
5478 } else {
5479 sz.rwidth() += textSize.width();
5480 sz.rheight() = qMax(a: textSize.height(), b: sz.height());
5481 }
5482 }
5483
5484 return subRule.boxSize(cs: subRule.adjustSize(sz));
5485 }
5486 sz = subRule.adjustSize(sz: csz);
5487 break;
5488 }
5489#endif // QT_CONFIG(tabbar)
5490
5491 case CT_MdiControls:
5492 if (const QStyleOptionComplex *ccOpt = qstyleoption_cast<const QStyleOptionComplex *>(opt)) {
5493 if (!hasStyleRule(obj: w, part: PseudoElement_MdiCloseButton)
5494 && !hasStyleRule(obj: w, part: PseudoElement_MdiNormalButton)
5495 && !hasStyleRule(obj: w, part: PseudoElement_MdiMinButton))
5496 break;
5497
5498 QList<QVariant> layout = rule.styleHint(sh: "button-layout"_L1).toList();
5499 if (layout.isEmpty())
5500 layout = subControlLayout(layout: "mNX"_L1);
5501
5502 int width = 0, height = 0;
5503 for (int i = 0; i < layout.size(); i++) {
5504 int layoutButton = layout[i].toInt();
5505 if (layoutButton < PseudoElement_MdiCloseButton
5506 || layoutButton > PseudoElement_MdiNormalButton)
5507 continue;
5508 QStyle::SubControl sc = knownPseudoElements[layoutButton].subControl;
5509 if (!(ccOpt->subControls & sc))
5510 continue;
5511 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: layoutButton);
5512 QSize sz = subRule.size();
5513 width += sz.width();
5514 height = qMax(a: height, b: sz.height());
5515 }
5516
5517 return QSize(width, height);
5518 }
5519 break;
5520
5521#if QT_CONFIG(itemviews)
5522 case CT_ItemViewItem: {
5523 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ViewItem);
5524 sz = baseStyle()->sizeFromContents(ct, opt, contentsSize: csz, w);
5525 sz = subRule.adjustSize(sz);
5526 if (subRule.hasBox() || subRule.hasBorder())
5527 sz = subRule.boxSize(cs: sz);
5528 return sz;
5529 }
5530#endif // QT_CONFIG(itemviews)
5531
5532 default:
5533 break;
5534 }
5535
5536 return baseStyle()->sizeFromContents(ct, opt, contentsSize: sz, w);
5537}
5538
5539/*!
5540 \internal
5541*/
5542static QLatin1StringView propertyNameForStandardPixmap(QStyle::StandardPixmap sp)
5543{
5544 switch (sp) {
5545 case QStyle::SP_TitleBarMenuButton: return "titlebar-menu-icon"_L1;
5546 case QStyle::SP_TitleBarMinButton: return "titlebar-minimize-icon"_L1;
5547 case QStyle::SP_TitleBarMaxButton: return "titlebar-maximize-icon"_L1;
5548 case QStyle::SP_TitleBarCloseButton: return "titlebar-close-icon"_L1;
5549 case QStyle::SP_TitleBarNormalButton: return "titlebar-normal-icon"_L1;
5550 case QStyle::SP_TitleBarShadeButton: return "titlebar-shade-icon"_L1;
5551 case QStyle::SP_TitleBarUnshadeButton: return "titlebar-unshade-icon"_L1;
5552 case QStyle::SP_TitleBarContextHelpButton: return "titlebar-contexthelp-icon"_L1;
5553 case QStyle::SP_DockWidgetCloseButton: return "dockwidget-close-icon"_L1;
5554 case QStyle::SP_MessageBoxInformation: return "messagebox-information-icon"_L1;
5555 case QStyle::SP_MessageBoxWarning: return "messagebox-warning-icon"_L1;
5556 case QStyle::SP_MessageBoxCritical: return "messagebox-critical-icon"_L1;
5557 case QStyle::SP_MessageBoxQuestion: return "messagebox-question-icon"_L1;
5558 case QStyle::SP_DesktopIcon: return "desktop-icon"_L1;
5559 case QStyle::SP_TrashIcon: return "trash-icon"_L1;
5560 case QStyle::SP_ComputerIcon: return "computer-icon"_L1;
5561 case QStyle::SP_DriveFDIcon: return "floppy-icon"_L1;
5562 case QStyle::SP_DriveHDIcon: return "harddisk-icon"_L1;
5563 case QStyle::SP_DriveCDIcon: return "cd-icon"_L1;
5564 case QStyle::SP_DriveDVDIcon: return "dvd-icon"_L1;
5565 case QStyle::SP_DriveNetIcon: return "network-icon"_L1;
5566 case QStyle::SP_DirOpenIcon: return "directory-open-icon"_L1;
5567 case QStyle::SP_DirClosedIcon: return "directory-closed-icon"_L1;
5568 case QStyle::SP_DirLinkIcon: return "directory-link-icon"_L1;
5569 case QStyle::SP_FileIcon: return "file-icon"_L1;
5570 case QStyle::SP_FileLinkIcon: return "file-link-icon"_L1;
5571 case QStyle::SP_FileDialogStart: return "filedialog-start-icon"_L1;
5572 case QStyle::SP_FileDialogEnd: return "filedialog-end-icon"_L1;
5573 case QStyle::SP_FileDialogToParent: return "filedialog-parent-directory-icon"_L1;
5574 case QStyle::SP_FileDialogNewFolder: return "filedialog-new-directory-icon"_L1;
5575 case QStyle::SP_FileDialogDetailedView: return "filedialog-detailedview-icon"_L1;
5576 case QStyle::SP_FileDialogInfoView: return "filedialog-infoview-icon"_L1;
5577 case QStyle::SP_FileDialogContentsView: return "filedialog-contentsview-icon"_L1;
5578 case QStyle::SP_FileDialogListView: return "filedialog-listview-icon"_L1;
5579 case QStyle::SP_FileDialogBack: return "filedialog-backward-icon"_L1;
5580 case QStyle::SP_DirIcon: return "directory-icon"_L1;
5581 case QStyle::SP_DialogOkButton: return "dialog-ok-icon"_L1;
5582 case QStyle::SP_DialogCancelButton: return "dialog-cancel-icon"_L1;
5583 case QStyle::SP_DialogHelpButton: return "dialog-help-icon"_L1;
5584 case QStyle::SP_DialogOpenButton: return "dialog-open-icon"_L1;
5585 case QStyle::SP_DialogSaveButton: return "dialog-save-icon"_L1;
5586 case QStyle::SP_DialogCloseButton: return "dialog-close-icon"_L1;
5587 case QStyle::SP_DialogApplyButton: return "dialog-apply-icon"_L1;
5588 case QStyle::SP_DialogResetButton: return "dialog-reset-icon"_L1;
5589 case QStyle::SP_DialogDiscardButton: return "dialog-discard-icon"_L1;
5590 case QStyle::SP_DialogYesButton: return "dialog-yes-icon"_L1;
5591 case QStyle::SP_DialogNoButton: return "dialog-no-icon"_L1;
5592 case QStyle::SP_ArrowUp: return "uparrow-icon"_L1;
5593 case QStyle::SP_ArrowDown: return "downarrow-icon"_L1;
5594 case QStyle::SP_ArrowLeft: return "leftarrow-icon"_L1;
5595 case QStyle::SP_ArrowRight: return "rightarrow-icon"_L1;
5596 case QStyle::SP_ArrowBack: return "backward-icon"_L1;
5597 case QStyle::SP_ArrowForward: return "forward-icon"_L1;
5598 case QStyle::SP_DirHomeIcon: return "home-icon"_L1;
5599 case QStyle::SP_LineEditClearButton: return "lineedit-clear-button-icon"_L1;
5600 default: return ""_L1;
5601 }
5602}
5603
5604QIcon QStyleSheetStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *opt,
5605 const QWidget *w) const
5606{
5607 RECURSION_GUARD(return baseStyle()->standardIcon(standardIcon, opt, w))
5608 QString s = propertyNameForStandardPixmap(sp: standardIcon);
5609 if (!s.isEmpty()) {
5610 QRenderRule rule = renderRule(obj: w, opt);
5611 if (rule.hasStyleHint(sh: s))
5612 return qvariant_cast<QIcon>(v: rule.styleHint(sh: s));
5613 }
5614 return baseStyle()->standardIcon(standardIcon, option: opt, widget: w);
5615}
5616
5617QPalette QStyleSheetStyle::standardPalette() const
5618{
5619 return baseStyle()->standardPalette();
5620}
5621
5622QPixmap QStyleSheetStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
5623 const QWidget *w) const
5624{
5625 RECURSION_GUARD(return baseStyle()->standardPixmap(standardPixmap, opt, w))
5626 QString s = propertyNameForStandardPixmap(sp: standardPixmap);
5627 if (!s.isEmpty()) {
5628 QRenderRule rule = renderRule(obj: w, opt);
5629 if (rule.hasStyleHint(sh: s)) {
5630 QIcon icon = qvariant_cast<QIcon>(v: rule.styleHint(sh: s));
5631 return icon.pixmap(w: 16, h: 16); // ###: unhard-code this if someone complains
5632 }
5633 }
5634 return baseStyle()->standardPixmap(standardPixmap, opt, widget: w);
5635}
5636
5637int QStyleSheetStyle::layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2,
5638 Qt::Orientation orientation, const QStyleOption *option,
5639 const QWidget *widget) const
5640{
5641 return baseStyle()->layoutSpacing(control1, control2, orientation, option, widget);
5642}
5643
5644int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
5645 QStyleHintReturn *shret) const
5646{
5647 RECURSION_GUARD(return baseStyle()->styleHint(sh, opt, w, shret))
5648 // Prevent endless loop if somebody use isActiveWindow property as selector.
5649 // QWidget::isActiveWindow uses this styleHint to determine if the window is active or not
5650 if (sh == SH_Widget_ShareActivation)
5651 return baseStyle()->styleHint(stylehint: sh, opt, widget: w, returnData: shret);
5652
5653 QRenderRule rule = renderRule(obj: w, opt);
5654 QString s;
5655 switch (sh) {
5656 case SH_LineEdit_PasswordCharacter: s = "lineedit-password-character"_L1; break;
5657 case SH_LineEdit_PasswordMaskDelay: s = "lineedit-password-mask-delay"_L1; break;
5658 case SH_DitherDisabledText: s = "dither-disabled-text"_L1; break;
5659 case SH_EtchDisabledText: s = "etch-disabled-text"_L1; break;
5660 case SH_ItemView_ActivateItemOnSingleClick: s = "activate-on-singleclick"_L1; break;
5661 case SH_ItemView_ShowDecorationSelected: s = "show-decoration-selected"_L1; break;
5662 case SH_Table_GridLineColor: s = "gridline-color"_L1; break;
5663 case SH_DialogButtonLayout: s = "button-layout"_L1; break;
5664 case SH_ToolTipLabel_Opacity: s = "opacity"_L1; break;
5665 case SH_ComboBox_Popup: s = "combobox-popup"_L1; break;
5666 case SH_ComboBox_ListMouseTracking: s = "combobox-list-mousetracking"_L1; break;
5667 case SH_MenuBar_AltKeyNavigation: s = "menubar-altkey-navigation"_L1; break;
5668 case SH_Menu_Scrollable: s = "menu-scrollable"_L1; break;
5669 case SH_DrawMenuBarSeparator: s = "menubar-separator"_L1; break;
5670 case SH_MenuBar_MouseTracking: s = "mouse-tracking"_L1; break;
5671 case SH_SpinBox_ClickAutoRepeatRate: s = "spinbox-click-autorepeat-rate"_L1; break;
5672 case SH_SpinControls_DisableOnBounds: s = "spincontrol-disable-on-bounds"_L1; break;
5673 case SH_MessageBox_TextInteractionFlags: s = "messagebox-text-interaction-flags"_L1; break;
5674 case SH_ToolButton_PopupDelay: s = "toolbutton-popup-delay"_L1; break;
5675 case SH_ToolBox_SelectedPageTitleBold:
5676 if (renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolBoxTab).hasFont)
5677 return 0;
5678 break;
5679 case SH_GroupBox_TextLabelColor:
5680 if (rule.hasPalette() && rule.palette()->foreground.style() != Qt::NoBrush)
5681 return rule.palette()->foreground.color().rgba();
5682 break;
5683 case SH_ScrollView_FrameOnlyAroundContents: s = "scrollview-frame-around-contents"_L1; break;
5684 case SH_ScrollBar_ContextMenu: s = "scrollbar-contextmenu"_L1; break;
5685 case SH_ScrollBar_LeftClickAbsolutePosition: s = "scrollbar-leftclick-absolute-position"_L1; break;
5686 case SH_ScrollBar_MiddleClickAbsolutePosition: s = "scrollbar-middleclick-absolute-position"_L1; break;
5687 case SH_ScrollBar_RollBetweenButtons: s = "scrollbar-roll-between-buttons"_L1; break;
5688 case SH_ScrollBar_ScrollWhenPointerLeavesControl: s = "scrollbar-scroll-when-pointer-leaves-control"_L1; break;
5689 case SH_TabBar_Alignment:
5690#if QT_CONFIG(tabwidget)
5691 if (qobject_cast<const QTabWidget *>(object: w)) {
5692 rule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabWidgetTabBar);
5693 if (rule.hasPosition())
5694 return rule.position()->position;
5695 }
5696#endif // QT_CONFIG(tabwidget)
5697 s = "alignment"_L1;
5698 break;
5699#if QT_CONFIG(tabbar)
5700 case SH_TabBar_CloseButtonPosition:
5701 rule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabBarTabCloseButton);
5702 if (rule.hasPosition()) {
5703 Qt::Alignment align = rule.position()->position;
5704 if (align & Qt::AlignLeft || align & Qt::AlignTop)
5705 return QTabBar::LeftSide;
5706 if (align & Qt::AlignRight || align & Qt::AlignBottom)
5707 return QTabBar::RightSide;
5708 }
5709 break;
5710#endif
5711 case SH_TabBar_ElideMode: s = "tabbar-elide-mode"_L1; break;
5712 case SH_TabBar_PreferNoArrows: s = "tabbar-prefer-no-arrows"_L1; break;
5713 case SH_ComboBox_PopupFrameStyle:
5714#if QT_CONFIG(combobox)
5715 if (qobject_cast<const QComboBox *>(object: w)) {
5716 QAbstractItemView *view = w->findChild<QAbstractItemView *>();
5717 if (view) {
5718 view->ensurePolished();
5719 QRenderRule subRule = renderRule(obj: view, element: PseudoElement_None);
5720 if (subRule.hasBox() || !subRule.hasNativeBorder())
5721 return QFrame::NoFrame;
5722 }
5723 }
5724#endif // QT_CONFIG(combobox)
5725 break;
5726 case SH_DialogButtonBox_ButtonsHaveIcons: s = "dialogbuttonbox-buttons-have-icons"_L1; break;
5727 case SH_Workspace_FillSpaceOnMaximize: s = "mdi-fill-space-on-maximize"_L1; break;
5728 case SH_TitleBar_NoBorder:
5729 if (rule.hasBorder())
5730 return !rule.border()->borders[LeftEdge];
5731 break;
5732 case SH_TitleBar_AutoRaise: { // plain absurd
5733 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TitleBar);
5734 if (subRule.hasDrawable())
5735 return 1;
5736 break;
5737 }
5738 case SH_ItemView_ArrowKeysNavigateIntoChildren: s = "arrow-keys-navigate-into-children"_L1; break;
5739 case SH_ItemView_PaintAlternatingRowColorsForEmptyArea: s = "paint-alternating-row-colors-for-empty-area"_L1; break;
5740 case SH_TitleBar_ShowToolTipsOnButtons: s = "titlebar-show-tooltips-on-buttons"_L1; break;
5741 case SH_Widget_Animation_Duration: s = "widget-animation-duration"_L1; break;
5742 case SH_ScrollBar_Transient:
5743 if (!rule.hasNativeBorder() || rule.hasBox() || rule.hasDrawable())
5744 return 0;
5745 break;
5746 default: break;
5747 }
5748 if (!s.isEmpty() && rule.hasStyleHint(sh: s)) {
5749 return rule.styleHint(sh: s).toInt();
5750 }
5751
5752 return baseStyle()->styleHint(stylehint: sh, opt, widget: w, returnData: shret);
5753}
5754
5755QRect QStyleSheetStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
5756 const QWidget *w) const
5757{
5758 RECURSION_GUARD(return baseStyle()->subControlRect(cc, opt, sc, w))
5759
5760 QRenderRule rule = renderRule(obj: w, opt);
5761 switch (cc) {
5762 case CC_ComboBox:
5763 if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
5764 if (rule.hasBox() || !rule.hasNativeBorder()) {
5765 switch (sc) {
5766 case SC_ComboBoxFrame: return rule.borderRect(r: opt->rect);
5767 case SC_ComboBoxEditField:
5768 {
5769 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ComboBoxDropDown);
5770 QRect r = rule.contentsRect(r: opt->rect);
5771 QRect r2 = positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_ComboBoxDropDown,
5772 rect: opt->rect, dir: opt->direction);
5773 if (subRule.hasPosition() && subRule.position()->position & Qt::AlignLeft) {
5774 return visualRect(direction: opt->direction, boundingRect: r, logicalRect: r.adjusted(xp1: r2.width(),yp1: 0,xp2: 0,yp2: 0));
5775 } else {
5776 return visualRect(direction: opt->direction, boundingRect: r, logicalRect: r.adjusted(xp1: 0,yp1: 0,xp2: -r2.width(),yp2: 0));
5777 }
5778 }
5779 case SC_ComboBoxArrow: {
5780 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ComboBoxDropDown);
5781 return positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_ComboBoxDropDown, rect: opt->rect, dir: opt->direction);
5782 }
5783 case SC_ComboBoxListBoxPopup:
5784 default:
5785 return baseStyle()->subControlRect(cc, opt, sc, widget: w);
5786 }
5787 }
5788
5789 QStyleOptionComboBox comboBox(*cb);
5790 comboBox.rect = rule.borderRect(r: opt->rect);
5791 return rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, opt: &comboBox, sc, widget: w)
5792 : QWindowsStyle::subControlRect(cc, opt: &comboBox, sc, w);
5793 }
5794 break;
5795
5796#if QT_CONFIG(spinbox)
5797 case CC_SpinBox:
5798 if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
5799 QRenderRule upRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxUpButton);
5800 QRenderRule downRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SpinBoxDownButton);
5801 bool ruleMatch = rule.hasBox() || !rule.hasNativeBorder();
5802 bool upRuleMatch = upRule.hasGeometry() || upRule.hasPosition();
5803 bool downRuleMatch = downRule.hasGeometry() || downRule.hasPosition();
5804 if (ruleMatch || upRuleMatch || downRuleMatch) {
5805 switch (sc) {
5806 case SC_SpinBoxFrame:
5807 return rule.borderRect(r: opt->rect);
5808 case SC_SpinBoxEditField:
5809 {
5810 QRect r = rule.contentsRect(r: opt->rect);
5811 // Use the widest button on each side to determine edit field size.
5812 Qt::Alignment upAlign, downAlign;
5813
5814 upAlign = upRule.hasPosition() ? upRule.position()->position
5815 : Qt::Alignment(Qt::AlignRight);
5816 upAlign = resolveAlignment(opt->direction, upAlign);
5817
5818 downAlign = downRule.hasPosition() ? downRule.position()->position
5819 : Qt::Alignment(Qt::AlignRight);
5820 downAlign = resolveAlignment(opt->direction, downAlign);
5821
5822 const bool hasButtons = (spin->buttonSymbols != QAbstractSpinBox::NoButtons);
5823 const int upSize = hasButtons
5824 ? subControlRect(cc: CC_SpinBox, opt, sc: SC_SpinBoxUp, w).width() : 0;
5825 const int downSize = hasButtons
5826 ? subControlRect(cc: CC_SpinBox, opt, sc: SC_SpinBoxDown, w).width() : 0;
5827
5828 int widestL = qMax(a: (upAlign & Qt::AlignLeft) ? upSize : 0,
5829 b: (downAlign & Qt::AlignLeft) ? downSize : 0);
5830 int widestR = qMax(a: (upAlign & Qt::AlignRight) ? upSize : 0,
5831 b: (downAlign & Qt::AlignRight) ? downSize : 0);
5832 r.setRight(r.right() - widestR);
5833 r.setLeft(r.left() + widestL);
5834 return r;
5835 }
5836 case SC_SpinBoxDown:
5837 if (downRuleMatch)
5838 return positionRect(w, rule1: rule, rule2: downRule, pe: PseudoElement_SpinBoxDownButton,
5839 rect: opt->rect, dir: opt->direction);
5840 break;
5841 case SC_SpinBoxUp:
5842 if (upRuleMatch)
5843 return positionRect(w, rule1: rule, rule2: upRule, pe: PseudoElement_SpinBoxUpButton,
5844 rect: opt->rect, dir: opt->direction);
5845 break;
5846 default:
5847 break;
5848 }
5849
5850 return baseStyle()->subControlRect(cc, opt, sc, widget: w);
5851 }
5852
5853 QStyleOptionSpinBox spinBox(*spin);
5854 spinBox.rect = rule.borderRect(r: opt->rect);
5855 return rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, opt: &spinBox, sc, widget: w)
5856 : QWindowsStyle::subControlRect(cc, opt: &spinBox, sc, w);
5857 }
5858 break;
5859#endif // QT_CONFIG(spinbox)
5860
5861 case CC_GroupBox:
5862 if (const QStyleOptionGroupBox *gb = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
5863 switch (sc) {
5864 case SC_GroupBoxFrame:
5865 case SC_GroupBoxContents: {
5866 if (rule.hasBox() || !rule.hasNativeBorder()) {
5867 return sc == SC_GroupBoxFrame ? rule.borderRect(r: opt->rect)
5868 : rule.contentsRect(r: opt->rect);
5869 }
5870 QStyleOptionGroupBox groupBox(*gb);
5871 groupBox.rect = rule.borderRect(r: opt->rect);
5872 return baseStyle()->subControlRect(cc, opt: &groupBox, sc, widget: w);
5873 }
5874 default:
5875 case SC_GroupBoxLabel:
5876 case SC_GroupBoxCheckBox: {
5877 QRenderRule indRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_GroupBoxIndicator);
5878 QRenderRule labelRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_GroupBoxTitle);
5879 if (!labelRule.hasPosition() && !labelRule.hasGeometry() && !labelRule.hasBox()
5880 && !labelRule.hasBorder() && !indRule.hasContentsSize()) {
5881 QStyleOptionGroupBox groupBox(*gb);
5882 groupBox.rect = rule.borderRect(r: opt->rect);
5883 return baseStyle()->subControlRect(cc, opt: &groupBox, sc, widget: w);
5884 }
5885 int tw = opt->fontMetrics.horizontalAdvance(gb->text);
5886 int th = opt->fontMetrics.height();
5887 int spacing = pixelMetric(m: QStyle::PM_CheckBoxLabelSpacing, opt, w);
5888 int iw = pixelMetric(m: QStyle::PM_IndicatorWidth, opt, w);
5889 int ih = pixelMetric(m: QStyle::PM_IndicatorHeight, opt, w);
5890
5891 if (gb->subControls & QStyle::SC_GroupBoxCheckBox) {
5892 tw = tw + iw + spacing;
5893 th = qMax(a: th, b: ih);
5894 }
5895 if (!labelRule.hasGeometry()) {
5896 labelRule.geo = new QStyleSheetGeometryData(tw, th, tw, th, -1, -1);
5897 } else {
5898 labelRule.geo->width = tw;
5899 labelRule.geo->height = th;
5900 }
5901 if (!labelRule.hasPosition()) {
5902 labelRule.p = new QStyleSheetPositionData(0, 0, 0, 0, defaultOrigin(pe: PseudoElement_GroupBoxTitle),
5903 gb->textAlignment, PositionMode_Static);
5904 }
5905 QRect r = positionRect(w, rule1: rule, rule2: labelRule, pe: PseudoElement_GroupBoxTitle,
5906 rect: opt->rect, dir: opt->direction);
5907 if (gb->subControls & SC_GroupBoxCheckBox) {
5908 r = labelRule.contentsRect(r);
5909 if (sc == SC_GroupBoxLabel) {
5910 r.setLeft(r.left() + iw + spacing);
5911 r.setTop(r.center().y() - th/2);
5912 } else {
5913 r = QRect(r.left(), r.center().y() - ih/2, iw, ih);
5914 }
5915 return r;
5916 } else {
5917 return labelRule.contentsRect(r);
5918 }
5919 }
5920 } // switch
5921 }
5922 break;
5923
5924 case CC_ToolButton:
5925 if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
5926 if (rule.hasBox() || !rule.hasNativeBorder()) {
5927 switch (sc) {
5928 case SC_ToolButton: return rule.borderRect(r: opt->rect);
5929 case SC_ToolButtonMenu: {
5930 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ToolButtonMenu);
5931 return positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_ToolButtonMenu, rect: opt->rect, dir: opt->direction);
5932 }
5933 default:
5934 break;
5935 }
5936 }
5937
5938 QStyleOptionToolButton tool(*tb);
5939 tool.rect = rule.borderRect(r: opt->rect);
5940 return rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, opt: &tool, sc, widget: w)
5941 : QWindowsStyle::subControlRect(cc, opt: &tool, sc, w);
5942 }
5943 break;
5944
5945#if QT_CONFIG(scrollbar)
5946 case CC_ScrollBar:
5947 if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
5948 QStyleOptionSlider styleOptionSlider(*sb);
5949 styleOptionSlider.rect = rule.borderRect(r: opt->rect);
5950 if (rule.hasDrawable() || rule.hasBox()) {
5951 QRect grooveRect;
5952 if (!rule.hasBox()) {
5953 grooveRect = rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, opt: sb, sc: SC_ScrollBarGroove, widget: w)
5954 : QWindowsStyle::subControlRect(cc, opt: sb, sc: SC_ScrollBarGroove, w);
5955 } else {
5956 grooveRect = rule.contentsRect(r: opt->rect);
5957 }
5958
5959 PseudoElement pe = PseudoElement_None;
5960
5961 switch (sc) {
5962 case SC_ScrollBarGroove:
5963 return grooveRect;
5964 case SC_ScrollBarAddPage:
5965 case SC_ScrollBarSubPage:
5966 case SC_ScrollBarSlider: {
5967 QRect contentRect = grooveRect;
5968 if (hasStyleRule(obj: w, part: PseudoElement_ScrollBarSlider)) {
5969 QRenderRule sliderRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ScrollBarSlider);
5970 Origin origin = sliderRule.hasPosition() ? sliderRule.position()->origin : defaultOrigin(pe: PseudoElement_ScrollBarSlider);
5971 contentRect = rule.originRect(rect: opt->rect, origin);
5972 }
5973 int maxlen = (styleOptionSlider.orientation == Qt::Horizontal) ? contentRect.width() : contentRect.height();
5974 int sliderlen;
5975 if (sb->maximum != sb->minimum) {
5976 uint range = sb->maximum - sb->minimum;
5977 sliderlen = (qint64(sb->pageStep) * maxlen) / (range + sb->pageStep);
5978
5979 int slidermin = pixelMetric(m: PM_ScrollBarSliderMin, opt: sb, w);
5980 if (sliderlen < slidermin || range > INT_MAX / 2)
5981 sliderlen = slidermin;
5982 if (sliderlen > maxlen)
5983 sliderlen = maxlen;
5984 } else {
5985 sliderlen = maxlen;
5986 }
5987 int sliderstart = (styleOptionSlider.orientation == Qt::Horizontal ? contentRect.left() : contentRect.top())
5988 + sliderPositionFromValue(min: sb->minimum, max: sb->maximum, val: sb->sliderPosition,
5989 space: maxlen - sliderlen, upsideDown: sb->upsideDown);
5990
5991 QRect sr = (sb->orientation == Qt::Horizontal)
5992 ? QRect(sliderstart, contentRect.top(), sliderlen, contentRect.height())
5993 : QRect(contentRect.left(), sliderstart, contentRect.width(), sliderlen);
5994 if (sc == SC_ScrollBarSubPage)
5995 sr = QRect(contentRect.topLeft(), sb->orientation == Qt::Horizontal ? sr.bottomLeft() : sr.topRight());
5996 else if (sc == SC_ScrollBarAddPage)
5997 sr = QRect(sb->orientation == Qt::Horizontal ? sr.topRight() : sr.bottomLeft(), contentRect.bottomRight());
5998 return visualRect(direction: styleOptionSlider.direction, boundingRect: grooveRect, logicalRect: sr);
5999 }
6000 case SC_ScrollBarAddLine: pe = PseudoElement_ScrollBarAddLine; break;
6001 case SC_ScrollBarSubLine: pe = PseudoElement_ScrollBarSubLine; break;
6002 case SC_ScrollBarFirst: pe = PseudoElement_ScrollBarFirst; break;
6003 case SC_ScrollBarLast: pe = PseudoElement_ScrollBarLast; break;
6004 default: break;
6005 }
6006 if (hasStyleRule(obj: w,part: pe)) {
6007 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: pe);
6008 if (subRule.hasPosition() || subRule.hasGeometry() || subRule.hasBox()) {
6009 const QStyleSheetPositionData *pos = subRule.position();
6010 QRect originRect = grooveRect;
6011 if (rule.hasBox()) {
6012 Origin origin = (pos && pos->origin != Origin_Unknown) ? pos->origin : defaultOrigin(pe);
6013 originRect = rule.originRect(rect: opt->rect, origin);
6014 }
6015 return positionRect(w, rule2: subRule, pe, originRect, dir: styleOptionSlider.direction);
6016 }
6017 }
6018 }
6019 return rule.baseStyleCanDraw() ? baseStyle()->subControlRect(cc, opt: &styleOptionSlider, sc, widget: w)
6020 : QWindowsStyle::subControlRect(cc, opt: &styleOptionSlider, sc, w);
6021 }
6022 break;
6023#endif // QT_CONFIG(scrollbar)
6024
6025#if QT_CONFIG(slider)
6026 case CC_Slider:
6027 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
6028 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_SliderGroove);
6029 if (!subRule.hasDrawable())
6030 break;
6031 subRule.img = nullptr;
6032 QRect gr = positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_SliderGroove, rect: opt->rect, dir: opt->direction);
6033 switch (sc) {
6034 case SC_SliderGroove:
6035 return gr;
6036 case SC_SliderHandle: {
6037 bool horizontal = slider->orientation & Qt::Horizontal;
6038 QRect cr = subRule.contentsRect(r: gr);
6039 QRenderRule subRule2 = renderRule(obj: w, opt, pseudoElement: PseudoElement_SliderHandle);
6040 int len = horizontal ? subRule2.size().width() : subRule2.size().height();
6041 subRule2.img = nullptr;
6042 subRule2.geo = nullptr;
6043 cr = positionRect(w, rule2: subRule2, pe: PseudoElement_SliderHandle, originRect: cr, dir: opt->direction);
6044 int thickness = horizontal ? cr.height() : cr.width();
6045 int sliderPos = sliderPositionFromValue(min: slider->minimum, max: slider->maximum, val: slider->sliderPosition,
6046 space: (horizontal ? cr.width() : cr.height()) - len, upsideDown: slider->upsideDown);
6047 cr = horizontal ? QRect(cr.x() + sliderPos, cr.y(), len, thickness)
6048 : QRect(cr.x(), cr.y() + sliderPos, thickness, len);
6049 return subRule2.borderRect(r: cr);
6050 break; }
6051 case SC_SliderTickmarks:
6052 // TODO...
6053 default:
6054 break;
6055 }
6056 }
6057 break;
6058#endif // QT_CONFIG(slider)
6059
6060 case CC_MdiControls:
6061 if (hasStyleRule(obj: w, part: PseudoElement_MdiCloseButton)
6062 || hasStyleRule(obj: w, part: PseudoElement_MdiNormalButton)
6063 || hasStyleRule(obj: w, part: PseudoElement_MdiMinButton)) {
6064 QList<QVariant> layout = rule.styleHint(sh: "button-layout"_L1).toList();
6065 if (layout.isEmpty())
6066 layout = subControlLayout(layout: "mNX"_L1);
6067
6068 int x = 0, width = 0;
6069 QRenderRule subRule;
6070 for (int i = 0; i < layout.size(); i++) {
6071 int layoutButton = layout[i].toInt();
6072 if (layoutButton < PseudoElement_MdiCloseButton
6073 || layoutButton > PseudoElement_MdiNormalButton)
6074 continue;
6075 QStyle::SubControl control = knownPseudoElements[layoutButton].subControl;
6076 if (!(opt->subControls & control))
6077 continue;
6078 subRule = renderRule(obj: w, opt, pseudoElement: layoutButton);
6079 width = subRule.size().width();
6080 if (sc == control)
6081 break;
6082 x += width;
6083 }
6084
6085 return subRule.borderRect(r: QRect(x, opt->rect.top(), width, opt->rect.height()));
6086 }
6087 break;
6088
6089 case CC_TitleBar:
6090 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
6091 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TitleBar);
6092 if (!subRule.hasDrawable() && !subRule.hasBox() && !subRule.hasBorder())
6093 break;
6094 QHash<QStyle::SubControl, QRect> layoutRects = titleBarLayout(w, tb);
6095 return layoutRects.value(key: sc);
6096 }
6097 break;
6098
6099 default:
6100 break;
6101 }
6102
6103 return baseStyle()->subControlRect(cc, opt, sc, widget: w);
6104}
6105
6106QRect QStyleSheetStyle::subElementRect(SubElement se, const QStyleOption *opt, const QWidget *w) const
6107{
6108 RECURSION_GUARD(return baseStyle()->subElementRect(se, opt, w))
6109
6110 QRenderRule rule = renderRule(obj: w, opt);
6111#if QT_CONFIG(tabbar)
6112 int pe = PseudoElement_None;
6113#endif
6114
6115 switch (se) {
6116 case SE_PushButtonContents:
6117 case SE_PushButtonBevel:
6118 case SE_PushButtonFocusRect:
6119 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
6120 if (btn->features & QStyleOptionButton::HasMenu
6121 && hasStyleRule(obj: w, part: PseudoElement_PushButtonMenuIndicator)) {
6122 QStyleOptionButton btnOpt(*btn);
6123 btnOpt.features &= ~QStyleOptionButton::HasMenu;
6124 return rule.baseStyleCanDraw() ? baseStyle()->subElementRect(subElement: se, option: &btnOpt, widget: w)
6125 : QWindowsStyle::subElementRect(r: se, opt: &btnOpt, widget: w);
6126 }
6127 if (rule.hasBox() || !rule.hasNativeBorder()) {
6128 return visualRect(direction: opt->direction, boundingRect: opt->rect, logicalRect: se == SE_PushButtonBevel
6129 ? rule.borderRect(r: opt->rect)
6130 : rule.contentsRect(r: opt->rect));
6131 }
6132 return rule.baseStyleCanDraw() ? baseStyle()->subElementRect(subElement: se, option: btn, widget: w)
6133 : QWindowsStyle::subElementRect(r: se, opt: btn, widget: w);
6134 }
6135 break;
6136
6137 case SE_LineEditContents:
6138 case SE_FrameContents:
6139 case SE_ShapedFrameContents:
6140 if (rule.hasBox() || !rule.hasNativeBorder()) {
6141 return visualRect(direction: opt->direction, boundingRect: opt->rect, logicalRect: rule.contentsRect(r: opt->rect));
6142 }
6143 break;
6144
6145 case SE_CheckBoxIndicator:
6146 case SE_RadioButtonIndicator:
6147 if (rule.hasBox() || rule.hasBorder() || hasStyleRule(obj: w, part: PseudoElement_Indicator)) {
6148 PseudoElement pe = se == SE_CheckBoxIndicator ? PseudoElement_Indicator : PseudoElement_ExclusiveIndicator;
6149 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: pe);
6150 return positionRect(w, rule1: rule, rule2: subRule, pe, rect: opt->rect, dir: opt->direction);
6151 }
6152 break;
6153
6154 case SE_CheckBoxContents:
6155 case SE_RadioButtonContents:
6156 if (rule.hasBox() || rule.hasBorder() || hasStyleRule(obj: w, part: PseudoElement_Indicator)) {
6157 bool isRadio = se == SE_RadioButtonContents;
6158 QRect ir = subElementRect(se: isRadio ? SE_RadioButtonIndicator : SE_CheckBoxIndicator,
6159 opt, w);
6160 ir = visualRect(direction: opt->direction, boundingRect: opt->rect, logicalRect: ir);
6161 int spacing = pixelMetric(m: isRadio ? PM_RadioButtonLabelSpacing : PM_CheckBoxLabelSpacing, opt: nullptr, w);
6162 QRect cr = rule.contentsRect(r: opt->rect);
6163 ir.setRect(ax: ir.left() + ir.width() + spacing, ay: cr.y(),
6164 aw: cr.width() - ir.width() - spacing, ah: cr.height());
6165 return visualRect(direction: opt->direction, boundingRect: opt->rect, logicalRect: ir);
6166 }
6167 break;
6168
6169 case SE_ToolBoxTabContents:
6170 if (w && hasStyleRule(obj: w->parentWidget(), part: PseudoElement_ToolBoxTab)) {
6171 QRenderRule subRule = renderRule(obj: w->parentWidget(), opt, pseudoElement: PseudoElement_ToolBoxTab);
6172 return visualRect(direction: opt->direction, boundingRect: opt->rect, logicalRect: subRule.contentsRect(r: opt->rect));
6173 }
6174 break;
6175
6176 case SE_RadioButtonFocusRect:
6177 case SE_RadioButtonClickRect: // focusrect | indicator
6178 if (rule.hasBox() || rule.hasBorder() || hasStyleRule(obj: w, part: PseudoElement_Indicator)) {
6179 return opt->rect;
6180 }
6181 break;
6182
6183 case SE_CheckBoxFocusRect:
6184 case SE_CheckBoxClickRect: // relies on indicator and contents
6185 return ParentStyle::subElementRect(r: se, opt, widget: w);
6186
6187#if QT_CONFIG(itemviews)
6188 case SE_ItemViewItemCheckIndicator:
6189 if (!qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
6190 return subElementRect(se: SE_CheckBoxIndicator, opt, w);
6191 }
6192 Q_FALLTHROUGH();
6193 case SE_ItemViewItemText:
6194 case SE_ItemViewItemDecoration:
6195 case SE_ItemViewItemFocusRect:
6196 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
6197 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_ViewItem);
6198 PseudoElement pe = PseudoElement_None;
6199 if (se == SE_ItemViewItemText || se == SE_ItemViewItemFocusRect)
6200 pe = PseudoElement_ViewItemText;
6201 else if (se == SE_ItemViewItemDecoration && vopt->features & QStyleOptionViewItem::HasDecoration)
6202 pe = PseudoElement_ViewItemIcon;
6203 else if (se == SE_ItemViewItemCheckIndicator && vopt->features & QStyleOptionViewItem::HasCheckIndicator)
6204 pe = PseudoElement_ViewItemIndicator;
6205 else
6206 break;
6207 if (subRule.hasGeometry() || subRule.hasBox() || !subRule.hasNativeBorder() || hasStyleRule(obj: w, part: pe)) {
6208 QRenderRule subRule2 = renderRule(obj: w, opt, pseudoElement: pe);
6209 QStyleOptionViewItem optCopy(*vopt);
6210 optCopy.rect = subRule.contentsRect(r: vopt->rect);
6211 QRect rect = ParentStyle::subElementRect(r: se, opt: &optCopy, widget: w);
6212 return positionRect(w, rule2: subRule2, pe, originRect: rect, dir: opt->direction);
6213 }
6214 }
6215 break;
6216#endif // QT_CONFIG(itemviews)
6217
6218 case SE_HeaderArrow: {
6219 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_HeaderViewUpArrow);
6220 if (subRule.hasPosition() || subRule.hasGeometry())
6221 return positionRect(w, rule1: rule, rule2: subRule, pe: PseudoElement_HeaderViewUpArrow, rect: opt->rect, dir: opt->direction);
6222 }
6223 break;
6224
6225 case SE_HeaderLabel: {
6226 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_HeaderViewSection);
6227 if (subRule.hasBox() || !subRule.hasNativeBorder())
6228 return subRule.contentsRect(r: opt->rect);
6229 }
6230 break;
6231
6232 case SE_ProgressBarGroove:
6233 case SE_ProgressBarContents:
6234 case SE_ProgressBarLabel:
6235 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
6236 if (rule.hasBox() || !rule.hasNativeBorder() || rule.hasPosition() || hasStyleRule(obj: w, part: PseudoElement_ProgressBarChunk)) {
6237 if (se == SE_ProgressBarGroove)
6238 return rule.borderRect(r: pb->rect);
6239 else if (se == SE_ProgressBarContents)
6240 return rule.contentsRect(r: pb->rect);
6241
6242 QSize sz = pb->fontMetrics.size(flags: 0, str: pb->text);
6243 return QStyle::alignedRect(direction: Qt::LeftToRight, alignment: rule.hasPosition() ? rule.position()->textAlignment : pb->textAlignment,
6244 size: sz, rectangle: pb->rect);
6245 }
6246 }
6247 break;
6248
6249#if QT_CONFIG(tabbar)
6250 case SE_TabWidgetLeftCorner:
6251 pe = PseudoElement_TabWidgetLeftCorner;
6252 Q_FALLTHROUGH();
6253 case SE_TabWidgetRightCorner:
6254 if (pe == PseudoElement_None)
6255 pe = PseudoElement_TabWidgetRightCorner;
6256 Q_FALLTHROUGH();
6257 case SE_TabWidgetTabBar:
6258 if (pe == PseudoElement_None)
6259 pe = PseudoElement_TabWidgetTabBar;
6260 Q_FALLTHROUGH();
6261 case SE_TabWidgetTabPane:
6262 case SE_TabWidgetTabContents:
6263 if (pe == PseudoElement_None)
6264 pe = PseudoElement_TabWidgetPane;
6265
6266 if (hasStyleRule(obj: w, part: pe)) {
6267 QRect r = QWindowsStyle::subElementRect(r: pe == PseudoElement_TabWidgetPane ? SE_TabWidgetTabPane : se, opt, widget: w);
6268 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: pe);
6269 r = positionRect(w, rule2: subRule, pe, originRect: r, dir: opt->direction);
6270 if (pe == PseudoElement_TabWidgetTabBar) {
6271 Q_ASSERT(opt);
6272 r = opt->rect.intersected(other: r);
6273 }
6274 if (se == SE_TabWidgetTabContents)
6275 r = subRule.contentsRect(r);
6276 return r;
6277 }
6278 break;
6279
6280 case SE_TabBarScrollLeftButton:
6281 case SE_TabBarScrollRightButton:
6282 if (hasStyleRule(obj: w, part: PseudoElement_TabBarScroller))
6283 return ParentStyle::subElementRect(r: se, opt, widget: w);
6284 break;
6285
6286 case SE_TabBarTearIndicator: {
6287 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabBarTear);
6288 if (subRule.hasContentsSize()) {
6289 QRect r;
6290 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
6291 switch (tab->shape) {
6292 case QTabBar::RoundedNorth:
6293 case QTabBar::TriangularNorth:
6294 case QTabBar::RoundedSouth:
6295 case QTabBar::TriangularSouth:
6296 r.setRect(ax: tab->rect.left(), ay: tab->rect.top(), aw: subRule.size().width(), ah: opt->rect.height());
6297 break;
6298 case QTabBar::RoundedWest:
6299 case QTabBar::TriangularWest:
6300 case QTabBar::RoundedEast:
6301 case QTabBar::TriangularEast:
6302 r.setRect(ax: tab->rect.left(), ay: tab->rect.top(), aw: opt->rect.width(), ah: subRule.size().height());
6303 break;
6304 default:
6305 break;
6306 }
6307 r = visualRect(direction: opt->direction, boundingRect: opt->rect, logicalRect: r);
6308 }
6309 return r;
6310 }
6311 break;
6312 }
6313 case SE_TabBarTabText:
6314 case SE_TabBarTabLeftButton:
6315 case SE_TabBarTabRightButton: {
6316 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_TabBarTab);
6317 if (subRule.hasBox() || !subRule.hasNativeBorder() || subRule.hasFont) {
6318 if (se == SE_TabBarTabText) {
6319 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
6320 const QTabBar *bar = qobject_cast<const QTabBar *>(object: w);
6321 const QRect optRect = bar && tab->tabIndex != -1 ? bar->tabRect(index: tab->tabIndex) : opt->rect;
6322 const QRect r = positionRect(w, rule2: subRule, pe: PseudoElement_TabBarTab, originRect: optRect, dir: opt->direction);
6323 QStyleOptionTab tabCopy(*tab);
6324 if (subRule.hasFont) {
6325 const QFont ruleFont = w ? subRule.font.resolve(w->font()) : subRule.font;
6326 tabCopy.fontMetrics = QFontMetrics(ruleFont);
6327 }
6328 tabCopy.rect = subRule.contentsRect(r);
6329 return ParentStyle::subElementRect(r: se, opt: &tabCopy, widget: w);
6330 }
6331 }
6332 return ParentStyle::subElementRect(r: se, opt, widget: w);
6333 }
6334 break;
6335 }
6336#endif // QT_CONFIG(tabbar)
6337
6338 case SE_DockWidgetCloseButton:
6339 case SE_DockWidgetFloatButton: {
6340 PseudoElement pe = (se == SE_DockWidgetCloseButton) ? PseudoElement_DockWidgetCloseButton : PseudoElement_DockWidgetFloatButton;
6341 QRenderRule subRule2 = renderRule(obj: w, opt, pseudoElement: pe);
6342 if (!subRule2.hasPosition())
6343 break;
6344 QRenderRule subRule = renderRule(obj: w, opt, pseudoElement: PseudoElement_DockWidgetTitle);
6345 return positionRect(w, rule1: subRule, rule2: subRule2, pe, rect: opt->rect, dir: opt->direction);
6346 }
6347
6348#if QT_CONFIG(toolbar)
6349 case SE_ToolBarHandle:
6350 if (hasStyleRule(obj: w, part: PseudoElement_ToolBarHandle))
6351 return ParentStyle::subElementRect(r: se, opt, widget: w);
6352 break;
6353#endif // QT_CONFIG(toolbar)
6354
6355 // On mac we make pixel adjustments to layouts which are not
6356 // desirable when you have custom style sheets on them
6357 case SE_CheckBoxLayoutItem:
6358 case SE_ComboBoxLayoutItem:
6359 case SE_DateTimeEditLayoutItem:
6360 case SE_LabelLayoutItem:
6361 case SE_ProgressBarLayoutItem:
6362 case SE_PushButtonLayoutItem:
6363 case SE_RadioButtonLayoutItem:
6364 case SE_SliderLayoutItem:
6365 case SE_SpinBoxLayoutItem:
6366 case SE_ToolButtonLayoutItem:
6367 case SE_FrameLayoutItem:
6368 case SE_GroupBoxLayoutItem:
6369 case SE_TabWidgetLayoutItem:
6370 if (!rule.hasNativeBorder())
6371 return opt->rect;
6372 break;
6373
6374 default:
6375 break;
6376 }
6377
6378 return baseStyle()->subElementRect(subElement: se, option: opt, widget: w);
6379}
6380
6381bool QStyleSheetStyle::event(QEvent *e)
6382{
6383 return (baseStyle()->event(event: e) && e->isAccepted()) || ParentStyle::event(event: e);
6384}
6385
6386void QStyleSheetStyle::updateStyleSheetFont(QWidget* w) const
6387{
6388 // Qt's fontDialog relies on the font of the sample edit for its selection,
6389 // we should never override it.
6390 if (w->objectName() == "qt_fontDialog_sampleEdit"_L1)
6391 return;
6392
6393 QWidget *container = containerWidget(w);
6394 QRenderRule rule = renderRule(obj: container, element: PseudoElement_None,
6395 state: PseudoClass_Active | PseudoClass_Enabled | extendedPseudoClass(w: container));
6396
6397 const bool useStyleSheetPropagationInWidgetStyles =
6398 QCoreApplication::testAttribute(attribute: Qt::AA_UseStyleSheetPropagationInWidgetStyles);
6399
6400 if (useStyleSheetPropagationInWidgetStyles) {
6401 unsetStyleSheetFont(w);
6402
6403 if (rule.font.resolveMask()) {
6404 QFont wf = w->d_func()->localFont();
6405 styleSheetCaches->customFontWidgets.insert(key: w, value: {.oldWidgetValue: wf, .resolveMask: rule.font.resolveMask()});
6406
6407 QFont font = rule.font.resolve(wf);
6408 font.setResolveMask(wf.resolveMask() | rule.font.resolveMask());
6409 w->setFont(font);
6410 }
6411 } else {
6412 QFont wf = w->d_func()->localFont();
6413 QFont font = rule.font.resolve(wf);
6414 font.setResolveMask(wf.resolveMask() | rule.font.resolveMask());
6415
6416 if ((!w->isWindow() || w->testAttribute(attribute: Qt::WA_WindowPropagation))
6417 && isNaturalChild(obj: w) && qobject_cast<QWidget *>(o: w->parent())) {
6418
6419 font = font.resolve(static_cast<QWidget *>(w->parent())->font());
6420 }
6421
6422 if (wf.resolveMask() == font.resolveMask() && wf == font)
6423 return;
6424
6425 w->data->fnt = font;
6426 w->d_func()->directFontResolveMask = font.resolveMask();
6427
6428 QEvent e(QEvent::FontChange);
6429 QCoreApplication::sendEvent(receiver: w, event: &e);
6430 }
6431}
6432
6433void QStyleSheetStyle::saveWidgetFont(QWidget* w, const QFont& font) const
6434{
6435 w->setProperty(name: "_q_styleSheetWidgetFont", value: font);
6436}
6437
6438void QStyleSheetStyle::clearWidgetFont(QWidget* w) const
6439{
6440 w->setProperty(name: "_q_styleSheetWidgetFont", value: QVariant());
6441}
6442
6443// Polish palette that should be used for a particular widget, with particular states
6444// (eg. :focus, :hover, ...)
6445// this is called by widgets that paint themself in their paint event
6446// Returns \c true if there is a new palette in pal.
6447bool QStyleSheetStyle::styleSheetPalette(const QWidget* w, const QStyleOption* opt, QPalette* pal)
6448{
6449 if (!w || !opt || !pal)
6450 return false;
6451
6452 RECURSION_GUARD(return false)
6453
6454 w = containerWidget(w);
6455
6456 QRenderRule rule = renderRule(obj: w, element: PseudoElement_None, state: pseudoClass(state: opt->state) | extendedPseudoClass(w));
6457 if (!rule.hasPalette())
6458 return false;
6459
6460 rule.configurePalette(p: pal, fr: QPalette::NoRole, br: QPalette::NoRole);
6461 return true;
6462}
6463
6464Qt::Alignment QStyleSheetStyle::resolveAlignment(Qt::LayoutDirection layDir, Qt::Alignment src)
6465{
6466 if (layDir == Qt::LeftToRight || src & Qt::AlignAbsolute)
6467 return src;
6468
6469 if (src & Qt::AlignLeft) {
6470 src &= ~Qt::AlignLeft;
6471 src |= Qt::AlignRight;
6472 } else if (src & Qt::AlignRight) {
6473 src &= ~Qt::AlignRight;
6474 src |= Qt::AlignLeft;
6475 }
6476 src |= Qt::AlignAbsolute;
6477 return src;
6478}
6479
6480// Returns whether the given QWidget has a "natural" parent, meaning that
6481// the parent contains this child as part of its normal operation.
6482// An example is the QTabBar inside a QTabWidget.
6483// This does not mean that any QTabBar which is a child of QTabWidget will
6484// match, only the one that was created by the QTabWidget initialization
6485// (and hence has the correct object name).
6486bool QStyleSheetStyle::isNaturalChild(const QObject *obj)
6487{
6488 if (obj->objectName().startsWith(s: "qt_"_L1))
6489 return true;
6490
6491 return false;
6492}
6493
6494QPixmap QStyleSheetStyle::loadPixmap(const QString &fileName, const QObject *context)
6495{
6496 qreal ratio = -1.0;
6497 if (const QWidget *widget = qobject_cast<const QWidget *>(o: context)) {
6498 if (QScreen *screen = QApplication::screenAt(point: widget->mapToGlobal(QPoint(0, 0))))
6499 ratio = screen->devicePixelRatio();
6500 }
6501
6502 if (ratio < 0) {
6503 if (const QApplication *app = qApp)
6504 ratio = app->devicePixelRatio();
6505 else
6506 ratio = 1.0;
6507 }
6508
6509 qreal sourceDevicePixelRatio = 1.0;
6510 QString resolvedFileName = qt_findAtNxFile(baseFileName: fileName, targetDevicePixelRatio: ratio, sourceDevicePixelRatio: &sourceDevicePixelRatio);
6511 QPixmap pixmap(resolvedFileName);
6512 pixmap.setDevicePixelRatio(sourceDevicePixelRatio);
6513 return pixmap;
6514}
6515
6516QT_END_NAMESPACE
6517
6518#include "moc_qstylesheetstyle_p.cpp"
6519
6520#endif // QT_CONFIG(style_stylesheet)
6521

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