diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h index 0942c3ed1d1..9e35328de41 100644 --- a/src/corelib/serialization/qdatastream.h +++ b/src/corelib/serialization/qdatastream.h @@ -68,7 +68,7 @@ public: Qt_6_3 = Qt_6_0, Qt_6_4 = Qt_6_0, Qt_6_5 = Qt_6_0, - Qt_6_6 = Qt_6_0, + Qt_6_6 = 21, Qt_DefaultCompiledVersion = Qt_6_6 #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) #error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion diff --git a/src/gui/kernel/qpalette.cpp b/src/gui/kernel/qpalette.cpp index 4abbcd5e655..bc5ebeb4de2 100644 --- a/src/gui/kernel/qpalette.cpp +++ b/src/gui/kernel/qpalette.cpp @@ -16,12 +16,17 @@ static int qt_palette_count = 1; static constexpr QPalette::ResolveMask colorRoleOffset(QPalette::ColorGroup colorGroup) { - return qToUnderlying(QPalette::NColorRoles) * qToUnderlying(colorGroup); + // Exclude NoRole; that bit is used for AccentColor + return (qToUnderlying(QPalette::NColorRoles) - 1) * qToUnderlying(colorGroup); } static constexpr QPalette::ResolveMask bitPosition(QPalette::ColorGroup colorGroup, QPalette::ColorRole colorRole) { + // Map AccentColor into NoRole for resolving purposes + if (colorRole == QPalette::AccentColor) + colorRole = QPalette::NoRole; + return colorRole + colorRoleOffset(colorGroup); } @@ -100,6 +105,24 @@ static void qt_placeholder_from_text(QPalette &pal, int alpha = 50) } } +static void qt_ensure_default_accent_color(QPalette &pal) +{ + // have a lighter/darker factor handy, depending on dark/light heuristics + const int lighter = pal.base().color().lightness() > pal.text().color().lightness() ? 130 : 70; + + // Act only for color groups where no accent color is set + for (int i = 0; i < QPalette::NColorGroups; ++i) { + const QPalette::ColorGroup group = static_cast(i); + if (!pal.isBrushSet(group, QPalette::AccentColor)) { + // Default to highlight if available, otherwise use a shade of base + const QBrush accentBrush = pal.isBrushSet(group, QPalette::Highlight) + ? pal.brush(group, QPalette::Highlight) + : pal.brush(group, QPalette::Base).color().lighter(lighter); + pal.setBrush(group, QPalette::AccentColor, accentBrush); + } + } +} + static void qt_palette_from_color(QPalette &pal, const QColor &button) { int h, s, v; @@ -124,6 +147,7 @@ static void qt_palette_from_color(QPalette &pal, const QColor &button) whiteBrush, buttonBrush, buttonBrush); qt_placeholder_from_text(pal); + qt_ensure_default_accent_color(pal); } /*! @@ -524,6 +548,13 @@ static void qt_palette_from_color(QPalette &pal, const QColor &button) item. By default, the highlight color is Qt::darkBlue. + \value AccentColor A color that typically contrasts or compliments + Base, Window and Button colors. It usually represents + the users' choice of desktop personalisation. + Styling of interactive components is a typical use case. + Unless explicitly set, it defaults to Highlight. + This enum value has been introduced in Qt 6.6. + \value HighlightedText A text color that contrasts with \c Highlight. By default, the highlighted text color is Qt::white. @@ -613,6 +644,7 @@ QPalette::QPalette(const QBrush &windowText, const QBrush &button, base, window); qt_placeholder_from_text(*this); + qt_ensure_default_accent_color(*this); } @@ -670,6 +702,7 @@ QPalette::QPalette(const QColor &button, const QColor &window) whiteBrush, baseBrush, windowBrush); qt_placeholder_from_text(*this); + qt_ensure_default_accent_color(*this); } /*! @@ -837,6 +870,10 @@ void QPalette::setBrush(ColorGroup cg, ColorRole cr, const QBrush &b) */ bool QPalette::isBrushSet(ColorGroup cg, ColorRole cr) const { + // NoRole has no resolve mask and should never be set anyway + if (cr == NoRole) + return false; + if (cg == Current) cg = currentGroup; @@ -885,8 +922,11 @@ void QPalette::detach() Returns \c true (usually quickly) if this palette is equal to \a p; otherwise returns \c false (slowly). - \note The current ColorGroup is not taken into account when - comparing palettes + \note The following is not taken into account when comparing palettes: + \list + \li the \c current ColorGroup + \li ColorRole NoRole \since 6.6 + \endlist \sa operator!=() */ @@ -896,6 +936,9 @@ bool QPalette::operator==(const QPalette &p) const return true; for(int grp = 0; grp < (int)NColorGroups; grp++) { for(int role = 0; role < (int)NColorRoles; role++) { + // Dont't verify NoRole, because it has no resolve bit + if (role == NoRole) + continue; if (d->data->br[grp][role] != p.d->data->br[grp][role]) return false; } @@ -979,6 +1022,10 @@ QPalette QPalette::resolve(const QPalette &other) const palette.detach(); for (int role = 0; role < int(NColorRoles); ++role) { + // Don't resolve NoRole, its bits are needed for AccentColor (see bitPosition) + if (role == NoRole) + continue; + for (int grp = 0; grp < int(NColorGroups); ++grp) { if (!(d->resolveMask & (ResolveMask(1) << bitPosition(ColorGroup(grp), ColorRole(role))))) { palette.d->data.detach(); @@ -1054,6 +1101,9 @@ QDataStream &operator<<(QDataStream &s, const QPalette &p) max = QPalette::AlternateBase + 1; else if (s.version() <= QDataStream::Qt_5_11) max = QPalette::ToolTipText + 1; + else if (s.version() <= QDataStream::Qt_6_5) + max = QPalette::PlaceholderText + 1; + for (int r = 0; r < max; r++) s << p.d->data->br[grp][r]; } @@ -1097,15 +1147,25 @@ QDataStream &operator>>(QDataStream &s, QPalette &p) } else if (s.version() <= QDataStream::Qt_5_11) { p = QPalette(); max = QPalette::ToolTipText + 1; + } else if (s.version() <= QDataStream::Qt_6_5) { + p = QPalette(); + max = QPalette::PlaceholderText + 1; } + QBrush tmp; for(int grp = 0; grp < (int)QPalette::NColorGroups; ++grp) { + const QPalette::ColorGroup group = static_cast(grp); for(int role = 0; role < max; ++role) { s >> tmp; - p.setBrush((QPalette::ColorGroup)grp, (QPalette::ColorRole)role, tmp); + p.setBrush(group, (QPalette::ColorRole)role, tmp); } + + // AccentColor defaults to Highlight for stream versions that don't have it. + if (s.version() < QDataStream::Qt_6_6) + p.setBrush(group, QPalette::AccentColor, p.brush(group, QPalette::Highlight)); } + } return s; } diff --git a/src/gui/kernel/qpalette.h b/src/gui/kernel/qpalette.h index 0a6dc0b3817..92d2502afe8 100644 --- a/src/gui/kernel/qpalette.h +++ b/src/gui/kernel/qpalette.h @@ -55,7 +55,8 @@ public: NoRole, ToolTipBase, ToolTipText, PlaceholderText, - NColorRoles = PlaceholderText + 1, + AccentColor, + NColorRoles = AccentColor + 1, }; Q_ENUM(ColorRole) @@ -98,6 +99,7 @@ public: inline const QBrush &link() const { return brush(Link); } inline const QBrush &linkVisited() const { return brush(LinkVisited); } inline const QBrush &placeholderText() const { return brush(PlaceholderText); } + inline const QBrush &accentColor() const { return brush(AccentColor); } bool operator==(const QPalette &p) const; inline bool operator!=(const QPalette &p) const { return !(operator==(p)); } diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index 6b6eb896ed9..938a0dab729 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -374,6 +374,7 @@ Q_GUI_EXPORT QPalette qt_fusionPalette() const QColor button = backGround; const QColor shadow = dark.darker(135); const QColor disabledShadow = shadow.lighter(150); + const QColor disabledHighlight(145, 145, 145); QColor placeholder = text; placeholder.setAlpha(128); @@ -392,7 +393,11 @@ Q_GUI_EXPORT QPalette qt_fusionPalette() fusionPalette.setBrush(QPalette::Active, QPalette::Highlight, highlight); fusionPalette.setBrush(QPalette::Inactive, QPalette::Highlight, highlight); - fusionPalette.setBrush(QPalette::Disabled, QPalette::Highlight, QColor(145, 145, 145)); + fusionPalette.setBrush(QPalette::Disabled, QPalette::Highlight, disabledHighlight); + + fusionPalette.setBrush(QPalette::Active, QPalette::AccentColor, highlight); + fusionPalette.setBrush(QPalette::Inactive, QPalette::AccentColor, highlight); + fusionPalette.setBrush(QPalette::Disabled, QPalette::AccentColor, disabledHighlight); fusionPalette.setBrush(QPalette::PlaceholderText, placeholder); diff --git a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp index a203861e303..3c9a83c5aa4 100644 --- a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp @@ -295,6 +295,7 @@ static int NColorRoles[] = { QPalette::PlaceholderText + 1, // Qt_5_12 QPalette::PlaceholderText + 1, // Qt_5_13, Qt_5_14, Qt_5_15 QPalette::PlaceholderText + 1, // Qt_6_0 + QPalette::AccentColor + 1, // Qt_6_6 0 // add the correct value for Qt_5_14 here later }; @@ -2392,8 +2393,8 @@ void tst_QDataStream::setVersion() */ // revise the test if new color roles or color groups are added - QVERIFY(QPalette::NColorRoles == QPalette::PlaceholderText + 1); - QCOMPARE(int(QPalette::NColorGroups), 3); + QCOMPARE(QPalette::NColorRoles, QPalette::AccentColor + 1); + QCOMPARE(static_cast(QPalette::NColorGroups), 3); QByteArray ba2; QPalette pal1, pal2; diff --git a/tests/auto/gui/kernel/qpalette/tst_qpalette.cpp b/tests/auto/gui/kernel/qpalette/tst_qpalette.cpp index 81036bee0ac..bf9799724e1 100644 --- a/tests/auto/gui/kernel/qpalette/tst_qpalette.cpp +++ b/tests/auto/gui/kernel/qpalette/tst_qpalette.cpp @@ -23,6 +23,7 @@ private Q_SLOTS: void cannotCheckIfInvalidBrushSet(); void checkIfBrushForCurrentGroupSet(); void cacheKey(); + void dataStream(); }; void tst_QPalette::roleValues_data() @@ -51,9 +52,10 @@ void tst_QPalette::roleValues_data() QTest::newRow("QPalette::ToolTipBase") << int(QPalette::ToolTipBase) << 18; QTest::newRow("QPalette::ToolTipText") << int(QPalette::ToolTipText) << 19; QTest::newRow("QPalette::PlaceholderText") << int(QPalette::PlaceholderText) << 20; + QTest::newRow("QPalette::AccentColor") << int(QPalette::AccentColor) << 21; // Change this value as you add more roles. - QTest::newRow("QPalette::NColorRoles") << int(QPalette::NColorRoles) << 21; + QTest::newRow("QPalette::NColorRoles") << int(QPalette::NColorRoles) << 22; } void tst_QPalette::roleValues() @@ -236,8 +238,14 @@ void tst_QPalette::setAllPossibleBrushes() } for (int r = 0; r < QPalette::NColorRoles; ++r) { + const QPalette::ColorRole role = static_cast(r); for (int g = 0; g < QPalette::NColorGroups; ++g) { - QVERIFY(p.isBrushSet(QPalette::ColorGroup(g), QPalette::ColorRole(r))); + const QPalette::ColorGroup group = static_cast(g); + // NoRole has no resolve bit => isBrushSet returns false + if (role == QPalette::NoRole) + QVERIFY(!p.isBrushSet(group, role)); + else + QVERIFY(p.isBrushSet(group, role)); } } } @@ -341,5 +349,44 @@ void tst_QPalette::cacheKey() loggerShallowDetach.dismiss(); } +void tst_QPalette::dataStream() +{ + const QColor highlight(42, 42, 42); + const QColor accent(13, 13, 13); + QPalette palette; + palette.setBrush(QPalette::Highlight, highlight); + palette.setBrush(QPalette::AccentColor, accent); + + // When saved with Qt_6_5 or earlier, AccentColor defaults to Highlight + { + QByteArray b; + { + QDataStream stream(&b, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_6_5); + stream << palette; + } + QPalette test; + QDataStream stream (&b, QIODevice::ReadOnly); + stream.setVersion(QDataStream::Qt_6_5); + stream >> test; + QCOMPARE(test.accentColor().color(), highlight); + } + + // When saved with Qt_6_6 or later, AccentColor is saved explicitly + { + QByteArray b; + { + QDataStream stream(&b, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_6_6); + stream << palette; + } + QPalette test; + QDataStream stream (&b, QIODevice::ReadOnly); + stream.setVersion(QDataStream::Qt_6_6); + stream >> test; + QCOMPARE(test.accentColor().color(), accent); + } +} + QTEST_MAIN(tst_QPalette) #include "tst_qpalette.moc"