Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtQuickTemplates2/private/qquicktheme_p.h>
#include <QtQuick/private/qquickpalette_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include "qqstylekittheme_p.h"
#include "qqstylekitstyle_p.h"
QT_BEGIN_NAMESPACE
/*!
\qmltype Theme
\inqmlmodule Qt.labs.StyleKit
\inherits AbstractStyle
\brief Defines color and style overrides for a color scheme.
A Theme overrides properties defined in a \l Style to provide a
distinct visual appearance for a particular color scheme. A style
typically configures structural properties such as implicit size,
padding, and radii, while a theme specifies colors, shadows, and
other visual attributes. However, this is not a restriction — a
theme can override any property that a style can set.
A style can provide a \l {Style::light}{light} and a
\l {Style::dark}{dark} theme. When \l {Style::themeName}{themeName}
is set to \c "System" (the default), the active theme follows the
\l {QStyleHints::colorScheme}{operating system's color scheme}.
You can also create additional themes using \l CustomTheme and
\l {Style::themeName}{switch between them} at runtime.
Properties that are not set in the theme fall back to those defined
in the \l Style.
The following example defines light, dark, and high-contrast
themes, each providing different colors for the controls:
\snippet ThemeSnippets.qml themes
For a complete example showing themes in action, see the
\l {StyleKit Quick Controls Example}.
\labs
\sa Style, CustomTheme, {StyleKit Property Resolution}
*/
static QQuickTheme::Scope scopeForType(QQStyleKitExtendableControlType type)
{
switch (type) {
case QQStyleKitReader::ControlType::AbstractButton:
case QQStyleKitReader::ControlType::Button:
case QQStyleKitReader::ControlType::DelayButton:
case QQStyleKitReader::ControlType::FlatButton:
case QQStyleKitReader::ControlType::RoundButton:
return QQuickTheme::Button;
case QQStyleKitReader::ControlType::CheckBox:
return QQuickTheme::CheckBox;
case QQStyleKitReader::ControlType::CheckDelegate:
case QQStyleKitReader::ControlType::RadioDelegate:
case QQStyleKitReader::ControlType::SwipeDelegate:
case QQStyleKitReader::ControlType::SwitchDelegate:
return QQuickTheme::ListView;
case QQStyleKitReader::ControlType::ComboBox:
return QQuickTheme::ComboBox;
case QQStyleKitReader::ControlType::GroupBox:
return QQuickTheme::GroupBox;
case QQStyleKitReader::ControlType::HeaderViewDelegate:
case QQStyleKitReader::ControlType::ItemDelegate:
case QQStyleKitReader::ControlType::TableViewDelegate:
case QQStyleKitReader::ControlType::TreeViewDelegate:
return QQuickTheme::ItemView;
case QQStyleKitReader::ControlType::Label:
return QQuickTheme::Label;
case QQStyleKitReader::ControlType::MenuBar:
case QQStyleKitReader::ControlType::MenuBarItem:
return QQuickTheme::MenuBar;
case QQStyleKitReader::ControlType::Menu:
case QQStyleKitReader::ControlType::MenuItem:
case QQStyleKitReader::ControlType::MenuSeparator:
return QQuickTheme::Menu;
case QQStyleKitReader::ControlType::RadioButton:
return QQuickTheme::RadioButton;
case QQStyleKitReader::ControlType::SpinBox:
return QQuickTheme::SpinBox;
case QQStyleKitReader::ControlType::SwitchControl:
return QQuickTheme::Switch;
case QQStyleKitReader::ControlType::TabBar:
case QQStyleKitReader::ControlType::TabButton:
return QQuickTheme::TabBar;
case QQStyleKitReader::ControlType::TextArea:
return QQuickTheme::TextArea;
case QQStyleKitReader::ControlType::TextInput:
case QQStyleKitReader::ControlType::TextField:
return QQuickTheme::TextField;
case QQStyleKitReader::ControlType::ToolBar:
case QQStyleKitReader::ControlType::ToolButton:
case QQStyleKitReader::ControlType::ToolSeparator:
return QQuickTheme::ToolBar;
case QQStyleKitReader::ControlType::ToolTip:
return QQuickTheme::ToolTip;
case QQStyleKitReader::ControlType::Tumbler:
return QQuickTheme::Tumbler;
default:
return QQuickTheme::System;
}
Q_UNREACHABLE();
}
QQStyleKitTheme::QQStyleKitTheme(QObject *parent)
: QQStyleKitStyleAndThemeBase(parent)
{
}
QQStyleKitStyle *QQStyleKitTheme::style() const
{
QObject *parentObj = parent();
if (!parentObj)
return nullptr;
Q_ASSERT(qobject_cast<QQStyleKitStyle *>(parentObj));
return static_cast<QQStyleKitStyle *>(parentObj);
}
QPalette QQStyleKitTheme::paletteForControlType(QQStyleKitExtendableControlType type) const
{
const QQuickTheme::Scope scope = scopeForType(type);
return m_effectivePalettes[int(scope)];
}
QFont QQStyleKitTheme::fontForControlType(QQStyleKitExtendableControlType type) const
{
const QQuickTheme::Scope scope = scopeForType(type);
return m_effectiveFonts[int(scope)];
}
void QQStyleKitTheme::updateThemePalettes()
{
const QQStyleKitPalette *pals = palettes();
if (!pals)
return;
// Collect palette fallback chain
QVector<const QQStyleKitPalette *> fbChain;
for (auto *fb = pals; fb; fb = fb->fallbackPalette())
fbChain.append(fb);
auto resolveFromPaletteChain = [&](QQuickTheme::Scope scope,
QQuickPalette* (QQStyleKitPalette::*getter)() const) -> QPalette
{
QPalette result;
// Apply from farthest fallback -> local
for (int i = fbChain.size() - 1; i >= 0; --i) {
const QQStyleKitPalette *fb = fbChain[i];
// Apply system palette first as a base, and override with scope-specific palette
if (scope != QQuickTheme::System && fb->isSet(QQuickTheme::System)) {
if (auto *sys = fb->system())
result = sys->toQPalette().resolve(result);
}
if (fb->isSet(scope)) {
if (auto *p = (fb->*getter)())
result = p->toQPalette().resolve(result);
}
}
return result;
};
auto setResolved = [&](QQuickPalette* (QQStyleKitPalette::*getter)() const,
QQuickTheme::Scope scope)
{
m_effectivePalettes[int(scope)] = resolveFromPaletteChain(scope, getter);
};
setResolved(&QQStyleKitPalette::system, QQuickTheme::System);
setResolved(&QQStyleKitPalette::button, QQuickTheme::Button);
setResolved(&QQStyleKitPalette::checkBox, QQuickTheme::CheckBox);
setResolved(&QQStyleKitPalette::comboBox, QQuickTheme::ComboBox);
setResolved(&QQStyleKitPalette::groupBox, QQuickTheme::GroupBox);
setResolved(&QQStyleKitPalette::itemDelegate, QQuickTheme::ItemView);
setResolved(&QQStyleKitPalette::label, QQuickTheme::Label);
setResolved(&QQStyleKitPalette::radioButton, QQuickTheme::RadioButton);
setResolved(&QQStyleKitPalette::spinBox, QQuickTheme::SpinBox);
setResolved(&QQStyleKitPalette::tabBar, QQuickTheme::TabBar);
setResolved(&QQStyleKitPalette::textArea, QQuickTheme::TextArea);
setResolved(&QQStyleKitPalette::textField, QQuickTheme::TextField);
setResolved(&QQStyleKitPalette::toolBar, QQuickTheme::ToolBar);
}
void QQStyleKitTheme::updateThemeFonts()
{
const QQStyleKitFont *fonts = this->fonts();
if (!fonts)
return;
// Collect font fallback chain
QVector<const QQStyleKitFont *> fbChain;
for (auto *fb = fonts; fb; fb = fb->fallbackFont())
fbChain.append(fb);
// QFont::resolve() keeps only the left-hand operand's resolve mask.
// Restore the mask explicitly, or every attribute contributed by an
// earlier layer of the chain is dropped again by font resolution.
auto merge = [](const QFont &a, const QFont &b) {
QFont merged = a.resolve(b);
merged.setResolveMask(b.resolveMask() | a.resolveMask());
return merged;
};
auto resolveFromFontChain = [&](QQuickTheme::Scope scope) -> QFont
{
QFont result;
// Apply from farthest fallback -> local
for (int i = fbChain.size() - 1; i >= 0; --i) {
const QQStyleKitFont *fb = fbChain[i];
// Apply system font first as a base, and override with scope-specific font
if (scope != QQuickTheme::System && fb->isSet(QQuickTheme::System)) {
result = merge(fb->fontForScope(QQuickTheme::System), result);
}
if (fb->isSet(scope)) {
result = merge(fb->fontForScope(scope), result);
}
}
return result;
};
for (int i = 0; i < NScopes; ++i) {
const QQuickTheme::Scope scope = static_cast<QQuickTheme::Scope>(i);
m_effectiveFonts[i] = resolveFromFontChain(scope);
}
}
void QQStyleKitTheme::componentComplete()
{
QQStyleKitControls::componentComplete();
m_completed = true;
}
QT_END_NAMESPACE
#include "moc_qqstylekittheme_p.cpp"