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#include <qtooltip.h>
69#include <qshareddata.h>
70#if QT_CONFIG(toolbutton)
71#include <qtoolbutton.h>
72#endif
73#if QT_CONFIG(scrollbar)
74#include <qscrollbar.h>
75#endif
76#if QT_CONFIG(abstractslider)
77#include <qabstractslider.h>
78#endif
79#include <qstring.h>
80#include <qfile.h>
81#if QT_CONFIG(checkbox)
82#include <qcheckbox.h>
83#endif
84#if QT_CONFIG(itemviews)
85#include <qheaderview.h>
86#endif
87#include <private/qwindowsstyle_p_p.h>
88#if QT_CONFIG(animation)
89#include <private/qstyleanimation_p.h>
90#endif
91#if QT_CONFIG(tabbar)
92#include <qtabbar.h>
93#endif
94#include <QMetaProperty>
95#if QT_CONFIG(mainwindow)
96#include <qmainwindow.h>
97#endif
98#if QT_CONFIG(dockwidget)
99#include <qdockwidget.h>
100#endif
101#if QT_CONFIG(mdiarea)
102#include <qmdisubwindow.h>
103#endif
104#if QT_CONFIG(dialog)
105#include <qdialog.h>
106#endif
107#include <private/qwidget_p.h>
108#if QT_CONFIG(spinbox)
109#include <QAbstractSpinBox>
110#endif
111#if QT_CONFIG(label)
112#include <QLabel>
113#endif
114#include "qdrawutil.h"
115
116#include <limits.h>
117#if QT_CONFIG(toolbar)
118#include <QtWidgets/qtoolbar.h>
119#endif
120
121#include <QtGui/qpainterpath.h>
122#include <QtGui/qscreen.h>
123
124QT_BEGIN_NAMESPACE
125
126using namespace QCss;
127
128
129class QStyleSheetStylePrivate : public QWindowsStylePrivate
130{
131 Q_DECLARE_PUBLIC(QStyleSheetStyle)
132public:
133 QStyleSheetStylePrivate() { }
134};
135
136
137static QStyleSheetStyleCaches *styleSheetCaches = nullptr;
138
139/* RECURSION_GUARD:
140 * the QStyleSheetStyle is a proxy. If used with others proxy style, we may end up with something like:
141 * QStyleSheetStyle -> ProxyStyle -> QStyleSheetStyle -> OriginalStyle
142 * Recursion may happen if the style call the widget()->style() again.
143 * Not to mention the performence penalty of having two lookup of rules.
144 *
145 * The first instance of QStyleSheetStyle will set globalStyleSheetStyle to itself. The second one
146 * will notice the globalStyleSheetStyle is not istelf and call its base style directly.
147 */
148static const QStyleSheetStyle *globalStyleSheetStyle = nullptr;
149class QStyleSheetStyleRecursionGuard
150{
151 public:
152 QStyleSheetStyleRecursionGuard(const QStyleSheetStyle *that)
153 : guarded(globalStyleSheetStyle == nullptr)
154 {
155 if (guarded) globalStyleSheetStyle = that;
156 }
157 ~QStyleSheetStyleRecursionGuard() { if (guarded) globalStyleSheetStyle = nullptr; }
158 bool guarded;
159};
160#define RECURSION_GUARD(RETURN) \
161 if (globalStyleSheetStyle != 0 && globalStyleSheetStyle != this) { RETURN; } \
162 QStyleSheetStyleRecursionGuard recursion_guard(this);
163
164#define ceil(x) ((int)(x) + ((x) > 0 && (x) != (int)(x)))
165
166enum PseudoElement {
167 PseudoElement_None,
168 PseudoElement_DownArrow,
169 PseudoElement_UpArrow,
170 PseudoElement_LeftArrow,
171 PseudoElement_RightArrow,
172 PseudoElement_Indicator,
173 PseudoElement_ExclusiveIndicator,
174 PseudoElement_PushButtonMenuIndicator,
175 PseudoElement_ComboBoxDropDown,
176 PseudoElement_ComboBoxArrow,
177 PseudoElement_Item,
178 PseudoElement_SpinBoxUpButton,
179 PseudoElement_SpinBoxUpArrow,
180 PseudoElement_SpinBoxDownButton,
181 PseudoElement_SpinBoxDownArrow,
182 PseudoElement_GroupBoxTitle,
183 PseudoElement_GroupBoxIndicator,
184 PseudoElement_ToolButtonMenu,
185 PseudoElement_ToolButtonMenuArrow,
186 PseudoElement_ToolButtonDownArrow,
187 PseudoElement_ToolBoxTab,
188 PseudoElement_ScrollBarSlider,
189 PseudoElement_ScrollBarAddPage,
190 PseudoElement_ScrollBarSubPage,
191 PseudoElement_ScrollBarAddLine,
192 PseudoElement_ScrollBarSubLine,
193 PseudoElement_ScrollBarFirst,
194 PseudoElement_ScrollBarLast,
195 PseudoElement_ScrollBarUpArrow,
196 PseudoElement_ScrollBarDownArrow,
197 PseudoElement_ScrollBarLeftArrow,
198 PseudoElement_ScrollBarRightArrow,
199 PseudoElement_SplitterHandle,
200 PseudoElement_ToolBarHandle,
201 PseudoElement_ToolBarSeparator,
202 PseudoElement_MenuScroller,
203 PseudoElement_MenuTearoff,
204 PseudoElement_MenuCheckMark,
205 PseudoElement_MenuSeparator,
206 PseudoElement_MenuIcon,
207 PseudoElement_MenuRightArrow,
208 PseudoElement_TreeViewBranch,
209 PseudoElement_HeaderViewSection,
210 PseudoElement_HeaderViewUpArrow,
211 PseudoElement_HeaderViewDownArrow,
212 PseudoElement_ProgressBarChunk,
213 PseudoElement_TabBarTab,
214 PseudoElement_TabBarScroller,
215 PseudoElement_TabBarTear,
216 PseudoElement_SliderGroove,
217 PseudoElement_SliderHandle,
218 PseudoElement_SliderAddPage,
219 PseudoElement_SliderSubPage,
220 PseudoElement_SliderTickmark,
221 PseudoElement_TabWidgetPane,
222 PseudoElement_TabWidgetTabBar,
223 PseudoElement_TabWidgetLeftCorner,
224 PseudoElement_TabWidgetRightCorner,
225 PseudoElement_DockWidgetTitle,
226 PseudoElement_DockWidgetCloseButton,
227 PseudoElement_DockWidgetFloatButton,
228 PseudoElement_DockWidgetSeparator,
229 PseudoElement_MdiCloseButton,
230 PseudoElement_MdiMinButton,
231 PseudoElement_MdiNormalButton,
232 PseudoElement_TitleBar,
233 PseudoElement_TitleBarCloseButton,
234 PseudoElement_TitleBarMinButton,
235 PseudoElement_TitleBarMaxButton,
236 PseudoElement_TitleBarShadeButton,
237 PseudoElement_TitleBarUnshadeButton,
238 PseudoElement_TitleBarNormalButton,
239 PseudoElement_TitleBarContextHelpButton,
240 PseudoElement_TitleBarSysMenu,
241 PseudoElement_ViewItem,
242 PseudoElement_ViewItemIcon,
243 PseudoElement_ViewItemText,
244 PseudoElement_ViewItemIndicator,
245 PseudoElement_ScrollAreaCorner,
246 PseudoElement_TabBarTabCloseButton,
247 NumPseudoElements
248};
249
250struct PseudoElementInfo {
251 QStyle::SubControl subControl;
252 const char name[19];
253};
254
255static const PseudoElementInfo knownPseudoElements[NumPseudoElements] = {
256 { QStyle::SC_None, "" },
257 { QStyle::SC_None, "down-arrow" },
258 { QStyle::SC_None, "up-arrow" },
259 { QStyle::SC_None, "left-arrow" },
260 { QStyle::SC_None, "right-arrow" },
261 { QStyle::SC_None, "indicator" },
262 { QStyle::SC_None, "indicator" },
263 { QStyle::SC_None, "menu-indicator" },
264 { QStyle::SC_ComboBoxArrow, "drop-down" },
265 { QStyle::SC_ComboBoxArrow, "down-arrow" },
266 { QStyle::SC_None, "item" },
267 { QStyle::SC_SpinBoxUp, "up-button" },
268 { QStyle::SC_SpinBoxUp, "up-arrow" },
269 { QStyle::SC_SpinBoxDown, "down-button" },
270 { QStyle::SC_SpinBoxDown, "down-arrow" },
271 { QStyle::SC_GroupBoxLabel, "title" },
272 { QStyle::SC_GroupBoxCheckBox, "indicator" },
273 { QStyle::SC_ToolButtonMenu, "menu-button" },
274 { QStyle::SC_ToolButtonMenu, "menu-arrow" },
275 { QStyle::SC_None, "menu-indicator" },
276 { QStyle::SC_None, "tab" },
277 { QStyle::SC_ScrollBarSlider, "handle" },
278 { QStyle::SC_ScrollBarAddPage, "add-page" },
279 { QStyle::SC_ScrollBarSubPage, "sub-page" },
280 { QStyle::SC_ScrollBarAddLine, "add-line" },
281 { QStyle::SC_ScrollBarSubLine, "sub-line" },
282 { QStyle::SC_ScrollBarFirst, "first" },
283 { QStyle::SC_ScrollBarLast, "last" },
284 { QStyle::SC_ScrollBarSubLine, "up-arrow" },
285 { QStyle::SC_ScrollBarAddLine, "down-arrow" },
286 { QStyle::SC_ScrollBarSubLine, "left-arrow" },
287 { QStyle::SC_ScrollBarAddLine, "right-arrow" },
288 { QStyle::SC_None, "handle" },
289 { QStyle::SC_None, "handle" },
290 { QStyle::SC_None, "separator" },
291 { QStyle::SC_None, "scroller" },
292 { QStyle::SC_None, "tearoff" },
293 { QStyle::SC_None, "indicator" },
294 { QStyle::SC_None, "separator" },
295 { QStyle::SC_None, "icon" },
296 { QStyle::SC_None, "right-arrow" },
297 { QStyle::SC_None, "branch" },
298 { QStyle::SC_None, "section" },
299 { QStyle::SC_None, "down-arrow" },
300 { QStyle::SC_None, "up-arrow" },
301 { QStyle::SC_None, "chunk" },
302 { QStyle::SC_None, "tab" },
303 { QStyle::SC_None, "scroller" },
304 { QStyle::SC_None, "tear" },
305 { QStyle::SC_SliderGroove, "groove" },
306 { QStyle::SC_SliderHandle, "handle" },
307 { QStyle::SC_None, "add-page" },
308 { QStyle::SC_None, "sub-page"},
309 { QStyle::SC_SliderTickmarks, "tick-mark" },
310 { QStyle::SC_None, "pane" },
311 { QStyle::SC_None, "tab-bar" },
312 { QStyle::SC_None, "left-corner" },
313 { QStyle::SC_None, "right-corner" },
314 { QStyle::SC_None, "title" },
315 { QStyle::SC_None, "close-button" },
316 { QStyle::SC_None, "float-button" },
317 { QStyle::SC_None, "separator" },
318 { QStyle::SC_MdiCloseButton, "close-button" },
319 { QStyle::SC_MdiMinButton, "minimize-button" },
320 { QStyle::SC_MdiNormalButton, "normal-button" },
321 { QStyle::SC_TitleBarLabel, "title" },
322 { QStyle::SC_TitleBarCloseButton, "close-button" },
323 { QStyle::SC_TitleBarMinButton, "minimize-button" },
324 { QStyle::SC_TitleBarMaxButton, "maximize-button" },
325 { QStyle::SC_TitleBarShadeButton, "shade-button" },
326 { QStyle::SC_TitleBarUnshadeButton, "unshade-button" },
327 { QStyle::SC_TitleBarNormalButton, "normal-button" },
328 { QStyle::SC_TitleBarContextHelpButton, "contexthelp-button" },
329 { QStyle::SC_TitleBarSysMenu, "sys-menu" },
330 { QStyle::SC_None, "item" },
331 { QStyle::SC_None, "icon" },
332 { QStyle::SC_None, "text" },
333 { QStyle::SC_None, "indicator" },
334 { QStyle::SC_None, "corner" },
335 { QStyle::SC_None, "close-button" },
336};
337
338
339struct QStyleSheetBorderImageData : public QSharedData
340{
341 QStyleSheetBorderImageData()
342 : horizStretch(QCss::TileMode_Unknown), vertStretch(QCss::TileMode_Unknown)
343 {
344 for (int i = 0; i < 4; i++)
345 cuts[i] = -1;
346 }
347 int cuts[4];
348 QPixmap pixmap;
349 QImage image;
350 QCss::TileMode horizStretch, vertStretch;
351};
352
353struct QStyleSheetBackgroundData : public QSharedData
354{
355 QStyleSheetBackgroundData(const QBrush& b, const QPixmap& p, QCss::Repeat r,
356 Qt::Alignment a, QCss::Origin o, Attachment t, QCss::Origin c)
357 : brush(b), pixmap(p), repeat(r), position(a), origin(o), attachment(t), clip(c) { }
358
359 bool isTransparent() const {
360 if (brush.style() != Qt::NoBrush)
361 return !brush.isOpaque();
362 return pixmap.isNull() ? false : pixmap.hasAlpha();
363 }
364 QBrush brush;
365 QPixmap pixmap;
366 QCss::Repeat repeat;
367 Qt::Alignment position;
368 QCss::Origin origin;
369 QCss::Attachment attachment;
370 QCss::Origin clip;
371};
372
373struct QStyleSheetBorderData : public QSharedData
374{
375 QStyleSheetBorderData() : bi(nullptr)
376 {
377 for (int i = 0; i < 4; i++) {
378 borders[i] = 0;
379 styles[i] = QCss::BorderStyle_None;
380 }
381 }
382
383 QStyleSheetBorderData(int *b, QBrush *c, QCss::BorderStyle *s, QSize *r) : bi(nullptr)
384 {
385 for (int i = 0; i < 4; i++) {
386 borders[i] = b[i];
387 styles[i] = s[i];
388 colors[i] = c[i];
389 radii[i] = r[i];
390 }
391 }
392
393 int borders[4];
394 QBrush colors[4];
395 QCss::BorderStyle styles[4];
396 QSize radii[4]; // topleft, topright, bottomleft, bottomright
397
398 const QStyleSheetBorderImageData *borderImage() const
399 { return bi; }
400 bool hasBorderImage() const { return bi!=nullptr; }
401
402 QSharedDataPointer<QStyleSheetBorderImageData> bi;
403
404 bool isOpaque() const
405 {
406 for (int i = 0; i < 4; i++) {
407 if (styles[i] == QCss::BorderStyle_Native || styles[i] == QCss::BorderStyle_None)
408 continue;
409 if (styles[i] >= QCss::BorderStyle_Dotted && styles[i] <= QCss::BorderStyle_DotDotDash
410 && styles[i] != BorderStyle_Solid)
411 return false;
412 if (!colors[i].isOpaque())
413 return false;
414 if (!radii[i].isEmpty())
415 return false;
416 }
417 if (bi != nullptr && bi->pixmap.hasAlpha())
418 return false;
419 return true;
420 }
421};
422
423
424struct QStyleSheetOutlineData : public QStyleSheetBorderData
425{
426 QStyleSheetOutlineData()
427 {
428 for (int i = 0; i < 4; i++) {
429 offsets[i] = 0;
430 }
431 }
432
433 QStyleSheetOutlineData(int *b, QBrush *c, QCss::BorderStyle *s, QSize *r, int *o)
434 : QStyleSheetBorderData(b, c, s, r)
435 {
436 for (int i = 0; i < 4; i++) {
437 offsets[i] = o[i];
438 }
439 }
440
441 int offsets[4];
442};
443
444struct QStyleSheetBoxData : public QSharedData
445{
446 QStyleSheetBoxData(int *m, int *p, int s) : spacing(s)
447 {
448 for (int i = 0; i < 4; i++) {
449 margins[i] = m[i];
450 paddings[i] = p[i];
451 }
452 }
453
454 int margins[4];
455 int paddings[4];
456
457 int spacing;
458};
459
460struct QStyleSheetPaletteData : public QSharedData
461{
462 QStyleSheetPaletteData(const QBrush &fg, const QBrush &sfg, const QBrush &sbg,
463 const QBrush &abg)
464 : foreground(fg), selectionForeground(sfg), selectionBackground(sbg),
465 alternateBackground(abg) { }
466
467 QBrush foreground;
468 QBrush selectionForeground;
469 QBrush selectionBackground;
470 QBrush alternateBackground;
471};
472
473struct QStyleSheetGeometryData : public QSharedData
474{
475 QStyleSheetGeometryData(int w, int h, int minw, int minh, int maxw, int maxh)
476 : minWidth(minw), minHeight(minh), width(w), height(h), maxWidth(maxw), maxHeight(maxh) { }
477
478 int minWidth, minHeight, width, height, maxWidth, maxHeight;
479};
480
481struct QStyleSheetPositionData : public QSharedData
482{
483 QStyleSheetPositionData(int l, int t, int r, int b, Origin o, Qt::Alignment p, QCss::PositionMode m, Qt::Alignment a = { })
484 : left(l), top(t), bottom(b), right(r), origin(o), position(p), mode(m), textAlignment(a) { }
485
486 int left, top, bottom, right;
487 Origin origin;
488 Qt::Alignment position;
489 QCss::PositionMode mode;
490 Qt::Alignment textAlignment;
491};
492
493struct QStyleSheetImageData : public QSharedData
494{
495 QStyleSheetImageData(const QIcon &i, Qt::Alignment a, const QSize &sz)
496 : icon(i), alignment(a), size(sz) { }
497
498 QIcon icon;
499 Qt::Alignment alignment;
500 QSize size;
501};
502
503class QRenderRule
504{
505public:
506 QRenderRule() : features(0), hasFont(false), pal(nullptr), b(nullptr), bg(nullptr), bd(nullptr), ou(nullptr), geo(nullptr), p(nullptr), img(nullptr), clipset(0) { }
507 QRenderRule(const QVector<QCss::Declaration> &, const QObject *);
508
509 QRect borderRect(const QRect &r) const;
510 QRect outlineRect(const QRect &r) const;
511 QRect paddingRect(const QRect &r) const;
512 QRect contentsRect(const QRect &r) const;
513
514 enum { Margin = 1, Border = 2, Padding = 4, All=Margin|Border|Padding };
515 QRect boxRect(const QRect &r, int flags = All) const;
516 QSize boxSize(const QSize &s, int flags = All) const;
517 QRect originRect(const QRect &rect, Origin origin) const;
518
519 QPainterPath borderClip(QRect rect);
520 void drawBorder(QPainter *, const QRect&);
521 void drawOutline(QPainter *, const QRect&);
522 void drawBorderImage(QPainter *, const QRect&);
523 void drawBackground(QPainter *, const QRect&, const QPoint& = QPoint(0, 0));
524 void drawBackgroundImage(QPainter *, const QRect&, QPoint = QPoint(0, 0));
525 void drawFrame(QPainter *, const QRect&);
526 void drawImage(QPainter *p, const QRect &rect);
527 void drawRule(QPainter *, const QRect&);
528 void configurePalette(QPalette *, QPalette::ColorGroup, const QWidget *, bool);
529 void configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette::ColorRole br);
530
531 const QStyleSheetPaletteData *palette() const { return pal; }
532 const QStyleSheetBoxData *box() const { return b; }
533 const QStyleSheetBackgroundData *background() const { return bg; }
534 const QStyleSheetBorderData *border() const { return bd; }
535 const QStyleSheetOutlineData *outline() const { return ou; }
536 const QStyleSheetGeometryData *geometry() const { return geo; }
537 const QStyleSheetPositionData *position() const { return p; }
538 const QStyleSheetImageData *icon() const { return iconPtr; }
539
540 bool hasModification() const;
541
542 bool hasPalette() const { return pal != nullptr; }
543 bool hasBackground() const { return bg != nullptr && (!bg->pixmap.isNull() || bg->brush.style() != Qt::NoBrush); }
544 bool hasGradientBackground() const { return bg && bg->brush.style() >= Qt::LinearGradientPattern
545 && bg->brush.style() <= Qt::ConicalGradientPattern; }
546
547 bool hasNativeBorder() const {
548 return bd == nullptr
549 || (!bd->hasBorderImage() && bd->styles[0] == BorderStyle_Native);
550 }
551
552 bool hasNativeOutline() const {
553 return (ou == nullptr
554 || (!ou->hasBorderImage() && ou->styles[0] == BorderStyle_Native));
555 }
556
557 bool baseStyleCanDraw() const {
558 if (!hasBackground() || (background()->brush.style() == Qt::NoBrush && bg->pixmap.isNull()))
559 return true;
560 if (bg && !bg->pixmap.isNull())
561 return false;
562 if (hasGradientBackground())
563 return features & StyleFeature_BackgroundGradient;
564 return features & StyleFeature_BackgroundColor;
565 }
566
567 bool hasBox() const { return b != nullptr; }
568 bool hasBorder() const { return bd != nullptr; }
569 bool hasOutline() const { return ou != nullptr; }
570 bool hasPosition() const { return p != nullptr; }
571 bool hasGeometry() const { return geo != nullptr; }
572 bool hasDrawable() const { return !hasNativeBorder() || hasBackground() || hasImage(); }
573 bool hasImage() const { return img != nullptr; }
574 bool hasIcon() const { return iconPtr != nullptr; }
575
576 QSize minimumContentsSize() const
577 { return geo ? QSize(geo->minWidth, geo->minHeight) : QSize(0, 0); }
578 QSize minimumSize() const
579 { return boxSize(minimumContentsSize()); }
580
581 QSize contentsSize() const
582 { return geo ? QSize(geo->width, geo->height)
583 : ((img && img->size.isValid()) ? img->size : QSize()); }
584 QSize contentsSize(const QSize &sz) const
585 {
586 QSize csz = contentsSize();
587 if (csz.width() == -1) csz.setWidth(sz.width());
588 if (csz.height() == -1) csz.setHeight(sz.height());
589 return csz;
590 }
591 bool hasContentsSize() const
592 { return (geo && (geo->width != -1 || geo->height != -1)) || (img && img->size.isValid()); }
593
594 QSize size() const { return boxSize(contentsSize()); }
595 QSize size(const QSize &sz) const { return boxSize(contentsSize(sz)); }
596 QSize adjustSize(const QSize &sz)
597 {
598 if (!geo)
599 return sz;
600 QSize csz = contentsSize();
601 if (csz.width() == -1) csz.setWidth(sz.width());
602 if (csz.height() == -1) csz.setHeight(sz.height());
603 if (geo->maxWidth != -1 && csz.width() > geo->maxWidth) csz.setWidth(geo->maxWidth);
604 if (geo->maxHeight != -1 && csz.height() > geo->maxHeight) csz.setHeight(geo->maxHeight);
605 csz=csz.expandedTo(QSize(geo->minWidth, geo->minHeight));
606 return csz;
607 }
608
609 bool hasStyleHint(const QString &sh) const { return styleHints.contains(sh); }
610 QVariant styleHint(const QString &sh) const { return styleHints.value(sh); }
611
612 void fixupBorder(int);
613
614 // Shouldn't be here
615 void setClip(QPainter *p, const QRect &rect);
616 void unsetClip(QPainter *);
617
618public:
619 int features;
620 QBrush defaultBackground;
621 QFont font; // Be careful using this font directly. Prefer using font.resolve( )
622 bool hasFont;
623
624 QHash<QString, QVariant> styleHints;
625
626 QSharedDataPointer<QStyleSheetPaletteData> pal;
627 QSharedDataPointer<QStyleSheetBoxData> b;
628 QSharedDataPointer<QStyleSheetBackgroundData> bg;
629 QSharedDataPointer<QStyleSheetBorderData> bd;
630 QSharedDataPointer<QStyleSheetOutlineData> ou;
631 QSharedDataPointer<QStyleSheetGeometryData> geo;
632 QSharedDataPointer<QStyleSheetPositionData> p;
633 QSharedDataPointer<QStyleSheetImageData> img;
634 QSharedDataPointer<QStyleSheetImageData> iconPtr;
635
636 int clipset;
637 QPainterPath clipPath;
638};
639Q_DECLARE_TYPEINFO(QRenderRule, Q_MOVABLE_TYPE);
640
641///////////////////////////////////////////////////////////////////////////////////////////
642static const char knownStyleHints[][45] = {
643 "activate-on-singleclick",
644 "alignment",
645 "arrow-keys-navigate-into-children",
646 "backward-icon",
647 "button-layout",
648 "cd-icon",
649 "combobox-list-mousetracking",
650 "combobox-popup",
651 "computer-icon",
652 "desktop-icon",
653 "dialog-apply-icon",
654 "dialog-cancel-icon",
655 "dialog-close-icon",
656 "dialog-discard-icon",
657 "dialog-help-icon",
658 "dialog-no-icon",
659 "dialog-ok-icon",
660 "dialog-open-icon",
661 "dialog-reset-icon",
662 "dialog-save-icon",
663 "dialog-yes-icon",
664 "dialogbuttonbox-buttons-have-icons",
665 "directory-closed-icon",
666 "directory-icon",
667 "directory-link-icon",
668 "directory-open-icon",
669 "dither-disable-text",
670 "dockwidget-close-icon",
671 "downarrow-icon",
672 "dvd-icon",
673 "etch-disabled-text",
674 "file-icon",
675 "file-link-icon",
676 "filedialog-backward-icon", // unused
677 "filedialog-contentsview-icon",
678 "filedialog-detailedview-icon",
679 "filedialog-end-icon",
680 "filedialog-infoview-icon",
681 "filedialog-listview-icon",
682 "filedialog-new-directory-icon",
683 "filedialog-parent-directory-icon",
684 "filedialog-start-icon",
685 "floppy-icon",
686 "forward-icon",
687 "gridline-color",
688 "harddisk-icon",
689 "home-icon",
690 "icon-size",
691 "leftarrow-icon",
692 "lineedit-password-character",
693 "lineedit-password-mask-delay",
694 "mdi-fill-space-on-maximize",
695 "menu-scrollable",
696 "menubar-altkey-navigation",
697 "menubar-separator",
698 "messagebox-critical-icon",
699 "messagebox-information-icon",
700 "messagebox-question-icon",
701 "messagebox-text-interaction-flags",
702 "messagebox-warning-icon",
703 "mouse-tracking",
704 "network-icon",
705 "opacity",
706 "paint-alternating-row-colors-for-empty-area",
707 "rightarrow-icon",
708 "scrollbar-contextmenu",
709 "scrollbar-leftclick-absolute-position",
710 "scrollbar-middleclick-absolute-position",
711 "scrollbar-roll-between-buttons",
712 "scrollbar-scroll-when-pointer-leaves-control",
713 "scrollview-frame-around-contents",
714 "show-decoration-selected",
715 "spinbox-click-autorepeat-rate",
716 "spincontrol-disable-on-bounds",
717 "tabbar-elide-mode",
718 "tabbar-prefer-no-arrows",
719 "titlebar-close-icon",
720 "titlebar-contexthelp-icon",
721 "titlebar-maximize-icon",
722 "titlebar-menu-icon",
723 "titlebar-minimize-icon",
724 "titlebar-normal-icon",
725 "titlebar-shade-icon",
726 "titlebar-show-tooltips-on-buttons",
727 "titlebar-unshade-icon",
728 "toolbutton-popup-delay",
729 "trash-icon",
730 "uparrow-icon",
731 "widget-animation-duration"
732};
733
734static const int numKnownStyleHints = sizeof(knownStyleHints)/sizeof(knownStyleHints[0]);
735
736static QList<QVariant> subControlLayout(const QString& layout)
737{
738 QList<QVariant> buttons;
739 for (int i = 0; i < layout.count(); i++) {
740 int button = layout[i].toLatin1();
741 switch (button) {
742 case 'm':
743 buttons.append(PseudoElement_MdiMinButton);
744 buttons.append(PseudoElement_TitleBarMinButton);
745 break;
746 case 'M':
747 buttons.append(PseudoElement_TitleBarMaxButton);
748 break;
749 case 'X':
750 buttons.append(PseudoElement_MdiCloseButton);
751 buttons.append(PseudoElement_TitleBarCloseButton);
752 break;
753 case 'N':
754 buttons.append(PseudoElement_MdiNormalButton);
755 buttons.append(PseudoElement_TitleBarNormalButton);
756 break;
757 case 'I':
758 buttons.append(PseudoElement_TitleBarSysMenu);
759 break;
760 case 'T':
761 buttons.append(PseudoElement_TitleBar);
762 break;
763 case 'H':
764 buttons.append(PseudoElement_TitleBarContextHelpButton);
765 break;
766 case 'S':
767 buttons.append(PseudoElement_TitleBarShadeButton);
768 break;
769 default:
770 buttons.append(button);
771 break;
772 }
773 }
774 return buttons;
775}
776
777namespace {
778 struct ButtonInfo {
779 QRenderRule rule;
780 int element;
781 int offset;
782 int where;
783 int width;
784 };
785}
786template <> class QTypeInfo<ButtonInfo> : public QTypeInfoMerger<ButtonInfo, QRenderRule, int> {};
787
788QHash<QStyle::SubControl, QRect> QStyleSheetStyle::titleBarLayout(const QWidget *w, const QStyleOptionTitleBar *tb) const
789{
790 QHash<QStyle::SubControl, QRect> layoutRects;
791 const bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
792 const bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
793 QRenderRule subRule = renderRule(w, tb);
794 QRect cr = subRule.contentsRect(tb->rect);
795 QList<QVariant> layout = subRule.styleHint(QLatin1String("button-layout")).toList();
796 if (layout.isEmpty())
797 layout = subControlLayout(QLatin1String("I(T)HSmMX"));
798
799 int offsets[3] = { 0, 0, 0 };
800 enum Where { Left, Right, Center, NoWhere } where = Left;
801 QVector<ButtonInfo> infos;
802 const int numLayouts = layout.size();
803 infos.reserve(numLayouts);
804 for (int i = 0; i < numLayouts; i++) {
805 const int element = layout[i].toInt();
806 if (element == '(') {
807 where = Center;
808 } else if (element == ')') {
809 where = Right;
810 } else {
811 ButtonInfo info;
812 info.element = element;
813 switch (element) {
814 case PseudoElement_TitleBar:
815 if (!(tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)))
816 continue;
817 break;
818 case PseudoElement_TitleBarContextHelpButton:
819 if (!(tb->titleBarFlags & Qt::WindowContextHelpButtonHint))
820 continue;
821 break;
822 case PseudoElement_TitleBarMinButton:
823 if (!(tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
824 continue;
825 if (isMinimized)
826 info.element = PseudoElement_TitleBarNormalButton;
827 break;
828 case PseudoElement_TitleBarMaxButton:
829 if (!(tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
830 continue;
831 if (isMaximized)
832 info.element = PseudoElement_TitleBarNormalButton;
833 break;
834 case PseudoElement_TitleBarShadeButton:
835 if (!(tb->titleBarFlags & Qt::WindowShadeButtonHint))
836 continue;
837 if (isMinimized)
838 info.element = PseudoElement_TitleBarUnshadeButton;
839 break;
840 case PseudoElement_TitleBarCloseButton:
841 case PseudoElement_TitleBarSysMenu:
842 if (!(tb->titleBarFlags & Qt::WindowSystemMenuHint))
843 continue;
844 break;
845 default:
846 continue;
847 }
848 if (info.element == PseudoElement_TitleBar) {
849 info.width = tb->fontMetrics.horizontalAdvance(tb->text) + 6;
850 subRule.geo = new QStyleSheetGeometryData(info.width, tb->fontMetrics.height(), -1, -1, -1, -1);
851 } else {
852 subRule = renderRule(w, tb, info.element);
853 info.width = subRule.size().width();
854 }
855 info.rule = subRule;
856 info.offset = offsets[where];
857 info.where = where;
858 infos.append(std::move(info));
859
860 offsets[where] += info.width;
861 }
862 }
863
864 for (int i = 0; i < infos.size(); i++) {
865 const ButtonInfo &info = infos[i];
866 QRect lr = cr;
867 switch (info.where) {
868 case Center: {
869 lr.setLeft(cr.left() + offsets[Left]);
870 lr.setRight(cr.right() - offsets[Right]);
871 QRect r(0, 0, offsets[Center], lr.height());
872 r.moveCenter(lr.center());
873 r.setLeft(r.left()+info.offset);
874 r.setWidth(info.width);
875 lr = r;
876 break; }
877 case Left:
878 lr.translate(info.offset, 0);
879 lr.setWidth(info.width);
880 break;
881 case Right:
882 lr.moveLeft(cr.right() + 1 - offsets[Right] + info.offset);
883 lr.setWidth(info.width);
884 break;
885 default:
886 break;
887 }
888 QStyle::SubControl control = knownPseudoElements[info.element].subControl;
889 layoutRects[control] = positionRect(w, info.rule, info.element, lr, tb->direction);
890 }
891
892 return layoutRects;
893}
894
895static QStyle::StandardPixmap subControlIcon(int pe)
896{
897 switch (pe) {
898 case PseudoElement_MdiCloseButton: return QStyle::SP_TitleBarCloseButton;
899 case PseudoElement_MdiMinButton: return QStyle::SP_TitleBarMinButton;
900 case PseudoElement_MdiNormalButton: return QStyle::SP_TitleBarNormalButton;
901 case PseudoElement_TitleBarCloseButton: return QStyle::SP_TitleBarCloseButton;
902 case PseudoElement_TitleBarMinButton: return QStyle::SP_TitleBarMinButton;
903 case PseudoElement_TitleBarMaxButton: return QStyle::SP_TitleBarMaxButton;
904 case PseudoElement_TitleBarShadeButton: return QStyle::SP_TitleBarShadeButton;
905 case PseudoElement_TitleBarUnshadeButton: return QStyle::SP_TitleBarUnshadeButton;
906 case PseudoElement_TitleBarNormalButton: return QStyle::SP_TitleBarNormalButton;
907 case PseudoElement_TitleBarContextHelpButton: return QStyle::SP_TitleBarContextHelpButton;
908 default: break;
909 }
910 return QStyle::SP_CustomBase;
911}
912
913QRenderRule::QRenderRule(const QVector<Declaration> &declarations, const QObject *object)
914: features(0), hasFont(false), pal(nullptr), b(nullptr), bg(nullptr), bd(nullptr), ou(nullptr), geo(nullptr), p(nullptr), img(nullptr), clipset(0)
915{
916 QPalette palette = QGuiApplication::palette(); // ###: ideally widget's palette
917 ValueExtractor v(declarations, palette);
918 features = v.extractStyleFeatures();
919
920 int w = -1, h = -1, minw = -1, minh = -1, maxw = -1, maxh = -1;
921 if (v.extractGeometry(&w, &h, &minw, &minh, &maxw, &maxh))
922 geo = new QStyleSheetGeometryData(w, h, minw, minh, maxw, maxh);
923
924 int left = 0, top = 0, right = 0, bottom = 0;
925 Origin origin = Origin_Unknown;
926 Qt::Alignment position;
927 QCss::PositionMode mode = PositionMode_Unknown;
928 Qt::Alignment textAlignment;
929 if (v.extractPosition(&left, &top, &right, &bottom, &origin, &position, &mode, &textAlignment))
930 p = new QStyleSheetPositionData(left, top, right, bottom, origin, position, mode, textAlignment);
931
932 int margins[4], paddings[4], spacing = -1;
933 for (int i = 0; i < 4; i++)
934 margins[i] = paddings[i] = 0;
935 if (v.extractBox(margins, paddings, &spacing))
936 b = new QStyleSheetBoxData(margins, paddings, spacing);
937
938 int borders[4];
939 QBrush colors[4];
940 QCss::BorderStyle styles[4];
941 QSize radii[4];
942 for (int i = 0; i < 4; i++) {
943 borders[i] = 0;
944 styles[i] = BorderStyle_None;
945 }
946 if (v.extractBorder(borders, colors, styles, radii))
947 bd = new QStyleSheetBorderData(borders, colors, styles, radii);
948
949 int offsets[4];
950 for (int i = 0; i < 4; i++) {
951 borders[i] = offsets[i] = 0;
952 styles[i] = BorderStyle_None;
953 }
954 if (v.extractOutline(borders, colors, styles, radii, offsets))
955 ou = new QStyleSheetOutlineData(borders, colors, styles, radii, offsets);
956
957 QBrush brush;
958 QString uri;
959 Repeat repeat = Repeat_XY;
960 Qt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft;
961 Attachment attachment = Attachment_Scroll;
962 origin = Origin_Padding;
963 Origin clip = Origin_Border;
964 if (v.extractBackground(&brush, &uri, &repeat, &alignment, &origin, &attachment, &clip)) {
965 QPixmap pixmap = QStyleSheetStyle::loadPixmap(uri, object);
966 if (!uri.isEmpty() && pixmap.isNull())
967 qWarning("Could not create pixmap from %s", qPrintable(QDir::toNativeSeparators(uri)));
968 bg = new QStyleSheetBackgroundData(brush, pixmap, repeat, alignment, origin, attachment, clip);
969 }
970
971 QBrush sfg, fg;
972 QBrush sbg, abg;
973 if (v.extractPalette(&fg, &sfg, &sbg, &abg))
974 pal = new QStyleSheetPaletteData(fg, sfg, sbg, abg);
975
976 QIcon imgIcon;
977 alignment = Qt::AlignCenter;
978 QSize imgSize;
979 if (v.extractImage(&imgIcon, &alignment, &imgSize))
980 img = new QStyleSheetImageData(imgIcon, alignment, imgSize);
981
982 QIcon icon;
983 QSize size;
984 if (v.extractIcon(&icon, &size))
985 iconPtr = new QStyleSheetImageData(icon, Qt::AlignCenter, size);
986
987 int adj = -255;
988 hasFont = v.extractFont(&font, &adj);
989
990#ifndef QT_NO_TOOLTIP
991 if (object && qstrcmp(object->metaObject()->className(), "QTipLabel") == 0)
992 palette = QToolTip::palette();
993#endif
994
995 for (int i = 0; i < declarations.count(); i++) {
996 const Declaration& decl = declarations.at(i);
997 if (decl.d->propertyId == BorderImage) {
998 QString uri;
999 QCss::TileMode horizStretch, vertStretch;
1000 int cuts[4];
1001
1002 decl.borderImageValue(&uri, cuts, &horizStretch, &vertStretch);
1003 if (uri.isEmpty() || uri == QLatin1String("none")) {
1004 if (bd && bd->bi)
1005 bd->bi->pixmap = QPixmap();
1006 } else {
1007 if (!bd)
1008 bd = new QStyleSheetBorderData;
1009 if (!bd->bi)
1010 bd->bi = new QStyleSheetBorderImageData;
1011
1012 QStyleSheetBorderImageData *bi = bd->bi;
1013 bi->pixmap = QStyleSheetStyle::loadPixmap(uri, object);
1014 for (int i = 0; i < 4; i++)
1015 bi->cuts[i] = cuts[i];
1016 bi->horizStretch = horizStretch;
1017 bi->vertStretch = vertStretch;
1018 }
1019 } else if (decl.d->propertyId == QtBackgroundRole) {
1020 if (bg && bg->brush.style() != Qt::NoBrush)
1021 continue;
1022 int role = decl.d->values.at(0).variant.toInt();
1023 if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
1024 defaultBackground = palette.color((QPalette::ColorRole)(role-Value_FirstColorRole));
1025 } else if (decl.d->property.startsWith(QLatin1String("qproperty-"), Qt::CaseInsensitive)) {
1026 // intentionally left blank...
1027 } else if (decl.d->propertyId == UnknownProperty) {
1028 bool knownStyleHint = false;
1029 for (int i = 0; i < numKnownStyleHints; i++) {
1030 QLatin1String styleHint(knownStyleHints[i]);
1031 if (decl.d->property.compare(styleHint) == 0) {
1032 QString hintName = QString(styleHint);
1033 QVariant hintValue;
1034 if (hintName.endsWith(QLatin1String("alignment"))) {
1035 hintValue = (int) decl.alignmentValue();
1036 } else if (hintName.endsWith(QLatin1String("color"))) {
1037 hintValue = (int) decl.colorValue().rgba();
1038 } else if (hintName.endsWith(QLatin1String("size"))) {
1039 hintValue = decl.sizeValue();
1040 } else if (hintName.endsWith(QLatin1String("icon"))) {
1041 hintValue = decl.iconValue();
1042 } else if (hintName == QLatin1String("button-layout")
1043 && decl.d->values.count() != 0 && decl.d->values.at(0).type == Value::String) {
1044 hintValue = subControlLayout(decl.d->values.at(0).variant.toString());
1045 } else {
1046 int integer;
1047 decl.intValue(&integer);
1048 hintValue = integer;
1049 }
1050 styleHints[decl.d->property] = hintValue;
1051 knownStyleHint = true;
1052 break;
1053 }
1054 }
1055 if (!knownStyleHint)
1056 qDebug("Unknown property %s", qPrintable(decl.d->property));
1057 }
1058 }
1059
1060 if (hasBorder()) {
1061 if (const QWidget *widget = qobject_cast<const QWidget *>(object)) {
1062 QStyleSheetStyle *style = const_cast<QStyleSheetStyle *>(globalStyleSheetStyle);
1063 if (!style)
1064 style = qt_styleSheet(widget->style());
1065 if (style)
1066 fixupBorder(style->nativeFrameWidth(widget));
1067 }
1068 if (border()->hasBorderImage())
1069 defaultBackground = QBrush();
1070 }
1071}
1072
1073QRect QRenderRule::borderRect(const QRect& r) const
1074{
1075 if (!hasBox())
1076 return r;
1077 const int* m = box()->margins;
1078 return r.adjusted(m[LeftEdge], m[TopEdge], -m[RightEdge], -m[BottomEdge]);
1079}
1080
1081QRect QRenderRule::outlineRect(const QRect& r) const
1082{
1083 QRect br = borderRect(r);
1084 if (!hasOutline())
1085 return br;
1086 const int *b = outline()->borders;
1087 return r.adjusted(b[LeftEdge], b[TopEdge], -b[RightEdge], -b[BottomEdge]);
1088}
1089
1090QRect QRenderRule::paddingRect(const QRect& r) const
1091{
1092 QRect br = borderRect(r);
1093 if (!hasBorder())
1094 return br;
1095 const int *b = border()->borders;
1096 return br.adjusted(b[LeftEdge], b[TopEdge], -b[RightEdge], -b[BottomEdge]);
1097}
1098
1099QRect QRenderRule::contentsRect(const QRect& r) const
1100{
1101 QRect pr = paddingRect(r);
1102 if (!hasBox())
1103 return pr;
1104 const int *p = box()->paddings;
1105 return pr.adjusted(p[LeftEdge], p[TopEdge], -p[RightEdge], -p[BottomEdge]);
1106}
1107
1108QRect QRenderRule::boxRect(const QRect& cr, int flags) const
1109{
1110 QRect r = cr;
1111 if (hasBox()) {
1112 if (flags & Margin) {
1113 const int *m = box()->margins;
1114 r.adjust(-m[LeftEdge], -m[TopEdge], m[RightEdge], m[BottomEdge]);
1115 }
1116 if (flags & Padding) {
1117 const int *p = box()->paddings;
1118 r.adjust(-p[LeftEdge], -p[TopEdge], p[RightEdge], p[BottomEdge]);
1119 }
1120 }
1121 if (hasBorder() && (flags & Border)) {
1122 const int *b = border()->borders;
1123 r.adjust(-b[LeftEdge], -b[TopEdge], b[RightEdge], b[BottomEdge]);
1124 }
1125 return r;
1126}
1127
1128QSize QRenderRule::boxSize(const QSize &cs, int flags) const
1129{
1130 QSize bs = boxRect(QRect(QPoint(0, 0), cs), flags).size();
1131 if (cs.width() < 0) bs.setWidth(-1);
1132 if (cs.height() < 0) bs.setHeight(-1);
1133 return bs;
1134}
1135
1136void QRenderRule::fixupBorder(int nativeWidth)
1137{
1138 if (bd == nullptr)
1139 return;
1140
1141 if (!bd->hasBorderImage() || bd->bi->pixmap.isNull()) {
1142 bd->bi = nullptr;
1143 // ignore the color, border of edges that have none border-style
1144 QBrush color = pal ? pal->foreground : QBrush();
1145 const bool hasRadius = bd->radii[0].isValid() || bd->radii[1].isValid()
1146 || bd->radii[2].isValid() || bd->radii[3].isValid();
1147 for (int i = 0; i < 4; i++) {
1148 if ((bd->styles[i] == BorderStyle_Native) && hasRadius)
1149 bd->styles[i] = BorderStyle_None;
1150
1151 switch (bd->styles[i]) {
1152 case BorderStyle_None:
1153 // border-style: none forces width to be 0
1154 bd->colors[i] = QBrush();
1155 bd->borders[i] = 0;
1156 break;
1157 case BorderStyle_Native:
1158 if (bd->borders[i] == 0)
1159 bd->borders[i] = nativeWidth;
1160 Q_FALLTHROUGH();
1161 default:
1162 if (bd->colors[i].style() == Qt::NoBrush) // auto-acquire 'color'
1163 bd->colors[i] = color;
1164 break;
1165 }
1166 }
1167
1168 return;
1169 }
1170
1171 // inspect the border image
1172 QStyleSheetBorderImageData *bi = bd->bi;
1173 if (bi->cuts[0] == -1) {
1174 for (int i = 0; i < 4; i++) // assume, cut = border
1175 bi->cuts[i] = int(border()->borders[i]);
1176 }
1177}
1178
1179void QRenderRule::drawBorderImage(QPainter *p, const QRect& rect)
1180{
1181 setClip(p, rect);
1182 static const Qt::TileRule tileMode2TileRule[] = {
1183 Qt::StretchTile, Qt::RoundTile, Qt::StretchTile, Qt::RepeatTile, Qt::StretchTile };
1184
1185 const QStyleSheetBorderImageData *borderImageData = border()->borderImage();
1186 const int *targetBorders = border()->borders;
1187 const int *sourceBorders = borderImageData->cuts;
1188 QMargins sourceMargins(sourceBorders[LeftEdge], sourceBorders[TopEdge],
1189 sourceBorders[RightEdge], sourceBorders[BottomEdge]);
1190 QMargins targetMargins(targetBorders[LeftEdge], targetBorders[TopEdge],
1191 targetBorders[RightEdge], targetBorders[BottomEdge]);
1192
1193 bool wasSmoothPixmapTransform = p->renderHints() & QPainter::SmoothPixmapTransform;
1194 p->setRenderHint(QPainter::SmoothPixmapTransform);
1195 qDrawBorderPixmap(p, rect, targetMargins, borderImageData->pixmap,
1196 QRect(QPoint(), borderImageData->pixmap.size()), sourceMargins,
1197 QTileRules(tileMode2TileRule[borderImageData->horizStretch], tileMode2TileRule[borderImageData->vertStretch]));
1198 p->setRenderHint(QPainter::SmoothPixmapTransform, wasSmoothPixmapTransform);
1199 unsetClip(p);
1200}
1201
1202QRect QRenderRule::originRect(const QRect &rect, Origin origin) const
1203{
1204 switch (origin) {
1205 case Origin_Padding:
1206 return paddingRect(rect);
1207 case Origin_Border:
1208 return borderRect(rect);
1209 case Origin_Content:
1210 return contentsRect(rect);
1211 case Origin_Margin:
1212 default:
1213 return rect;
1214 }
1215}
1216
1217void QRenderRule::drawBackgroundImage(QPainter *p, const QRect &rect, QPoint off)
1218{
1219 if (!hasBackground())
1220 return;
1221
1222 const QPixmap& bgp = background()->pixmap;
1223 if (bgp.isNull())
1224 return;
1225
1226 setClip(p, borderRect(rect));
1227
1228 if (background()->origin != background()->clip) {
1229 p->save();
1230 p->setClipRect(originRect(rect, background()->clip), Qt::IntersectClip);
1231 }
1232
1233 if (background()->attachment == Attachment_Fixed)
1234 off = QPoint(0, 0);
1235
1236 QSize bgpSize = bgp.size() / bgp.devicePixelRatio();
1237 int bgpHeight = bgpSize.height();
1238 int bgpWidth = bgpSize.width();
1239 QRect r = originRect(rect, background()->origin);
1240 QRect aligned = QStyle::alignedRect(Qt::LeftToRight, background()->position, bgpSize, r);
1241 QRect inter = aligned.translated(-off).intersected(r);
1242
1243 switch (background()->repeat) {
1244 case Repeat_Y:
1245 p->drawTiledPixmap(inter.x(), r.y(), inter.width(), r.height(), bgp,
1246 inter.x() - aligned.x() + off.x(),
1247 bgpHeight - int(aligned.y() - r.y()) % bgpHeight + off.y());
1248 break;
1249 case Repeat_X:
1250 p->drawTiledPixmap(r.x(), inter.y(), r.width(), inter.height(), bgp,
1251 bgpWidth - int(aligned.x() - r.x())%bgpWidth + off.x(),
1252 inter.y() - aligned.y() + off.y());
1253 break;
1254 case Repeat_XY:
1255 p->drawTiledPixmap(r, bgp,
1256 QPoint(bgpWidth - int(aligned.x() - r.x())% bgpWidth + off.x(),
1257 bgpHeight - int(aligned.y() - r.y())%bgpHeight + off.y()));
1258 break;
1259 case Repeat_None:
1260 default:
1261 p->drawPixmap(inter.x(), inter.y(), bgp, inter.x() - aligned.x() + off.x(),
1262 inter.y() - aligned.y() + off.y(), bgp.width() , bgp.height());
1263 break;
1264 }
1265
1266
1267 if (background()->origin != background()->clip)
1268 p->restore();
1269
1270 unsetClip(p);
1271}
1272
1273void QRenderRule::drawOutline(QPainter *p, const QRect &rect)
1274{
1275 if (!hasOutline())
1276 return;
1277
1278 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1279 p->setRenderHint(QPainter::Antialiasing);
1280 qDrawBorder(p, rect, ou->styles, ou->borders, ou->colors, ou->radii);
1281 p->setRenderHint(QPainter::Antialiasing, wasAntialiased);
1282}
1283
1284void QRenderRule::drawBorder(QPainter *p, const QRect& rect)
1285{
1286 if (!hasBorder())
1287 return;
1288
1289 if (border()->hasBorderImage()) {
1290 drawBorderImage(p, rect);
1291 return;
1292 }
1293
1294 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1295 p->setRenderHint(QPainter::Antialiasing);
1296 qDrawBorder(p, rect, bd->styles, bd->borders, bd->colors, bd->radii);
1297 p->setRenderHint(QPainter::Antialiasing, wasAntialiased);
1298}
1299
1300QPainterPath QRenderRule::borderClip(QRect r)
1301{
1302 if (!hasBorder())
1303 return QPainterPath();
1304
1305 QSize tlr, trr, blr, brr;
1306 qNormalizeRadii(r, bd->radii, &tlr, &trr, &blr, &brr);
1307 if (tlr.isNull() && trr.isNull() && blr.isNull() && brr.isNull())
1308 return QPainterPath();
1309
1310 const QRectF rect(r);
1311 const int *borders = border()->borders;
1312 QPainterPath path;
1313 qreal curY = rect.y() + borders[TopEdge]/2.0;
1314 path.moveTo(rect.x() + tlr.width(), curY);
1315 path.lineTo(rect.right() - trr.width(), curY);
1316 qreal curX = rect.right() - borders[RightEdge]/2.0;
1317 path.arcTo(curX - 2*trr.width() + borders[RightEdge], curY,
1318 trr.width()*2 - borders[RightEdge], trr.height()*2 - borders[TopEdge], 90, -90);
1319
1320 path.lineTo(curX, rect.bottom() - brr.height());
1321 curY = rect.bottom() - borders[BottomEdge]/2.0;
1322 path.arcTo(curX - 2*brr.width() + borders[RightEdge], curY - 2*brr.height() + borders[BottomEdge],
1323 brr.width()*2 - borders[RightEdge], brr.height()*2 - borders[BottomEdge], 0, -90);
1324
1325 path.lineTo(rect.x() + blr.width(), curY);
1326 curX = rect.left() + borders[LeftEdge]/2.0;
1327 path.arcTo(curX, rect.bottom() - 2*blr.height() + borders[BottomEdge]/2,
1328 blr.width()*2 - borders[LeftEdge], blr.height()*2 - borders[BottomEdge], 270, -90);
1329
1330 path.lineTo(curX, rect.top() + tlr.height());
1331 path.arcTo(curX, rect.top() + borders[TopEdge]/2,
1332 tlr.width()*2 - borders[LeftEdge], tlr.height()*2 - borders[TopEdge], 180, -90);
1333
1334 path.closeSubpath();
1335 return path;
1336}
1337
1338/*! \internal
1339 Clip the painter to the border (in case we are using radius border)
1340 */
1341void QRenderRule::setClip(QPainter *p, const QRect &rect)
1342{
1343 if (clipset++)
1344 return;
1345 clipPath = borderClip(rect);
1346 if (!clipPath.isEmpty()) {
1347 p->save();
1348 p->setClipPath(clipPath, Qt::IntersectClip);
1349 }
1350}
1351
1352void QRenderRule::unsetClip(QPainter *p)
1353{
1354 if (--clipset)
1355 return;
1356 if (!clipPath.isEmpty())
1357 p->restore();
1358}
1359
1360void QRenderRule::drawBackground(QPainter *p, const QRect& rect, const QPoint& off)
1361{
1362 QBrush brush = hasBackground() ? background()->brush : QBrush();
1363 if (brush.style() == Qt::NoBrush)
1364 brush = defaultBackground;
1365
1366 if (brush.style() != Qt::NoBrush) {
1367 Origin origin = hasBackground() ? background()->clip : Origin_Border;
1368 // ### fix for gradients
1369 const QPainterPath &borderPath = borderClip(originRect(rect, origin));
1370 if (!borderPath.isEmpty()) {
1371 // Drawn intead of being used as clipping path for better visual quality
1372 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1373 p->setRenderHint(QPainter::Antialiasing);
1374 p->fillPath(borderPath, brush);
1375 p->setRenderHint(QPainter::Antialiasing, wasAntialiased);
1376 } else {
1377 p->fillRect(originRect(rect, origin), brush);
1378 }
1379 }
1380
1381 drawBackgroundImage(p, rect, off);
1382}
1383
1384void QRenderRule::drawFrame(QPainter *p, const QRect& rect)
1385{
1386 drawBackground(p, rect);
1387 if (hasBorder())
1388 drawBorder(p, borderRect(rect));
1389}
1390
1391void QRenderRule::drawImage(QPainter *p, const QRect &rect)
1392{
1393 if (!hasImage())
1394 return;
1395 img->icon.paint(p, rect, img->alignment);
1396}
1397
1398void QRenderRule::drawRule(QPainter *p, const QRect& rect)
1399{
1400 drawFrame(p, rect);
1401 drawImage(p, contentsRect(rect));
1402}
1403
1404// *shudder* , *horror*, *whoa* <-- what you might feel when you see the functions below
1405void QRenderRule::configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette::ColorRole br)
1406{
1407 if (bg && bg->brush.style() != Qt::NoBrush) {
1408 if (br != QPalette::NoRole)
1409 p->setBrush(br, bg->brush);
1410 p->setBrush(QPalette::Window, bg->brush);
1411 if (bg->brush.style() == Qt::SolidPattern) {
1412 p->setBrush(QPalette::Light, bg->brush.color().lighter(115));
1413 p->setBrush(QPalette::Midlight, bg->brush.color().lighter(107));
1414 p->setBrush(QPalette::Dark, bg->brush.color().darker(150));
1415 p->setBrush(QPalette::Shadow, bg->brush.color().darker(300));
1416 }
1417 }
1418
1419 if (!hasPalette())
1420 return;
1421
1422 if (pal->foreground.style() != Qt::NoBrush) {
1423 if (fr != QPalette::NoRole)
1424 p->setBrush(fr, pal->foreground);
1425 p->setBrush(QPalette::WindowText, pal->foreground);
1426 p->setBrush(QPalette::Text, pal->foreground);
1427 }
1428 if (pal->selectionBackground.style() != Qt::NoBrush)
1429 p->setBrush(QPalette::Highlight, pal->selectionBackground);
1430 if (pal->selectionForeground.style() != Qt::NoBrush)
1431 p->setBrush(QPalette::HighlightedText, pal->selectionForeground);
1432 if (pal->alternateBackground.style() != Qt::NoBrush)
1433 p->setBrush(QPalette::AlternateBase, pal->alternateBackground);
1434}
1435
1436void QRenderRule::configurePalette(QPalette *p, QPalette::ColorGroup cg, const QWidget *w, bool embedded)
1437{
1438 if (bg && bg->brush.style() != Qt::NoBrush) {
1439 p->setBrush(cg, QPalette::Base, bg->brush); // for windows, windowxp
1440 p->setBrush(cg, QPalette::Button, bg->brush); // for plastique
1441 p->setBrush(cg, w->backgroundRole(), bg->brush);
1442 p->setBrush(cg, QPalette::Window, bg->brush);
1443 }
1444
1445 if (embedded) {
1446 /* For embedded widgets (ComboBox, SpinBox and ScrollArea) we want the embedded widget
1447 * to be transparent when we have a transparent background or border image */
1448 if ((hasBackground() && background()->isTransparent())
1449 || (hasBorder() && border()->hasBorderImage() && !border()->borderImage()->pixmap.isNull()))
1450 p->setBrush(cg, w->backgroundRole(), Qt::NoBrush);
1451 }
1452
1453 if (!hasPalette())
1454 return;
1455
1456 if (pal->foreground.style() != Qt::NoBrush) {
1457 p->setBrush(cg, QPalette::ButtonText, pal->foreground);
1458 p->setBrush(cg, w->foregroundRole(), pal->foreground);
1459 p->setBrush(cg, QPalette::WindowText, pal->foreground);
1460 p->setBrush(cg, QPalette::Text, pal->foreground);
1461 }
1462 if (pal->selectionBackground.style() != Qt::NoBrush)
1463 p->setBrush(cg, QPalette::Highlight, pal->selectionBackground);
1464 if (pal->selectionForeground.style() != Qt::NoBrush)
1465 p->setBrush(cg, QPalette::HighlightedText, pal->selectionForeground);
1466 if (pal->alternateBackground.style() != Qt::NoBrush)
1467 p->setBrush(cg, QPalette::AlternateBase, pal->alternateBackground);
1468}
1469
1470bool QRenderRule::hasModification() const
1471{
1472 return hasPalette() ||
1473 hasBackground() ||
1474 hasGradientBackground() ||
1475 !hasNativeBorder() ||
1476 !hasNativeOutline() ||
1477 hasBox() ||
1478 hasPosition() ||
1479 hasGeometry() ||
1480 hasImage() ||
1481 hasFont ||
1482 !styleHints.isEmpty();
1483}
1484
1485///////////////////////////////////////////////////////////////////////////////
1486// Style rules
1487#define OBJECT_PTR(x) (static_cast<QObject *>(x.ptr))
1488
1489static inline QObject *parentObject(const QObject *obj)
1490{
1491#if QT_CONFIG(tooltip)
1492 if (qobject_cast<const QLabel *>(obj) && qstrcmp(obj->metaObject()->className(), "QTipLabel") == 0) {
1493 QObject *p = qvariant_cast<QObject *>(obj->property("_q_stylesheet_parent"));
1494 if (p)
1495 return p;
1496 }
1497#endif
1498 return obj->parent();
1499}
1500
1501class QStyleSheetStyleSelector : public StyleSelector
1502{
1503public:
1504 QStyleSheetStyleSelector() { }
1505
1506 QStringList nodeNames(NodePtr node) const override
1507 {
1508 if (isNullNode(node))
1509 return QStringList();
1510 const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject();
1511#ifndef QT_NO_TOOLTIP
1512 if (qstrcmp(metaObject->className(), "QTipLabel") == 0)
1513 return QStringList(QLatin1String("QToolTip"));
1514#endif
1515 QStringList result;
1516 do {
1517 result += QString::fromLatin1(metaObject->className()).replace(QLatin1Char(':'), QLatin1Char('-'));
1518 metaObject = metaObject->superClass();
1519 } while (metaObject != nullptr);
1520 return result;
1521 }
1522 QString attribute(NodePtr node, const QString& name) const override
1523 {
1524 if (isNullNode(node))
1525 return QString();
1526
1527 QHash<QString, QString> &cache = m_attributeCache[OBJECT_PTR(node)];
1528 QHash<QString, QString>::const_iterator cacheIt = cache.constFind(name);
1529 if (cacheIt != cache.constEnd())
1530 return cacheIt.value();
1531
1532 QObject *obj = OBJECT_PTR(node);
1533 QVariant value = obj->property(name.toLatin1());
1534 if (!value.isValid()) {
1535 if (name == QLatin1String("class")) {
1536 QString className = QString::fromLatin1(obj->metaObject()->className());
1537 if (className.contains(QLatin1Char(':')))
1538 className.replace(QLatin1Char(':'), QLatin1Char('-'));
1539 cache[name] = className;
1540 return className;
1541 } else if (name == QLatin1String("style")) {
1542 QWidget *w = qobject_cast<QWidget *>(obj);
1543 QStyleSheetStyle *proxy = w ? qt_styleSheet(w->style()) : nullptr;
1544 if (proxy) {
1545 QString styleName = QString::fromLatin1(proxy->baseStyle()->metaObject()->className());
1546 cache[name] = styleName;
1547 return styleName;
1548 }
1549 }
1550 }
1551 QString valueStr = (value.userType() == QMetaType::QStringList
1552 || value.userType() == QMetaType::QVariantList)
1553 ? value.toStringList().join(QLatin1Char(' '))
1554 : value.toString();
1555 cache[name] = valueStr;
1556 return valueStr;
1557 }
1558 bool nodeNameEquals(NodePtr node, const QString& nodeName) const override
1559 {
1560 if (isNullNode(node))
1561 return false;
1562 const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject();
1563#ifndef QT_NO_TOOLTIP
1564 if (qstrcmp(metaObject->className(), "QTipLabel") == 0)
1565 return nodeName == QLatin1String("QToolTip");
1566#endif
1567 do {
1568 const ushort *uc = (const ushort *)nodeName.constData();
1569 const ushort *e = uc + nodeName.length();
1570 const uchar *c = (const uchar *)metaObject->className();
1571 while (*c && uc != e && (*uc == *c || (*c == ':' && *uc == '-'))) {
1572 ++uc;
1573 ++c;
1574 }
1575 if (uc == e && !*c)
1576 return true;
1577 metaObject = metaObject->superClass();
1578 } while (metaObject != nullptr);
1579 return false;
1580 }
1581 bool hasAttributes(NodePtr) const override
1582 { return true; }
1583 QStringList nodeIds(NodePtr node) const override
1584 { return isNullNode(node) ? QStringList() : QStringList(OBJECT_PTR(node)->objectName()); }
1585 bool isNullNode(NodePtr node) const override
1586 { return node.ptr == nullptr; }
1587 NodePtr parentNode(NodePtr node) const override
1588 { NodePtr n; n.ptr = isNullNode(node) ? nullptr : parentObject(OBJECT_PTR(node)); return n; }
1589 NodePtr previousSiblingNode(NodePtr) const override
1590 { NodePtr n; n.ptr = nullptr; return n; }
1591 NodePtr duplicateNode(NodePtr node) const override
1592 { return node; }
1593 void freeNode(NodePtr) const override
1594 { }
1595
1596private:
1597 mutable QHash<const QObject *, QHash<QString, QString> > m_attributeCache;
1598};
1599
1600QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QObject *obj) const
1601{
1602 QHash<const QObject *, QVector<StyleRule> >::const_iterator cacheIt = styleSheetCaches->styleRulesCache.constFind(obj);
1603 if (cacheIt != styleSheetCaches->styleRulesCache.constEnd())
1604 return cacheIt.value();
1605
1606 if (!initObject(obj)) {
1607 return QVector<StyleRule>();
1608 }
1609
1610 QStyleSheetStyleSelector styleSelector;
1611
1612 StyleSheet defaultSs;
1613 QHash<const void *, StyleSheet>::const_iterator defaultCacheIt = styleSheetCaches->styleSheetCache.constFind(baseStyle());
1614 if (defaultCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1615 defaultSs = getDefaultStyleSheet();
1616 QStyle *bs = baseStyle();
1617 styleSheetCaches->styleSheetCache.insert(bs, defaultSs);
1618 QObject::connect(bs, SIGNAL(destroyed(QObject*)), styleSheetCaches, SLOT(styleDestroyed(QObject*)), Qt::UniqueConnection);
1619 } else {
1620 defaultSs = defaultCacheIt.value();
1621 }
1622 styleSelector.styleSheets += defaultSs;
1623
1624 if (!qApp->styleSheet().isEmpty()) {
1625 StyleSheet appSs;
1626 QHash<const void *, StyleSheet>::const_iterator appCacheIt = styleSheetCaches->styleSheetCache.constFind(qApp);
1627 if (appCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1628 QString ss = qApp->styleSheet();
1629 if (ss.startsWith(QLatin1String("file:///")))
1630 ss.remove(0, 8);
1631 parser.init(ss, qApp->styleSheet() != ss);
1632 if (Q_UNLIKELY(!parser.parse(&appSs)))
1633 qWarning("Could not parse application stylesheet");
1634 appSs.origin = StyleSheetOrigin_Inline;
1635 appSs.depth = 1;
1636 styleSheetCaches->styleSheetCache.insert(qApp, appSs);
1637 } else {
1638 appSs = appCacheIt.value();
1639 }
1640 styleSelector.styleSheets += appSs;
1641 }
1642
1643 QVector<QCss::StyleSheet> objectSs;
1644 for (const QObject *o = obj; o; o = parentObject(o)) {
1645 QString styleSheet = o->property("styleSheet").toString();
1646 if (styleSheet.isEmpty())
1647 continue;
1648 StyleSheet ss;
1649 QHash<const void *, StyleSheet>::const_iterator objCacheIt = styleSheetCaches->styleSheetCache.constFind(o);
1650 if (objCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1651 parser.init(styleSheet);
1652 if (!parser.parse(&ss)) {
1653 parser.init(QLatin1String("* {") + styleSheet + QLatin1Char('}'));
1654 if (Q_UNLIKELY(!parser.parse(&ss)))
1655 qWarning() << "Could not parse stylesheet of object" << o;
1656 }
1657 ss.origin = StyleSheetOrigin_Inline;
1658 styleSheetCaches->styleSheetCache.insert(o, ss);
1659 } else {
1660 ss = objCacheIt.value();
1661 }
1662 objectSs.append(ss);
1663 }
1664
1665 for (int i = 0; i < objectSs.count(); i++)
1666 objectSs[i].depth = objectSs.count() - i + 2;
1667
1668 styleSelector.styleSheets += objectSs;
1669
1670 StyleSelector::NodePtr n;
1671 n.ptr = const_cast<QObject *>(obj);
1672 QVector<QCss::StyleRule> rules = styleSelector.styleRulesForNode(n);
1673 styleSheetCaches->styleRulesCache.insert(obj, rules);
1674 return rules;
1675}
1676
1677/////////////////////////////////////////////////////////////////////////////////////////
1678// Rendering rules
1679static QVector<Declaration> declarations(const QVector<StyleRule> &styleRules, const QString &part, quint64 pseudoClass = PseudoClass_Unspecified)
1680{
1681 QVector<Declaration> decls;
1682 for (int i = 0; i < styleRules.count(); i++) {
1683 const Selector& selector = styleRules.at(i).selectors.at(0);
1684 // Rules with pseudo elements don't cascade. This is an intentional
1685 // diversion for CSS
1686 if (part.compare(selector.pseudoElement(), Qt::CaseInsensitive) != 0)
1687 continue;
1688 quint64 negated = 0;
1689 quint64 cssClass = selector.pseudoClass(&negated);
1690 if ((pseudoClass == PseudoClass_Any) || (cssClass == PseudoClass_Unspecified)
1691 || ((((cssClass & pseudoClass) == cssClass)) && ((negated & pseudoClass) == 0)))
1692 decls += styleRules.at(i).declarations;
1693 }
1694 return decls;
1695}
1696
1697int QStyleSheetStyle::nativeFrameWidth(const QWidget *w)
1698{
1699 QStyle *base = baseStyle();
1700
1701#if QT_CONFIG(spinbox)
1702 if (qobject_cast<const QAbstractSpinBox *>(w))
1703 return base->pixelMetric(QStyle::PM_SpinBoxFrameWidth, nullptr, w);
1704#endif
1705
1706#if QT_CONFIG(combobox)
1707 if (qobject_cast<const QComboBox *>(w))
1708 return base->pixelMetric(QStyle::PM_ComboBoxFrameWidth, nullptr, w);
1709#endif
1710
1711#if QT_CONFIG(menu)
1712 if (qobject_cast<const QMenu *>(w))
1713 return base->pixelMetric(QStyle::PM_MenuPanelWidth, nullptr, w);
1714#endif
1715
1716#if QT_CONFIG(menubar)
1717 if (qobject_cast<const QMenuBar *>(w))
1718 return base->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, w);
1719#endif
1720#ifndef QT_NO_FRAME
1721 if (const QFrame *frame = qobject_cast<const QFrame *>(w)) {
1722 if (frame->frameShape() == QFrame::NoFrame)
1723 return 0;
1724 }
1725#endif
1726
1727 if (qstrcmp(w->metaObject()->className(), "QTipLabel") == 0)
1728 return base->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, nullptr, w);
1729
1730 return base->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, w);
1731}
1732
1733static quint64 pseudoClass(QStyle::State state)
1734{
1735 quint64 pc = 0;
1736 if (state & QStyle::State_Enabled) {
1737 pc |= PseudoClass_Enabled;
1738 if (state & QStyle::State_MouseOver)
1739 pc |= PseudoClass_Hover;
1740 } else {
1741 pc |= PseudoClass_Disabled;
1742 }
1743 if (state & QStyle::State_Active)
1744 pc |= PseudoClass_Active;
1745 if (state & QStyle::State_Window)
1746 pc |= PseudoClass_Window;
1747 if (state & QStyle::State_Sunken)
1748 pc |= PseudoClass_Pressed;
1749 if (state & QStyle::State_HasFocus)
1750 pc |= PseudoClass_Focus;
1751 if (state & QStyle::State_On)
1752 pc |= (PseudoClass_On | PseudoClass_Checked);
1753 if (state & QStyle::State_Off)
1754 pc |= (PseudoClass_Off | PseudoClass_Unchecked);
1755 if (state & QStyle::State_NoChange)
1756 pc |= PseudoClass_Indeterminate;
1757 if (state & QStyle::State_Selected)
1758 pc |= PseudoClass_Selected;
1759 if (state & QStyle::State_Horizontal)
1760 pc |= PseudoClass_Horizontal;
1761 else
1762 pc |= PseudoClass_Vertical;
1763 if (state & (QStyle::State_Open | QStyle::State_On | QStyle::State_Sunken))
1764 pc |= PseudoClass_Open;
1765 else
1766 pc |= PseudoClass_Closed;
1767 if (state & QStyle::State_Children)
1768 pc |= PseudoClass_Children;
1769 if (state & QStyle::State_Sibling)
1770 pc |= PseudoClass_Sibling;
1771 if (state & QStyle::State_ReadOnly)
1772 pc |= PseudoClass_ReadOnly;
1773 if (state & QStyle::State_Item)
1774 pc |= PseudoClass_Item;
1775#ifdef QT_KEYPAD_NAVIGATION
1776 if (state & QStyle::State_HasEditFocus)
1777 pc |= PseudoClass_EditFocus;
1778#endif
1779 return pc;
1780}
1781
1782static void qt_check_if_internal_object(const QObject **obj, int *element)
1783{
1784#if !QT_CONFIG(dockwidget)
1785 Q_UNUSED(obj);
1786 Q_UNUSED(element);
1787#else
1788 if (*obj && qstrcmp((*obj)->metaObject()->className(), "QDockWidgetTitleButton") == 0) {
1789 if ((*obj)->objectName() == QLatin1String("qt_dockwidget_closebutton")) {
1790 *element = PseudoElement_DockWidgetCloseButton;
1791 } else if ((*obj)->objectName() == QLatin1String("qt_dockwidget_floatbutton")) {
1792 *element = PseudoElement_DockWidgetFloatButton;
1793 }
1794 *obj = (*obj)->parent();
1795 }
1796#endif
1797}
1798
1799QRenderRule QStyleSheetStyle::renderRule(const QObject *obj, int element, quint64 state) const
1800{
1801 qt_check_if_internal_object(&obj, &element);
1802 QHash<quint64, QRenderRule> &cache = styleSheetCaches->renderRulesCache[obj][element];
1803 QHash<quint64, QRenderRule>::const_iterator cacheIt = cache.constFind(state);
1804 if (cacheIt != cache.constEnd())
1805 return cacheIt.value();
1806
1807 if (!initObject(obj))
1808 return QRenderRule();
1809
1810 quint64 stateMask = 0;
1811 const QVector<StyleRule> rules = styleRules(obj);
1812 for (int i = 0; i < rules.count(); i++) {
1813 const Selector& selector = rules.at(i).selectors.at(0);
1814 quint64 negated = 0;
1815 stateMask |= selector.pseudoClass(&negated);
1816 stateMask |= negated;
1817 }
1818
1819 cacheIt = cache.constFind(state & stateMask);
1820 if (cacheIt != cache.constEnd()) {
1821 const QRenderRule &newRule = cacheIt.value();
1822 cache[state] = newRule;
1823 return newRule;
1824 }
1825
1826
1827 const QString part = QLatin1String(knownPseudoElements[element].name);
1828 QVector<Declaration> decls = declarations(rules, part, state);
1829 QRenderRule newRule(decls, obj);
1830 cache[state] = newRule;
1831 if ((state & stateMask) != state)
1832 cache[state&stateMask] = newRule;
1833 return newRule;
1834}
1835
1836QRenderRule QStyleSheetStyle::renderRule(const QObject *obj, const QStyleOption *opt, int pseudoElement) const
1837{
1838 quint64 extraClass = 0;
1839 QStyle::State state = opt ? opt->state : QStyle::State(QStyle::State_None);
1840
1841 if (const QStyleOptionComplex *complex = qstyleoption_cast<const QStyleOptionComplex *>(opt)) {
1842 if (pseudoElement != PseudoElement_None) {
1843 // if not an active subcontrol, just pass enabled/disabled
1844 QStyle::SubControl subControl = knownPseudoElements[pseudoElement].subControl;
1845
1846 if (!(complex->activeSubControls & subControl))
1847 state &= (QStyle::State_Enabled | QStyle::State_Horizontal | QStyle::State_HasFocus);
1848 }
1849
1850 switch (pseudoElement) {
1851 case PseudoElement_ComboBoxDropDown:
1852 case PseudoElement_ComboBoxArrow:
1853 state |= (complex->state & (QStyle::State_On|QStyle::State_ReadOnly));
1854 break;
1855 case PseudoElement_SpinBoxUpButton:
1856 case PseudoElement_SpinBoxDownButton:
1857 case PseudoElement_SpinBoxUpArrow:
1858 case PseudoElement_SpinBoxDownArrow:
1859#if QT_CONFIG(spinbox)
1860 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
1861 bool on = false;
1862 bool up = pseudoElement == PseudoElement_SpinBoxUpButton
1863 || pseudoElement == PseudoElement_SpinBoxUpArrow;
1864 if ((sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) && up)
1865 on = true;
1866 else if ((sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) && !up)
1867 on = true;
1868 state |= (on ? QStyle::State_On : QStyle::State_Off);
1869 }
1870#endif // QT_CONFIG(spinbox)
1871 break;
1872 case PseudoElement_GroupBoxTitle:
1873 state |= (complex->state & (QStyle::State_MouseOver | QStyle::State_Sunken));
1874 break;
1875 case PseudoElement_ToolButtonMenu:
1876 case PseudoElement_ToolButtonMenuArrow:
1877 case PseudoElement_ToolButtonDownArrow:
1878 state |= complex->state & QStyle::State_MouseOver;
1879 if (complex->state & QStyle::State_Sunken ||
1880 complex->activeSubControls & QStyle::SC_ToolButtonMenu)
1881 state |= QStyle::State_Sunken;
1882 break;
1883 case PseudoElement_SliderGroove:
1884 state |= complex->state & QStyle::State_MouseOver;
1885 break;
1886 default:
1887 break;
1888 }
1889
1890 if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
1891 // QStyle::State_On is set when the popup is being shown
1892 // Propagate EditField Pressed state
1893 if (pseudoElement == PseudoElement_None
1894 && (complex->activeSubControls & QStyle::SC_ComboBoxEditField)
1895 && (!(state & QStyle::State_MouseOver))) {
1896 state |= QStyle::State_Sunken;
1897 }
1898
1899 if (!combo->frame)
1900 extraClass |= PseudoClass_Frameless;
1901 if (!combo->editable)
1902 extraClass |= PseudoClass_ReadOnly;
1903 else
1904 extraClass |= PseudoClass_Editable;
1905#if QT_CONFIG(spinbox)
1906 } else if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
1907 if (!spin->frame)
1908 extraClass |= PseudoClass_Frameless;
1909#endif // QT_CONFIG(spinbox)
1910 } else if (const QStyleOptionGroupBox *gb = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
1911 if (gb->features & QStyleOptionFrame::Flat)
1912 extraClass |= PseudoClass_Flat;
1913 if (gb->lineWidth == 0)
1914 extraClass |= PseudoClass_Frameless;
1915 } else if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
1916 if (tb->titleBarState & Qt::WindowMinimized) {
1917 extraClass |= PseudoClass_Minimized;
1918 }
1919 else if (tb->titleBarState & Qt::WindowMaximized)
1920 extraClass |= PseudoClass_Maximized;
1921 }
1922 } else {
1923 // handle simple style options
1924 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
1925 if (mi->menuItemType == QStyleOptionMenuItem::DefaultItem)
1926 extraClass |= PseudoClass_Default;
1927 if (mi->checkType == QStyleOptionMenuItem::Exclusive)
1928 extraClass |= PseudoClass_Exclusive;
1929 else if (mi->checkType == QStyleOptionMenuItem::NonExclusive)
1930 extraClass |= PseudoClass_NonExclusive;
1931 if (mi->checkType != QStyleOptionMenuItem::NotCheckable)
1932 extraClass |= (mi->checked) ? (PseudoClass_On|PseudoClass_Checked)
1933 : (PseudoClass_Off|PseudoClass_Unchecked);
1934 } else if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
1935 if (hdr->position == QStyleOptionHeader::OnlyOneSection)
1936 extraClass |= PseudoClass_OnlyOne;
1937 else if (hdr->position == QStyleOptionHeader::Beginning)
1938 extraClass |= PseudoClass_First;
1939 else if (hdr->position == QStyleOptionHeader::End)
1940 extraClass |= PseudoClass_Last;
1941 else if (hdr->position == QStyleOptionHeader::Middle)
1942 extraClass |= PseudoClass_Middle;
1943
1944 if (hdr->selectedPosition == QStyleOptionHeader::NextAndPreviousAreSelected)
1945 extraClass |= (PseudoClass_NextSelected | PseudoClass_PreviousSelected);
1946 else if (hdr->selectedPosition == QStyleOptionHeader::NextIsSelected)
1947 extraClass |= PseudoClass_NextSelected;
1948 else if (hdr->selectedPosition == QStyleOptionHeader::PreviousIsSelected)
1949 extraClass |= PseudoClass_PreviousSelected;
1950#if QT_CONFIG(tabwidget)
1951 } else if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
1952 switch (tab->shape) {
1953 case QTabBar::RoundedNorth:
1954 case QTabBar::TriangularNorth:
1955 extraClass |= PseudoClass_Top;
1956 break;
1957 case QTabBar::RoundedSouth:
1958 case QTabBar::TriangularSouth:
1959 extraClass |= PseudoClass_Bottom;
1960 break;
1961 case QTabBar::RoundedEast:
1962 case QTabBar::TriangularEast:
1963 extraClass |= PseudoClass_Right;
1964 break;
1965 case QTabBar::RoundedWest:
1966 case QTabBar::TriangularWest:
1967 extraClass |= PseudoClass_Left;
1968 break;
1969 default:
1970 break;
1971 }
1972#endif
1973#if QT_CONFIG(tabbar)
1974 } else if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
1975 if (tab->position == QStyleOptionTab::OnlyOneTab)
1976 extraClass |= PseudoClass_OnlyOne;
1977 else if (tab->position == QStyleOptionTab::Beginning)
1978 extraClass |= PseudoClass_First;
1979 else if (tab->position == QStyleOptionTab::End)
1980 extraClass |= PseudoClass_Last;
1981 else if (tab->position == QStyleOptionTab::Middle)
1982 extraClass |= PseudoClass_Middle;
1983
1984 if (tab->selectedPosition == QStyleOptionTab::NextIsSelected)
1985 extraClass |= PseudoClass_NextSelected;
1986 else if (tab->selectedPosition == QStyleOptionTab::PreviousIsSelected)
1987 extraClass |= PseudoClass_PreviousSelected;
1988
1989 switch (tab->shape) {
1990 case QTabBar::RoundedNorth:
1991 case QTabBar::TriangularNorth:
1992 extraClass |= PseudoClass_Top;
1993 break;
1994 case QTabBar::RoundedSouth:
1995 case QTabBar::TriangularSouth:
1996 extraClass |= PseudoClass_Bottom;
1997 break;
1998 case QTabBar::RoundedEast:
1999 case QTabBar::TriangularEast:
2000 extraClass |= PseudoClass_Right;
2001 break;
2002 case QTabBar::RoundedWest:
2003 case QTabBar::TriangularWest:
2004 extraClass |= PseudoClass_Left;
2005 break;
2006 default:
2007 break;
2008 }
2009#endif // QT_CONFIG(tabbar)
2010 } else if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2011 if (btn->features & QStyleOptionButton::Flat)
2012 extraClass |= PseudoClass_Flat;
2013 if (btn->features & QStyleOptionButton::DefaultButton)
2014 extraClass |= PseudoClass_Default;
2015 } else if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
2016 if (frm->lineWidth == 0)
2017 extraClass |= PseudoClass_Frameless;
2018 if (frm->features & QStyleOptionFrame::Flat)
2019 extraClass |= PseudoClass_Flat;
2020 }
2021#if QT_CONFIG(toolbar)
2022 else if (const QStyleOptionToolBar *tb = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
2023 if (tb->toolBarArea == Qt::LeftToolBarArea)
2024 extraClass |= PseudoClass_Left;
2025 else if (tb->toolBarArea == Qt::RightToolBarArea)
2026 extraClass |= PseudoClass_Right;
2027 else if (tb->toolBarArea == Qt::TopToolBarArea)
2028 extraClass |= PseudoClass_Top;
2029 else if (tb->toolBarArea == Qt::BottomToolBarArea)
2030 extraClass |= PseudoClass_Bottom;
2031
2032 if (tb->positionWithinLine == QStyleOptionToolBar::Beginning)
2033 extraClass |= PseudoClass_First;
2034 else if (tb->positionWithinLine == QStyleOptionToolBar::Middle)
2035 extraClass |= PseudoClass_Middle;
2036 else if (tb->positionWithinLine == QStyleOptionToolBar::End)
2037 extraClass |= PseudoClass_Last;
2038 else if (tb->positionWithinLine == QStyleOptionToolBar::OnlyOne)
2039 extraClass |= PseudoClass_OnlyOne;
2040 }
2041#endif // QT_CONFIG(toolbar)
2042#if QT_CONFIG(toolbox)
2043 else if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) {
2044 if (tb->position == QStyleOptionToolBox::OnlyOneTab)
2045 extraClass |= PseudoClass_OnlyOne;
2046 else if (tb->position == QStyleOptionToolBox::Beginning)
2047 extraClass |= PseudoClass_First;
2048 else if (tb->position == QStyleOptionToolBox::End)
2049 extraClass |= PseudoClass_Last;
2050 else if (tb->position == QStyleOptionToolBox::Middle)
2051 extraClass |= PseudoClass_Middle;
2052
2053 if (tb->selectedPosition == QStyleOptionToolBox::NextIsSelected)
2054 extraClass |= PseudoClass_NextSelected;
2055 else if (tb->selectedPosition == QStyleOptionToolBox::PreviousIsSelected)
2056 extraClass |= PseudoClass_PreviousSelected;
2057 }
2058#endif // QT_CONFIG(toolbox)
2059#if QT_CONFIG(dockwidget)
2060 else if (const QStyleOptionDockWidget *dw = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
2061 if (dw->verticalTitleBar)
2062 extraClass |= PseudoClass_Vertical;
2063 else
2064 extraClass |= PseudoClass_Horizontal;
2065 if (dw->closable)
2066 extraClass |= PseudoClass_Closable;
2067 if (dw->floatable)
2068 extraClass |= PseudoClass_Floatable;
2069 if (dw->movable)
2070 extraClass |= PseudoClass_Movable;
2071 }
2072#endif // QT_CONFIG(dockwidget)
2073#if QT_CONFIG(itemviews)
2074 else if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
2075 if (vopt->features & QStyleOptionViewItem::Alternate)
2076 extraClass |= PseudoClass_Alternate;
2077 if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne)
2078 extraClass |= PseudoClass_OnlyOne;
2079 else if (vopt->viewItemPosition == QStyleOptionViewItem::Beginning)
2080 extraClass |= PseudoClass_First;
2081 else if (vopt->viewItemPosition == QStyleOptionViewItem::End)
2082 extraClass |= PseudoClass_Last;
2083 else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle)
2084 extraClass |= PseudoClass_Middle;
2085
2086 }
2087#endif
2088#if QT_CONFIG(lineedit)
2089 // LineEdit sets Sunken flag to indicate Sunken frame (argh)
2090 if (const QLineEdit *lineEdit = qobject_cast<const QLineEdit *>(obj)) {
2091 state &= ~QStyle::State_Sunken;
2092 if (lineEdit->hasFrame()) {
2093 extraClass &= ~PseudoClass_Frameless;
2094 } else {
2095 extraClass |= PseudoClass_Frameless;
2096 }
2097 } else
2098#endif
2099 if (const QFrame *frm = qobject_cast<const QFrame *>(obj)) {
2100 if (frm->lineWidth() == 0)
2101 extraClass |= PseudoClass_Frameless;
2102 }
2103 }
2104
2105 return renderRule(obj, pseudoElement, pseudoClass(state) | extraClass);
2106}
2107
2108bool QStyleSheetStyle::hasStyleRule(const QObject *obj, int part) const
2109{
2110 QHash<int, bool> &cache = styleSheetCaches->hasStyleRuleCache[obj];
2111 QHash<int, bool>::const_iterator cacheIt = cache.constFind(part);
2112 if (cacheIt != cache.constEnd())
2113 return cacheIt.value();
2114
2115 if (!initObject(obj))
2116 return false;
2117
2118
2119 const QVector<StyleRule> &rules = styleRules(obj);
2120 if (part == PseudoElement_None) {
2121 bool result = obj && !rules.isEmpty();
2122 cache[part] = result;
2123 return result;
2124 }
2125
2126 QString pseudoElement = QLatin1String(knownPseudoElements[part].name);
2127 for (int i = 0; i < rules.count(); i++) {
2128 const Selector& selector = rules.at(i).selectors.at(0);
2129 if (pseudoElement.compare(selector.pseudoElement(), Qt::CaseInsensitive) == 0) {
2130 cache[part] = true;
2131 return true;
2132 }
2133 }
2134
2135 cache[part] = false;
2136 return false;
2137}
2138
2139static Origin defaultOrigin(int pe)
2140{
2141 switch (pe) {
2142 case PseudoElement_ScrollBarAddPage:
2143 case PseudoElement_ScrollBarSubPage:
2144 case PseudoElement_ScrollBarAddLine:
2145 case PseudoElement_ScrollBarSubLine:
2146 case PseudoElement_ScrollBarFirst:
2147 case PseudoElement_ScrollBarLast:
2148 case PseudoElement_GroupBoxTitle:
2149 case PseudoElement_GroupBoxIndicator: // never used
2150 case PseudoElement_ToolButtonMenu:
2151 case PseudoElement_SliderAddPage:
2152 case PseudoElement_SliderSubPage:
2153 return Origin_Border;
2154
2155 case PseudoElement_SpinBoxUpButton:
2156 case PseudoElement_SpinBoxDownButton:
2157 case PseudoElement_PushButtonMenuIndicator:
2158 case PseudoElement_ComboBoxDropDown:
2159 case PseudoElement_ToolButtonDownArrow:
2160 case PseudoElement_MenuCheckMark:
2161 case PseudoElement_MenuIcon:
2162 case PseudoElement_MenuRightArrow:
2163 return Origin_Padding;
2164
2165 case PseudoElement_Indicator:
2166 case PseudoElement_ExclusiveIndicator:
2167 case PseudoElement_ComboBoxArrow:
2168 case PseudoElement_ScrollBarSlider:
2169 case PseudoElement_ScrollBarUpArrow:
2170 case