1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qpalette_p.h"
5#include "qguiapplication_p.h"
6#include "qdatastream.h"
7#include "qvariant.h"
8#include "qdebug.h"
9
10#include <QtCore/qmetaobject.h>
11
12QT_BEGIN_NAMESPACE
13
14static_assert(QPalettePrivate::bitPosition(colorGroup: QPalette::ColorGroup(QPalette::NColorGroups - 1),
15 colorRole: QPalette::ColorRole(QPalette::NColorRoles - 1))
16 < sizeof(QPalette::ResolveMask) * CHAR_BIT,
17 "The resolve mask type is not wide enough to fit the entire bit mask.");
18
19static QColor qt_mix_colors(QColor a, QColor b)
20{
21 return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2,
22 (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2);
23}
24
25/*!
26 \internal
27
28 Derive undefined \l PlaceholderText colors from \l Text colors.
29 Unless already set, PlaceholderText colors will be derived from their Text pendents.
30 Colors of existing PlaceHolderText brushes will not be replaced.
31
32 \a alpha represents the dim factor as a percentage. By default, a PlaceHolderText color
33 becomes a 50% more transparent version of the corresponding Text color.
34*/
35static void qt_placeholder_from_text(QPalette &pal, int alpha = 50)
36{
37 if (alpha < 0 or alpha > 100)
38 return;
39
40 for (int cg = 0; cg < int(QPalette::NColorGroups); ++cg) {
41 const QPalette::ColorGroup group = QPalette::ColorGroup(cg);
42
43 // skip if the brush has been set already
44 if (!pal.isBrushSet(cg: group, cr: QPalette::PlaceholderText)) {
45 QColor c = pal.color(cg: group, cr: QPalette::Text);
46 const int a = (c.alpha() * alpha) / 100;
47 c.setAlpha(a);
48 pal.setColor(acg: group, acr: QPalette::PlaceholderText, acolor: c);
49 }
50 }
51}
52
53static void qt_ensure_default_accent_color(QPalette &pal)
54{
55 // have a lighter/darker factor handy, depending on dark/light heuristics
56 const int lighter = pal.base().color().lightness() > pal.text().color().lightness() ? 130 : 70;
57
58 // Act only for color groups where no accent color is set
59 for (int i = 0; i < QPalette::NColorGroups; ++i) {
60 const QPalette::ColorGroup group = static_cast<QPalette::ColorGroup>(i);
61 if (!pal.isBrushSet(cg: group, cr: QPalette::Accent)) {
62 // Default to highlight if available, otherwise use a shade of base
63 const QBrush accentBrush = pal.isBrushSet(cg: group, cr: QPalette::Highlight)
64 ? pal.brush(cg: group, cr: QPalette::Highlight)
65 : pal.brush(cg: group, cr: QPalette::Base).color().lighter(f: lighter);
66 pal.setBrush(cg: group, cr: QPalette::Accent, brush: accentBrush);
67 }
68 }
69}
70
71static void qt_palette_from_color(QPalette &pal, const QColor &button)
72{
73 int h, s, v;
74 button.getHsv(h: &h, s: &s, v: &v);
75 // inactive and active are the same..
76 const QBrush whiteBrush = QBrush(Qt::white);
77 const QBrush blackBrush = QBrush(Qt::black);
78 const QBrush baseBrush = v > 128 ? whiteBrush : blackBrush;
79 const QBrush foregroundBrush = v > 128 ? blackBrush : whiteBrush;
80 const QBrush buttonBrush = QBrush(button);
81 const QBrush buttonBrushDark = QBrush(button.darker());
82 const QBrush buttonBrushDark150 = QBrush(button.darker(f: 150));
83 const QBrush buttonBrushLight150 = QBrush(button.lighter(f: 150));
84 pal.setColorGroup(cr: QPalette::Active, windowText: foregroundBrush, button: buttonBrush, light: buttonBrushLight150,
85 dark: buttonBrushDark, mid: buttonBrushDark150, text: foregroundBrush, bright_text: whiteBrush,
86 base: baseBrush, window: buttonBrush);
87 pal.setColorGroup(cr: QPalette::Inactive, windowText: foregroundBrush, button: buttonBrush, light: buttonBrushLight150,
88 dark: buttonBrushDark, mid: buttonBrushDark150, text: foregroundBrush, bright_text: whiteBrush,
89 base: baseBrush, window: buttonBrush);
90 pal.setColorGroup(cr: QPalette::Disabled, windowText: buttonBrushDark, button: buttonBrush, light: buttonBrushLight150,
91 dark: buttonBrushDark, mid: buttonBrushDark150, text: buttonBrushDark,
92 bright_text: whiteBrush, base: buttonBrush, window: buttonBrush);
93
94 qt_placeholder_from_text(pal);
95 qt_ensure_default_accent_color(pal);
96}
97
98/*!
99 \fn QPalette &QPalette::operator=(QPalette &&other)
100
101 Move-assigns \a other to this QPalette instance.
102
103 \since 5.2
104*/
105
106/*!
107 \fn const QColor &QPalette::color(ColorRole role) const
108
109 \overload
110
111 Returns the color that has been set for the given color \a role in
112 the current ColorGroup.
113
114 \sa brush(), ColorRole
115 */
116
117/*!
118 \fn const QBrush &QPalette::brush(ColorRole role) const
119
120 \overload
121
122 Returns the brush that has been set for the given color \a role in
123 the current ColorGroup.
124
125 \sa color(), setBrush(), ColorRole
126*/
127
128/*!
129 \fn void QPalette::setColor(ColorRole role, const QColor &color)
130
131 \overload
132
133 Sets the color used for the given color \a role, in all color
134 groups, to the specified solid \a color.
135
136 \sa brush(), setColor(), ColorRole
137*/
138
139/*!
140 \fn void QPalette::setBrush(ColorRole role, const QBrush &brush)
141
142 Sets the brush for the given color \a role to the specified \a
143 brush for all groups in the palette.
144
145 \sa brush(), setColor(), ColorRole
146*/
147
148/*!
149 \fn const QBrush & QPalette::windowText() const
150
151 Returns the window text (general foreground) brush of the
152 current color group.
153
154 \sa ColorRole, brush()
155*/
156
157/*!
158 \fn const QBrush & QPalette::button() const
159
160 Returns the button brush of the current color group.
161
162 \sa ColorRole, brush()
163*/
164
165/*!
166 \fn const QBrush & QPalette::light() const
167
168 Returns the light brush of the current color group.
169
170 \sa ColorRole, brush()
171*/
172
173/*!
174 \fn const QBrush& QPalette::midlight() const
175
176 Returns the midlight brush of the current color group.
177
178 \sa ColorRole, brush()
179*/
180
181/*!
182 \fn const QBrush & QPalette::dark() const
183
184 Returns the dark brush of the current color group.
185
186 \sa ColorRole, brush()
187*/
188
189/*!
190 \fn const QBrush & QPalette::mid() const
191
192 Returns the mid brush of the current color group.
193
194 \sa ColorRole, brush()
195*/
196
197/*!
198 \fn const QBrush & QPalette::text() const
199
200 Returns the text foreground brush of the current color group.
201
202 \sa ColorRole, brush()
203*/
204
205/*!
206 \fn const QBrush & QPalette::brightText() const
207
208 Returns the bright text foreground brush of the current color group.
209
210 \sa ColorRole, brush()
211*/
212
213/*!
214 \fn const QBrush & QPalette::buttonText() const
215
216 Returns the button text foreground brush of the current color group.
217
218 \sa ColorRole, brush()
219*/
220
221/*!
222 \fn const QBrush & QPalette::base() const
223
224 Returns the base brush of the current color group.
225
226 \sa ColorRole, brush()
227*/
228
229/*!
230 \fn const QBrush & QPalette::alternateBase() const
231
232 Returns the alternate base brush of the current color group.
233
234 \sa ColorRole, brush()
235*/
236
237/*!
238 \fn const QBrush & QPalette::toolTipBase() const
239 \since 4.4
240
241 Returns the tool tip base brush of the current color group. This brush is
242 used by QToolTip and QWhatsThis.
243
244 \note Tool tips use the Inactive color group of QPalette, because tool
245 tips are not active windows.
246
247 \sa ColorRole, brush()
248*/
249
250/*!
251 \fn const QBrush & QPalette::toolTipText() const
252 \since 4.4
253
254 Returns the tool tip text brush of the current color group. This brush is
255 used by QToolTip and QWhatsThis.
256
257 \note Tool tips use the Inactive color group of QPalette, because tool
258 tips are not active windows.
259
260 \sa ColorRole, brush()
261*/
262
263/*!
264 \fn const QBrush & QPalette::window() const
265
266 Returns the window (general background) brush of the current
267 color group.
268
269 \sa ColorRole, brush()
270*/
271
272/*!
273 \fn const QBrush & QPalette::shadow() const
274
275 Returns the shadow brush of the current color group.
276
277 \sa ColorRole, brush()
278*/
279
280/*!
281 \fn const QBrush & QPalette::highlight() const
282
283 Returns the highlight brush of the current color group.
284
285 \sa ColorRole, brush()
286*/
287
288/*!
289 \fn const QBrush & QPalette::highlightedText() const
290
291 Returns the highlighted text brush of the current color group.
292
293 \sa ColorRole, brush()
294*/
295
296/*!
297 \fn const QBrush & QPalette::accent() const
298 \since 6.6
299
300 Returns the accent brush of the current color group.
301
302 \sa ColorRole, brush()
303*/
304
305/*!
306 \fn const QBrush & QPalette::link() const
307
308 Returns the unvisited link text brush of the current color group.
309
310 \sa ColorRole, brush()
311*/
312
313/*!
314 \fn const QBrush & QPalette::linkVisited() const
315
316 Returns the visited link text brush of the current color group.
317
318 \sa ColorRole, brush()
319*/
320
321/*!
322 \fn const QBrush & QPalette::placeholderText() const
323 \since 5.12
324
325 Returns the placeholder text brush of the current color group.
326
327 \note Before Qt 5.12, the placeholder text color was hard-coded as QPalette::text().color()
328 with an alpha of 128 applied. In Qt 6, it is an independent color.
329
330 \sa ColorRole, brush()
331*/
332
333/*!
334 \fn ColorGroup QPalette::currentColorGroup() const
335
336 Returns the palette's current color group.
337*/
338
339/*!
340 \fn void QPalette::setCurrentColorGroup(ColorGroup cg)
341
342 Set the palette's current color group to \a cg.
343*/
344
345/*!
346 \class QPalette
347
348 \brief The QPalette class contains color groups for each widget state.
349
350 \inmodule QtGui
351 \ingroup appearance
352 \ingroup shared
353
354 A palette consists of three color groups: \e Active, \e Disabled,
355 and \e Inactive. All widgets in Qt contain a palette and
356 use their palette to draw themselves. This makes the user
357 interface easily configurable and easier to keep consistent.
358
359
360 If you create a new widget we strongly recommend that you use the
361 colors in the palette rather than hard-coding specific colors.
362
363 The color groups:
364 \list
365 \li The Active group is used for the window that has keyboard focus.
366 \li The Inactive group is used for other windows.
367 \li The Disabled group is used for widgets (not windows) that are
368 disabled for some reason.
369 \endlist
370
371 Both active and inactive windows can contain disabled widgets.
372 (Disabled widgets are often called \e inaccessible or \e{grayed
373 out}.)
374
375 In most styles, Active and Inactive look the same.
376
377 Colors and brushes can be set for particular roles in any of a palette's
378 color groups with setColor() and setBrush(). A color group contains a
379 group of colors used by widgets for drawing themselves. We recommend that
380 widgets use color group roles from the palette such as "foreground" and
381 "base" rather than literal colors like "red" or "turquoise". The color
382 roles are enumerated and defined in the \l ColorRole documentation.
383
384 We strongly recommend that you use the default palette of the
385 current style (returned by QGuiApplication::palette()) and
386 modify that as necessary. This is done by Qt's widgets when they
387 are drawn.
388
389 To modify a color group you call the functions
390 setColor() and setBrush(), depending on whether you want a pure
391 color or a pixmap pattern.
392
393 There are also corresponding color() and brush() getters, and a
394 commonly used convenience function to get the ColorRole for the current ColorGroup:
395 window(), windowText(), base(), etc.
396
397
398 You can copy a palette using the copy constructor and test to see
399 if two palettes are \e identical using isCopyOf().
400
401 QPalette is optimized by the use of \l{implicit sharing},
402 so it is very efficient to pass QPalette objects as arguments.
403
404 \warning Some styles do not use the palette for all drawing, for
405 instance, if they make use of native theme engines. This is the
406 case for both the Windows Vista and the \macos
407 styles.
408
409 \sa QApplication::setPalette(), QWidget::setPalette(), QColor
410*/
411
412/*!
413 \enum QPalette::ColorGroup
414
415 \value Disabled
416 \value Active
417 \value Inactive
418 \value Normal synonym for Active
419
420 \omitvalue All
421 \omitvalue NColorGroups
422 \omitvalue Current
423*/
424
425/*!
426 \enum QPalette::ColorRole
427
428 \image palette.png Color Roles
429
430 The ColorRole enum defines the different symbolic color roles used
431 in current GUIs.
432
433 The central roles are:
434
435 \value Window A general background color.
436
437 \value WindowText A general foreground color.
438
439 \value Base Used mostly as the background color for text entry widgets,
440 but can also be used for other painting - such as the
441 background of combobox drop down lists and toolbar handles.
442 It is usually white or another light color.
443
444 \value AlternateBase Used as the alternate background color in views with
445 alternating row colors (see
446 QAbstractItemView::setAlternatingRowColors()).
447
448 \value ToolTipBase Used as the background color for QToolTip and
449 QWhatsThis. Tool tips use the Inactive color group
450 of QPalette, because tool tips are not active
451 windows.
452
453 \value ToolTipText Used as the foreground color for QToolTip and
454 QWhatsThis. Tool tips use the Inactive color group
455 of QPalette, because tool tips are not active
456 windows.
457
458 \value PlaceholderText Used as the placeholder color for various text input widgets.
459 This enum value has been introduced in Qt 5.12
460
461 \value Text The foreground color used with \c Base. This is usually
462 the same as the \c WindowText, in which case it must provide
463 good contrast with \c Window and \c Base.
464
465 \value Button The general button background color. This background can be different from
466 \c Window as some styles require a different background color for buttons.
467
468 \value ButtonText A foreground color used with the \c Button color.
469
470 \value BrightText A text color that is very different from
471 \c WindowText, and contrasts well with e.g. \c
472 Dark. Typically used for text that needs to be
473 drawn where \c Text or \c WindowText would give
474 poor contrast, such as on pressed push buttons.
475 Note that text colors can be used for things
476 other than just words; text colors are \e
477 usually used for text, but it's quite common to
478 use the text color roles for lines, icons, etc.
479
480
481 There are some color roles used mostly for 3D bevel and shadow effects.
482 All of these are normally derived from \c Window, and used in ways that
483 depend on that relationship. For example, buttons depend on it to make the
484 bevels look attractive, and Motif scroll bars depend on \c Mid to be
485 slightly different from \c Window.
486
487 \value Light Lighter than \c Button color.
488
489 \value Midlight Between \c Button and \c Light.
490
491 \value Dark Darker than \c Button.
492
493 \value Mid Between \c Button and \c Dark.
494
495 \value Shadow A very dark color. By default, the shadow color is
496 Qt::black.
497
498
499 Selected (marked) items have two roles:
500
501 \value Highlight A color to indicate a selected item or the current
502 item. By default, the highlight color is
503 Qt::darkBlue.
504
505 \value [since 6.6] Accent
506 A color that typically contrasts or complements
507 Base, Window and Button colors. It usually represents
508 the users' choice of desktop personalisation.
509 Styling of interactive components is a typical use case.
510 Unless explicitly set, it defaults to Highlight.
511
512 \value HighlightedText A text color that contrasts with \c Highlight.
513 By default, the highlighted text color is Qt::white.
514
515 There are two color roles related to hyperlinks:
516
517 \value Link A text color used for unvisited hyperlinks.
518 By default, the link color is Qt::blue.
519
520 \value LinkVisited A text color used for already visited hyperlinks.
521 By default, the linkvisited color is Qt::magenta.
522
523 Note that we do not use the \c Link and \c LinkVisited roles when
524 rendering rich text in Qt, and that we recommend that you use CSS
525 and the QTextDocument::setDefaultStyleSheet() function to alter
526 the appearance of links. For example:
527
528 \snippet textdocument-css/main.cpp 0
529
530 \value NoRole No role; this special role is often used to indicate that a
531 role has not been assigned.
532
533 \omitvalue NColorRoles
534*/
535
536/*!
537 Constructs an empty palette object with no color roles set.
538
539 When used as the palette of a QWidget the colors are resolved
540 as described by QWidget::setPalette().
541
542 \sa QApplication::setPalette(), QApplication::palette()
543*/
544QPalette::QPalette()
545 : d(nullptr)
546{
547 // Initialize to application palette if present, else default to black.
548 // This makes it possible to instantiate QPalette outside QGuiApplication,
549 // for example in the platform plugins.
550 if (QGuiApplicationPrivate::app_pal) {
551 d = QGuiApplicationPrivate::app_pal->d;
552 d->ref.ref();
553 setResolveMask(0);
554 } else {
555 init();
556 qt_palette_from_color(pal&: *this, button: Qt::black);
557 d->resolveMask = 0;
558 }
559}
560
561/*!
562 Constructs a palette from the \a button color. The other colors are
563 automatically calculated, based on this color. \c Window will be
564 the button color as well.
565*/
566QPalette::QPalette(const QColor &button)
567{
568 init();
569 qt_palette_from_color(pal&: *this, button);
570}
571
572/*!
573 Constructs a palette from the \a button color. The other colors are
574 automatically calculated, based on this color. \c Window will be
575 the button color as well.
576*/
577QPalette::QPalette(Qt::GlobalColor button)
578{
579 init();
580 qt_palette_from_color(pal&: *this, button);
581}
582
583/*!
584 Constructs a palette. You can pass either brushes, pixmaps or
585 plain colors for \a windowText, \a button, \a light, \a dark, \a
586 mid, \a text, \a bright_text, \a base and \a window.
587
588 \sa QBrush
589*/
590QPalette::QPalette(const QBrush &windowText, const QBrush &button,
591 const QBrush &light, const QBrush &dark,
592 const QBrush &mid, const QBrush &text,
593 const QBrush &bright_text, const QBrush &base,
594 const QBrush &window)
595{
596 init();
597 setColorGroup(cr: All, windowText, button, light, dark, mid, text, bright_text,
598 base, window);
599
600 qt_placeholder_from_text(pal&: *this);
601 qt_ensure_default_accent_color(pal&: *this);
602}
603
604
605/*!
606 \deprecated
607
608 Constructs a palette with the specified \a windowText, \a
609 window, \a light, \a dark, \a mid, \a text, and \a base colors.
610 The button color will be set to the window color.
611*/
612QPalette::QPalette(const QColor &windowText, const QColor &window,
613 const QColor &light, const QColor &dark, const QColor &mid,
614 const QColor &text, const QColor &base)
615{
616 init();
617 const QBrush windowBrush(window);
618 const QBrush lightBrush(light);
619 setColorGroup(cr: All, windowText: QBrush(windowText), button: windowBrush, light: lightBrush,
620 dark: QBrush(dark), mid: QBrush(mid), text: QBrush(text), bright_text: lightBrush,
621 base: QBrush(base), window: windowBrush);
622}
623
624/*!
625 Constructs a palette from a \a button color and a \a window.
626 The other colors are automatically calculated, based on these
627 colors.
628*/
629QPalette::QPalette(const QColor &button, const QColor &window)
630{
631 init();
632 int h, s, v;
633 window.getHsv(h: &h, s: &s, v: &v);
634
635 const QBrush windowBrush = QBrush(window);
636 const QBrush whiteBrush = QBrush(Qt::white);
637 const QBrush blackBrush = QBrush(Qt::black);
638 const QBrush baseBrush = v > 128 ? whiteBrush : blackBrush;
639 const QBrush foregroundBrush = v > 128 ? blackBrush : whiteBrush;
640 const QBrush disabledForeground = QBrush(Qt::darkGray);
641
642 const QBrush buttonBrush = QBrush(button);
643 const QBrush buttonBrushDark = QBrush(button.darker());
644 const QBrush buttonBrushDark150 = QBrush(button.darker(f: 150));
645 const QBrush buttonBrushLight150 = QBrush(button.lighter(f: 150));
646
647 //inactive and active are identical
648 setColorGroup(cr: Inactive, windowText: foregroundBrush, button: buttonBrush, light: buttonBrushLight150, dark: buttonBrushDark,
649 mid: buttonBrushDark150, text: foregroundBrush, bright_text: whiteBrush, base: baseBrush,
650 window: windowBrush);
651 setColorGroup(cr: Active, windowText: foregroundBrush, button: buttonBrush, light: buttonBrushLight150, dark: buttonBrushDark,
652 mid: buttonBrushDark150, text: foregroundBrush, bright_text: whiteBrush, base: baseBrush,
653 window: windowBrush);
654 setColorGroup(cr: Disabled, windowText: disabledForeground, button: buttonBrush, light: buttonBrushLight150,
655 dark: buttonBrushDark, mid: buttonBrushDark150, text: disabledForeground,
656 bright_text: whiteBrush, base: baseBrush, window: windowBrush);
657
658 qt_placeholder_from_text(pal&: *this);
659 qt_ensure_default_accent_color(pal&: *this);
660}
661
662/*!
663 Constructs a copy of \a p.
664
665 This constructor is fast thanks to \l{implicit sharing}.
666*/
667QPalette::QPalette(const QPalette &p)
668 : d(p.d), currentGroup(p.currentGroup)
669{
670 d->ref.ref();
671}
672
673/*!
674 \fn QPalette::QPalette(QPalette &&other)
675 \since 5.4
676
677 Move-constructs a QPalette instance, making it point at the same
678 object that \a other was pointing to.
679
680 After being moved from, you can only assign to or destroy \a other.
681 Any other operation will result in undefined behavior.
682*/
683
684/*!
685 Destroys the palette.
686*/
687QPalette::~QPalette()
688{
689 if (d && !d->ref.deref())
690 delete d;
691}
692
693/*!\internal*/
694void QPalette::init()
695{
696 d = new QPalettePrivate;
697}
698
699/*!
700 Assigns \a p to this palette and returns a reference to this
701 palette.
702
703 This operation is fast thanks to \l{implicit sharing}.
704*/
705QPalette &QPalette::operator=(const QPalette &p)
706{
707 p.d->ref.ref();
708 currentGroup = p.currentGroup;
709 if (d && !d->ref.deref())
710 delete d;
711 d = p.d;
712 return *this;
713}
714
715/*!
716 \fn void QPalette::swap(QPalette &other)
717 \since 5.0
718 \memberswap{palette instance}
719*/
720
721/*!
722 Returns the palette as a QVariant
723*/
724QPalette::operator QVariant() const
725{
726 return QVariant::fromValue(value: *this);
727}
728
729/*!
730 \fn const QColor &QPalette::color(ColorGroup group, ColorRole role) const
731
732 Returns the color in the specified color \a group, used for the
733 given color \a role.
734
735 \sa brush(), setColor(), ColorRole
736*/
737
738/*!
739 \fn const QBrush &QPalette::brush(ColorGroup group, ColorRole role) const
740
741 Returns the brush in the specified color \a group, used for the
742 given color \a role.
743
744 \sa color(), setBrush(), ColorRole
745*/
746const QBrush &QPalette::brush(ColorGroup gr, ColorRole cr) const
747{
748 Q_ASSERT(cr < NColorRoles);
749 if (gr >= (int)NColorGroups) {
750 if (gr == Current) {
751 gr = currentGroup;
752 } else {
753 qWarning(msg: "QPalette::brush: Unknown ColorGroup: %d", (int)gr);
754 gr = Active;
755 }
756 }
757 return d->data->br[gr][cr];
758}
759
760/*!
761 \fn void QPalette::setColor(ColorGroup group, ColorRole role, const QColor &color)
762
763 Sets the color in the specified color \a group, used for the given
764 color \a role, to the specified solid \a color.
765
766 \sa setBrush(), color(), ColorRole
767*/
768
769/*!
770 \fn void QPalette::setBrush(ColorGroup group, ColorRole role, const QBrush &brush)
771 \overload
772
773 Sets the brush in the specified color \a group, used for the given
774 color \a role, to \a brush.
775
776 \sa brush(), setColor(), ColorRole
777*/
778void QPalette::setBrush(ColorGroup cg, ColorRole cr, const QBrush &b)
779{
780 Q_ASSERT(cr < NColorRoles);
781
782 if (cg == All) {
783 for (uint i = 0; i < NColorGroups; i++)
784 setBrush(cg: ColorGroup(i), cr, b);
785 return;
786 }
787
788 if (cg == Current) {
789 cg = currentGroup;
790 } else if (cg >= NColorGroups) {
791 qWarning(msg: "QPalette::setBrush: Unknown ColorGroup: %d", cg);
792 cg = Active;
793 }
794
795 const auto newResolveMask = d->resolveMask | ResolveMask(1) << QPalettePrivate::bitPosition(colorGroup: cg, colorRole: cr);
796 const auto valueChanged = d->data->br[cg][cr] != b;
797
798 if (valueChanged) {
799 detach();
800 d->data.detach();
801 d->data->br[cg][cr] = b;
802 } else if (d->resolveMask != newResolveMask) {
803 detach();
804 }
805
806 d->resolveMask = newResolveMask;
807}
808
809/*!
810 \since 4.2
811
812 Returns \c true if the ColorGroup \a cg and ColorRole \a cr has been
813 set previously on this palette; otherwise returns \c false.
814
815 The ColorGroup \a cg should be less than QPalette::NColorGroups,
816 but you can use QPalette::Current. In this case, the previously
817 set current color group will be used.
818
819 The ColorRole \a cr should be less than QPalette::NColorRoles.
820
821 \sa setBrush(), currentColorGroup()
822*/
823bool QPalette::isBrushSet(ColorGroup cg, ColorRole cr) const
824{
825 // NoRole has no resolve mask and should never be set anyway
826 if (cr == NoRole)
827 return false;
828
829 if (cg == Current)
830 cg = currentGroup;
831
832 if (cg >= NColorGroups) {
833 qWarning() << "Wrong color group:" << cg;
834 return false;
835 }
836
837 if (cr >= NColorRoles) {
838 qWarning() << "Wrong color role:" << cr;
839 return false;
840 }
841
842 return d->resolveMask & (ResolveMask(1) << QPalettePrivate::bitPosition(colorGroup: cg, colorRole: cr));
843}
844
845/*!
846 \internal
847*/
848void QPalette::detach()
849{
850 if (d->ref.loadRelaxed() != 1) {
851 QPalettePrivate *x = new QPalettePrivate(d->data);
852 x->resolveMask = d->resolveMask;
853 if (!d->ref.deref())
854 delete d;
855 d = x;
856 } else {
857 d->detach_no = ++QPalettePrivate::qt_palette_private_count;
858 }
859}
860
861/*!
862 \fn bool QPalette::operator!=(const QPalette &p) const
863
864 Returns \c true (slowly) if this palette is different from \a p;
865 otherwise returns \c false (usually quickly).
866
867 \note The current ColorGroup is not taken into account when
868 comparing palettes
869
870 \sa operator==()
871*/
872
873/*!
874 Returns \c true (usually quickly) if this palette is equal to \a p;
875 otherwise returns \c false (slowly).
876
877 \note The following is not taken into account when comparing palettes:
878 \list
879 \li the \c current ColorGroup
880 \li ColorRole NoRole \since 6.6
881 \endlist
882
883 \sa operator!=()
884*/
885bool QPalette::operator==(const QPalette &p) const
886{
887 if (isCopyOf(p) || d->data == p.d->data)
888 return true;
889 for(int grp = 0; grp < (int)NColorGroups; grp++) {
890 for(int role = 0; role < (int)NColorRoles; role++) {
891 // Dont't verify NoRole, because it has no resolve bit
892 if (role == NoRole)
893 continue;
894 if (d->data->br[grp][role] != p.d->data->br[grp][role])
895 return false;
896 }
897 }
898 return true;
899}
900
901/*!
902 \fn bool QPalette::isEqual(ColorGroup cg1, ColorGroup cg2) const
903
904 Returns \c true (usually quickly) if color group \a cg1 is equal to
905 \a cg2; otherwise returns \c false.
906*/
907bool QPalette::isEqual(QPalette::ColorGroup group1, QPalette::ColorGroup group2) const
908{
909 if (group1 >= (int)NColorGroups) {
910 if (group1 == Current) {
911 group1 = currentGroup;
912 } else {
913 qWarning(msg: "QPalette::brush: Unknown ColorGroup(1): %d", (int)group1);
914 group1 = Active;
915 }
916 }
917 if (group2 >= (int)NColorGroups) {
918 if (group2 == Current) {
919 group2 = currentGroup;
920 } else {
921 qWarning(msg: "QPalette::brush: Unknown ColorGroup(2): %d", (int)group2);
922 group2 = Active;
923 }
924 }
925 if (group1 == group2)
926 return true;
927 for(int role = 0; role < (int)NColorRoles; role++) {
928 if (d->data->br[group1][role] != d->data->br[group2][role])
929 return false;
930 }
931 return true;
932}
933
934/*!
935 Returns a number that identifies the contents of this QPalette
936 object. Distinct QPalette objects can have the same key if
937 they refer to the same contents.
938
939 The cacheKey() will change when the palette is altered.
940*/
941qint64 QPalette::cacheKey() const
942{
943 return (((qint64) d->data->ser_no) << 32) | ((qint64) (d->detach_no));
944}
945
946static constexpr QPalette::ResolveMask allResolveMask()
947{
948 QPalette::ResolveMask mask = {0};
949 for (int role = 0; role < int(QPalette::NColorRoles); ++role) {
950 for (int grp = 0; grp < int(QPalette::NColorGroups); ++grp) {
951 mask |= (QPalette::ResolveMask(1) << QPalettePrivate::bitPosition(colorGroup: QPalette::ColorGroup(grp), colorRole: QPalette::ColorRole(role)));
952 }
953 }
954 return mask;
955}
956
957/*!
958 Returns a new QPalette that is a union of this instance and \a other.
959 Color roles set in this instance take precedence. Roles that are not
960 set in this instance will be taken from \a other.
961
962 \sa isBrushSet
963*/
964QPalette QPalette::resolve(const QPalette &other) const
965{
966 if ((*this == other && d->resolveMask == other.d->resolveMask)
967 || d->resolveMask == 0) {
968 QPalette o = other;
969 o.setResolveMask(d->resolveMask);
970 return o;
971 }
972
973 if (d->resolveMask == allResolveMask())
974 return *this;
975
976 QPalette palette(*this);
977 palette.detach();
978
979 for (int role = 0; role < int(NColorRoles); ++role) {
980 // Don't resolve NoRole, its bits are needed for Accent (see bitPosition)
981 if (role == NoRole)
982 continue;
983
984 for (int grp = 0; grp < int(NColorGroups); ++grp) {
985 if (!(d->resolveMask & (ResolveMask(1) << QPalettePrivate::bitPosition(colorGroup: ColorGroup(grp), colorRole: ColorRole(role))))) {
986 palette.d->data.detach();
987 palette.d->data->br[grp][role] = other.d->data->br[grp][role];
988 }
989 }
990 }
991
992 palette.d->resolveMask |= other.d->resolveMask;
993
994 return palette;
995}
996
997/*!
998 \internal
999*/
1000QPalette::ResolveMask QPalette::resolveMask() const
1001{
1002 return d->resolveMask;
1003}
1004
1005/*!
1006 \internal
1007*/
1008void QPalette::setResolveMask(QPalette::ResolveMask mask)
1009{
1010 if (mask == d->resolveMask)
1011 return;
1012
1013 detach();
1014 d->resolveMask = mask;
1015}
1016
1017/*!
1018 \typedef ResolveMask
1019 \internal
1020
1021 A bit mask that stores which colors the palette instance explicitly defines,
1022 and which ones are inherited from a parent.
1023*/
1024
1025/*****************************************************************************
1026 QPalette stream functions
1027 *****************************************************************************/
1028
1029#ifndef QT_NO_DATASTREAM
1030
1031static const int NumOldRoles = 7;
1032static const int oldRoles[7] = { QPalette::WindowText, QPalette::Window, QPalette::Light,
1033 QPalette::Dark, QPalette::Mid, QPalette::Text, QPalette::Base };
1034
1035/*!
1036 \relates QPalette
1037
1038 Writes the palette, \a p to the stream \a s and returns a
1039 reference to the stream.
1040
1041 \sa{Serializing Qt Data Types}{Format of the QDataStream operators}
1042*/
1043
1044QDataStream &operator<<(QDataStream &s, const QPalette &p)
1045{
1046 for (int grp = 0; grp < (int)QPalette::NColorGroups; grp++) {
1047 if (s.version() == 1) {
1048 // Qt 1.x
1049 for (int i = 0; i < NumOldRoles; ++i)
1050 s << p.d->data->br[grp][oldRoles[i]].color();
1051 } else {
1052 int max = (int)QPalette::NColorRoles;
1053 if (s.version() <= QDataStream::Qt_2_1)
1054 max = QPalette::HighlightedText + 1;
1055 else if (s.version() <= QDataStream::Qt_4_3)
1056 max = QPalette::AlternateBase + 1;
1057 else if (s.version() <= QDataStream::Qt_5_11)
1058 max = QPalette::ToolTipText + 1;
1059 else if (s.version() <= QDataStream::Qt_6_5)
1060 max = QPalette::PlaceholderText + 1;
1061
1062 for (int r = 0; r < max; r++)
1063 s << p.d->data->br[grp][r];
1064 }
1065 }
1066 return s;
1067}
1068
1069static void readV1ColorGroup(QDataStream &s, QPalette &pal, QPalette::ColorGroup grp)
1070{
1071 for (int i = 0; i < NumOldRoles; ++i) {
1072 QColor col;
1073 s >> col;
1074 pal.setColor(acg: grp, acr: (QPalette::ColorRole)oldRoles[i], acolor: col);
1075 }
1076}
1077
1078/*!
1079 \relates QPalette
1080
1081 Reads a palette from the stream, \a s into the palette \a p, and
1082 returns a reference to the stream.
1083
1084 \sa{Serializing Qt Data Types}{Format of the QDataStream operators}
1085*/
1086
1087QDataStream &operator>>(QDataStream &s, QPalette &p)
1088{
1089 if (s.version() == 1) {
1090 p = QPalette();
1091 readV1ColorGroup(s, pal&: p, grp: QPalette::Active);
1092 readV1ColorGroup(s, pal&: p, grp: QPalette::Disabled);
1093 readV1ColorGroup(s, pal&: p, grp: QPalette::Inactive);
1094 } else {
1095 int max = QPalette::NColorRoles;
1096 if (s.version() <= QDataStream::Qt_2_1) {
1097 p = QPalette();
1098 max = QPalette::HighlightedText + 1;
1099 } else if (s.version() <= QDataStream::Qt_4_3) {
1100 p = QPalette();
1101 max = QPalette::AlternateBase + 1;
1102 } else if (s.version() <= QDataStream::Qt_5_11) {
1103 p = QPalette();
1104 max = QPalette::ToolTipText + 1;
1105 } else if (s.version() <= QDataStream::Qt_6_5) {
1106 p = QPalette();
1107 max = QPalette::PlaceholderText + 1;
1108 }
1109
1110
1111 QBrush tmp;
1112 for(int grp = 0; grp < (int)QPalette::NColorGroups; ++grp) {
1113 const QPalette::ColorGroup group = static_cast<QPalette::ColorGroup>(grp);
1114 for(int role = 0; role < max; ++role) {
1115 s >> tmp;
1116 p.setBrush(cg: group, cr: (QPalette::ColorRole)role, b: tmp);
1117 }
1118
1119 // Accent defaults to Highlight for stream versions that don't have it.
1120 if (s.version() < QDataStream::Qt_6_6)
1121 p.setBrush(cg: group, cr: QPalette::Accent, b: p.brush(gr: group, cr: QPalette::Highlight));
1122 }
1123
1124 }
1125 return s;
1126}
1127#endif //QT_NO_DATASTREAM
1128
1129/*!
1130 Returns \c true if this palette and \a p are copies of each other,
1131 i.e. one of them was created as a copy of the other and neither
1132 was subsequently modified; otherwise returns \c false. This is much
1133 stricter than equality.
1134
1135 \sa operator=(), operator==()
1136*/
1137
1138bool QPalette::isCopyOf(const QPalette &p) const
1139{
1140 return d == p.d;
1141}
1142
1143/*!
1144
1145 Sets a the group at \a cg. You can pass either brushes, pixmaps or
1146 plain colors for \a windowText, \a button, \a light, \a dark, \a
1147 mid, \a text, \a bright_text, \a base and \a window.
1148
1149 \sa QBrush
1150*/
1151void QPalette::setColorGroup(ColorGroup cg, const QBrush &windowText, const QBrush &button,
1152 const QBrush &light, const QBrush &dark, const QBrush &mid,
1153 const QBrush &text, const QBrush &bright_text, const QBrush &base,
1154 const QBrush &window)
1155{
1156 QBrush alt_base = QBrush(qt_mix_colors(a: base.color(), b: button.color()));
1157 QBrush mid_light = QBrush(qt_mix_colors(a: button.color(), b: light.color()));
1158 QColor toolTipBase(255, 255, 220);
1159 QColor toolTipText(0, 0, 0);
1160
1161 setColorGroup(cr: cg, windowText, button, light, dark, mid, text, bright_text, base,
1162 alternate_base: alt_base, window, midlight: mid_light, button_text: text,
1163 shadow: QBrush(Qt::black), highlight: QBrush(Qt::darkBlue), highlighted_text: QBrush(Qt::white),
1164 link: QBrush(Qt::blue), link_visited: QBrush(Qt::magenta), toolTipBase: QBrush(toolTipBase),
1165 toolTipText: QBrush(toolTipText));
1166
1167 for (int cr = Highlight; cr <= LinkVisited; ++cr) {
1168 if (cg == All) {
1169 for (int group = Active; group < NColorGroups; ++group) {
1170 d->resolveMask &= ~(ResolveMask(1) << QPalettePrivate::bitPosition(colorGroup: ColorGroup(group), colorRole: ColorRole(cr)));
1171 }
1172 } else {
1173 d->resolveMask &= ~(ResolveMask(1) << QPalettePrivate::bitPosition(colorGroup: ColorGroup(cg), colorRole: ColorRole(cr)));
1174 }
1175 }
1176}
1177
1178
1179/*!\internal*/
1180void
1181QPalette::setColorGroup(ColorGroup cg, const QBrush &foreground, const QBrush &button,
1182 const QBrush &light, const QBrush &dark, const QBrush &mid,
1183 const QBrush &text, const QBrush &bright_text,
1184 const QBrush &base, const QBrush &alternate_base,
1185 const QBrush &background, const QBrush &midlight,
1186 const QBrush &button_text, const QBrush &shadow,
1187 const QBrush &highlight, const QBrush &highlighted_text,
1188 const QBrush &link, const QBrush &link_visited)
1189{
1190 setColorGroup(cr: cg, windowText: foreground, button, light, dark, mid,
1191 text, bright_text, base, alternate_base, window: background,
1192 midlight, button_text, shadow, highlight, highlighted_text,
1193 link, link_visited, toolTipBase: background, toolTipText: foreground);
1194}
1195
1196/*!\internal*/
1197void QPalette::setColorGroup(ColorGroup cg, const QBrush &foreground, const QBrush &button,
1198 const QBrush &light, const QBrush &dark, const QBrush &mid,
1199 const QBrush &text, const QBrush &bright_text,
1200 const QBrush &base, const QBrush &alternate_base,
1201 const QBrush &background, const QBrush &midlight,
1202 const QBrush &button_text, const QBrush &shadow,
1203 const QBrush &highlight, const QBrush &highlighted_text,
1204 const QBrush &link, const QBrush &link_visited,
1205 const QBrush &toolTipBase, const QBrush &toolTipText)
1206{
1207 setBrush(cg, cr: WindowText, b: foreground);
1208 setBrush(cg, cr: Button, b: button);
1209 setBrush(cg, cr: Light, b: light);
1210 setBrush(cg, cr: Dark, b: dark);
1211 setBrush(cg, cr: Mid, b: mid);
1212 setBrush(cg, cr: Text, b: text);
1213 setBrush(cg, cr: BrightText, b: bright_text);
1214 setBrush(cg, cr: Base, b: base);
1215 setBrush(cg, cr: AlternateBase, b: alternate_base);
1216 setBrush(cg, cr: Window, b: background);
1217 setBrush(cg, cr: Midlight, b: midlight);
1218 setBrush(cg, cr: ButtonText, b: button_text);
1219 setBrush(cg, cr: Shadow, b: shadow);
1220 setBrush(cg, cr: Highlight, b: highlight);
1221 setBrush(cg, cr: HighlightedText, b: highlighted_text);
1222 setBrush(cg, cr: Link, b: link);
1223 setBrush(cg, cr: LinkVisited, b: link_visited);
1224 setBrush(cg, cr: ToolTipBase, b: toolTipBase);
1225 setBrush(cg, cr: ToolTipText, b: toolTipText);
1226}
1227
1228#ifndef QT_NO_DEBUG_STREAM
1229static QString groupsToString(const QPalette &p, QPalette::ColorRole cr)
1230{
1231 const auto groupEnum = QMetaEnum::fromType<QPalette::ColorGroup>();
1232
1233 QString groupString;
1234 for (int group = 0; group < QPalette::NColorGroups; ++group) {
1235 const auto cg = QPalette::ColorGroup(group);
1236
1237 if (p.isBrushSet(cg, cr)) {
1238 const auto &color = p.color(cg, cr);
1239 groupString += QString::fromUtf8(utf8: groupEnum.valueToKey(value: cg)) + u':' +
1240 color.name(format: QColor::HexArgb) + u',';
1241 }
1242 }
1243 groupString.chop(n: 1);
1244
1245 return groupString;
1246}
1247
1248static QString rolesToString(const QPalette &p)
1249{
1250 const auto roleEnum = QMetaEnum::fromType<QPalette::ColorRole>();
1251
1252 QString roleString;
1253 for (int role = 0; role < QPalette::NColorRoles; ++role) {
1254 const auto cr = QPalette::ColorRole(role);
1255
1256 auto groupString = groupsToString(p, cr);
1257 if (!groupString.isEmpty())
1258 roleString += QString::fromUtf8(utf8: roleEnum.valueToKey(value: cr)) + QStringLiteral(":[") +
1259 groupString + QStringLiteral("],");
1260 }
1261 roleString.chop(n: 1);
1262
1263 return roleString;
1264}
1265
1266QDebug operator<<(QDebug dbg, const QPalette &p)
1267{
1268 QDebugStateSaver saver(dbg);
1269 dbg.nospace();
1270
1271 dbg << "QPalette(resolve=" << Qt::hex << Qt::showbase << p.resolveMask();
1272
1273 auto roleString = rolesToString(p);
1274 if (!roleString.isEmpty())
1275 dbg << ',' << roleString;
1276
1277 dbg << ')';
1278
1279 return dbg;
1280 }
1281#endif
1282
1283QT_END_NAMESPACE
1284
1285#include "moc_qpalette.cpp"
1286

source code of qtbase/src/gui/kernel/qpalette.cpp