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